afterbuild/ops
ERR-394/Stripe · mode
ERR-394
Your card was declined / No such price

appears when:After switching to live API keys but real charges still silently fail or never appear in live dashboard

Stripe stuck in test mode in production

Revenue is not reaching Stripe live-mode. pk_test_ still loads in the browser bundle or the live webhook endpoint was never created.

Last updated 17 April 2026 · 6 min read · By Hyder Shah
Direct answer
Stripe stuck in test mode in production means a pk_test_ or sk_test_ key is still in the Vercel Production scope and the live-mode webhook endpoint does not exist. Swap every Stripe env var to live-mode values, create the webhook in the live dashboard, recreate products and prices in live mode, and redeploy so the new NEXT_PUBLIC_ key reaches the browser bundle.

Quick fix for Stripe stuck in test mode

.env (Vercel Production)
bash
01# Vercel → Settings → Environment Variables → Production scope02STRIPE_SECRET_KEY=sk_live_...03NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...04STRIPE_WEBHOOK_SECRET=whsec_live_...05STRIPE_PRICE_PRO=price_live_...06STRIPE_PRICE_ENTERPRISE=price_live_...07 08# Then: Deployments → Redeploy (NEXT_PUBLIC_ values bake at build time)
Paste into Vercel → Settings → Environment Variables (Production scope) and redeploy

Deeper fixes when the quick fix fails

01 · Grep and rewire hard-coded price IDs

lib/stripe-prices.ts
typescript
01// ❌ Hard-coded test IDs — break on live key swap02const PRICE_PRO = "price_1OXa8kTest...";03 04// ✅ Read from env so live/test toggle per environment05const PRICE_PRO = process.env.STRIPE_PRICE_PRO!;

02 · Inspect the loaded bundle for pk_test_

Open production in incognito, DevTools → Network → filter JS. Search the loaded files for pk_test_. Presence confirms the bundle is stale. Purge the Vercel deployment cache and trigger a redeploy — NEXT_PUBLIC_ values are build-time inlined.

Why AI-built apps hit Stripe stuck in test mode

AI app generators wire up Stripe using the test-mode secret from the first page of Stripe docs. Works immediately — the 4242 4242 4242 4242 test card passes, the webhook fires against the scaffolded route, the UI shows success. Founder ships. Days later a real customer tries to pay and the transaction silently fails with Your card was declined or Checkout never redirects.

Stripe’s test and live modes are essentially two different databases: test customers, products, subscriptions, and webhook endpoints do not exist in live mode and vice versa. A working test integration does not imply a working live integration — every entity must be recreated in live and every env var swapped. AI generators never know this distinction because the conversation was always scoped to test mode.

Vercel compounds the problem by scoping env vars across Production, Preview, and Development. Founders update one scope and assume the others follow. They do not. NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY is further trapped in the built JavaScript bundle — the browser keeps loading the old test key until the app is redeployed.

Stripe stuck in test mode by AI builder

How often each AI builder ships this error and the pattern that produces it.

AI builder × Stripe stuck in test mode
BuilderFrequencyPattern
LovableEvery Stripe scaffoldShips pk_test_/sk_test_ and never creates live webhook
Bolt.newCommonHard-codes price_test IDs in shared config file
v0CommonNEXT_PUBLIC_ prefix correct but Production scope missed
CursorRareLeaves test webhook secret in live deploy env
Base44CommonCreates only a test-mode webhook; live mode never configured

Related errors we fix

Stop Stripe stuck in test mode recurring in AI-built apps

Still stuck with Stripe stuck in test mode?

Emergency triage · $299 · 48h turnaround
We restore service and write the root-cause report.
start the triage →

Stripe stuck in test mode questions

How do I know if my Stripe app is still in test mode in production?+
Three signs: the Stripe Dashboard Test-mode toggle is highlighted when you click Customers from your production app; real cards are rejected with 'No such payment_method'; pk_test_ or sk_test_ prefixes appear in server logs or the JavaScript bundle. Any one means you are in test mode and no real money will move.
Can I switch from test to live by changing one env var?+
No. Live mode has its own secret key, publishable key, webhook endpoints, webhook signing secrets, and customer and product catalogue. Create the products and prices in live mode (or copy them), register the webhook in live mode, and update every env var on Vercel. One key without the others leaves half the integration broken.
What happens if I accidentally mix test and live keys?+
The SDK throws 'No such customer' or 'No such price'. Test-mode IDs do not exist in live mode — they are separate databases on Stripe's side. A code reference to cus_test123 with a live key returns 404 at the SDK level and the charge never happens. AI-generated apps often ship with hard-coded price IDs that silently break in production.
Why does Vercel keep using my test key even after I updated it?+
Vercel env vars are scoped per environment (Production, Preview, Development). Updating Preview but not Production leaves production on the old key. NEXT_PUBLIC_ variables bake into the bundle at build time — the browser keeps loading the old test key until a fresh deploy runs. Redeploy after changing env vars and inspect the bundle in the browser network tab.
Can I test my live integration without charging real money?+
Only to a limited degree. Stripe offers live-mode test cards in a handful of regions. Safer pattern: make a $1 real charge with your own card, confirm the webhook fires, confirm the database updates, then refund from the Stripe Dashboard. Budget $5 of disposable charges when verifying a live deploy. Never deploy to production without running at least one real charge end-to-end.
Next step

Ship the fix. Keep the fix.

Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.

About the author

Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.

Sources