The timeline
1
Renewal attempt fails
The billing engine tries to charge the card at
next_bill_at. The card declines.- Subscription moves to
past_due - Invoice stays
open subscription.past_duewebhook fires- Customer still has access — don’t cut them off yet
2
Retry day +1
We try again 24 hours later. If it succeeds:
subscription.recovered, invoice.paid, access continues as normal.If it fails: we move to the next retry.3
Retry day +3
Third attempt, 3 days after the original failure.
4
Retry day +7 (payday-aware)
Fourth attempt. If day +7 falls after the 25th of the month, we move this retry to the 25th instead — the most common salary/payroll date in Nigeria. Higher probability of funds being available.
5
Retry day +14
Final attempt. If this fails, the subscription moves to
grace.subscription.gracewebhook fires- Customer still has access during grace (default 7 days, configurable)
6
Grace period expires
If the invoice isn’t paid before the grace period ends:
- Subscription moves to
delinquent subscription.delinquentwebhook fires- Access is cut off —
has_access: falsefrom the entitlements endpoint
Soft vs hard declines
We classify decline codes into two categories:
Common hard codes:
do_not_honor, stolen_card, lost_card, account_closed, invalid_account.
Common soft codes: insufficient_funds, withdrawal_limit_exceeded, try_again_later.
Recovery via bank transfer
Duringpast_due or grace, the customer can pay the outstanding invoice by bank transfer to their virtual account — even if they’re on a card subscription. We auto-reconcile it and restore access immediately. No manual intervention needed on your end.
subscription.recovered fires as soon as the transfer is matched.
Webhooks to handle
What to do in your app
Onsubscription.past_due:
- Show a non-blocking warning banner
- Send a payment failure email with a link to update their card
- Don’t block features yet — most cards recover within a retry
subscription.grace:
- Show a countdown: “Your account will be suspended in N days”
- Send escalating emails (day 1, day 5, day 7 of grace)
- Optionally block non-critical features to create urgency
subscription.delinquent:
- Block access to paid features
- Redirect to a payment recovery page showing their VA account details or a card update link
- Keep their data — don’t delete anything. They might pay tomorrow.
subscription.recovered:
- Restore access immediately
- Send a “welcome back” confirmation email
- Clear any warning states in your UI
Recovering with “Update payment”
Automatic retries only work when there’s a card token to charge. For a subscription with no card on file (e.g. it was funded by bank transfer), or when the customer wants to pay right now, generate a checkout and let them settle the outstanding invoice:checkoutLink. On settlement Plinth marks the outstanding
invoice paid, returns the subscription to active, advances it into the paid period, and emits
subscription.recovered. This works from past_due, grace, or delinquent.
Paying by card captures a token, so future renewals auto-charge and dunning won’t recur. Paying
by transfer recovers the current period but leaves no token — the next renewal will prompt for
payment again. Surface an “add a card to auto-renew” nudge for transfer-funded subscriptions.