Turn Your App into Revenue: Building Paywalls in Android With Jetpack Compose
In this article, you'll learn how to seamlessly implement in-app subscriptions and paywall features in Android using Jetpack Compose and the RevenueCat SDK.

In-app subscriptions have become a popular way to monetize mobile applications that offer recurring value to users. This model is widely adopted across industries, including AI platforms, social networks, productivity tools, and much more. Subscriptions appeal particularly to heavy users seeking an ad-free experience and unrestricted access to premium features.
However, building an in-app subscription feature from scratch can be painful. You’ll have to manage platform-specific complexities, such as adapting to the frequently changing API surfaces of Google Play Billing Library on Android. This includes handling API migrations, deprecations, and maintaining secure server infrastructure, databases, and custom APIs.
In this article, you’ll learn how to seamlessly implement in-app subscriptions and paywall features in Android using Jetpack Compose and the RevenueCat SDK by exploring the open-source project Cat Paywall Compose.

App Architecture
Cat Paywall Compose follows Google’s official architecture guidelines, providing an architectural design for small- to medium-sized projects. The architecture is structured into two main layers: the UI Layer and the Data Layer, as you’ve seen in the illustrated below:

UI Layer
The UI layer is the topmost layer of the architecture and directly interacts with users through visual elements such as text, buttons, and menus. These elements come together to form screens that users engage with. This layer also includes the ViewModel, which is responsible for managing and exposing UI state. The ViewModel helps preserve state across configuration changes, such as screen rotations or changes to the device’s language settings.
That said, the UI layer consists of UI elements and ViewModels (UI state holders). UI elements interact with ViewModels following a unidirectional data flow (UDF) pattern, where state flows downward and events flow upward. When a user triggers an event, the UI elements send it to the ViewModel, which transforms data from the data layer into UI state and serves it as the source of truth. This state is then observed by the UI elements and reflected in the interface accordingly.
Data Layer
The data layer is responsible for managing domain-specific data that the UI layer consumes. It is composed of multiple repositories, each interacting with one or more data sources, such as network services or local databases to retrieve the necessary data. In this project, the data layer primarily communicates with remote sources, including the RevenueCat backend, where most operations are handled by the SDK, enabling direct integration within the repositories.
Set up Google Play Billing and RevenueCat SDK
In this section, you’ll set up Google Play Billing and RevenueCat dashboard. By connecting the Google Play Billing to RevenueCat dashboard, you’ll be able to get unified user identity, simplified subscription management, cross-platform analytics, and accurate pricing insights across app stores. For more reasons to use RevenueCat, check out the Google I/O 2023 “Make More Money on Android” round-up.
Now, let’s start to configure the project step-by-step.
1. Google Play Product Setup
First, log in to the Google Play Console, select your application, then navigate to Monetize with Play > Products in the sidebar, and click on Subscriptions, as shown in the image below.

If you see a message saying “Your app doesn’t have any subscriptions yet. Upload a new APK,” you’ll need to add the following permission to your `AndroidManifest.xml` and upload an APK to the Play Console:
<uses-permission android:name="com.android.vending.BILLING" />
After uploading your APK with the required billing permission, you’ll be able to create a new subscription. Simply create subscriptions, as shown in the image below:

For more details, you can follow the Google Play Product Setup instructions.
2. Set Up RevenueCat Account
Next, if you don’t already have a RevenueCat account, sign up here to manage your products, monetization, analytics, and more. All you need is an email address.
3. Configure Google Play Store Service Credentials
Next, configure a set of Google Play Store service credentials to properly connect your app with the RevenueCat backend by following the steps in the Google Play Store service credentials guide. These credentials are required for RevenueCat’s servers to securely communicate with Google on your behalf.
1. Enable the Google Developer and Reporting API
2. Create a Service Account
3. Grant Financial Access to RevenueCat
4. Enter the Credentials JSON in RevenueCat
Your Google Play application is now connected to RevenueCat, allowing you to automatically import the in-app products and subscriptions you’ve created on Google Play.
To do this, navigate to the Products tab in your project settings on the RevenueCat dashboard, click the + New button, and select Import Products. RevenueCat will display a list of available store products for you to import.

After importing your products, you’ll see that the in-app purchase items have been successfully added to your RevenueCat project, as shown in the image below:

4. Create Offerings
At this point, most of the basic setup is complete—now it’s time to create your first Offering. Offerings represent the set of products displayed to users on your paywall. You can create a new offering by following the steps in the Creating an Offering guide. Once created, you’ll see the result reflected as shown below:

5. Create Paywalls
Let’s create Paywalls within the RevenueCat dashboard. RevenueCat Paywalls allow you to remotely configure your entire paywall UI, no code changes or app updates needed. It offers a wide range of components (text, images, icons, stacks, carousels, timelines), support for variables, localization, and more. For full details, refer to the Creating Paywalls guide.
To get started, navigate to the Paywalls tab in your project settings, click the + New button, and you’ll be presented with the template selection screen, as shown in the image below:

RevenueCat offers pre-built templates that you can use to quickly configure your products. Alternatively, you can start from scratch or choose from the available templates. Once a template is selected, you’ll be taken directly to the Paywall Editor screen:

