ń# Simplifications — Channel-Tiered Subscriptions
Levers to reduce complexity on the channel-tiered-subscriptions 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.TieredPlansEnabledflag- 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— addTrialStartDate,TrialEndDate,IsTrialConvertedcore/PicTv.Application/Webhooks/Commands/StripeWebhook/StripeWebhookCommandHandler.cs:85— guard for$0trial-create invoicecore/PicTv.Application/Services/PaymentProviders/StripeProviderService.cs— overloadCreateSubscriptionAsyncto passtrial_period_days- New:
Premium/Commands/StartTrialSubscription/— creates Stripe Checkout session insubscriptionmode 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.jsonentry:TieredPremiumChannelId
What you don't need to build
- ❌ New enums (
PremiumContentScope,PremiumPlanTier) - ❌
ChannelPremiumConfigflag changes - ❌ PDF upload command / signed URLs / email template
- ❌
HasAccessAsyncvideo-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"
- Revenue flowing in 1–2 weeks instead of 4–6.
- Real user feedback before committing to the expensive pieces. Maybe nobody uses the livestream-only tier; maybe everyone wants the PDF.
- No App Store rejection risk in v1. Apple subscription review is the longest-tail risk in the original plan.
- Stripe Checkout handles the boring stuff correctly the first time — card validation, 3DS, retries, tax. Custom flows accumulate edge-case bugs.
- The existing single-tier Premium keeps working for every other channel. Zero blast radius outside the one target channel.