← Files RevenueCatARCHIVED FILE
skills/revenuecat-entitlements-gate/platforms/kmp.md
3.77 KB · Oct 2, 2026 · 00:24 UTC
# revenuecat-entitlements-gate: Kotlin Multiplatform
`purchases-kmp` wraps the native iOS and Android SDKs. The commonMain API looks the same on both sides; only the initial configuration differs (see `integrate-revenuecat`).
## One shot check (coroutines, commonMain)
Use the `awaitCustomerInfo` suspend extension from `com.revenuecat.purchases.kmp.ktx`. It throws a `PurchasesException` on failure.
```kotlin
import com.revenuecat.purchases.kmp.Purchases
import com.revenuecat.purchases.kmp.ktx.awaitCustomerInfo
import com.revenuecat.purchases.kmp.models.PurchasesException
suspend fun hasPremium(): Boolean = try {
val info = Purchases.sharedInstance.awaitCustomerInfo()
info.entitlements.active["premium"] != null
} catch (e: PurchasesException) {
// Network or auth error. Treat as "no access" and log for diagnostics.
false
}
```
## One shot check (callbacks)
If you do not want the coroutine dependency:
```kotlin
Purchases.sharedInstance.getCustomerInfo(
onError = { /* treat as no access */ },
onSuccess = { info ->
val hasPremium = info.entitlements.active["premium"] != null
// publish to your state holder
}
)
```
## Reactive subscription
`purchases-kmp` exposes a single `PurchasesDelegate`. Implement `onCustomerInfoUpdated` and publish the current entitlement state into a `MutableStateFlow` that your UI observes.
```kotlin
import com.revenuecat.purchases.kmp.Purchases
import com.revenuecat.purchases.kmp.PurchasesDelegate
import com.revenuecat.purchases.kmp.models.CustomerInfo
import com.revenuecat.purchases.kmp.models.StoreProduct
import com.revenuecat.purchases.kmp.models.StoreTransaction
import com.revenuecat.purchases.kmp.models.PurchasesError
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
object EntitlementsRepository : PurchasesDelegate {
private val _hasPremium = MutableStateFlow(false)
val hasPremium = _hasPremium.asStateFlow()
fun start() {
Purchases.sharedInstance.delegate = this
}
override fun onCustomerInfoUpdated(customerInfo: CustomerInfo) {
_hasPremium.value = customerInfo.entitlements.active["premium"] != null
}
override fun onPurchasePromoProduct(
product: StoreProduct,
startPurchase: (
onError: (error: PurchasesError, userCancelled: Boolean) -> Unit,
onSuccess: (storeTransaction: StoreTransaction, customerInfo: CustomerInfo) -> Unit
) -> Unit
) {
// Ignore App Store promoted purchases here, or forward to your purchase flow.
}
}
```
Seed the flow once at startup so the first emission does not wait for an update:
```kotlin
// In a coroutine scope tied to app lifetime.
runCatching {
val info = Purchases.sharedInstance.awaitCustomerInfo()
EntitlementsRepository.onCustomerInfoUpdated(info)
}
```
## Notes
- `Purchases.sharedInstance.delegate` holds a single reference. If you need to fan out to multiple consumers, funnel through a single repository (as shown) and expose a `StateFlow`.
- Replace `"premium"` with the entitlement identifier configured in the RevenueCat dashboard. It is case sensitive.
- If your installed version of `purchases-kmp` does not expose `awaitCustomerInfo` in `com.revenuecat.purchases.kmp.ktx`, prefer what the IDE autocompletes and see the `purchases-kmp` README. Some versions ship result based variants in a separate module.
## Verify
1. A sandbox user with the entitlement renders the premium UI; a fresh user sees the paywall.
2. Make a sandbox purchase on each target. The delegate's `onCustomerInfoUpdated` fires and the state flow updates without relaunching.
3. On each platform, check the native logs (Xcode console on iOS, logcat on Android) for `Purchases` entries. The KMP SDK is a thin wrapper and its logs come from the native SDKs.
SHA-256: 5a10497781b03d93a52654f3e3b9bdf3b1968eae748c3870780471e43d7a5bd2