A delivery is not a business action
A webhook is a message about something that happened elsewhere. Treating every delivery as permission to repeat a business action creates a subtle bug: the same event can create two orders, send two emails or credit an account twice.
Separate receiving a message from applying its effect. For example, an invoice event might first become a durable inbox record. A worker can then decide whether that event should change the invoice. This gives the application somewhere to record progress and somewhere to restart after a failure.
Design a small event inbox
An inbox record can contain the provider, event identifier, received time, processing state and an attempt counter. Keep the event payload only when there is a clear operational reason, with an appropriate retention policy.
Use a database uniqueness constraint on the provider and event identifier. Two application instances can receive the same delivery simultaneously; a uniqueness constraint resolves that race more reliably than checking a JavaScript variable.
A useful state model is received, processing, completed and failed. A failed event needs an explanation that an engineer can act on, rather than a permanent processing label that nobody notices.
Acknowledge after a durable handoff
Verify the sender using the provider's documented signature procedure. For Stripe, verification requires the raw request body. Once the verified event has been durably accepted, respond promptly and move expensive processing out of the request path.
A successful HTTP response should mean the handoff succeeded. It should not mean that an untracked promise was started and might disappear when the serverless invocation ends.
Make the effect safe as well
Deduplicating the inbox is only half the design. Imagine a worker updates an order and then crashes before recording completion. A later attempt must not perform an irreversible action again.
Tie the state change and completion record together where a database transaction can do so. For external side effects, use a stable operation identifier and the downstream service's idempotency mechanism when available. Decide how operators will reconcile uncertain outcomes.
Test the awkward sequence
Send the same event twice, deliver two events out of order, and interrupt processing after the first database update. Check the resulting business records, not just the HTTP status. The test passes when the system converges on one correct result and leaves enough evidence to explain what happened.
