Stripe

Updated May 02, 2026

Overview

Stripe is the most common provider Splithook is used with. Stripe's HMAC-SHA256 signing scheme includes a timestamp, which means replays break signature verification unless the timestamp is refreshed — exactly what Splithook's re-signing handles.

Configure the webhook in Stripe

  1. Open the Stripe Dashboard → Developers → Webhooks.
  2. Click Add endpoint.
  3. Paste your Splithook endpoint URL: https://splithook.com/e/{slug}.
  4. Select the events you want to receive (or "Receive all events").
  5. Click Add endpoint.

Stripe immediately starts sending events to Splithook.

Add the signing secret

  1. In the Stripe Dashboard → Webhooks → your endpoint → Reveal (Signing secret).
  2. Copy the value (starts with whsec_).
  3. In Splithook → Settings → Signing secrets → New.
  4. Provider: Stripe. Paste the secret. Save.
  5. In your destination settings, set Signing mode to Re-sign and select this secret.
Use a different secret for each environment

Your staging handler should have its own whsec_ secret — different from the one Stripe uses. This is the secret you upload to Splithook for re-signing.

Verify signatures in your handler

Use the official Stripe SDK — it handles the timestamp tolerance and signature comparison:

// Node.js
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }
  // handle event...
  res.json({ received: true });
});
# Python
import stripe
event = stripe.Webhook.construct_event(payload, sig_header, os.environ['STRIPE_WEBHOOK_SECRET'])
// PHP
$event = \Stripe\Webhook::constructEvent($payload, $sigHeader, $_ENV['STRIPE_WEBHOOK_SECRET']);

How Splithook re-signs Stripe events

The stripe-signature header format:

stripe-signature: t=1714900000,v1=abc123...,v0=legacy...

On replay, Splithook:

  1. Replaces t with Math.floor(Date.now() / 1000).
  2. Recomputes HMAC-SHA256(secret, "{new_t}.{body}").
  3. Sends stripe-signature: t={new_t},v1={new_v1} (drops legacy v0).

Your handler's 5-minute tolerance window resets. The payload is unchanged.

Filtering by event type

Stripe sends the event type in body.type. Use it in your destination filter:

body.type == 'charge.failed'
body.type matches '^payment_intent\.'
body.type in ['invoice.paid', 'invoice.payment_failed']

For live-mode-only forwarding:

body.livemode == true

Testing with Stripe CLI

The Stripe CLI can forward test events directly to your Splithook endpoint:

stripe trigger charge.failed --stripe-account=acct_xxx \
  --override charge:capture_method=manual \
  | curl -X POST https://splithook.com/e/{slug} \
    -H "Content-Type: application/json" \
    -d @-

Or use the Stripe Dashboard → Webhooks → Send test webhook.