ń# Simplifications — Channel-Tiered Subscriptions Levers to reduce complexity on the [channel-tiered-subscriptions](./channel-tiered-subscriptions.md) feature, ranked by **complexity-reduction-per-feature-loss**. The first three are the big wins. --- ## High-impact cuts ### 1. Ship web-only first; defer mobile IAP entirely Biggest single win. Netflix and Spotify do this on iOS — users can't subscribe inside the app; they sign up on the website, and the app just recognizes the existing subscription on login. Apple explicitly allows this. **Saves:** - Phase 4 entirely (~1–2 weeks of code) - 1–2 weeks of App Store review latency - 15–30% commission on every mobile-originated subscription **Drops complexity:** 8 → 6 --- ### 2. Drop the "Livestream-only" tier The single most expensive piece of code: real-time event-state checking, can't be cached, race conditions when broadcasts end, and only benefits one of four plans. If livestream gating is the killer feature, ship it as v2. **Drops complexity:** 6 → 5 --- ### 3. Use Stripe Checkout + Customer Portal Stripe hosts: - Trial sign-up page - Card collection - Upgrade / downgrade / cancel UI - Dunning (failed-payment retry flow) - Receipts and invoices - Trial-ending reminder emails (configurable in Dashboard) Your backend only reacts to webhooks. Skip building any of that UI yourself. **Saves:** ~1 week of frontend + backend plumbing. --- ## Medium-impact cuts ### 4. Drop the 6-month plan Standard Stripe intervals are month/year. Six months requires `interval_count: 6` plumbing and a new `BillingIntervalMonths` field. Most SaaS offers monthly + yearly only. **If you drop this**, you can use the existing `PremiumPlanType` enum unchanged. --- ### 5. Drop the PDF perk Requires upload infra, signed URLs, idempotency tracking, a new `EmailType`, and email template work. The channel owner can email subscribers the PDF manually using Mailchimp or their own tool — they own the subscriber list anyway. **Removes:** an entire mini-feature. --- ### 6. Skip "coexistence with legacy Premium" — make it a hard switch A channel uses either the old single-tier OR the new tiered, never both. Removes a class of edge cases (user has both, upgrade collision, refund collision). Channels currently on legacy stay on legacy; new ones use tiered. --- ### 7. Hardcode the one channel for v1 The original feature request was for one specific channel. If only one channel needs it today, gate behind `TieredPremiumChannelId` in `appsettings.json`. **Skips:** - Admin UI for per-channel opt-in - `ChannelPremiumConfig.TieredPlansEnabled` flag - Part of the channel migration Make it configurable in v2 if a second channel asks. --- ### 8. Skip the Redis cache redesign Keep the existing binary "does this user have access to this channel?" cache. For tier-aware playback decisions, just do a DB query on the video-playback path. **Trade-off:** ~5–20ms slower per video load. Optimize later if metrics show it matters. --- ### 9. Don't build trial-reminder emails Stripe sends them automatically when enabled in the Dashboard — 7-day and 3-day before trial end. Skip the `customer.subscription.trial_will_end` webhook handler. --- ## Low-impact cuts ### 10. Skip the refund-revocation fix It's a known pre-existing gap, unrelated to this feature. File a separate ticket. ### 11. Skip `CheckTrialEligibility` as a separate query Just include `trialEligible: bool` on the existing `GetChannelTieredPlans` response. --- ## Recommended MVP | Decision | MVP | Eventually | |---|---|---| | Payment rails | Stripe web only | + Apple IAP, + Google Play | | Number of plans | 2 (Monthly · All + Yearly · All) | + 6-month, + Livestream-only | | Trial | 14 days, card upfront via Stripe Checkout | (same) | | PDF perk | Skip | Add later if subscribers ask | | Channel scope | Hardcoded `TieredPremiumChannelId` config | Per-channel admin toggle | | Access control | Reuse existing binary `HasAccessAsync` | Add video-aware overload when livestream-only tier ships | | Frontend | Stripe Checkout (hosted) + Customer Portal | Custom UI if needed | | Webhook lifecycle | Fix the `$0` invoice bug + add `IsTrialConverted` flag | (same) | **MVP complexity:** ~4/10 **Time estimate:** ~1 week of backend work + a few days of frontend integration with Stripe's hosted pages. --- ## What this MVP actually requires ### Code changes (much smaller scope) - `core/PicTv.Domain/Entities/PremiumSubscription.cs` — add `TrialStartDate`, `TrialEndDate`, `IsTrialConverted` - `core/PicTv.Application/Webhooks/Commands/StripeWebhook/StripeWebhookCommandHandler.cs:85` — guard for `$0` trial-create invoice - `core/PicTv.Application/Services/PaymentProviders/StripeProviderService.cs` — overload `CreateSubscriptionAsync` to pass `trial_period_days` - New: `Premium/Commands/StartTrialSubscription/` — creates Stripe Checkout session in `subscription` mode with 14-day trial, returns redirect URL - New: `Premium/Commands/OpenCustomerPortal/` — creates Stripe Billing Portal session for the user, returns redirect URL - One EF migration for the 3 new columns - One `appsettings.json` entry: `TieredPremiumChannelId` ### What you don't need to build - ❌ New enums (`PremiumContentScope`, `PremiumPlanTier`) - ❌ `ChannelPremiumConfig` flag changes - ❌ PDF upload command / signed URLs / email template - ❌ `HasAccessAsync` video-aware overload - ❌ Redis cache schema change - ❌ Trial-reminder webhook handler - ❌ Mobile IAP commands or webhook subscription-type branches - ❌ Provider discriminator on subscriptions - ❌ Apple/Google product ID columns on plans - ❌ Admin UI for per-channel toggle - ❌ Custom subscribe / cancel / upgrade UI --- ## Re-adding the cut pieces later Every cut above is independently re-addable when there is evidence the feature is worth the cost. Approximate re-addition cost from MVP: | Add-back | Cost | |---|---| | 6-month plan | ~1 day (one Stripe Price + one DB field) | | PDF perk | ~3 days | | Livestream-only tier | ~1 week (access-control rewrite + caching strategy) | | Per-channel admin toggle | ~2 days | | Apple IAP | ~1 week + App Store review | | Google Play Billing | ~3–5 days | | Custom frontend (no Stripe Checkout) | ~1 week | --- ## Why this MVP-first path is better than "build it all" 1. **Revenue flowing in 1–2 weeks** instead of 4–6. 2. **Real user feedback** before committing to the expensive pieces. Maybe nobody uses the livestream-only tier; maybe everyone wants the PDF. 3. **No App Store rejection risk** in v1. Apple subscription review is the longest-tail risk in the original plan. 4. **Stripe Checkout handles the boring stuff** correctly the first time — card validation, 3DS, retries, tax. Custom flows accumulate edge-case bugs. 5. **The existing single-tier Premium keeps working** for every other channel. Zero blast radius outside the one target channel.