Building idempotent payment webhook handlers that survive network retries, race conditions, and duplicate delivery with MongoDB change streams.
Razorpay, like all payment providers, guarantees 'at-least-once' delivery for webhooks. This means the same payment.captured event can arrive multiple times. Without proper handling, this leads to duplicate order fulfillment — a critical business bug.
The solution was to use the Razorpay payment ID as an idempotency key. Before processing any webhook, the handler checks MongoDB for an existing order with that payment ID.
Zero duplicate orders in production across 1,500+ webhook events
All webhook events processed within 500ms average
Webhook handler handles bursts of 50 concurrent retries gracefully