Redirect url going to login page of my app

I’ve completed code.
but from call back file redirect url go to the admin

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

const PLAN_CONFIGS = {

basic: { price: 9.99 },

pro: { price: 29.99 },

enterprise: { price: 59.99 },

};

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

// ─────────────────────────────────────

// SHOPIFY AUTHENTICATION

// ─────────────────────────────────────

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

const url = new URL(request.url);

const plan = url.searchParams.get(“plan”);

const shop = url.searchParams.get(“shop”);

const chargeId = url.searchParams.get(“charge_id”);

console.log(“:package: Billing callback:”, {

plan,

shop,

chargeId,

});

// ─────────────────────────────────────

// VALIDATION

// ─────────────────────────────────────

if (!plan || !shop || !PLAN_CONFIGS[plan]) {

return redirectToEmbeddedApp(redirect, shop, false);

}

try {

// ─────────────────────────────────────

// SAVE SUBSCRIPTION

// ─────────────────────────────────────

const laravelRes = await fetch(

https://shopapps.perceptioncare.in/api/subscriptions”,

  {

method: “POST”,

headers: {

“Content-Type”: “application/json”,

    },

body: JSON.stringify({

shop_domain: shop,

shopify_admin_id: shop,

plan,

price: PLAN_CONFIGS[plan].price,

status: “active”,

charge_id: chargeId ?? null,

    }),

  }

);

const laravelData = await laravelRes.json();

console.log(“:satellite_antenna: Laravel response:”, laravelData);

return redirectToEmbeddedApp(

redirect,

shop,

laravelData.success

);

} catch (error) {

console.error(“:cross_mark: Callback error:”, error);

return redirectToEmbeddedApp(

redirect,

shop,

false

);

}

};

// ─────────────────────────────────────

// REDIRECT HELPER

// ─────────────────────────────────────

function redirectToEmbeddedApp(

redirect,

shop,

success = true

) {

const appHandle = “thinkcontent”;

const storeHandle = shop.replace(

“.myshopify.com”,

“”

);

const redirectUrl =

`https://admin.shopify.com/store/${storeHandle}\` +

`/apps/${appHandle}/app/plans?` +

`${success ? “success=true” : “error=true”}`;

console.log(“:right_arrow: Redirect URL:”, redirectUrl);

return redirect(redirectUrl, {

target: “_top”,

});

}

export default function BillingCallback() {

return null;

}

my flow is

my flow is plan page subscribe → then run api for billing(appSubscriptionCreate) → added return url for billing callback(const returnUrl = `${process.env.SHOPIFY_APP_URL}/app/billing/callback?plan=${plan}&shop=${session.shop}`; ) ->from callback added redirect url( const appHandle = “thinkcontent”; const redirectUrl = `https://admin.shopify.com/admin/apps/${appHandle}\` + `/app/plans?${success ? “success=true” : “error=true”}`; ) problem is after approve it’s redirect to login from my shopify app url login page

Hey @Pallavi_Prajapati1 - thanks for reaching out.

From the code you shared, I think the main issue is that the billing returnUrl is taking the merchant back to your app without the embedded app context, specifically the host value. Since your callback immediately runs authenticate.admin(request), if that request doesn’t have the right embedded context/session available, it can send the merchant back through your app login flow instead of continuing to /app/plans.

I’d try two changes here:

  1. Preserve host when building the billing returnUrl:
const currentUrl = new URL(request.url);
const host = currentUrl.searchParams.get("host");

const returnUrl = new URL("/app/billing/callback", process.env.SHOPIFY_APP_URL);
returnUrl.searchParams.set("plan", plan);
returnUrl.searchParams.set("shop", session.shop);

if (host) {
  returnUrl.searchParams.set("host", host);
}
  1. In the callback, use the Remix redirect helper from authenticate.admin(request) and redirect back to an app-relative route instead of manually building the full admin.shopify.com URL:
const { session, redirect } = await authenticate.admin(request);

// save/verify billing for session.shop here

return redirect(`/app/plans?${success ? "success=true" : "error=true"}`);

I’d also recommend using session.shop after authentication rather than trusting the shop query param when saving the subscription. If you need to confirm the billing state, you can also query the shop’s active app subscriptions after the approval rather than relying only on a callback param.

Hope this helps! Let me know if I can help out further.