Payments

Webhook-verified payments: how to stop paid orders going missing

Short answer: create every order on your server, treat the customer's browser redirect as a hint rather than proof, confirm payment only from the provider's signed webhook, make webhook handlers idempotent, and run a reconciliation job that compares provider records with your database.

The bug every marketplace meets eventually

A customer pays, sees a success screen at the payment provider, and then lands back on your site to find their order still marked unpaid. Or worse: the browser tab closed during the redirect and your system never heard about the payment at all. Support tickets follow, and trust drops.

We designed Wooziee's ticketing checkout — Razorpay in India and Stripe in the UK edition — around preventing exactly this. The pattern below works with any provider that offers server-side orders and signed webhooks.

1. Create the order on your server first

Before the payment screen opens, your back end creates an order with the amount, currency, items and a unique reference, and asks the provider to create a matching payment order or session. The client only receives the provider's order ID — never the authority to decide the amount.

  • Amounts and inventory are calculated server-side, so they cannot be changed in the browser.
  • Every payment has a matching internal order before any money moves.
  • Inventory can be held for a short window while the customer pays.

2. Treat the redirect as a hint, not proof

When the customer returns from the payment page, show a “confirming your payment” state and poll your own API. Do not mark the order paid based on query parameters in the redirect URL — they can be missing, delayed or forged.

3. Confirm with a signed webhook

The provider calls your webhook endpoint server-to-server when a payment is captured. Your handler should:

  1. Verify the signature using the provider's webhook secret before reading anything else.
  2. Look up the internal order by the provider's order or session ID.
  3. Check the amount and currency match what you expected.
  4. Mark the order paid and issue the tickets, licences or goods — inside one database transaction.
  5. Return a success status quickly, and push slow work (emails, PDFs, notifications) onto a queue.

4. Make handlers idempotent

Providers retry webhooks, and sometimes deliver events out of order. Store each processed event ID and skip duplicates; guard state transitions so a “paid” order cannot be paid again, and a refunded order cannot be re-fulfilled. Idempotency turns retries from a risk into a safety net.

5. Reconcile on a schedule

Even with webhooks, something will eventually slip: an outage, a misconfigured endpoint, a manual refund in the provider dashboard. A scheduled job that compares the provider's payments with your orders catches these before customers do, and flags anything it cannot resolve automatically for a human.

6. Payouts and refunds need the same care

Marketplaces also pay money out — to organisers, sellers or partners. Treat payouts (for example Razorpay X) and refunds as first-class records with their own states, provider references and reconciliation, rather than side effects of an order update.

A checklist you can use today

  • Orders and amounts are created server-side
  • Webhook signatures are verified before processing
  • Event IDs are stored and duplicates ignored
  • Fulfilment happens in the same transaction as the status change
  • Slow work runs on a queue with retries
  • A reconciliation job runs at least daily and alerts on mismatches

If your payment flow does not tick every box, our API & integration engineering team can review it, or start with a fixed-scope codebase audit.

FAQ

Quick answers

Why should payment capture be confirmed by a webhook?

Because the customer's browser is unreliable: it can close, lose connection or be tampered with. A signed webhook sent server-to-server by the payment provider is the trustworthy signal that money was captured.

What does idempotent mean for a webhook handler?

It means processing the same event twice has the same effect as processing it once. Providers retry webhooks, so handlers must recognise events they have already applied.

How often should payments be reconciled?

At least daily, and more often for high volumes. A reconciliation job compares provider records with your orders and flags any mismatch for review.

Next step

Have a product to build or a system to fix?

Tell us what you are working on. You will get a considered reply from an engineer — with questions, a suggested approach or an honest “we are not the right fit”.