Demivolt logo

Avoid 20 Retries: Payment Webhooks for Lithuanian Developers

Published 30 September 2026

Developer-first, Lithuania-ready reference for payment webhooks. Verify V2 signatures, build idempotent receivers, handle retries (≈20 attempts/48h), and...

Avoid 20 Retries: Payment Webhooks for Lithuanian Developers

A payment webhook is an HTTPS POST that a payment platform sends to your server the moment a transaction event happens, carrying a JSON payload with the event type, data, and timestamp. Because delivery is at-least-once, the single rule that matters most is this: build an idempotent receiver that verifies the signature, deduplicates by request ID, and returns HTTP 2xx fast.


TL;DR:

  • Building an idempotent webhook handler that verifies signatures and deduplicates based on request ID is essential due to at-least-once delivery.
  • Endpoints must be exposed with valid HTTPS, accept only POST requests with correct headers, and respond quickly with appropriate status codes for success or errors.
  • Signature validation should bind timestamp, request ID, and raw body to prevent replay attacks, accepting only requests within a five-minute window.
  • Retry intervals follow an exponential backoff up to six hours, and responses should be carefully managed to distinguish transient errors from permanent failures.
  • Securing endpoints involves IP whitelisting, rate limiting, secret rotation, and detailed logging of rejection reasons to prevent abuse and detect attacks.

DemivoltBuild Payment Flows With More ControlDemivolt provides compliant business banking and payment infrastructure for companies managing SEPA, SWIFT, cards, and cross-border operations.Explore Demivolt

Table of Contents

How webhooks work for payments

When an event occurs (a payment confirms, a card transaction fails, a KYC check completes) the sender fires an HTTPS POST to your registered endpoint with Content-Type: application/json. The body follows a predictable envelope: { event, data, timestamp }, giving you the event name, the object it affects, and when it happened.

The real integration work happens in the headers, not the body. According to the UR webhook developer reference, you should expect to parse:

  • X-Webhook-Request-Id: a UUID v4 that uniquely identifies this delivery attempt, used for deduplication.
  • X-Webhook-Timestamp: when the sender dispatched the request, used for signature binding and replay checks.
  • X-Webhook-Event-Type: the event name, letting you route payloads without parsing the body first.
  • X-Webhook-Attempt: the retry count, useful for logging and alerting.

Because delivery is at-least-once, every one of these webhooks may arrive more than once. Your endpoint needs to treat a duplicate delivery as normal, not as an error condition.

Endpoint requirements for payment processing

Before a payment platform will deliver reliably, your endpoint needs to meet a short list of conditions. Get these wrong and you will see silent failures or unnecessary retries clogging your logs.

  1. Expose a public HTTPS endpoint with a CA-signed TLS certificate; self-signed certificates are typically rejected outright.
  2. Accept POST requests only, validate the Content-Type header, and reject anything that does not match the expected envelope.
  3. Respond with HTTP 2xx within the sender’s timeout window, commonly around 30 seconds, to mark the delivery as successful.
  4. Return 4xx for permanent errors (malformed payload, unknown event) so the sender does not waste retries on a request that will never succeed.
  5. Return 5xx for transient errors (database unavailable, downstream timeout) so the sender knows to retry later.
  6. Process asynchronously: acknowledge the request immediately, then hand the payload to a queue or background worker for the actual business logic.

Pro Tip: Separate your “receive and acknowledge” code path from your “process the event” code path. The former should do almost nothing except validate the signature and enqueue the payload.

Security: signature verification and replay protection

Signature verification is what stops an attacker from forging payment confirmation events. The UR webhook developer reference recommends binding the timestamp, request ID, and raw body into a single signed message rather than signing the body alone, since a message signed on the body only can be replayed with a different timestamp.

  • Construct the signing string as timestamp.request_id.body and compare it against the signature delivered in the X-Api-Signature-V2 header.
  • Accept the V2 signature scheme where available since it binds all three components and closes the replay window that a body-only signature leaves open.
  • Reject any request whose timestamp falls outside a plus or minus 5 minute window, a common replay-protection threshold cited in the UR webhook developer reference.
  • When signature validation fails, log the failure with its reason code and return a 4xx response so the sender does not keep retrying a request that will never verify.

Binding the timestamp and request ID into the signed payload, rather than signing the body in isolation, is the practice that closes most replay attack vectors according to the UR webhook developer reference. Treat any receiver that skips this binding as incomplete.

Delivery and retry semantics you should expect

Payment webhooks are delivered at-least-once, and the sending platform assumes your endpoint may be briefly unavailable. The UR webhook developer reference describes a jittered exponential backoff with a base delay around 30 seconds, a cap near 6 hours, and roughly 20 attempts spread across about 48 hours before a message is marked dead.

  • Retryable responses: any 5xx status, network errors, connection timeouts, and the 408 and 429 status codes.
  • Non-retryable responses: most other 4xx codes, or any response carrying an X-Webhook-No-Retry: true header.
  • After the final attempt, the message typically moves to a dead-letter state and is persisted for manual or automated requeue.
  • Log the X-Webhook-Attempt header on every request so you can correlate attempt count with latency and diagnose whether failures sit on your side or the sender’s.

