Adminobject
Contains functions for authenticating and interacting with the Admin API.
This function can handle requests for apps embedded in the Admin, Admin extensions, or non-embedded apps.
Authenticates requests coming from the Shopify admin.
The shape of the returned object changes depending on the config.
Anchor to authenticate.admin-parametersParameters
- Anchor to requestrequestRequestrequired
AuthenticateAdmin
- request
Request
Promise<AdminContext<Config, Resources>>
export type AuthenticateAdmin<
Config extends AppConfigArg,
Resources extends ShopifyRestResources = ShopifyRestResources,
> = (request: Request) => Promise<AdminContext<Config, Resources>>;
AdminContext
Config['isEmbeddedApp'] extends false
? NonEmbeddedAdminContext<Config, Resources>
: EmbeddedAdminContext<Config, Resources>
NonEmbeddedAdminContext
- session
The session for the user who made the request. This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice. Use this to get shop or user-specific data.
Session
- admin
Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.
AdminApiContext<Resources>
- billing
Billing methods for this store, based on the plans defined in the `billing` config option.
BillingContext<Config>
- cors
A function that ensures the CORS headers are set correctly for the response.
EnsureCORSFunction
export interface NonEmbeddedAdminContext<
Config extends AppConfigArg,
Resources extends ShopifyRestResources = ShopifyRestResources,
> extends AdminContextInternal<Config, Resources> {}
AdminApiContext
- rest
Methods for interacting with the Shopify Admin REST API There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.
RestClientWithResources<Resources>
- graphql
Methods for interacting with the Shopify Admin GraphQL API
GraphQLClient
export interface AdminApiContext<
Resources extends ShopifyRestResources = ShopifyRestResources,
> {
/**
* Methods for interacting with the Shopify Admin REST API
*
* There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.
*
* {@link https://shopify.dev/docs/api/admin-rest}
*
* @example
* <caption>Using REST resources.</caption>
* <description>Getting the number of orders in a store using REST resources.</description>
*
* ```ts
* // /app/shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-remix/server";
* import { restResources } from "@shopify/shopify-api/rest/admin/2023-07";
*
* const shopify = shopifyApp({
* restResources,
* // ...etc
* });
* export default shopify;
* export const authenticate = shopify.authenticate;
* ```
*
* ```ts
* // /app/routes/**\/*.ts
* import { LoaderArgs, json } from "@remix-run/node";
* import { authenticate } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { admin, session } = await authenticate.admin(request);
* return json(admin.rest.resources.Order.count({ session }));
* };
* ```
*
* @example
* <caption>Performing a GET request to the REST API.</caption>
* <description>Use `admin.rest.<method>` to make custom requests to the API.</description>
*
* ```ts
* // /app/shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-remix/server";
* import { restResources } from "@shopify/shopify-api/rest/admin/2023-04";
*
* const shopify = shopifyApp({
* restResources,
* // ...etc
* });
* export default shopify;
* export const authenticate = shopify.authenticate;
* ```
*
* ```ts
* // /app/routes/**\/*.ts
* import { LoaderArgs, json } from "@remix-run/node";
* import { authenticate } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { admin, session } = await authenticate.admin(request);
* const response = await admin.rest.get({ path: "/customers/count.json" });
* const customers = await response.json();
* return json({ customers });
* };
* ```
*/
rest: RestClientWithResources<Resources>;
/**
* Methods for interacting with the Shopify Admin GraphQL API
*
* {@link https://shopify.dev/docs/api/admin-graphql}
* {@link https://github.com/Shopify/shopify-api-js/blob/main/docs/reference/clients/Graphql.md}
*
* @example
* <caption>Querying the GraphQL API.</caption>
* <description>Use `admin.graphql` to make query / mutation requests.</description>
* ```ts
* import { ActionArgs } from "@remix-run/node";
* import { authenticate } from "../shopify.server";
*
* export async function action({ request }: ActionArgs) {
* const { admin } = await authenticate.admin(request);
*
* const response = await admin.graphql(
* `#graphql
* mutation populateProduct($input: ProductInput!) {
* productCreate(input: $input) {
* product {
* id
* }
* }
* }`,
* { variables: { input: { title: "Product Name" } } }
* );
*
* const productData = await response.json();
* return json({ data: productData.data });
* }
* ```
*/
graphql: GraphQLClient;
}
RestClientWithResources
RemixRestClient & {resources: Resources}
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;
}
EmbeddedAdminContext
- sessionToken
The decoded and validated session token for the request. Returned only if `isEmbeddedApp` is `true`.
JwtPayload
- redirect
A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded apps. Returned only if `isEmbeddedApp` is `true`.
RedirectFunction
- session
The session for the user who made the request. This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice. Use this to get shop or user-specific data.
Session
- admin
Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.
AdminApiContext<Resources>
- billing
Billing methods for this store, based on the plans defined in the `billing` config option.
BillingContext<Config>
- cors
A function that ensures the CORS headers are set correctly for the response.
EnsureCORSFunction
export interface EmbeddedAdminContext<
Config extends AppConfigArg,
Resources extends ShopifyRestResources = ShopifyRestResources,
> extends AdminContextInternal<Config, Resources> {
/**
* The decoded and validated session token for the request.
*
* Returned only if `isEmbeddedApp` is `true`.
*
* {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}
*
* @example
* <caption>Using the decoded session token.</caption>
* <description>Get user-specific data using the `sessionToken` object.</description>
* ```ts
* // shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-remix/server";
*
* const shopify = shopifyApp({
* // ...etc
* useOnlineTokens: true,
* });
* export default shopify;
* export const authenticate = shopify.authenticate;
* ```
* ```ts
* // /app/routes/**\/*.ts
* import { LoaderArgs, json } from "@remix-run/node";
* import { authenticate } from "../shopify.server";
* import { getMyAppData } from "~/db/model.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { sessionToken } = await authenticate.public.checkout(
* request
* );
* return json(await getMyAppData({user: sessionToken.sub}));
* };
* ```
*/
sessionToken: JwtPayload;
/**
* A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded
* apps.
*
* Returned only if `isEmbeddedApp` is `true`.
*
* @example
* <caption>Redirecting to an app route.</caption>
* <description>Use the `redirect` helper to safely redirect between pages.</description>
* ```ts
* // /app/routes/admin/my-route.ts
* import { LoaderArgs, json } from "@remix-run/node";
* import { authenticate } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { session, redirect } = await authenticate.admin(request);
* return redirect("/");
* };
* ```
*
* @example
* <caption>Redirecting outside of Shopify admin.</caption>
* <description>Pass in a `target` option of `_top` or `_parent` to go to an external URL.</description>
* ```ts
* // /app/routes/admin/my-route.ts
* import { LoaderArgs, json } from "@remix-run/node";
* import { authenticate } from "../shopify.server";
*
* export const loader = async ({ request }: LoaderArgs) => {
* const { session, redirect } = await authenticate.admin(request);
* return redirect("/", { target: '_parent' });
* };
* ```
*/
redirect: RedirectFunction;
}
RedirectFunction
- url
string
- init
RedirectInit
TypedResponse<never>
export type RedirectFunction = (
url: string,
init?: RedirectInit,
) => TypedResponse<never>;
RedirectInit
number | (ResponseInit & {target?: RedirectTarget})
RedirectTarget
'_self' | '_parent' | '_top'
Anchor to examplesExamples
Anchor to example-sessiontokensessionToken
Anchor to example-using-the-decoded-session-tokenUsing the decoded session token
Get user-specific data using the object.
Using the decoded session token
examples
Using the decoded session token
description
Get user-specific data using the `sessionToken` object.
shopify.server.ts
import { shopifyApp } from "@shopify/shopify-app-remix/server"; const shopify = shopifyApp({ // ...etc useOnlineTokens: true, }); export default shopify; export const authenticate = shopify.authenticate;
/app/routes/**\/*.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; import { getMyAppData } from "~/db/model.server"; export const loader = async ({ request }: LoaderArgs) => { const { sessionToken } = await authenticate.public.checkout( request ); return json(await getMyAppData({user: sessionToken.sub})); };
Anchor to example-redirectredirect
Anchor to example-redirecting-to-an-app-routeRedirecting to an app route
Use the redirect
helper to safely redirect between pages.
Anchor to example-redirecting-outside-of-shopify-adminRedirecting outside of Shopify admin
Pass in a target
option of _top
or _parent
to go to an external URL.
Redirecting to an app route
/app/routes/admin/my-route.ts
examples
Redirecting to an app route
description
Use the `redirect` helper to safely redirect between pages.
/app/routes/admin/my-route.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { const { session, redirect } = await authenticate.admin(request); return redirect("/"); };
Redirecting outside of Shopify admin
description
Pass in a `target` option of `_top` or `_parent` to go to an external URL.
/app/routes/admin/my-route.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { const { session, redirect } = await authenticate.admin(request); return redirect("/", { target: '_parent' }); };
Anchor to example-sessionsession
Anchor to example-using-offline-sessionsUsing offline sessions
Get your app's shop-specific data using an offline session.
Anchor to example-using-online-sessionsUsing online sessions
Get your app's user-specific data using an online session.
Using offline sessions
examples
Using offline sessions
description
Get your app's shop-specific data using an offline session.
shopify.server.ts
import { shopifyApp } from "@shopify/shopify-app-remix/server"; const shopify = shopifyApp({ // ...etc }); export default shopify; export const authenticate = shopify.authenticate;
/app/routes/**\/*.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; import { getMyAppData } from "~/db/model.server"; export const loader = async ({ request }: LoaderArgs) => { const { session } = await authenticate.admin(request); return json(await getMyAppData({shop: session.shop)); };
Using online sessions
description
Get your app's user-specific data using an online session.
shopify.server.ts
import { shopifyApp } from "@shopify/shopify-app-remix/server"; const shopify = shopifyApp({ // ...etc useOnlineTokens: true, }); export default shopify; export const authenticate = shopify.authenticate;
/app/routes/**\/*.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; import { getMyAppData } from "~/db/model.server"; export const loader = async ({ request }: LoaderArgs) => { const { session } = await authenticate.admin(request); return json(await getMyAppData({user: session.onlineAccessInfo!.id})); };
Anchor to example-setting-cors-headers-for-a-admin-requestSetting CORS headers for a admin request
Use the cors
helper to ensure your app can respond to requests from admin extensions.
Setting CORS headers for a admin request
/app/routes/admin/my-route.ts
examples
Setting CORS headers for a admin request
description
Use the `cors` helper to ensure your app can respond to requests from admin extensions.
/app/routes/admin/my-route.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; import { getMyAppData } from "~/db/model.server"; export const loader = async ({ request }: LoaderArgs) => { const { session, cors } = await authenticate.admin(request); return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id}))); };
Anchor to example-using-rest-resourcesUsing REST resources
Getting the number of orders in a store using REST resources.
Anchor to example-performing-a-get-request-to-the-rest-apiPerforming a GET request to the REST API
Use admin.rest.<method>
to make custom requests to the API.
Using REST resources
examples
Using REST resources
description
Getting the number of orders in a store using REST resources.
/app/shopify.server.ts
import { shopifyApp } from "@shopify/shopify-app-remix/server"; import { restResources } from "@shopify/shopify-api/rest/admin/2023-07"; const shopify = shopifyApp({ restResources, // ...etc }); export default shopify; export const authenticate = shopify.authenticate;
/app/routes/**\/*.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { const { admin, session } = await authenticate.admin(request); return json(admin.rest.resources.Order.count({ session })); };
Performing a GET request to the REST API
description
Use `admin.rest.<method>` to make custom requests to the API.
/app/shopify.server.ts
import { shopifyApp } from "@shopify/shopify-app-remix/server"; import { restResources } from "@shopify/shopify-api/rest/admin/2023-04"; const shopify = shopifyApp({ restResources, // ...etc }); export default shopify; export const authenticate = shopify.authenticate;
/app/routes/**\/*.ts
import { LoaderArgs, json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const loader = async ({ request }: LoaderArgs) => { const { admin, session } = await authenticate.admin(request); const response = await admin.rest.get({ path: "/customers/count.json" }); const customers = await response.json(); return json({ customers }); };
Anchor to example-graphqlgraphql
Anchor to example-querying-the-graphql-apiQuerying the GraphQL API
Use admin.graphql
to make query / mutation requests.
Querying the GraphQL API
Example
examples
Querying the GraphQL API
description
Use `admin.graphql` to make query / mutation requests.
Example
import { ActionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export async function action({ request }: ActionArgs) { const { admin } = await authenticate.admin(request); const response = await admin.graphql( `#graphql mutation populateProduct($input: ProductInput!) { productCreate(input: $input) { product { id } } }`, { variables: { input: { title: "Product Name" } } } ); const productData = await response.json(); return json({ data: productData.data }); }
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;