# Premium Entitlement Specification ## Purpose Track whether the current user holds the permanent, non-consumable premium unlock, and expose that flag to every gated surface (freemium-gating, ad-display, android-auto-media) both from the UI layer and headlessly (Android Auto, registered before `runApp`). ## Requirements ### Requirement: One-Time Non-Consumable Purchase The system MUST let the user buy a single non-consumable product via `in_app_purchase` from the Settings row or any contextual upsell. On a successful purchase, entitlement MUST flip to premium immediately, app-wide, with no restart required. #### Scenario: Successful purchase - GIVEN a free-tier user taps "buy premium" from Settings or a contextual upsell - WHEN the purchase completes successfully - THEN entitlement becomes premium immediately, without restarting the app #### Scenario: Purchase cancelled or failed - GIVEN a free-tier user starts the purchase flow - WHEN the user cancels or the purchase fails - THEN entitlement remains free tier, and no charge or partial state is left behind #### Scenario: Already-purchased attempt is idempotent - GIVEN a user already holds premium entitlement - WHEN they somehow re-trigger the buy flow - THEN no duplicate charge occurs and entitlement stays premium ### Requirement: Restore Purchases Settings MUST expose a "restore purchases" action that re-queries Play Billing and unlocks entitlement when a prior purchase is found. #### Scenario: Restore finds a prior purchase - GIVEN a reinstall or new device with no local entitlement flag - WHEN the user taps "restore purchases" and a valid purchase exists on the Play account - THEN entitlement becomes premium #### Scenario: Restore finds nothing - GIVEN a user with no prior purchase - WHEN they tap "restore purchases" - THEN the user stays on the free tier with a clear, non-error-looking result (not a crash or ambiguous failure) ### Requirement: Persisted, Fail-Open Entitlement Entitlement MUST persist locally under a versioned key (e.g. `compra_premium_v1`) and MUST be readable offline. If an entitlement check cannot complete (no network, Play Billing unreachable), the system MUST fail-open: trust the last persisted flag rather than lock out a payer. (Previously: no entitlement concept existed.) #### Scenario: Offline cold start after purchase - GIVEN a user purchased premium previously - WHEN they open the app fully offline - THEN premium entitlement is honored from the persisted flag #### Scenario: Failed check does not falsely grant premium - GIVEN a free-tier user with no persisted premium flag - WHEN an entitlement check fails - THEN the user remains free tier (fail-open trusts the last flag, it does not invent one) ### Requirement: Headless-Safe Entitlement Read Entitlement MUST be resolvable via a prefs-lazy fallback (no `BuildContext`/`Provider` dependency), for callers such as `PluriWaveAudioHandler` that register before the widget tree exists. #### Scenario: Android Auto cold start - GIVEN the audio handler is constructed before `runApp` - WHEN it needs to know the current entitlement to build the browse tree - THEN it resolves entitlement via the prefs-lazy path without requiring a `Provider` ### Requirement: Instant Unlock Propagation A successful purchase or restore MUST notify all listeners (top banner, gated screens, cached Android Auto entitlement) immediately, without an app restart. #### Scenario: Banner disappears immediately on purchase - GIVEN the ad banner is visible when the user completes a purchase - WHEN the purchase confirms - THEN the banner disappears immediately, with no restart