Tutorial January 17, 2026

How to Debug Stripe Webhooks on Localhost (The Easy Way)

Stop re-triggering test payments in the Stripe dashboard. Learn how to capture a webhook once and replay it as many times as you need while debugging locally.

8 min read

If you've ever debugged a Stripe webhook, you know the pain. You set up ngrok, configure your endpoint in the Stripe dashboard, trigger a test payment, wait for the webhook to arrive... and then realize you need to add a log statement. So you trigger another payment. And another. And another.

Three hours later, you've triggered 47 test payments, your ngrok session has restarted twice (giving you a new URL each time), and you still haven't figured out why your code isn't handling the payment_intent.succeeded event correctly.

There's a better way.

The Problem with Traditional Webhook Debugging

The traditional approach to debugging webhooks has several pain points:

  • You can't reproduce the exact payload — Each test webhook has different IDs, timestamps, and sometimes slightly different data
  • Tunnel URLs change — Free ngrok sessions give you a new URL on restart, requiring you to update Stripe's dashboard
  • It's slow — Creating a test payment, waiting for the webhook, checking your logs, making a change, repeat
  • No breakpoint debugging — By the time you set a breakpoint, the webhook has already timed out
The core issue

You're debugging a race condition between your code and an external service. You need to control when the webhook arrives, but with traditional methods, you can't.

The Capture-and-Replay Approach

What if instead of triggering a new webhook every time, you could capture one and replay it as many times as you need?

That's the idea behind HookReplay. Here's how it works:

  1. Capture — Point Stripe at a HookReplay URL. When a webhook arrives, it's stored.
  2. Inspect — View the full payload, headers, and metadata in the dashboard.
  3. Replay — Send that exact webhook to your localhost, as many times as you need.

This means you trigger the test payment once, capture the webhook, and then replay it 100 times while you debug your code. Set breakpoints. Add log statements. Fix bugs. All without waiting for Stripe to send another event.

Step-by-Step: Debugging Stripe Webhooks with HookReplay

Let's walk through a complete example.

Create a HookReplay account and endpoint

Sign up at hookreplay.dev (free tier available). Create a workspace for your project, then create a webhook endpoint. You'll get a unique URL like:

https://hookreplay.dev/hook/abc123xyz

Configure Stripe to send webhooks to HookReplay

In the Stripe Dashboard, go to Developers → Webhooks → Add endpoint.

Paste your HookReplay URL and select the events you want to receive:

  • payment_intent.succeeded
  • payment_intent.payment_failed
  • checkout.session.completed
  • ...or any other events you're working with

Trigger a test webhook from Stripe

You have two options:

  • Use Stripe's test event button — In the webhook details, click "Send test webhook"
  • Create a real test payment — Use Stripe's test mode with card 4242 4242 4242 4242

The webhook will be captured and appear in your HookReplay dashboard.

Install and connect the CLI

Install the HookReplay CLI:

npm
npm install -g hookreplay

Get an API key from your account settings, then connect:

terminal
$ hookreplay

● hookreplay> config api-key hr_xxxxxxxxxxxx
✓ API key saved!

● hookreplay> connect
✓ Connected! Waiting for replay requests...

Replay the webhook to localhost

In the HookReplay dashboard, find the captured webhook and click Replay.

Enter your local endpoint URL (e.g., http://localhost:3000/api/stripe/webhook) and click Send.

The CLI will forward the exact webhook payload to your local server:

terminal
● hookreplay> 
↳ Replaying payment_intent.succeeded to http://localhost:3000/api/stripe/webhook
✓ 200 OK (45ms)

Debugging with Breakpoints

Here's where the magic happens. Because you control when the webhook is replayed, you can:

  1. Set a breakpoint in your webhook handler
  2. Start your server in debug mode
  3. Click "Replay" in HookReplay
  4. Step through your code at your own pace

No more racing against Stripe's timeout. No more adding console.log statements and re-triggering the event. Just normal, comfortable debugging.

Pro tip: Edit the payload

Want to test what happens when a field is null or has an unexpected value? Edit the payload before replaying. Perfect for testing edge cases without waiting for them to happen organically.

Testing Edge Cases

One of the most powerful features of the capture-and-replay approach is testing edge cases. With traditional webhook debugging, you're limited to whatever data Stripe sends you. With HookReplay, you can modify the payload before replaying.

Want to test:

  • What if the amount is zero? — Edit amount_received to 0
  • What if a field is missing? — Remove the field from the JSON
  • What if there are multiple line items? — Add more items to the array
  • What if the customer is null? — Set customer to null

This is especially useful for testing error handling. How does your code behave when Stripe sends unexpected data? Now you can find out without waiting for production bugs.

Handling Signature Verification

Stripe webhooks come with a signature header (Stripe-Signature) that you should verify in production. During development, you have options:

Option 1: Disable verification in development

Many developers disable signature verification in their local environment. This is fine for development as long as you enable it in production.

javascript
// Node.js example
const event = process.env.NODE_ENV === 'production'
  ? stripe.webhooks.constructEvent(body, sig, webhookSecret)
  : JSON.parse(body); // Skip verification in development

Option 2: Use Stripe's test mode webhook secret

When you use Stripe's "Send test webhook" button, it uses your test mode webhook secret. You can use this secret locally to verify signatures on test webhooks.

Important

Never disable signature verification in production. It protects against attackers sending fake webhook events to your endpoint.

Common Stripe Webhook Issues and How to Debug Them

Issue: "No such customer" error

You receive a webhook with a customer ID, but when you try to fetch the customer from Stripe, you get an error.

Cause: You're using your live mode API key but the webhook was sent from test mode (or vice versa).

Debug with HookReplay: Inspect the webhook payload. Check if the IDs start with cus_ (which they should). Then check which API key your code is using.

Issue: Duplicate processing

Your code runs the same logic multiple times for a single payment.

Cause: You're not implementing idempotency. Stripe may send the same webhook multiple times.

Debug with HookReplay: Replay the same webhook 5 times in a row. Does your code handle it correctly? It should only process the first one and ignore duplicates.

Issue: Missing event types

Your webhook handler works for payment_intent.succeeded but fails silently for checkout.session.completed.

Debug with HookReplay: Capture both event types and replay them. Step through your code to see where the second event type gets dropped.

Summary

Debugging Stripe webhooks doesn't have to be painful. By capturing webhooks and replaying them on demand, you can:

  • Debug with breakpoints at your own pace
  • Test edge cases by editing payloads
  • Save hours of development time
  • Keep your sanity intact

The capture-and-replay approach works for any webhook, not just Stripe. GitHub, Shopify, Twilio, Paddle — if it sends webhooks, you can capture and replay them.

Ready to stop chasing webhooks?

Try HookReplay free and save hours on your next webhook debugging session.

Get Started Free
HR

HookReplay Team

We're building the fastest way to debug webhooks on localhost. Follow us on Twitter for tips and updates.