Stripe Payment Failure Webhook
Configure Stripe webhooks to receive real-time payment failure events and extract failure metadata
Instructions
Stripe Payment Failure Webhook
Prerequisites
- Stripe account with API access (secret key and webhook signing secret)
- An HTTP endpoint to receive webhooks (n8n, custom server, or serverless function)
Webhook Configuration
1. Create the webhook endpoint in Stripe
Via Stripe API:
curl https://api.stripe.com/v1/webhook_endpoints \
-u sk_live_YOUR_KEY: \
-d url="https://your-n8n-instance.com/webhook/stripe-payment-failures" \
-d "enabled_events[]"="invoice.payment_failed" \
-d "enabled_events[]"="charge.failed" \
-d "enabled_events[]"="customer.subscription.updated" \
-d "enabled_events[]"="payment_intent.payment_failed"
Store the returned webhook_signing_secret (whsec_...) for signature verification.
2. Verify webhook signatures
Every incoming webhook must be verified to prevent spoofing. Use the Stripe SDK:
const event = stripe.webhooks.constructEvent(
request.body, // raw body (string or Buffer)
request.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
If verification fails, return 400 and log the attempt. Never process unverified webhooks.
3. Extract failure metadata from the event payload
For invoice.payment_failed events, extract:
{
"customer_id": "event.data.object.customer",
"subscription_id": "event.data.object.subscription",
"invoice_id": "event.data.object.id",
"amount_due": "event.data.object.amount_due",
"currency": "event.data.object.currency",
"attempt_count": "event.data.object.attempt_count",
"next_payment_attempt": "event.data.object.next_payment_attempt",
"failure_code": "event.data.object.last_payment_error.code",
"failure_message": "event.data.object.last_payment_error.message",
"payment_method_type": "event.data.object.last_payment_error.payment_method.type",
"payment_method_last4": "event.data.object.last_payment_error.payment_method.card.last4",
"billing_reason": "event.data.object.billing_reason"
}
Key failure codes to classify:
card_declined— generic decline, could be insufficient funds, fraud block, etc.expired_card— card has passed its expiration dateinsufficient_funds— not enough balanceauthentication_required— needs 3D Secure / SCAprocessing_error— temporary processor issue, likely to succeed on retry
4. Respond correctly
Return HTTP 200 within 5 seconds to acknowledge receipt. Process the event asynchronously. If you return a non-2xx status, Stripe retries with exponential backoff for up to 72 hours.
Error Handling
- If the webhook endpoint is down, Stripe queues events for up to 72 hours
- Monitor webhook delivery health in Stripe Dashboard > Developers > Webhooks
- Set up a Stripe webhook health check: query the Stripe API for pending webhooks weekly
- If failure rate exceeds 5%, check endpoint availability and response times
Alternative Tools
| Tool | Webhook Event | Documentation |
|------|--------------|---------------|
| Stripe | invoice.payment_failed | stripe.com/docs/api/events |
| Paddle | subscription.payment_failed | developer.paddle.com/webhooks |
| Chargebee | payment_failed | apidocs.chargebee.com |
| Recurly | failed_payment | recurly.com/developers |
| Braintree | subscription_went_past_due | developer.paypal.com/braintree |