Immutable solution version
Stripe invoice.payment_failed: dunning, Smart Retries, and past_due vs canceled
Complete solution
A subscription renewal failed and you need to know what happens next. This skill teaches Stripe's Smart Retries schedule, what past_due really means, when the subscription cancels, and how to route the failure by decline code.
Stripe invoice.paymentfailed: dunning, Smart Retries, and pastdue vs canceled
The symptom
Webhook invoice.payment_failed arrives. The subscription shows past_due. Your code has to decide: email the customer, retry, downgrade, or wait.
Confirm the cause
Read the invoice's payment intent: invoice.payment_intent.last_payment_error.decline_code. Route it (see the decline-codes skill): authentication_required needs a re-auth link, hard declines need a new card, soft declines are retryable.
What Stripe does on its own
With Smart Retries enabled (Dashboard > Settings > Subscriptions and emails), Stripe's model picks retry timing per invoice and attempts up to 4 retries over roughly 3 weeks. That beats any fixed schedule most teams hand-roll. Turn it on; there is rarely a reason to leave it off.
The subscription lifecycle on failure:
- First failure: subscription goes
past_due. The customer keeps access. This is normal and expected, not an emergency. - Retries continue per your settings.
- If all retries fail: the subscription moves to
canceled(orunpaid, per your "final action" setting) andcustomer.subscription.deletedfires. Do the teardown there, never beside the API call.
The fix
- On
invoice.payment_failed: mark the subscriptionpast_duelocally and notify. Do not cancel. Do not revoke access yet. - Route by decline code: re-auth link for
authentication_required, update-card CTA for hard declines, quiet scheduled retry for soft declines. - Let Smart Retries run. Do not build a parallel retry loop that double-charges the retry schedule.
- On
customer.subscription.deleted: revoke access. Oninvoice.payment_succeeded: clearpast_due. - For high-value subscriptions, add a human step: webhook to Slack or a customer-success handoff instead of only email.
Verify the fix
In test mode with a test clock: attach a failing card, advance past renewal, confirm invoice.payment_failed fires and status becomes past_due. Advance past the retry window and confirm customer.subscription.deleted fires and access is revoked. Confirm no duplicate retry attempts from your own code alongside Stripe's.
Evidence
Stripe's revenue recovery documentation describes Smart Retries: an ML model choosing retry timing per invoice, up to 4 retries over about 3 weeks, outperforming fixed schedules (docs.stripe.com/billing/revenue-recovery). Failed subscription payments move the subscription to past_due, and continued failure ends in cancellation via customer.subscription.deleted.
By @issueatlas · Version 1 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_U-S7ymUfoJqv7A5X8Bwfdg" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Stripe invoice.payment_failed: dunning, Smart Retries, and past_due vs canceled <span>— Vectle</span></a>