Table of Contents
Understanding In-App Purchases on Android
In-app purchases are a way for your app to earn money from users after they have installed it. Instead of paying once to download the app, users can buy digital items or features from inside the app itself. On Android, the standard way to implement this is with Google Play Billing.
In this chapter you will learn what kinds of in-app purchases exist, what rules you must follow, and how a typical purchase flow works at a high level. You will also see the basic structure of the code you use to work with Google Play Billing, without going into all the implementation details that belong in more advanced chapters.
Types of In-App Purchases
Google Play supports several kinds of digital products that you can sell in your app. The two main groups are one time products and subscriptions.
One time products are called inapp products in Google Play Billing. Inside that group there are two common patterns. The first is a non consumable purchase. This is a product that the user buys once and keeps forever, such as removing ads, unlocking the full version of the app, or enabling a premium feature. The second is a consumable purchase, such as in game currency or extra lives, which the user can buy multiple times and which you usually reduce or remove from their balance when they use it.
Subscriptions are recurring products that charge the user at regular intervals, for example every month or every year. They are used for content or services that continue over time, such as streaming libraries, online tools, or premium sections of your app. From a coding perspective, subscriptions are handled similarly to one time products, but their billing behavior and user expectations are different.
You define all of these product types in the Google Play Console. For each product you create a unique product ID, a price, and some description data that will appear in the purchase dialogs.
Important rule: On Google Play, you must use Google Play Billing for all digital goods and services that are consumed within your app, including coins, extra content, and premium features.
What You Are Allowed to Sell
In-app purchases on Google Play are for digital items and access only. This includes game items, extra levels, premium tools, ad removal, online features, and subscriptions to digital content.
You cannot use Google Play Billing for physical goods or services that happen in the real world. For example, you must not sell real clothing, food delivery, taxi rides, or real event tickets as Google Play in-app purchases. Those should be handled with your own payment system or a third party processor, and Google Play has strict policies about how those are presented.
If you mix different kinds of offerings inside your app, you must carefully separate digital in-app purchases from any physical purchases and follow the policy for both.
Google Play Billing Basics
To offer in-app purchases you integrate the Google Play Billing Library into your app. This library provides a BillingClient class which connects your app to the Play Store app on the device. Through this client you can check which products are available, start the purchase flow, and receive the results.
At a high level, your app does not collect credit card details or store payment information. Instead, when the user decides to buy something, you ask the Billing Library to launch a purchase screen. The system shows a secure Google Play purchase dialog with the price, the product name, and the payment methods. After the user confirms or cancels, the system calls your app back with the result.
Here is a simplified example of how you create and connect a BillingClient:
private lateinit var billingClient: BillingClient
private fun setupBillingClient(context: Context) {
billingClient = BillingClient.newBuilder(context)
.setListener { billingResult, purchases ->
// Handle purchase updates here
}
.enablePendingPurchases()
.build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingSetupFinished(result: BillingResult) {
if (result.responseCode == BillingClient.BillingResponseCode.OK) {
// Ready to query products
}
}
override fun onBillingServiceDisconnected() {
// Try to restart the connection later
}
})
}
You will use the BillingClient to query product details, start purchases, and process purchase updates.
The Purchase Flow from the User Perspective
When you design in-app purchases, it is important to understand the purchase flow from the user’s point of view. A typical sequence looks like this. The user taps a button in your app such as Buy premium or Get 100 coins. Your app calls the Billing Library to start the billing flow for a specific product ID. The Google Play purchase UI appears on top of your app and shows the product name, price, and their Google account. The user confirms or cancels the purchase. The system processes the payment and returns the result to your app through a callback. Your app unlocks the content or updates the user’s balance if the purchase was successful.
You do not design your own payment screens for digital items on Google Play. Instead, you design the buttons and the overall store layout inside your app, and let Google Play show the final confirmation UI.
Product IDs and Product Details
Every in-app product must have a unique product ID. This ID is a string that you choose, often with a format such as premium_remove_ads or coins_100. You use this ID in your code whenever you want to refer to the product.
Once the billing connection is ready, you usually query Google Play for product details. This lets you get the latest price and description directly from the store, instead of hardcoding them in your app. That way, if you change the price in the Google Play Console, your app will show the correct value without an update.
Here is a simple example of querying product details for a one time product:
private fun queryInAppProducts() {
val productList = listOf(
QueryProductDetailsParams.Product.newBuilder()
.setProductId("premium_remove_ads")
.setProductType(BillingClient.ProductType.INAPP)
.build()
)
val params = QueryProductDetailsParams.newBuilder()
.setProductList(productList)
.build()
billingClient.queryProductDetailsAsync(params) { billingResult, productDetailsList ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// Use productDetailsList to show price and title in your UI
}
}
}
You follow a similar approach for subscriptions, but you use BillingClient.ProductType.SUBS and often handle subscription offers and different billing periods.
Starting a Purchase
When the user taps the buy button in your UI, you start the billing flow for the corresponding product. You pass the ProductDetails object that you previously queried, and the Billing Library presents the purchase dialog.
Here is a simplified example that starts the purchase flow for a one time product:
private fun launchPurchaseFlow(productDetails: ProductDetails) {
val productDetailsParams =
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.build()
val billingFlowParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList(listOf(productDetailsParams))
.build()
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)
// billingResult tells you if the flow was launched, not if purchase succeeded
}
The result of launchBillingFlow only tells you whether the purchase screen was successfully shown. The real purchase result arrives later in the callback you set with setListener when you created the BillingClient.
Handling Purchase Results
When Google Play finishes processing a purchase, it triggers your purchase update listener. In that callback you receive a list of Purchase objects and a BillingResult that indicates success or failure.
At a high level, you should follow this pattern. Check if the response code indicates success. If there are purchases, verify each purchase and then deliver the item or unlock the feature. For consumable products, consume the purchase using the Billing Library, so the user can buy it again. For non consumable products or subscriptions, acknowledge the purchase so that Google Play knows you have granted the item and does not refund it automatically.
Here is a very simplified sketch of handling purchases:
private val purchasesUpdatedListener =
PurchasesUpdatedListener { billingResult, purchases ->
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
handlePurchase(purchase)
}
}
}
private fun handlePurchase(purchase: Purchase) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
// TODO: verify purchase, then grant item
if (!purchase.isAcknowledged) {
val params = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
billingClient.acknowledgePurchase(params) { result ->
// Handle acknowledgment result if needed
}
}
}
}In a real app you should verify purchases securely, usually on a backend server. For a beginner project you might start with simple local checks, but you should still understand that true security needs a server side verification to protect against tampering.
Important rule: All non consumable in-app products and subscriptions must be acknowledged. If you do not acknowledge them, Google Play can refund them automatically and the user may lose access.
Consumables, Non Consumables, and Subscriptions in Practice
Once the technical pieces are in place, you need to design how your app behaves for each product type.
For a consumable, you treat each successful purchase as an increase in some quantity, such as coins. When the user spends coins, you decrease their balance and keep track of it, for example in a local database or on a server. After consumption, the purchase token is freed and the user can buy the same product again as many times as they want.
For a non consumable, you unlock a permanent feature. A common pattern is to store a simple flag in your app’s storage that says the user is premium. On app startup, you query Google Play for existing purchases and update the flag accordingly, so the user keeps their purchase even after reinstalling or changing devices.
For subscriptions, you do not manage a simple counter or flag only on the device. Instead, you rely on Google Play’s subscription status, and often your own server, to decide whether the user is currently active or expired. Subscriptions can be upgraded, downgraded, paused, and renewed, so you should always think of them as time based access, not as a one time event.
Restoring Purchases
Users expect that if they buy something once, they do not need to pay again when they reinstall the app or move to a new device, as long as they use the same Google account. To support this, you must restore their purchases.
With Google Play Billing, you usually restore by querying existing purchases when the app starts or when the user taps a Restore purchases button. The Billing Library provides methods to get current in-app purchases and subscriptions. Based on those results, you unlock the correct features in your app.
This restore process applies to non consumable purchases and active subscriptions. Consumable products that have already been used are not restored, because they are meant to be used up.
Designing a Good Purchase Experience
A technical integration alone is not enough. The way you present in-app purchases should be clear, honest, and easy to understand.
You should always show the price in a clear way, for example using the formatted price returned from Google Play instead of hardcoding a currency. It is also important to explain what the user gets when they buy a product and whether it is a one time unlock, a consumable, or a recurring subscription.
Make sure that users can find information about cancellations and refunds, especially for subscriptions. For example, you might have a help screen that explains that subscription management happens through the Google Play Store, and you can provide a button that opens the relevant Google Play screen.
Finally, keep your logic simple and predictable. If the app says that buying premium removes all ads, then make sure ads are truly removed everywhere. If a subscription includes certain content, that content should remain available as long as the subscription is active and should lock again only after it truly expires.
Security and Validation Basics
In-app purchases involve real money, so you must think about basic security from the start. Although full security is an advanced topic, as a beginner you should at least understand the main ideas.
Your app receives a purchase token and some signed data from Google Play. You can use that data to check that the purchase is valid. The most secure way is to send the token to your own backend server, which then talks to Google Play servers to verify it. The server then informs your app whether the purchase is real and not faked.
While you are learning, you might not build a backend right away. You can still structure your code in a way that makes it easy to add server validation later. For example, you can isolate all purchase verification logic in one class or function instead of spreading it around your app.
You should also avoid trusting only simple client side flags. For example, never unlock premium features just by setting a local boolean without checking the real purchase state again. On app start, always confirm entitlement with Google Play for non consumable purchases and subscriptions.
Testing In-App Purchases
Before you publish your app, you should test the full in-app purchase flow. Google Play provides test accounts and special testing tools so that you can go through payments without using real money.
In basic testing, you upload your app to an internal testing track in the Google Play Console, add license test accounts, and then install the app from the Play Store with one of those test accounts. When you try to buy a product, Google Play shows a special test purchase dialog, and you can simulate success, failure, and refunds.
You should test all the following scenarios at least once. Successful purchase and immediate unlock of content. Handling user cancellation gracefully. Restoring purchases after reinstalling or switching devices. Behavior when the network is weak or unavailable. For subscriptions, test what happens when a subscription starts, renews, and expires.
By thoroughly testing your in-app purchases, you reduce user frustration and avoid monetization bugs that might prevent users from paying or receiving what they bought.
Putting It All Together
In-app purchases allow you to charge for digital items and features directly inside your app while using Google Play Billing for secure payments. You create products and subscriptions in the Google Play Console, integrate the Billing Library in your app, and design a clear and fair purchase experience.
At a high level, the main tasks are to connect to Google Play Billing through BillingClient, query product details and show them in your UI, start the purchase flow when the user decides to buy, handle purchase results safely, acknowledge or consume purchases as required, restore past purchases when needed, and respect all Google Play policies about what you can sell and how.
With these fundamentals, you are ready to explore a deeper, more technical implementation of in-app purchases and to connect your monetization strategy to real features in your Android apps.