Skip to content

Kotlin (Android)

orca-android wraps Google Play Billing so you don’t have to deal with BillingClient connection state, product queries, and acknowledgement flows yourself. You call Orca, it talks to Play Billing and your Orca dashboard underneath.

Requires Java 11+ and minSdk 24.

Add JitPack as a repository:

dependencyResolutionManagement {
repositories {
maven { url = uri("https://jitpack.io") }
google()
mavenCentral()
}
}

Then add the dependency:

implementation("com.github.maxint-app:orca-android:main")

Do this in your Application.onCreate(). It needs an app-level Context because it registers activity lifecycle callbacks internally — that’s how it knows which Activity to launch the Play purchase flow from later.

import com.maxint.orca.core.*
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Orca.configure(
this,
OrcaConfiguration(
publicKey = "your_public_key",
environment = OrcaEnvironment.SANDBOX, // PRODUCTION for release
customerEmail = null,
)
)
}
}

Call this once you know who’s logged in. Orca uses this email to check and record entitlements — it’s independent of the Google account making the purchase.

Orca.identify("user@example.com")

queryProducts() fetches your entitlements, queries Play Billing for the matching SKUs, and returns them merged and ready to render:

val products = Orca.queryProducts()
products.forEach {
println("${it.name} — ${it.formattedPrice}")
}

products is List<StoreProduct>, a sealed interface with three concrete types:

when (product) {
is StoreProduct.Subscription -> {
// product.subscriptionPeriodDays tells you the billing period
}
is StoreProduct.Consumable -> {
// repeatable purchases, like in-game currency
}
is StoreProduct.NonConsumable -> {
// one-time unlocks
}
}

Every type has id, name, price, formattedPrice, currencyCode, and entitlement — the last one is what you hand to purchase.

Pass the entitlement from the product the user picked. This must be called from a screen where an Activity is currently in the foreground — that’s how the Play purchase sheet gets launched.

val selected = products.first { it.id == "pro_monthly" }
Orca.purchase(selected.entitlement)

Trying to buy a non-consumable the customer already owns throws an IllegalStateException instead of double-charging them.

val active = Orca.activeEntitlements()
val isPro = active.any { it.entitlementId == "pro_tier" }

Or work with product data directly if that’s more convenient for your UI:

val activeProducts = Orca.activeProduct()

Both of these are safe to call repeatedly — results are cached until the next purchase.

Orca.logout()

A paywall list in Compose that fetches products and buys the tapped one:

@Composable
fun Paywall() {
var products by remember { mutableStateOf<List<StoreProduct>>(emptyList()) }
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
products = Orca.queryProducts()
}
LazyColumn {
items(products) { product ->
Button(onClick = {
scope.launch { Orca.purchase(product.entitlement) }
}) {
Text("${product.name} — ${product.formattedPrice}")
}
}
}
}

The raw catalog Orca has configured, without the Play Billing product details merged in. Useful if you want to build your own comparison table:

val entitlements = Orca.listEntitlements()
  • All the network and billing calls are suspend functions — call them from a coroutine.
  • Purchases require a visible Activity; calling purchase with the app backgrounded throws.
  • There’s no built-in purchase event stream in this SDK — after a purchase completes, just re-fetch activeEntitlements() to refresh your UI state.

orca-android is client-only. Cancelling subscriptions, listing customers, or validating webhooks needs a server SDK running on your backend: