How to test Stripe payments without real money
Test a Stripe subscription from Checkout through a signed webhook, stored access, duplicate delivery, and cancellation.
Use a Stripe sandbox, test API keys and a test card to complete your own Checkout flow without moving money. Then follow the signed webhook into your database and check that the right user gains—and later loses—access. A Checkout success page alone does not prove those steps happened.
This walkthrough tests a subscription in a local Next.js app. Stripe's testing guide explains sandbox test values and its Billing testing guide covers further lifecycle cases.
Before you start
You need an existing working billing implementation, a Stripe account and sandbox, the Stripe CLI, and a local database containing a disposable test user. The app needs all of the following:
- a sign-in flow that verifies that user on the server;
- a pricing page that calls a server Checkout endpoint with an approved plan;
- a Stripe webhook endpoint that verifies the raw request and persists subscription and event-processing state;
- a protected page and its data endpoints that read that stored state; and
- a way to inspect the test user's billing record and event-processing record, plus a subscription management or cancellation path.
Complete how to add Stripe billing to Next.js first, including its migration, Checkout button, webhook route, billing_events and billing_subscriptions writer, paidAccess() reader and portal button. Add the page and API callers from the premium-page guide. The commands below use the billing guide's webhook path, /api/webhooks/stripe, and local origin, http://localhost:3000. Substitute your actual route and port if yours differ. Inspect billing_subscriptions for the user's status and access_until, and billing_events for the processed event ID.
Keep this sandbox and database separate from live customers and production credentials. Do not enter a real card number. Use a fresh test user without an active subscription so the before-and-after result is unambiguous.
1. Set up a sandbox price and local configuration
In the Stripe Dashboard, select the sandbox you intend to use. Create a test product with a recurring price and copy its Price ID. Configure the server-side STRIPE_PRICE_ID to use that sandbox Price ID; the example offers this one price and does not accept a price from the browser.
For the billing guide, configure STRIPE_SECRET_KEY with the sandbox secret key, STRIPE_PRICE_ID with the sandbox price, and APP_URL with the local origin. Use the environment file or secret mechanism your app already reads; keep secret values out of source control, screenshots and logs. The webhook secret comes from the listener in the next step. Confirm that the sandbox key, sandbox price and CLI account all refer to the same sandbox. Stripe recommends separate sandboxes for development and CI where practical.
2. Forward signed events to your app
Authenticate the installed CLI as directed by Stripe's CLI documentation, selecting the same sandbox as the app. In a terminal, start the listener:
stripe listen --forward-to localhost:3000/api/webhooks/stripeThe listener prints a webhook signing secret. Set your app's server-only STRIPE_WEBHOOK_SECRET to that listener secret, then start or restart the Next.js app using its normal development command in a second terminal. Leave the listener running. The listener secret is for this local forwarding path; a registered hosted endpoint has its own secret. Stripe's listener reference describes the forwarding behavior.
Open the app, sign in as the disposable user and visit the premium page. It should deny access before payment. Record the user ID and the absence or inactive state of that user's subscription in your local database. Do not put credentials in your notes.
3. Complete your app's Checkout
From the signed-in app, choose the monthly plan. Your own server should create a sandbox Checkout Session and redirect you to Stripe's hosted Checkout. Enter Stripe's test card 4242 4242 4242 4242, any future expiry, any three-digit CVC and the requested test address details. Submit the payment. Stripe lists this card and other scenarios in its test card documentation.
Expected result: Checkout reports success and returns to your app. The listener shows events from that purchase, including the events your handler subscribes to, and the endpoint returns successful responses when processing succeeds. A success redirect may arrive before the webhook finishes; show a pending state and wait for processing instead of treating the URL as an entitlement.
4. Trace the event to stored access
In the sandbox Dashboard, open the completed Checkout Session and its customer or subscription. Match those IDs to the test user's local billing record. In your database or application inspection view, verify that the subscription record belongs to that user, has the expected test Price and current status, and that the relevant event ID has a committed row in billing_events. Then reload the protected page as the same user: it should allow access based on that stored state. Check a second signed-in user without a subscription and an anonymous request; both must still be denied. Also call the protected API or server action directly as the unpaid user, rather than testing only the page.
If Checkout succeeds but access stays pending, inspect the listener response, signature configuration, application logs and event-processing record. A 400 signature failure often means the endpoint read or changed the body before verification or is using a different signing secret. A server error after verification needs an application fix and retry; do not mark the event complete manually to make the page pass. Stripe's webhook troubleshooting guide covers delivery and signature failures.
5. Resend the purchase event
Copy the ID of a successfully processed event from the listener or sandbox event details. With the same listener running, resend that actual event in a third terminal, replacing the example argument with its ID:
stripe events resend evt_REPLACE_WITH_TEST_EVENT_IDThe CLI resend command sends to the CLI's local webhook endpoint by default. Confirm the listener receives the same event ID again, your endpoint responds successfully, and the committed event row remains present. The subscription row must remain correct and the processed event must remain a single row. The example does not send a purchase email; if you add an external side effect, verify its own durable deduplication separately. Inspect the relevant records or delivery sink, not just the HTTP status. The example processes synchronously; if you add a queue, wait until its work reaches a terminal result before checking.
stripe trigger creates synthetic events and is useful for a focused handler check. It does not prove your app created Checkout, associated the customer with the right user, or stored access for that purchase.
6. Check cancellation and failure
Cancel the test subscription through your app's billing portal, or use the sandbox Dashboard if that is the supported management path. Choose the cancellation timing your product supports. Observe the resulting subscription event in the listener and inspect the updated subscription and access records. If access continues until the paid period ends, verify that date and then test the end-of-period transition; if your policy removes access immediately, verify denial on a fresh page request and direct API request. The expected result is your documented policy, not an assumption that every cancellation is immediate.
Repeat with a fresh test user and a decline scenario from Stripe's test cards. A failed payment must not grant paid access. Test an invalid signature against your endpoint using your application's test harness and verify it is rejected without changing billing state. Stripe's Billing testing guide also describes simulating subscription time and payment events.
Stop the listener when finished. A successful local test proves the local sandbox path you exercised; it does not verify your deployed endpoint or live account. Before launch, configure and test the intended hosted environment separately.
For CoreMVP, use the Stripe billing Docs for the template's exact setup and verification path; its billing foundation connects Checkout, verified events and stored access. You still configure and test your own provider account and product policy.