Using payment webhooks can significantly improve your company’s operational flow. Some platforms use API polling – one of the standard payment models that works by asking the payment gateway’s API about the payment status.
How do payment webhooks work? Technically, instead of repeatedly asking a payment provider for updates, a merchant’s backend receives an HTTP notification the moment an event occurs.
In other words, if API polling is asking for confirmation every 10 seconds, webhooks are like requesting: “Notify me the moment the payment arrives.” This isn’t a perfect analogy, but it gives you a sense of what is going on behind the scenes.
Today we share a guide on how payment webhooks work end-to-end: the event lifecycle, the JSON payload you’ll actually receive, how to lock down your endpoint with signature verification, and the operational habits that keep real-time pay-in notifications reliable in production. Learn more about payment gateway webhook integration below.
Open an account
in Genome online
Understanding payment webhooks in modern payment architecture
Webhooks vs. API polling for payment status updates
Let’s talk about the polling model first. Timing depends on settings and the capabilities of your provider. A payment can be settled in 3 seconds, but if your system only checks every 30 seconds, the payment is already confirmed, but you don’t know about it. You will only find out during the next scheduled request for information.
Thus, a 10-second cooldown is a more realistic model. However, polling every 10 seconds against a few thousand transactions in the process will burn through your limits. Most importantly, 90% of them will return nothing, just the confirmation that the payment hasn’t cleared yet.
This is not an exaggeration: if your platform gets fewer than 3 purchases per minute / approximately 4000 daily orders, but you poll every 10 seconds to maintain a “real-time” feel, you generate 25920 requests per day. As a result, over 20000 requests exist solely to check on pending states.
Push-based payment webhooks are not comparable: one payment request per actual event, no artificial delays.
For a small volume of transactions, API polling is cost-effective. On a larger scale, however, polling creates requests even when nothing has changed. It also introduces a choice between polling frequently enough to provide good user experience and polling slowly enough to avoid unnecessary traffic and rate-limit pressure.
This is why real-time pay-in notifications should be mainstream for any at least mid-size business. That matters most if you’re using instant payment methods – SEPA Instant, Open Banking transfers, card pay-ins. When you use these, the payment itself clears in seconds, and a slow confirmation loop becomes the actual bottleneck in checkout.
Anatomy of a real-time pay-in webhook payload
A payment may generate more than one callback as its status changes. Your integration should handle the relevant events and should not assume they will arrive in order.
For instance, for Genome’s hosted payment page, the possible Pay by Bank payment events are:
INCOMING_PAYMENT_CREATED – The customer has given consent, the payment was successfully initiated at their bank, and they returned to Genome without errors.
INCOMING_PLEDGE – Genome has received confirmation from external systems that the funds are on their way.
INCOMING_SUCCESS – The funds have reached the merchant’s account. This is the final confirmation of payment.
INCOMING_DECLINE – The hosted payment page session was declined; no successful transaction was initiated during that session.
Because callbacks can arrive out of order, treat INCOMING_SUCCESS as the authoritative confirmation that the funds have arrived.
Essential payload parameters
Regardless of the provider, a pay-in webhook payload tends to carry the same core fields:
Transaction ID/payment ID – the provider’s unique identifier for this payment.
Event ID – a unique identifier for this delivery of the webhook, distinct from the transaction ID. This is what makes idempotent handling possible (more below).
Merchant order reference – your own reference, so you can match the webhook to the order that triggered it without a second lookup.
Amount and currency – the settled amount, checked against the expected order amount before you release goods or services.
Status – where the payment sits in its lifecycle (processing, succeeded, failed, returned).
Payment method details – scheme used (SEPA Instant, Open Banking, card), payer account or masked card details.
Timestamp – when the event occurred. It is used for reconciliation and the replay-protection check below.
Real JSON payload structure
An incoming pay-in notification gives your system the key information it needs to identify and process a completed payment. This usually includes the transaction reference, payment status, amount, currency, payment method, and the date and time when the transaction was processed.
The transaction object contains either a bank_transfer sub-object or a credit_card sub-object – never both at the same time. Which one appears depends on the payment method.
Use the top-level order.payment_method_type field (OPEN_BANKING or CC) to determine which sub-object to expect.
The notification can also contain payer and beneficiary details, account identifiers, and references that help match the incoming payment with the correct customer, invoice, or order.
Once the notification reaches your system, you can use these details to update the payment status automatically, trigger reconciliation, and continue the relevant business process without a manual payment check.
The exact fields and structure depend on the provider’s API, so developers should always use the current API documentation as the source of truth when setting up the integration.
Securing your endpoint with signature verification (webhook payload verification)
Your webhook URL is a public HTTP endpoint. This means anyone can send a fake HTTP POST request to your server posing as your payment provider.
This is why webhook payload verification exists. Webhook payload verification uses cryptography to prove that the request actually originated from the payment provider and was not forged or altered in transit before your application acts on it.
How HMAC signature verification payment works
The standard mechanism is an HMAC (hash-based message authentication code) computed over the raw request body using a shared secret key known only to you and the provider.
For webhook signature verification, the HMAC signature verification requires the payment gateway to hash the payload with that secret and send the result in a header (commonly the “X-Signature”, or a provider-specific header). The merchant’s server recomputes the hash independently using the raw request body and compares the two values.
This is the core of webhook signature verification. Skipping this check means trusting any unverified request that hits your endpoint. If you skip it, your platform will be open to spoofed transactions and fraudulent orders.
Step-by-step signature validation logic
Signature validation helps confirm that a webhook request really came from the expected sender and that its content was not changed in transit by somebody else.
The process starts with the raw request body. It is important to use the raw request data exactly as received, because even small changes in formatting can produce a different cryptographic result.
Next, extract the signature from the incoming request headers and generate the expected hash locally using your integration secret and the raw payload.
At Genome, we use four mandatory headers in every callback. You can find those and an example of usage in our documentation.
Header | Description |
User-Agent | Custom user agent data |
Content-Type |
|
X-Version | Callback version. Current value is 1. |
X-Signature | HMAC signature for callback. Hexadecimal digit |
Once calculated, compare your local hash against the header signature. To do it, use a constant-time comparison method to protect against timing-side-channel attacks. If everything matches, you’re good to proceed. If you see a mismatch, reject the request immediately with an HTTP 400 or 403 status.
Two things often cause webhook verification problems. First, your middleware may parse the request body into JSON before your webhook handler receives it. This can change the original data, so the signature no longer matches.
Second, comparing signatures with “===” is not recommended – use a time-constant string comparison function instead.
Open an account
in Genome online
Preventing replay attacks with timestamps
But the problem with a valid signature is that it only proves the payload wasn’t altered at any stage by someone- it doesn’t prove the request is fresh.
For example, if criminals intercept a genuine webhook, they can replay that exact request later, and it will still pass signature verification.
You need a timestamp check alongside the HMAC. Reject anything where the event timestamp is more than a few minutes old (five minutes is common), which bounds how long a captured request stays usable.
Build the timestamp into what you sign over, not just the body, so an attacker can’t strip an old timestamp and swap in a fresh one without invalidating the signature.
Best practices for reliable webhook handling
Implementing idempotent event handling
A webhook isn’t a one-time message you can safely process and forget. Payment providers may send the same event more than once, retry delivery if your server doesn’t respond quickly, or resend the event after a temporary failure.
Duplicate deliveries for the same event are expected behavior, not a bug for every major payment provider. A payment gateway webhook integration that isn’t idempotent will double-credit a balance or fulfill an order twice if a delivery is retried after a slow response.
What you should do: the most obvious solution is to store the eventId from every processed webhook, and before doing any work, check whether that ID has been seen already. If it has, return 200 immediately without reprocessing – one cheap check that eliminates an entire class of double-processing bugs.
Responding with fast HTTP 200 OK status codes
Keep your webhook endpoint quick. If the provider doesn’t get a successful response in time, it may send the same event again. Save the event or put it in a queue, return 200 OK, then handle tasks such as updating the order, recording the payment, or sending a notification in the background.
Your system should also recognize duplicate events so it doesn’t process the same payment twice.
Error handling and automated retry policies
When your endpoint fails to respond or returns an error status (such as a 5xx server error or a request timeout), payment providers fall back to exponential backoff. This mechanism increases the delay between retry attempts, often spanning a 24- to 72-hour window, ensuring that a brief server outage does not result in lost transaction events.
To handle retries effectively, your integration should assume that webhooks may be delivered more than once or arrive with delays. Keep your request processing idempotent so retries remain harmless, log every delivery attempt alongside its unique event-id, and ensure failed events are tracked for easy investigation and debugging.
Integrating real-time notifications with Genome
Genome provides developers with an API payments suite designed to handle end-to-end payment workflows, from incoming pay-in webhooks to automated payouts.
Benefit from our webhooks – get notified when incoming payments arrive. Receive instant HTTP callbacks the moment funds clear into your account. Webhooks eliminate manual status polling, making it simple to trigger real-time balance updates and plan your operations accordingly!
To access integration guides, request parameters, and header specifications, visit the Genome developer portal and explore our complete API documentation.
Unlock Genome’s business services, including SEPA Instant andd Credit Transfers, international SWIFT transfers for business, merchant accounts, and more.





