In-app purchases and especially subscriptions are the most popular methods to monetize an app. On the one hand, a subscription allows a developer to develop content and a product, on the other hand, they help a user to get a more high-quality app in general. In-app purchases are subject to 30% commission, but if a user has been subscribed for more than a year or an app earns less than $1М per year, the commission is 15%.
This is the first article from the series dedicated to in-app purchases in Android apps. In this series, we will cover the topics starting with the creation of in-app purchases and up to server validation and analytics:
In this article, we’ll explain how to:
Before we start, make sure you:
Now, let’s get down to business and create our first product.
Switch to your developer account and choose an app.
Then, in the menu on the left, find the Products section, select Subscriptions and press Create a Subscription.
Then, we’ll see the subscription configurator. Here are some important points.
Scroll down and choose the subscription period. In our case, it’s a week. Set up the price.
Usually, you set the price in the basic account currency, and the system converts the price automatically. But you can also edit the price for a specific country manually.
Please notice that Google shows the tax for every country. It’s great, App Store Connect doesn’t do so.
Scroll down and choose (if needed):
Regardless of the fact that subscriptions are monetized more effectively on iOS, Play Console’s admin board is more convenient, it’s organized and localized better, and it works faster.
The process of product creation is made as simple as possible. Here we told how to create products on iOS.
Once the products are created, let’s work on the architecture for accepting and processing purchases. In general, the process looks like this:
In this part, let’s take a closer look at the first two points.
Adding Billing Library to a project:
At the time of this writing, the latest version is 4.0.0. You can replace it with any other version at any moment.
Let’s create a wrapper class that will cover the logic of interaction with Google Play and initialize BillingClient from Billing Library in it. Let’s call this class BillingClientWrapper.
This class will implement PurchasesUpdatedListener interface. We will override a method for it now – onPurchasesUpdated(billingResult: BillingResult, purchaseList: MutableList<Purchase>?) – it’s needed right after a purchase is made, but we will describe the implementation process in the next article.
Google recommends to avoid having more than one active connection between BillingClient and Google Play to prevent a callback about a purchase made from being executed several times. Thus, you should have one unique BillingClient in a singleton class. The class in the example isn’t a singleton, but we can use dependency injection (for example, with the help of Dagger or Koin) in the way, allowing only one instance to exist at a single point in time.
To make any request with Billing Library, BillingClient must have an active connection with Google Play at the moment when the request is being made, but the connection may be lost at some moment. For the sake of convenience, let’s write a wrapper allowing to make any requests only when the connection is active.
To get the products, we need their IDs that we set in the market. But it’s not enough for a request, we also need the product type (subscriptions or one-time purchases) that’s why we can get a general list of products by “combining” the results of two requests.
The request for products is asynchronous, so we need a callback that will either provide us with a list of products or return an error model. When an error occurs, Billing Library returns one of its BillingResponseCodes, as well as debugMessage. Let’s create callback interface and a model for an error:
Here’s a code for a private method for getting data about a specific type of products and a public method that will “combine” the results of two requests and provide a user with the final list of products or display an error message.
Thus, we got valuable information about the products (SkuDetails) where we can see localized names, prices, product type, as well as billing period and information about introductory price and trial period (if it’s available for this user) for subscriptions. Here’s what the final class looks like:
That’s all for today. In the next articles, we’re going to tell you about purchase implementation, testing, and error handling.