← All writing
Shipping & Ops·Aug 21, 2026·6 min read

Stripe checkout in production is a different project than the demo

Redirecting to a Checkout Session and rendering a success page takes an afternoon. Handling the webhook that arrives before the redirect — or not at all — is the part that actually ships a store.

H

Hammad Iqbal

Software Engineer

A client asking for a real storefront, not another page-builder template, meant checkout had to survive the cases a demo never hits: the tab closed mid-redirect, the webhook that arrives twice, the payment that succeeds while the browser thinks it failed.

In brief
  • The success page and the webhook are two separate sources of truth — trust the webhook.
  • Webhooks are delivered at-least-once, not exactly-once, so the handler has to be idempotent regardless.
  • An order that's 'paid but not fulfilled' needs a visible state, not just a database row nobody checks.

The redirect isn't confirmation of payment

A customer can close the tab after paying but before the success redirect finishes loading, which means anything wired to that redirect — order confirmation, inventory decrement, fulfillment — is trusting a page load that isn't guaranteed to happen. The webhook fired by Stripe's servers is the actual source of truth; the redirect is just a nicer experience for the customer who stuck around.

Webhooks are delivered at-least-once, not exactly-once

Stripe retries a webhook delivery if it doesn't get a fast 200 back, which means the same event can hit the handler twice. Without an idempotency check, that's a duplicated order or a double-charged inventory decrement the first time a retry overlaps a slow database write.

  • Store the Stripe event ID and skip anything already processed
  • Verify the webhook signature before trusting the payload
  • Log every webhook received, even ones you ignore

Give the admin panel a 'paid, not yet fulfilled' state

Building the admin panel for product and order management, the useful state wasn't 'paid' or 'shipped' — it was the gap in between. An order stuck in that middle state for an hour is either a fulfillment bottleneck or a webhook that silently failed, and either way a human needs to see it, not discover it from a customer email.

Takeaway

The Stripe integration that survives production is the webhook handler, not the checkout button. Budget for it accordingly.

Have a project this kind of thinking applies to?

Tell me what you're building — I read every message myself.

Available for new projects

4+

Years exp.

8+

Projects shipped

<6h

Avg. reply time

Let's build something

Tell me about your project.

Share what you're building, your timeline, and the best way to reach you.