# Billing Contains function used to bill merchants for your app. This object is returned on authenticated Admin requests. ## billing Provides utilities that apps can use to request billing for the app using the Admin API. ### BillingContext ### require Checks if the shop has an active payment for any plan defined in the `billing` config option. ### check Checks if the shop has an active payment for any plan defined in the `billing` config option. ### request Requests payment for the plan. ### cancel 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. ### onFailure How to handle the request if the shop doesn't have an active payment for any plan. ### isTest Whether to consider test purchases. ### BillingCheckResponseObject ### hasActivePayment Whether the user has an active payment method. ### oneTimePurchases The one-time purchases the shop has. ### appSubscriptions The active subscriptions the shop has. ### OneTimePurchase ### id The ID of the one-time purchase. ### name The name of the purchased plan. ### test Whether this is a test purchase. ### status The status of the one-time purchase. ### AppSubscription ### id The ID of the app subscription. ### name The name of the purchased plan. ### test Whether this is a test subscription. ### lineItems ### ActiveSubscriptionLineItem ### id ### plan ### AppPlan ### pricingDetails ### RecurringAppPlan ### interval ### price ### discount ### BillingInterval ### OneTime ### Every30Days ### Annual ### Usage ### Money ### amount ### currencyCode ### AppPlanDiscount ### durationLimitInIntervals ### remainingDurationInIntervals ### priceAfterDiscount ### value ### AppPlanDiscountAmount BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage ### BillingConfigSubscriptionPlanDiscountAmount ### amount The amount to discount. Cannot be set if `percentage` is set. ### percentage The percentage to discount. Cannot be set if `amount` is set. ### BillingConfigSubscriptionPlanDiscountPercentage ### amount The amount to discount. Cannot be set if `percentage` is set. ### percentage The percentage to discount. Cannot be set if `amount` is set. ### UsageAppPlan ### balanceUsed ### cappedAmount ### terms ### CheckBillingOptions ### plans The plans to check for. Must be one of the values defined in the `billing` config option. ### isTest Whether to consider test purchases. ### RequestBillingOptions ### plan The plan to request. Must be one of the values defined in the `billing` config option. ### isTest Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged. ### returnUrl The URL to return to after the merchant approves the payment. ### CancelBillingOptions ### subscriptionId The ID of the subscription to cancel. ### prorate Whether to prorate the cancellation. ### isTest ## Related - [Admin context](/docs/api/shopify-app-remix/authenticate/admin) ## Examples Contains function used to bill merchants for your app. This object is returned on authenticated Admin requests. ### require ### Requesting billing right away ```typescript 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 }; ``` ```typescript 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 ```typescript 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 }; ``` ```typescript 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 ```typescript 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 }; ``` ```typescript 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; ``` ### check ### Check what billing plans a merchant is subscribed to ```typescript 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) }; ``` ```typescript 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 ### Using a custom return URL ```typescript 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 }; ``` ```typescript 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 ### Cancelling a subscription ```typescript 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 }; ``` ```typescript 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; ```