Testing Stripe Webhooks with HookDeploy
Complete guide to capturing, inspecting, and debugging Stripe webhook events using HookDeploy, the Stripe CLI, and signature verification.
HookDeploy Team · May 22, 2026
Stripe sends webhooks for almost every important event — payments, subscriptions, invoices, disputes. This guide walks through setting up HookDeploy as your Stripe endpoint, testing locally, and handling the details that trip up most integrations.
Stripe webhook events overview
Stripe organizes events by resource type. Common ones during development:
| Event | When it fires |
|---|---|
payment_intent.succeeded | A PaymentIntent completes successfully |
payment_intent.payment_failed | A payment attempt fails |
customer.subscription.created | A new subscription starts |
customer.subscription.updated | Plan change, renewal, or status change |
invoice.payment_succeeded | An invoice is paid |
checkout.session.completed | A Checkout Session finishes |
Each event is a JSON POST with an id, type, data.object, and created timestamp. Stripe signs every payload with your webhook signing secret.
Set up HookDeploy as your Stripe endpoint
- Sign up at app.hookdeploy.dev
- Create an endpoint named
Stripe dev - Copy your URL:
https://hookdeploy.dev/h/{slug}
In the Stripe Dashboard:
- Go to Developers → Webhooks
- Click Add endpoint
- Paste your HookDeploy URL
- Select events — start with
payment_intent.succeededandcheckout.session.completed - Click Add endpoint
- Copy the Signing secret (
whsec_...) — you need this for verification
Trigger a test event from Stripe
Send a test event from the Stripe Dashboard:
- Open your webhook endpoint in Stripe
- Click Send test webhook
- Choose
payment_intent.succeeded - Click Send test webhook
Within a second, the event appears in HookDeploy. Open the inspector to see the full payload, headers, and Stripe’s signature header.
Use the Stripe CLI alongside HookDeploy
The Stripe CLI is useful for triggering events without the Dashboard:
# Install Stripe CLI, then login
stripe login
# Forward Stripe events to your HookDeploy URL
stripe listen --forward-to https://hookdeploy.dev/h/YOUR_SLUG
The CLI prints a webhook signing secret (whsec_...) for local verification. Use this secret in your application when testing against CLI-forwarded events.
Trigger specific events:
stripe trigger payment_intent.succeeded
stripe trigger checkout.session.completed
stripe trigger customer.subscription.created
Events flow: Stripe → Stripe CLI → HookDeploy. You inspect in HookDeploy, then replay to your local handler when ready.
Verify Stripe signatures
Never process a webhook without verifying the signature. Stripe sends Stripe-Signature with timestamp and signature:
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
// In your Express handler:
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
request.rawBody, // must be raw bytes, not parsed JSON
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// event is verified — safe to process
console.log(event.type, event.data.object.id);
Handle Stripe retries
Stripe retries failed deliveries for up to 3 days with exponential backoff. If your handler returns non-2xx, Stripe retries.
HookDeploy always returns 200 to Stripe when capturing — your handler is not involved yet. When you forward or replay to your server:
- Return
200quickly after enqueueing work - Process asynchronously for slow operations
- Make handlers idempotent — Stripe may deliver the same event more than once
Check event.id against a processed-events table before acting.
Common Stripe webhook issues
Event not appearing in HookDeploy
- Verify the endpoint URL in Stripe matches your HookDeploy slug exactly
- Check HookDeploy endpoint is not paused
- Confirm you have not hit your plan’s monthly request limit
Signature verification fails
- Using the wrong
whsec_secret (Dashboard secret vs CLI secret) - Body was parsed as JSON before verification — use raw body middleware
- Clock skew — Stripe rejects timestamps older than 5 minutes
Missing fields in payload
- You are looking at a test event — test payloads are simplified
- Use
stripe triggeror real test mode transactions for full objects
Duplicate events
- Normal Stripe behavior on retries. Use idempotency on
event.id
Test with curl
Simulate a minimal Stripe-like POST to your HookDeploy URL:
curl -X POST "https://hookdeploy.dev/h/YOUR_SLUG" \
-H "Content-Type: application/json" \
-H "Stripe-Signature: t=1234567890,v1=test" \
-d '{
"id": "evt_test_webhook",
"type": "payment_intent.succeeded",
"data": {
"object": {
"id": "pi_test_123",
"amount": 2000,
"currency": "usd",
"status": "succeeded"
}
}
}'
This won’t pass signature verification in your handler, but it validates your HookDeploy capture pipeline.
Replay to your local handler
Once you have a captured event in HookDeploy:
- Start your local server (via ngrok or Cloudflare Tunnel)
- Open the request in HookDeploy
- Click Replay and enter your local URL:
https://your-tunnel.ngrok.io/webhooks/stripe
HookDeploy re-sends the original payload. Your handler processes it as if Stripe sent it directly.
Next steps
- Webhook security best practices — signature verification deep dive
- Local webhook testing — ngrok vs forwarding vs replay
- Webhook debugging guide — systematic troubleshooting
Related guides
- Webhook security best practices
Signature verification for Stripe, GitHub, and Shopify. HMAC-SHA256, replay attacks, IP allowlisting, HTTPS, and secret rotation.
- Local webhook testing — four approaches compared
Compare ngrok, Cloudflare Tunnel, HookDeploy forwarding, and VS Code port forwarding for getting webhooks to your local dev server.
- Webhook debugging guide
Systematic approach to debugging webhook issues — delivery logs, signatures, payloads, curl testing, and common HTTP error codes.