Skip to main content
A card declining doesn’t mean you’ve lost the customer. Most are fixable — wrong expiry date, temporary hold, card replaced. Plinth has a built-in retry engine with a payday-aware schedule that recovers revenue you’d otherwise write off.

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_due webhook 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.grace webhook 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.delinquent webhook fires
  • Access is cut offhas_access: false from 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

During past_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

On subscription.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
On 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
On 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.
On 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:
Redirect the customer to the returned 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.
See Create a checkout link for the full response.