Simulating this backoff schedule in a staging environment before launch will save you from discovering it during a live incident.

Idempotency, ordering, and deduplication patterns

Retries mean your processing logic must produce the same result whether it runs once or five times. The safest anchor is the request ID delivered in X-Webhook-Request-Id: store it with a unique constraint and reject or ignore any insert that collides.

  • Dedupe on the request ID using a unique database constraint rather than an application-level check, which closes race conditions under concurrent delivery.
  • Add a logical uniqueness constraint on (event_type, partner_id, business_key) as a second line of defense when a sender ever reuses or regenerates a request ID.
  • Do not assume global ordering: delivery may be ordered per partner but interleaved across partners, so handlers need to tolerate out-of-sequence events for unrelated resources.
  • Common patterns include upserting by idempotency key, wrapping the dedupe check and business update in a single database transaction, and using a short-lived cache to reject duplicate deliveries before they touch the database.

Pro Tip: Store the request ID and its processing outcome, not just a boolean flag. When you need to debug a disputed transaction six months later, that record is your audit trail.

Common payment event types and their payloads

Payment platforms typically expose event names such as transaction_v2, card_spending_failed, payment_confirmed, and a family of KYC events prefixed fma.*. Subscribing only to the events your system actually consumes keeps your processing surface smaller and your logs easier to read.

  • Persist the event ID, timestamp, and event type on every record so you can reconcile against the sender’s own event log later.
  • Store amounts in minor units (cents, not decimal currency) alongside the currency code to avoid rounding drift.
  • Track status and direction fields consistently, since a payment can move through several statuses before settling.
  • Watch for type inconsistencies: some fields arrive as strings in one event and numbers in another, so normalize at the parsing boundary, not deep inside business logic.
Field Typical type Purpose
event string Identifies the event name, e.g. payment_confirmed
data.amount integer (minor units) Transaction amount without decimal rounding
data.currency string (ISO code) Currency the amount is denominated in
data.status string Current lifecycle status of the transaction
timestamp string (ISO format) When the sender generated the event

Testing and debugging webhooks effectively

A webhook receiver that has never seen a retry, a duplicate, or a signature failure is not tested. Before going live, run your endpoint through a deliberate set of failure scenarios.

  1. Expose your local server with a tunnel such as ngrok or localtunnel and confirm signature verification passes against a real delivery.
  2. Replay the same request ID twice and confirm your handler produces one database change, not two.
  3. Return a 5xx deliberately and confirm the sender retries on the schedule your monitoring expects.
  4. Send a payload with a stale timestamp and confirm your receiver rejects it as a replay rather than processing it.
  5. Watch your metrics dashboard for attempts per message, dead-letter queue size, and signature failure rate during the test run.

Pro Tip: Keep a small library of recorded payloads, including at least one of every event type you subscribe to, so a schema change on the sender’s side surfaces in a test run instead of production.

How Demivolt supports webhook-driven payment integrations

A regulated European fintech platform can provide businesses with dedicated IBANs, SEPA and SWIFT payment rails, and card programs tied to event-driven infrastructure. Integrating typically means four steps: register your endpoint, choose which events to subscribe to, implement signature verification and idempotent processing, and request sandbox access to test before going live. Review the payments product page for the specifics of the payment event stream.

Securing endpoints beyond signature checks

Signature verification stops forged payloads, but it will not stop an attacker from flooding your endpoint with junk traffic or probing it for weaknesses. Two additional controls close most of the remaining gap.

IP whitelisting restricts inbound connections to the sender’s published IP ranges, so even a request with a stolen or guessed URL gets dropped at the network layer before it reaches your application code. This is a coarse filter, not a replacement for signature checks, since IP ranges can occasionally shift and need periodic review.

Rate limiting protects your infrastructure from being overwhelmed, whether by a misbehaving retry storm or a deliberate attack. Cap requests per second per source and return a 429 when the limit is exceeded, since 429 is one of the status codes senders treat as retryable rather than a hard failure.

Beyond these two controls, keep your webhook secret rotated on a schedule, store it in a secrets manager rather than in code, and restrict which services inside your infrastructure can read it. Log every rejected request with enough detail (source IP, missing header, failed check) to distinguish an attack pattern from a misconfiguration on your own side. None of these controls is exotic. What matters is applying all of them together, since signature verification alone leaves the transport and infrastructure layers unprotected.

Handling webhook failures and setting up alerts

A webhook failure that nobody notices for three days is worse than one that pages someone at 2 AM. Build monitoring around three numbers: attempts per message, dead-letter queue size, and signature failure rate, all of which the UR webhook developer reference flags as core operational signals.

Webhook failure metrics and retry timeline

A rising attempt count on a specific message usually means your endpoint is returning 5xx when it should return 2xx, often from a downstream dependency timing out. A growing dead-letter queue means messages are exhausting their retry budget, roughly 20 attempts over 48 hours, without ever succeeding, which points to a bug rather than transient flakiness. A spike in signature failures often means a secret rotation went out of sync between sender and receiver.

