Skip to main content
To get a better understanding of how paywalls are served, visit here.
Migrating from v0? View the migration guide.

Present a Paywall

Install the SDK

Add the Helium SDK to your project using Gradle.

Requirements

  • Kotlin Version: 2.0.0 or higher
  • Java Version: 8 or higher
  • Minimum Android SDK: 23 or higher
  • Compile Android SDK: 35 or higher

1. Add repositories to your settings.gradle.kts file:

Ensure you have mavenCentral() and google() in your repositories blocks.
// settings.gradle.kts

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // You might have other repositories here
    }
}
If you don’t have a dependencyResolutionManagement block, ensure google() and mavenCentral() are present in your pluginManagement { repositories { ... } } block.

2. Add the dependency to your module-level build.gradle.kts file (e.g., app/build.gradle.kts):

// app/build.gradle.kts

dependencies {
    implementation("com.tryhelium.paywall:core:4.0.0")
}

Initialize Helium

Find your API key here. If you do not have a Helium account set up yet, you can still integrate but will not be able to show a real paywall.
Initialize the Helium SDK as early as possible in your app’s lifecycle.
Helium.initialize(
    context = this,
    apiKey = "YOUR_API_KEY",
    environment = HeliumEnvironment.PRODUCTION,
)
The supplied environment can be SANDBOX or PRODUCTION. If not sure, use PRODUCTION because Helium will automatically treat debug builds as SANDBOX.
Choose the appropriate location based on your app’s architecture:
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        //*** Add this:
        configureHelium()
    }

    //*** And this:
    private fun configureHelium() {
        Helium.initialize(
            context = this,
            apiKey = "YOUR_API_KEY",
            environment = HeliumEnvironment.PRODUCTION,
        )
    }
}
And add necessary imports:
import com.tryhelium.paywall.core.Helium

Show Your First Paywall 🎉

Set up a trigger and workflow in the dashboard to show your desired paywall.
Call presentPaywall wherever you want to show a full-screen paywall:
Helium.presentPaywall(
    trigger = "premium",
    onPaywallNotShown = { reason ->
        // Handle any scenario where the paywall does not show
    }
)
Helium.presentPaywall
method
You should now be able to see Helium paywalls in your app! Well done! 🎉
Looking for alternative presentation methods? Check out the guide on Ways to Show a Paywall.

Recommended Setup

Here are some common additional steps that you may want to consider.

Identifying Users

Identifying users is optional but can help with targeting and when forwarding events to external analytics platforms. If you are not sure, you probably do not need to identify your users.
Identify users as early as you can to maximize consistency in metrics and targeting. Ideally right before you call Helium.initialize!
Set a custom user ID:
Helium.identity.userId = "custom-user-id"
Set custom user traits for targeting and analytics visibility:
Helium.identity.setUserTraits(HeliumUserTraits(
    traits = mapOf(
        "hasOnboarded" to HeliumUserTraitsArgument.BoolParam(true),
        "accountAge" to HeliumUserTraitsArgument.IntParam(30)
    )
))
// or Helium.identity.addUserTraits() if you don't want to clear existing traits

Helium Events

Helium dispatches various events during paywall presentation and purchase flow. You can optionally handle these events in your mobile app. You can also configure Helium to forward them to your existing analytics provider.

PaywallEventHandlers

You can create an instance of PaywallEventHandlers and provide lambdas for the events you are interested in. The available handlers are:
  • onOpen: Called when a paywall is displayed to the user.
  • onClose: Called when a paywall is closed for any reason.
  • onDismissed: Called when the user explicitly dismisses a paywall without purchasing.
  • onPurchaseSucceeded: Called when a purchase completes successfully.
  • onCustomPaywallAction: Called when a custom action is triggered from the paywall.
  • onAnyEvent: Called for any of the above events.
To register your handlers, use Helium.shared.addPaywallEventListener. You can either tie the listener to a lifecycle (recommended) or manage it manually. Lifecycle-Aware (Recommended) Pass a LifecycleOwner (like an Activity or Fragment) to have the listener automatically removed when the lifecycle is destroyed.
class MyActivity : AppCompatActivity() {
    private val paywallEventHandlers = PaywallEventHandlers(
        onOpen = { event -> print("Paywall opened: ${event.paywallName}") },
        onClose = { event -> print("Paywall closed: ${event.paywallName}") }
        // ... other event handlers
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Add the listener once, it will be cleaned up automatically
        Helium.shared.addPaywallEventListener(this, paywallEventHandlers)
    }
}
If you don’t provide a LifecycleOwner, you are responsible for removing the listener with removeHeliumEventListener() to prevent memory leaks.

Global Event Listener

For a more centralized approach, your class can implement the HeliumEventListener interface and handle all events in a single onHeliumEvent method.
import com.tryhelium.paywall.core.event.*

class MyActivity : AppCompatActivity(), HeliumEventListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Add the listener once, it will be cleaned up automatically
        Helium.shared.addPaywallEventListener(this, this)
    }
    override fun onHeliumEvent(event: HeliumEvent) {
        when (event) {
            is PaywallOpen -> {
                // Handle paywall open
            }
            is PaywallClose -> {
                // Handle paywall close
            }
            // ... handle other event types
        }
    }
}

