← All guides

Stripe test mode vs live mode: a 15-minute check before your first real payment

By Novruz Veliev, Senior QA Engineer · ShipClarity · Published 28 September 2026

Stripe gives you two copies of everything: one for testing, one for real money. Keys, products, prices, webhooks, payment links and the customer portal all exist twice. Most launch-day payment bugs I see in Lovable and Bolt apps come from one piece still pointing at the test copy while the rest has moved to live.

The symptoms are quiet. Checkout works for you, because you test with the card that always works. A real customer gets an error, or pays and never gets access, and you hear about it days later, if at all. Below is the check I run before an app takes its first real payment. It takes about 15 minutes and one small real purchase.

In this article
  1. Find out which key your live site actually uses
  2. Prices and products don't move to live by themselves
  3. Webhooks: a separate endpoint and a separate secret
  4. Payment links and the customer portal
  5. One real purchase, then a refund
  6. After the switch: keep testing without touching live

1Find out which key your live site actually uses

Every Stripe key says which copy it belongs to. Publishable keys start with pk_test_ or pk_live_, secret keys with sk_test_ or sk_live_. The publishable key sits in your frontend, the secret key must only live on a server - in a Lovable or Bolt app that usually means Supabase Edge Function secrets or your hosting's environment variables.

How to check

Open your live site, then DevTools → Sources, and search the loaded JavaScript for pk_test_. If it's there, your checkout still starts in test mode. While you're in there, search for sk_ too: a secret key in the browser is a bigger problem than test mode, and it needs rotating in the Stripe dashboard today.

Then check the server side: in Supabase, Edge Functions → Secrets (or wherever your project keeps them), look at the Stripe secret. It's common to switch the publishable key and forget the secret one, or the other way round. Both have to say live.

2Prices and products don't move to live by themselves

A product or price you created in test mode does not exist in live mode. Its ID (price_...) is only valid in the copy where it was created. If your code or your AI builder hardcoded test price IDs, live checkout fails with an error like "No such price", even though the key is right.

How to check

Stripe dashboard, test mode switched off → Product catalog. Every plan your pricing page sells should be there with the same amount, currency and interval. Then compare the price IDs your app uses (search your code or Edge Functions for price_) with the IDs shown in live mode. They will be different strings - that's expected - and every one in your code must be the live one.

Check the amounts while you're there. A $19 plan in test mode and a $190 plan in live mode, or monthly in one and yearly in the other, is a mistake that nobody catches with a test card.

3Webhooks: a separate endpoint and a separate secret

Webhooks are how Stripe tells your app "this person paid". They're set up per mode. A webhook endpoint you added in test mode receives nothing from live payments, and its signing secret (whsec_...) is different from the live one. The result is the worst kind of bug: the customer is charged, and your app never unlocks their account.

How to check

Live mode → Developers → Webhooks. There must be an endpoint pointing at your production URL, listening to the events your app uses (typically checkout.session.completed, and the subscription events if you sell plans). Copy its signing secret and make sure your server uses that one, not the test one. After the real purchase in step 5, open the endpoint and check that the event shows as delivered, not failed.

If you sell through Stripe Payment Links, look at the link behind every "Buy" button. Test links contain test_ in the address, and the page they open shows a "Test mode" badge. A live site with a test payment link takes no money at all.

The customer portal - where subscribers update a card or cancel - is also configured separately for each mode. If "Manage subscription" opens an error for a real customer, the portal was only set up in test mode.

5One real purchase, then a refund

This is the step people skip, and the only one that proves the whole chain. Buy your cheapest plan with a real card, as a new user. Then check four things: the charge appears in the live dashboard, the webhook shows as delivered, your app gave the account what it paid for, and the receipt email arrived (Stripe doesn't send receipts for test payments, so this is the first time you'll see one). Refund yourself from the dashboard when you're done.

One more quick test: on your live checkout, try the test card 4242 4242 4242 4242. It should be declined. If it goes through, your live checkout is still running in test mode.

6After the switch: keep testing without touching live

Once you're live, keep test mode (or a Stripe sandbox) for everything you try next, with its own keys in a staging copy of the app. The mistake to avoid is the reverse of launch day: switching the live app back to test keys "for five minutes" to debug something, and forgetting. If you ever do, repeat step 1 before you log off.

Want someone to run the money path with you?

The checks above cover the switch itself. What they don't cover: upgrades and downgrades between plans, failed renewals, coupons, refunds that should remove access, and what a second user sees after the first one pays.

Run the free outside scan See what a full audit covers

The payment path, test and live, is one of the manual checks in a Pre-Launch Audit, done with real test accounts and screen recordings of every step.