What happens when you retry a payment?
A timeout tells you that the response did not arrive. It does not tell you whether the payment was created. Explore what changes when a retry uses the same key, a new key or different payment details.
Synthetic exercise. No money moves. Scenario playback runs in your browser. This is an educational reference model, not a live API client, a conformance test of any provider or a settlement record.
Step through a scenario
The interactive simulator is not running. It needs JavaScript and may also be blocked by your browser or network. Every scenario still works as a worked example: see what each scenario demonstrates for the steps, totals and lessons.
The simulator stopped unexpectedly. No result is shown rather than a possibly wrong one. The worked examples below are unaffected.
Learning goal
- Create requests sent
- 0
- Provider payment records
- 0
- Completed total (simulated)
- €0.00
- Order fulfilments
- 0
Provider state simulation
What the provider holds. A real application cannot see this panel.
Application knowledge
Only what responses, status lookups and processed webhooks have told it.
Customer message
Follows application knowledge, never provider-only facts.
Reason code:
Event timeline
Logical steps, not real-world times. Each step can record several entries.
No actions yet. Press Next step to send the first request.
What each scenario demonstrates
Every scenario begins with nothing sent and one intended €30.00 order (order-demo-001, payee supplier-demo). Create requests include any lost before reaching the provider; status lookups and webhook deliveries are not create requests. The completed total is simulated, not settlement evidence.
| Scenario | Steps | Create requests | Provider records | Completed total | Fulfilments | Lesson |
|---|---|---|---|---|---|---|
| R01 Lost response, same-key retry | Provider creates P1 for key-A; the response is lost. Retry key-A with identical details; the cached 201 returns P1. P1 completes; success event E1 arrives. | 2 | 1 | €30.00 | 1 | The same key and details recover the original payment. One payment, one fulfilment. |
| R02 Lost response, new-key retry | Provider creates P1 for key-A; the response is lost. Retry as submission-B with key-B; the provider creates P2. Both complete. P2’s success arrives, then P1’s. | 2 | 2 | €60.00 | 1 | A new key is a new payment. The order is fulfilled once, but two payments totalling €60.00 now need review. |
| R03 Request lost before the provider | The first key-A request never reaches the provider. Retry key-A with identical details; the provider creates P1. P1 completes; its success arrives. | 2 | 1 | €30.00 | 1 | The application sees the same timeout as in R01, although the provider holds nothing. A same-key retry is safe either way. |
| R04 Same key, changed amount | Provider creates P1 for key-A at €30.00; the response is lost. Retry key-A at €35.00: 409 conflict. Retry key-A at the original €30.00: P1 is returned. P1 completes; its success arrives. | 3 | 1 | €30.00 | 1 | A conflict creates nothing, keeps the original binding and does not mean the original payment failed. |
| R05 Duplicate webhook deliveries | Recover P1 by same-key retry; P1 completes. Event E1 is delivered three times, then a distinct event E2 reports the same success. | 2 | 1 | €30.00 | 1 | Event IDs catch repeat deliveries; order state catches a different event about the same result. |
| R06 Older event arrives last | Recover P1 by same-key retry. A processing event E1 is generated and held; P1 completes (E2). E2 arrives, then the older E1. | 2 | 1 | €30.00 | 1 | The late processing event triggers a current-status lookup, which still says succeeded. No rollback. |
| R07 No webhook: retrieve status | Recover P1 by same-key retry. P1 completes, but no event is delivered. The application retrieves P1 by the ID it already knows. | 2 | 1 | €30.00 | 1 | Until the retrieval, the provider has succeeded but the application still says processing and fulfils nothing. |
Why a timeout is not a failure
When a create request times out, the application cannot tell whether the request was lost on the way out (R03) or the provider created the payment and the response was lost on the way back (R01). Its knowledge is identical in both cases. Treating the timeout as a failure and releasing the order, or sending a fresh request, can produce a second payment.
The safe move is to keep the original submission unresolved and ask again in a way the provider can recognise: the same idempotency key with exactly the same details. In this model the provider then returns its cached original 201 response, even if the payment has since moved on. A current status needs a separate lookup.
Same key, new key or changed details
- Same key, same details (R01, R03): the provider recognises the retry. One payment.
- New key (R02): the provider sees a new request. It does not deduplicate on the order reference alone, so the order now has two payments. The first submission stays unresolved until evidence about it arrives; learning about the second says nothing about the first.
- Same key, changed details (R04): the key is bound to the order, submission reference, amount, currency and recipient. A changed amount is refused with a conflict that creates nothing and leaves the original binding intact.
Real providers differ in key scope, which fields are compared and how long keys are kept. Stripe, for example, documents that it saves the status code and body of the first request for a key, compares later parameters with the original and may remove keys once they are at least 24 hours old: Stripe idempotent requests. Check the contract of the provider you integrate with.
Duplicate, late and missing webhooks
A webhook can arrive more than once, out of order, or not at all. In this model the application records each processed event ID and acknowledges a repeat without effect (R05). For each new event it looks up the payment’s current status before acting, so an older event cannot roll back a confirmed payment (R06). Fulfilment is applied at most once per order, so a second event describing the same success does nothing more.
When no event arrives, the application stays unconfirmed until it retrieves the payment by an ID it has already learned (R07). It cannot look up a payment whose ID it never received. That is why the lost-response scenarios recover the ID first. Stripe’s documentation describes the same practical concerns: events are not guaranteed to arrive in order, and the same event, or two separate events, can be received for one change.
Model assumptions and limits
The nine assumptions behind every scenario
- The illustrated provider supports atomic, scoped idempotent creation and keeps each key for the whole exercise. It does not deduplicate a second key because the order reference is the same.
- The key scope is the merchant, the create-payment operation and the key. The key binds the order, submission reference, amount, currency and recipient; property order does not matter.
- Creation returns a cached
201response with a provider payment ID and theprocessingstate. Exact replay returns that original response. Changed details return a synthetic409without creating a record or changing the binding. - Payments move from
processingtosucceeded, adding to the simulated total exactly once. These labels are not bank settlement evidence or a mapping to every provider status. - Every payment created in these scenarios eventually succeeds. Failures, cancellations, refunds, key expiry, concurrent in-flight requests and partial captures are outside V1.
- A status lookup needs a provider payment ID learned from a response or an authenticated webhook.
- Webhooks are trusted synthetic inputs with an event ID and payment ID. Each new event triggers a status lookup before the application updates its record. No signature verification is modelled.
- Provider status includes the synthetic order and submission references, so a previously unseen payment can be matched to its order.
- Fulfilment happens at most once per order, after a current successful payment whose amount, currency and recipient match. A second successful payment is flagged for review; it does not trigger a second fulfilment or an automatic refund.
The downloaded JSON trace includes these assumptions. It demonstrates the reference model only; it is not an audit receipt, certification or a ZILLQ API test result.
Explore payments in the ZILLQ sandbox.
The sandbox is a separate synthetic environment with consent, payment initiation, recipient checks and recorded outcomes. It may take a moment to start. This simulator does not contact it.
Explore payments in the ZILLQ sandbox → · Explore the platform