How to Test Stripe Webhooks: Complete Guide
How to inspect Stripe payment events, verify signatures, and test the event flow your backend actually depends on.
Why Stripe Webhooks Need Real Testing
Stripe webhooks are asynchronous. A customer can complete checkout successfully while your own backend is still waiting for a later webhook to mark the order paid, provision access, or send a receipt. If that webhook fails, the customer sees success while your system quietly falls behind.
That is why Stripe is worth testing with real payloads instead of trimmed-down mocks. The details that break handlers usually live in the full object shape, the delivery headers, or the order in which events arrive.
Key Stripe Webhook Events You Should Handle First
| Event | When It Fires | Priority |
|---|---|---|
payment_intent.succeeded | Payment completed | Critical |
payment_intent.payment_failed | Payment declined | Critical |
checkout.session.completed | Checkout flow finished | Critical |
invoice.paid | Subscription invoice paid | High |
customer.subscription.deleted | Subscription cancelled | High |
charge.dispute.created | Customer filed a dispute | High |
Method 1: Test with Requex.me (Fastest)
If you want to see the raw Stripe payload before your own code touches it, a temporary inspection endpoint is the fastest starting point. Requex.me works well for that:
- Visit requex.me and copy your unique webhook URL.
- Go to Stripe Dashboard → Developers → Webhooks → Add Endpoint.
- Paste your Requex URL, select the events you want to test, and click "Add endpoint".
- Click "Send test webhook" in Stripe's dashboard for any event type.
- Watch the payload appear instantly in your Requex dashboard.
That gives you the exact JSON Stripe sent, including nested `data.object` fields, metadata, and delivery details that are easy to miss in docs.
Method 2: Stripe CLI for Local Testing
The Stripe CLI provides direct forwarding to your local server:
# Install Stripe CLI brew install stripe/stripe-cli/stripe # Login to your Stripe account stripe login # Forward events to your local server stripe listen --forward-to localhost:3000/api/webhooks/stripe # In another terminal, trigger test events: stripe trigger payment_intent.succeeded stripe trigger customer.subscription.created stripe trigger invoice.payment_failed
The CLI provides a temporary webhook signing secret (displayed when you run stripe listen). Use this secret in your local environment for signature verification.
Stripe Webhook Signature Verification
Stripe signs every webhook with your endpoint's signing secret. Always verify this signature in production:
// Node.js / Express example
const stripe = require('stripe')('sk_test_...');
app.post('/api/webhooks/stripe', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
console.log('Webhook signature verification failed:', err.message);
return res.status(400).send('Webhook Error');
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Fulfill the order...
break;
case 'payment_intent.payment_failed':
// Notify the customer...
break;
default:
console.log('Unhandled event type:', event.type);
}
res.status(200).json({received: true});
});⚠️ Critical: You must use express.raw() instead of express.json() for the webhook route. Stripe's signature verification requires the raw request body.
Common Stripe Webhook Issues
401 Unauthorized errors
If Stripe receives a 401 from your endpoint, it will retry the webhook. This typically means your handler is rejecting the request before signature verification passes. See the webhook 401 unauthorized guide for diagnosis steps.
Signature verification fails every time
Make sure you're using the webhook endpoint secret (starts with whsec_), not your API secret key (starts with sk_). Also ensure the body hasn't been parsed by middleware before verification.
Events received out of order
Stripe doesn't guarantee event order. A invoice.paid might arrive before invoice.created. Always fetch the latest state from the API if order matters.
Duplicate events
Stripe retries failed deliveries. Store processed event IDs in your database and skip duplicates. Use the event.id field as your idempotency key.
Language-Specific Stripe Webhook Handlers
See full implementation guides for your language stack:
Testing Checklist for Stripe Webhooks
- ✓Test
payment_intent.succeeded— Verify order fulfillment - ✓Test
payment_intent.payment_failed— Verify customer notification - ✓Test signature verification with valid and invalid signatures
- ✓Test idempotency by sending the same event twice
- ✓Test timeout handling (use Requex.me delay simulation)
- ✓Test in both Stripe test mode and live mode
Start Testing Webhooks Now
Generate your unique URL and test webhooks instantly. Free, no signup.
Open Webhook Tester →