All Docs
FeaturesPosibl Life & Gym AppUpdated March 14, 2026

Fixing Stripe Checkout Redirects: Setting NEXT_PUBLIC_APP_URL

Fixing Stripe Checkout Redirects: Setting NEXT_PUBLIC_APP_URL

Affected versions: all versions prior to v1.0.21
File: src/lib/routers/billing.ts

What Happened

The billing router builds Stripe checkout session redirect URLs by interpolating process.env.NEXT_PUBLIC_APP_URL:

// billing.ts line 47
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard/billing?success=true`,

// billing.ts line 73
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard/billing?cancelled=true`,

Because NEXT_PUBLIC_APP_URL was never defined in the project's environment configuration, both values resolved to undefined at runtime. Stripe rejects checkout session requests that contain invalid redirect URLs, so any attempt to initiate a billing flow would fail silently or throw a Stripe API error.

How to Fix It

Step 1 — Set the environment variable

Add NEXT_PUBLIC_APP_URL to every environment where the application runs.

Local development (.env.local):

NEXT_PUBLIC_APP_URL=http://localhost:3000

Vercel (Production & Preview):

  1. Open your project in the Vercel dashboard.
  2. Go to Settings → Environment Variables.
  3. Add a new variable:
    • Name: NEXT_PUBLIC_APP_URL
    • Value: https://your-domain.com (use your actual production domain)
    • Environments: ✅ Production ✅ Preview ✅ Development
  4. Redeploy the application for the change to take effect.

Step 2 (Optional) — Apply the resilient fallback

For teams using Vercel preview deployments, the URL changes with every branch deploy. Instead of manually updating NEXT_PUBLIC_APP_URL for each preview, update billing.ts to fall back to Vercel's automatically injected VERCEL_URL:

const base =
  process.env.NEXT_PUBLIC_APP_URL ??
  (process.env.VERCEL_URL
    ? `https://${process.env.VERCEL_URL}`
    : 'http://localhost:3000');

// Then use `base` in your session config:
const session = await stripe.checkout.sessions.create({
  // ...
  success_url: `${base}/dashboard/billing?success=true`,
  cancel_url:  `${base}/dashboard/billing?cancelled=true`,
});

Important: Vercel sets VERCEL_URL to the hostname only (e.g. my-app-abc123.vercel.app), without a protocol. Always prepend https:// as shown above.

Priority Resolution Order

PriorityVariableExample value
1 (highest)NEXT_PUBLIC_APP_URLhttps://app.mygym.com
2VERCEL_URLhttps://my-app-abc123.vercel.app
3 (fallback)hardcoded localhosthttp://localhost:3000

Why NEXT_PUBLIC_ Prefix Matters

Next.js only exposes environment variables to the browser bundle when they are prefixed with NEXT_PUBLIC_. Because Stripe checkout URLs are constructed server-side in a tRPC router, the prefix is not strictly required for this use case — but using it ensures the same base URL is also accessible in client-side code if needed elsewhere (e.g. for constructing absolute links in emails or webhooks).

Verification

After setting the variable and redeploying:

  1. Open the billing page in your application.
  2. Initiate a test checkout using a Stripe test card.
  3. Confirm you are redirected to /dashboard/billing?success=true on completion.
  4. Confirm you are redirected to /dashboard/billing?cancelled=true on cancellation.

If either redirect lands on a Stripe error page or a 404, double-check that the environment variable is set correctly and that the deployment has been rebuilt after the change.