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.