Shopify One-Time Billing billing.request() Fails With “Error while billing the store”

Context-

I am building a public Shopify app using:

  • @shopify/shopify-app-react-router

  • React Router (not Remix)

  • Shopify Admin embedded app

  • Manual pricing (Billing API)

  • One-time charges (BillingInterval.OneTime)

  • App installed on a dev store

  • App opened from Shopify Admin (not submission preview)


-What I Have Already Confirmed

  • Pricing method is Manual pricing (not Managed pricing)

  • App was uninstalled and reinstalled after pricing change

  • Billing plans are defined only in code, not in Partner Dashboard

  • Billing keys are consistent between UI and shopify.server.js

  • App is accessed via:

    https://admin.shopify.com/store/<store>/apps/<app-name>
    
    
  • I am not using:

    • billing.check()

    • billing.require()

  • Billing is triggered only via billing.request()


-Billing Configuration (shopify.server.js)

billing: {
  SILVER_ONETIME: {
    amount: 50,
    currencyCode: "EUR",
    interval: BillingInterval.OneTime,
    name: "Silver Plan",
  },
  GOLD_ONETIME: {
    amount: 100,
    currencyCode: "EUR",
    interval: BillingInterval.OneTime,
    name: "Gold Plan",
  },
  PLATINUM_ONETIME: {
    amount: 300,
    currencyCode: "EUR",
    interval: BillingInterval.OneTime,
    name: "Platinum Plan",
  },
},


- Billing Trigger (UI → Action)

return billing.request({
  plan: plan.key,
  isTest: true,
  returnUrl: `${process.env.SHOPIFY_APP_URL}/app`,
});

  • Triggered via a native HTML <form method="post">

  • No client-side navigation issues

  • Action executes successfully until billing request


Actual Problem

When clicking “Select Plan”, the Shopify billing approval page does not open.

Instead, the server throws a generic error:

Error: Error while billing the store
    at Object.request (.../billing/request.ts)
    at Object.requestBilling (.../authenticate/admin/billing/request.ts)

There is:

  • No approval URL

  • No redirect

  • No detailed GraphQL error returned

  • No indication of what exactly is invalid


-Why This Is Confusing

  • All official prerequisites for Billing API appear to be satisfied

  • Manual pricing is enabled

  • One-time billing should be supported

  • The error message is too generic and provides no actionable feedback

  • The same error appears even after reinstalling the app

For the reference I have added the code below as well-
#Below is the code of app. billing.jsx

import { useLoaderData } from “react-router”;

import {

Page,

Card,

Button,

Text,

InlineStack,

BlockStack,

Layout,

Box,

} from “@shopify/polaris”;

import { authenticate } from “../shopify.server”;

const PLANS = {

silver: { name: “Silver”, key: “SILVER_ONETIME”, price: 50 },

gold: { name: “Gold”, key: “GOLD_ONETIME”, price: 100 },

platinum: { name: “Platinum”, key: “PLATINUM_ONETIME”, price: 300 },

};

export const loader = async ({ request }) => {

await authenticate.admin(request);

return { plans: PLANS };

};

