Monetization — Requirements Traceability & Status
2026-07-18 · updated 2026-07-20 · every requirement from lo/lissin-monetization-plan/ (8 docs) mapped to done / partial / not-started, with how & what's left. Non-code work foregrounded.
The engineering stack is integrated through backend #522 (+ #523/#524), admin #168, and iOS #317. It remains observe-only / additive / flag-gated. The offer-ladder dismissal correction is pushed separately as c6305160 and awaits merge. Remaining work is product sign-off, App Store & RevenueCat configuration, analytics, sandbox QA, build upload, and the release ramp.
Overview & progress by area
Percentages are judgment calls to show shape, not precise metrics.
The non-code work — the big picture
These are the requirement areas that are not primarily engineering. They gate go-live and are mostly owned by product / growth / ops:
| Area | Owner | Status | Where it stands |
|---|---|---|---|
| Product sign-offs | Product | PARTIAL | Core quotas confirmed; prices, discount durations, some caps still on PRD defaults. |
| App Store Connect | iOS/Billing | NOT STARTED | Base products exist; trial intro offer, rescue products, promo/win-back, tax/banking, privacy links not configured. |
| RevenueCat dashboard | iOS/Billing | PARTIAL | Entitlements + default offering exist; rescue offerings, placements, paywall-UI events, Customer Center not set. |
| Grandfathering operations | Admin/Support | DONE | Built across backend #515 + admin #163: single-user grants, cohort builder→preview→bulk apply, hand-picked multi-select, and campaign audit. Hardening (two-person approval, expiry dashboard) is a follow-up. |
| Nudges & lifecycle copy | Product/Growth | PARTIAL | Usage nudges + trial-countdown banner shipped on iOS (#312); frequency caps, suppression policy, and copy ownership still product/growth work. |
| Analytics plan | Data/Growth | NOT STARTED | Event/metric list defined in PRD; not instrumented. |
| QA / store test matrix | QA | NOT STARTED | Sandbox purchase / trial / offer / restore matrix not run. |
Phase 0 — product decisions & sign-offs (Product)
The PRD marks these as "must confirm before store config or enforcement ships."
| Decision | Status | Detail |
|---|---|---|
| Recurring-show generation (shows/week) | DONE | Confirmed: Free 1, Plus-trial 1 total, Plus 2, Pro 5. Encoded & test-locked. |
| One-time episode generation (episodes/week) | DONE | Confirmed: Free 2/week, Plus-trial 4 total in trial, Plus 10/week, Pro 50/week. |
| Free Explore playback | DONE | Confirmed: 45 min/week, rolls one week (cap 90). |
| Standard Plus/Pro prices | PENDING | Weekly/monthly/yearly prices need sign-off before store config. |
| 50% / 65% discount duration | PENDING | First-period vs multi-period; cadence shown (monthly recommended). |
| One-time + file rollover caps | DEFAULTED | One-time weekly caps default to 2x base (Free 4 / Plus 20 / Pro 100); files Plus 6 / Pro 50 — confirm. |
| Pro files normalization (25/wk @25MB) | DEFAULTED | Derived from the ambiguous "14-5 minutes" spoken requirement — confirm. |
| Grandfather cohort / permanent-access policy | PENDING | Cutoff, plan, dates, revocable?, exclusions. |
| Nudge frequency caps & copy ownership | PENDING | Per-session / per-72h caps; who writes copy. |
| Storefront availability / localized pricing | PENDING | Which storefronts; localization strategy. |
How the defaults are safe: all numbers live in a versioned plan_versions DB table (seeded from code), so a sign-off change is a data edit, not a redeploy — and later an admin-dashboard edit.
App Store Connect configuration (iOS/Billing)
| Item | Status | Detail |
|---|---|---|
| Paid Apps Agreement, tax/banking | NOT STARTED | Prerequisite for any paid IAP. |
| Single subscription group (Plus/Pro) | CHECK | 6 products exist (app.lissin.audio.{plus,pro}.{weekly,monthly,yearly}); verify one group + Pro>Plus service level. |
| Localized names/descriptions/prices | NOT STARTED | Plus review screenshots. |
| 3-day Free introductory offer (Plus) | NOT STARTED | Backend already projects period_type=TRIAL; the App Store intro offer itself must be created. |
| 50% / 65% rescue products/offers | NOT STARTED | Real intro-priced products for the rescue ladder. |
| Promotional / win-back offers | NOT STARTED | For existing/lapsed subscribers. |
| Privacy Policy / Terms links | NOT STARTED | Required on subscription surfaces. |
RevenueCat configuration (iOS/Billing)
| Item | Status | Detail |
|---|---|---|
| App, SDK key, ASC API key, IAP key | PARTIAL | Public SDK key ships in the app; verify the server-side keys. |
Entitlements plus / pro | DONE | Exist and are what the backend derives tier from. |
| Default offering | PARTIAL | A current offering exists; rescue offerings do not. |
Offerings default / save50 / save65, win-back | NOT STARTED | iOS already consumes these offering ids (#311, graceful fallback) — the user must still create them in the RC dashboard for the ladder to present real prices. |
| Paywalls per offering (Paywall Builder) | NOT STARTED | With eligibility-aware trial copy + renewal terms. |
| RevenueCat Placements (per iOS registry) | NOT STARTED | onboarding, locked-episode, generation, cadence, document, remix, pro-sources, etc. |
| Paywall-UI webhook events | NOT STARTED | Impressions/closes/exit-offers. |
| Dev vs prod webhooks + secrets | PARTIAL | Dev webhook secret set & verified; prod secret still required before go-live. |
| Customer Center (management) | NOT STARTED | Optional branded self-service; else Apple native sheet. |
Trial & offer ladder
| Requirement | Status | How / what's left |
|---|---|---|
| 3-day trial — backend projection | DONE | Webhook maps TRIAL → trial phase + window; restricted plus_trial limits apply. (code) |
| 3-day trial — App Store intro offer | NOT STARTED | Create the Free 3-day intro offer on Plus products. (store config) |
| 3-day trial — iOS UI | DONE | Trial countdown banner on home + usage nudges shipped on iOS #312; renders on live balances. (code) |
| Offer state machine (standard→50→65) | DONE | iOS #311 rescue ladder: standard→50%→65% downsell on dismiss; persists/resumes; resets on purchase; commits stage only after the offering loads; re-entrancy guarded (13 tests). (code) |
| Exit-offer presentation | PARTIAL | Core ladder is merged. Dismissal-routing fix c6305160 is pushed but awaits merge; exact RC save50/save65 offerings and sandbox verification are still required. |
Admin & grandfathering (operations) (Admin/Support)
| Requirement | Status | How / what's left |
|---|---|---|
| Single-user grant / revoke / history / attribution | DONE | On PR #163 → dev (mergeable); grant overlays store access, never lowers a paid tier; panel now shows phase/trial/balances. (code) |
| Grant-as-overlay + downgrade guard | DONE | Backend: a grant only applies if ≥ store tier; expiry reveals underlying tier. (code) |
| Cohort builder + filters | DONE | backend #515 + admin #163: cohort builder with allow-listed filters (no raw SQL), builder→preview→two-click apply in the dashboard. (code) |
| Hand-picked user selection | DONE | #515 hand-picked user-ID preview/apply; admin Users page multi-select → "Grandfather selected (N)". (code) |
| Dry-run preview (counts, conflicts) | DONE | Cohort preview returns counts before apply; overlay-safe against existing grants. (code) |
| Bulk batch apply (idempotent, bounded) | DONE | #515 idempotent bulk apply, bounded (cap 1000 / hard 5000), overlay-safe; 29/29 tests. (code) |
| Audit view | DONE | Campaign audit list + per-campaign drill-down (#515) surfaced as an admin grandfathering audit page + nav (#163). (code) |
| Two-person approval + expiry dashboard | NOT STARTED | Grandfather-hardening follow-up: reauth/approver token above a threshold, and an expiry-timeline dashboard. |
| Backend-enforced billing roles | PARTIAL | Admin grandfathering UI + endpoints gated on billing.manage; admin-security review comments fixed. Broader per-permission enforcement on all billing routes still hardening. |
In-app nudges & lifecycle (Product/Growth)
| Requirement | Status | Detail |
|---|---|---|
| Jump-notification surfaces | DONE | Home usage nudges + trial-countdown banner shipped on iOS #312, driven by live meter balances. (code) |
| Eligible moments | PARTIAL | Usage-approaching-limit and trial-ending moments fire on home (#312); rollover-credit / locked-feature / campaign-offer moments not yet surfaced. |
| Frequency caps & suppression | NOT STARTED | 1 nudge/session, 1 push/72h, none within 24h of dismissal; suppress during playback/creation/onboarding — policy/caps not yet implemented. |
| Server-side exposure persistence | NOT STARTED | Cross-device frequency caps (needs an exposure table + decision API). |
Subscription management UX (iOS IPRD) (iOS)
| Requirement | Status | How / what's left |
|---|---|---|
| Plan & Billing destination | DONE | iOS #312 rebuilds Plan & Billing on the live-balance access contract (phase, trial, per-meter balances). (code) |
| State matrix (trial/paid/canceled-active/grace/grandfathered/pending-change) | PARTIAL | Trial / paid / grandfathered states render from the live contract; grace / canceled-active / pending-change still need per-state screens (and backend pending-change fields). |
| Native manage-subscriptions sheet | NOT STARTED | Apple sheet (or RevenueCat Customer Center) for change/cancel. |
| Upgrade/downgrade timing + pending-change | NOT STARTED | Backend must add pending-product/effective-date fields; iOS shows "Pro until [date], then Plus". |
| Restore / nothing-to-restore / offline / delayed-activation | PARTIAL | Restore + server-authoritative refresh exist; full edge-state polish pending. |
Analytics & success metrics (Data/Growth)
| Requirement | Status | Detail |
|---|---|---|
| Events (gate/paywall/purchase/trial/quota/nudge/grant) | NOT STARTED | Defined in PRD; not emitted. Some playback analytics already exist via PostHog. |
| Dimensions (placement, offer stage, tier, phase, quota, cohort…) | NOT STARTED | Needs consistent tagging across surfaces. |
| Primary metrics (paid conv, trial→paid, LTV, mismatch rate…) | NOT STARTED | Dashboards to be built. |
| Observe-only usage data | DONE | #514 immutable usage ledger meters generation / upload / Explore-playback / SSE and records "would-deny" logs to reconcile before enforcement is flipped on. (code) |
Store test matrix & QA (QA)
| Requirement | Status | Detail |
|---|---|---|
| Sandbox trial start / ineligible standard terms | NOT STARTED | Needs the App Store intro offer configured first. |
| Standard→50→65 exit flows | PARTIAL | iOS ladder unit-tested (13 tests, #311); end-to-end sandbox run needs the RC save50/save65 offerings configured first. |
| Convert / cancel / expire / billing issue | NOT STARTED | Lifecycle matrix. |
| Upgrade / downgrade / restore / transfer / refund | PARTIAL | Backend handles TRANSFER + expiry + downgrade guard (unit-tested); full end-to-end sandbox not run. |
| Dev/TestFlight never projects to prod | PARTIAL | Env now recorded per event; verify separation in dashboards. |
| Webhook replay / out-of-order / idempotency | DONE | Dedup by provider event id + stale-event ordering guard, unit-tested. (code) |
Engineering (code) — what's done (Backend/iOS/Admin)
The original six-PR foundation and the nine creator-feature workstreams are now integrated through backend #522 (+ #523/#524), admin #168, and iOS #317. They remain observe-only / additive / flag-gated. Historical validation recorded 356 iOS tests passing on Xcode 26.5, 29/29 backend billing+grandfather tests, and clean admin builds. Dismissal fix c6305160 awaits merge and fresh Xcode/sandbox validation.
- Backend #514 (→dev, 17 commits): versioned plan contract →
plan_versionsDB (dashboard-editable) → immutableusage_ledger+usage_accounts→QuotaService→ expandedGET /billing/access(phase, planVersion, serverTime, trial, per-meter balances) → RevenueCat webhook trial-phase projection → observe-only metering (generation / upload / Explore-playback / SSE) → flag-gated 402 enforcement (BILLING_ENFORCE[_DOMAIN], default OFF, fail-open). 3 migrations. - Backend #515 (→dev, 4 commits): grandfathering — cohort preview + idempotent bulk apply (allow-listed filters) and hand-picked user-ID selection (preview/apply) + campaign audit (list + per-campaign drill-down). Idempotent, bounded (cap 1000 / hard 5000), overlay-safe. 29/29 tests.
- Admin #163 (→dev, 11 commits): grants / revoke / history / operator-attribution; expanded access contract display; cohort grandfathering UI (builder→preview→two-click apply); Users-page multi-select → "Grandfather selected (N)"; grandfathering audit page + nav. Gated on
billing.manage. - iOS #309 (→develop, 9 commits): decodes the expanded
/billing/access, aligns the fallback plan to the Phase-0 numbers, and maps 402 →PaywallPlacement. - iOS #311 (merged): offer-stage rescue ladder (standard→50%→65% downsell; persists/resumes; resets on purchase). Follow-up
c6305160routes RevenueCat Close through the ladder, blocks swipe bypass, and requires exact discount offerings; it awaits merge. - iOS #312 (→develop, 12 commits, stacks on #311): Phase 4 — gates (transcript, playback-speed ceiling free 1.0 / plus 1.5 / pro 2.0, premium soundscapes, Explore listening-time via meter balance, file size
fileMaxBytes+ upload count) via a fail-openEntitlementGate; trial countdown banner + usage nudges on home; Plan & Billing on live balances.pro_sourcesintentionally not wired (no such feature yet).
Enforcement is real but dark: backend metering + 402s cover generation / upload / Explore-playback / SSE behind BILLING_ENFORCE[_DOMAIN] (default OFF, fail-open); iOS gates run through a fail-open EntitlementGate. Not wired: pro_sources (intentional — the feature doesn't exist yet), and grandfather hardening (two-person approval, expiry dashboard) is a follow-up.
Rollout phases (PRD doc 07)
| Phase | Status | Note |
|---|---|---|
| 0 · Product lock | PARTIAL | Core numbers confirmed; prices/policy pending. |
| 1 · Foundation merge + env | DONE | Integrated through backend #522 (+ #523/#524), admin #168, and iOS #317. Offer dismissal follow-up c6305160 remains a release prerequisite. |
| 2 · Store & RevenueCat catalog | NOT STARTED | The largest remaining non-code block (user-owned). |
| 3 · Backend DB, quota, enforcement | DONE | Shipped #514/#515 (enforcement behind flags, default OFF). |
| 4 · iOS placements & management | DONE | Gates, offer-stage ladder, trial UI, nudges, and live-balance Plan & Billing all shipped (#309/#311/#312); RC placement config lives in phase 2. |
| 5 · Admin grandfathering & ops | DONE | Single-user + cohort + hand-picked + bulk + audit shipped (#515/#163); hardening (approval/expiry) a follow-up. |
| 6 · QA & release ramp | NOT STARTED | After store config exists. |
| 7 · Experimentation | NOT STARTED | Post-stability. |
Prod webhook secret verified with a real event · backend usage/denials authoritative (not placeholders) · trial/price/savings/renewal match the App Store sheet · grandfather cohort applied & audited · restore/refund/expiry/transfer tested · kill switches + incident playbook ready · no legacy tier consumer contradicts canonical access. Most of these are store-config + QA + ops, not code.
What's left, prioritized
The engineering is done; what remains is mostly the user's / non-code work — merging, store & RevenueCat setup, sign-offs, and the QA/enforcement ramp.
- Merge the 6 PRs — iOS order #309→#311→#312; backend one at a time (no concurrency guard). User
- App Store Connect + RevenueCat catalog — create the
default/save50/save65offerings, the 3-day trial intro offer, placements, paywalls, and the prod webhook. The biggest non-code block and the gate for real purchases. User / iOS-Billing - Product price & discount sign-offs (standard prices, 50% / 65% durations, remaining caps, cohort/permanent policy, nudge caps). Product
- Flip enforcement flags → QA → cohort → % ramp — reconcile "would-deny" logs, run the sandbox/TestFlight matrix, then ramp
BILLING_ENFORCEby domain. User / QA - Analytics instrumentation + dashboards (events, dimensions, conversion/trial/LTV metrics). Data
- Non-blocking follow-ups — auto-present paywall on live 402; a DEBUG
-billingScenarioflag for screenshots; wirepro_sourceswhen that feature exists; grandfather hardening (two-person approval, expiry dashboard). Eng (later)
Sources: lo/lissin-monetization-plan/ (01 master PRD, 03 RevenueCat/App-Store, 04 backend/db, 05 iOS paywalls, 06 admin/grandfathering, 07 rollout, 08 iOS subscription-mgmt IPRD). Code status from PRs #514 & #515 (backend→dev), #163 (admin→dev), and #309 / #311 / #312 (iOS→develop).