Skip to content

Conversation

@vegaro
Copy link
Member

@vegaro vegaro commented Jan 19, 2026

Summary

Adds support for product changes (upgrades/downgrades) and Play Store offer configuration in paywalls.

What's Changed

This PR introduces the data models and logic needed to handle product changes and Play Store offer configuration in paywalls.

New Configuration Models

ProductChangeConfig configures how upgrades and downgrades work. Upgrades default to CHARGE_PRORATED_PRICE (Google's recommendation) and downgrades default to DEFERRED. The SerializableReplacementMode enum maps to GoogleReplacementMode for the different proration options.

For Play Store offers, PlayStoreOfferConfig works similarly to iOS's applePromoOfferProductCode, specifying which Play Store offer to use for a specific package.

How Offer Resolution Works

The new PlayStoreOfferResolver handles finding the right subscription option based on the configuration. When a package has a PlayStoreOfferConfig, the resolver searches the product's subscription options for one matching the configured offer ID. If it finds a match, it returns a ConfiguredOffer result. If the offer isn't found or no configuration exists, it falls back to the product's default option wrapped in a NoConfiguration result.

How Base Plan Changes Work

When a user with an active subscription purchases a different package, the SDK now checks if both packages grant the same entitlement. If they do, it treats this as a product change rather than creating a parallel subscription.

The ProductChangeConfig determines the replacement mode based on whether it's an upgrade or downgrade (calculated by comparing price per unit time). For upgrades, the default CHARGE_PRORATED_PRICE charges the price difference immediately and keeps the same billing cycle. For downgrades, the default DEFERRED mode waits until the current subscription expires before switching to the new plan.

This works for any base plan change: monthly to annual, basic to premium, or any other combination where the entitlement stays the same but the underlying product changes.

Variable Processor Updates

VariableProcessorV2 now accepts an optional SubscriptionOption context when processing variables. This means variables can show pricing for specific offers instead of always using the default option. The changes are backward compatible since the parameter is optional.

ViewModel Integration

PaywallViewModelImpl ties everything together. When handling a package purchase, it uses PlayStoreOfferResolver to get the configured offer (if any), then passes the resolved subscription option to the variable processor. This ensures that the pricing displayed in the paywall matches the actual offer the user will get.

Why?

This enables three important scenarios:

First, users can now upgrade or downgrade subscriptions with proper proration handling instead of ending up with parallel subscriptions. The ProductChangeConfig specifies exactly how to handle the transition.

Second, specific promotional offers can be configured in paywalls. This is useful for win-back campaigns, trial extensions, or any scenario where a specific offer should be shown in the paywall instead of just the default.

Third, paywall variables now show accurate pricing for configured offers. Before this, variables would always use the default option's pricing even if a different offer was configured.

@codecov
Copy link

codecov bot commented Jan 19, 2026

Codecov Report

❌ Patch coverage is 14.28571% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.03%. Comparing base (26d175d) to head (a573778).

Files with missing lines Patch % Lines
.../paywalls/components/common/ProductChangeConfig.kt 0.00% 15 Missing ⚠️
...paywalls/components/common/PlayStoreOfferConfig.kt 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3031      +/-   ##
==========================================
- Coverage   79.13%   79.03%   -0.11%     
==========================================
  Files         341      343       +2     
  Lines       13567    13588      +21     
  Branches     1824     1827       +3     
==========================================
+ Hits        10736    10739       +3     
- Misses       2075     2093      +18     
  Partials      756      756              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants