A personalised, lifecycle-aware renewal system for India's biggest multi-sport streaming app — reaching the right user, on the right surface, at the right moment, with the right offer.
FanCode is India's multi-sport streaming platform, where fans buy passes to watch live tournaments. As the business shifted toward auto-renewing monthly & yearly subscriptions (via Juspay mandates), a structural problem surfaced: more than half of fresh mandates were being cancelled, and users weren't being informed about their utilisation, validity, or how to renew. Renewal isn't a screen — it's a personalised lifecycle that plays out across many surfaces and many moments.
The PRD framed the problem from two angles: a user who is left in the dark about their subscription, and a business bleeding recurring revenue through cancellation and one-time behaviour.
Users aren't informed about their subscription utilisation or pass validity — both before and after expiry — so they can't see what they'd lose by letting it lapse.
Users cancel or pause mandates mid-validity, many opt for one-time passes over recurring, and there was nothing nudging them to renew once a pass expired.
of 45k fresh mandates bought in Nov '24 were revoked or paused.
of users who revoke their mandate do so in Month 1.
cancel right after the pre-debit notification (24h before debit).
of revokes happen on the PSP app, not on FanCode — a visibility blind spot.
+5% increase in users opting into recurring mandates, and a +10% increase in the M1 renewal rate for recurring subscribers — by informing users of their value and nudging renewal across the full lifecycle.
Before any visual polish, the entire problem space was explored in the FC-Renewals file — the lifecycle window, the personalisation logic, every payment type, surface and edge case. The hard part wasn't the screens; it was the rules that decide which screen, which offer, and which words each user sees.
I mapped a single 15-day window — 5 days before expiry → day of expiry → 10 days post expiry — and defined exactly how each surface behaves within it. Home nudges show once per day (auto-dismiss at session end, one session/day, cached 24h); the MDP CTA persists until the user's next purchase; post-expiry escalates to a full-page interstitial every alternate day. A cancellation after the pre-debit notification simply drops the user into the post-expiry flow.
Every offer is driven by the Master Renewal ETL (MRETL) — it pulls each user's last subscription, computes their matches, tours, watch-time and amount saved, then sorts them into a bucket that maps to a discount flow. The Audience Engine decides who sees a nudge on each home-page load; MRETL decides what they're offered. No discount is hard-coded — all three flows are CMS-configurable per cycle.
High-intent / first cycle or value < ₹199 — push hardest to convert.
Second cycle onwards with healthy value — a nudge, not a giveaway.
Already availed a multi-cycle discount — protect margin.
A nudge is only useful if it matches where the user is. Pre-expiry nudges were mapped along two axes — lifecycle stage and payment type (one-time vs recurring mandate) — with dedicated handling for recurring users whose mandate is paused, revoked or cancelled: they're treated like one-time users because they need to register a fresh mandate.
The new Renew Page opens with a consumption recap generated from MRETL, before any price. The headline copy adapts to what the user actually did last month — surfacing savings, matches and tours when the data is strong, and a category benchmark when it isn't. It carries at most two CMS-configured subscriptions, defaulting to the user's last-purchased type.

Once a pass lapses, urgency rises — but so does the risk of being annoying. The post-expiry system steps up gradually with a full-page interstitial on alternate days, always anchored to live, relevant matches, and only for the 10-day grace window before the user is fully lapsed.
The resolved, production-ready UI was consolidated in the Recurring Subs Renewal file. Built on shared, CMS-driven components — so the Renew Flow Configurator can change discount %, cycles and copy across Renew Page, PL and Profile without a new release.
The final home nudge steps through the lifecycle, escalating tone as expiry approaches — and crucially holding the discount back until the post-expiry window, so we never train users to wait for a deal. The discount shown (NA / 25% / 50%) is resolved live from the user's MRETL bucket.

The highest-intent moment is when a user is actively trying to watch a live match. So the renewal CTA lives right inside the Match Detail Page — its copy changing with expiry status & discount — and persists from 5 days before expiry until the user's next purchase. This became the single largest renewal-driving surface.
The shipped Renew Page carries the consumption-recap concept through to pixel-level, with DreamCoins & coupon popups (no upfront coupons), transparent savings, and full payment success / error states for repeat monthly subscribers — all populated by MRETL.
A single-cycle discount buys one more month. A discount spread across two or more billing cycles changes the math — it defers the next "should I renew?" decision, gives the streaming habit longer to form, and smooths users across the gaps between tournaments. This required new backend capability (multi-cycle discounting) and dedicated UDF copy.
The pricing copy itself flexes: a single-month discount reads "₹X only for the 1st month — renew at ₹Y after", while a multi-cycle offer reads "₹X for the next N months; ₹Y after." By committing a hesitating user across multiple cycles at a low-risk price, the offer extends time-on-subscription rather than just deferring churn by a month — by the time it ends, they've usually watched enough sport for the full price to feel justified.
Each user's discount depth is set by their MRETL bucket and configured per surface in the Renew Flow Configurator (Monthly / Yearly tabs × High / Medium / Low sections, each with a % and a cycle count):
Against a Nov '24 baseline of 53% mandate cancellation and 47–53% M1 renewal, post-launch reads (weekly cohort averages from production & Juspay funnels; no A/B in Phase 1) showed the renewal system working.
| Month | Home Contrib / Renew |
Match Detail Contrib / Renew |
Pass Details Contrib / Renew |
Profile Contrib / Renew |
|---|---|---|---|---|
| 2025-05 | 5% / 78% | 21% / 69% | 4% / 74% | 8% / 81% |
| 2025-06 | 13% / 80% | 57% / 69% | 7% / 75% | 14% / 80% |
| 2025-07 | 20% / 77% | 57% / 67% | 7% / 76% | 14% / 80% |
| 2025-08 | 17% / 75% | 61% / 61% | 8% / 69% | 14% / 76% |
| 2025-09 | 14% / 64% | 61% / 55% | 10% / 64% | 15% / 70% |
| 2025-10 | 12% / 67% | 57% / 55% | 12% / 62% | 19% / 69% |
Contribution = share of total renewals originating on that surface; Renew% = of users who reached the renewal page from that surface, the share who renewed. The Match Detail Page grew from 21% → ~57–61% of renewal contribution as the in-context CTA scaled — validating the "meet intent where it's highest" bet.
With the core renewal engine live on Android, Phase 2 extends it to harder cases — most notably a Cancellation Prevention Flow that intervenes before the mandate is revoked, and a PL-upsell that pulls one-time buyers into recurring.
The system is the design. The screens were the easy part — the real work was the window, bucketing and copy logic that decide which experience each user gets. A good rule-set scales further than a good screen.
Context beats frequency. The Match Detail Page — where intent is highest — became the biggest renewal driver. Meeting users mid-interest mattered far more than nudging more often.
Respectful retention compounds. Holding discounts until post-expiry and only deep-discounting where it pays off protected both trust and margin.