The Shopify Subscriptions Reference App provides comprehensive contract management capabilities, allowing merchants to control subscription lifecycles and update contract details.
Core Operations
Pause Subscriptions Temporarily stop a subscription while retaining customer information
Resume Subscriptions Reactivate paused subscriptions and continue billing cycles
Cancel Subscriptions Permanently end a subscription contract
Update Details Modify subscription address, payment methods, and line items
Pause a Subscription
Merchants can pause active subscriptions to temporarily stop billing without canceling the contract.
Navigate to Contract Details
Access the subscription contract from the contracts list page
Click Pause Action
Select the pause action from the contract details page
Confirm Pause
The subscription status changes to PAUSED and billing cycles are suspended
Implementation
The pause functionality is implemented in app/routes/app.contracts.$id.pause/route.ts:12-37:
export async function action ({
request ,
params ,
} : ActionFunctionArgs ) : Promise < TypedResponse < WithToast >> {
const { admin , session } = await authenticate . admin ( request );
const contractId = composeGid ( 'SubscriptionContract' , params . id || '' );
try {
await new SubscriptionContractPauseService (
admin . graphql ,
session . shop ,
contractId ,
). run ();
return json ( toast ( t ( 'actions.pause.success' )));
} catch ( e ) {
return json ( toast ( t ( 'actions.pause.error' ), { isError: true }));
}
}
Paused subscriptions retain all contract details including customer information, line items, and billing schedules.
Resume a Subscription
Paused subscriptions can be reactivated to continue regular billing cycles.
Access Paused Contract
Navigate to a contract with PAUSED status
Click Resume Action
Select the resume action to reactivate billing
Billing Resumes
The subscription status changes to ACTIVE and the next billing cycle is scheduled
Implementation
The resume functionality is implemented in app/routes/app.contracts.$id.resume/route.ts:15-40:
export async function action ({
request ,
params ,
} : ActionFunctionArgs ) : Promise < TypedResponse < WithToast >> {
const { admin , session } = await authenticate . admin ( request );
const contractId = composeGid ( 'SubscriptionContract' , params . id || '' );
try {
await new SubscriptionContractResumeService (
admin . graphql ,
session . shop ,
contractId ,
). run ();
return json ( toast ( t ( 'actions.resume.success' )));
} catch ( e ) {
return json ( toast ( t ( 'actions.resume.error' ), { isError: true }));
}
}
Cancel a Subscription
Canceling a subscription permanently ends the contract and stops all future billing.
Open Cancel Modal
Click the cancel action on the contract details page
Confirm Cancellation
Confirm the irreversible cancellation action
Contract Terminated
The subscription status changes to CANCELLED and no further billing occurs
Implementation
The cancel functionality is implemented in app/routes/app.contracts.$id.cancel/route.ts:12-34:
export async function action ({
request ,
params ,
} : ActionFunctionArgs ) : Promise < TypedResponse < WithToast <{ error ?: boolean }>>> {
const { admin , session } = await authenticate . admin ( request );
const contractId = composeGid ( 'SubscriptionContract' , params . id || '' );
try {
await new SubscriptionContractCancelService (
admin . graphql ,
session . shop ,
contractId ,
). run ();
return json ( toast ( t ( 'actions.cancel.success' )));
} catch ( e ) {
return json ({
error: true ,
... toast ( t ( 'actions.cancel.error' ), { isError: true }),
});
}
}
Canceling a subscription is permanent and cannot be undone. Consider pausing instead if temporary suspension is needed.
Update Shipping Address
Merchants can update the shipping address for subscription deliveries.
Open Address Modal
Click the edit address action on the contract overview card
Enter New Address
Provide the updated shipping address details
Select Delivery Method
Choose an appropriate delivery method for the new address
Save Changes
The address update is committed to the subscription contract
Implementation
Address updates use the SubscriptionContractDraft model in app/routes/app.contracts.$id.address-update/route.ts:15-57:
export async function action ({
request ,
params ,
} : ActionFunctionArgs ) : Promise < TypedResponse < WithToast <{ error ?: boolean }>>> {
const body = await request . formData ();
const { admin , session } = await authenticate . admin ( request );
const contractId = composeGid ( 'SubscriptionContract' , params . id || '' );
const address = body . get ( 'address' ) as string ;
const deliveryMethodName = body . get ( 'deliveryMethodName' ) as string ;
const addressObject = JSON . parse ( address );
// Build draft from existing contract
const draft = await buildDraftFromContract (
session . shop ,
contractId ,
admin . graphql ,
);
// Update address and delivery method
const result = await draft . updateAddress ( addressObject , deliveryMethodName );
if ( ! result ) {
return json ({
error: true ,
... toast ( t ( 'edit.actions.updateAddress.error' ), { isError: true }),
});
}
// Commit the draft to apply changes
const draftCommitted = await draft . commit ();
if ( ! draftCommitted ) {
return json ({
error: true ,
... toast ( t ( 'edit.actions.updateAddress.error' ), { isError: true }),
});
}
return json ( toast ( t ( 'edit.actions.updateAddress.success' )));
}
Address updates use the Shopify Subscription Contract Draft API to ensure atomic updates and maintain data consistency.
Update Payment Method
Merchants can trigger payment method update emails to customers.
Open Payment Method Card
Navigate to the customer payment method section
Send Update Email
Click to send a payment method update request to the customer
Customer Updates
The customer receives an email with a secure link to update their payment method
Implementation
Payment method updates are handled in app/routes/app.contracts.$id.payment-update/route.ts:16-72:
export async function action ({
request ,
params ,
} : ActionFunctionArgs ) : Promise < TypedResponse < WithToast <{ error ?: boolean }>>> {
const { admin } = await authenticate . admin ( request );
const contractId = composeGid ( 'SubscriptionContract' , params . id || '' );
// Fetch payment details
const response = await admin . graphql ( PaymentUpdateDetailsQuery , {
variables: { contractId },
});
const { data } = await response . json ();
const { name : shopName , contactEmail : shopEmail } = data ?. shop ;
const subscriptionContract = data ?. subscriptionContract ;
const customerEmail = subscriptionContract ?. customer ?. email ;
const customerPaymentMethodId =
subscriptionContract ?. customerPaymentMethod ?. id ;
// Send payment method update email
const sendEmailResponse = await sendPaymentMethodUpdateEmail (
admin . graphql ,
customerPaymentMethodId ,
{
from: shopEmail ,
to: customerEmail ,
subject: t ( 'paymentMethodDetails.emailModal.subjectMessage' , { shopName }),
},
);
const { customer , userErrors } = sendEmailResponse || {};
if ( ! customer || userErrors ?. length ) {
return json ({
error: true ,
... toast ( t ( 'paymentMethodDetails.emailModal.errorMessage' ), {
isError: true ,
}),
});
}
return json ( toast ( t ( 'paymentMethodDetails.emailModal.successMessage' )));
}
Payment method updates are initiated via email for security. Customers use a secure Shopify-hosted page to update their payment information.
Edit Subscription Details
Merchants can modify subscription line items, quantities, and pricing:
Edit line item quantities
Update product variants
Modify pricing (with custom pricing modal)
Add or remove line items
These operations use the SubscriptionContractDraft API to ensure consistent updates across the contract.
Contract Status Flow
Best Practices
Use Pause for Temporary Stops Recommend pausing instead of canceling when customers need a break
Validate Address Changes Ensure delivery methods are compatible with new shipping addresses
Communicate Changes Send notifications to customers when contract details change
Handle Errors Gracefully Provide clear error messages and retry options for failed operations