In this article
Design retry handling around one business intention instead of generating a new payment for every attempt.
A timeout creates a question: did the request reach the payment service? It does not automatically create permission to charge again. Idempotency is useful when the application preserves the relationship between a business intention and the requests made to carry it out. The key belongs to that intention, not to the latest click.
Read the actual duplicate contract
Moov’s idempotency guide describes deterministic keys for supported transfer and refund operations. It states that a repeated transfer key produces a duplicate-conflict response and that keys do not expire. Do not assume another provider’s behavior, such as returning the original success body, applies here.
The practical consequence is to preserve your own operation record. Before submission, record the business intent, intended participants, amount, currency, and the generated key. Keep any returned provider reference associated with that record. A network failure should lead the application back to the existing record, not through a second code path that invents a fresh key.
Define what counts as the same intention
One invoice may allow multiple legitimate payments, and one order may need a separately authorized partial refund. Therefore, an invoice number alone may be too coarse as a universal key. Conversely, a random value created on every request is too narrow because retries become unrelated. Define a distinct internal operation for each authorized movement and use a stable identity for its attempts.
If the user changes the amount or recipient, do not silently reuse the previous operation as though nothing changed. Resolve the earlier attempt first where necessary, then record the changed instruction as a separate business decision. A stable key is not a substitute for validating the intended payload or confirming authority to move money.
Fictional worked example: two tabs, one purchase
In a fictional application called Willow Supplies, a buyer opens the same $84 order in two browser tabs. Both tabs show a pay button. The first request reaches the backend, but the response to the browser is interrupted. The buyer clicks the second tab’s button, believing the first attempt did nothing.
Willow’s design routes both attempts to the same recorded payment intention. The backend coordinates access to that record and preserves its retry identity. If it sees evidence of an existing provider operation, it investigates or retrieves that operation rather than treating the second click as a new sale.
A duplicate response does not prompt the software to rotate the key until a request succeeds. That would defeat the safety boundary. The team instead distinguishes “the intended operation may already exist” from “a newly authorized operation should be created.” No payment or concurrency test was executed for this fictional example.
Protect the boundary inside your application
External idempotency helps only where the supported API operation honors it. Your own application must also prevent duplicate fulfillment, repeated ledger postings, and multiple notification jobs when it observes the same result twice. Use internal uniqueness and transaction controls appropriate to the data model. Do not claim that one request header makes the entire workflow exactly once.
Store enough information to investigate conflicts without retaining secrets. A diagnostic record can contain the intent identifier, safe payload summary, key reference, timestamps, and provider object reference. It should not contain authentication headers or full payment instrument details. Give support a recovery action, not a generic instruction to “try again with another key.”
Retry identity checklist
- Create the internal intention before submitting its request.
- Keep one stable retry identity for that intention.
- Validate that repeat attempts refer to the same intended operation.
- Preserve returned provider references immediately.
- Investigate duplicate conflicts before considering a new operation.
- Protect internal business effects from repeated observations.
- Separate a legitimate new payment or refund from a transport retry.
Limits and review questions
This pattern does not authorize payments or guarantee that every endpoint supports idempotency. Confirm the current endpoint contract, key format, scope, and duplicate behavior for the operation you use. The review question is simple: if a response disappears, can the system recover the original intention without asking the customer to risk a second movement? If the answer is unclear, the retry design is not finished.