How can I redirect users to the checkout URL?

Topic summary

A developer is attempting to redirect users to a Shopify checkout URL generated via the Billing API in a Remix app. The standard redirect(confirmationUrl) approach fails because Shopify admin pages include X-Frame-Options headers that prevent iframe embedding.

Attempted Solutions:

  • Using Shopify App Bridge’s Redirect action resulted in error: “vite_ssr_import_6.default is not a function”
  • Various redirect imports from react-router, @shopify/app-bridge/actions, and @remix-run/node were unsuccessful
  • window.location = confirmationUrl failed with “window is not defined” error

Proposed Solution:
A respondent suggests the App Bridge createApp + Redirect approach is correct, but requires configuration changes:

  • Replace shopOrigin parameter with host parameter
  • Extract the host query parameter from the URL using URLSearchParams in a useEffect hook
  • Store it in sessionStorage and pass to createApp

The discussion remains open as the original poster hasn’t confirmed whether this solution resolved the issue.

Summarized with AI on October 25. AI used: claude-sonnet-4-5-20250929.

Hi there,

I’m trying to figure out how I can redirect a user from my app to the checkout URL that is generated from this Shopify Billing API code in Remix:

const { session, admin } = await authenticate.admin(request);
  console.log("Session:", session);
if (!session) {
  console.log("Session not found, initiating authentication.");
}
if (!admin) {
  console.log("Admin not found, initiating authentication.");
}
  const response = await admin.graphql(
    `#graphql
    mutation AppPurchaseOneTimeCreate($name: String!, $price: MoneyInput!, $returnUrl: URL!) {
      appPurchaseOneTimeCreate(name: $name, returnUrl: $returnUrl, price: $price) {
        userErrors {
          field
          message
        }
        appPurchaseOneTime {
          createdAt
          id
        }
        confirmationUrl
      }
    }`,
    {
      variables: {
        "name": "1000 imported orders.",
        "returnUrl": "http://super-duper.shopifyapps.com/",
        "price": {
          "amount": 10.0,
          "currencyCode": "GBP",
        },
      },
    }
  );
  const data = await response.json();
  console.log("GraphQL Response:", JSON.stringify(data, null, 2));
  const confirmationUrl = data.data.appPurchaseOneTimeCreate.confirmationUrl;
  console.log(confirmationUrl);
  
  return redirect(confirmationUrl);

The checkout URL is retrieved from the GraphQL response, called “confirmationUrl”, but when I try to redirect the user to that URL by using “return redirect (confirmationUrl);”, I get this error: “Refused to display ‘https://admin.shopify.com/’ in a frame because it set ‘X-Frame-Options’ to ‘deny’.”.

My understanding is that Shopify adds the “X-Frame-Options: DENY” header to their admin pages, which prevents those pages from being embedded in an iframe.

So I tried to use Shopify App Bridge’s “Redirect” action to break out of the iframe and navigate in the top-level browser window by using this code:

import { Redirect } from '@shopify/app-bridge/actions';
import createApp from '@shopify/app-bridge';

const app = createApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  shopOrigin: 'your-shop.myshopify.com',
});

const redirect = Redirect.create(app);
redirect.dispatch(Redirect.Action.REMOTE, confirmationUrl);

but I got the error “Error: vite_ssr_import_6.default is not a function”.

Importing “redirect”, such as “import { redirect } from ‘react-router’; import { redirect } from “@shopify/app-bridge/actions”; import { redirect } from “@remix-run/node”;”, hasn’t worked in redirecting the user either.

I’ve also tried using “window.location = confirmationUrl” but I’m not sure how to resolve the error I get from it which is “window is not defined”.

I think the createApp function with Redirect is the correct approach in this case but you have to change the settings object for the app.
Instead of shopOrigin you have to use host. This is a query param that is in your URL on loading your app.
You can catch it by using this snippet of code in your initial load component:

useEffect(() => {
    const urlHost = new URLSearchParams(window.location.search).get("host");

    if (urlHost) {
      sessionStorage.setItem("shopifyHost", urlHost);
    }
  }, []);

Then you just do:

const host = sessionStorage?.getItem("shopifyHost");
const app = createApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  host,
});