← ClaudeAtlas

android-in-app-purchaseslisted

In-app purchase patterns for Android using Play Billing Library 6+ - BillingClient connection lifecycle, one-time products and subscriptions, purchase flow with PurchasesUpdatedListener, server-side verification, acknowledging and consuming purchases, pending purchase handling, and exposing entitlement state via a repository-backed Flow. Use this skill whenever implementing consumable items, one-time purchases, subscription flows, or purchase restoration. Trigger on phrases like "in-app purchase", "IAP", "Play Billing", "subscription", "BillingClient", "purchase flow", "entitlement", "consumable", or "one-time product".
lenorebreakneck630/claude-zero-to-hero-android-KMP · ★ 1 · AI & Automation · score 64
Install: claude install-skill lenorebreakneck630/claude-zero-to-hero-android-KMP
# Android In-App Purchases ## Core Principles - The BillingClient connection is a long-lived resource — manage it carefully and reconnect on disconnect. - Never grant entitlement based on client-side purchase data alone. Always verify server-side. - Acknowledge every non-consumable purchase within three days or Google will reverse it. - Consume purchases only for consumable items (coins, credits, etc.) that can be re-purchased. - Expose purchase/entitlement state as a `Flow` from a repository; the domain and presentation layers consume it without touching the Billing API directly. --- ## Dependencies ```kotlin // build.gradle.kts implementation(libs.android.billing) implementation(libs.android.billing.ktx) ``` ```toml [libraries] android-billing = { module = "com.android.billingclient:billing", version.ref = "billing" } android-billing-ktx = { module = "com.android.billingclient:billing-ktx", version.ref = "billing" } [versions] billing = "6.2.1" ``` --- ## BillingClient Setup and Connection The `BillingClient` should live in the data layer, scoped to the application lifetime: ```kotlin class BillingDataSource( private val context: Context, private val coroutineScope: CoroutineScope ) : PurchasesUpdatedListener { private val _purchaseUpdates = MutableSharedFlow<List<Purchase>>( extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST ) val purchaseUpdates: SharedFlow<List<Purchase>> = _purchaseUpdates.asSharedFlow(