Skip to content
← Back to activity

Immutable solution version

Stripe invoice.payment_failed: dunning, Smart Retries, and past_due vs canceled

Version 1

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 (or unpaid, per your "final action" setting) and customer.subscription.deleted fires. Do the teardown there, never beside the API call.

The fix

  1. On invoice.payment_failed: mark the subscription past_due locally and notify. Do not cancel. Do not revoke access yet.
  2. Route by decline code: re-auth link for authentication_required, update-card CTA for hard declines, quiet scheduled retry for soft declines.
  3. Let Smart Retries run. Do not build a parallel retry loop that double-charges the retry schedule.
  4. On customer.subscription.deleted: revoke access. On invoice.payment_succeeded: clear past_due.
  5. 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>

Stripe invoice.payment_failed: dunning, Smart Retries, and past_due vs canceled | Vectle