1. Add repositories to your settings.gradle.kts file:
Ensure you have mavenCentral() and google() in your repositories blocks.
Copy
Ask AI
// settings.gradle.ktspluginManagement { 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.
// app/build.gradle.ktsdependencies { // For advanced cases - only include this if you want to display your paywall // as an embedded view. implementation("com.tryhelium.paywall:compose-ui:4.0.0")}
Copy
Ask AI
// app/build.gradle.ktsdependencies { // If you're using RevenueCat, add this dependency: implementation("com.tryhelium.paywall:revenue-cat:4.0.0")}
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:
Application
Activity
Copy
Ask AI
class MyApplication : Application() { override fun onCreate() { super.onCreate() // Add this: configureHelium() } // And this: private fun configureHelium() { // Identify user and adjust Helium.config if needed (see next sections). // Then call initialize: Helium.initialize( context = this, apiKey = "YOUR_API_KEY", environment = HeliumEnvironment.PRODUCTION, ) }}
Copy
Ask AI
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Add this: configureHelium() } // And this: private fun configureHelium() { // Identify user and adjust Helium.config if needed (see next sections). // Then call initialize: Helium.initialize( context = this, apiKey = "YOUR_API_KEY", environment = HeliumEnvironment.PRODUCTION, ) }}
And add necessary imports:
Copy
Ask AI
import com.tryhelium.paywall.core.Helium
Helium’s initialization runs on a background thread, so you don’t have to worry about it affecting your app’s launch time.
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:
Copy
Ask AI
Helium.identity.userId = "custom-user-id"
Set custom user traits for targeting and analytics visibility:
Copy
Ask AI
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
You must have a trigger and workflow configured in the dashboard in order to show a paywall.
Call presentPaywall when you want to show a full-screen paywall. For example:
Copy
Ask AI
Helium.presentPaywall( trigger = "premium", onPaywallNotShown = { reason -> when (reason) { is PaywallNotShownReason.TargetingHoldout -> { // User is in holdout group } is PaywallNotShownReason.AlreadyEntitled -> { // User already has access // In order for this case to be hit, `config.dontShowIfAlreadyEntitled` must be true } is PaywallNotShownReason.Error -> { // Handle the rare case where a paywall // fails to show (see Fallbacks section) } } })
(Optional) Configuration for this paywall presentation.
Copy
Ask AI
data class PaywallPresentationConfig( // Activity to present from. SDK auto-tracks if not provided. val fromActivityContext: Activity? = null, // Custom traits to send to the paywall val customPaywallTraits: HeliumUserTraits? = null, // Don't show paywall if user is entitled to a product in paywall val dontShowIfAlreadyEntitled: Boolean = false, // Disable system back button closing the paywall val disableSystemBackNavigation: Boolean = false, // How the paywall animates in val presentationStyle: HeliumPresentationStyle = HeliumPresentationStyle.SLIDE_UP, // Show in fullscreen "immersive" mode which will hide system status bars. val fullscreen: Boolean = false)
(Optional but highly recommended) Handle any scenario where the paywall does not show. If user is already entitled and config.dontShowIfAlreadyEntitled is true, onPaywallNotShown(AlreadyEntitled) will be called only if onEntitled is not provided.
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.
The Helium SDK allows you to listen for various paywall-related events. This is useful for tracking analytics, responding to user interactions, or handling the paywall lifecycle.There are two ways to listen for events: using the PaywallEventHandlers class for specific callbacks, or implementing the HeliumEventListener interface to receive all events.
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.
Copy
Ask AI
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.
For a more centralized approach, your class can implement the HeliumEventListener interface and handle all events in a single onHeliumEvent method.
Copy
Ask AI
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 } }}
By default, Helium will handle purchases for you! The SDK automatically creates a PlayStorePaywallDelegate if you don’t provide one. This section is for those who want to use RevenueCat or implement custom purchase logic.
Use one of our pre-built HeliumPaywallDelegate implementations or create a custom delegate. Set the delegate before calling initialize().
PlayStorePaywallDelegate
RevenueCatPaywallDelegate
Custom Delegate
The PlayStorePaywallDelegate handles purchases using Google Play Billing:
Copy
Ask AI
// Optional - SDK auto-creates this if heliumPaywallDelegate is not setHelium.config.heliumPaywallDelegate = PlayStorePaywallDelegate(this)
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.)
Make sure you added the revenue-cat dependency as shown in the installation section.
Use RevenueCatPaywallDelegate to handle purchases through RevenueCat:
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 like so:
If you use an external payment processor like Stripe, Helium’s entitlement helpers may not be reliable. We recommend implementing your own entitlement checking in that case. If you use Stripe with RevenueCat, we recommend using RevenueCat’s entitlement APIs instead.
The Helium SDK provides multiple ways to check user entitlements and subscription status.
Entitlement Helper Methods
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.
It is highly recommended that you set up fallbacks in the uncommon case where a paywall fails to display. Please follow the linked guide to do so.Note that if you attempt to display a paywall while it is still being downloaded, a loading state will show.By default, Helium will show this loading state as needed (a shimmer view for up to 7 seconds). You can configure this behavior:
If the budget expires before the paywall is ready, a fallback paywall will show if available. Otherwise, the loading state will hide and a PaywallOpenFailed event will be dispatched.
See the Fallbacks Guide for more details on downloading and configuring fallbacks.
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:
Copy
Ask AI
// In your Activity or FragmentlifecycleScope.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 } } }}
Hiding Paywalls Programmatically
You can programmatically hide paywalls using:
Copy
Ask AI
// Hide the current paywallHelium.hidePaywall()// Hide all currently displayed paywallsHelium.hideAllPaywalls()
Reset Helium
Reset Helium entirely so you can call initialize again. Only for advanced use cases.