Subscription downgrade and upgrade in mobile app

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Subscription downgrade and upgrade in mobile app
Medium
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementing Subscription Downgrade/Upgrade in Mobile Applications

Changing subscription tiers is one of the most confusing parts of StoreKit. Apple and Google handle transitions differently, and "just buy another product" is not an upgrade. Without proper implementation, users end up with two active subscriptions or the transition doesn't happen until the end of the current period without any notification.

How It Works on iOS

In the App Store, all subscriptions within one Subscription Group are automatically managed by Apple: you can't buy two products from the same group simultaneously. When purchasing a new product from the same group, Apple applies one of three policies:

  • Immediate upgrade (moving to higher tier): new subscription activates immediately, user is credited with a prorated portion of the remaining period
  • Crossgrade at renewal (moving to equivalent tier): new subscription starts at the next renewal date
  • Downgrade at renewal (moving to lower tier): current subscription continues until end of period, then new subscription activates

Tier level is set in App Store Connect → Subscription Group → drag-and-drop product order. Top = highest.

Client Implementation (StoreKit 2)

To transition between tiers, call product.purchase() like a regular purchase — StoreKit determines the transition type:

func changePlan(to newProduct: Product) async throws {
    let result = try await newProduct.purchase(options: [
        .appAccountToken(userAccountToken) // bind to user account
    ])

    switch result {
    case .success(let verification):
        if case .verified(let transaction) = verification {
            // Determine transition type
            if let upgradeInfo = transaction.subscriptionGroupID {
                await handlePlanChange(transaction: transaction)
            }
            await transaction.finish()
        }

    case .pending:
        // Transition scheduled for next period
        showPendingChangeNotification()

    case .userCancelled:
        break
    }
}

After successful new tier purchase, check current active entitlement:

func getCurrentActivePlan() async -> String? {
    for await result in Transaction.currentEntitlements {
        if case .verified(let transaction) = result,
           transaction.productType == .autoRenewableSubscription {
            return transaction.productID
        }
    }
    return nil
}

Transition UI/UX

The main problem is user confusion about when changes take effect. You must explicitly explain:

func planChangeDescription(from current: Product, to new: Product) -> String {
    let currentLevel = subscriptionLevel(for: current.id)
    let newLevel = subscriptionLevel(for: new.id)

    if newLevel > currentLevel {
        return "Upgrade to \(new.displayName) will activate immediately. Remaining period credit will be applied."
    } else if newLevel == currentLevel {
        return "Upgrade to \(new.displayName) will take effect at next renewal."
    } else {
        return "Current plan \(current.displayName) remains active until \(currentExpirationDate). Then \(new.displayName) begins."
    }
}

A confirmation modal with explicit terms is mandatory.

Google Play Billing: Proration Modes

On Android, subscription changes require explicit ProrationMode in BillingFlowParams:

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        listOf(
            BillingFlowParams.ProductDetailsParams.newBuilder()
                .setProductDetails(newProductDetails)
                .setOfferToken(newOfferToken)
                .build()
        )
    )
    .setSubscriptionUpdateParams(
        BillingFlowParams.SubscriptionUpdateParams.newBuilder()
            .setOldPurchaseToken(currentPurchaseToken)
            .setSubscriptionReplacementMode(
                BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.CHARGE_PRORATED_PRICE
                // For downgrade: WITH_TIME_PRORATION
                // For immediate without credit: CHARGE_FULL_PRICE
            )
            .build()
    )
    .build()

Wrong ReplacementMode is a common error. CHARGE_PRORATED_PRICE for downgrade causes immediate charge at new price without compensation — user loses money.

Scenario iOS Policy Android ReplacementMode
Upgrade Immediate, prorated credit CHARGE_PRORATED_PRICE
Downgrade End of period WITH_TIME_PRORATION
Crossgrade (same level) Next renewal DEFERRED

Pending State

Downgrade creates a pending transaction — subscription is purchased but not yet active. On iOS this is .pending in purchase() result. On Android — PENDING in Purchase.purchaseState. You must store this state and notify user about scheduled change.

What's Included in the Work

  • Defining tier levels in App Store Connect / Google Play Console
  • Client-side logic for purchase with immediate / pending handling
  • UI with transition terms for each scenario (up/down/cross)
  • Handling Transaction.updates to track pending activation
  • Google Play: correct ReplacementMode for each transition type
  • Server-side status sync via App Store Server Notifications / RTDN

Timeline

3–5 days — depends on number of tiers and platforms. Pricing is calculated individually based on requirement analysis.