Billingobject
Contains function used to bill merchants for your app.
This object is returned on authenticated Admin requests.
Anchor to billingbilling
Provides utilities that apps can use to request billing for the app using the Admin API.
- Anchor to requirerequire(options: RequireBillingOptions<Config>) => Promise<BillingCheckResponseObject>required
Checks if the shop has an active payment for any plan defined in the
billing
config option.- Anchor to checkcheck(options: CheckBillingOptions<Config>) => Promise<BillingCheckResponseObject>required
Checks if the shop has an active payment for any plan defined in the
billing
config option.- Anchor to requestrequest(options: RequestBillingOptions<Config>) => Promise<never>required
Requests payment for the plan.
- Anchor to cancelcancel(options: CancelBillingOptions) => Promise<AppSubscription>required
Cancels an ongoing subscription, given its ID.
RequireBillingOptions
- plans
The plans to check for. Must be one of the values defined in the `billing` config option.
(keyof Config["billing"])[]
- onFailure
How to handle the request if the shop doesn't have an active payment for any plan.
(error: any) => Promise<Response>
- isTest
Whether to consider test purchases.
boolean
export interface RequireBillingOptions<Config extends AppConfigArg>
extends Omit<BillingCheckParams, 'session' | 'plans' | 'returnObject'> {
/**
* The plans to check for. Must be one of the values defined in the `billing` config option.
*/
plans: (keyof Config['billing'])[];
/**
* How to handle the request if the shop doesn't have an active payment for any plan.
*/
onFailure: (error: any) => Promise<Response>;
}
BillingCheckResponseObject
- hasActivePayment
Whether the user has an active payment method.
boolean
- oneTimePurchases
The one-time purchases the shop has.
OneTimePurchase[]
- appSubscriptions
The active subscriptions the shop has.
AppSubscription[]
export interface BillingCheckResponseObject {
/**
* Whether the user has an active payment method.
*/
hasActivePayment: boolean;
/**
* The one-time purchases the shop has.
*/
oneTimePurchases: OneTimePurchase[];
/**
* The active subscriptions the shop has.
*/
appSubscriptions: AppSubscription[];
}
OneTimePurchase
- id
The ID of the one-time purchase.
string
- name
The name of the purchased plan.
string
- test
Whether this is a test purchase.
boolean
- status
The status of the one-time purchase.
string
export interface OneTimePurchase {
/**
* The ID of the one-time purchase.
*/
id: string;
/**
* The name of the purchased plan.
*/
name: string;
/**
* Whether this is a test purchase.
*/
test: boolean;
/**
* The status of the one-time purchase.
*/
status: string;
}
AppSubscription
- id
The ID of the app subscription.
string
- name
The name of the purchased plan.
string
- test
Whether this is a test subscription.
boolean
- lineItems
ActiveSubscriptionLineItem[]
export interface AppSubscription {
/**
* The ID of the app subscription.
*/
id: string;
/**
* The name of the purchased plan.
*/
name: string;
/**
* Whether this is a test subscription.
*/
test: boolean;
/*
* The line items for this plan. This will become mandatory in v10.
*/
lineItems?: ActiveSubscriptionLineItem[];
}
ActiveSubscriptionLineItem
- id
string
- plan
AppPlan
export interface ActiveSubscriptionLineItem {
/*
* The ID of the line item.
*/
id: string;
/*
* The details of the plan.
*/
plan: AppPlan;
}
AppPlan
- pricingDetails
RecurringAppPlan | UsageAppPlan
export interface AppPlan {
/*
* The pricing details of the plan.
*/
pricingDetails: RecurringAppPlan | UsageAppPlan;
}
RecurringAppPlan
- interval
BillingInterval.Every30Days | BillingInterval.Annual
- price
Money
- discount
AppPlanDiscount
export interface RecurringAppPlan {
/*
* The interval for this plan is charged on.
*/
interval: BillingInterval.Every30Days | BillingInterval.Annual;
/*
* The price of the plan.
*/
price: Money;
/*
* The discount applied to the plan.
*/
discount: AppPlanDiscount;
}
BillingInterval
- OneTime
ONE_TIME
- Every30Days
EVERY_30_DAYS
- Annual
ANNUAL
- Usage
USAGE
export enum BillingInterval {
OneTime = 'ONE_TIME',
Every30Days = 'EVERY_30_DAYS',
Annual = 'ANNUAL',
Usage = 'USAGE',
}
Money
- amount
number
- currencyCode
string
interface Money {
amount: number;
currencyCode: string;
}
AppPlanDiscount
- durationLimitInIntervals
number
- remainingDurationInIntervals
number
- priceAfterDiscount
Money
- value
AppPlanDiscountAmount
export interface AppPlanDiscount {
/*
* The total number of intervals the discount applies to.
*/
durationLimitInIntervals: number;
/*
* The remaining number of intervals the discount applies to.
*/
remainingDurationInIntervals: number;
/*
* The price after the discount is applied.
*/
priceAfterDiscount: Money;
/*
* The value of the discount applied every billing interval.
*/
value: AppPlanDiscountAmount;
}
AppPlanDiscountAmount
BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage
BillingConfigSubscriptionPlanDiscountAmount
- amount
The amount to discount. Cannot be set if `percentage` is set.
number
- percentage
The percentage to discount. Cannot be set if `amount` is set.
never
export interface BillingConfigSubscriptionPlanDiscountAmount {
/**
* The amount to discount.
*
* Cannot be set if `percentage` is set.
*/
amount: number;
/**
* The percentage to discount.
*
* Cannot be set if `amount` is set.
*/
percentage?: never;
}
BillingConfigSubscriptionPlanDiscountPercentage
- amount
The amount to discount. Cannot be set if `percentage` is set.
never
- percentage
The percentage to discount. Cannot be set if `amount` is set.
number
export interface BillingConfigSubscriptionPlanDiscountPercentage {
/**
* The amount to discount.
*
* Cannot be set if `percentage` is set.
*/
amount?: never;
/**
* The percentage to discount.
*
* Cannot be set if `amount` is set.
*/
percentage: number;
}
UsageAppPlan
- balanceUsed
Money
- cappedAmount
Money
- terms
string
export interface UsageAppPlan {
/*
* The total usage records for interval.
*/
balanceUsed: Money;
/*
* The capped amount prevents the merchant from being charged for any usage over that amount during a billing period.
*/
cappedAmount: Money;
/*
* The terms and conditions for app usage pricing.
*/
terms: string;
}
CheckBillingOptions
- plans
The plans to check for. Must be one of the values defined in the `billing` config option.
(keyof Config["billing"])[]
- isTest
Whether to consider test purchases.
boolean
export interface CheckBillingOptions<Config extends AppConfigArg>
extends Omit<BillingCheckParams, 'session' | 'plans' | 'returnObject'> {
/**
* The plans to check for. Must be one of the values defined in the `billing` config option.
*/
plans: (keyof Config['billing'])[];
}
RequestBillingOptions
- plan
The plan to request. Must be one of the values defined in the `billing` config option.
keyof Config["billing"]
- isTest
Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.
boolean
- returnUrl
The URL to return to after the merchant approves the payment.
string
export interface RequestBillingOptions<Config extends AppConfigArg>
extends Omit<BillingRequestParams, 'session' | 'plan' | 'returnObject'> {
/**
* The plan to request. Must be one of the values defined in the `billing` config option.
*/
plan: keyof Config['billing'];
/**
* Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.
*/
isTest?: boolean;
/**
* The URL to return to after the merchant approves the payment.
*/
returnUrl?: string;
}
CancelBillingOptions
- subscriptionId
The ID of the subscription to cancel.
string
- prorate
Whether to prorate the cancellation.
boolean
- isTest
boolean
export interface CancelBillingOptions {
/**
* The ID of the subscription to cancel.
*/
subscriptionId: string;
/**
* Whether to prorate the cancellation.
*
* {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}
*/
prorate?: boolean;
/*
* Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.
*/
isTest?: boolean;
}
Anchor to examplesExamples
Anchor to example-requirerequire
Anchor to example-requesting-billing-right-awayRequesting billing right away
Call billing.request
in the callback to immediately redirect to the Shopify page to request payment.
Anchor to example-redirect-to-a-plan-selection-pageRedirect to a plan selection page
When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.
Anchor to example-requesting-billing-with-line-itemsRequesting billing with line items
Call billing.request
with the future flag enabled
Requesting billing right away
Examples
Requesting billing right away
Description
Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.
/app/routes/**\/*.ts
import { LoaderFunctionArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { billing } = await authenticate.admin(request); await billing.require({ plans: [MONTHLY_PLAN], isTest: true, onFailure: async () => billing.request({ plan: MONTHLY_PLAN }), }); // App logic };
shopify.server.ts
import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server"; export const MONTHLY_PLAN = 'Monthly subscription'; export const ANNUAL_PLAN = 'Annual subscription'; const shopify = shopifyApp({ // ...etc billing: { [MONTHLY_PLAN]: { amount: 5, currencyCode: 'USD', interval: BillingInterval.Every30Days, }, [ANNUAL_PLAN]: { amount: 50, currencyCode: 'USD', interval: BillingInterval.Annual, }, } }); export default shopify; export const authenticate = shopify.authenticate;
Redirect to a plan selection page
Description
When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.
/app/routes/**\/*.ts
import { LoaderFunctionArgs, redirect } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { billing } = await authenticate.admin(request); const billingCheck = await billing.require({ plans: [MONTHLY_PLAN, ANNUAL_PLAN], isTest: true, onFailure: () => redirect('/select-plan'), }); const subscription = billingCheck.appSubscriptions[0]; console.log(`Shop is on ${subscription.name} (id ${subscription.id})`); // App logic };
shopify.server.ts
import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server"; export const MONTHLY_PLAN = 'Monthly subscription'; export const ANNUAL_PLAN = 'Annual subscription'; const shopify = shopifyApp({ // ...etc billing: { [MONTHLY_PLAN]: { amount: 5, currencyCode: 'USD', interval: BillingInterval.Every30Days, }, [ANNUAL_PLAN]: { amount: 50, currencyCode: 'USD', interval: BillingInterval.Annual, }, } }); export default shopify; export const authenticate = shopify.authenticate;
Requesting billing with line items
Description
Call `billing.request` with the `v3_lineItemBilling` future flag enabled
/app/routes/**\/*.ts
import { LoaderFunctionArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { billing } = await authenticate.admin(request); await billing.require({ plans: [MONTHLY_PLAN], isTest: true, onFailure: async () => billing.request({ plan: MONTHLY_PLAN }), }); // App logic };
shopify.server.ts
import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server"; export const MONTHLY_PLAN = 'Monthly subscription'; export const ANNUAL_PLAN = 'Annual subscription'; const shopify = shopifyApp({ // ...etc billing: { [MONTHLY_PLAN]: { lineItems: [ { amount: 5, currencyCode: 'USD', interval: BillingInterval.Every30Days, }, { amount: 1, currencyCode: 'USD', interval: BillingInterval.Usage. terms: '1 dollar per 1000 emails', }, ], }, } future: {v3_lineItemBilling: true} }); export default shopify; export const authenticate = shopify.authenticate;
Anchor to example-check-what-billing-plans-a-merchant-is-subscribed-toCheck what billing plans a merchant is subscribed to
Use billing.check if you want to determine which plans are in use. Unlike require
, check
does notthrow an error if no active billing plans are present.
Check what billing plans a merchant is subscribed to
Examples
Check what billing plans a merchant is subscribed to
Description
Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.
/app/routes/**\/*.ts
import { LoaderFunctionArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { billing } = await authenticate.admin(request); const { hasActivePayment, appSubscriptions } = await billing.check({ plans: [MONTHLY_PLAN], isTest: false, }); console.log(hasActivePayment) console.log(appSubscriptions) };
shopify.server.ts
import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server"; export const MONTHLY_PLAN = 'Monthly subscription'; export const ANNUAL_PLAN = 'Annual subscription'; const shopify = shopifyApp({ // ...etc billing: { [MONTHLY_PLAN]: { amount: 5, currencyCode: 'USD', interval: BillingInterval.Every30Days, }, [ANNUAL_PLAN]: { amount: 50, currencyCode: 'USD', interval: BillingInterval.Annual, }, } }); export default shopify; export const authenticate = shopify.authenticate;
Anchor to example-requestrequest
Anchor to example-using-a-custom-return-urlUsing a custom return URL
Change where the merchant is returned to after approving the purchase using the option.
Using a custom return URL
Examples
Using a custom return URL
Description
Change where the merchant is returned to after approving the purchase using the `returnUrl` option.
/app/routes/**\/*.ts
import { LoaderFunctionArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { billing } = await authenticate.admin(request); await billing.require({ plans: [MONTHLY_PLAN], onFailure: async () => billing.request({ plan: MONTHLY_PLAN, isTest: true, returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page', }), }); // App logic };
shopify.server.ts
import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server"; export const MONTHLY_PLAN = 'Monthly subscription'; export const ANNUAL_PLAN = 'Annual subscription'; const shopify = shopifyApp({ // ...etc billing: { [MONTHLY_PLAN]: { amount: 5, currencyCode: 'USD', interval: BillingInterval.Every30Days, }, [ANNUAL_PLAN]: { amount: 50, currencyCode: 'USD', interval: BillingInterval.Annual, }, } }); export default shopify; export const authenticate = shopify.authenticate;
Anchor to example-cancelcancel
Anchor to example-cancelling-a-subscriptionCancelling a subscription
Use the billing.cancel
function to cancel an active subscription with the id returned from billing.require
.
Cancelling a subscription
Examples
Cancelling a subscription
Description
Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.
/app/routes/cancel-subscription.ts
import { LoaderFunctionArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const { billing } = await authenticate.admin(request); const billingCheck = await billing.require({ plans: [MONTHLY_PLAN], onFailure: async () => billing.request({ plan: MONTHLY_PLAN }), }); const subscription = billingCheck.appSubscriptions[0]; const cancelledSubscription = await billing.cancel({ subscriptionId: subscription.id, isTest: true, prorate: true, }); // App logic };
shopify.server.ts
import { shopifyApp, BillingInterval } from "@shopify/shopify-app-remix/server"; export const MONTHLY_PLAN = 'Monthly subscription'; export const ANNUAL_PLAN = 'Annual subscription'; const shopify = shopifyApp({ // ...etc billing: { [MONTHLY_PLAN]: { amount: 5, currencyCode: 'USD', interval: BillingInterval.Every30Days, }, [ANNUAL_PLAN]: { amount: 50, currencyCode: 'USD', interval: BillingInterval.Annual, }, } }); export default shopify; export const authenticate = shopify.authenticate;