The client sends the same unique key with the first request and with every retry. The server uses this key to make sure the payment runs only once.
An idempotent operation gives the same result if you run it once or many times.
On the client:
- Create one unique key, such as a UUID, for each logical operation. For example, one key for "pay this order".
- Send it in an
Idempotency-Keyheader. - On a retry, send the same key again.
On the server:
- Store the key with a fingerprint of the request. A fingerprint is a hash of the request body, so the server can tell if two requests are the same.
- When processing finishes, also store the response status and body.
- When a request arrives with a key the server already knows, it does not charge again. It returns the stored response.
- If the first request is still running, the server returns an error such as 409, so the client retries later.
- If the same key arrives with a different body, the server rejects the request.
Safety details:
- Write the key record atomically with the payment, or lock it with a unique constraint in the database. Atomically means both are saved together or neither is. This stops two retries that arrive at the same time from both passing.
- Scope keys per client, so two clients can't clash on the same key.
- Expire keys after a retention window, such as 24 hours.
- The client sends a unique
Idempotency-Keyfor each operation and reuses it on every retry. - The server stores the key, a fingerprint of the request and the final response.
- A repeated request gets the stored response, and the server does not run the operation again.
- The server handles duplicates that arrive while the first request is still running, and rejects a reused key with a different body.
- Key storage is atomic with the operation, scoped per client, and expires after a time window.
Read more