Skip to content

Latest commit

 

History

History
339 lines (271 loc) · 22.2 KB

File metadata and controls

339 lines (271 loc) · 22.2 KB

Configuring App Billing

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.

Using managed app pricing

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.

Using the billing API

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().

Configuring LineItem billing

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.

Configuring a Subscription Plan with a Single LineItem

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,
});

Configuring a Subscription Plan with Multiple LineItems

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,
});

Configuring a one-time charge

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,
});

Subscription Plan with LineItems

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.

Recurring Charge LineItem

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.

Usage Charge LineItem

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.

One Time Billing Plans

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

Configuring Billing

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,
        },
      },
    },
  },
});

Recurring Billing Plans

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 either amount or percentage but not both.

Usage Billing Plans

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.
  1. Prior to ApiVersion.April23 the currency code must be USD.

When should the app check for payment?

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:

  1. 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.
  2. 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.

Canceling a subscription

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.

Creating a usage record

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

Updating the capped amount for a usage billing plan

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

Back to guide index