Knowledge > Runbooks > Technical Ops > Debug a Failed Stripe Webhook
Debug a Failed Stripe Webhook
Diagnose and fix a Stripe webhook that failed, returned an error, or was never delivered. Stripe retries failed webhooks automatically for 3 days — fix the issue before retrying to avoid duplicate processing.
Prerequisites
- Stripe CLI installed and logged in (
stripe login) - Access to Vercel logs
- The failed event ID (from Stripe Dashboard or a user report)
- For live mode writes:
STRIPE_LIVE_SECRET_KEYfromknowledge/.env
Steps
-
Find the failed webhook event in Stripe
In Stripe Dashboard → Developers → Webhooks → select the endpoint → view recent deliveries. Or search by event ID in Stripe Dashboard → Developers → Events.
Using CLI (test mode):
stripe events list --limit 10For live mode:
stripe events list --limit 10 --api-key $STRIPE_LIVE_SECRET_KEY -
Review the response code and body
In the Stripe Dashboard event detail, check:
- HTTP response code from your endpoint (200 = success, anything else = failure)
- Response body (look for error messages from your handler)
- Delivery timestamp and latency
Common failure codes:
500— server error in the webhook handler (check Vercel logs)400— bad request, often a signature verification failure404— wrong webhook URL configuredtimeout— handler took longer than 30 seconds
-
Check Vercel logs for the webhook route
vercel logs --tailOr filter by function in Vercel Dashboard → Project → Functions →
/api/stripe/webhook.Webhook routes by codebase:
- ChurchWiseAI:
churchwiseai.com/api/stripe/webhook - PewSearch:
pewsearch.com/api/stripe/webhook - ITW:
illustratetheword.com/api/stripe/webhook
- ChurchWiseAI:
-
Diagnose the root cause
Signature verification failure (400 error):
- The
STRIPE_WEBHOOK_SECRETenv var doesn't match the endpoint's signing secret - Verify: Stripe Dashboard → Endpoint → Signing secret → compare to Vercel env var
- Fix:
echo "whsec_..." | vercel env add STRIPE_WEBHOOK_SECRET production
Event type not handled (silently dropped):
- The event type isn't in the handler's switch/if block
- Known gap:
customer.subscription.trial_will_end(FA-004 pending) - Fix: add the event type to the handler
Handler code bug (500 error):
- Check Vercel logs for the stack trace
- Common issues: null reference when subscription metadata missing, wrong table name, type mismatch
Wrong webhook URL (404):
- Verify the URL in Stripe matches the deployed route exactly
- Stripe Dashboard → Developers → Webhooks → check endpoint URL
- The
-
Replay the event to test your fix
After deploying the fix, replay the event:
stripe events resend evt_1234567890abcdefOr replay from Stripe Dashboard → event detail → "Resend" button.
To trigger a test event locally:
stripe listen --forward-to localhost:3002/api/stripe/webhook# In a second terminal:stripe trigger checkout.session.completedstripe trigger customer.subscription.updatedstripe trigger invoice.payment_failed -
Verify the event was processed correctly
Check the database for the expected state change:
-- Confirm subscription was updated in premium_churchesSELECT church_id, plan, tier, stripe_subscription_id, updated_atFROM premium_churchesORDER BY updated_at DESC LIMIT 5;Check
ops_error_reportsfor new errors triggered by the replay:SELECT * FROM ops_error_reportsWHERE route ILIKE '%stripe%' AND created_at > now() - interval '10 minutes';
Verification
- Stripe Dashboard shows event delivery status as
200 OK - Database reflects the correct subscription state
- No new errors in
ops_error_reportsfor the webhook route
Rollback
If a replayed event causes duplicate data (e.g., double-inserted subscription record):
- Identify the duplicate row in
premium_churches - Get founder approval before deleting
- Delete the duplicate using the record
id, not a bulk delete
See Also
- Error Log Triage Runbook
- Stripe Revenue Review
- PRICING.md — event types and expected state changes