Integration Guide

Stripe Webhook Integration Guide

Complete guide to integrating Stripe webhooks with signature verification, event handling, and production deployment. Includes code examples for Node.js, Python, and more.

Updated: January 17, 202612 min read

Why Use Stripe Webhooks?

Stripe webhooks notify your application about payment events in real-time. Instead of polling the Stripe API, webhooks push updates to your server when:

✓ Payment succeeds or fails

Immediately update order status and send confirmations

✓ Subscription changes

Handle renewals, cancellations, and upgrades

✓ Disputes and refunds

Respond to chargebacks and process refunds

✓ Customer updates

Sync customer data and payment methods

Prerequisites

A Stripe account (sign up at stripe.com)
A server with an HTTPS endpoint (required for production)
Node.js, Python, or your preferred backend language

Step 1: Create a Webhook Endpoint

First, create an endpoint that accepts POST requests. Here's an example using Express.js:

Node.js / Express.js

const express = require('express');
const app = express();

// IMPORTANT: Use raw body for signature verification
app.post('/webhooks/stripe',
  express.raw({type: 'application/json'}),
  async (req, res) => {
    const sig = req.headers['stripe-signature'];
    const payload = req.body;
    
    try {
      // We'll add verification in the next step
      console.log('Received webhook:', payload);
      res.sendStatus(200);
    } catch (err) {
      console.error('Webhook error:', err.message);
      res.sendStatus(400);
    }
  }
);

app.listen(3000, () => console.log('Server running on port 3000'));

Critical: Use Raw Body

You must use express.raw() instead of express.json() for the webhook endpoint. Stripe's signature verification requires the raw request body.

Step 2: Verify Webhook Signatures

Always verify webhook signatures to ensure requests come from Stripe. Install the Stripe SDK:

npm install stripe

Signature Verification

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;

app.post('/webhooks/stripe',
  express.raw({type: 'application/json'}),
  async (req, res) => {
    const sig = req.headers['stripe-signature'];
    let event;

    try {
      // Verify the webhook signature
      event = stripe.webhooks.constructEvent(
        req.body,
        sig,
        endpointSecret
      );
    } catch (err) {
      console.error('Webhook signature verification failed:', err.message);
      return res.sendStatus(400);
    }

    // Signature verified - process the event
    console.log('Verified event:', event.type);
    res.sendStatus(200);
  }
);

Getting Your Webhook Secret

  1. 1. Go to Stripe Dashboard → Webhooks
  2. 2. Click "Add endpoint"
  3. 3. Enter your endpoint URL (e.g., https://yourapp.com/webhooks/stripe)
  4. 4. Select events to listen for
  5. 5. Copy the "Signing secret" (starts with whsec_)
  6. 6. Add to your environment variables as STRIPE_WEBHOOK_SECRET

Step 3: Handle Webhook Events

Process different event types based on your business logic:

Event Handling Example

app.post('/webhooks/stripe',
  express.raw({type: 'application/json'}),
  async (req, res) => {
    const sig = req.headers['stripe-signature'];
    let event;

    try {
      event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
    } catch (err) {
      return res.sendStatus(400);
    }

    // Handle the event
    switch (event.type) {
      case 'payment_intent.succeeded':
        const paymentIntent = event.data.object;
        await handlePaymentSuccess(paymentIntent);
        break;

      case 'payment_intent.payment_failed':
        const failedPayment = event.data.object;
        await handlePaymentFailure(failedPayment);
        break;

      case 'customer.subscription.created':
        const subscription = event.data.object;
        await handleNewSubscription(subscription);
        break;

      case 'customer.subscription.deleted':
        const canceledSub = event.data.object;
        await handleCanceledSubscription(canceledSub);
        break;

      case 'invoice.payment_succeeded':
        const invoice = event.data.object;
        await handleInvoicePayment(invoice);
        break;

      default:
        console.log(`Unhandled event type: ${event.type}`);
    }

    res.sendStatus(200);
  }
);

async function handlePaymentSuccess(paymentIntent) {
  console.log('Payment succeeded:', paymentIntent.id);
  
  // Update order status in database
  await db.orders.update({
    stripePaymentIntentId: paymentIntent.id
  }, {
    status: 'paid',
    paidAt: new Date()
  });
  
  // Send confirmation email
  await sendOrderConfirmation(paymentIntent.metadata.orderId);
}

Common Stripe Webhook Events

payment_intent.succeededPayment completed
payment_intent.payment_failedPayment failed
customer.subscription.createdNew subscription
customer.subscription.updatedSubscription changed
customer.subscription.deletedSubscription canceled
invoice.payment_succeededInvoice paid

View all events: Stripe Event Types

Step 4: Test Webhooks Locally

You have two options for testing Stripe webhooks during development:

Option 1: Stripe CLI

Install Stripe CLI:

brew install stripe/stripe-cli/stripe

Forward webhooks to localhost:

stripe listen --forward-to localhost:3000/webhooks/stripe

Trigger test events:

stripe trigger payment_intent.succeeded

Option 2: hookVM

Create a hookVM endpoint and use the CLI:

hookvm flow listen --endpoint-id ep_xxx --port 3000

Benefits:

  • • Inspect webhook payloads in dashboard
  • • Replay webhooks for debugging
  • • Works with any webhook provider

Step 5: Deploy to Production

1. Deploy Your Endpoint

Deploy your webhook endpoint to a server with HTTPS enabled. Stripe requires HTTPS for production webhooks.

Example URL: https://api.yourapp.com/webhooks/stripe

2. Register in Stripe Dashboard

  1. • Go to Stripe Dashboard → Webhooks
  2. • Click "Add endpoint"
  3. • Enter your production URL
  4. • Select events to listen for (or select "all events" for testing)
  5. • Copy the webhook signing secret
  6. • Add secret to production environment variables

3. Monitor Webhook Deliveries

Monitor webhook deliveries in the Stripe Dashboard. Check for failed deliveries and review error messages. Stripe automatically retries failed webhooks with exponential backoff.

Best Practices

Return 200 quickly

Acknowledge receipt immediately, process asynchronously to avoid timeouts

Make handlers idempotent

Stripe may send the same webhook multiple times - handle duplicates safely

Use metadata for context

Add order IDs or user IDs to Stripe objects to link events to your data

Log all webhook events

Keep audit logs for debugging and compliance

Simplify Stripe Webhook Management

hookVM handles signature verification, retry logic, and provides a dashboard to inspect and replay Stripe webhooks. Focus on your business logic, not infrastructure.