Stripe webhook testing is fastest when you can receive real event payloads on localhost without exposing your machine directly. The workflow below gives you three things in one loop: a stable public URL, full request history, and local handler feedback.
Architecture
Stripe sends events to your webhooks.cc endpoint URL. webhooks.cc stores each request and, when you run the CLI tunnel, forwards the request to your local server. You get capture, replay, and local execution at the same time.
Stripe -> https://go.webhooks.cc/w/<slug> -> whk tunnel -> http://localhost:3000/webhooks
1. Start your local webhook handler
npm run dev
Make sure your webhook route accepts POST requests and reads the raw body before JSON parsing. Stripe signature verification depends on the exact raw payload.
2. Open a tunnel to localhost
whk tunnel 3000
The command prints the endpoint slug and forwards incoming requests to port 3000. Keep this process running during your test session.
3. Configure Stripe webhook destination
In Stripe Dashboard, set your webhook destination to:https://go.webhooks.cc/w/<slug>
Subscribe to the events you actually handle first (for example:payment_intent.succeeded, checkout.session.completed) instead of enabling every event.
4. Verify Stripe signatures in your app
const event = stripe.webhooks.constructEvent( rawBody, req.headers["stripe-signature"], process.env.STRIPE_WEBHOOK_SECRET );
To test this quickly, use the dashboard Send button with the Stripe template presets. Set your mock webhook secret to the same value your app verifies against.
5. Tight debug loop
- Trigger a Stripe test event.
- Inspect headers and body in the dashboard request viewer.
- Replay the request to localhost after code changes.
- Add a deterministic SDK assertion for CI once behavior is stable.
const req = await client.requests.waitFor(endpoint.slug, {
timeout: "30s",
match: matchHeader("stripe-signature"),
});Continue with webhook testing in CI/CD with TypeScript.