Crypto payment webhooks: a secure integration guide
Build reliable crypto payment webhooks with HMAC verification, timestamp checks, idempotency, status mapping, retries, logging, and safe order fulfilment.
Make the server-side event your source of truth
A crypto checkout often returns the customer to your website after payment, but that browser navigation is not a trustworthy payment signal. The user may close the page before returning, open the return URL without paying, or reconnect after the invoice state has changed. Your order system needs a server-to-server source of truth.
BluPay sends webhook events as an invoice moves through its lifecycle. Your endpoint validates the request, records the event, maps the invoice state to the local order, and responds promptly. If delivery fails, the provider can retry without relying on the customer’s browser.
Keep the local state machine conservative. “Detected” means a transaction has appeared, not that fulfilment is safe. “Paid” should represent the provider’s confirmed terminal payment state. Other terminal outcomes, such as expired or cancelled, should close the payment attempt without marking the order as fulfilled.
Verify the signature, timestamp, and payload
A public webhook URL will eventually receive noise and malicious requests. Authenticate every delivery before using any field to change an order. BluPay signs webhook bodies with HMAC-SHA256 using the shop’s webhook secret and includes signature, timestamp, and event-identification headers.
- Read and preserve the exact raw HTTP request body.
- Require the expected signature, timestamp, and event ID headers.
- Reject timestamps outside the allowed clock-skew window.
- Build the signed message exactly as specified by the API.
- Calculate HMAC-SHA256 with the webhook secret.
- Compare the expected and received signatures with a constant-time comparison.
- Only then parse and validate the JSON payload.
Timestamp validation limits replay: a correctly signed request captured today should not be accepted indefinitely. It also means your production servers need synchronized clocks. Never log the webhook secret or the full authorization credentials while debugging a failed signature.
Treat duplicate webhook delivery as normal
Webhooks are commonly delivered at least once, not exactly once. A provider may send the same event again because your response was lost after processing, the request timed out, or a delivery worker retried. If the handler creates a shipment, sends a credit, or emails a licence on every request, one payment can trigger the action multiple times.
Store a unique event ID before applying side effects. The insert and order transition should be protected by a transaction or another atomic mechanism. When the same event arrives again, return success without repeating the work. Also make the business transition itself safe: moving an order from Processing to Processing should not fulfil it again.
Invoice creation needs a related safeguard. Send an idempotency key derived from the merchant order or payment attempt. If your server retries after a network timeout, the gateway can return the original invoice rather than creating another one the customer might pay.
Map gateway states to business states deliberately
Provider invoice states and commerce order states describe different systems. Write an explicit mapping instead of copying a string into the order. A typical integration keeps the order in Pending payment while the invoice is awaiting funds, may add a private note when a transaction is detected, and moves to Processing or Completed only after the paid event.
- Awaiting payment: the invoice exists but no qualifying transfer has been detected.
- Detected or confirming: show progress to the customer, but do not ship or grant irreversible access.
- Paid: mark the order paid and continue the normal fulfilment workflow.
- Expired or cancelled: close that payment attempt while preserving order history.
- Failed: record the reason for support and allow a new payment attempt if appropriate.
Validate that the invoice belongs to the expected merchant shop and order reference. Compare amount and currency where the API contract requires it. Do not accept an arbitrary order ID from the payload and update that record without checking the relationship established when the invoice was created.
Respond quickly and move slow work out of the request
A webhook endpoint should verify, record, enqueue necessary work, and return a successful response quickly. Slow email, inventory, analytics, or third-party calls increase timeouts and duplicate deliveries. Move those actions to a job queue where they can retry independently.
Use an appropriate non-success response for an invalid signature or malformed body. For a temporary internal failure, return an error so delivery can retry. For an already processed event, return success because replaying it cannot improve the result. Avoid redirects and HTML error pages; webhook clients expect a direct machine-readable response.
Plan for events to arrive out of order. A delayed “detected” event must not move an already paid order backward. Define allowed forward transitions and make terminal states monotonic. When local state is uncertain, retrieve the invoice through the authenticated BluPay API and reconcile rather than guessing from a single delivery.
Log enough to debug without leaking secrets
Useful operational fields include event ID, event type, invoice ID, merchant order reference, received timestamp, signature-validation result, previous and next order state, processing duration, and response status. Redact API keys, webhook secrets, customer data not needed for diagnosis, and full request headers.
Alert on sustained signature failures, rising non-2xx responses, a growing processing queue, events that cannot find a matching order, and paid invoices that remain unfulfilled. A small reconciliation job can periodically compare recent gateway invoices with local orders and repair cases where the webhook could not be applied.
Test more than the happy path. Your automated suite should cover a valid paid event, invalid signature, missing headers, stale timestamp, duplicate event, unknown invoice, already-paid order, out-of-order status, malformed JSON, and a temporary database failure. BluPay’s WooCommerce plugin applies these controls for store owners; custom integrations can follow the same model using the developer tools.
Frequently asked questions
Why are payment webhooks sent more than once?
Providers retry when delivery times out or a successful response is not received. Consumers must therefore treat duplicate delivery as normal and make event processing idempotent.
Should a success-page redirect mark an order as paid?
No. Browser redirects can be interrupted or forged. The server should update payment state only after validating a signed webhook or independently fetching the invoice from the authenticated API.
Should the webhook signature use the parsed JSON body?
No. HMAC verification must use the exact raw request bytes specified by the provider. Parsing and serializing JSON can change whitespace or ordering and produce a different signature.
What should the endpoint return for an already processed event?
Return a successful response that identifies the event as a duplicate. The event has already reached the desired state, so retrying it will not help.
Ready to accept crypto?
Create a BluPay shop, test in sandbox, and move to production when your flow is ready.