Each of the 180 subscribers sits at a fixed angle on the billing-cycle ring, one per day of a 30-day cycle. When a charge is attempted, it declines with probability d (the base decline chance, tunable at left) and is assigned a reason — insufficient funds, an expired card, or a fraud/bank block — drawn from the mix on the left. A declined subscriber does not churn immediately: it enters a dunning cascade, retried on the offsets defined by the selected schedule (e.g. Standard = days 1, 3, 7 after the decline).
The probability that a given retry succeeds grows with elapsed time since the original decline, following an exponential-approach recovery curve, reason by reason:
p_recover(t) = min(0.97, p_max · (1 − e^(−t / τ)) · boost)
insufficient funds: p_max=0.85 τ=4d (recovers fast — payday)
expired card: p_max=0.35 τ=9d (needs the user to update it)
fraud / bank block: p_max=0.12 τ=6d (rarely clears on retry alone)
boost = 1.35 under the "Smart (ML-timed)" schedule, 1.0 otherwise
If a subscriber survives every retry offset in the schedule without a success, it churns involuntarily — the customer never chose to leave, the payment simply never went through. Recovery rate is recovered ÷ (recovered + churned); revenue recovered accumulates the monthly price for every subscriber saved by a retry. Drag the ring to rotate it, scroll/pinch to zoom — the lower panel plots active / retrying / churned counts and cumulative recovered revenue across the whole run so you can see how schedule choice and decline mix reshape the outcome over time. This is exactly the model behind production dunning tools (Stripe Smart Retries, Recurly, Chargebee): choosing when to retry, not just whether, is what determines how much otherwise-lost subscription revenue comes back.