All articles

Connected commerce

Make payment effects repeat-safe even when webhook delivery repeats

An endpoint can receive a legitimate event twice, or learn about a later state before an earlier one. The important invariant is that your business action remains correct when delivery is repeated, delayed or interrupted.

KIURO Editorial · Published: · Reviewed:

Stripe's webhook guide documents duplicate delivery and no guaranteed event order, and requires signature checks against the raw body. It recommends quick acknowledgement and asynchronous handling. This evergreen guidance defines the delivery contract, not a new release. (Stripe)

Stripe's idempotency mechanism for outgoing API requests is a separate facility. It does not automatically make your incoming webhook handler, database transaction or fulfilment provider idempotent. API-version behaviour matters, so follow the request documentation for the endpoint you actually call. (Stripe)

Use three identities for three different jobs

Names are illustrative; storage and scope depend on the application.
IdentityPurposeWhat it cannot establish alone
Event delivery IDAvoid reprocessing the same event recordWhether another event already caused the same business effect
Business-effect keyMake one intended action repeat-safeWhether a distinct refund, adjustment or partial fulfilment should be allowed
Outgoing idempotency keyRetry a supported provider API operation consistentlyWhether local state and a remote side effect committed together

For a hypothetical order, marking it paid is one effect. Issuing a refund is another. A key that deduplicates everything by order ID could suppress a legitimate later operation. Define keys around the action and relevant account/object context, with explicit rules for partial payments or multiple charges.

Acknowledge only after you can recover the work

Illustrative architecture, not production-ready SDK code:
verify signature using unmodified body and endpoint secret
persist accepted event in a durable inbox with a unique event key
return success after durable acceptance

worker:
  load current business state and required provider evidence
  begin transaction
    claim the action-specific business-effect key
    apply allowed state transition
    append any remote action to a durable outbox
  commit

outbox worker:
  send using the provider's supported retry/idempotency contract
  record observed result; reconcile uncertain outcomes

The durable inbox closes the gap between acknowledging receipt and losing an in-memory task. The transaction couples local state with the intent to perform a remote action. It still does not create a distributed transaction with the remote provider: after a timeout, investigate or reconcile the uncertain result before issuing a logically new operation.

Test disagreement, not only the happy path

Suggested non-production cases and invariants.
CaseWhat to verify
Same event delivered twiceOne accepted effect, with repeated delivery visible in the log
Different events concern the same paid orderThe business transition is applied once where that is the intended rule
Events arrive in reverse orderThe state machine does not undo a later authoritative state
Worker stops after local commitPending outbox work resumes rather than disappearing
Remote timeout after possible successReconciliation determines outcome without inventing a second action

Avoid deciding order from timestamps alone. When a snapshot is insufficient or evidence is missing, retrieve the appropriate current object through the documented API and retain its relationship to the event. An event log and the current business record serve different purposes.

Keep an exception queue with an owner

A retry limit is not a resolution policy. Give permanently failing or ambiguous events a visible state, a responsible person and a safe replay procedure. Logs should carry identifiers and failure categories rather than full payment or customer payloads everywhere.

Budget for reconciliation and operational support as well as implementation. No exactly-once guarantee, live charge or measured conversion improvement is claimed here. Payment integration and system integration can define those boundaries as part of a connected commerce project.

Sources

Privacy & analytics

With your permission, Kiuro uses Google Analytics 4 to understand page visits and interactions such as navigation between pages, reading depth, project views, contact-link clicks and starting or successfully submitting a form. Google also processes browser, device and approximate location information. The provider is Google Ireland Limited (Ireland); processing may also take place at Google LLC in the United States.

We do not send your name, email address or message content. Advertising storage, advertising personalisation and Google signals are disabled. Google Analytics is not loaded before you allow it.

After consent, a temporary record in this tab is valid for 30 seconds and consumed when the next page loads. Your choice is saved on this browser for six months. Analytics cookies are configured to expire after six months. You can change your choice here at any time; rejecting stops measurement and removes this website’s Analytics cookies.

Your light/dark and motion preferences are stored separately on this device so the interface remembers your choices. Contact: hello@kiuro.ch.