VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Groups
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Kotlin SDK
Kotlin quickstart
Display a paywall
Make and restore purchases
Check access
Identify users
Evaluate feature flags
Capture analytics
Groups
Track screens
Development mode
Kotlin SDK reference
Errors
Diagnostic codes
Troubleshooting

Kotlin quickstart

Add Voidhash to a native Android app, buy a product and unlock a perk.

Install the Kotlin SDK in an existing Android app, sell a product, and unlock a perk with a test purchase. The test purchase runs in development mode, so you don't need Google Play Console yet.

Before you start

  • Your app sets minSdk 23 and compileSdk 35, and builds with Android Gradle Plugin 8.9.0 and Kotlin 2.0.21.
  • In Studio, you have a project with a perk premium and a product pro_monthly that unlocks it and has a billing duration. See Products and perks.

Set up

Install the SDK

The SDK is distributed as source in the Voidhash repository. Clone it into your project:

git clone https://github.com/voidhashcom/voidhash.git third_party/voidhash

Include the SDK's Gradle build in yours:

settings.gradle.kts
includeBuild("third_party/voidhash/libraries/android")

Depend on the SDK. Gradle substitutes the included module for this coordinate, so it needs no version:

app/build.gradle.kts
android {
    buildFeatures {
        buildConfig = true
    }
}

dependencies {
    implementation("com.voidhash:sdk")
    implementation("androidx.lifecycle:lifecycle-process:2.8.7")
}

The included build needs to find the Android SDK too: set ANDROID_HOME, or copy your project's local.properties into third_party/voidhash/libraries/android.

To pin a commit, see Install.

Add the billing permission to AndroidManifest.xml. The SDK's manifest contributes the others.

AndroidManifest.xml
<uses-permission android:name="com.android.vending.BILLING" />

Configure the client

Configure Voidhash in your Application class, and register it with android:name=".App" on the manifest's <application> element.

App.kt
import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.voidhash.sdk.Voidhash
import com.voidhash.sdk.VoidhashOptions
import kotlinx.coroutines.launch

class App : Application() {
    override fun onCreate() {
        super.onCreate()

        val voidhash = Voidhash.configure(
            context = this,
            publishableKey = if (BuildConfig.DEBUG) "vh_pk_test_..." else "vh_pk_...",
            options = VoidhashOptions(
                debug = BuildConfig.DEBUG,
                dev = BuildConfig.DEBUG,
            ),
        )

        ProcessLifecycleOwner.get().lifecycleScope.launch {
            voidhash.initialize()
        }
    }
}

Debug builds use the test publishable key and buy from a simulated store; release builds use the production key and Google Play. Copy both keys from Settings → API keys. See API keys.

configure returns at once. initialize() connects to Google Play, loads your project's configuration and resumes unfinished purchases. Elsewhere in your app, reach the client through Voidhash.shared.

Show products and buy

Load your products, show the price, and buy pro_monthly from an activity:

MainActivity.kt
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.voidhash.sdk.Voidhash
import com.voidhash.sdk.VoidhashException
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val voidhash = Voidhash.shared ?: return
        val button = Button(this).also { setContentView(it) }

        lifecycleScope.launch {
            val product = voidhash.getProducts().firstOrNull { it.slug == "pro_monthly" }
                ?: return@launch
            button.text = "Upgrade for ${product.displayPrice}"
            button.setOnClickListener {
                lifecycleScope.launch {
                    try {
                        voidhash.purchase(this@MainActivity, product)
                        if (voidhash.getCurrentPerson()?.hasActivePerk("premium") == true) {
                            button.text = "Premium unlocked"
                        }
                    } catch (error: CancellationException) {
                        throw error
                    } catch (error: VoidhashException) {
                        when (error.code) {
                            "USER_CANCELLED" -> Unit
                            "PURCHASE_PENDING" -> show("Waiting for approval.")
                            else -> show("Purchase failed: ${error.description}")
                        }
                    }
                }
            }
        }
    }

    private fun show(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show()
    }
}

purchase opens the purchase sheet, sends the purchase to Voidhash and refreshes the person. USER_CANCELLED means the person closed the sheet, which isn't a failure. PURCHASE_PENDING waits for approval, such as a pending payment method; the SDK unlocks the perk once it completes. See Make and restore purchases for every failure.

Check access

Gate premium features on the perk, not on the purchase result:

import com.voidhash.sdk.Voidhash

suspend fun hasPremium(): Boolean =
    Voidhash.shared?.getCurrentPerson()?.hasActivePerk("premium") == true

The SDK refreshes the person after every purchase and restore. See Check access for caching and offline behavior.

Test a purchase

Run a debug build on a device or emulator and tap the button. A Test purchase confirmation sheet replaces Google Play's, and the button changes to "Premium unlocked". The purchase appears in Studio under the Test environment.

Buy through Google Play (optional)

Once the simulated purchase works, test the real Google Play sheet. License testers aren't charged.

  1. Connect Google Play (Help center) and map pro_monthly to your Play product.
  2. Add your Google account as a license tester and as a tester of an internal test track.
  3. Upload a release build, which uses the production key without dev, to that track and install it from Google Play.

The purchase appears in Studio under Sandbox. See Test purchases (Help center).

Next steps

  • See a complete sample app: products, restore, a paywall with a fallback screen, sign-in and a feature flag.
  • Identify users when they sign in.
  • Troubleshooting when something doesn't work.
  • Show a paywall designed in Studio.

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousKotlin SDKNextDisplay a paywall

On this page

Before you startSet upInstall the SDKConfigure the clientShow products and buyCheck accessTest a purchaseBuy through Google Play (optional)Next steps