Idempotency keys: stop charging customers twice when they double-click Pay
A customer taps Pay . The spinner hangs for four seconds, so they tap it again. Or their phone drops from Wi-Fi to mobile data halfway through the request, and the app retries. Your server sees two identical POST requests, and unless something stops it, the customer is charged twice. Nothing is broken in that story. The network is doing what networks do, and the client is doing what good clients…
When a customer accidentally clicks the "Pay" button twice, or their internet connection fluctuates, they often cause their server to process the same payment request multiple times. This double-charge scenario can be avoided by implementing idempotent requests. An idempotent request is one that can be repeated without changing the outcome, ensuring the customer is only charged once.
Stripe's API and the IETF have proposed a standard for idempotency keys using an HTTP header named "Idempotency-Key." To implement this, the client generates a unique key, such as a UUID, for each action. The server stores the response for each key it receives. If the same key is sent again, the server returns the stored response instead of performing the action again.
This system requires a few conditions to be met: the key must be claimed atomically to prevent two requests from both appearing as the first; a repeat request during the processing of the first must wait or be told to retry; a key reused with different data must be rejected to prevent errors; and keys must be scoped per user to avoid collisions.
The server uses a PostgreSQL table with a primary key on (user_id, key) to ensure only one row exists for each unique key pair. This design guarantees that the "claim this key" action is atomic, preventing simultaneous requests from both being processed as the first. The middleware that handles this process checks for the presence of the "Idempotency-Key" header, generates a request hash from the request body, and attempts to claim the key.
If another request is already using the same key, the middleware determines whether to retry or return an error based on the current status of the key.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.