You found the bug (a redirect, a parsed body, the wrong signing secret), pushed a fix, and now you're waiting for the next real payment to see if it worked. You don't have to. Stripe gives you three ways to send an event to your endpoint on demand.
This is the best test, because it's the real event your handler choked on, sent to the real endpoint. Stripe's docs list two ways (Stripe docs: Receive Stripe events in your webhook endpoint):
stripe events resend <event_id> --webhook-endpoint=<endpoint_id> command. This works for up to 30 days after the event creation."Get the event ID (evt_...) from the endpoint's event deliveries in the Dashboard, and the endpoint ID (we_...) from the endpoint's page. The CLI reference adds that "You must pass --webhook-endpoint=we_123456 to resend the event to a specific webhook endpoint", and that requests "run in a sandbox by default", so add --live for a live-mode event (Stripe CLI reference: events resend).
stripe events resend evt_123 --webhook-endpoint=we_123 --live
Then open the endpoint's deliveries and look for a 2xx. If it's still failing, the status code tells you what's left (7-step checklist).
Two things to know first. A manual resend "doesn't cancel Stripe's automatic retry behavior, even if it results in a 2xx status code" (source), so the same event can arrive again later; make the handler skip event IDs it has already processed (duplicate events). And if the customer already got access some other way, a resend may grant it twice unless the grant is idempotent.
To step through the handler locally, forward events with the Stripe CLI: stripe listen --forward-to localhost:4242/webhook (Stripe docs: Test webhooks with the Stripe CLI). The stripe listen command "outputs the" signing secret to use in your handler (source), and "The webhook signing secret provided will not change between restarts to the listen command" (Stripe CLI reference: listen).
That secret only works for events forwarded by the CLI. Your deployed endpoint has its own whsec_; mixing them up is a classic cause of signature failures (test vs live webhook secret). With stripe listen running, stripe events resend <event_id> without the endpoint flag sends the event "to the CLI's local webhook endpoint" (source).
If you don't have a failed event to reuse, create one in a sandbox. stripe trigger checkout.session.completed is on the CLI's supported list, and the reference warns that "triggering events causes side effects: all necessary API objects will be created in the process" (Stripe CLI reference: trigger). Do it in a sandbox, not live.
A triggered session won't carry your app's client_reference_id or metadata, so your handler may not find a user to unlock. You can pass your own values with --add or --override, for example --add checkout_session:metadata.user_id=test_user (source). Or run one checkout in your app in a sandbox with a Stripe test card, which exercises the real flow from button to unlock.
All three send properly signed events, so they show that Stripe can reach you and your handler accepts real events. They don't show that your endpoint rejects unsigned or forged ones (can someone fake a Stripe payment?). That's one of the things the free check below tests.
Paste your webhook URL. WebhookProof sends 3 harmless test requests and tells you whether Stripe can reach it and whether it rejects unsigned events. If it finds a problem, the $9 fix pack gives you the exact copy-paste fix for your stack (refundable for 14 days).
Run the free checkIn the Dashboard, open the event and click Resend (up to 15 days after the event was created), or run stripe events resend <event_id> --webhook-endpoint=<endpoint_id> with the Stripe CLI (up to 30 days). Add --live for a live-mode event.
No. Stripe says a manual resend does not cancel its automatic retry behavior, even if the resend gets a 2xx, so make your handler skip event IDs it has already processed.
Yes, stripe trigger checkout.session.completed is supported. It creates the API objects it needs, so run it in a sandbox, and use --add or --override to set metadata your handler expects.
stripe listen prints its own signing secret, which only matches events the CLI forwards. Your deployed endpoint needs the whsec_ secret from that endpoint in the Dashboard.
Stripe webhook not working? Check these 7 things in order · A customer paid but didn't get access. What went wrong? · No signatures found matching the expected signature for payload · Stripe webhook failing with a 307 or 308 redirect
Last reviewed 2026-10-28. WebhookProof is an independent tool, not affiliated with Stripe, Inc.. Quotes are from the linked documentation.