Crypto Gateway Index

Guide

Crypto Payment Webhooks and Callbacks

A payment webhook tells your server that payment state changed. Handlers must verify the signature, be idempotent, return quickly and reconcile independently. Callbacks arrive more than once and sometimes out of order, so treating them as a reliable single delivery is the most common integration bug in this category.

What is a payment callback?

An HTTP request from the provider to an endpoint you own, sent when something changes about a payment. Created, seen on chain, confirmed, expired, underpaid, refunded. The exact event vocabulary differs by provider and the shape of the problem does not.

Why is this harder than it looks?

Because the network between two servers is not reliable and neither side can tell the difference between the failure modes.

When the provider sends an event and does not get a timely 200, it does not know whether your server never received it, received it and crashed, or received it and processed it successfully before timing out on the response. The only safe assumption is failure, so it retries. Which means your handler will see the same event again, and possibly several times.

Events can also arrive out of order. A confirmation may land before the notification that the payment was seen, particularly on fast networks where the two states occur within the same second.

What does a correct handler do?

Verify the signature first. Before parsing the body, before logging it, before anything. Providers sign callbacks with a shared secret; an unsigned or badly signed request is not a payment event and should not be treated as one.

Deduplicate on the event identifier. Record identifiers you have processed and make a repeat a no-op. Deduplicating on the payment identifier instead is a common mistake, because a single payment legitimately produces several distinct events.

Return 200 immediately, then work. Acknowledge receipt, put the event on a queue, and fulfil asynchronously. A handler that does the fulfilment inline will eventually time out under load, and its punishment for being slow is more traffic.

Check state transitions rather than assuming them. If an event says confirmed and your order is already marked confirmed, do nothing. If an event arrives for a state earlier than the one you have recorded, ignore it rather than moving backwards.

Reconcile on a schedule anyway. See the full catalogue for which providers document their event APIs. Once an hour, ask the provider for payments in a window and compare against your own records. This catches everything the callback path missed, and it will catch something.

What should you ask a provider?

How long do you retry, and on what schedule? Can I replay a delivery from the dashboard? Is there an endpoint to list events for a window? Do you guarantee ordering, and if not, does each event carry a sequence or a timestamp I can order on?

The replay question is the practical one. A provider that lets you re-send a delivery turns a bad deployment into a five minute fix, and one that does not turns it into a reconciliation project.

Where should you look next?

The integration checklist covers the rest of the build. The developer API shortlist ranks providers on documentation and API quality.

A worked handler shape

Verify the signature. Reject anything that fails, without parsing. Look up the event identifier; if you have seen it, return 200 and stop. Otherwise record it, enqueue the work, return 200. Process from the queue, where retries and failures are yours to control rather than the provider’s.

That shape survives duplicate delivery, slow downstream systems, and a deploy in the middle of a busy hour. Handlers that fulfil inline survive none of those.

Ordering, and why you should not rely on it

Events describing the same payment can arrive out of order, particularly on fast networks where several states occur within a second. Treat each event as an assertion about state rather than a step in a sequence.

Practically: keep a state machine on your side, ignore transitions that move backwards, and key on the payment’s current state rather than on the last event you happened to receive.

What to ask a provider

How long do you retry, on what schedule, and can I replay a delivery from the dashboard? Is there an endpoint listing events for a time window? Does each event carry a sequence number or a timestamp I can order on?

The replay question is the practical one. A provider that lets you re-send turns a bad deploy into a five minute fix; one that does not turns it into a reconciliation project against chain data.

The reconciliation job

Once an hour, ask the provider for payments in a window and compare against your records. Alert on discrepancies rather than logging them. This catches everything the callback path missed, and over a long enough period it will catch something.

Where to go next

The integration checklist covers the rest of the build around this handler. The testing guide covers proving it works before traffic arrives, and the developer API shortlist covers which providers document their event APIs properly.

Testing the handler properly

Send the same event twice and confirm the second is a no-op. Send events out of order and confirm your state machine ignores the backwards transition. Send an event with a bad signature and confirm it is rejected before parsing.

Those three tests take an afternoon to write and they cover the failures that actually occur in production. Testing the happy path proves the integration compiles; testing these proves it survives.

If your provider supports replaying a delivery from its dashboard, use that rather than constructing payloads by hand, since it exercises the real path including the signature.

One final check before launch

Confirm the endpoint is reachable from outside your network, from a machine that is not on your VPN. This sounds trivial and it is a recurring cause of integrations that pass every internal test and receive nothing in production.

Read next

Questions merchants ask

Why does the same webhook arrive twice?

Because the provider cannot distinguish a lost delivery from a slow handler. If your endpoint does not answer quickly enough, the provider assumes failure and retries. Idempotency on the event identifier is the only reliable defence.

What if my server is down when the webhook fires?

Most providers retry on a backoff schedule for some hours. That is a safety net rather than a guarantee, which is why a scheduled reconciliation pass against the provider's API is not optional.

Can I poll instead of using webhooks?

For low volume, yes, and it is simpler to reason about. It scales badly and adds latency between payment and fulfilment. Most integrations end up doing both, with polling as the reconciliation layer.

What should I do if a webhook never arrives?

Nothing, if your reconciliation job is running: it will pick the payment up on its next pass. That is precisely why the reconciliation job is not optional.

Can I verify the signature after parsing the body?

No. Parse nothing until the signature verifies. An unsigned request arriving at a payment endpoint is not a payment event and should not be treated as one.

Last checked 15 days ago
What changed