Skip to content
Platform

Payment Gateway Webhooks: Receive, Verify and Never Miss a Payment Update

A webhook is how your payment provider tells your server that something happened: a payment succeeded, a refund went through, a payout failed. How webhooks differ from the return URL and the status API, how to verify signatures, why you must handle retries, duplicates and out-of-order events, and how to test it all.

LILPE Editorial Team7 October 2026 8 min read
Cover for a developer guide to receiving and verifying payment gateway webhooks

The most common complaint in online payments, "I paid but my order says unpaid", is usually a webhook problem. The payment went through. The customer's browser never made it back to the store, and the server-to-server message that should have marked the order paid was missed, rejected or processed wrongly. Nothing was lost except trust.

Webhooks look simple: your payment provider sends an HTTP request to a URL on your server when something happens. Getting them right means handling the things that make them hard: they can be forged, retried, duplicated, delayed and delivered out of order. This guide covers each, for developers building an integration and for product owners who want to know what to ask for.

Return URL, webhook and status API

Three mechanisms tell your system about a payment, and they're not equally trustworthy.

Ways your system learns a payment's result, compared
Compared onReturn (redirect) URLWebhookStatus API
Who sends itThe customer's browser or appThe provider's servers, to yoursYour server asks the provider
WhenWhen the customer comes back, if they doWhen the event happens, with retriesWhenever you ask
Can it be lost?Often: closed tabs, lost signal, app switchesRarely, if you acknowledge it and the provider retriesNo, but you must remember to ask
Can it be faked?Easily, by the customerNot if you verify its signatureNo
Use it forShowing the customer a pageUpdating orders and triggering fulfilmentChecking stuck orders and answering support queries

Providers name these differently. Some call the webhook a "callback" or "notification URL"; some use "callback URL" for the browser return. What matters is who sends it: anything that travels through the customer's browser is a display hint, never proof of payment.

What a webhook delivers

Event names and formats vary by provider, but most send events in these families:

  • Payments: authorised, captured or succeeded, failed, pending.
  • Refunds: created, processed, failed.
  • Disputes: a chargeback or dispute raised against a payment, and its outcome.
  • Settlements: a settlement paid to your bank, with its reference.
  • Payouts: queued, processed, failed or reversed, with the bank reference when it succeeds.
  • Mandates: a UPI AutoPay or card mandate created, paused, revoked or debited.

Each event normally carries an event ID, an event type, a timestamp, and the object it's about: the payment, refund or payout, including your own order reference if you passed one. That reference is what lets you find the right order without guessing.

Rule 1: verify every webhook

Your webhook URL is reachable from the internet, so anyone can send it a request that looks like "payment successful". If your server believes it, you've given away goods. Providers prevent this by signing each webhook, typically by computing a keyed hash (an HMAC) of the request body with a secret shared only between you and the provider, and sending the result in a header.

  1. Read the raw request body exactly as received, before any JSON parsing or reformatting. Signatures are computed over the exact bytes.
  2. Compute the signature the same way the provider's documentation describes, using your webhook secret for that environment.
  3. Compare it to the signature header using a constant-time comparison, and reject the request if they differ.
  4. If the provider includes a timestamp in what's signed, reject events older than a few minutes, so a captured webhook can't be replayed later.
  5. Only then parse the body and act on it.

Rule 2: acknowledge fast, process after

Providers expect a quick success response (an HTTP 2xx). If your endpoint is slow, times out or returns an error, the provider assumes delivery failed and retries later, usually several times with growing gaps. So the endpoint should do as little as possible before replying:

A webhook endpoint that acknowledges quickly
  1. ReceiveRaw body and headers
  2. VerifySignature and timestamp
  3. StoreSave the event with its ID
  4. AcknowledgeReturn 2xx immediately
  5. ProcessA background job updates orders, sends emails, ships

Return an error only when you genuinely want a retry, for example when your database is unavailable. Returning success for a webhook you failed to store means the provider won't send it again.

Rule 3: expect duplicates

Retries mean the same event can arrive twice, or five times, especially when your acknowledgement was slow. Your processing must be idempotent: handling the same event again must have no further effect.

  • Record each event ID when you store it, with a uniqueness rule in your database, and skip events you've already processed.
  • Make actions conditional on state, not on the event: "mark paid if not already paid", "ship if not already shipped".
  • Send customer emails and messages from your state changes, not directly from the webhook, so duplicates don't become duplicate emails.

Rule 4: expect events out of order

