Stripe

Stripe Checkout not working

Either the server never created a Checkout Session, or it did and the browser never arrived at checkout.stripe.com. Those are different bugs with the same blank button.

Symptoms

Common causes

Price id and secret key are from different modes

A price_ id from test mode only exists for the test secret key. Live keys return no such price. Generated apps copy the id from whichever mode the chat used.

Publishable key and secret key are from different accounts

The client token and the server key have to belong to the same Stripe account. A sample key left in the frontend produces a session the page cannot open.

The session exists and the redirect is blocked

Browsers enforce Content-Security-Policy form-action on the redirect that follows a form POST. If checkout.stripe.com is not allowed, the 303 is dropped and the tab stays on your billing page. The session is sitting in Stripe, unused.

The button targets a route this deploy deleted

A regeneration renamed /api/checkout or removed the server action. The page still shows the button. The request 404s.

Success and cancel URLs point at the preview

Checkout opens, payment works, and Stripe returns the customer to a host that no longer exists. It looks like checkout “lost” the order.

The price is archived, or live charges are not enabled

An archived price and an account that has not finished live activation both fail at session creation. The UI often shows a generic error, or none.

Fix it

  1. Reproduce in a private window

    Click the button and note the URL that is requested. A 404 is a routing bug. A 500 is in the handler. A 303 toward checkout.stripe.com that never navigates is a browser policy or a JavaScript router eating the redirect.

  2. Find the session id

    In the server log or the Stripe Dashboard, see whether a cs_test_ or cs_live_ session was created. No session means the failure is before Stripe. A session means the failure is between your response and the browser.

  3. Read the session

    Open it in the Dashboard. Check the mode, the price, the amount, and the success and cancel URLs. Fix the price id and the keys so they belong to that same mode and account.

  4. Allow the Checkout host in form-action

    If you set Content-Security-Policy, form-action needs https://checkout.stripe.com. The customer portal needs https://billing.stripe.com. A policy of form-action 'self' blocks the redirect even when the session was created.

  5. Click it on a phone

    A control below the fold, or under a cookie banner, never submits. The desktop screenshot will not show that.

  6. Hit the URL you published

    After a deploy, test the production domain. The preview can still have the old keys and the old route.

Catch it automatically

Map the checkout on a domain you verified. Production mode stops when the payment page is on screen and checks the price. Staging, on a preview you own, may type Stripe's published test card and nothing else. That catches a deleted route and a wrong price without charging anyone.

Watch the next break before a customer does.

Map the path once. The next publish does not get to fail in private.

Questions

The Dashboard shows a session, but the customer saw nothing. Where is the bug?

The session was created. Look at the redirect from your site to checkout.stripe.com. A Content-Security-Policy form-action, a client-side router, or a success URL on a dead host will do this.

Why does test mode work?

The price id, the secret key, and the webhook endpoint are all per mode. A working test checkout says nothing about the live price id or the live endpoint.

Can FunnelWatch finish a real payment?

No. Production checks stop on the payment page. Any card number other than Stripe's published test card is rejected before it is typed, and only on staging.

Related