Dunning & Payment Recovery

RevKeen's smart retry engine automatically recovers failed payments and reduces involuntary churn

When a subscription payment fails, RevKeen automatically enters a dunning process that intelligently retries payments while keeping your customers informed. This process typically recovers 30-50% of failed payments without any manual intervention.

How Dunning Works

When a payment fails, RevKeen classifies the decline code and determines the best recovery strategy:

📄

1. Classify Decline

Analyze the decline code to determine if it's a temporary (soft) or permanent (hard) decline.
📄

2. Smart Retry

Schedule automatic retries based on decline type—soft declines get retried on Days 1, 3, and 7.
📄

3. Notify Customer

Send targeted emails at each stage to encourage customers to update their payment method.

Decline Code Categories

CategoryDescriptionActionExample Codes
Hard DeclineCard permanently blocked or invalidNo retry—request new card200, 204, 303, 530, 531
Soft DeclineTemporary issue (insufficient funds, etc.)Auto-retry on schedule300, 301, 304, 402, 521
Action RequiredCustomer must update cardEmail customer to update202, 223, 225

Default Retry Schedule

The default retry schedule balances recovery rates with customer experience:

DayActionEmail SentService Access
Day 0Payment fails, subscription enters past_due"Payment Failed"✅ Full access
Day 1First retry attemptNone (if successful) / "Retry Failed"✅ Full access
Day 3Second retry attempt"Urgent: Update Payment Method"⚠️ Warning shown
Day 7Final retry attempt"Service Suspended" (if failed)❌ Suspended
You can customize the retry schedule and grace period in your client settings. Longer grace periods may recover more payments but also increase revenue delay.

Grace Period Configuration

The grace period determines how long customers retain access while their payment is being retried. Configure these settings per-client:

SettingDescriptionDefault
grace_period_daysDays before service suspension7
max_retry_attemptsMaximum payment retry attempts3
retry_scheduleDays between retries[1, 3, 7]
dunning_email_enabledSend dunning emailstrue
auto_cancel_unpaidCancel after max retries exhaustedfalse

Dunning Email Sequence

RevKeen sends a series of emails to help customers recover their subscription:

📧Day 0: Payment Failed

"We couldn't charge your card. Please update your payment method to avoid service interruption."

🔄Day 1: Retry Failed

"Still having trouble charging your card. We'll retry again soon, but please update your payment method."

⚠️Day 3: Urgent Action Needed

"Action needed to avoid service interruption. Update your payment method now."

Day 7: Service Suspended

"Your subscription has been suspended due to payment failure. Update your payment method to restore access."

Subscription States During Dunning

StateDescriptionService Access
past_duePayment failed, in grace period with retries scheduled⚠️ Limited
unpaidAll retries exhausted, service suspended❌ None
canceledTerminated after dunning (if auto_cancel_unpaid=true)❌ None
When auto_cancel_unpaid is set to true, the subscription will automatically cancel after all retries are exhausted. Otherwise, it remains in unpaid status until manually resolved.

Handling Dunning via API

While dunning is automatic, you can interact with it programmatically:

Check Subscription Dunning Status

const subscription = await client.subscriptions.retrieve('sub_xxxxxxxx');

if (subscription.data.status === 'past_due') {
  console.log('Subscription is in dunning');
  console.log('Grace period ends:', subscription.data.current_period_end);
}

if (subscription.data.status === 'unpaid') {
  console.log('All retries exhausted - needs manual intervention');
}

Manually Retry a Failed Payment

// Get the past-due invoice
const invoices = await client.invoices.list({
  subscription: 'sub_xxxxxxxx',
  status: 'past_due',
});

// Manually retry payment
const result = await client.invoices.pay(invoices.data[0].id);

if (result.data.status === 'paid') {
  console.log('Payment recovered successfully!');
}

Dunning Webhooks

Listen to these webhooks to stay informed about dunning progress:

EventDescription
invoice.payment_failedPayment attempt failed
invoice.payment_action_requiredCustomer action needed (e.g., 3DS verification)
subscription.past_dueSubscription entered past_due status
subscription.unpaidAll retries exhausted
invoice.paidPayment recovered
invoice.marked_uncollectibleInvoice written off as bad debt
app.post('/webhooks/revkeen', async (req, res) => {
  const event = req.body;
  
  switch (event.type) {
    case 'invoice.payment_failed':
      // Log failed payment, maybe trigger internal notification
      console.log('Payment failed for invoice:', event.data.id);
      break;
      
    case 'subscription.past_due':
      // Optionally show in-app warning to customer
      await showPastDueBanner(event.data.customer_id);
      break;
      
    case 'subscription.unpaid':
      // Suspend service access
      await suspendAccess(event.data.customer_id);
      break;
      
    case 'invoice.paid':
      // Payment recovered! Restore access
      await restoreAccess(event.data.customer_id);
      break;
  }
  
  res.status(200).send('OK');
});

Best Practices

📄

Keep Cards Updated

Encourage customers to enable card updates with their bank. Use thecustomer.update_payment_methodwebhook to prompt updates before expiration.
📄

Grace Period Length

7 days is the sweet spot for most businesses—long enough to recover payments, short enough to minimize revenue lag.
📄

Custom Retry Schedule

For high-value subscriptions, consider extending retries to 14 days with more attempts. For low-value, shorter schedules reduce bad debt.
📄

Monitor Recovery Rates

Track your dunning recovery rate in analytics. Aim for 30-50% recovery of failed payments. Below 20% may indicate card quality issues.
Never disable dunning emails completely. They are proven to significantly improve recovery rates and reduce involuntary churn.

Related Resources