← Back to blog

2026-01-28 · 6 min · mzapayments team

Webhook idempotency: handling duplicate payment events safely

Why providers retry deliveries, how to verify signatures, and patterns to keep ledger updates exactly once.

Payment webhooks are at-least-once deliveries. Networks, load balancers, and your own retry logic can cause the same event to arrive twice. Your integration should assume duplicates, not treat them as bugs.

Verify before you mutate. Validate the signature or shared secret configured on your webhook endpoint before parsing the body into business logic. Reject unsigned test traffic in production environments.

Use a stable event identifier. Store the provider event id (or a hash of type + payment id + status) in a deduplication table. If you have already processed that id, return HTTP 200 without changing balances.

Make handlers small and ordered. Separate “record raw event” from “update invoice status” from “fulfill the order.” Each step should be safe to retry independently.

Respond quickly. Acknowledge within a few seconds; defer heavy work to a queue if needed. Slow endpoints cause unnecessary retries that amplify duplicate volume.

Test failure modes in sandbox. mzapayments lets you register webhook URLs per environment and inspect delivery attempts from the merchant dashboard. Simulate success, slow responses, and intentional 500 errors to confirm your idempotency keys hold.

When you promote to live traffic, keep sandbox endpoints for regression tests. The best webhook integrations look boring in production because duplicates are a solved case.