Stripe Webhooks Integration

Complete guide to webhooks, debugging, and real-time event monitoring with HookMetry

Stripe Webhook Integration

Stripe uses HMAC-SHA256 signatures in the Stripe-Signature header with a timestamp prefix to prevent replay attacks.

Critical: Use the Stripe SDK

Never manually verify Stripe signatures. Stripe's verification also checks the timestamp tolerance (5-minute window). The SDK handles this correctly; DIY implementations often get it wrong.

Step 1 — Get Your Webhook Secret

  1. Go to Stripe Dashboard → Developers → Webhooks
  2. Click on your webhook endpoint (or create one)
  3. Click "Reveal" next to Signing secret
  4. Copy the secret — it starts with whsec_

Step 2 — Configure in Hookmetry

  1. Create a new endpoint → Validation Type: Stripe
  2. Paste your whsec_ secret
  3. Copy your generated Hookmetry URL
  4. Add this URL to your Stripe webhook settings under "Webhook endpoints"
  5. Select the events you want to listen to

Step 3 — Verify on Your Server

Install: npm install stripe

const express = require('express');
const stripe  = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app     = express();

// Must use express.raw() — NOT express.json() — for this route
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,                              // raw Buffer
      sig,
      process.env.STRIPE_WEBHOOK_SECRET      // whsec_...
    );
  } catch (err) {
    return res.status(400).send('Webhook error: ' + err.message);
  }

  switch (event.type) {
    case 'payment_intent.succeeded':
      const pi = event.data.object;
      console.log('Payment succeeded:', pi.id, pi.amount / 100, pi.currency);
      break;
    case 'customer.subscription.deleted':
      console.log('Subscription cancelled:', event.data.object.id);
      break;
    default:
      console.log('Unhandled event:', event.type);
  }

  res.status(200).json({ received: true });
});

Common Stripe Event Types

EventWhen it fires
payment_intent.succeededPayment fully collected
payment_intent.payment_failedCard declined or payment failed
customer.subscription.createdNew subscription started
customer.subscription.deletedSubscription cancelled or expired
invoice.payment_succeededSubscription renewal paid
checkout.session.completedCheckout session finished

Common Stripe Signature Failures

  • "No signatures found": Using express.json() globally — body is already parsed when Stripe SDK receives it
  • "Timestamp too old": Server clock drift. Sync server time via NTP.
  • Wrong secret: Using the API key (sk_...) instead of the webhook signing secret (whsec_...)

Was this page helpful?

Your feedback helps us improve the docs.