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: <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: <Config>) => Promise<never>required
Requests payment for the plan.
- Anchor to cancelcancel(options: ) => Promise<AppSubscription>required
Cancels an ongoing subscription, given its ID.
BillingContext
- require
Checks if the shop has an active payment for any plan defined in the `billing` config option.
(options: RequireBillingOptions<Config>) => Promise<BillingCheckResponseObject>
- request
Requests payment for the plan.
(options: RequestBillingOptions<Config>) => Promise<never>
- cancel
Cancels an ongoing subscription, given its ID.
(options: CancelBillingOptions) => Promise<AppSubscription>
export interface BillingContext<Config extends AppConfigArg> {
/**
* Checks if the shop has an active payment for any plan defined in the `billing` config option.
*
* @returns A promise that resolves to an object containing the active purchases for the shop.
*
* @example
* <caption>Requesting billing right away.</caption>
* <description>Call `billing.request` in the `onFailure` callback to immediately request payment.</description>
* ```ts
* // /app/routes/**\/*.ts
* import { LoaderArgs } from "@remix-run/node";
* import { authenticate, MONTHLY_PLAN } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { billing } = await authenticate.admin(request);
* await billing.require({
* plans: [MONTHLY_PLAN],
* isTest: true,
* onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),
* });
*
* // App logic
* };
* ```
* ```ts
* // 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;
* ```
*
* @example
* <caption>Using a plan selection page.</caption>
* <description>Redirect to a different page in the `onFailure` callback, where the merchant can select a billing plan.</description>
* ```ts
* // /app/routes/**\/*.ts
* import { LoaderArgs, redirect } from "@remix-run/node";
* import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* 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
* };
* ```
* ```ts
* // 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;
* ```
*/
require: (
options: RequireBillingOptions<Config>,
) => Promise<BillingCheckResponseObject>;
/**
* Requests payment for the plan.
*
* @returns Redirects to the confirmation URL for the payment.
*
* @example
* <caption>Using a custom return URL.</caption>
* <description>Change where the merchant is returned to after approving the purchase using the `returnUrl` option.</description>
* ```ts
* // /app/routes/**\/*.ts
* import { LoaderArgs } from "@remix-run/node";
* import { authenticate, MONTHLY_PLAN } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { billing } = await authenticate.admin(request);
* await billing.require({
* plans: [MONTHLY_PLAN],
* onFailure: async () => billing.request({
* plan: MONTHLY_PLAN,
* isTest: true,
* returnUrl: '/billing-complete',
* }),
* });
*
* // App logic
* };
* ```
* ```ts
* // 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;
* ```
*/
request: (options: RequestBillingOptions<Config>) => Promise<never>;
/**
* Cancels an ongoing subscription, given its ID.
*
* @returns The cancelled subscription.
*
* @example
* <caption>Cancelling a subscription.</caption>
* <description>Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.</description>
* ```ts
* // /app/routes/cancel-subscription.ts
* import { LoaderArgs } from "@remix-run/node";
* import { authenticate, MONTHLY_PLAN } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* 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
* };
* ```
* ```ts
* // 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;
* ```
*/
cancel: (options: CancelBillingOptions) => Promise<AppSubscription>;
}
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
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>;
}
RequestBillingOptions
- plan
The plan to request. Must be one of the values defined in the `billing` config option.
keyof Config["billing"]
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'];
}
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;
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 request payment.
Anchor to example-using-a-plan-selection-pageUsing a plan selection page
Redirect to a different page in the callback, where the merchant can select a billing plan.
Requesting billing right away
examples
Requesting billing right away
description
Call `billing.request` in the `onFailure` callback to immediately request payment.
/app/routes/**\/*.ts
import { LoaderArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { 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;
Using a plan selection page
description
Redirect to a different page in the `onFailure` callback, where the merchant can select a billing plan.
/app/routes/**\/*.ts
import { LoaderArgs, redirect } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { 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;
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 { LoaderArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { const { billing } = await authenticate.admin(request); await billing.require({ plans: [MONTHLY_PLAN], onFailure: async () => billing.request({ plan: MONTHLY_PLAN, isTest: true, returnUrl: '/billing-complete', }), }); // 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 { LoaderArgs } from "@remix-run/node"; import { authenticate, MONTHLY_PLAN } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { 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;