The Paywall Editor, introduced in Paywalls v2, offers a Figma-like interface that allows you to design and customize the entire paywall UI. Once you’ve made your changes, click Save Changes followed by Publish Paywall, the updated design will be automatically delivered to all users (Android and iOS) through a server-driven UI system.
This means you don’t need to release a new version of your app to deliver updated paywall UIs, one of the most powerful features of RevenueCat’s Paywalls. Even non-technical team members, like product managers, can make changes and run experiments dynamically to explore different monetization strategies.
6. Configure the Android Project
Finally, if you’re building and testing within your own project, simply follow the Android SDK installation guide to integrate the RevenueCat SDK into your Android app.
If you’re working with the Cat Paywall Compose open-source project, you’ll need to retrieve your RevenueCat API key from the dashboard. To do this, navigate to the API Keys tab in your project settings on the RevenueCat dashboard, you’ll see the screen shown below:

Click the Show key button to reveal your API key. Then, create a new file named secrets.properties in the root directory of your project and add the following line:
REVENUECAT_API_KEY=<YOUR_API_KEY>
Also, be sure to update the application ID so it matches the one registered in your Google Play Console.
Now, build and run the project. If everything is set up correctly, you should see the paywall dialog appear on the details screen.
Note: Ensure your device is signed into Google Play, especially if you’re running the project on an emulator.
Implementing Paywalls in Jetpack Compose
Let’s explore how to implement Paywalls in your Android project using Jetpack Compose. This section assumes that you’ve already initialized the RevenueCat SDK, similar to how it’s done in the CatArticlesApp class.
Business Logic in a Repository
First, you need to fetch the offering information from the RevenueCat dashboard, as it defines the available purchase options for the user. This can be done easily using the Purchases.sharedInstance.awaitOfferings() method, as shown in the example below:
internal class DetailsRepositoryImpl @Inject constructor(
@Dispatcher(CatArticlesDispatchers.IO) private val ioDispatcher: CoroutineDispatcher,
) : DetailsRepository {
override fun fetchOffering(): Flow<ApiResponse<Offering>> = flow {
try {
val offerings = Purchases.sharedInstance.awaitOfferings()
offerings.current?.let { currentOffering ->
val response = ApiResponse.of { currentOffering }
emit(response)
}
} catch (e: PurchasesException) {
ApiResponse.exception(e)
}
}.flowOn(ioDispatcher)
}
RevenueCat’s Android SDK supports Kotlin coroutines, making it easy to integrate into projects that already use coroutine-based architecture. As shown in the example above, you can retrieve the current offering configured in the RevenueCat dashboard, which is exposed as a Flow.
Jetpack Compose
Everything should be set up. If you’ve already added the com.revenuecat.purchases:purchases-ui package, you can easily build the Paywall UIs in Jetpack Compose.
To display a paywall screen or dialog, RevenueCat’s UI library offers built-in components like PaywallDialog, which are fully customizable using various configuration options. You can easily implement and adjust the paywall’s appearance and behavior using the following code example:
val offering by viewModel.offering.collectAsState()
PaywallDialog(
PaywallDialogOptions.Builder()
.setDismissRequest { }
.setOffering(offering)
.setFontProvider(..)
.setCustomPurchaseLogic(..)
.setListener(object : PaywallListener {
override fun onPurchaseStarted(rcPackage: Package) {
super.onPurchaseStarted(rcPackage)
}
override fun onPurchaseCompleted(customerInfo: CustomerInfo, storeTransaction: StoreTransaction) {
super.onPurchaseCompleted(customerInfo, storeTransaction)
customer = customerInfo
}
override fun onPurchaseError(error: PurchasesError) {
super.onPurchaseError(error)
}
override fun onPurchaseCancelled() {
super.onPurchaseCancelled()
}
})
.build()
)
For detailed instructions on how to display Paywalls, refer to the Displaying Paywalls guide.
Troubleshooting
When building your project, you might encounter the following error:
Error fetching offerings – PurchasesError(code=ConfigurationError, underlyingErrorMessage=There’s a problem with your configuration. None of the products registered in the RevenueCat dashboard could be fetched from the Play Store)
If this happens, check the following:
1. Ensure you’ve correctly uploaded the Google Play Store service credentials to the RevenueCat dashboard.
2. Verify that the application ID matches the one registered in the Google Play Console.
3. If you’re testing on an emulator, make sure it’s signed in to Google Play with a valid Google account.
How to Test In-App Subscription
Thorough testing of your in-app purchases is essential to ensure a smooth purchase experience for potential customers. If you’re wondering how to effectively test in-app subscriptions in your Android app, refer to this comprehensive guide. It provides detailed steps to test in-app subscriptions properly, helping you identify and fix potential issues before launching your product on Google Play.
Wrapping Up
In this tutorial, you’ve learned how to set up the Google Play and RevenueCat dashboards, as well as how to implement paywall features in Android using Jetpack Compose. Building an in-app subscription system can be complex, especially with the frequent changes in the Google Play Billing Library and managing unified user identity, subscription lifecycle, cross-platform analytics, and accurate pricing across app stores. We simplify all of these aspects for both large teams and individual developers, and – ultimately – help you make more money.
As always, happy coding!
— Jaewoong
You might also like
- Blog post
Android In-App Subscription Tutorial
Get up and running with in-app subscription on Android
- Blog post
How we built the RevenueCat SDK for Kotlin Multiplatform
Explore the architecture and key decisions behind building the RevenueCat Kotlin Multiplatform SDK, designed to streamline in-app purchases across platforms.
- Blog post
An overview of Google Play Billing Library 5
Everything you need to know about Google Play Billing Library 5