Crypto Payment Webhooks: Events, Security and Reliable Order Fulfilment
The webhook is the moment your store learns that a crypto payment is final. Here is how to design a handler that is secure, idempotent and boringly reliable, with an event map and a production checklist.

Crypto payment webhooks are HTTP requests a payment gateway sends to your server whenever an invoice changes status: the transfer is detected, confirmed on-chain, paid short, overpaid or expired. Your handler verifies the signature, updates the order and triggers fulfilment. Because crypto payments arrive asynchronously, minutes after the customer leaves your page, the webhook, not the browser, is the source of truth.
This guide focuses on the webhook side of an integration: the events to expect, how to verify them, how to make processing safe when the same event arrives twice, and how to test and monitor it. For creating invoices, see our crypto payment API integration guide.
Why webhooks instead of polling or redirects?
- Redirects are not proof — a customer returning to your "thank you" page tells you nothing about whether money arrived.
- Polling is wasteful — asking the API every few seconds for every open invoice does not scale and still lags.
- Webhooks are push — the gateway tells you the moment something changes, with a signature you can verify.
The events you will receive
| Event | Meaning | What your handler should do |
|---|---|---|
| invoice.paid | Transfer detected, confirmations pending | Show "payment received, confirming"; do not deliver yet |
| invoice.confirmed | Required confirmations reached | Mark paid and fulfil the order |
| invoice.underpaid | Less than expected, below your tolerance | Tell the customer how much is missing; wait for a top-up |
| invoice.overpaid | More than expected arrived | Fulfil; decide how to handle the difference |
| invoice.expired | No valid payment within the time window | Release reserved stock; keep the order unpaid |
These are the event names mistKET uses. Other gateways use similar statuses with different names, so map them to your own order states in one place.
Verifying the signature
Anyone can send a POST request to a public URL, so every webhook must be authenticated. mistKET signs each delivery with HMAC-SHA256 and sends these headers:
- X-Mistket-Event — the event name.
- X-Mistket-Timestamp — Unix time included in the signature.
- X-Mistket-Signature —
sha256=followed by the HMAC of{timestamp}.{raw body}with your webhook secret. - X-Mistket-Delivery — a delivery ID for logging.
- Read the raw body — before any JSON parsing, because re-encoding changes the bytes.
- Recompute the HMAC — over the timestamp, a dot and the raw body, with your webhook secret.
- Compare in constant time — use
hash_equals,hmac.compare_digestorcrypto.timingSafeEqual. - Reject stale timestamps — for example anything older than five minutes, to block replays.
- Only then parse and act — and return a 2xx response quickly.
The construction follows the standard HMAC pattern; keep the secret on the server and rotate it if it ever leaks.
Choosing and protecting the callback URL
Use a dedicated HTTPS path such as /webhooks/mistket, separate from your normal pages, and pass it as the callback URL when you create each invoice. Exclude it from CSRF protection, caching and redirects; a 301 redirect or a cached response is a common reason webhooks silently fail. Do not require a session cookie, since the gateway is not a logged-in user; the HMAC signature is the authentication. Finally, log every delivery with its event, order ID and response code so you can answer \"did we get the webhook?\" in seconds.
Idempotency: the same event can arrive twice
If your endpoint times out or returns an error, the gateway retries with back-off for hours. Network glitches can also deliver an event you already processed. Your handler must therefore be idempotent:
- Key on the order ID — look up the order your invoice references.
- Check the current state — if the order is already fulfilled, acknowledge and do nothing.
- Use a transaction or lock — so two parallel deliveries cannot both ship the product.
- Store the delivery ID — useful for debugging duplicates.
Designing order states around events
A simple state machine prevents most bugs: pending → confirming (on paid) → paid (on confirmed) → fulfilled, with side branches for underpaid and expired. Allow only forward transitions. An invoice.paid that arrives after invoice.confirmed, for example, should never move an order backwards. Late payments to expired invoices should create a record for review rather than silently reopening the order.
Handling the awkward cases
- Underpaid — tell the customer the exact missing amount and that they can send it to the same payment page; partial payments add up.
- Overpaid — deliver, then decide on store credit or a refund transfer according to your policy.
- Expired — release stock and offer a fresh invoice if the customer still wants to buy.
- Wrong network — this is handled in support, not code; show the network clearly at checkout to prevent it.
Testing and monitoring
- Expose a test endpoint — over HTTPS, with logging of headers and raw bodies.
- Trigger every event — paid, confirmed, underpaid, overpaid and expired, using small real payments.
- Replay a delivery — confirm your handler ignores the duplicate.
- Break the signature — change one byte and check the request is rejected.
- Watch delivery logs — the merchant dashboard shows each attempt, response code and transaction hash.
- Alert on failures — a spike in non-2xx responses means orders are not being fulfilled.
Bots and paid communities follow the same pattern; see how it fits a chat flow in accepting crypto payments in a Telegram bot.
Webhooks with mistKET
mistKET sends HMAC-signed webhooks for every invoice status, retries failed deliveries with back-off and shows delivery logs, expected and received amounts and transaction hashes in the merchant panel. Requests to the API are signed the same way, so one helper function covers both directions.
Review request and webhook samples in the developer section, check pricing and manage keys from the merchant login. To get access, apply on Telegram at @mistnetwork.


