The useful starting point
Treat a payment as a lifecycle, not a single API response. Define which updates can change its state, how repeated work is handled and how uncertain outcomes are investigated.
A timeout: a worked example
Imagine a request leaving your service and the connection closing before the response arrives. The application knows that it did not receive an answer. It may not yet know whether the outside system accepted the request.
Automatically telling the customer to pay again can turn uncertainty into a second problem. A workflow needs an explicit way to represent the unresolved state and check what happened before deciding the next action.
Trace one transaction through uncertainty
Illustrative example · a simplified lifecycle, not a client’s state model or an incident report
- 1Request recorded with a reference
- 2Provider outcome is still uncertain
- 3Authoritative result is confirmed
- 4Records are checked and the outcome is visible
| Observation | What the application actually knows | Handling to define |
|---|---|---|
| A request is recorded as TX-DEMO-01 | There is one intended operation with a reference | Keep a way to correlate internal work and outside requests |
| The connection times out | A response was not received; acceptance may be unresolved | Represent uncertainty and check the original operation before retrying movement |
| A completion update arrives | The provider reports a result for that operation | Validate and correlate the update before applying the permitted transition |
| The same event arrives again | There is another delivery of previously handled information | Avoid repeating the business effect; retain enough history to explain the delivery |
| An older processing update arrives late | Arrival time alone does not establish the latest state | Use the agreed transition rules and authoritative information |
The important distinction is between another attempt to deliver information and another intended payment. Give both concepts explicit identities. Otherwise an application can confuse repeated communication with new work.
Separate three responsibilities
Request identity
Decide how an intended operation is identified and how a retry relates to it. An event identifier from one provider may help detect repeated delivery, but it is not automatically the identifier of the whole business operation.
State authority
Define which source can establish a result and which transitions are allowed. A completion label should reflect the product’s actual meaning of completion, including the relevant provider contract.
Operational explanation
Give the team a way to inspect the references, meaningful updates and unresolved questions. That view helps distinguish a missing message, an uncertain provider outcome and a mismatch between records.
Use a failure-focused acceptance checklist
Alongside the ordinary journey, exercise repeated delivery, a missing response, a late update and recovery after interrupted processing. Check both the resulting state and the operational explanation.
Passing one duplicate-event test is not proof that every possible financial effect is protected. Identify which action must not repeat, where its identity is retained and how a recovery can establish what already happened. That is more useful than attaching a broad reliability label to the integration.
Repeated messages need predictable handling
A webhook is an update sent from another system to your application. Delivery can be retried, so handling an update twice must not blindly repeat a financial action.
Idempotency is the property that repeating a request does not create an additional effect. The important design question is what identifies the operation being repeated and where the system records that it has already been handled. A duplicate event and a separate legitimate payment must remain distinguishable.
Arrival order is not the whole transaction history
Different messages may describe different stages of the same operation. A late update should not automatically overwrite a later, authoritative state merely because it arrived last.
Write down the permitted transitions and which source can establish each one. Where provider information conflicts or remains incomplete, define how the transaction is checked and who can see that uncertainty. The exact state model depends on the provider contract and the product.
Make investigation part of the product
A useful operational view should connect the internal transaction reference to relevant external references and the history of meaningful changes. That gives the team a starting point for tracing a payment without relying on a customer-facing label alone.
- What identifies the original operation?
- What happens if the same request or update arrives again?
- Can a late message move a completed transaction backwards?
- How is an uncertain result checked?
- Which information is available to the person resolving it?
The systems behind our payments work
Codek’s wider payments work includes banking and payout integrations with provider webhooks, consumer apps, business tools, ledger and reconciliation components. Our role spans the services that handle a transaction and the interfaces through which users and operators understand it.
Those responsibilities make correlation and state clarity useful engineering questions, rather than just API integration details. The payments case shows the product scope and continuing engagement. The timeline above is a constructed example used to explain the problem, not a reconstruction of a private transaction.
For a documented provider example of retries, duplicate events and ordering, see Stripe’s webhook documentation (opens in a new tab). The reference illustrates delivery behaviour without identifying any client’s providers.
Common questions
Does an idempotency key solve every duplicate payment problem?
It solves a defined repetition problem only within the behaviour and lifetime of the system using it. Request identity, callback handling, state transitions and recovery still need to be considered across the full workflow.
Can we mark a payment failed after an API timeout?
A missing response is not necessarily a final payment outcome. Define how the original operation is checked and how uncertainty is represented according to the integration’s contract.