Stage 1 of 13 · Model
What the provider's behaviour implies
The provider's documentation is a list of facts. A design starts by turning each one into a consequence. Before you write any code, decide which of these statements follow from the constraints.
What you need to know first
A timeout ends your waiting, not the other side's work. When a request to the provider times out, three things are possible: the request never arrived, it arrived and failed, or it arrived and succeeded and only the response was lost.
From your side these look identical. A design that treats a timeout as "failed" will one day mark a successful charge as failed and let the buyer pay again.
An idempotency key is a value you send with a request so the receiver can recognise a repeat. This provider stores each key for 24 hours: a second request with the same key returns the first result instead of charging again. See Idempotency.
A webhook is the provider calling your server when something changes. These are delivered at least once (possibly twice), in no guaranteed order, and retried for three days if your endpoint fails.
A request with idempotency key K succeeds. 30 hours later, a buggy client retries with the same key K. What does the provider do?
Creates a new charge: it has forgotten K.
The provider only remembers keys for 24 hours. After that, the same key looks new. Your own records have to catch late retries.
At peak, 50 orders a minute. About how many requests a second is that to the provider, counting one create per order?
About 0.83 per second.
50 ÷ 60 ≈ 0.83 a second. The provider allows 100. Scale isn't the problem in this design; uncertainty and concurrency are.
What the stage asks
Which statements follow from how the provider behaves?
- Fails
If the call to create a payment times out, the buyer was not charged.
The request may have reached the provider and succeeded, with only the response lost or late. A timeout ends your waiting, not the provider's work. The outcome is unknown; see Timeouts and unknown outcomes.
- Holds
Repeating a create-payment request with the same idempotency key within 24 hours returns the original result instead of charging again.
That is the contract the provider offers, and the foundation of the whole design. It also implies that a retry after 24 hours, or with a new key, is a brand-new charge.
- Fails
Because webhooks are retried for three days, they are a complete record of every outcome.
If your endpoint is broken for longer than the retry window, misconfigured, or rejecting signatures after a secret rotation, events are lost for good. And until one arrives, you cannot tell "not yet" from "never". Something has to ask the provider; see Reconciliation.
- Holds
A
payment.processingwebhook can arrive after thepayment.succeededwebhook for the same payment.Each event is delivered and retried independently. If the first delivery of
processingfailed, its retry can land aftersucceeded. Handlers must never let an older event regress newer state. - Fails
At 50 orders a minute, the provider's 100 requests/second limit constrains the design.
50 a minute is under one request per second, two orders of magnitude below the limit, even with retries and lookups. It is worth knowing the limit exists, but it does not shape this design. It will at 100x.
The reasoning
- A timeout means the outcome is unknown; the charge may have succeeded.
- Idempotency keys deduplicate retries only within the provider's retention window.
- Webhooks arrive late, twice, out of order or not at all, so something must ask the provider directly.
Three facts drive everything that follows:
- Outcomes can be unknown. Any call can end without telling you what happened. The design needs a state for "we asked and do not know yet" and a way to find out.
- The provider deduplicates by key, for 24 hours. Every retry of the same intent must carry the same key, and the key must be tied to a record you write before calling.
- Events are hints, not commands. Webhooks arrive late, twice or out of order, and sometimes not at all. They must be applied in a way that tolerates all four.
Notice what is absent: nothing in the brief is a scale problem. The difficulty in payments is almost entirely uncertainty and concurrency, not throughput.