The problem
Subscription businesses quietly lose 5–10% of their revenue every month to involuntary churn — cards that expire, banks that decline, networks that time out. Most teams know it's happening, but they can't see it. The Stripe dashboard tells you a payment failed. It doesn't tell you which retry rule saved the account, which cohort of customers is most fragile, or how long it took to recover the revenue.
Papilio needed a product that made that invisible layer visible — and let merchants actually change something about it.
What we built
We built the whole surface: recovery analytics, retry queue, flows, customer view, and per-rule performance drilldowns. Recovery rate, time-to-capture and net revenue saved on the front page, so the answer to 'is this working?' takes ten seconds, not a data pull.
Under the surface, we designed the schema, wired up Stripe webhooks, and built the ingest pipeline that keeps the analytics view fresh without polling. The retry rule engine and its drilldowns were the piece that took the longest to get right — the honest answer to 'which retry logic actually works' turns out to depend heavily on cohort and card network, and the UI had to make that legible without drowning the operator in options.
Stack and infrastructure
- React + TypeScript on the client with TanStack Query for cache-first data flow.
- Postgres for the source of truth; event-sourced tables for the payment lifecycle.
- Stripe webhooks feeding an idempotent ingest layer — every event is safe to replay.
- Design system built alongside the product so new modules stay visually consistent without extra work.
Outcome
The analytics surface is the daily driver for merchants tuning their recovery rules. New surfaces — flows, customer view, and rule-level drilldowns — ship without triggering a redesign, because the design system carries the weight. The product roadmap is now expanding outwards from a stable core, not fighting the foundation.
