Wire the call into the success branch of your StoreKit / Billing transaction handler. Use distinct event names per purchase category if you want to differentiate consumables from non-consumables (iap_consumable, iap_nonconsumable). Don't pass the SKU, price, or transaction ID.
▸Install the Kotlin (Android) SDK
// build.gradle.kts (app module)
dependencies {
implementation("com.respectlytics:respectlytics-kotlin:3.0.0")
}
Pure Kotlin coroutines implementation. No Java dependencies, no Google Play Services dependencies. ~300KB DEX overhead — compare to roughly 3.8MB for Firebase Analytics (a measurable cold-start improvement on lower-end devices).
▸Initialize Respectlytics in Kotlin (Android)
import com.respectlytics.android.Respectlytics
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Respectlytics.configure(this, appKey = "<YOUR_APP_KEY>")
}
}
Initialize once in Application.onCreate. No additional permissions in the manifest — INTERNET is sufficient. The SDK does not request AD_ID, does not query AdvertisingIdClient, and does not declare ACCESS_NETWORK_STATE.
▸Track the event in Kotlin (Android)
import com.android.billingclient.api.*
import com.respectlytics.android.Respectlytics
class IapHandler(private val skuTypes: Map<String, String>) : PurchasesUpdatedListener {
override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) return
purchases?.forEach { purchase ->
if (purchase.purchaseState != Purchase.PurchaseState.PURCHASED) return@forEach
val isConsumable = purchase.products.any { skuTypes[it] == "consumable" }
Respectlytics.track(if (isConsumable) "iap_consumable" else "iap_nonconsumable")
}
}
}
The Google Play Billing Library doesn't tell you whether a SKU is consumable — you maintain that mapping app-side. Pass it via constructor as shown.
✦Privacy & implementation notes
Consumables (a single in-game gold pack) and non-consumables (a one-time premium upgrade) have very different product implications: consumables drive ARPU directly, non-consumables drive long-term retention. Mixing them under one iap event hides the most useful breakdown.
Always finish the transaction with the platform billing API after firing the analytics event — not before. If your track call were to throw (it doesn't, but defensively), you'd want the platform to keep its retry behavior. Respectlytics's track is a fire-and-forget call into a RAM queue; it never blocks transaction completion.
Many teams discover the com.google.android.gms.permission.AD_ID permission in their merged manifest only after Google Play flags them — usually because a transitive dependency dragged it in. Respectlytics's Kotlin SDK has no Google Play Services dependency at all, so it cannot contribute to that merge.
The SDK is implemented as pure Kotlin coroutines with no Java sources, no RxJava, and no platform channels. Events are queued in a Channel<Event> buffered to a small ring (RAM-only), drained by a coroutine that flushes every 30 seconds or on backgrounding. There is no SharedPreferences usage.
⇋How this compares to other analytics SDKs
| In-app purchase event | Firebase Analytics | Mixpanel | Respectlytics |
|---|---|---|---|
| SKU stored | Recommended | Recommended | Use distinct event_name |
| Price / currency stored | Recommended | Recommended | Rejected by API |
| Transaction ID stored | Yes | Yes | Rejected by API |
| Per-user spend total | Yes | Yes | Use billing system |
| Purchase *rate* by country / platform | Yes | Yes | Yes (default) |
❓Frequently asked questions
How do we differentiate consumables from non-consumables?
Distinct event names: iap_consumable, iap_nonconsumable, optionally iap_subscription for subscriptions. Don't embed the type as a parameter — the API rejects parameters.
What about lifetime spend per user?
Out of scope for Respectlytics. Your billing system or RevenueCat already computes lifetime spend per user, with authoritative timestamps and refund-aware totals. Asking your product analytics to do this duplicates a system of record and produces drift.
Do we differentiate purchases by SKU at all?
Only if a SKU breakdown is genuinely actionable for product decisions, and only via distinct event names: iap_gold_pack, iap_starter_bundle. Keep the set small — under 10 SKUs is fine; past that, bucket the long tail.
Should we fire on purchase initiation or completion?
Completion. Initiation-fire inflates the event count with abandoned platform billing prompts. The transaction observer's .purchased state is the right hook.