Set alert thresholds on all three rather than waiting for a customer to report a missing payment confirmation. Route dead-letter messages to a queue your team can inspect and manually requeue once the underlying issue is fixed, rather than discarding them. Pair this with a runbook: who gets paged, what the first three diagnostic steps are, and how to safely replay a batch of failed deliveries without reprocessing ones that already succeeded. The goal is to turn a webhook outage into a known, bounded incident instead of a mystery someone discovers during reconciliation.

Privacy and compliance considerations for payment data

Payment webhooks routinely carry personal and financial data: transaction amounts, account identifiers, sometimes KYC status changes. Treat the payload itself as sensitive data from the moment it lands on your server, not just the database row you eventually write it into.

Encrypt payloads at rest and in transit, and restrict which internal services can read the raw webhook body versus a redacted summary. Log the metadata you need for debugging (event type, request ID, timestamp) without logging full payment details in plaintext application logs, since logs are often retained longer and reviewed by more people than production databases. Apply the same data minimization principle to what you store long term: keep what your business logic and audit trail require, and avoid keeping full payloads indefinitely if you do not have a defined retention reason.

Webhook payload redaction and retention flow

KYC-related events deserve particular care given their sensitivity. If your integration consumes onboarding or verification events, our guide on KYC in banking covers how these checks fit into a broader compliance workflow. Whatever your jurisdiction, confirm your data handling aligns with your local data protection framework before you store or forward payment webhook payloads to a third party, since obligations vary by market and by the type of data involved.

Integrating webhooks with payment platforms in practice

The shape of a webhook integration is consistent across payment platforms even when field names differ. You register an endpoint, select the events you want, verify signatures on arrival, and process asynchronously.

Plugin-based integrations, such as the OpenCart integration documented by Cost+, typically walk through server prerequisites, supported payment methods, and a step-by-step setup that mirrors the checklist earlier in this guide: HTTPS endpoint, credential configuration, and a test transaction before going live. Product platforms that expose broader event catalogs, illustrated by developer resources such as Wink Travel’s public repositories, often document dozens of event types with example payloads, which is worth reviewing even if you only plan to subscribe to a handful at launch.

For a narrower reference on request and response shapes specific to payment APIs, our payment API integration guide walks through the request and response patterns you will encounter when connecting a payments API to your own systems. Whichever platform you integrate with, the fundamentals stay the same: idempotent handlers, verified signatures, and asynchronous processing behind a fast acknowledgment.

What actually matters once you are in production

Most webhook outages trace back to skipping one of three things: signature verification, idempotent processing, or basic observability. Automate dead-message replay so a bad deploy does not turn into a weekend of manual reconciliation. Fast acknowledgment matters, but only if the background processing behind it is just as disciplined.

— dd

Demivolt: integration-ready infrastructure for payment teams

Building a webhook receiver is only half the integration. The other half is the account and payment infrastructure generating those events in the first place, and that is where a regulated banking partner matters. A regulated fintech provider can offer dedicated IBAN accounts, SEPA and SWIFT payment processing, and card programs that plug into an event-driven workflow, enabling teams to focus integration time on business logic rather than compliance details.

Demivolt

If you are evaluating infrastructure for a payments integration, here is where to start:

  • Review the business accounts product page for dedicated IBANs and multi-account structures.
  • Check the payments page for SEPA and SWIFT processing details relevant to your webhook event subscriptions.
  • Look at card programmes if your integration needs to react to card authorization or spending events.
  • Explore Banking-as-a-Service if you are building a white-label product on top of regulated infrastructure.

Visit Demivolt to open an account or request sandbox access for your integration.

FAQ

What is the difference between a webhook and a polling API?

A webhook pushes payment events to your server the moment they happen, while polling means your server repeatedly asks the API whether anything changed. Webhooks reduce latency and unnecessary requests, but they require your endpoint to be reachable and to handle retries correctly.

Why do payment webhooks arrive more than once?

Payment platforms use at-least-once delivery, so a webhook is retried whenever the sender does not receive a timely 2xx response. Your receiver needs to deduplicate using the request ID header rather than assuming each delivery is unique.

How do I verify that a webhook actually came from the payment platform?

Verify the signature by reconstructing the signed message from the timestamp, request ID, and raw body, then comparing it against the signature header, a method the UR webhook developer reference recommends specifically because it closes replay attack windows. Reject any request with a timestamp older than roughly 5 minutes even if the signature otherwise checks out.

What should my endpoint return to stop unnecessary retries?

Return a 2xx status as soon as you have safely queued the event, and return a 4xx for a payload you will never be able to process, such as an unknown event type. Reserve 5xx for genuine transient failures, since that is what tells the sender to retry rather than give up.

Does Demivolt provide webhook notifications for payment events?

Demivolt provides integration-ready payment APIs as part of its payments product, built for businesses that need SEPA and SWIFT processing tied to their own systems. Specific event subscription details are best confirmed directly through Demivolt’s developer onboarding process.