Payment engineering / 01

Payment webhooks in WooCommerce: design for the second delivery

A practical guide to verified payment events, duplicate deliveries and safe recovery, with a checklist for testing a WooCommerce gateway.

A payment webhook should report a provider event, not blindly change an order. Verify who sent it, record it durably, check the payment it refers to and make processing repeatable. The shopper’s return page is a separate signal, not proof that funds were received.

One purchase, three independent timelines

Consider a shopper who completes a card payment, closes the tab and immediately contacts the store because the order still looks pending. There are at least three timelines: the browser journey, the payment provider’s records and the store’s order history. They can briefly disagree without any component necessarily being broken. A reliable integration has to bring those timelines back together.

The return page is useful for the customer experience. It can display the latest known order state and explain that confirmation is still being checked. It should not grant paid status merely because a URL contains a success-looking parameter. A customer can reload a return URL, lose a connection or arrive before the provider’s event. None of those browser actions should create a second financial effect.

Our starting question when designing a payment flow is therefore not only “does the payment work?” It is “what happens when the event arrives twice, the tab never returns or the first processing attempt stops halfway?” That question produces a much more useful test plan than a single successful card transaction.

Verify the sender and the payment separately

The provider’s documented authentication mechanism is the first boundary. For a signed webhook, verification must use the exact request representation required by that provider. For example, Stripe documents signature verification against the raw request body. Parsing and reserializing JSON before checking that signature can change the bytes that are being authenticated.

Authenticating the sender does not answer every business question. The event must also belong to the expected account and environment, refer to the correct order and payment, and agree with the server’s amount and currency. In our preferred design, a valid event triggers a canonical provider lookup where appropriate, rather than turning every supplied field directly into an order update.

This separation is especially valuable when test and live credentials coexist during development. A correctly signed test event must never be mistaken for a live purchase. Likewise, an otherwise valid payment for one account must not settle an unrelated order just because an identifier looks familiar. Treat identity, environment and financial matching as explicit checks.

Record receipt before acknowledging it

A short webhook request is easier to operate than a request that also sends mail, provisions access and performs several slow remote calls. We prefer an inbox record for the verified receipt and a durable job for the work that follows. The acknowledgement then means that the event has been accepted into a recoverable processing path.

There is an important detail: recording the receipt and scheduling the work need one reliable boundary. If the inbox commits but the job disappears, the system can remember an event that will never be handled. If the job exists without the corresponding verified receipt, a worker may lack the evidence it needs. A transaction or an equivalent durable mechanism should cover both records.

A worker failure is then a state that can be inspected and retried, rather than a reason to invent a successful order state. Operations need to show what was received, what was attempted and which step still needs attention. An HTTP success response alone is not evidence that all downstream business effects have completed.

The second delivery must be ordinary

Stripe’s delivery guidance describes retries and warns against assuming event order. The design implication is broader than any single provider: receiving a notification again should be a routine condition. Record the provider’s event identity, make the financial transition repeatable and distinguish a duplicate receipt from a new payment.

Event deduplication and financial idempotency solve related but different problems. Two deliveries of one event can share an event ID. Two distinct events can still refer to one financial change. A store therefore needs a stable payment identity as well as an event identity. The same captured amount must not generate a second fulfilment just because another notification describes it.

A practical invariant for testing is simple: replay the same confirmed payment several times and compare the resulting order, stock and fulfilment records. The expected state should remain stable. Do not stop at checking that the endpoint returned a status code. Check the records that could actually affect a customer or the merchant’s accounting.

Process financial state, not arrival order

Suppose a payment succeeds after an earlier attempt failed. Now imagine that an older failure notification arrives after the success notification. A handler that simply applies the last received label can regress the order even though the provider still shows a completed payment. Arrival time is not a dependable model of financial truth.

We prefer explicit transitions driven by the current verified payment state and a history of effects already applied. When events disagree, a canonical lookup and a review state are more useful than an arbitrary overwrite. A review state should explain the discrepancy and preserve the evidence needed to resolve it.

Refunds and disputes require their own modelling. A partial refund is not automatically a full cancellation, and a later successful event should not erase a terminal refund. Map each adjustment to the relevant payment and amount. If there is ambiguity, make it visible to an operator instead of guessing which order line or entitlement should change.

Design the recovery path before you need it

Webhooks are an important input, but a store should also be able to reconcile an order with its provider when a delivery is missed or processing remains uncertain. Reconciliation should use the same verified transition rules as normal processing. A separate repair script that writes a different interpretation of “paid” can introduce a second source of truth.

Retries need limits, backoff and a visible final state. A job that keeps failing indefinitely can hide an operational incident, while a one-shot handler that gives up silently loses the opportunity to recover. An operator should be able to distinguish a temporary network problem, a permanently invalid event and a financial mismatch requiring a human decision.

Observability does not require collecting every request body forever. Keep the identifiers, timestamps, reason codes and evidence necessary to understand the transition. Restrict access and avoid logging card data, credentials or full personal payloads by default. A useful diagnostic record lets someone follow one order without turning the log into a second customer database.

Test a story, not just an endpoint

An effective test starts with a specific customer story. Complete a payment without returning to the store. Deliver the confirmation twice. Interrupt a worker after receipt but before fulfilment. Retry after a timeout. Then inspect the order and its history. These scenarios expose assumptions that a happy-path browser test usually leaves untouched.

Include a payment failure followed by a successful retry, a partial refund followed by another event and a provider response that does not match the expected currency or amount. In each case, state the invariant before running the test. The result should be explainable in business terms, such as “one fulfilment” or “no paid status from an unverified return”.

For VivaGo, these are development principles and acceptance scenarios, not a claim that the unreleased plugin already supports every provider event or WooCommerce combination. VivaGo remains in development. Its tested compatibility and exact supported payment flows will be published with the checked release. The useful lesson today is to make duplicate delivery and interrupted work part of the design from the beginning.

A compact webhook acceptance checklist

  • A return URL alone cannot mark an order paid.
  • Authentication, account, environment, amount and currency are checked.
  • Receipt and queued work survive the same failure boundary.
  • Repeated events cannot duplicate financial or fulfilment effects.
  • Older events cannot blindly overwrite a verified newer state.
  • Missed deliveries have a documented reconciliation path.
  • Operators can inspect failures without exposing secrets.

Sources and further reading

About this article

Prepared with AI assistance and checked against the linked technical documentation. These articles explain our development approach. They do not replace the documentation or commercial terms of a released product. Contact us to report a correction.

Report a correction

Continue reading

Meet VivaGo · Documentation