Churn Recovery6 min read

Involuntary Churn Playbook: Recover Expired Cards Before They Cancel

Most churn is not a breakup. It is an expired card. The playbook: catch the fail in seconds, send a pay link, alert the founder, mark recovered when they pay.

1. Most churn is not a breakup

A customer who loved the product last week does not wake up and cancel because the card expired. They bounce. Stripe retries. Nobody tells them in a channel they actually open. Two weeks later they are “churned” in your dashboard — and you start a win-back campaign for a person who never chose to leave.

Involuntary churn is the cheapest revenue to recover because the intent is still there. You do not need a discount. You need speed and a pay link.

2. The 15-minute playbook

  1. Catch the fail in seconds. Add a Stripe endpoint for invoice.payment_failed and invoice.payment_succeeded. Verify the signature. No unsigned events.
  2. Email the customer immediately with the amount and the hosted invoice URL. One button. No lecture.
  3. Alert the founder on Telegram. You should know a $29 miss happened before they tweet about downtime.
  4. Let Stripe retry. Each retry re-fires the webhook. Map attempt count to the next step. Do not run a cron.
  5. Mark recovered on success. When the card works, flip the row. Your dashboard should show dollars won back, not a graveyard of failed attempts.

3. Decline codes tell you what to say

expired_card wants a new number. insufficient_funds wants a retry after payday. do_not_honor wants the customer to call their bank. If you send the same email for all three, you leave money on the table. Capture decline_code on the log and show it in analytics.

if (event.type === "invoice.payment_failed") {
  const invoice = event.data.object;
  await sendRecoveryEmail({
    customerEmail: invoice.customer_email,
    amount: format(invoice.amount_due, invoice.currency),
    hostedInvoiceUrl: invoice.hosted_invoice_url,
  });
}

if (event.type === "invoice.payment_succeeded") {
  await markRecovered(userId, invoice.customer_email);
}

That is the whole engine. Wire it once. Every failed charge after that is a recovery, not a write-off.