Skip to content

Conversation

@cmonfortep
Copy link
Contributor

@cmonfortep cmonfortep commented Dec 17, 2025

Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1212502267333651?focus=true

Description

Supports BE v2 features endpoint
Enables FE V2 messaging
Introduces FF for transition

Steps to test this PR

Feature 1

  • Fresh install internal release (or remove applicationIdSuffix ".debug" in build.gradle) so it can obtain subscripton products and plans from Play Store
  • skip onboarding
  • Navigate to settings
  • Ensure Subscription option visible
  • Purchase Subscription
  • Ensure all behave as usual (including duck.ai visibility)

Feature 2

  • Continue from Feature 1 test
  • Go to feature flag inventory -> enable tierMessagingEnabled
  • Go to settings
  • Ensure Subscription settings still present

Feature 3

  • Fresh install internal release (or remove applicationIdSuffix ".debug" in build.gradle) so it can obtain subscripton products and plans from Play Store
  • skip onboarding
  • Navigate to settings
  • Ensure Subscription option visible
  • Go to feature flag inventory -> enable tierMessagingEnabled
  • Go to settings
  • Ensure Subscription option visible
  • Purchase Subscription
  • Ensure all behave as usual (including duck.ai visibility)

UI changes

Before After
!(Upload before screenshot) (Upload after screenshot)

Note

Adds tier-based subscription messaging backed by v2 features/entitlements, with storage and runtime fallbacks to legacy features behind a flag.

  • Feature Flags:
    • Add privacyPro.tierMessagingEnabled to gate tier-based payloads and FE exposure via getFeatureConfig (useGetSubscriptionTierOptions).
  • API/Model:
    • Use v2 cached endpoint featuresV2(sku) returning tiered features; introduce Entitlement-driven flow.
  • Storage:
    • AuthRepository adds v2 entitlements persistence (set/getFeaturesV2) and falls back to v1 features when needed; SubscriptionsDataStore adds subscriptionEntitlements key.
  • SubscriptionsManager:
    • getSubscriptionOffer now returns SubscriptionOffer with tier and entitlements (legacy features derived); new getEntitlementsForPlan with v2-first, v1 fallback.
    • getFeatures derives from entitlements for current plan.
  • Messaging/UI:
    • Add JS method getSubscriptionTierOptions and tier-based JSON payload building in SubscriptionWebViewViewModel.
    • getFeatureConfig now includes useGetSubscriptionTierOptions flag.
  • Fetch on Startup:
    • SubscriptionFeaturesFetcher fetches v2 entitlements when flag on; otherwise fetches legacy features.
  • Tests:
    • Update/add tests across manager, repository, messaging, view model, and fetcher for v2 entitlements, tier options, and fallbacks.

Written by Cursor Bugbot for commit 8e20b1c. This will update automatically on new commits. Configure here.

@cmonfortep
Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

return@withContext subscriptionsDataStore.subscriptionFeatures
?.let(featuresAdapter::fromJson)
?.get(basePlanId) ?: emptySet()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing V2 fallback when tier flag toggled OFF

The flag transition logic has an asymmetry that could cause issues during rollback. When tierMessagingEnabled is ON, getFeatures falls back from V2 to V1 if V2 is empty. However, when the flag is OFF, it only reads from V1 with no fallback to V2. Combined with the fetcher which exclusively populates V2 when flag is ON (and V1 when flag is OFF), this means: if a user's app ran with the flag ON (V2 populated, V1 empty), and the flag is later toggled OFF remotely, getFeatures returns an empty set until the app restarts and the fetcher repopulates V1. This could temporarily hide subscription offers from affected users during emergency flag rollbacks.


Please tell me if this was useful or not with a 👍 or 👎.

Additional Locations (1)

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expected scenario: Users are in legacy version, we will enable FF for v2.

  • Transition should work
  • We offer a fallback to cover FF change during runtime

Moving from v2 -> v1, not expected. If happens, restarting the app will fix. I think tradeoff is acceptable

@cmonfortep cmonfortep force-pushed the feature/cristian/migrate_subscription_v2_messaging branch from 2178318 to 8e20b1c Compare December 17, 2025 17:18
* with the new tier-based payload structure supporting Plus/Pro tiers.
* The flag is exposed to FE via getFeatureConfig.
*/
@Toggle.DefaultValue(DefaultFeatureValue.FALSE)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Planning to change this to True before merging. Keeping false to facilitate testing scenario.

Copy link
Contributor

@lmac012 lmac012 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good and works as expected 👌

fun refreshSubscriptionPlanFeatures(): Toggle

@Toggle.DefaultValue(DefaultFeatureValue.TRUE)
fun useClientWithCacheForFeatures(): Toggle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this Toggle since we stopped using it in this PR.

class SubscriptionFeaturesFetcher @Inject constructor(
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
private val playBillingManager: PlayBillingManager,
private val subscriptionsService: SubscriptionsService,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dependency is not used anymore

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants