Skip to main content
Stripe webhooks allow your application to receive real-time notifications about events in your Stripe account. This guide shows you how to test webhooks during local development.

Webhook Implementation

The API implements webhook handling at /api/webhooks/stripe with signature verification and event processing. Here’s how it works:
// Webhook endpoint with signature verification
const signature = req.headers['stripe-signature'];
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

// Verify webhook signature
const event = stripe.webhooks.constructEvent(req.body, signature, webhookSecret);

// Process different event types
switch (event.type) {
  case 'payment_intent.succeeded':
    // Handle successful payment
    break;
  case 'payment_intent.payment_failed':
    // Handle failed payment
    break;
  case 'refund.created':
    // Handle refund creation
    break;
  case 'charge.refunded':
    // Handle charge refund
    break;
}
The webhook endpoint requires raw request body for signature verification. Make sure your middleware doesn’t parse the body as JSON before the webhook handler receives it.

Testing Methods

There are two primary methods for testing webhooks locally:

Stripe CLI

Official Stripe tool that forwards webhook events to your local server

ngrok

Creates a public URL that tunnels to your localhost

Method 1: Using Stripe CLI

The Stripe CLI is the recommended approach for testing webhooks locally.
1
Install Stripe CLI
2
macOS
brew install stripe/stripe-cli/stripe
Linux
wget https://github.com/stripe/stripe-cli/releases/latest/download/stripe_linux_x86_64.tar.gz
tar -xvf stripe_linux_x86_64.tar.gz
sudo mv stripe /usr/local/bin/
Windows
scoop install stripe
3
Authenticate with Stripe
4
stripe login
5
This will open your browser for authentication.
6
Forward Events to Local Server
7
Start forwarding webhook events to your local application:
8
stripe listen --forward-to localhost:3000/api/webhooks/stripe
9
Expected Output:
10
Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxx (^C to quit)
11
Copy the webhook signing secret and add it to your .env file as STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
12
Trigger Test Events
13
In a new terminal, trigger test events:
14
stripe trigger payment_intent.succeeded
15
You’ll see the event in your application logs:
16
Webhook: payment_intent.succeeded pi_xxxxxxxxxxxxx

Method 2: Using ngrok

ngrok creates a public URL that tunnels to your local server, allowing Stripe to send real webhook events.
1
Install ngrok
2
# macOS
brew install ngrok

# Or download from https://ngrok.com/download
3
Start ngrok Tunnel
4
ngrok http 3000
5
Expected Output:
6
Forwarding  https://abc123.ngrok.io -> http://localhost:3000
7
Configure Stripe Webhook
8
  • Go to the Stripe Dashboard
  • Click “Add endpoint”
  • Enter your ngrok URL: https://abc123.ngrok.io/api/webhooks/stripe
  • Select events to listen to
  • Copy the webhook signing secret
  • Add it to your .env file as STRIPE_WEBHOOK_SECRET
  • 9
    Test with Real Events
    10
    Perform actions in your Stripe Dashboard or through the API, and your local server will receive the webhook events.
    ngrok URLs change each time you restart (on the free plan). Update your Stripe webhook endpoint accordingly.

    Common Webhook Events

    The API handles these webhook events:
    Event TypeDescriptionWhen It Fires
    payment_intent.succeededPayment completed successfullyAfter confirming a payment intent
    payment_intent.payment_failedPayment failedWhen card is declined or has insufficient funds
    refund.createdRefund initiatedWhen a refund is created
    charge.refundedCharge refundedWhen a charge is refunded

    Verifying Webhook Signatures

    The API automatically verifies webhook signatures to ensure events are from Stripe:
    try {
      event = stripe.webhooks.constructEvent(req.body, signature, webhookSecret);
    } catch (error) {
      return res.status(400).json({
        status: false,
        message: `Webhook signature verification failed: ${error.message}`,
      });
    }
    

    Security Best Practice

    Always verify webhook signatures in production to prevent unauthorized parties from sending fake webhook events to your endpoint.

    Error Handling

    Missing Signature

    {
      "status": false,
      "message": "Missing stripe-signature header"
    }
    

    Missing Webhook Secret

    {
      "status": false,
      "message": "Missing STRIPE_WEBHOOK_SECRET in environment variables"
    }
    

    Invalid Signature

    {
      "status": false,
      "message": "Webhook signature verification failed: ..."
    }
    

    Testing Workflow

    1

    Start your local server

    npm start
    
    2

    Start webhook forwarding

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

    Update your .env file

    Add the webhook signing secret from the CLI output
    4

    Trigger test events

    stripe trigger payment_intent.succeeded
    stripe trigger payment_intent.payment_failed
    stripe trigger refund.created
    
    5

    Monitor your logs

    Watch your application console for webhook event logs

    Troubleshooting

    Make sure you’re using the correct webhook secret for your environment. The Stripe CLI generates a different secret than the Stripe Dashboard.
    • CLI secret: Starts with whsec_ (from stripe listen output)
    • Dashboard secret: Starts with whsec_ (from webhook endpoint settings)
    1. Verify your server is running on the correct port
    2. Check that the webhook URL is correct
    3. Ensure no firewall is blocking the connection
    4. For ngrok, confirm the tunnel is active
    The webhook endpoint requires the raw request body. If you’re using body parsing middleware, you may need to configure it to skip webhook routes:
    app.use('/api/webhooks/stripe', express.raw({ type: 'application/json' }));
    

    Next Steps

    Build docs developers (and LLMs) love