Subscription pricing is rarely static. Market conditions change, costs fluctuate, and business strategies evolve. At some point, you will likely need to adjust your subscription prices, whether increasing them to reflect added value or decreasing them to attract more users. However, changing subscription prices on Google Play is not as simple as updating a number in a dashboard.
Price changes affect existing subscribers differently than new customers, require specific notification flows, and must be handled carefully to maintain user trust and comply with Google Play’s policies. Luckily, this article will explain how subscription price changes work on Google Play in-depth. We’ll cover:
- Mechanics of changing prices for both new and existing subscribers
- Differences between opt-in and opt-out price increases
- Notification requirements and timelines
- Implementation details (with a walk through example)
- How RevenueCat can help manage price changes gracefully across your subscriber base
How price changes affect different subscriber groups
When you modify a subscription price in the Google Play Console or via the API, the change doesn’t affect all users in the same way. Here’s how Google Play handles new subscribers and existing subscribers differently:
New subscribers
For new purchases, price changes take effect relatively quickly, typically within a few hours of making the change. Once the new price is active, anyone who initiates a new subscription purchase will see and pay the updated price. No special handling is required for this group; they simply see the current price when they reach the purchase screen.
Existing subscribers: the legacy price cohort
Existing subscribers are a different matter entirely. By default, when you change a subscription price, current subscribers are placed into what Google calls a legacy price cohort. These users continue paying their original price at each renewal, completely unaffected by the price change. This default behavior protects users from unexpected billing changes and gives you control over when and how to migrate them to new pricing.
This legacy cohort mechanism means changing a price in the Play Console does not automatically change what existing subscribers pay. You must explicitly choose to migrate subscribers to the new price.
Ending a legacy price cohort
When you decide to move existing subscribers from their legacy price to a new price, you use the price migration API. This initiates either a price increase or price decrease flow, depending on whether the new price is higher or lower than what users currently pay.
Using the migration API
To migrate subscribers to a new price, you call the monetization.subscriptions.basePlans.migratePrices endpoint on your backend side:
1// Backend service for initiating price migration
2class PriceMigrationService(
3 private val androidPublisher: AndroidPublisher
4) {
5 fun migrateSubscribersToNewPrice(
6 packageName: String,
7 productId: String,
8 basePlanId: String,
9 regions: List<String>,
10 newPriceAmountMicros: Long,
11 currencyCode: String
12 ) {
13 val regionalConfigs = regions.map { regionCode ->
14 RegionalPriceMigrationConfig().apply {
15 this.regionCode = regionCode
16 this.priceIncreaseType = "OPT_IN" // or "OPT_OUT" if eligible
17 this.oldestAllowedPriceVersionTime = null // migrate all legacy cohorts
18 }
19 }
20
21 val request = MigratePricesRequest().apply {
22 this.regionalPriceMigrationConfigs = regionalConfigs
23 }
24
25 androidPublisher
26 .monetization()
27 .subscriptions()
28 .basePlans()
29 .migratePrices(packageName, productId, basePlanId, request)
30 .execute()
31 }
32}
The migration is specific to each region, allowing you to roll out price changes gradually across different markets or handle regional pricing differences independently.
Price decrease flow
When the new price is lower than what users currently pay, the migration process is straightforward and easy for users. Price decreases are automatically applied without requiring explicit user acceptance.
How price decreases work
When you migrate subscribers to a lower price, Google Play sends email notifications informing users of the price decrease. Users then begin paying the lower price at their next renewal, and no user action is required since the decrease happens automatically.
There is one timing nuance to be aware of: Google Play may authorize payment up to 48 hours before renewal (or up to five days in India and Brazil). If a user’s payment was already authorized at the higher price before the decrease was applied, they will pay the higher price for that renewal but will receive the lower price on subsequent renewals.
Handling price decreases in your app
From an implementation perspective, price decreases require minimal handling. You may want to communicate the good news to users:
1class PriceChangeManager(
2 private val backendApi: BackendApi
3) {
4 suspend fun checkForPriceChanges(userId: String): PriceChangeInfo? {
5 val subscriptionStatus = backendApi.getSubscriptionStatus(userId)
6 val priceChange = subscriptionStatus.pendingPriceChange ?: return null
7
8 return when {
9 priceChange.newPriceMicros < priceChange.currentPriceMicros -> {
10 PriceChangeInfo.Decrease(
11 currentPrice = formatPrice(priceChange.currentPriceMicros),
12 newPrice = formatPrice(priceChange.newPriceMicros),
13 effectiveDate = priceChange.effectiveDate
14 )
15 }
16 else -> {
17 // Handle price increase (covered in next section)
18 handlePriceIncrease(priceChange)
19 }
20 }
21 }
22}
23
24// In your UI layer
25fun showPriceDecreaseNotification(info: PriceChangeInfo.Decrease) {
26 showBanner(
27 title = "Good news!",
28 message = "Your subscription price is decreasing from ${info.currentPrice} " +
29 "to ${info.newPrice} starting ${formatDate(info.effectiveDate)}."
30 )
31}
Price decreases are generally positive events for users, so your primary concern is ensuring they are aware of the change rather than managing any acceptance flow.
Price increase flow: opt-in increases
Price increases are more complex because they require user awareness and, in most cases, explicit acceptance. The default method for price increases is the opt-in flow, where users must explicitly agree to the new price before being charged.
The opt-in timeline
The opt-in price increase flow follows a specific timeline with distinct phases:
| Phase | Duration | What Happens |
| Freeze period | Days 1 to 7 | Google Play sends no notifications; developer can notify users |
| Notification period | Days 8 to 37 | Google Play sends email and push notifications |
| Effective date | Day 37 onwards | Price increase takes effect; charged at next renewal |
The seven day freeze period at the beginning is intentional. It gives you the opportunity to notify users through your own channels before Google Play’s automated notifications begin. This allows you to control the messaging and potentially explain the value users receive for the increased price.
User acceptance requirement
For opt-in price increases, users must explicitly accept the new price. They do this through the Play Store subscription management screen, where they see a dialog explaining the price change and can either accept or decline.
If a user doesn’t accept the price increase before their first renewal at the new price, their subscription is automatically canceled. They retain access until the end of their current billing period, but the subscription will not renew.
Handling opt-in increases in your app
Your app should detect pending price increases and guide users through the acceptance process:
1class OptInPriceIncreaseManager(
2 private val billingClient: BillingClient,
3 private val backendApi: BackendApi
4) {
5 sealed class PriceIncreaseState {
6 object None : PriceIncreaseState()
7 data class Pending(
8 val currentPrice: String,
9 val newPrice: String,
10 val effectiveDate: Instant,
11 val inFreezePeriod: Boolean
12 ) : PriceIncreaseState()
13 object Accepted : PriceIncreaseState()
14 object Declined : PriceIncreaseState()
15 }
16
17 suspend fun checkPriceIncreaseStatus(userId: String): PriceIncreaseState {
18 val subscriptionStatus = backendApi.getSubscriptionStatus(userId)
19 val priceChange = subscriptionStatus.pendingPriceChange
20
21 if (priceChange == null || priceChange.newPriceMicros <= priceChange.currentPriceMicros) {
22 return PriceIncreaseState.None
23 }
24
25 return when (priceChange.state) {
26 "OUTSTANDING" -> {
27 val freezePeriodEnd = priceChange.initiatedAt.plus(Duration.ofDays(7))
28 PriceIncreaseState.Pending(
29 currentPrice = formatPrice(priceChange.currentPriceMicros),
30 newPrice = formatPrice(priceChange.newPriceMicros),
31 effectiveDate = priceChange.effectiveDate,
32 inFreezePeriod = Instant.now().isBefore(freezePeriodEnd)
33 )
34 }
35 "CONFIRMED" -> PriceIncreaseState.Accepted
36 "CANCELED" -> PriceIncreaseState.Declined
37 else -> PriceIncreaseState.None
38 }
39 }
40
41 fun showPriceIncreaseUI(
42 activity: Activity,
43 state: PriceIncreaseState.Pending
44 ) {
45 if (state.inFreezePeriod) {
46 // During freeze period, show your own messaging
47 showCustomPriceIncreaseDialog(
48 currentPrice = state.currentPrice,
49 newPrice = state.newPrice,
50 effectiveDate = state.effectiveDate,
51 onAcceptClick = { openPlayStoreSubscriptionSettings(activity) }
52 )
53 } else {
54 // After freeze period, can also use Google's in app messaging
55 showInAppMessage(activity)
56 }
57 }
58
59 private fun showInAppMessage(activity: Activity) {
60 val params = InAppMessageParams.newBuilder()
61 .addInAppMessageCategoryToShow(
62 InAppMessageParams.InAppMessageCategoryId.SUBSCRIPTION_PRICE_CHANGE
63 )
64 .build()
65
66 billingClient.showInAppMessages(activity, params) { result ->
67 // Handle the result
68 when (result.responseCode) {
69 InAppMessageResult.InAppMessageResponseCode.NO_ACTION_NEEDED -> {
70 // No price change message needed or user already responded
71 }
72 InAppMessageResult.InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED -> {
73 // User interacted with the message - refresh subscription status
74 refreshSubscriptionStatus()
75 }
76 }
77 }
78 }
79
80 private fun openPlayStoreSubscriptionSettings(activity: Activity) {
81 val intent = Intent(Intent.ACTION_VIEW).apply {
82 data = Uri.parse(
83 "<https://play.google.com/store/account/subscriptions>"
84 )
85 setPackage("com.android.vending")
86 }
87 activity.startActivity(intent)
88 }
89}
Communicating value during price increases
The freeze period is your opportunity to communicate directly with users about why the price is increasing. Effective communication can significantly improve acceptance rates:
1fun showCustomPriceIncreaseDialog(
2 currentPrice: String,
3 newPrice: String,
4 effectiveDate: Instant,
5 onAcceptClick: () -> Unit
6) {
7 showDialog(
8 title = "Subscription Update",
9 message = """
10 Starting ${formatDate(effectiveDate)}, your subscription will change
11 from $currentPrice to $newPrice per month.
12
13 Since you subscribed, we've added:
14 • Advanced analytics dashboard
15 • Offline mode for all content
16 • Priority customer support
17 • And 15+ other features
18
19 To continue enjoying these features, please confirm the new price
20 in your Play Store subscription settings.
21 """.trimIndent(),
22 positiveButton = "Review in Play Store" to onAcceptClick,
23 negativeButton = "Maybe Later" to { /* dismiss */ }
24 )
25}
Price increase flow: opt-out increases
In certain regions and under specific conditions, Google Play allows opt-out price increases. With opt-out increases, users are notified of the price change but are automatically charged the new price unless they explicitly cancel or change their plan.
Eligibility requirements
Opt-out price increases are not universally available. Eligibility depends on several factors including regional availability (only certain countries support opt-out increases), frequency limits on how often you can use them, maximum percentage or absolute amount restrictions per country, and additional developer eligibility requirements. Because of these restrictions, opt-out increases should be considered a supplementary option rather than your primary approach to price changes.
Opt-out timeline
The opt-out timeline differs from opt-in increases:
| Aspect | Opt In | opt-out |
| Freeze period | 7 days | None |
| Notification period | 30 days | 30 or 60 days (varies by country) |
| User action required | Must accept | Can cancel to avoid |
| Default behavior | Subscription cancels | New price charged |
The notification period for opt-out varies by country. Some require 30 days notice, while others require 60 days. Google Play handles these regional requirements automatically when you initiate an opt-out migration.
Handling opt-out increases
From an implementation perspective, opt-out increases are simpler because users do not need to take action to continue their subscription:
1fun handleOptOutPriceIncrease(priceChange: PriceChangeInfo) {
2 // For opt-out increases, the state will be "CONFIRMED" rather than "OUTSTANDING"
3 // Users will be charged the new price automatically unless they cancel
4
5 showNotification(
6 title = "Subscription Price Update",
7 message = "Starting ${formatDate(priceChange.effectiveDate)}, " +
8 "your subscription will be ${priceChange.newPrice}/month. " +
9 "No action needed to continue your subscription."
10 )
11}
However, you should still communicate clearly with users about the upcoming change, even though their action is not required.
In-app notification requirements
Regardless of whether you use opt-in or opt-out price increases, Google Play requires you to display in-app notices about price changes. This requirement applies across all device types where your app runs.
Mandatory notification surfaces
You must show price change notifications on mobile devices (phones and tablets), Android TV, and other streaming devices. The only exception is watches, where in-app notification is recommended but not strictly required due to the limited screen real estate.
Notification timing
For opt-in increases, the recommended approach is to show your own custom messaging explaining the value proposition during the freeze period (days one to seven), then continue showing reminders and use Google’s In App Messaging API after the freeze period ends (days eight onwards).
1class PriceChangeNotificationManager(
2 private val billingClient: BillingClient
3) {
4 fun showPriceChangeNotificationIfNeeded(
5 activity: Activity,
6 priceIncreaseState: PriceIncreaseState
7 ) {
8 when (priceIncreaseState) {
9 is PriceIncreaseState.Pending -> {
10 // Always show some form of notification for pending increases
11 if (priceIncreaseState.inFreezePeriod) {
12 showCustomNotificationBanner(activity, priceIncreaseState)
13 } else {
14 // Use Google's in app messaging
15 showGoogleInAppMessage(activity)
16 }
17 }
18 else -> {
19 // No notification needed
20 }
21 }
22 }
23
24 private fun showCustomNotificationBanner(
25 activity: Activity,
26 state: PriceIncreaseState.Pending
27 ) {
28 // Show a subtle banner at the top of the screen
29 val banner = PriceChangeBanner(activity).apply {
30 setMessage(
31 "Your subscription price will change to ${state.newPrice} " +
32 "on ${formatDate(state.effectiveDate)}. Tap to review."
33 )
34 setOnClickListener {
35 openPriceChangeDetails(activity, state)
36 }
37 }
38 banner.show()
39 }
40
41 private fun showGoogleInAppMessage(activity: Activity) {
42 val params = InAppMessageParams.newBuilder()
43 .addInAppMessageCategoryToShow(
44 InAppMessageParams.InAppMessageCategoryId.SUBSCRIPTION_PRICE_CHANGE
45 )
46 .build()
47
48 billingClient.showInAppMessages(activity, params) { /* handle result */ }
49 }
50}
Handling overlapping price changes
What happens if you initiate a new price change while a previous one is still pending? Google Play handles this by canceling the previous price change and applying the new one.
The cancellation and replacement flow
When overlapping price changes occur, the old price migration is marked as CANCELED and you receive a SUBSCRIPTION_PRICE_CHANGE_UPDATED Real-Time Developer Notification (RTDN). The new price migration then takes effect, and users only need to respond to the latest price change. This behavior prevents users from being forced to accept multiple sequential price increases, which would create a poor user experience.
Tracking price change status
Your backend should process RTDN notifications to track price change status:
1// Backend notification handler
2class PriceChangeNotificationHandler(
3 private val subscriptionRepository: SubscriptionRepository,
4 private val playDeveloperApi: AndroidPublisher
5) {
6 fun handlePriceChangeNotification(notification: DeveloperNotification) {
7 val purchaseToken = notification.subscriptionNotification.purchaseToken
8
9 when (notification.subscriptionNotification.notificationType) {
10 NotificationType.SUBSCRIPTION_PRICE_CHANGE_UPDATED -> {
11 // Query the current state of the price change
12 val subscription = playDeveloperApi
13 .purchases()
14 .subscriptionsv2()
15 .get(packageName, purchaseToken)
16 .execute()
17
18 val priceChangeState = subscription.lineItems[0]
19 .autoRenewingPlan
20 ?.priceChangeDetails
21
22 if (priceChangeState != null) {
23 subscriptionRepository.updatePriceChangeStatus(
24 purchaseToken = purchaseToken,
25 state = priceChangeState.priceChangeState,
26 newPriceMicros = priceChangeState.newPrice?.priceMicros,
27 expectedNewPriceChargeTime = priceChangeState.expectedNewPriceChargeTime
28 )
29
30 // Notify app layer to update UI if needed
31 notifyPriceChangeUpdated(purchaseToken, priceChangeState)
32 }
33 }
34 }
35 }
36}
Recovering from accidental price changes
Mistakes happen. If you accidentally change a price or initiate a migration you did not intend, the recovery process depends on the type of change and how much time has passed.
Reverting opt-in increases
For opt-in price increases, the timing of your revert matters significantly. If you revert within seven days (the freeze period), users will not have received any notifications from Google Play, so the change is essentially invisible to them. After seven days, reverting will cancel the price change for users who have not yet been charged at the new price, but some users may have already received notifications, which could cause confusion.
Reverting opt-out increases
For opt-out price increases, reverting to the original price cancels the increase for users who have not yet been charged. Keep in mind the authorization timing. Users whose payment was already authorized (up to five days before renewal in some regions) may still be charged.
Reverting price decreases
If you need to cancel a price decrease and return to the original higher price, start by reverting to the original price in the Play Console and choosing whether the increase should be opt-in or opt-out. The timing relative to user renewals determines the outcome: if the time between revert and user renewal is greater than the notification window (30 to 60 days depending on country), users will pay the original price at their next renewal. If the time is less than the notification window, users will be charged the lower price once, then go through the standard price increase notification flow.
Installment subscriptions and price changes
If your subscription uses installment plans (where users commit to a certain number of payments), price changes behave differently.
For installment subscriptions, price changes only apply at the end of the active commitment period. You cannot change the price for users in the middle of an installment, and the new price takes effect at the first renewal after the commitment ends. For example, if a user is on a 12 month installment plan followed by monthly auto renewal, any price change you make will only take effect after their 12 month commitment completes and they move to the monthly renewal phase.
Testing price changes
Before rolling out price changes to your production subscriber base, you should thoroughly test the flows using Google Play’s testing tools.
Using license testers
License testers can receive price change notifications without affecting real subscribers. Configure license testers in the Play Console and use them to verify notification delivery and timing, in app messaging display, acceptance and decline flows, and state transitions in your backend.
Play Billing Lab
Google provides the Play Billing Lab app for testing billing scenarios. Use it to simulate price change scenarios and verify your app handles each state correctly.
Audit trail
The Play Console maintains a change log of all price modifications. Use this to review when prices were updated, who initiated the changes, and which regions were affected. This audit trail is invaluable for investigating issues or reviewing the history of accidental changes.
How RevenueCat simplifies price change management
Managing price changes across a large subscriber base involves significant complexity. This includes tracking migration status for each user, processing RTDN notifications, displaying appropriate in-app messaging, and handling edge cases. RevenueCat abstracts away much of this complexity while providing additional tools for managing price changes effectively.
Automatic state tracking
RevenueCat processes Google Play’s RTDN notifications on your behalf, maintaining up to date subscription state including pending price changes. Instead of building infrastructure to receive and process notifications, you simply query RevenueCat for the current customer state:
1fun checkForPriceChanges() {
2 Purchases.sharedInstance.getCustomerInfoWith { customerInfo ->
3 val entitlement = customerInfo.entitlements["premium"]
4
5 // RevenueCat's CustomerInfo reflects current subscription state
6 // including any pending price changes processed from RTDN
7
8 if (entitlement?.isActive == true) {
9 // Check for billing issues that might indicate price change problems
10 entitlement.billingIssueDetectedAt?.let { issueDate ->
11 showBillingRecoveryUI(customerInfo.managementURL)
12 }
13 }
14 }
15}
Management URL for user actions
RevenueCat’s CustomerInfo includes a managementURL property that provides a direct link to Google Play’s subscription management screen. This is where users can accept or decline price changes:
1fun guideToPriceChangeAcceptance(customerInfo: CustomerInfo) {
2 val managementUrl = customerInfo.managementURL
3
4 showDialog(
5 title = "Action Required",
6 message = "Please review the upcoming changes to your subscription.",
7 positiveButton = "Open Settings" to {
8 openUrl(managementUrl)
9 }
10 )
11}
Cross-platform consistency
For apps that span multiple platforms, RevenueCat ensures consistent subscription state across Android, iOS, and other platforms. If a user accepts a price change on one device, the updated status is reflected across all their devices without additional implementation.
Webhooks for server side handling
RevenueCat provides webhooks that notify your server of subscription events in a normalized format. This is simpler than processing raw RTDN notifications and includes events related to price changes:
1// Your webhook handler
2fun handleRevenueCatWebhook(event: WebhookEvent) {
3 when (event.type) {
4 "RENEWAL" -> {
5 // Renewal completed - check if price changed
6 val transaction = event.transaction
7 if (transaction.priceInPurchasedCurrency != previousPrice) {
8 // Price change took effect
9 updateUserPricing(event.appUserId, transaction.priceInPurchasedCurrency)
10 }
11 }
12 "CANCELLATION" -> {
13 // User canceled, might be due to price change rejection
14 val reason = event.cancellationReason
15 if (reason == "PRICE_INCREASE") {
16 // Track churn related to price changes
17 analytics.trackPriceChangeChurn(event.appUserId)
18 }
19 }
20 }
21}
Analytics for price change impact
RevenueCat’s Chart provides analytics that help you understand the impact of price changes, including churn rates correlated with price change timing, conversion rates for subscribers who received price increase notifications, and revenue impact analysis before and after price changes. These insights help you make informed decisions about pricing strategy and identify the optimal timing and communication approach for future price changes.
Best practices with RevenueCat
When using RevenueCat for price change management, focus on proactive communication by using customer attributes to identify users with pending price changes and send targeted communications during the freeze period. Monitor key metrics such as the conversion rate from price increase notification to acceptance and the churn rate among affected users. Always provide users with an easy path to the Play Store subscription settings via the management URL. Finally, leverage RevenueCat’s normalized data to handle edge cases gracefully, such as users who change plans during a pending price increase.
Best practices for subscription price changes
Based on the mechanics we have covered, here are key practices to implement for successful price changes:
Plan your communication strategy
Before initiating a price change, plan how you will communicate with affected users. Prepare messaging that explains the value users receive, decide whether to use the freeze period for custom outreach, and consider offering alternatives such as annual plans or grandfathered pricing for users sensitive to price changes.
Use regional rollouts
If you are increasing prices globally, consider rolling out changes region by region. Test in smaller markets first to gauge response, adjust messaging based on early feedback, and allow time to address issues before they affect your largest markets.
Monitor acceptance rates
Track how many users accept opt-in price increases versus letting their subscriptions cancel:
1class PriceChangeAnalytics(
2 private val analytics: AnalyticsService
3) {
4 fun trackPriceChangeOutcome(
5 userId: String,
6 originalPrice: Long,
7 newPrice: Long,
8 outcome: PriceChangeOutcome
9 ) {
10 analytics.track(
11 event = "price_change_outcome",
12 properties = mapOf(
13 "user_id" to userId,
14 "original_price_micros" to originalPrice,
15 "new_price_micros" to newPrice,
16 "increase_percentage" to calculatePercentage(originalPrice, newPrice),
17 "outcome" to outcome.name
18 )
19 )
20 }
21}
22
23enum class PriceChangeOutcome {
24 ACCEPTED,
25 DECLINED,
26 SUBSCRIPTION_CANCELED,
27 NO_RESPONSE_BEFORE_DEADLINE
28}
If acceptance rates are lower than expected, you may need to adjust your communication strategy or reconsider the price increase amount.
Provide alternatives
Users who are unwilling to pay the higher price may be willing to continue at a different tier. Consider offering a downgrade path to a plan with lower pricing, providing an annual option at a discounted effective monthly rate, or creating a ‘lite’ tier that retains users who would otherwise churn.
Handle the transition gracefully in your app
Ensure your app gracefully handles all states during a price change. Users with pending price increases should see clear messaging, users who decline should not lose access immediately, and the transition between prices should be seamless for users who accept.
Conclusion
Subscription price changes on Google Play involve a carefully orchestrated process designed to protect users while giving developers flexibility. The key concepts to understand are the legacy price cohort that shields existing subscribers from automatic price changes, the distinction between opt-in and opt-out price increases and their respective timelines, the notification requirements that apply across device types, and the regional variations that affect timing and eligibility.
For price decreases, the process is straightforward. Users automatically receive the lower price at their next renewal. For price increases, the default opt-in flow requires explicit user acceptance, with a seven day freeze period followed by 30 days of Google Play notifications before the effective date.
Implementing price changes directly requires processing RTDN notifications, maintaining subscription state on your backend, and building UI to guide users through the acceptance process. RevenueCat simplifies this by handling notification processing automatically and providing normalized data, analytics, and the management URL for user actions, so you can focus on your business and more important work.
Whatever approach you take, successful price changes require clear communication with users about the value they receive. The freeze period is your opportunity to control the narrative before automated notifications begin. Use it wisely, and you can maintain user trust while adjusting your pricing to reflect the value your app delivers.For complete documentation on subscription price changes, refer to the official Android Developer documentation and RevenueCat’s subscription management guide.

