Subscription Flow Tutorial
This Crystallize tutorial details the full process of creating and managing subscriptions, orders, and subscription-related events.
Creating a Subscription Contract
Subscription contracts tie subscription plans and customers together. Each contract represents an agreement with a customer regarding what product they’re buying and how they’re paying for it. The contract is what you use to keep track of usage and renewals.
Subscription contracts can be created programmatically with the Subscription API or with the Crystallize App. In order to create subscription contracts, you first must ensure the following:
- You have one or more customers defined within your tenant.
- You’ve created at least one subscription plan and have attached it to at least one product variant in your tenant’s catalogue. See our subscription plan documentation for more information.
- You have the requisite permissions for everything you'll be working with below. Refer to our documentation on roles and permissions for more information.
To create a subscription contract with the Subscription API, use the following mutation:
#API Endpoint: https://api.crystallize.com/tenant_identifier/subscriptions
mutation CreateSubscription {
subscriptionContracts {
create(input: {
tenantId: "TENANT_ID"
subscriptionPlan: {
identifier: "PLAN_IDENTIFIER"
periodId: "PERIOD_ID"
}
customerIdentifier: "legolasgreenleaf@fellowship.com"
item: {
name: "ITEM_NAME"
sku: "ITEM_SKU"
}
recurring: {
price: 10.00
currency: "EUR"
}
status: {
activeUntil: "2045-12-25T00:00:00"
renewAt: "2024-10-14T00:00:00"
price: 10.00
currency: "EUR"
}
}){
id
}
}
}
Let’s take a look at the input arguments here:
- tenantId: This is your tenant’s ID, NOT the tenant identifier.
- subscriptionPlan:
- identifier: A string that identifies the specific subscription plan. This is a predefined plan that the customer is subscribing to.
- periodId: The ID representing the duration or frequency of the subscription period, such as weekly or monthly.
- customerIdentifier: Identifier of the customer you would like to create the contract for.
- item: The name and the SKU of the item being subscribed to.
- recurring:
- price: The recurring price of the subscription, which will be charged at each renewal.
- currency: The currency in which the subscription price is charged (in this case, EUR).
- status:
- activeUntil: This ISO string represents the exact date and time when the subscription will be active until.
- renewAt: This ISO string indicates when the next renewal of the subscription should occur.
- price: The price at which the subscription will renew. This might be the same as the recurring price or different depending on promotions or pricing changes.
- currency: The currency for the renewal price (EUR here).
Once a contract is created for a customer, it will appear in the Crystallize App when you access the customer's record.
Managing Webhook on Contract Creation
You can set up a webhook that triggers when a new subscription contract has been created to further do something. This can be done manually within the Crystallize App or programmatically with the PIM API. In the App, you can access a sample query that can be edited based on your requirements.
Creating an Order
Now that you have a subscription contract in place, you must create an order for it. This is done with the Order API:
mutation createOrder {
orders {
create(
input: {
customer: {
firstName: "Legolas"
lastName: "Greenleaf"
identifier: "legolasgreenleaf@fellowship.com"
addresses: [
{
type: billing
streetNumber: "16"
street: "Greenwood"
city: "Woodland Realm"
country: "Ithilien"
postalCode: "9999"
email: "legolasgreenleaf@fellowship.com"
}
]
}
cart: {
name: "Jaguara Coffee"
sku: "jaguara-coffee"
imageUrl: "https://media.crystallize.com/lotr/23/1/27/6/@200/jaguara-coffee.avif"
quantity: 1
price: {
gross: 10
net: 10
tax: { name: "No Tax", percent: 0 }
currency: "EUR"
}
}
payment: {
provider: custom
custom: {
properties: { property: "payment_method", value: "Card" }
}
}
total: {
gross: 10
net: 10
currency: "EUR"
tax: { name: "No Tax", percent: 0 }
}
}
) {
id
}
}
}
The createOrder mutation takes a few input arguments. This includes the information about the customer: name, address (can be multiple, the example only has billing address for simplicity’s sake), customer identifier, etc. It includes information about the cart such as items and meta information. The cart here only has one item, but it can be an array of items. It also includes information about the payment and the total price of the cart items.
You as the user have full control over the information provided to the Order API. Behind the scenes, it doesn’t interact with any other API. It returns exactly what you provide to it.
Managing Webhook on Order Creation
Once an order is successfully placed in Crystallize, you would probably want to do things such as inform the customer. As orders in Crystallize are created asynchronously, the correct way to go about doing this is to set up a webhook that fires when an order has been created. The Crystallize App offers a sample query for this, too, which you can modify as needed.
Fulfilment Pipeline Changes
Fulfilment pipelines are a good solution for order management. Once an order has been placed, it can exist in as many pipelines as you require.
You can move orders between different pipeline stages within the Crystallize App or via the PIM API. Below is an example mutation to change the pipeline stage for an order. It takes three arguments: orderId, pipelineId, and stageId.
#API Endpoint: https://pim.crystallize.com/graphql
mutation SET_PIPELINE_STAGE {
order {
setPipelineStage(
orderId: "ORDER_ID"
pipelineId: "PIPELINE_ID"
stageId: "STAGE_ID"
) {
id
}
}
}
Managing Webhook on Pipeline State Change
A webhook can also be configured for a pipeline stage change to trigger specific functionality. Keep in mind that when an order is first added to a pipeline, this counts as a pipeline state change.
Renewing Subscriptions
Similar to contract creation, you can configure a webhook to trigger on renewal as well. You can provide a GraphQL query, like how we did earlier for contract creation, or you can leave it empty and the payload will contain the following information: contract ID, tenant ID, and user ID.
Once the renewal webhook has been triggered, you can create a renewal order based on it.