Stripe: payments that never charge twice
Payment APIs where a retry after a timeout must never move money twice.
The idea
A client whose request timed out does not know whether the payment went through. If retrying could charge twice, nobody could safely retry. Stripe's answer is the Idempotency key: the client attaches a unique key to every request that changes something, and the server does the work at most once per key, returning the stored result to any repeat.
Doing that correctly is harder than it sounds, and Brandur Leach's companion post works through it in Postgres: split a request into steps that each commit atomically with a recovery point, so a retry resumes rather than restarts, and treat each call to an outside service as a step that may need its own key. Michelle Bu's history of the payments API adds the other half: once some payment methods take days to confirm, a payment is a state machine, and the API has to say so.
Read the originals
Written by the engineers who built it.
- Stripe's payments APIs: the first ten years
Michelle Bu · Post, Dec 2020
How payment methods that confirm asynchronously broke the original API, and why the replacement models a payment as one explicit state machine.
- Implementing Stripe-like Idempotency Keys in Postgres
Brandur Leach · Post, Oct 2017
The long version, with code: how to make a multi-step request safe to retry when some of its steps call other services.
- Designing robust and predictable APIs with idempotency
Brandur Leach · Post, Feb 2017
The short, standard explanation of idempotency keys, and of retrying with backoff and jitter.
Practise it
Make the decisions yourself, then compare.