This topic describes how to migrate from Google Play Billing Library 4 to Google Play Billing Library 5 and how to use new subscription capabilities.
Overview
Google Play Billing Library 5 introduced subscription base plans and subscription offers. These features expand the ways by which you can sell subscriptions, reducing operational costs by removing the need to create and manage an ever-increasing number of SKUs. For more information, see Recent changes to Subscriptions in Play Console.
All existing subscription products were automatically converted to this new paradigm as part of the May 2022 release of Play Billing Library 5 and the new subscriptions platform. To read more about this conversion, see the Working with older subscriptions section in the Play Console Help article.
Using the Play Developer Console or Play Developer API, you can now configure a single subscription with multiple base plans, each with multiple offers. Subscription offers have flexible pricing models and eligibility options. You can create offers across the subscription lifecycle using a variety of auto-renewing and prepaid plans. For more information, see the integration guide.
However, if you are not planning on adopting these new features right away, you can still migrate your app to Play Billing Library 5 and plan your backend components migration later, as long as your product catalog stays in a backwards compatible configuration.
Migration Steps
Create your backend product catalog
We recommend creating new products following the entity structure in the new subscription platform for your Play Billing Library 5 integration before migrating your app. You can consolidate duplicate products in your old catalog representing the same entitlement benefits under a single subscription and use base plan and offer configurations to represent all the options that you want to offer. For more information about this recommendation, see the Working with older subscriptions section of the Play Console Help article.
We recommend you don't modify the converted subscription products after the
May 2022 release, you should leave them as they are to be sold with the versions of
your app using deprecated methods (for example, querySkuDetailsAsync()
) without introducing changes that
could affect these older builds.
The conversion process made the subscription products that were in your catalog before May 2022 read-only to avoid accidental changes that could result in issues with your existing integration. Making changes to these subscriptions is possible, but there would be implications that could affect your frontend and backend integrations:
On the frontend, app versions using
querySkuDetailsAsync()
to obtain subscription product details can only sell backwards compatible base plans and offers, and there can only be one backwards compatible base plan and offer combination, so if you add new plans or offers to the converted subscriptions, the new additional base plans or offers won’t be able to be sold on these older versions of your app.On the backend, if you edit your converted subscriptions in the Play Console UI, you won’t be able to manage them with the
inappproducts
endpoint, if you were calling the endpoint for this purpose. You should also migrate to the new subscription purchase status endpoint (purchases.subscriptionsv2.get
) to manage purchases for these subscriptions, as the old purchase status endpoint (purchases.subscriptions.get
) only returns the data necessary to handle backwards compatible base plans and offers purchases. Read the Managing subscription purchase status section for more information.
Manage your backend subscription catalog with the new API
If you manage your subscription product catalog automatically with the Google Play Developer API, you need to use the new subscription product definition endpoints to create and manage subscriptions, base plans, and offers. Read the May 2022 subscription features guide to learn more about the product catalog API changes for this release.
To migrate an automatic product catalog management module for
Google Play Billing subscriptions, replace the
inappproducts
API with the new Subscription Publishing API to manage and publish
your subscription catalog. There are three new endpoints:
Monetization.subscriptions
to manage subscription products.Monetization.basePlans
to manage base plans for subscriptions.Monetization.offers
to manage offers for base plans.
These new endpoints have all the necessary functionality to leverage all the new capabilities in your catalog: base plan and offer tags, regional targeting, prepaid plans, and more.
You should still use the
inappproducts
API to manage your in-app product catalog for one-time purchase products.
Versions of your app using deprecated methods (for example, querySkuDetailsAsync()
) won’t be able to sell any base plans or offers that are not backwards compatible. You can read about backwards compatible offers here.
Update Google Play Billing Library
Once you have created your new subscription products catalog,
you can migrate your app to Google Billing Library 5. Replace the existing
Play Billing Library dependency with the updated version to
your app’s build.gradle
file.
dependencies {
def billingVersion = "5.0.0"
implementation "com.android.billingclient:billing:$billingVersion"
}
Your project should build right away, even if you haven’t modified any calls to methods, as we have built backwards compatibility on the Play Billing Library 5. However, the concept of SKU is considered deprecated.
Initializing the Billing Client and establishing a connection to Google Play
The first steps to launch purchases from an Android app remain the same:
Showing products available to buy
To obtain all offers a user is eligible to purchase:
- Replace
SkuDetailsParams
withQueryProductDetailsParams
- Switch the
BillingClient.querySkuDetailsAsync()
call to useBillingClient.queryProductDetailsAsync()
Note that query results are now ProductDetails
instead of SkuDetails
.
Each ProductDetails
item contains the information about the product
(ID, title, type, and so on). For subscription products, ProductDetails
contains a List<ProductDetails.SubscriptionOfferDetails>
, which is the
list of the subscription offer details. For one-time purchase products,
ProductDetails
contains a ProductDetails.OneTimePurchaseOfferDetails
. These
can be used to decide which offers to show to the users.
The following example shows how your app might look before and after making these changes:
Before
Kotlin
val skuList = ArrayList<String>() skuList.add("up_basic_sub") val params = SkuDetailsParams.newBuilder() params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS) billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList -> // Process the result }
Java
List<String> skuList = new ArrayList<>(); skuList.add("up_basic_sub"); SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); params.setSkusList(skuList).setType(SkuType.SUBS); billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() { @Override public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) { // Process the result. } } );
After
Kotlin
val productList = listOf( QueryProductDetailsParams.Product.newBuilder() .setProductId("up_basic_sub") .setProductType(BillingClient.ProductType.SUBS) .build() ) val params = QueryProductDetailsParams.newBuilder().setProductList(productList) billingClient.queryProductDetailsAsync(params.build()) { billingResult, productDetailsList -> // Process the result }
Java
ImmutableList<Product> productList = ImmutableList.of(Product.newBuilder() .setProductId("up_basic_sub") .setProductType(ProductType.SUBS) .build()); QueryProductDetailsParams params = QueryProductDetailsParams.newBuilder() .setProductList(productList) .build(); billingClient.queryProductDetailsAsync( params, new ProductDetailsResponseListener() { public void onProductDetailsResponse(BillingResult billingResult, List<ProductDetails> productDetailsList) { // Process the result } } );
The callback for queryProductDetailsAsync
returns a List<ProductDetails>
.
Each ProductDetails
item contains the information about the product
(ID, title, type, and so on). The main difference is that subscription
products now also contain a List<ProductDetails.SubscriptionOfferDetails>
that contains all offers available to the user.
Since previous versions of the Play Billing Library do not support the new
objects (subscriptions, base plans, offers, and so on), the new system
translates each subscription SKU into a single backwards compatible
base plan and offer. Available one-time purchase products are also
ported to a ProductDetails
object. The offer details of a one-time
purchase product can be accessed with the
getOneTimePurchaseOfferDetails()
method.
Rarely, some devices are unable to support ProductDetails
and queryProductDetailsAsync()
,
usually due to outdated versions of
Google Play Services. To ensure
proper support for this scenario, call
isFeatureSupported()
for the PRODUCT_DETAILS
feature before calling queryProductDetailsAsync
. If the response is
OK
,
the device supports the feature and you can proceed with calling queryProductDetailsAsync()
.
If the response is FEATURE_NOT_SUPPORTED
,
you can instead request the available backwards-compatible products list with
querySkuDetailsAsync()
.
To learn more about how to use the Play Billing Library 5 backwards compatibility
features, see the May 2022 subscription features guide.
Launching the offer purchase flow
Launching a purchase flow for an offer is very similar to launching a flow for a SKU. To start a purchase request using version 5, do the following:
- Instead of using
SkuDetails
forBillingFlowParams
, useProductDetailsParams
. - The offer(s) details, such as offer ID, base plan ID, and more can be obtained using the
SubscriptionOfferDetails
object.
To purchase a product with the user's selected offer, get the offerToken
of the selected offer and pass it into the ProductDetailsParams
object.
Once you've created a BillingFlowParams
object, launching the billing flow
with the BillingClient
remains the same.
The following example show how your app might look before and after making these changes:
Before
Kotlin
// An activity reference from which the billing flow will be launched. val activity : Activity = ...; // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync(). val billingFlowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .build() val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
Java
// An activity reference from which the billing flow will be launched. Activity activity = ...; // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync(). BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setSkuDetails(skuDetails) .build(); BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
After
Kotlin
// An activity reference from which the billing flow will be launched. val activity : Activity = ...; // Retrieve a value for "productDetails" by calling queryProductDetailsAsync() // Get the offerToken of the selected offer val offerToken = productDetails.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken val productDetailsParamsList = listOf( BillingFlowParams.ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() ) val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build() // Launch the billing flow val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
Java
// Retrieve a value for "productDetails" by calling queryProductDetailsAsync() // Get the offerToken of the selected offer String offerToken = productDetails .getSubscriptionOfferDetails() .get(selectedOfferIndex) .getOfferToken(); // Set the parameters for the offer that will be presented // in the billing flow creating separate productDetailsParamsList variable ImmutableList<ProductDetailsParams> productDetailsParamsList = ImmutableList.of( ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() ); BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) .build(); // Launch the billing flow BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
Processing the purchases
Processing purchases with Google Play Billing Library 5 remains similar to previous versions.
To pull all active purchases owned by the user and query for new purchases, do the following:
- Instead of passing a
BillingClient.SkuType
value toqueryPurchasesAsync()
, pass aQueryPurchasesParams
object that contains aBillingClient.ProductType
value.
The following example show how your app might look before and after making these changes:
Before
Kotlin
billingClient.queryPurchasesAsync(BillingClient.SkuType.SUBS) { billingResult, purchaseList -> { // Process the result } }
Java
billingClient.queryPurchasesAsync( BillingClient.SkuType.SUBS, new PurchasesResponseListener() { public void onQueryPurchasesResponse( BillingResult billingResult, List<Purchase> purchases) { // process the result } } );
After
Kotlin
billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder() .setProductType(BillingClient.ProductType.SUBS) .build() ) { billingResult, purchaseList -> // Process the result }
Java
billingClient.queryPurchasesAsync( QueryPurchasesParams.newBuilder().setProductType(ProductType.SUBS).build(), new PurchasesResponseListener() { public void onQueryPurchasesResponse( BillingResult billingResult, List<Purchase> purchases) { // Process the result } } );
The steps to manage out of app purchases and pending transactions haven’t changed.
Manage subscription purchase status with the new API in your backend
You should migrate your subscriptions purchase status management component in your backend to be ready to handle purchases of the new products created in previous steps. Your current subscriptions purchase status management component should work as usual for the converted subscription products you defined before the May 2022 launch, and it should suffice to manage purchases of backward compatible offers, but it doesn’t support any of the new functionality.
You need to implement the new Subscription Purchases API for your subscriptions purchase status management module, which checks the purchase status and manages Play Billing subscription entitlements in your backend. The old version of the API doesn’t return all the necessary details to manage purchases in the new platform. For details on changes from previous versions, see the guide to the May 2022 new subscription features.
You would normally call the Subscription Purchases API every time you receive a
SubscriptionNotification
Real Time Developer Notification to pull the
latest information about the subscription status. You need to replace your
calls to purchases.subscriptions.get
with the new version of
the Subscription Purchases API, purchases.subscriptionsv2.get
.
There’s a new resource called
SubscriptionPurchaseV2
that provides enough
information to manage purchase entitlement for subscriptions in the new model.
This new endpoint returns the status for all your subscription products and all your purchases, regardless of the version of the app that sold them and when the product was defined (before or after the May 2022 release), so after the migration you will only need this version of your subscription purchase status management module.