#44Submit Order (Prevent Double-Charge)
Idempotency keys, dedup window, retry storms.

Build the submit-order endpoint of a commerce / payments system so that a user is charged exactly once and gets exactly one order — no matter how many times the request is retried. This is a focused problem: we are not designing a whole marketplace (no search, no catalog, no cart, no waiting room). We are designing the one write that touches money, and the three failure realities that make it hard:

  1. The network lies. The response to a successful POST /orders can be dropped before it reaches the client. The client cannot tell "charged" from "never arrived", so it retries — and a naive server charges again.
  2. Two systems can't commit atomically. The charge lives in Stripe; the order lives in your database. There is no distributed transaction across them. A crash between the charge and the order commit leaves the user debited with nothing to show for it.
  3. Failures synchronize. When a downstream (the payment provider) blips, every client retries at once. Without discipline, that 50%-failure moment amplifies traffic ~3.5× and the system cannot recover — a retry storm.

The load-bearing answer is an idempotency key: a client-minted token that names the intent ("this one Pay action"), carried on every retry. The server claims the key in a durable store before doing anything expensive, records its progress as it goes, and on any repeat of the key returns the original outcome instead of redoing the work. Layered on top:

  • a dedup window — how long the key is remembered (24h, aligned with the payment provider's own window),
  • a recovery-point state machine so a half-finished intent can be resumed to completion rather than restarted,
  • and retry-storm hygiene (client backoff + jitter, a gateway retry budget) so retries stay cheap and bounded.

The canonical references are Stripe's idempotency contract and Brandur's Postgres implementation — both are cited throughout.

Reading: Stripe — Designing robust APIs with idempotency · Stripe API Reference — Idempotent Requests (24h window, 409 concurrent, param-mismatch error) · Brandur — Implementing Stripe-like Idempotency Keys in Postgres (recovery points + atomic phases) · AWS Builders' Library — Timeouts, retries, and backoff with jitter (Marc Brooker) · Marc Brooker — Exponential Backoff And Jitter · Google SRE Book — Handling Overload + Addressing Cascading Failures (client-side throttling, retry budgets)
idempotency keys
dedup window
retry storms
recovery-point resume
request fingerprinting
outbox for exactly-once side-effects