The new subscription model and Google Play Billing Library 5.0 overview
Tags: android, billing, googleplay, mobile
Published: 2022-05-26 07:43:25

During the annual I/O conference, Google introduced its new major version of the Google Play Billing Library. Besides classic method deprecations and removals, there was also vast information about the new architecture of subscriptions, which aims to simplify the way you can create, manage and sell in-app purchases. In this article, I will explore the updates to Google Play Billing Library 5.0 and dig deeper into the most interesting ones.

## The new subscription model
Google Play Billing System is the main tool that helps you to monetize your application with subscriptions. If you’re not familiar with this service yet, please read [this article](https://qonversion.io/blog/a-complete-guide-to-google-play-in-app-purchases-and-subscriptions-implementation/). With the latest version of its library, Google has changed the structure of how subscription products are defined, and this has led to changes in how they are sold in-app and managed on your backend. 

With Google Play Billing Library 5.0. Google has introduced new subscription architecture, which adds such entities as [base plans and offers](https://support.google.com/googleplay/android-developer/answer/12154973). Let’s take a look. 

Let’s say you have a premium subscription in your app and offer your clients weekly, monthly and annual subscriptions. Before these updates, you would have needed to create three different subscriptions - one per period. Then imagine that you wanted to offer a free trial to users who’ve left your app to persuade them to return. So, then you would also need to create one more annual subscription with a free trial.

These are just two of the most common cases, causing the creation of lots of subscriptions with the previous subscription model - there could be many more examples. 

But from now on, you can handle all of these scenarios with just a single subscription. Google has divided a subscription model into three entities - subscription itself, base plans, and offers.

![The new subscription model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fb4mot3yt1hjhgswx9au.png)

The new subscription entity includes: 

- an identifier
- title
- description
- taxes information

The rest of the information is set up via base plans and offers. 

A base plan contains: 

- its identifier 
- renewal type (auto-renewing or prepaid) 
- duration 
- grace period 
- prices and availability for different regions
- and some less important settings

You can find the whole list of details in [the official documentation](https://support.google.com/googleplay/android-developer/answer/12154973). 

An offer contains:

- its identifier
- eligibility criteria - an option determining which users can access this offer (first-time subscribers, those who upgrade from another subscription, or developer determined), 
- phases including free trials and single or recurring payments (phases usage is shown in the screenshot below) 
- and a price discount - either fixed amount or percentage or absolute discount depending on the base plan price 

![Subscription phases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rindbqcgis2tsltgb9w3.png)

Each subscription can contain one or more base plans, which in turn may contain several offers (see the scheme above). So, for the example above you can create one premium subscription with three base plans - one per period, and an offer with a free trial for the annual base plan.

## Purchasing new subscriptions
Speaking of code, in Google Play Billing Library 5.0, the `SkuDetails` class is deprecated as well as every other entity or method containing `Sku` in its name. Now you should consider using `ProductDetails` for that purpose. Product details contain information about a subscription, including its base plans and offers.

Before, you requested subscription details like those below:
```
val params = SkuDetailsParams.newBuilder()
    .setType(BillingClient.SkuType.SUBS)
    .setSkusList(listOf("premium"))
    .build()

billingClient.querySkuDetailsAsync(params) { 
        billingResult, skuDetailsList -> // Process the result
}
```

Now, you should do the following: 
```
val productList = listOf(
    QueryProductDetailsParams.Product.newBuilder()
        .setProductType(BillingClient.SkuType.SUBS)
        .setProductId("premium")
        .build()
)

val params = QueryProductDetailsParams.newBuilder()
    .setProductList(productList)
    .build()

billingClient.queryProductDetailsAsync(params) {
    billingResult, productDetailsList -> // Process the result
}
```

And, for launching the purchase flow, you used the following constructions:
```
val billingFlowParams = BillingFlowParams.newBuilder()
    .setSkuDetails(skuDetails)
    .build()

val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
```

Whereas now they should look like those that follow:
```
// Note that `subscriptionOfferDetails` can be null if it is an in-app product, not a subscription.
val offerToken = productDetails.subscriptionOfferDetails?.get(selectedOfferIndex)?.offerToken ?: return

val productDetailsParamsList = listOf(
    BillingFlowParams.ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails)
        .setOfferToken(offerToken)
        .build()
)

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)

val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams) 
```
You can also look at the migration examples in [the Google official migration steps](https://developer.android.com/google/play/billing/migrate-gpblv5). Note, that there are a few typos in their examples which are fixed here.

Let’s overview the changes. 

As I mentioned, you should now use `ProductDetails` for querying subscription details and purchasing flow. In addition, you should provide an `offerToken` to billing flow params as each `ProductDetails` may contain several offers. Note, that the offer details array (`productDetails.subscriptionOfferDetails`) always contains base plan details (if any base plan exists), so even if your subscription contains only a base plan without offers it will still have `offerToken` for purchase.

You could notice that the new purchase flow accepts multiple `ProductDetails` instead of one `SkuDetails` like in the previous version. But if you provide more than one `ProductDetails`, the flow will end up with an error. [The official integration guide](https://developer.android.com/google/play/billing/integrate#launch) gives the wrong example of using the `setProductDetails` and `setOfferToken` methods right on the `BillingFlowParams.Builder` class, but there is only the `setProductDetailsParamsList` [method available](https://developer.android.com/reference/com/android/billingclient/api/BillingFlowParams.Builder#setProductDetailsParamsList(java.util.List%3Ccom.android.billingclient.api.BillingFlowParams.ProductDetailsParams%3E)) for those purposes. It seems as if Google decided to switch from single product details to multiple quite recently with a vision for the future when Billing will support multiple different subscription purchases at once.

The rest flow - namely processing the purchase - remains the same as in the Google Play Billing Library 4.0.

## Subscriptions backward compatibility
Well, everything sounds good so far, but what if you don’t want to migrate right now? Google has taken care of it.

Despite Google Play Console already working only with the new subscription model, all the old subscriptions are converted to the new format automatically, saving their backward compatibility. This means that you see your subscriptions in the new format on Google Console but can still work with them as you did before on your app. 

You will also notice that all the old subscriptions are made read-only after migration. Google warns you that editing will disable InAppProducts API support for that subscription.

![Read-only subscription editing warning](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u95s1fvvp29ywow8y5hp.png)
You might be using this API in your backend for fetching some product details, so be careful - after making the subscription editable you will receive errors (as shown below) from that API if you try to fetch converted subscription info. The same will happen to all new subscriptions created after May, 11th.
```
HTTP code - 422
Non-existent in-app product: com.qonversion.sample\/ProductId{productId=article_test_trial}
```
So, if you use InAppProducts API, consider [upgrading it](https://developer.android.com/google/play/billing/compatibility#managing_subscription_status) before creating new subscriptions or editing old ones.

While surfing through the new subscriptions UI in Google Play Console, you will find “Backwards compatible” tags near base plans and offers of subscriptions. This means that if you purchase those subscriptions using deprecated `SkuDetails` (as you do in Google Play Billing Library 4.0), you will buy exactly those compatible base plans/offers. If there is only a compatible base plan, without any compatible offer, then that base plan will be purchased. If there are both compatible base plan and offer available then if the offer is eligible to the current user, it will be purchased, else - base plan. You can choose another base plan/offer as backward compatible if you would like.

![Choosing backward compatible base plan or offer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1apgby5mnzt30fu9tr2w.png)
Note - this Backwards compatibility relates to Google Play Billing Library but not to InAppProducts API. As mentioned, if you begin using new features, for example, multiple base plans or offers for one subscription or just convert it from read-only to editable, that subscription can still be Backwards compatible for an app, but not for API.

## The other updates
There were several minor updates in the Google Play Billing Library 5.0 which are worth briefly mentioning.

- The method `queryPurchases` deprecated in Google Play Billing Library 4.0 was removed - no more synchronized requests, only `queryPurchasesAsync`. 
- `launchPriceChangeFlow` has been deprecated - [the new recommended price change flow](https://developer.android.com/google/play/billing/subscriptions#price-change) requires clients' confirmation via the Google Play subscriptions page, so you should use deep links for navigation there instead of calling the `BillingClient` method.
- Added the `setIsOfferPersonalized` method to [indicate a personalized price](https://developer.android.com/google/play/billing/integrate#personalized-price) for EU clients following the Consumer Rights Directive. This option adds a line to the bottom of the Google Billing purchase screen noting that a price is personalized.

## The new subscriptions model and Qonversion
While we are working on Google Play Billing Library 5.0 support, you can still use Qonversion SDK for your in-app purchases. We are now using Google Play Billing Library 4.0, which means we can manage only subscriptions with backward-compatible base plans and offers. You are still able to edit properties that were available in the previous subscriptions model like a price, grace period, trial duration, and so on as well as create new subscriptions. Just be sure that after you still have at least one backward compatible base plan and, if needed, a backward-compatible offer. And, we still require a whole subscription identifier for configuring products on our Product Center, not base plan or offer identifiers.

If you have any questions, please let us know. We will be happy to assist you. 

## Useful links

- [Google Play Billing Library 5.0 release notes](https://developer.android.com/google/play/billing/release-notes#5-0)
- [Google Play Billing Library 4 to 5 Migration Guide](https://developer.android.com/google/play/billing/migrate-gpblv5)
- [May 2022 subscription changes guide](https://developer.android.com/google/play/billing/compatibility)
- [Recent changes to subscriptions in Play Console](https://support.google.com/googleplay/android-developer/answer/12124625)
- [Understanding subscriptions](https://support.google.com/googleplay/android-developer/answer/12154973?hl=en)