Stripe "invoice.payment_failed" vs "charge.failed": which webhook to handle
Chooses the right Stripe webhook for failed invoice payments. Use invoice.payment_failed for billing recovery logic and charge.failed only when you need low-level payment detail. Not for successful payment confirmation.
TL;DR
For failed subscription or invoice payments, handle invoice.paymentfailed: it carries the invoice, the customer, and the billing context your dunning flow needs. charge.failed is the lower-level event about a single charge attempt, useful for analytics or decline-code dashboards but missing the invoice linkage. Handling both usually double-counts. Pick invoice.paymentfailed as the recovery trigger and subscribe to charge.failed only for payment-ops telemetry.
The query
Stripe "invoice.payment_failed" vs "charge.failed": which webhook to handleUse this when
- Deciding which Stripe webhook drives your dunning flow
- You get duplicate failed-payment alerts from both events
- Building a decline-code dashboard for payment ops
Not for
- Successful payments (use invoice.paid / invoice.payment_succeeded)
- Disputes or refunds (different event families entirely)
Steps
- Subscribe to invoice.payment_failed and inspect its payload: invoice id, customer, attempt count.
Expected output: You have everything the dunning flow needs in one event.
- Route it to your recovery queue: email the customer, open the portal link, schedule retries.
Expected output: Failed invoices enter recovery automatically.
- If you want decline-code telemetry, also listen to charge.failed and join on payment_intent.
Expected output: Your dashboard gets decline reasons without driving recovery.
- Dedupe: do not send customer emails from both handlers.
Expected output: Customers get one recovery email, not two.
- Verify the signature on both endpoints and log the event id for idempotency.
Expected output: Replays do not double-trigger the flow.
Variant phrasings
Stripe invoice.payment_failed vs charge.failed
which webhook for failed subscription payment Stripe
Stripe dunning webhook invoice payment failed
Root cause
invoice.payment_failed sits at the billing layer and charge.failed at the payments layer; Stripe emits both because one failed invoice can involve multiple charge attempts. Recovery logic belongs at the billing layer where the customer and invoice context live.
Edge cases
- Smart retries can emit several charge.failed events for one invoice.payment_failed; count at the invoice level
- invoice.payment_failed also fires for non-card failures like no payment method, so do not assume a decline
Provenance
Resolved from the public thread: https://vectle.com/posts/pstAH-vdHJyS2viUoL8C6NNQ
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.