Events aren't guaranteed to arrive in the order they happened. A "payment pending" webhook delayed by a retry can arrive after "payment succeeded". If you simply apply each event as it comes, a paid order can slip back to pending.

The fix is to treat payment status as a state machine with allowed transitions. Pending can move to succeeded or failed; succeeded can move to refunded or disputed; nothing moves a succeeded payment back to pending. When an event would make an illegal move, log it and ignore it. If in doubt, fetch the current status from the provider and use that.

Handling payment events that arrive out of order
Current stateEvent arrivesWhat to do
PendingSucceededMark paid and fulfil
SucceededPending (delayed)Ignore; a succeeded payment doesn't go back
FailedSucceededCheck the status API; late successes do happen, so act on the provider's current status
SucceededRefund processedRecord the refund against the payment

Rule 5: don't rely on webhooks alone

Webhooks are reliable, not infallible. An endpoint outage longer than the provider's retry window, a misconfigured URL after a deploy, or a bug that acknowledged events without storing them can all leave gaps. Two backstops catch them:

  • A scheduled job that checks, through the status API, every order still pending after a set time, such as 15 or 30 minutes. This is what resolves UPI payments that stayed pending, which our guide to UPI payment pending explains from the customer's side.
  • Daily reconciliation that matches your orders against the provider's transaction and settlement reports, so any payment your webhooks missed shows up as an exception. Our payment reconciliation guide sets out the routine.

Rule 6: keep the endpoint and secret safe

The webhook endpoint is part of your payment security, and deserves the same care:

  • Serve it only over HTTPS.
  • Keep the webhook secret in server-side configuration, separate for test and live, and rotate it if it might have leaked.
  • Don't put the secret, or anything that bypasses signature checks, in a query string or log file.
  • Return generic errors; don't echo internal details back to the caller.
  • If your provider publishes the addresses it sends from, an allowlist adds a layer, but never replaces signature verification.

The rest of the picture is in our payment gateway security guide.

Rule 7: log and monitor

When a customer says they paid and you can't see it, the webhook log is the first place to look. Store each event's raw body, headers, receipt time, verification result and processing outcome. Then watch a few numbers: failed verifications (a spike can mean a wrong secret after a deploy, or an attack), processing failures, the age of the oldest unprocessed event, and orders pending longer than normal. Alert on changes, not just thresholds.

Testing webhooks before you go live

Most providers can send test events from their sandbox, and many let you resend a past event. Use that to test the hard cases, not just the happy path:

Webhook test cases worth running
TestExpected result
Valid payment-succeeded eventOrder marked paid once; fulfilment starts
Same event sent twiceSecond one recognised and skipped
Event with a tampered body or wrong signatureRejected with an error; nothing changes
Old event replayed (if timestamps are signed)Rejected
Pending event arriving after succeededIgnored; order stays paid
Endpoint down for a few minutesEvents retried and processed after recovery
Refund and dispute eventsRecorded against the right payment

Our guide to payment gateway integration has a wider test plan covering the checkout itself.

With more than one provider

Each provider has its own event names, payload shapes, signature scheme and retry behaviour. With two or three providers, the cleanest approach is to verify each provider's webhooks with its own method at the edge, then translate them into one internal event format before any business logic sees them. Orchestration platforms do this for you: LILPE's unified webhooks page describes how provider callbacks are verified, normalised into one signed event format and delivered to your endpoint.

Frequently asked questions

What is a webhook in a payment gateway?

An HTTP request your payment provider's servers send to a URL on your server when something happens, such as a payment succeeding, a refund being processed or a payout failing. It's the reliable way for your system to learn a payment's result.

What is the difference between a webhook and a callback URL?

Providers use the terms differently. What matters is who sends the request: a return or redirect through the customer's browser can be lost or faked and is only for showing a page, while a server-to-server webhook, verified by its signature, is what you should use to update orders.

How do I verify a payment webhook?

Compute the signature over the raw request body using your webhook secret, exactly as your provider documents, and compare it with the signature header using a constant-time comparison. Reject mismatches and, if timestamps are signed, events that are too old.

Why did I receive the same webhook twice?

Providers retry when they don't get a quick success response, and sometimes even when they did. Duplicates are normal, so record event IDs and make your processing idempotent.

What if my server was down and missed webhooks?

Providers usually retry for a period. For anything beyond that, a scheduled status check on pending orders and daily reconciliation against the provider's reports will catch what was missed. Many providers also let you view and resend past events from their dashboard.

Route every payment to where it succeeds.

Tell us which providers you use today and where payments get stuck. We'll show you what the setup would look like on LILPE.