Skip to documentation
Open dashboard
Docs/Build your integration

Kotlin and Google Play Billing

Set up the JVM core and Android purchase completion adapter.

Download this chapter
On this page

The JVM core uses the dev.appvisionos:iap-client:0.5.0 coordinates. The optional Android AAR is dev.appvisionos:iap-play-billing:0.5.0, with a Google Play Billing 9.1 adapter. The Billing adapter currently requires Android API 24 or later. These are source/integration coordinates in the current preview, not a promise that a public Maven registry already serves them.

Integrate the source packages

Use the approved repository checkout. A composite build can substitute the source modules in your application's settings.gradle.kts:

kotlin
includeBuild("../cherrysub/sdk/kotlin") {
    dependencySubstitution {
        substitute(module("dev.appvisionos:iap-client")).using(project(":"))
        substitute(module("dev.appvisionos:iap-play-billing")).using(project(":play-billing"))
    }
}

Adjust the relative path to your checkout. Add the corresponding dependencies to your app module and configure Google/Maven repositories as required by your build. The source build uses JVM toolchain 17; follow its checked-in Gradle/Kotlin versions when validating compatibility.

Configure the client

sessionApi below is your own authenticated token broker client, not an SDK endpoint that can accept arbitrary user identities.

kotlin
import dev.appvisionos.iap.*
import kotlin.time.Instant

val tokens = InMemoryCustomerTokenProvider(
    fetcher = CustomerTokenFetcher { appUserId ->
        val response = sessionApi.createIapToken(appUserId)
        CustomerToken(response.token, Instant.parse(response.expiresAt))
    },
)
val iap = IAPClient(
    baseUrl = "https://api-staging.cherrysub.com",
    publishableKey = "iap_pk_REPLACE_WITH_YOUR_KEY",
    customerTokenProvider = tokens,
)

The core owns its Ktor/OkHttp transport and can be used without BillingClient. Never distribute an app secret key in the APK.

Present products and submit purchases

Read iap.currentProductSet(user.id), select Google packages in returned order, and ask BillingClient for their product details and offers. Your app selects the purchase offer and starts the billing flow.

kotlin
import com.android.billingclient.api.BillingClient
import dev.appvisionos.iap.playbilling.GooglePlayPurchaseCompletion
import dev.appvisionos.iap.playbilling.submitAndCompleteGooglePlayPurchase

iap.submitAndCompleteGooglePlayPurchase(
    appUserId = user.id,
    purchase = purchase,
    productType = BillingClient.ProductType.SUBS,
    completion = GooglePlayPurchaseCompletion { verifiedPurchase ->
        storefront.acknowledgeOrConsume(verifiedPurchase)
    },
)
val subscriber = iap.subscriber(user.id)
val hasPro = subscriber.accessLevels["pro"]?.active == true

storefront.acknowledgeOrConsume is your application-owned completion policy. Subscriptions and non-consumables generally require acknowledgement; consumable behavior is chosen by your app. The adapter invokes your policy only after backend acceptance.

Every state except PurchaseState.PURCHASED is rejected before network I/O. The adapter maps BillingClient's subs/inapp values to subscription/one_time_product. A raw purchase-token method remains available for custom integration.

Recovery, lifecycle, and errors

Handle BillingClient updates and recovery queries, not only the initial UI callback. Leave pending purchases pending. Retry valid unfinished purchases after transient verification failure; do not acknowledge merely because the store UI returned.

The token cache is per user, refreshes early, coalesces requests, and is invalidated with tokens.invalidate(user.id). A 401 triggers one forced refresh; other errors are not universally retried. On teardown, call iap.close() for the core client's resources.

Test

Use the repository's Android sandbox app as a source-integrated example. Verify subscriptions and one-time products with their respective product types, a pending purchase, duplicate submission, offline recovery, refund/revocation, and a restore under a second application account. Confirm the same app/package and sandbox environment in CherrySub. Google setup · Test matrix.