How to monetize Android apps with ad-free subscriptions using RevenueCat

A step-by-step approach to monetizing Android users with an optional ad-free upgrade.

Jaewoong Eum
Published

Most apps monetize through ads using things like AdMob or Audience Network. To maximize your earnings with ads, you need to show as many of them as possible, which makes for a pretty lousy user experience.

There’s a middle ground, though: Show ads to users with low willingness to pay, and use a subscription to offer those users that love your service an ad free experience. This article will walk you through how to implement this using RevenueCat’s Android SDK.

Connect to Google Play Store

First, connect your Google Play Store account to the RevenueCat dashboard to easily import all existing products. This integration enables unified user identities, simplified subscription management, cross-platform analytics, and accurate pricing insights across app stores.

To do this, simply follow the official guide on connecting to the Google Play Store. If you want to explore more detailed steps, refer to the Google Play Product Setup article for the one by one step guidelines.

Set up products in the RevenueCat dashboard

Once you’ve connected your Google Play Store account to the RevenueCat dashboard, the next step is configuring your products. If you’re new to in-app purchases or subscription-based apps, it’s important to understand the structure of product configuration. There are four key item types you should become familiar with.

  • Product: Individual in-app purchases configured directly within store platforms (e.g., Apple App Store or Google Play).
  • Offering: An offering is a group of products displayed to users as a paywall, configured in the RevenueCat dashboard and used to manage pricing experiments and segmentation.
  • Package: A package represents equivalent products across different platforms (iOS, Android, web) bundled under a single identifier within an offering.
  • Entitlement: An entitlement defines the access or feature level granted to users after a purchase, abstracting product identifiers and simplifying permission management across apps.

For easier understanding, you can refer to the visual diagram in the RevenueCat In-App Purchases Cheat Sheet below:

Once you’re familiar with the concepts above, or if you already have experience, you can start configuring products in the dashboard by following the quickstart guide or referring to this article to get up to speed.

Integrate purchases SDK into your project

Now it’s time to integrate RevenueCat’s Purchases SDK into your project. To get started, add the following dependency to your build.gradle.kts file:

1implementation("com.revenuecat.purchases:purchases:8.17.1")

Next, initialize the Purchases SDK in your Application class using the code below:

1/**
2 * Initialize the RevenueCat Purchases SDK.
3 *
4 * `appUserID` is nil, so an anonymous ID will be generated automatically by the Purchases SDK.
5 * Read more about Identifying Users here: https://docs.revenuecat.com/docs/user-ids
6 *
7 * `purchasesAreCompletedBy` is set to REVENUECAT, so Purchases will automatically handle finishing transactions.
8 * Read more about finishing transactions here: https://www.revenuecat.com/docs/migrating-to-revenuecat/sdk-or-not/finishing-transactions
9 */
10val builder = PurchasesConfiguration.Builder(this, BuildConfig.REVENUECAT_API_KEY)
11Purchases.configure(
12  builder
13    .purchasesAreCompletedBy(PurchasesAreCompletedBy.REVENUECAT)
14    .appUserID(null)
15    .diagnosticsEnabled(true)
16    .build(),
17)

Validating user entitlements

Now let’s validate user entitlements. As discussed earlier, an entitlement defines the level of access or features granted to a user after a purchase, so you can use it to determine whether to show an ad banner or not.

You can simply check if a user has an active entitlement or not by using the code below:

1val ENTITLEMENT_IDENTIFIER = ".." // get specific entitlement identifier from your RevenueCat dashboard
2val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()
3val isEntitled = customerInfo?.entitlements[ENTITLEMENT_IDENTIFIER]?.isActive == true

As shown in the code above, after retrieving the customer info using Purchases.sharedInstance.awaitCustomerInfo() within a coroutine scope, you can check whether the customer is entitled to a specific entitlement.

Disabling an Admob banner

Once you’ve determined whether a user is entitled to a specific entitlement, you can decide whether to display an Admob banner based on your product requirements, as shown in the example below:

1@Composable
2fun Bottom(isEntitled: Boolean) {
3    if (isEntitled) {
4      // if the user is granted access to this entitlement. don't need to display a banner.
5    } else {
6      // display a banner UI here
7      AndroidView(
8    	modifier = Modifier.fillMaxWidth(),
9        factory = { context ->
10            AdView(context).apply {
11                setAdSize(AdSize.BANNER)
12                adUnitId = "ca-app-pub-xxxx"
13                loadAd(AdRequest.Builder().build())
14            }
15        },
16        update = { adView ->
17            adView.loadAd(AdRequest.Builder().build())
18        }
19    )    
20    }
21}

Disabling an Audience Network banner

Just like with AdMob, you can easily disable an Audience Network banner using the same logic, as shown in the example below:

1@Composable
2fun Bottom(isEntitled: Boolean) {
3    if (isEntitled) {
4      // if the user is granted access to this entitlement. don't need to display a banner.
5    } else {
6      // display a banner UI here
7      ..
8    }
9}

Implement in-app purchase for ad-free feature

Now, let’s implement in-app purchases to offer an ad-free experience. This approach benefits both sides — your service can boost revenue, while power users enjoy a cleaner, uninterrupted experience through a paid upgrade.

First, retrieve the appropriate product information from the RevenueCat dashboard. You can do this easily by calling Purchases.sharedInstance.awaitGetProducts(), as shown in the example below:

1// fetches the product information from the RevenueCat server
2val products = Purchases.sharedInstance.awaitGetProducts(
3  productIds = listOf("paywall_tester.subs"),
4)
5
6// proceed in-app purchases
7val purchaseResult = Purchases.sharedInstance.awaitPurchase(
8  purchaseParams = PurchaseParams.Builder(
9    activity = context as Activity,
10    storeProduct = products.first(),
11  ).build()
12)

In this case, if you have multiple products like paywall_tester.subs:weekly, paywall_tester.subs:monthly, and paywall_tester.subs:yearly, you should use paywall_tester.subs as the value for the productIds field. This will allow you to retrieve all related product information as a list.

After that, you can initiate the in-app purchase flow by calling Purchases.sharedInstance.awaitPurchase(). This will trigger the Google Play purchase dialog, allowing the user to complete the transaction — just like that, you’ve implemented in-app purchases with only a few lines of code.

Conclusion

By combining in-app purchases with ad gating, you can serve both high-intent and low-intent users — maximizing revenue while giving premium users a cleaner experience. Using RevenueCat, implementing this dual-monetization strategy is both scalable and straightforward.

As always, happy coding!

Jaewoong

You might also like

Share this post

Want to see how RevenueCat can help?

RevenueCat enables us to have one single source of truth for subscriptions and revenue data.

Olivier Lemarié, PhotoroomOlivier Lemarié, Photoroom
Read Case Study