RevenueHogdocs
SDKs

Android SDK

Add the RevenueHog Kotlin SDK to an Android app to identify users, attach attributes, map Play purchases to users and serve paywall SKUs from the dashboard.

The Android SDK (com.revenuehog:revenuehog-android) is a Kotlin library with zero runtime dependencies: it uses HttpURLConnection and org.json, which ship with the platform.

Requirements

  • minSdk 24, compileSdk 35, Java 11.
  • The INTERNET permission.
  • An app in your workspace whose package name matches. RevenueHog resolves Android writes to a connected Google Play app with that package first, then to an App Store app with the same ID. See Google Play.

Install

The artifact is not on Maven Central yet. Publish it to your local Maven repository from a checkout of the SDK repository:

git clone https://github.com/Parra-Inc/revenue-hog-sdks
cd revenue-hog-sdks/kotlin
./gradlew :revenuehog-android:publishToMavenLocal

Then add mavenLocal() to your repositories and depend on it:

build.gradle.kts
dependencies {
    implementation("com.revenuehog:revenuehog-android:0.2.0")
}

You can also include the module from source.

Quickstart

Configure in Application.onCreate()

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        RevenueHog.configure(this)
    }
}

Identify users

RevenueHog.identify("user_42") // after login
RevenueHog.reset()             // on logout

Attribute purchases

Wrap your Play Billing listener:

val listener = RevenueHog.billingListener(myListener) as PurchasesUpdatedListener
val billingClient = BillingClient.newBuilder(context)
    .setListener(listener)
    .enablePendingPurchases()
    .build()

Or call attributePurchase from your own listener:

override fun onPurchasesUpdated(result: BillingResult, purchases: List<Purchase>?) {
    purchases?.forEach { purchase ->
        RevenueHog.attributePurchase(
            purchaseToken = purchase.purchaseToken,
            productId = purchase.products.firstOrNull(),
        )
    }
}

How auth works

There is no API key and no attestation on Android yet (Play Integrity is planned). Requests carry no Authorization header and go through the quarantine path: RevenueHog accepts them when the package name resolves to exactly one app and labels the data unverified. See Authentication.

Play purchase tokens

attributePurchase sends the Play purchaseToken as originalTransactionId. When the write resolves to a Google Play app, the server stores the token as the subscription's lineage key, the ID every Play event of that subscription carries, so the attribution joins the customer in the live feed. Older SDK builds that send no platform on attribute calls are still recognized: a long non-numeric ID is treated as a Play token.

SDK README versus server

The SDK's README still describes Play ingestion as future work. The server now ingests Google Play through Google Play and joins SDK attributions to Play customers as described above.

API reference

All public calls are fire-and-forget onto a single background daemon thread named revenuehog. None throw.

CallWhat it does
RevenueHog.configure(context, options = Options())Sets up the SDK and flushes the offline queue. Call once in Application.onCreate().
RevenueHog.identify(userId)Links the anonymous ID, and every purchase reported under it, to your user ID. Clears the paywall cache when the user changes.
RevenueHog.setAttributes(map)Attaches a flat Map<String, String> to the user's profile.
RevenueHog.attributePurchase(purchaseToken, productId?)Links a Play purchase to the current user.
RevenueHog.billingListener(delegate?)Returns a PurchasesUpdatedListener proxy that attributes every purchase and then calls your delegate. Built with reflection, so the SDK never depends on Play Billing. If Play Billing is not on the classpath, returns delegate unchanged.
RevenueHog.reset()Forgets the user on logout and issues a fresh anonymous ID.
RevenueHog.flush()Retries the offline queue.
RevenueHog.paywall(entitlement, fallback, forceVariant?, onResult)Fetches the ordered SKU list. onResult runs on the SDK thread. See Paywall.
RevenueHog.paywallShown(paywall, rendered)Reports that the paywall appeared, with the product IDs that rendered.

All methods are @JvmStatic, so Java callers use RevenueHog.identify("user_42").

Options

RevenueHog.configure(this, Options(
    baseUrl = "https://hog.internal.example",
    logLevel = LogLevel.DEBUG,
))

Prop

Type

Paywall and PaywallSku

data class PaywallSku(val productId: String, val kind: String) // "subscription", "iap" or "unknown"

data class Paywall(
    val entitlement: String,
    val skus: List<PaywallSku>,
    val experimentId: String? = null,
    val variantKey: String? = null,
    val isFallback: Boolean,
) { val productIds: List<String> }
RevenueHog.paywall("pro", listOf("com.example.pro.annual", "com.example.pro.monthly")) { paywall ->
    // runs on the SDK thread: hop to main before touching views
    // query Play Billing with paywall.productIds, re-sort to that order, render
    RevenueHog.paywallShown(paywall, renderedProductIds)
}

The paywall fetch uses a 2.5 second connect and read timeout. If paywall is called before configure, onResult receives the fallback immediately.

What is stored

Everything lives in the SharedPreferences file dev.revenuehog.sdk: user ID (rh_user_id), anonymous ID (rh_anon_id), sent purchases (rh_sent_txns), install ID (rh_install_id), paywall cache (rh_paywall_cache) and the offline queue (rh_queue, cap 100).

What is sent

Your user ID or anonymous ID, the package name as bundleId, platform: "android", OS version (Build.VERSION.RELEASE), device model (Build.MODEL), locale and the attributes you pass. No advertising ID.

Behavior notes

  • Failed requests (network error, 429, 5xx) retry up to 3 times with backoff of 500 ms then 1 s, then queue. Other non-2xx responses are dropped.
  • The SDK skips purchases already reported for the current user.
  • The Android SDK does not send environment on paywall fetches, so the server treats them as Production.

Troubleshooting

Last updated on

On this page