Enable purchases in your custom paywall in Android SDK
This guide describes how to integrate Adapty into your custom paywalls. Keep full control over paywall implementation, while the Adapty SDK fetches products, handles new purchases, and restores previous ones.
This guide is for developers who are implementing custom paywalls. If you want the easiest way to enable purchases, use the Adapty Paywall Builder. With Paywall Builder, you create paywalls in a no-code visual editor, Adapty handles all purchase logic automatically, and you can test different designs without republishing your app.
Before you start
Set up products
To enable in-app purchases, you need to understand three key concepts:
- Products – anything users can buy (subscriptions, consumables, lifetime access)
- Paywalls – configurations that define which products to offer. In Adapty, paywalls are the only way to retrieve products, but this design lets you modify products, prices, and offers without touching your app code.
- Placements – where and when you show paywalls in your app (like
main,onboarding,settings). You set up paywalls for placements in the dashboard, then request them by placement ID in your code. This makes it easy to run A/B tests and show different paywalls to different users.
Make sure you understand these concepts even if you work with your custom paywall. Basically, they are just your way to manage the products you sell in your app.
To implement your custom paywall, you will need to create a paywall and add it to a placement. This setup allows you to retrieve your products. To understand what you need to do in the dashboard, follow the quickstart guide here.
Manage users
You can work either with or without backend authentication on your side.
However, the Adapty SDK handles anonymous and identified users differently. Read the identification quickstart guide to understand the specifics and ensure you are working with users properly.
Step 1. Get products
To retrieve products for your custom paywall, you need to:
- Get the
paywallobject by passing placement ID to thegetPaywallmethod. - Get the products array for this paywall using the
getPaywallProductsmethod.
- Kotlin
- Java
import com.adapty.Adapty
import com.adapty.models.AdaptyPaywall
import com.adapty.models.AdaptyPaywallProduct
import com.adapty.utils.AdaptyResult
fun loadPaywall() {
Adapty.getPaywall("YOUR_PLACEMENT_ID") { result ->
when (result) {
is AdaptyResult.Success -> {
val paywall = result.value
Adapty.getPaywallProducts(paywall) { productResult ->
when (productResult) {
is AdaptyResult.Success -> {
val products = productResult.value
// Use products to build your custom paywall UI
}
is AdaptyResult.Error -> {
val error = productResult.error
// Handle the error
}
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
}
import com.adapty.Adapty;
import com.adapty.models.AdaptyPaywall;
import com.adapty.models.AdaptyPaywallProduct;
import com.adapty.utils.AdaptyError;
import com.adapty.utils.AdaptyResult;
import java.util.List;
public void loadPaywall() {
Adapty.getPaywall("YOUR_PLACEMENT_ID", result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPaywall paywall = ((AdaptyResult.Success<AdaptyPaywall>) result).getValue();
Adapty.getPaywallProducts(paywall, productResult -> {
if (productResult instanceof AdaptyResult.Success) {
List<AdaptyPaywallProduct> products = ((AdaptyResult.Success<List<AdaptyPaywallProduct>>) productResult).getValue();
// Use products to build your custom paywall UI
} else if (productResult instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) productResult).getError();
// Handle the error
}
});
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
}
Step 2. Accept purchases
When a user taps on a product in your custom paywall, call the makePurchase method with the selected product. This will handle the purchase flow and return the updated profile.
- Kotlin
- Java
import android.app.Activity
import com.adapty.Adapty
import com.adapty.models.AdaptyPaywallProduct
import com.adapty.models.AdaptyPurchaseResult
import com.adapty.utils.AdaptyResult
fun purchaseProduct(activity: Activity, product: AdaptyPaywallProduct) {
Adapty.makePurchase(activity, product) { result ->
when (result) {
is AdaptyResult.Success -> {
when (val purchaseResult = result.value) {
is AdaptyPurchaseResult.Success -> {
val profile = purchaseResult.profile
// Purchase successful, profile updated
}
is AdaptyPurchaseResult.UserCanceled -> {
// User canceled the purchase
}
is AdaptyPurchaseResult.Pending -> {
// Purchase is pending (e.g., user will pay offline with cash)
}
}
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
}
import android.app.Activity;
import com.adapty.Adapty;
import com.adapty.models.AdaptyPaywallProduct;
import com.adapty.models.AdaptyProfile;
import com.adapty.models.AdaptyPurchaseResult;
import com.adapty.utils.AdaptyError;
import com.adapty.utils.AdaptyResult;
public void purchaseProduct(Activity activity, AdaptyPaywallProduct product) {
Adapty.makePurchase(activity, product, null, result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyPurchaseResult purchaseResult = ((AdaptyResult.Success<AdaptyPurchaseResult>) result).getValue();
if (purchaseResult instanceof AdaptyPurchaseResult.Success) {
AdaptyProfile profile = ((AdaptyPurchaseResult.Success) purchaseResult).getProfile();
// Purchase successful, profile updated
} else if (purchaseResult instanceof AdaptyPurchaseResult.UserCanceled) {
// User canceled the purchase
} else if (purchaseResult instanceof AdaptyPurchaseResult.Pending) {
// Purchase is pending (e.g., user will pay offline with cash)
}
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
}
Step 3. Restore purchases
Google Play and other app stores require all apps with subscriptions to provide a way users can restore their purchases.
Call the restorePurchases method when the user taps the restore button. This will sync their purchase history with Adapty and return the updated profile.
- Kotlin
- Java
import com.adapty.Adapty
import com.adapty.utils.AdaptyResult
fun restorePurchases() {
Adapty.restorePurchases { result ->
when (result) {
is AdaptyResult.Success -> {
val profile = result.value
// Restore successful, profile updated
}
is AdaptyResult.Error -> {
val error = result.error
// Handle the error
}
}
}
}
import com.adapty.Adapty;
import com.adapty.models.AdaptyProfile;
import com.adapty.utils.AdaptyError;
import com.adapty.utils.AdaptyResult;
public void restorePurchases() {
Adapty.restorePurchases(result -> {
if (result instanceof AdaptyResult.Success) {
AdaptyProfile profile = ((AdaptyResult.Success<AdaptyProfile>) result).getValue();
// Restore successful, profile updated
} else if (result instanceof AdaptyResult.Error) {
AdaptyError error = ((AdaptyResult.Error) result).getError();
// Handle the error
}
});
}
Next steps
Your paywall is ready to be displayed in the app. To see how this works in a production-ready implementation, check out the ProductListFragment.kt in our example app, which demonstrates purchase handling with proper error handling, UI feedback, and subscription management.
Next, check whether users have completed their purchase to determine whether to display the paywall or grant access to paid features.