APIs and integrations

3 min read·Published ·Updated

Idempotency: Retry the Request, Not the Payment

A timeout does not establish whether money moved. What must survive between attempts, and which recovery scenarios should the team test?

After a payment timeout, the application does not know whether the charge happened. The response may have disappeared after a successful operation. Disabling the Pay button prevents another click, but it does not stop retries by the application, an intermediary or a support operator.

Idempotency means repeating the same operation does not create an additional intended business effect. It does not guarantee success or require every response to be byte-for-byte identical. The API contract defines how a retry reports the outcome.

The key identifies intent, not a click

A payment attempt needs a durable identifier. The application records it before sending the request and reuses it when recovering a lost connection. Generating another key after each timeout tells the server that this is another operation.

Identical amounts are not enough to identify duplicates: a customer can legitimately make two purchases of the same value. Conversely, reusing an existing key with a different amount should trigger a mismatch check. Scope the key to the appropriate caller and operation; knowing someone else's key must not grant access to their result. The Amazon Builders’ Library discussion explains these distinctions.

A preliminary lookup is not sufficient

Two concurrent requests can both find that a key has not yet been registered. Registration must be atomic, and a processing marker must agree with the local state change it represents. A transaction and a uniqueness constraint can protect that boundary in the application's own database.

An external charge is not automatically included in that local transaction. When communication fails, preserve an unknown-outcome state, look up the original operation with the provider and retry according to its actual contract. Starting a replacement payment simply because the first request timed out is dangerous.

Key retention and error handling are provider-specific too. Stripe's documentation distinguishes its API versions; behaviour observed in one implementation should not be assumed for every payment API.

Three release checks

Inspect both the application record and the provider's result. A log message saying “duplicate rejected” is not sufficient evidence. Where the outcome cannot be established automatically, support needs an operation identifier, a reconciliation owner and a clear rule against creating a replacement charge before resolving the first attempt.