Billing issue

Topic summary

A developer encounters a 401 Unauthorized error when implementing Shopify billing plan upgrades in a Remix app. The code extracts a plan selection from URL parameters (basic/business/pro) and uses billing.require() with an onFailure callback that triggers billing.request() to initiate the payment flow.

Key Issue:
The billing process returns a 401 error, though refreshing the page successfully redirects to the approval page.

Suggested Solutions:

  • Validate the plan query parameter is correctly formatted
  • Add logging inside billing.require() to debug unexpected results
  • Verify redirect URL formatting in billing.request()
  • Check proper handling of 302 redirect responses thrown by billing.request()

A community member references a related thread about billing redirect issues in Remix apps, suggesting the error handling for redirect responses may need adjustment. The original poster acknowledges the responses but hasn’t confirmed resolution.

Summarized with AI on October 24. AI used: claude-sonnet-4-5-20250929.
import { redirect } from "@remix-run/node";
import { authenticate, BASIC_PLAN, BUSINESS_PLAN, PRO_PLAN } from "../shopify.server";

export const loader = async ({ request }) => {
   try {
      const { billing, session } = await authenticate.admin(request);
      const { shop } = session;
      const myShop = shop.replace(".myshopify.com", "");

      // Extract the plan from the URL query parameters
      const url = new URL(request.url);
      const selectedPlan = url.searchParams.get("plan");

      const plansNames = {
         basic: BASIC_PLAN,
         business: BUSINESS_PLAN,
         pro: PRO_PLAN,
      };

      const plan = plansNames[selectedPlan];

      if (!plan) {
         console.log("Invalid plan selected");
         return new Response("Invalid plan selected", { status: 400 });
      }

      try {
         await billing.require({
            plans: [plan],
            onFailure: async () => {
               await billing.request({
                  plan: plan,
                  isTest: true,
                  returnUrl: `https://admin.shopify.com/store/${myShop}/apps/${process.env.APP_NAME}/app/pricing`
               });
            }
         });

         // If we reach here, it means the billing.require didn't throw and didn't return true
         return new Response("Unexpected billing result", { status: 500 });

      } catch (error) {
         if (error instanceof Response && error.status === 302) {
            // This is the redirect to the payment URL
            return error;
         }

         console.error("Error upgrading plan:", error);
         return new Response("Error processing upgrade request", { status: 500 });
      }
   } catch (authError) {
      console.error("Authentication failed:", authError);
      return new Response("Authentication failed", { status: 401 });
   }
};

This is my code why I am getting this error , When i try to change the plan

this is the error :

Error upgrading plan: Response {
15:41:43 β”‚         remix β”‚   [Symbol(realm)]: { settingsObject: {} },
15:41:43 β”‚         remix β”‚   [Symbol(state)]: {
15:41:43 β”‚         remix β”‚     aborted: false,
15:41:43 β”‚         remix β”‚     rangeRequested: false,
15:41:43 β”‚         remix β”‚     timingAllowPassed: false,
15:41:43 β”‚         remix β”‚     requestIncludesCredentials: false,
15:41:43 β”‚         remix β”‚     type: 'default',
15:41:43 β”‚         remix β”‚     status: 401,
15:41:43 β”‚         remix β”‚     timingInfo: null,
15:41:43 β”‚         remix β”‚     cacheState: '',
15:41:43 β”‚         remix β”‚     statusText: 'Unauthorized',
15:41:43 β”‚         remix β”‚     headersList: HeadersList {
15:41:43 β”‚         remix β”‚       cookies: null,
15:41:43 β”‚         remix β”‚       [Symbol(headers map)]: [Map],
15:41:43 β”‚         remix β”‚       [Symbol(headers map sorted)]: null
15:41:43 β”‚         remix β”‚     },
15:41:43 β”‚         remix β”‚     urlList: []
15:41:43 β”‚         remix β”‚   },
15:41:43 β”‚         remix β”‚   [Symbol(headers)]: HeadersList {
15:41:43 β”‚         remix β”‚     cookies: null,
15:41:43 β”‚         remix β”‚     [Symbol(headers map)]: Map(1) {
15:41:43 β”‚         remix β”‚       'x-shopify-api-request-failure-reauthorize-url' => [Object]
15:41:43 β”‚         remix β”‚     },
15:41:43 β”‚         remix β”‚     [Symbol(headers map sorted)]: null
15:41:43 β”‚         remix β”‚   }
15:41:43 β”‚         remix β”‚ }

the thing is that ,
when i refresh the page it took me to the approval page

The error may be due to these reasons:

  • Invalid selectedPlan: Ensure the plan query parameter in the URL is valid (e.g., β€œbasic”, β€œbusiness”, or β€œpro”). If it’s invalid, the code returns a 400 error.

  • Billing API: If billing.require doesn’t throw an error or return true, it reaches the β€œUnexpected billing result” message. Add logging inside billing.require to debug.

  • Redirect Handling: If billing.request causes a 302 redirect, the code correctly returns that response. Check if the redirect URL is correct.

Log values of selectedPlan and check the results from billing.require to pinpoint the issue.

1 Like

thank you for your response

I think billing.request() throws a redirect (302) response sometimes. I see your code is catching and returning those errors, but not re-throwing them.

Sounds like you’ve already found a solution, but if not this might be relevant: Billing Redirect Not Working On Remix - 401 Error using "billing.request()" - #2 by stefanb1234