In node express template, how to redirect the user from the billing api confirmation url back to the return url

Hello, I am building a Shopify app using the node express template with Javascript. I need to use the billing api to create a one time purchase link for the user. It’s called the confirmation url in Shopify term I guess.

The GraphQL mutation is this

mutation AppPurchaseOneTimeCreate($name: String!, $price: MoneyInput!, $returnUrl: URL!, $test: Boolean) {
  appPurchaseOneTimeCreate(name: $name, returnUrl: $returnUrl, price: $price, test: $test) {
    userErrors {
      field
      message
    }
    appPurchaseOneTime {
      createdAt
      id
    }
    confirmationUrl
  }
}

And the input looks like this:

variables: {
  name: "One Time Purchase",
  // returnUrl: `${shopifyAppHost}/api/purchase/callback`,
  // returnUrl: `${shopifyAppHost}/api/purchase/callback?shop=${shopDomain}`,
  // returnUrl: shopifyAppHost,
  // returnUrl: `https://${shopDomain}/admin/apps/${process.env.SHOPIFY_API_KEY}/api/purchase/callback`,
  // returnUrl: `https://${shopDomain}/admin/apps/${process.env.SHOPIFY_API_KEY}`,
  returnUrl: `${shopifyAppHost}/api/purchase/callback?shop=${shopDomain}`,
  test: true,
  price: {
    amount: amount,
    currencyCode: "CAD",
  },
},

I have tried different return urls but none of them take the user back. For this version, I got the error:

[shopify-api/ERROR] Missing Authorization header, was the request made with authenticatedFetch? | {isOnline: false}

On the frontend, I use this to redirect the user:

const createBillingMutation = useMutation({
  mutationFn: async () => {
    const response = await fetch("/api/billing", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ amount }),
    });

    console.log("********** MakePaymentButton createBillingMutation response:", response);

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error || "Failed to create billing request");
    }

    return await response.json();
  },
  onSuccess: (data) => {
    // TODO call backend endpoint to add credits to user
    alert("Billing success: " + JSON.stringify(data, null, 2));
    alert(`Billing transaction id ${data.appPurchaseId}`);
    console.log("Billing success:", data);
    top.location.href = data.confirmationUrl;
  },
  onError: (error) => {
    console.error("Billing error:", error);
  },
});

I was hesitant to use top.location.href to redirect the user because that doesn’t seem to retain any authentication or session info.

However, I was unable to figure how to properly, with authentication, redirect the user to the confirmation url and take the user back to the return url.

Can someone teach me how to implement this? I am not able to find much explanation on the internet.

Thank you!

If you are using Shopify App Bridge, you can use Shopify App Bridge Navigation API:

open(data.confirmationUrl, '_top')

Doc: Navigating to external URL’s

Your request does not pass Shopify’s validation. The Shopify app validates whether the request originates from a valid session within your store app. For the request to be valid, your request headers or query parameters must include session tokens, either in the headers or the request URL.
so the result prompts “Missing Authorization header, was the request made with authenticatedFetch? | {isOnline: false}”

by the way you should use authenticatedFetch instead of fetch. this will automatically include session tokens in your header.