← Files RevenueCatARCHIVED FILE
skills/revenuecat-paywall/platforms/android.md
4.65 KB · Oct 4, 2026 · 12:24 UTC
# revenuecat-paywall: Android (native Kotlin)
## Install
Find the latest stable release at <https://github.com/RevenueCat/purchases-android/releases> and substitute that tag for `<latest>` in the snippet below. If GitHub is unreachable, ask the user for a version to pin or check their existing project files for one.
Add the `purchases-ui` artifact alongside `purchases`:
```kotlin
// app/build.gradle.kts
dependencies {
implementation("com.revenuecat.purchases:purchases:<latest>")
implementation("com.revenuecat.purchases:purchases-ui:<latest>")
}
```
`purchases-ui` depends on Jetpack Compose. If your app is not already a Compose app, enable Compose in the module:
```kotlin
android {
buildFeatures { compose = true }
composeOptions { kotlinCompilerExtensionVersion = "…" }
}
```
## Implement
Two APIs exist: the `Paywall` composable and the `PaywallActivityLauncher`. Pick one based on how your app is built.
### Compose: embed `Paywall` directly
```kotlin
import androidx.compose.runtime.Composable
import com.revenuecat.purchases.CustomerInfo
import com.revenuecat.purchases.Package
import com.revenuecat.purchases.PurchasesError
import com.revenuecat.purchases.models.StoreTransaction
import com.revenuecat.purchases.ui.revenuecatui.Paywall
import com.revenuecat.purchases.ui.revenuecatui.PaywallListener
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions
@Composable
fun PremiumUpsell(onDismiss: () -> Unit) {
val options = PaywallOptions.Builder(dismissRequest = onDismiss)
.setShouldDisplayDismissButton(true)
.setListener(object : PaywallListener {
override fun onPurchaseCompleted(
customerInfo: CustomerInfo,
storeTransaction: StoreTransaction,
) {
onDismiss()
}
override fun onPurchaseError(error: PurchasesError) {
// show a toast / log
}
})
.build()
Paywall(options = options)
}
```
`PaywallOptions.Builder` also has `setOffering(offering)` if you need to force a specific offering. Without it, the paywall uses `Offerings.current`.
### Activity: launch from any `ComponentActivity` or `Fragment`
```kotlin
import androidx.activity.ComponentActivity
import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallActivityLauncher
import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallResult
import com.revenuecat.purchases.ui.revenuecatui.activity.PaywallResultHandler
class MainActivity : ComponentActivity() {
private val paywallLauncher = PaywallActivityLauncher(
resultCaller = this,
resultHandler = PaywallResultHandler { result ->
when (result) {
is PaywallResult.Purchased -> { /* entitlement granted */ }
is PaywallResult.Cancelled -> { /* user dismissed */ }
is PaywallResult.Restored -> { /* restore succeeded */ }
is PaywallResult.Error -> { /* error */ }
}
},
)
private fun openPaywall() {
paywallLauncher.launch(
offering = null, // null = Offerings.current
shouldDisplayDismissButton = true,
)
}
}
```
`PaywallActivityLauncher` must be instantiated during the host `Activity` or `Fragment`'s `onCreate`. Calling `launch(…)` any time after that opens `PaywallActivity` in full screen.
## Notes
- `PaywallActivityLauncher` does not support launching from a plain `Context`. It requires an `ActivityResultCaller` (a `ComponentActivity` or `Fragment`).
- `PaywallListener` on the composable and `PaywallResultHandler` on the launcher report overlapping events. Pick one path per entry point; do not mix them.
- `setShouldDisplayDismissButton(true)` only affects original template paywalls. V2 Paywalls ignore it and render their own dismiss affordance.
- If the dashboard offering has no paywall attached, `Paywall` renders a default template. Confirm the offering has a paywall in the RevenueCat dashboard.
- Do not call `Purchases.sharedInstance.purchase(…)` alongside the paywall. The RevenueCatUI paywall runs the purchase internally.
## Verify
Run the app on a device or emulator signed into a Google sandbox tester:
1. Trigger the code path that opens the paywall. The template configured in the dashboard renders.
2. Tap a package and complete a test purchase. The paywall closes and either `PaywallListener.onPurchaseCompleted` fires (composable) or the result handler receives `PaywallResult.Purchased` (activity).
3. Dismiss with the close button. `dismissRequest` (composable) or `PaywallResult.Cancelled` (activity) fires.
4. Run `adb logcat -s Purchases` and confirm a successful transaction log line around the purchase.
SHA-256: 2f5dd6f51696177ebb8bc963eab35641175b9694c5c88020c37d6fb9c8f41d33