Kotlin SDK
Requirements, installation and client setup of the Voidhash Android SDK.
The Kotlin SDK adds purchases, access checks, paywalls, feature flags and analytics to native Android apps that sell through Google Play. Start with the quickstart. Options and methods are in the Kotlin SDK reference.
Compatibility
minSdk23 andcompileSdk35. Play Billing 9 requirescompileSdk35 of your app too.- Android Gradle Plugin 8.9.0, Kotlin 2.0.21, Java 8 bytecode.
- Play Billing 9.1.0, which the SDK brings in.
Install
The Kotlin SDK installs from a clone of the Voidhash repository, as a Gradle included build:
git clone https://github.com/voidhashcom/voidhash.git third_party/voidhashincludeBuild("third_party/voidhash/libraries/android")dependencies {
implementation("com.voidhash:sdk")
}The quickstart lists the rest of the setup.
To pin a build, check out a commit in the clone (git -C third_party/voidhash checkout <commit>), or
add the repository as a Git submodule so your repository records the commit.
The SDK is an alpha release and isn't on Maven Central yet. Pin a commit and update regularly. See Versions and releases.
The SDK is three Gradle modules in libraries/android of the
Voidhash repository:
| Module | Coordinate | Contents |
|---|---|---|
sdk | com.voidhash:sdk | The SDK you call: com.voidhash.sdk. |
core | com.voidhash:core | The shared engine sdk depends on. You don't add it yourself. |
ui | com.voidhash:ui | The optional paywall renderer, with a native library for arm64-v8a and x86_64. |
Add the paywall renderer builds the ui
module's native library. To vendor the sources instead, copy core, sdk and ui into your project, include them,
and name their paths in gradle.properties. Modules included as :core and :sdk need no
properties.
include(":voidhash-core", ":voidhash-sdk", ":voidhash-ui")
project(":voidhash-core").projectDir = file("third_party/voidhash/core")
project(":voidhash-sdk").projectDir = file("third_party/voidhash/sdk")
project(":voidhash-ui").projectDir = file("third_party/voidhash/ui")voidhash.coreProjectPath=:voidhash-core
voidhash.sdkProjectPath=:voidhash-sdkThen depend on project(":voidhash-sdk") and project(":voidhash-ui").
Data collection
Account for what the SDK sends when you fill in Google Play's Data safety form.
| Data | When the SDK collects it | Used for |
|---|---|---|
| Device ID | Always. The SDK creates an anonymous ID for each install. | App functionality, analytics |
| User ID | When you call identify with your user ID. | App functionality, analytics |
| Email address and name | Only when you pass them to identify or set them as attributes. | App functionality |
| Purchase history | Always. The SDK reports the Google Play purchases it observes. | App functionality, analytics |
| Product interaction | While analytics consent is granted, the default: captured events, lifecycle events and screen views. | Analytics |
| Advertising data | While consent is granted: the campaign, ad and click IDs behind the install, from Google Play or Meta's apps, unless installAttribution = false. | Analytics |
| Crash data | While consent is granted: that the app crashed, and in which version. No crash logs. | Analytics |
| Coarse location | While consent is granted. Voidhash derives a city-level location from the request's IP address. | Analytics |
Events also carry the app name, version and build, package name, device brand and model, OS version, locale and time zone. The SDK reads no advertising ID or Android ID, and shares no data for tracking.
The SDK stores its state in the com.voidhash.sdk.cache shared preferences file and in
files/voidhash/. Android Auto Backup includes both, so a restored device keeps the anonymous ID. To
start restored installs with a new anonymous ID, exclude com.voidhash.sdk.cache.xml (domain
sharedpref) in your backup rules. Keep files/voidhash/, which holds queued purchases. An
anonymous buyer on a restored device then needs restorePurchases() to regain access.
Create the client
Configure one client when your app starts, then initialize it:
val voidhash = Voidhash.configure(
context = this,
publishableKey = "vh_pk_...",
options = VoidhashOptions(debug = BuildConfig.DEBUG),
)
ProcessLifecycleOwner.get().lifecycleScope.launch {
voidhash.initialize()
}Ship the publishable key, never a secret key. Release builds, including Play testing tracks, use the production key; debug builds in development mode use the test key. See API keys.
configure is synchronous and returns the client, also available as Voidhash.shared. Until
initialize() runs, the client does no work, so you can defer it until consent or sign-in.
initialize() loads stored state, then connects to Google Play, loads your project's configuration
and resumes unfinished purchases in the background. It never fails because Voidhash is
unreachable, and repeated calls are safe. Until the configuration has loaded, purchase throws
CONFIGURATION_MISSING.
Voidhash runs only in your app's main process. In another process, such as a service with its own
android:process, configure returns an inert client. If your UI runs in a named process, set
runInSecondaryProcess = true and configure Voidhash in that process only.
If your app has a directBootAware component, Android can start it after a restart before the
device is first unlocked. The app's storage isn't available then, so the client waits for the
unlock. It keeps the newest 100 events and screens and up to 100 identify and attribute calls in
memory, always keeps a reported purchase, and answers reads with no value, marked stale and
expired. getDistinctId() and sessionId return temporary IDs until the unlock; don't store or
send them. purchase, restorePurchases() and syncPurchases() throw DEVICE_LOCKED, and
presentFlow returns FLOW_UNAVAILABLE.
Once the device is unlocked, the SDK starts on its own and makes the held calls in order, and
initialize() returns once the client works. The client configure returned keeps working, so
you don't need to read Voidhash.shared again.
Calling configure again replaces the client: the previous one stops, and its queued data moves to
the new one when both use the same key and baseUrl. The previous client's identity, attribute and
store methods throw CLIENT_SHUT_DOWN. For every option VoidhashOptions takes, see
Client options.
Next steps
Something wrong or missing on this page? Tell us by email or on Discord.