Android SDK

Add subscriptions to an Android app in ~5 minutes. Metera verifies every Google Play purchase server-side and tracks who has access to what.

Requirements: Android minSdk 24+, Kotlin, Google Play Billing Library v7.

Base URL

Point the SDK at https://api.meterahq.com. That is the API; meterahq.com is the dashboard and does not serve these endpoints.

API (SDK, REST)https://api.meterahq.com
Dashboard (keys, plans, offerings)https://meterahq.com

Do not guess this host, and do not copy it from an older integration. Your app key is sent as Authorization: Bearer app_… on every request, so a wrong serverURL does not merely fail — it hands a live credential to whoever owns that domain. Metera is not metera.cn, metera.xyz, metera.io or metera.com; all four are third parties. If a purchase-verification call ever returns 401, check the host before you check the key.

1. Add the module

The SDK is the metera-android-sdk repo (module :metera). Clone it next to your app and include it as a composite build:

git clone https://github.com/pytronhq/metera-android-sdk.git
// settings.gradle.kts
includeBuild("../metera-android-sdk")

…then depend on it in your app module:

dependencies {
    implementation("xyz.pytron:metera")
}

2. Configure at launch

In Application.onCreate:

import xyz.pytron.metera.Metera

Metera.configure(
    context = this,
    apiKey = "app_xxx",                    // the app's API key, from the dashboard
    appUserId = null,                      // null → anonymous user
    serverURL = "https://api.meterahq.com"   // Metera's API host — see below
)

Omit appUserId to start anonymous. All Metera.* calls are suspend functions — call them from a coroutine. serverURL must be https://api.meterahq.com; the apiKey is the app key from the dashboard's API-keys screen and is scoped to one app.

3. Show a paywall

val offerings = Metera.getOfferings()
val offering = offerings.current ?: return

offering.packages.forEach { pkg ->
    // Render pkg.displayName; look up price via Play Billing if you want it.
}

Offerings are configured in the dashboard — you change the paywall without shipping an app update.

4. Purchase

try {
    val info = Metera.purchase(activity, package_)
    if (info.isActive("pro")) { /* unlock */ }
} catch (e: MeteraException.PurchaseCancelled) {
    // user backed out — no-op
}

purchase runs the Play Billing flow, sends the purchase token to Metera for authoritative server-side verification (and acknowledgement), and returns fresh CustomerInfo. It needs the current Activity.

5. Gate features

val info = Metera.getCustomerInfo()
if (info.isActive("pro")) {
    // pro features
} else {
    // show upgrade prompt
}

getCustomerInfo() fetches from the backend; Metera.cachedCustomerInfo returns the last value with no network call.

isActive("pro") wraps GET /v1/users/{appUserId}/entitlements, whose entitlements map contains only currently-active entitlements — an expired or never-granted one is an absent key, not isActive: false. If you read the REST payload directly, gate on presence (see the REST API). The string "pro" is the entitlement identifier you created in the dashboard — it must match exactly.

6. Restore purchases

Wire a "Restore Purchases" button to Metera.restorePurchases(). It re-syncs every subscription Play still considers valid.

7. Identity

// On login — merges the anonymous user into the real account:
Metera.logIn("user@example.com")

// On logout — starts a fresh anonymous user:
Metera.logOut()

8. React to renewals & refunds

Metera.customerInfoUpdates is a StateFlow<CustomerInfo?> — collect it to keep the UI in sync as state changes:

lifecycleScope.launch {
    Metera.customerInfoUpdates.collect { info ->
        info?.let { updateUI(proActive = it.isActive("pro")) }
    }
}

Notes

  • The Metera app must have a Google service account configured in the dashboard, with Play Developer API access, for verification to work.
  • obfuscatedAccountId (a PII-free per-install token) is attached to every purchase so Play RTDN notifications can be attributed to the user.
  • Prefer the raw HTTP contract? See the REST API.