intermediate · investigation
The Customer Charged Twice
A customer's card shows two identical charges 12 seconds apart for one checkout. Support finds a handful of similar cases, all from the mobile app, all on poor connections.
Evidence
10:41:02 POST /payments amount=4999 device=ios -> (response lost: client timeout at 10s) 10:41:14 POST /payments amount=4999 device=ios -> 200 charge_id=ch_2 <- automatic retry ledger: ch_1 created 10:41:07 (original request DID succeed, response never arrived) API docs, "Retries" section: (does not exist) request headers: no Idempotency-Key; retry sent identical body
Investigate
Inspect Client retry behavior
The mobile HTTP client retries timed-out requests once — reasonable per its docs, and nothing in the API contract told it POST /payments was unsafe to retry.
Inspect Timeline reconstruction
The first request succeeded at 10:41:07, after the client gave up at 10s — the classic ambiguous outcome: the response was lost, not the operation.
Inspect Idempotency support
The payments endpoint has no idempotency-key mechanism at all: two syntactically identical POSTs are two charges, by design.
Inspect Server-side dedup heuristics
A “same card + same amount within 60s” warning was proposed and rejected last year as too false-positive-prone; nothing replaced it.