Create and manage subscription contracts
A subscription contract is the agreement between a customer and a merchant over a specific term for recurring purchases over a set or undefined period of time.
This tutorial shows you how to create and manage subscription contracts by illustrating two use cases: "Subscribe and save" subscriptions and "Prepaid" subscriptions.
Requirements
Anchor link to section titled "Requirements"- You've completed our Getting started with the GraphQL Admin and REST Admin APIs guide and you're authenticated with the API.
- You've created products and product variants in your development store.
- You've completed our Create and manage selling plans tutorial.
Scope requirements
Anchor link to section titled "Scope requirements"To use the GraphQL mutations, your app needs to request the following access scopes for a Shopify store:
read_own_subscription_contracts
: Allows an app to read subscription contract mutations for contracts they own.write_own_subscription_contracts
: Allows an app to write subscription contract mutations for contracts they own.
Subscription contracts
Anchor link to section titled "Subscription contracts"A subscription contract is the result of purchasing a selling plan that has recurring policies. After a customer purchases a subscription product or variant at checkout, Shopify generates a subscription contract and shares the contract with your app using a webhook.
Subscription contracts generate new orders for merchants to fulfill at subscription renewal time. They're also the mechanism by which apps will bill customers for their subscriptions.
View subscription contracts object descriptions
API object | Description |
---|---|
Subscription contract | Represents the agreement for a set of items delivered to a customer, on a specific billing and delivery schedule and at a specific price.
A subscription contract is different from an order. An order is a customer's completed request to purchase one or more products from a shop. An order is created when a customer completes the checkout process, during which time they provide an email address or phone number, billing address, and payment information. Subscription contracts might not be immediately available when an order is created. It's best to rely on subscription contract webhooks to be notified when contracts are created. |
Contract line | Represents the price and quantity of items in a subscription contract. |
Pricing policy | Represents the future intent to change the price after a given number of billing cycles. When the customer completes a checkout, Shopify creates a contract, and the app developer is notified of which policies the customer chose. |
Subscription order line | Links a subscription contract to its related order line items, and optionally, to the billing attempt that created this order line. A subscription order line is created when one of the following events occurs:
|
Subscription draft | Represents the intent to change a subscription. If a customer wants to change their subscription, then the app needs to create a subscription draft and commit the change to make the changes active. |
Draft change | Represents a change made to a subscription draft, such as changing a contract line. |
Billing attempt | Represents an attempt at executing a billing cycle and charging the customer payment method for a subscription contract. |
Query the available contracts on a store
Anchor link to section titled "Query the available contracts on a store"To view the available contracts on a store, you can query SubscriptionContract
. In the following example, the response body returns all of the available contracts, including the status of the contract and information about the customer and billing policy.
A subscription contract can have one of the following statuses:
ACTIVE
: The contract is active and continuing as per its policies.PAUSED
: The contract is temporarily paused and is expected to resume in the future.EXPIRED
: The contract has ended per the expected circumstances. All billing and delivery cycles of the subscriptions were executed.CANCELLED
: The contract ended by an unplanned customer action.FAILED
: The contract ended because billing failed and no further billing attempts are expected.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Create a new subscription draft
Anchor link to section titled "Create a new subscription draft"A subscription draft captures the intent to change a subscription contract. Apps can incrementally build subscription contracts.
A subscription draft provides a way to get the projected state of the contract with all of the updates applied. Subscription contracts should always be up-to-date and accurate so that you can report on subscriptions, email subscribers, and build flows based on subscription changes.
The following example shows the creation of a new subscription draft for a monthly "Subscribe and save" subscription, with a minimum commitment of three orders.
For error codes related to subscription contracts, refer to SubscriptionDraftErrorCode
.
Example request
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Prepaid contracts are created by defining a delivery policy that is more frequent than the billing policy. In the following example, the policies combine to define a prepaid subscription with three monthly deliveries.
For error codes related to subscription contracts, refer to SubscriptionDraftErrorCode
.
Example request
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Update a subscription draft
Anchor link to section titled "Update a subscription draft"You can call the subscriptionDraftUpdate
mutation to make changes to a subscription draft. In the following example, the delivery policy is changed to every two months.
Example request
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Add a line to the subscription draft
Anchor link to section titled "Add a line to the subscription draft"You can call the subscriptionDraftLineAdd
mutation to add a subscription line to the subscription draft. In the following example, a subscription line is added to specify a product variant with its quantity and price.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Update a line on a subscription contract
Anchor link to section titled "Update a line on a subscription contract"You can call the subscriptionDraftLineUpdate
mutation to update a line on the subscription draft. In this example, quantity
is updated to 10
.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Remove a line on a subscription draft
Anchor link to section titled "Remove a line on a subscription draft"You can call the subscriptionDraftLineRemove
mutation and supply the lineId
to remove a subscription line on the subscription draft.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Commit a subscription draft
Anchor link to section titled "Commit a subscription draft"When you are satisfied with the state of the draft subscription, you can commit it. When you commit a draft subscription, all of the changes are made active.
Example request
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Viewing subscription contract details
Anchor link to section titled "Viewing subscription contract details"The View subscription button on the customer subscriptions card and the order subscriptions card allows merchants to navigate to the app and view the subscription contract details.
Customers page in the Shopify admin
Anchor link to section titled "Customers page in the Shopify admin"Order page in the Shopify admin
Anchor link to section titled "Order page in the Shopify admin"Redirecting to the subscription contract within the app
Anchor link to section titled "Redirecting to the subscription contract within the app"To redirect merchants to the relevant subscription contract, the app needs to implement a specific endpoint. After implemented, the endpoint redirects to the subscription contract page within the app for the subscription defined by subscription_contract_id
.
You can customize the View subscription link by managing the Subscription link app extension in your Partner Dashboard:
You can modify the link target URL, save the changes, or remove the app extension if it's already present:
If you don't customize the View subscription link, then the link is hardcoded. The hardcoded link has the following format:
{app_application_url}/subscriptions?customer_id={customer_id}&hmac={hmac}&id={subscription_contract_id}&shop={myshopify_domain}
Update a subscription contract
Anchor link to section titled "Update a subscription contract"When a customer updates their payment method or makes a change to their subscription, you need to update the appropriate subscription contract.
To update a subscription contract, use the subscriptionContractUpdate
mutation and supply the subscription contract ID. The call returns a draft with the existing state of the contract.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Create a billing attempt
Anchor link to section titled "Create a billing attempt"To schedule and automate the billing of subscriptions, apps need to create a billing attempt. A subscription is renewed when an app makes a billing attempt.
A billing attempt represents an attempt at executing a billing cycle and charging the customer payment method for a subscription contract. A billing attempt executes a contract in its current state and ensures that the app’s contract data is synced with Shopify before billing.
A billing attempt starts in a pending status. After it has been processed, it either transitions to successful or failed, both of which are terminal states:
- If the billing attempt is successful, then an order is created.
- If the billing attempt fails, then it means that the transaction has failed.
If an action is pending on the part of the customer in regards to 3D Secure, then a 3D Secure challenge can occur before the billing attempt transitions to a terminal state.
Example call
Anchor link to section titled "Example call"To create a billing attempt, specify the following inputs in the subscriptionBillingAttemptCreate
mutation:
subscriptionContractId
: The ID of the subscription contract.subscriptionBillingAttemptInput
idempotencyKey
: A unique key generated by the client to avoid duplicate payments.test
: Whether to execute the payment in test mode, if possible. The default isfalse
. When set totrue
, it will ignore the payment gateway’s mode. When set tofalse
, it will use the payment gateway’s mode.originTime
: An optional field that changes the way fulfillment intervals are calculated. If nothing is provided, fulfillment is calculated using the date that the billing attempt was successful. Otherwise, fulfillment is calculated using the providedoriginTime
value. The UTC offset oforiginTime
should match the shop'stimezoneOffset
.
Billing attempts are processed asynchronously, which means the resulting order will not be available right away. You can fetch the billing attempt and inspect the ready
field to find out whether the order has been created (true
) or not (false
).
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Because the order isn't ready right away, you can query the SubScriptionBillingAttempt to get the resulting order information.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
About 3D Secure
Anchor link to section titled "About 3D Secure"Shopify handles 3D Secure authentication by emailing the customer when the financial institution requires a challenge. This flow is demonstrated in the diagram below:
You can poll the subscriptionBillingAttempt
object until the nextActionUrl
field is available to see the URL. It's up to apps to attempt re-billing for failed payment attempts.
Example request:
POST /admin/api/2021-01/graphql.json
View response
JSON response:
Subscription-related webhooks
Anchor link to section titled "Subscription-related webhooks"Your app can subscribe to the following subscription-related webhooks. To learn how to set up and consume webhooks, refer to Configuring webhooks.
customer_payment_methods/create
Description | Payload | Required scope |
---|---|---|
Emitted when a payment method is created. | { customer_id, token, instrument_type, payment_instrument, } |
read_customer_payment_methods |
customer_payment_methods/update
Description | Payload | Required scope |
---|---|---|
Emitted when a payment method is updated. | { customer_id, token, instrument_type, payment_instrument, } |
read_customer_payment_methods |
customer_payment_methods/revoke
Description | Payload | Required scope |
---|---|---|
Emitted when the payment method is revoked. | Not applicable | read_customer_payment_methods |
subscription_contracts/create
Description | Payload | Required scope |
---|---|---|
Emitted when a contract is first created. This is deferred until after the order is created. | { admin_graphql_api_id, id, billing_policy, delivery_policy, admin_graphql_api_customer_id, customer_id, currency_code, status, admin_graphql_api_origin_order_id, origin_order_id } |
read_own_subscription_contracts |
subscription_contracts/update
Description | Payload | Required scope |
---|---|---|
Emitted when a contract is updated. | { admin_graphql_api_id, id, billing_policy, delivery_policy, admin_graphql_api_customer_id, customer_id, currency_code, status, admin_graphql_api_origin_order_id, origin_order_id } |
read_own_subscription_contracts |
subscription_billing_attempts/success
Description | Payload | Required scope |
---|---|---|
Emitted when billing succeeds. | { id, admin_graphql_api_id, idempotency_key, order_id, admin_graphql_api_order_id, subscription_contract_id, admin_graphql_api_subscription_contract_id, ready, error_message, } |
read_own_subscription_contracts |
subscription_billing_attempts/failure
Description | Payload | Required scope |
---|---|---|
Emitted when billing fails. | { id, admin_graphql_api_id, idempotency_key, order_id, admin_graphql_api_order_id, subscription_contract_id, admin_graphql_api_subscription_contract_id, ready, error_message, }In this case, order_id might be null . The error_message will be populated. |
read_own_subscription_contracts |
subscription_billing_attempts/challenged
Description | Payload | Required scope |
---|---|---|
Emitted when the financial institution challenges the subscription billing attempt charge as per 3D Secure. | { id, admin_graphql_api_id, idempotency_key, order_id, admin_graphql_api_order_id, subscription_contract_id, admin_graphql_api_subscription_contract_id, ready, error_message, } |
read_own_subscription_contracts |
Create and manage discounts on subscriptions: Learn how to apply different types of discounts on subscriptions, including percentage, fixed amount, and free shipping discounts.
Create and manage subscription shipping rates and delivery methods: Learn how to update delivery methods on subscriptions and understand the shipping rates customers see during checkout.