Fallback Paywalls

It is highly recommended that you set up “fallbacks” to handle the rare case when a paywall fails to display. Please follow this guide to do so.
Do this after you have a paywall created that you want to use in production.

Checking Subscription Status & Entitlements

Check entitlements before showing paywalls to avoid showing a paywall to a user who should not see it.
Helium.presentPaywall(
    trigger = "my_paywall_trigger",
    config = PaywallPresentationConfig(
        dontShowIfAlreadyEntitled = true,
    ),
    onPaywallNotShown = { paywallNotShownReason ->
        // handle paywall not shown
    }
)
hasAnyEntitlement() Checks if the user has purchased any subscription or non-consumable product.hasAnyActiveSubscription() Checks if the user has any active subscription.hasEntitlementForPaywall(trigger: String) Checks if the user has entitlements for any product in a specific paywall. Returns null if paywall configuration hasn’t been downloaded yet.

Advanced

Consumables

This only applies if using the default PlayStorePaywallDelegate for purchase handling. If you are using RevenueCatPaywallDelegate, configure RevenueCat to consume appropriate purchases. If you use a custom HeliumPaywallDelegate, you are responsible for consuming purchases.
If your app sells consumable products (e.g., coins, credits, or tokens) and you want Helium to consume purchases for these products, relay these product IDs to Helium:
Helium.config.consumableIds = setOf("coins_100", "gems_50")

RevenueCat

By default, Helium will handle purchases for you! This section is typically for users who already use RevenueCat in their app.
Helium integrates seamlessly with RevenueCat so you can continue to let RevenueCat handle your purchases and entitlements.

Install HeliumRevenueCat

Add the RevenueCat dependency to your module-level build.gradle.kts:
// app/build.gradle.kts

dependencies {
    implementation("com.tryhelium.paywall:core:4.0.0")
    implementation("com.tryhelium.paywall:revenue-cat:4.0.0")
}

Configure Helium to use RevenueCat for Purchases

Simply use Helium’s pre-built RevenueCatPaywallDelegate:
Helium.config.heliumPaywallDelegate = RevenueCatPaywallDelegate()
Make sure to initialize RevenueCat before creating the RevenueCatPaywallDelegate!
It is best to do this configuration before you call Helium.initialize.

RevenueCat appUserID

If you ever change the appUserID of a user, keep Helium in sync with:
Helium.identify.revenueCatAppUserId = Purchases.sharedInstance.appUserID

Custom Purchase Handling

By default, Helium will handle purchases for you! This section is for those who want to implement custom purchase logic.
Want to add some custom behavior but still use the built-in purchase logic? Just subclass PlayStorePaywallDelegate or RevenueCatPaywallDelegate! (Be sure to make a super call for any overridden methods.)
You can create a custom delegate and implement your own purchase logic. The HeliumPaywallDelegate is defined as follows:
interface HeliumPaywallDelegate {
    suspend fun makePurchase(
        productDetails: ProductDetails,
        basePlanId: String?,
        offerId: String?,
    ): HeliumPaywallTransactionStatus
    suspend fun restorePurchases(): Boolean
    fun onHeliumEvent(event: HeliumEvent)
}

Additional Features

For the full public API and detailed parameter documentation, see the inline docstrings in the SDK source. Import Helium in your project and use your IDE’s autocomplete or jump-to-definition to explore all available methods and types.
In most cases there is no need to check download status. Helium will display a loading indication if a paywall is presented before download has completed.
The downloadStatus is a Kotlin Flow that emits HeliumConfigStatus states. The possible states are:
  • HeliumConfigStatus.NotYetDownloaded: The initial state before the download has started.
  • HeliumConfigStatus.Downloading: Indicates that the paywall configuration is currently being downloaded.
  • HeliumConfigStatus.DownloadFailure: Indicates that the paywall configuration download has failed.
  • HeliumConfigStatus.DownloadSuccess: Indicates that the paywall configuration has been successfully downloaded.
Here’s how you can observe the downloadStatus flow in your Activity or Fragment:
// In your Activity or Fragment
lifecycleScope.launch {
    Helium.shared.downloadStatus.collect { status ->
        when (status) {
            is HeliumConfigStatus.NotYetDownloaded -> {
                // Handle not yet downloaded state
            }
            is HeliumConfigStatus.Downloading -> {
                // Handle downloading state
            }
            is HeliumConfigStatus.DownloadFailure -> {
                // Handle download failure
            }
            is HeliumConfigStatus.DownloadSuccess -> {
                // Handle download success
            }
        }
    }
}
You can programmatically hide paywalls using:
// Hide the current paywall
Helium.hidePaywall()

// Hide all currently displayed paywalls
Helium.hideAllPaywalls()
Reset Helium entirely so you can call initialize again, for example after changing user traits that can affect the paywalls a user might see via targeting.
Helium.resetHelium()