Skip to main content

A plan unpaid 60 days is canceled in Stripe and churned as Non-payment

Status: accepted Date: 2026-10-20

Decision

  1. The rule is planAutoChurn in lib/retentionBilling.ts. A plan qualifies when the billing check plans row 3 of ADR 0014 (past due), and its oldest open invoice is 60 or more Denver days old. Row 3 already excludes:
    • arrangement labels that hold the card;
    • the app’s paid-through arrangement;
    • a competing subscription;
    • any billing review.
  2. A qualifying plan is held for staff when any of these holds:
    • the plan is not monthly;
    • Stripe shows a payment after the first missed invoice, or an open invoice is partly paid (Stripe does not record when that money arrived);
    • the card is in the On Hold stage, or the app moved it to Critical from On Hold;
    • Hold Status is anything other than blank, Active or MIA, including unreadable;
    • any charge on the Stripe customer was disputed, or the dispute list cannot be read (that failure never stops the rest of the billing check);
    • the card’s stage move needs staff review: a closed or Churned card, a restart (ADR 0014 row 10), or a restored stage.
  3. With RETENTION_AUTO_CHURN_ENABLED and RETENTION_BILLING_WRITES_ENABLED both true, the app takes these steps, in order:
    1. It claims one of at most 10 cancellations in any 24 hours.
    2. It re-checks, right before writing, that the plan can still bill, that the invoice that started the clock is still open and wholly unpaid, that no invoice was paid since the first miss, and that the CRM card still qualifies (re-planned from its fields, stage, hold and any new dispute). Otherwise it releases the claim and writes nothing.
    3. It cancels the subscription immediately, with no final invoice and no proration, and reads it back.
    4. It voids every unpaid open invoice on the account-bound plans, one at a time. A lost response is settled by reading the invoice back.
    A partly paid invoice is left open. When either flag is off, each check saves the report on retentionBillingState.autoChurn and writes nothing to Stripe.
  4. The CRM card is not written in the same check. The next check sees the canceled plan and churns the card through row 6 of ADR 0014, with the lost reason Non-payment — 60+ days. The reason is matched by its exact name. If it is missing or the list cannot be read, the card closes without it.
  5. For a plan the app canceled this way, service ends at the latest paid invoice period, or at a staff paid-through arrangement if that is later, rather than at the cancellation time. The card therefore churns on the next check, and its paid-through date is the last period the owner paid for. Every other cancellation keeps ADR 0014’s service end.
  6. The app makes one attempt per plan. A cancellation Stripe does not confirm is saved as failed and is never retried automatically. Once the cancellation is confirmed, a failed void does not undo it.
This replaces the FM-2888 position from 2026-10-17 that a past-due owner stays Past due and Critical until they pay or staff cancel. The app writes to Stripe only here.

Context

Kevin set the rule on 2026-10-20: 60 days after the first missed payment, if the owner is not on hold and there is no other evidence, the card closes as lost, the Stripe subscription is canceled, and every failed-payment invoice is voided. Owners with no billing stay manual. A one-time cleanup applied the same rule by hand that day. It covered 62 owners, including unlinked ones and both Stripe accounts:
  • 155 invoices voided ($200,560);
  • 2 plans canceled;
  • 13 cards closed.
That run produced the refinements recorded above:
  • Clock: it starts at the oldest open invoice on the live plan, not the last payment. Edgar Hicks’s extension would otherwise have closed him.
  • Membership only: only membership plans count. This rule reads only the account-bound plans.
  • Recent payment: a payment after the first miss holds the owner.

Considered options

  • Close the card first, then cancel in Stripe. Rejected: a failed Stripe step would leave a closed card on a plan that is still charging. That was the Evan Torkelson and Michael Hicks problem the cleanup found.
  • Void only invoices older than 60 days. Rejected (Kevin): once the plan is canceled, newer invoices bill for service that was never given. Stripe would keep sending reminders for them.
  • Hold on a recent Slack message or a staff edit in the last 14 days. Rejected for the app: neither signal is available to the billing check. Staff hold an owner with the On Hold stage or Hold Status, which the app reads.
  • Use the cancellation time as the service end. Rejected: the card would sit at Canceled for a day, then show a paid-through date the owner never paid for.

Consequences

  • Rollout is report first. Each check saves autoChurn with status due or held, and the Billing panel shows it. Turn the flag on only after the report matches what staff expect. Unsetting it stops new Stripe writes at the next check.
  • Canceling a plan may suspend the owner’s software through the CRM’s SaaS settings. Depending on Stripe settings, it may also send a cancellation email. Voiding sends nothing.
  • The rule covers only owners the app has linked, and only the My Funding Machine Stripe account. Other owners still need a person or another one-time run.
  • Known gaps:
    • A canceling claim whose result was never saved counts as an app cancellation for its service end and lost reason. It counts toward the daily limit and is not retried.
    • A partly paid invoice holds the plan, and if one appears after the cancellation it stays open for staff.
    • A void that still fails after readback is not retried. The plan is already canceled, and ADR 0014 treats open invoices on a canceled plan as void.