Building reliable payment systems with Stripe
· 2 min read · #stripe #payments #backend
Payment code is the part of a product where "usually works" is not a state you can ship. The API itself is not the hard part — Stripe's client is pleasant. The hard part is that money state lives in two systems and they only agree eventually.
Treat the webhook as the source of truth
The browser returning from checkout is a hint, not a fact. The user may close the tab; the redirect may fail; the card may be captured minutes later after a bank challenge. The only event I mark an order paid from is the webhook.
export async function handleEvent(event: Stripe.Event) {
// Idempotency first: events are delivered at least once.
const fresh = await db.events.insertIfNew(event.id);
if (!fresh) return;
switch (event.type) {
case "checkout.session.completed":
await markPaid(event.data.object.metadata.orderId);
break;
case "charge.refunded":
await markRefunded(event.data.object.metadata.orderId);
break;
}
}
Two lines of idempotency at the top remove an entire category of duplicate-fulfilment bugs.
Never trust amounts from the client
The price is decided on the server, from your own catalogue, at the moment the session is created. Anything that arrives from the browser is an identifier, never a number.
Write the state machine down
Orders drift into states nobody planned for: paid then refunded, authorised then expired, paid twice by an impatient customer. Writing the allowed transitions explicitly turns those into rejected transitions instead of corrupted rows.
If you can't draw the order lifecycle on a napkin, the reconciliation job will find out for you.
Test against real failure
Stripe's CLI replays events locally, which makes it easy to test the ugly cases: the duplicate delivery, the refund that arrives before the payment record, the webhook that times out and retries. Those paths run rarely in production and matter enormously when they do.
None of this is clever. Payments reward carefulness rather than cleverness, which is one of the things I like about working on them.