Some partners might wish to charge merchants for using their app. Shopify makes it possible to do that in two ways, so that merchants can pay for apps as part of their Shopify payments:
- Using Shopify managed app pricing
- Using the Admin billing API to trigger purchases in the Shopify platform.
In this guide you'll learn how to use this package for both of those scenarios.
- Configuring App Billing
The easiest way to add billing to your app is to follow the Shopify managed app pricing documentation. You can set up one or more plans for the app, and Shopify will host a page where merchants can select which plan they want.
Since Shopify handles billing in this scenario, you don't have to add a billing
setting to your configuration, but you can still use the check
method to get the plans for the current merchant.
If you'd prefer to have full control over billing, you can use the helpers in this package to interact with the billing API directly.
See the billing reference for details on how to call those endpoints, using this configuration.
To trigger the billing behaviour, you'll need to set the billing
value when calling shopifyApi()
.
With the future flag v10_lineItemBilling
, the billing configuration can now specify the the AppSubscriptionLineItems
. This will allow you to create app subscription plans with both recurring and usage based charges.
Subscription plans can have 1 or 2 line items. There can be a maximum of 1 of each type of plan Usage and Recurring. Usage line items can only be used in conjunction with recurring line items when the recurring line item interval is BillingInterval.Every30Days
.
For configuring billing without line items see Configuring Billing.
import {
shopifyApi,
BillingInterval,
BillingReplacementBehavior,
} from '@shopify/shopify-api';
const shopify = shopifyApi({
// ...
billing: {
{
"Single LineItem Plan": {
replacementBehavior: BillingReplacementBehavior.ApplyImmediately,
trialDays: 7,
lineItems: [
{
interval: BillingInterval.Every30Days,
amount: 30,
currencyCode: "USD",
discount: {
durationLimitInIntervals: 3,
value: {
amount: 10,
},
},
}
],
},
},
}
future: {
v10_lineItemBilling: true,
});
import {
shopifyApi,
BillingInterval,
BillingReplacementBehavior,
} from '@shopify/shopify-api';
const shopify = shopifyApi({
// ...
billing: {
{
"Multiple LineItems Plan": {
replacementBehavior: BillingReplacementBehavior.ApplyImmediately,
trialDays: 7,
lineItems: [
{
interval: BillingInterval.Every30Days,
amount: 30,
currencyCode: "USD",
discount: {
durationLimitInIntervals: 3,
value: {
amount: 10,
},
},
},
{
interval: BillingInterval.Usage,
amount: 30,
currencyCode: "USD",
terms: "per 1000 emails",
},
],
},
},
},
future: {
v10_lineItemBilling: true,
});
import {
shopifyApi,
BillingInterval,
BillingReplacementBehavior,
} from '@shopify/shopify-api';
const shopify = shopifyApi({
// ...
billing: {
{
"One Time Charge": {
interval: BillingInterval.OneTime,
amount: 30,
currencyCode: "USD",
},
},
}
future: {
v10_lineItemBilling: true,
});
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
LineItems |
LineItems[] |
Yes | - | An array of LineItems to be included in the subscription plan. |
trialDays |
number |
No | - | Give merchants this many days before charging |
replacementBehavior |
BillingReplacementBehavior |
No | - | BillingReplacementBehavior value, see the reference for more information. |
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
interval |
EVERY_30_DAYS , ANNUAL |
Yes | - | BillingInterval.Every30Days , BillingInterval.Annual value |
amount |
number |
Yes | - | The amount to charge |
currencyCode |
string |
Yes | - | The currency to charge, USD or merchant's shop currency1 |
discount.durationLimitInIntervals |
number |
No | - | The number of billing intervals to apply the discount for. See the reference for more information |
discount.value.amount |
number |
No | - | The amount of the discount in the currency that the merchant is being billed in. |
discount.value.percentage |
number |
No | - | The percentage value of the discount. |
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
interval |
USAGE |
Yes | - | BillingInterval.Usage |
amount |
number |
Yes | - | The maximum amount the merchant will be charged |
currencyCode |
string |
Yes | - | The currency to charge, USD or merchant's shop currency1 |
usageTerms |
string |
Yes | - | These terms stipulate the pricing model for the charges that an app creates. |
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
interval |
ONE_TIME |
Yes | - | BillingInterval.OneTime value |
amount |
number |
Yes | - | The amount to charge |
currencyCode |
string |
Yes | - | The currency to charge, USD or merchant's shop currency1 |
Prior to the future flag v10_lineItemBilling
and when the flag is set to false you will configure billing as follows. For example, the following configuration will allow you to charge merchants $30 every 30 days. The first three charges will be discounted by $10, so merchants would be charged $20.
import {
shopifyApi,
BillingInterval,
BillingReplacementBehavior,
} from '@shopify/shopify-api';
const shopify = shopifyApi({
// ...
billing: {
'My billing plan': {
interval: BillingInterval.Every30Days,
amount: 30,
currencyCode: 'USD',
replacementBehavior: BillingReplacementBehavior.ApplyImmediately,
discount: {
durationLimitInIntervals: 3,
value: {
amount: 10,
},
},
},
},
});
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
interval |
EVERY_30_DAYS , ANNUAL |
Yes | - | BillingInterval.Every30Days , BillingInterval.Annual value |
amount |
number |
Yes | - | The amount to charge |
currencyCode |
string |
Yes | - | The currency to charge, USD or merchant's shop currency1 |
trialDays |
number |
No | - | Give merchants this many days before charging |
replacementBehavior |
BillingReplacementBehavior |
No | - | BillingReplacementBehavior value, see the reference for more information. |
discount.durationLimitInIntervals |
number |
No | - | The number of billing intervals to apply the discount for. See the reference for more information |
discount.value.amount |
number |
No | - | The amount of the discount in the currency that the merchant is being billed in. |
discount.value.percentage |
number |
No | - | The percentage value of the discount. |
Note
discount.value
can only include eitheramount
orpercentage
but not both.
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
interval |
USAGE |
Yes | - | BillingInterval.Usage |
amount |
number |
Yes | - | The maximum amount the merchant will be charged |
currencyCode |
string |
Yes | - | The currency to charge, USD or merchant's shop currency1 |
usageTerms |
string |
Yes | - | These terms stipulate the pricing model for the charges that an app creates. |
trialDays |
number |
No | - | Give merchants this many days before charging |
replacementBehavior |
BillingReplacementBehavior |
No | - | BillingReplacementBehavior value, see the reference for more information. |
- Prior to
ApiVersion.April23
the currency code must beUSD
.
As mentioned above, billing requires a session to access the API, which means that the app must actually be installed before it can request payment.
With the check
method, your app can block access to specific endpoints, or to the app as a whole.
If you're gating access to the entire app, you should check for billing:
- After OAuth completes, you'll get the session back from
shopify.auth.callback
. You can use the session to ensure billing takes place as part of the authentication flow. - When validating requests from the frontend. Since the check requires API access, you can only run it in requests that work with
shopify.session.getCurrentId
.
Note: the merchant may refuse payment when prompted or cancel subscriptions later on, but the app will already be installed at that point. We recommend using billing webhooks to revoke access for merchants when they cancel / decline payment.
With the cancel
method you'll be able to cancel a single subscription. First, you'll need to obtain the id for the subscription you wish to cancel. You can use the subscriptions
method to obtain a list of current subscriptions.
As of version 7.3.0
, the check
and request
methods can take an optional returnObject
parameter that modifies the return value to be an object that will include the payment/subscription plan id
's, among other data. This allows the app to save the id's for a future call to cancel
. See the billing reference for more details.
const activeSubscriptions = await shopify.billing.subscriptions({
session: res.locals.shopify.session,
});
// activeSubscriptions will be an array of subscription details, e.g.,
// [
// {
// "name": "My Shopify Subscription Charge",
// "id": "gid://shopify/AppSubscription/1234567890",
// "test": true
// },
// ],
The cancel
method will require the session
object that is setup during authorization as well as the subscription id.
The call to cancel
will return an AppSubscription
object, containing the details of the subscription just cancelled successfully, and will throw a BillingError
if any errors occur.
// using the example activeSubscriptions response above...
const subscriptionId = activeSubscriptions[0].id; // "gid://shopify/AppSubscription/1234567890"
const canceledSubscription = await shopify.billing.cancel({
session,
subscriptionId,
prorate: true, // Whether to issue prorated credits for the unused portion of the app subscription. Defaults to true.
});
// canceledSubscription will have the following shape:
// {
// id: string;
// name: string;
// test: boolean;
// }
See the billing reference for details on how to call the subscriptions
and cancel
endpoints.
When using a usage based billing plan you must create usage records to charge the merchant. The createUsageRecord
method will create a usage record for a usage billing plan.
The call to createUsageRecord
will return an UsageRecord
object, containing the details of the usage record just created successfully, and will throw a BillingError
if any errors occur.
See the billing reference for details on how to call the createUsageRecord
endpoint.
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
session |
Session |
Yes | - | The session to be used for this request |
description |
string |
Yes | - | The description of the usage record. |
price.amount |
number |
Yes | - | The amount and currency to be charged |
price.currency |
string |
Yes | - | The amount and currency to be charged |
subscriptionLineItemId |
string |
No | - | The line item to create the usage record for. If no value is provided a record will be created on the active usage line item |
idempotencyKey |
string |
No | - | A unique key that can be passed to the request to prevent duplicate charges |
isTest |
Boolean |
No | - | Whether this is a test charge |
A usage-based subscription is a pricing model that charges merchants continuously based on app use during Shopify's 30-day billing cycle. The capped amount is the maximum that a merchant is billed for during the cycle.
The response from updateUsageCappedAmount
contains a confirmationUrl
that will need to be sent to the merchant to confirm the update.
See the billing reference for details on how to call the updateUsageCappedAmount
endpoint.
Parameter | Type | Required? | Default Value | Notes |
---|---|---|---|---|
session |
Session |
Yes | - | The session to be used for this request |
subscriptionLineItemId |
string |
Yes | - | The line item to update the maximum charge for |
cappedAmount.amount |
number |
Yes | - | The maximum amount to charge the merchant |
cappedAmount.currency |
string |
Yes | - | The currency to charge |