export const action = async ({ request }) => {

const { billing } = await authenticate.admin(request);

const formData = await request.formData();

const planId = formData.get(“plan”);

const plan = PLANS[planId];

if (!plan) {

throw new Response("Invalid plan", { status: 400 });

}

return billing.request({

plan: plan.key,

isTest: true,

returnUrl: \`${process.env.SHOPIFY_APP_URL}/app\`,

});

};

export default function Billing() {

const { plans } = useLoaderData();

return (

*<Page* title="Choose your plan"*>*

  *<Layout>*

    *<Layout.Section>*

      *<InlineStack* gap="400" align="center"*>*

        {Object.entries(plans).map((\[id, plan\]) => (

          *<Card* key={id}*>*

            *<Box* padding="500"*>*

              *<BlockStack* gap="400" align="center"*>*

                *<Text* variant="headingLg"*>*{plan.name}*</Text>*

                *<Text* variant="heading2xl"*>*€{plan.price}*</Text>*

                *<*form method="post"*>*

                  *<*input type="hidden" name="plan" value={id} */>*

                  *<Button* submit variant="primary"*>*

                    Select Plan

                  *</Button>*

                *</*form*>*

              *</BlockStack>*

            *</Box>*

          *</Card>*

        ))}

      *</InlineStack>*

    *</Layout.Section>*

  *</Layout>*

*</Page>*

);

}

For the reference I have added the code below as well-
#Below is the code for shopify.server.js

const shopify = shopifyApp({

apiKey: process.env.SHOPIFY_API_KEY,

apiSecretKey: process.env.SHOPIFY_API_SECRET || “”,

apiVersion: ApiVersion.October25,

scopes: process.env.SCOPES?.split(“,”),

appUrl: process.env.SHOPIFY_APP_URL,

authPathPrefix: “/auth”,

sessionStorage: new PrismaSessionStorage(prisma),

distribution: AppDistribution.AppStore,

…(process.env.SHOP_CUSTOM_DOMAIN

? { customShopDomains: \[process.env.SHOP_CUSTOM_DOMAIN\] }

: {}),

billing: {

SILVER_ONETIME: {

  amount: 50,

  currencyCode: "EUR",

  interval: BillingInterval.OneTime,

  name: "Silver Plan",

},

GOLD_ONETIME: {

  amount: 100,

  currencyCode: "EUR",

  interval: BillingInterval.OneTime,

  name: "Gold Plan",

},

PLATINUM_ONETIME: {

  amount: 300,

  currencyCode: "EUR",

  interval: BillingInterval.OneTime,

  name: "Platinum Plan", 

},

},

});

export default shopify;

export const apiVersion = ApiVersion.October25;

export const authenticate = shopify.authenticate;

export const unauthenticated = shopify.unauthenticated;

export const login = shopify.login;

export const registerWebhooks = shopify.registerWebhooks;

export const sessionStorage = shopify.sessionStorage;

Is the app published on the app store?

If so, then it’s not possible to create a “test” charge in a published app, only unpublished apps.

its not published yet.

I would take a closer look at the FriendPayShoipfy/node_modules/lib/billing/request.ts module and add either a debugger statement or a few console.logs to determine what the cause of the problem is.

Most likely you have a try…catch statement that is failing to log the actual exception thrown.

inside the code there is not try.. catch

import { useLoaderData } from “react-router”;

import {

Page,

Card,

Button,

Text,

InlineStack,

BlockStack,

Layout,

Box,

} from “@shopify/polaris”;

import { authenticate } from “../shopify.server”;

const PLANS = {

silver: { name: “Silver”, key: “SILVER_ONETIME”, price: 50 },

gold: { name: “Gold”, key: “GOLD_ONETIME”, price: 100 },

platinum: { name: “Platinum”, key: “PLATINUM_ONETIME”, price: 300 },

};

export const loader = async ({ request }) => {

await authenticate.admin(request);

return { plans: PLANS };

};

export const action = async ({ request }) => {

const { billing } = await authenticate.admin(request);

const formData = await request.formData();

const planId = formData.get(“plan”);

const plan = PLANS[planId];

if (!plan) {

throw new Response("Invalid plan", { status: 400 });

}

return billing.request({

plan: plan.key,

isTest: true,

returnUrl: \`${process.env.SHOPIFY_APP_URL}/app\`,

});

};

export default function Billing() {

const { plans } = useLoaderData();

return (

*<Page* title="Choose your plan"*>*

  *<Layout>*

    *<Layout.Section>*

      *<InlineStack* gap="400" align="center"*>*

        {Object.entries(plans).map((\[id, plan\]) => (

          *<Card* key={id}*>*

            *<Box* padding="500"*>*

              *<BlockStack* gap="400" align="center"*>*

                *<Text* variant="headingLg"*>*{plan.name}*</Text>*

                *<Text* variant="heading2xl"*>*€{plan.price}*</Text>*



              

                *<*form method="post"*>*

                  *<*input type="hidden" name="plan" value={id} */>*

                  *<Button* submit variant="primary"*>*

                    Select Plan

                  *</Button>*

                *</*form*>*

              *</BlockStack>*

            *</Box>*

          *</Card>*

        ))}

      *</InlineStack>*

    *</Layout.Section>*

  *</Layout>*

*</Page>*

);

}

errorData: [
{
field: null,
message: ‘Managed Pricing Apps cannot use the Billing API (to create charges).’
}
]

Ah the errorData has the answer:

‘Managed Pricing Apps cannot use the Billing API (to create charges).’

You cannot have both managed pricing and manual initiation of subscriptions. So you’ll have to pick either continuing to allow Shopify to manage billing, or you can disable that and use the API directly.


We have chosen manual pricing. Managed pricing is disabled, and we are handling subscription creation and billing directly via the Billing API.the issue is still occurring

Is it possible your development environment is using a Shopify App Public Key from another app that does have managed pricing enabled?

You mentioned this is an unpublished app, have you tried creating a new one just as a test to make sure you didn’t have managed pricing enabled at some point? That would prove it’s a caching issue on Shopify’s end.