Use this page with AI
Copy this into your coding assistant and add your request.
Read https://openiap.dev/docs/features/subscription/upgrade-downgrade and https://openiap.dev/llms.txt. Follow the reading instructions, detailed reference, and linked guides relevant to my task before making changes.
Inspect my existing project and reuse its framework and conventions. Ask me for missing product decisions. Implement the requested behavior and run the applicable checks.
Show the working result, the commands and actual test results, and any remaining limitations. Keep your explanation brief.
My request: [describe what customers should be able to do]Subscription Upgrade & Downgrade
Understanding how each platform handles subscription tier changes is crucial for correctly detecting and displaying upgrade/downgrade states in your app. Both iOS and Android support subscription changes, but they handle them differently.
Overview#
When users change their subscription tier (e.g., from monthly to yearly, or from basic to premium), both platforms support upgrades and downgrades:
- Upgrades: Moving to a higher-tier or longer-duration subscription
- Downgrades: Moving to a lower-tier or shorter-duration subscription
The key difference is in how and when these changes take effect. Select your platform below to see specific implementation details:
Key Fields to Understand#
Upgrade Behavior (Charged Immediately)#
When a user upgrades to a higher-tier subscription (e.g., monthly → yearly, basic → premium):
- User is charged immediately for the new subscription tier
- A new transaction is created (though
Transaction.updatesmay initially show the old product - this is a known StoreKit issue) autoRenewPreferenceimmediately reflects the new producttransaction.productIDupdates within a few minutes
Use pendingUpgradeProductId to detect when an upgrade is in progress, especially during the brief period when productId hasn't updated yet.
Downgrade Behavior (Applied at Next Renewal)#
When a user downgrades to a lower-tier subscription (e.g., yearly → monthly, premium → basic):
- No new transaction is created
- Change is reflected in the
renewalInfoobject only autoRenewPreferenceindicates the new (downgraded) product (Stack Overflow - autoRenewalPreference usage)transaction.productIDremains unchanged until the renewal date- User retains access to current tier until expiration
Check autoRenewPreference against productId to detect scheduled downgrades and inform users when the change will take effect.
Using pendingUpgradeProductId#
The pendingUpgradeProductId field is specifically designed to detect subscription tier changes. It is automatically calculated by comparing productID and autoRenewPreference.
How it works
The field is automatically calculated by comparing autoRenewPreference and productId:
Usage in your app
Simply check if pendingUpgradeProductId exists to detect tier changes in progress:
Best Practices#
- Always check pendingUpgradeProductId when displaying subscription status
- Show upgrade-in-progress UI when
pendingUpgradeProductIdis present - For downgrades, inform users when the change will take effect (at renewal)
- Listen to purchase events to update UI when
productIdfinally updates - Test both scenarios in sandbox environment to understand the timing
Here's a complete React component that demonstrates all best practices for handling subscription tier changes:
Official References#
- Apple · App Store Subscriptions
Subscription groups, upgrade/downgrade hierarchy, and tier order - Apple · Product.SubscriptionInfo.RenewalInfo
autoRenewPreferenceis the source of truth for the next-renewed product ID - Apple Developer Forums: Auto-renewing Subscription Updates
Explains why Transaction.updates may show old product after upgrade - Apple Developer Forums: How to know when user upgrades/downgrades
Official guidance on using autoRenewPreference vs productID - Stack Overflow: autoRenewalPreference usage in StoreKit 2
Community discussion on upgrade vs downgrade transaction behavior