Skip to main content
Juliano Alves
Back to blog

Stripe webhooks: verification and idempotency

1 min read
By Juliano Alves

Payment providers will retry webhooks; events can arrive out of order. Your handler must be idempotent and verify authenticity before mutating money-moving state.

Signature verification#

Always use the SDK’s constructEvent (or equivalent) with the raw request body and the Stripe-Signature header. JSON parsing first breaks verification.

Idempotency table#

Store event.id in a processed_webhook_events table with a unique constraint. Wrap business logic in a transaction:

  1. Insert event.id (or noop if duplicate).
  2. If insert succeeded, apply domain changes (extend subscription, mark invoice paid).
  3. Commit.

Duplicate delivery returns 200 quickly without double-shipping goods.

Return fast#

Offload heavy work to a queue if processing exceeds provider timeouts; acknowledge with 200 only after you persist the event ID and enqueue work—or use at-least-once semantics your queue understands.

Testing#

Use Stripe CLI stripe listen --forward-to localhost:3000/api/webhooks and fixture JSON for unit tests of your handler’s branching.

Summary#

Treat webhooks as untrusted, duplicated, reordered inputs. Cryptographic verification + idempotent persistence is the minimum bar before touching ledgers or entitlements.

© 2026 Juliano Alves. All rights reserved.