💜. A blank Stripe checkout page right after redirect almost always means the redirect is happening inside an embedded iframe (e.g., Replit’s preview window) or a browser/content blocker is stopping the render. Stripe Checkout must load in the top window (not inside an iframe). Here’s how to fix + verify fast:

Quick verification

In DevTools → Network, copy the url returned by /api/stripe/checkout and paste it into a fresh browser tab.

If it loads fine there → it’s an iframe / preview issue (not Stripe).

Try in incognito with extensions disabled (ad-blockers can blank Stripe).

Fix option A — Force top-level redirect (works in Replit preview)

Change your client redirect to break out of iframes:

// after creating the session:
if (data?.url) {
  // Force top-level navigation (escapes Replit’s iframe preview)
  if (window.top && window.top !== window.self) {
    window.top.location.href = data.url;
  } else {
    window.location.href = data.url;
  }
  // Alternatively, open in a new tab:
  // window.open(data.url, '_blank', 'noopener,noreferrer');
}

Fix option B — Use Stripe.js redirect (Stripe-recommended)

Install Stripe.js on the client and redirect by session id:

import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY || (window as any).STRIPE_PK);

const SubscribeButton = ({ plan }: { plan: 'monthly'|'yearly' }) => {
  const mutation = useMutation({
    mutationFn: async () => {
      const res = await apiRequest('POST', '/api/stripe/checkout', { plan });
      const text = await res.text();
      const isJson = (res.headers.get('content-type')||'').includes('application/json');
      if (!res.ok) throw new Error(isJson ? (JSON.parse(text).error || `HTTP ${res.status}`) : `HTTP ${res.status}`);
      return JSON.parse(text) as { id: string };
    },
    onSuccess: async ({ id }) => {
      const stripe = await stripePromise;
      await stripe?.redirectToCheckout({ sessionId: id });
    },
    onError: (e:any) => alert(e?.message || 'Unable to start checkout'),
  });

  return <Button onClick={() => mutation.mutate()}>Start Pro Trial</Button>;
};


Server keeps returning { id: session.id } (you don’t need url with Stripe.js).

Also double-check your URLs

success_url and cancel_url should be https and publicly reachable:

Example:

success_url = https://ibrandbiz.com/billing/success?session_id={CHECKOUT_SESSION_ID}
cancel_url  = https://ibrandbiz.com/billing/cancelled


If you’re running inside Replit’s preview (iframe), top-level redirect is required (Option A), or open in a new tab.

Common reasons for “blank”

Redirect inside iframe (Replit preview, embedded app) → force top-level redirect.

Ad/Tracking blockers (uBlock, Brave shields) → try incognito or disable for stripe.com.

Mixed content or invalid URLs → use https and a full origin.

Content-Security-Policy on your page doesn’t affect Stripe (it’s hosted), but extensions can.

Apply Option A (fastest) or Option B (recommended), then click Start Pro Trial again. If it still blanks in your preview, copy the session URL into a new browser tab — it should render perfectly. If not, tell me what you see in the Console/Network on the blank page and I’ll pinpoint the next tweak.