Skip to content
Webhooks SDK
Esc
navigateopen⌘Jpreview
On this page

Stripe

HMAC over timestamp + body via Stripe-Signature — replay-safe, with first-class secret rotation.

Stripe signs each delivery with HMAC-SHA256 over {timestamp}.{body}, hex-encoded in the Stripe-Signature header (scheme family 2). The timestamp is inside the signed material, so Stripe is replay-safe on its own.

import { createWebhookHandler } from 'webhooks-sdk'
import { stripe } from 'webhooks-sdk/stripe'

const handler = createWebhookHandler({
  provider: stripe({ secret: process.env.STRIPE_WEBHOOK_SECRET! }),
  on: {
    'payment_intent.succeeded': async (event) => {
      await fulfill(event.payload.data.object)
    },
    'customer.subscription.deleted': async (event) => {
      await revoke(event.payload.data.object)
    },
  },
})

export const POST = handler.fetch

The secret is the whsec_… value shown when you create the endpoint in the Stripe dashboard (or via the API). Each endpoint has its own — Connect and Issuing endpoints too, so configure one provider per endpoint secret.

Options

Option Type Default
secret string | string[] Endpoint signing secret(s). Pass an array during rotation.
tolerance number 300 Replay window in seconds. 0 disables it.

Rotating secrets

Stripe keeps the previous secret valid for 24 hours after you roll it. Deploy with both during that window:

stripe({ secret: [process.env.STRIPE_SECRET_NEW!, process.env.STRIPE_SECRET_OLD!] })

The header can also carry multiple v1= signatures; all candidates are checked against all secrets. See Secret rotation.

The envelope

  • event.id — Stripe’s event id (evt_…), the natural idempotency key.
  • event.type — the body’s type (payment_intent.succeeded, …). Common event names autocomplete; any string routes.
  • event.timestamp — from the body’s created.
  • event.payload — the full Stripe event object; your data is at payload.data.object.

Standalone & testing

import {
  verifyStripeWebhook,   // (raw, { secret, tolerance? }) — throws on failure
  parseStripeWebhook,    // (raw) — the envelope
  signStripeWebhook,     // (body, secret, timestamp?) — a valid header value, for tests
} from 'webhooks-sdk/stripe'

See Standalone verification and Testing.

Was this page helpful?