Chapter 7: Subscription Upgrades and Downgrades
Handling plan changes directly means choosing among six replacement modes, understanding how each affects billing timing, building SubscriptionProductReplacementParams, and managing the linkedPurchaseToken chain on your backend after every plan change.
With RevenueCat, the same replacement modes are available and the same billing behaviors apply. The difference is that PurchaseParams handles the parameters in a single builder, and RevenueCat manages the linkedPurchaseToken chain on the backend for you.
The Replacement Modes
RevenueCat exposes Google's replacement modes through GoogleReplacementMode:
Note: KEEP_EXISTING is not currently in GoogleReplacementMode. For add-on style multi-line purchases, use PurchaseParams.Builder.addOnPackages() (experimental API).
Performing a Plan Change
The oldProductId should be the subscription product ID only, do not include the base plan ID. If you pass a string like "basic_monthly:monthly_plan", RevenueCat strips the base plan suffix automatically.
Choosing a Replacement Mode
The same logic as the raw billing handbook applies:
The default mode in PurchaseParams is WITHOUT_PRORATION if you do not set googleReplacementMode().
The linkedPurchaseToken Chain
When a user upgrades, Google creates a new purchase token linked to the old one via linkedPurchaseToken. On the raw billing stack, your backend must follow this chain to identify the current active token and invalidate old ones.
With RevenueCat, the backend handles this automatically. When RevenueCat verifies the new purchase token and sees a linkedPurchaseToken, it resolves the chain, marks the old subscription as replaced, and attributes both tokens to the same user. Your app just reads customerInfo.entitlements["pro"]?.isActive, the correct value is already computed.
You do not write token chain traversal code.
Deferred Upgrades
With DEFERRED mode, Google issues a new purchase token immediately even though the plan switch does not take effect until the next renewal. The new token has two line items: one active (current plan) and one pending (new plan).
RevenueCat handles this correctly. During the deferral window, customerInfo.entitlements reflects the current plan. After the renewal fires and Google sends an RTDN, RevenueCat updates the entitlement to reflect the new plan. Your app just reads customerInfo, the state is always correct.