Blog
3 min read

Stripe Test Mode and Sandboxes: Test Payments Without Real Money

How to test Stripe payments safely: test keys vs live keys, Stripe sandboxes, the test card numbers for success, declines and 3D Secure, testing webhooks locally with the Stripe CLI, simulating subscriptions with test clocks, and the checklist for switching to live mode.

Before your app takes real money, you need to test it with fake money. Stripe makes this easy: everything you do in test mode or a sandbox looks real — payments, customers, subscriptions, webhooks — but no card is charged.

Test keys vs live keys

Every Stripe account has two sets of API keys:

Test Live
Publishable key pk_test_... pk_live_...
Secret key sk_test_... sk_live_...
Charges real cards No Yes

Use test keys in development and staging, live keys only in production. Keep the secret key on your server — never in frontend code. (Hide API Keys in an AI-Built App)

Sandboxes

Stripe now offers sandboxes: isolated test environments inside your account. You can create several — one for local development, one for CI, one for a teammate — so test data and settings don't collide. Stripe recommends general sandboxes for new integrations, and the older "test mode" sandbox for existing integrations that depend on it. Find them under Sandboxes in the Dashboard's account picker.

Test card numbers

In test mode, use these with any future expiry date and any 3-digit CVC:

Card number What happens
4242 4242 4242 4242 Payment succeeds
4000 0025 0000 3155 Requires 3D Secure authentication
4000 0000 0000 0002 Declined (generic)
4000 0000 0000 9995 Declined (insufficient funds)

Stripe's testing docs list many more, including specific countries, disputes and other payment methods. Never use real card numbers in test mode — Stripe rejects them.

Test the unhappy paths too: declines and 3D Secure are where integrations usually break.

Test webhooks locally

Your app learns about payments through webhooks. Stripe can't reach localhost, so use the Stripe CLI to forward events:

stripe login
stripe listen --forward-to localhost:3000/api/webhooks/stripe

The CLI prints a webhook signing secret (whsec_...) — put it in your local environment so signature verification passes. Then trigger events on demand:

stripe trigger checkout.session.completed
stripe trigger invoice.payment_failed

(Test Webhooks Locally, Handle Webhooks Reliably)

Test subscriptions with test clocks

Waiting a month to see if renewal works isn't practical. Test clocks let you create a customer and subscription attached to a simulated clock, then move time forward to trigger renewals, trial endings and failed payments in seconds. (Stripe Subscriptions Explained)

Before you switch to live

  • Successful payment, decline and 3D Secure all handled.
  • Webhook endpoint created in live mode with its own signing secret (test and live secrets differ).
  • Your app grants access from the webhook, not just the redirect page.
  • Live keys only on the production server; test keys everywhere else.
  • Products and prices recreated in live mode (they don't copy over).
  • Receipts, tax and refund settings checked.

Then make one small real purchase yourself and refund it.

The summary

  • Test keys (sk_test_) and sandboxes never move real money.
  • 4242 4242 4242 4242 succeeds; test declines and 3D Secure too.
  • Use stripe listen for local webhooks and test clocks for subscriptions.

EasySpawn runs your app on a server with HTTPS from day one, so your live Stripe webhook endpoint just works — and Claude Code can wire up and test the whole flow. See how it works or join the waitlist.

Related: Add Stripe Payments to an AI-Built App · Stripe Payment Links vs Checkout · Stripe vs Paddle vs Lemon Squeezy · Idempotency Keys

Keep reading