The AppDNA SDK supports server-driven paywalls that are configured remotely and rendered natively using Jetpack Compose. Paywalls handle the full purchase flow, from presenting product options to completing the transaction.
Present a Paywall
Present a paywall by passing the current Activity, the paywall ID, and an optional context:
import ai.appdna.sdk.AppDNA
import ai.appdna.sdk.paywall.PaywallContext
AppDNA.presentPaywall(
activity = this,
id = "premium_paywall",
context = PaywallContext(placement = "settings"),
listener = paywallListener
)
Paywall Module
The AppDNA.paywall module provides direct access to paywall functionality:
| Method | Description |
|---|
present(activity, paywallId, context?) | Presents the paywall |
setDelegate(delegate: AppDNAPaywallDelegate?) | Sets the paywall delegate |
Paywall Context
Pass additional context to control paywall behavior and tracking:
import ai.appdna.sdk.paywall.PaywallContext
val context = PaywallContext(
placement = "settings",
experiment = "paywall_test",
variant = "b"
)
AppDNA.paywall.present(
activity = this,
paywallId = "premium_paywall",
context = context
)
PaywallContext Properties
| Property | Type | Description |
|---|
placement | String | Where the paywall was triggered (e.g., “settings”, “onboarding”) |
experiment | String? | Experiment identifier for A/B testing |
variant | String? | Variant identifier within the experiment |
The placement property is used for analytics segmentation. Use descriptive placement names to track which entry points convert best.
Paywall Delegate
Implement AppDNAPaywallDelegate to receive paywall lifecycle callbacks:
import ai.appdna.sdk.paywall.AppDNAPaywallDelegate
import ai.appdna.sdk.paywall.PaywallAction
import ai.appdna.sdk.billing.TransactionInfo
class MyPaywallDelegate : AppDNAPaywallDelegate {
override fun onPaywallPresented(paywallId: String) {
Log.d("Paywall", "Paywall presented: $paywallId")
}
override fun onPaywallAction(paywallId: String, action: PaywallAction) {
Log.d("Paywall", "Action: $action on paywall: $paywallId")
}
override fun onPaywallPurchaseStarted(paywallId: String, productId: String) {
Log.d("Paywall", "Purchase started: $productId")
}
override fun onPaywallPurchaseCompleted(
paywallId: String,
productId: String,
transaction: TransactionInfo
) {
Log.d("Paywall", "Purchase completed: $productId")
// Dismiss paywall or navigate to premium content
}
override fun onPaywallPurchaseFailed(paywallId: String, error: Exception) {
Log.e("Paywall", "Purchase failed on paywall: $paywallId", error)
}
override fun onPaywallDismissed(paywallId: String) {
Log.d("Paywall", "Paywall dismissed: $paywallId")
}
}
// Set the delegate
AppDNA.paywall.setDelegate(MyPaywallDelegate())
Paywall Action
The PaywallAction enum represents user interactions within the paywall:
| Value | Description |
|---|
CTA_TAPPED | User tapped the primary call-to-action button |
FEATURE_SELECTED | User selected a feature to view details |
PLAN_CHANGED | User switched between plan options |
LINK_TAPPED | User tapped a link (e.g., terms, privacy) |
CUSTOM | Custom action defined in the paywall config |
Dismiss Reason
The DismissReason enum indicates how the paywall was closed:
| Value | Description |
|---|
PURCHASED | Paywall dismissed after a successful purchase |
DISMISSED | User explicitly dismissed the paywall |
TAPPED_OUTSIDE | User tapped outside the paywall to close it |
PROGRAMMATIC | Paywall was dismissed programmatically |
Auto-Tracked Events
The paywall module automatically tracks the following events:
| Event | Triggered When |
|---|
paywall_view | Paywall is presented to the user |
paywall_close | Paywall is dismissed |
purchase_started | User initiates a purchase from the paywall |
Purchase completion and failure events are tracked by the billing module, not the paywall module. See the Billing documentation for the full list of purchase-related events.
Paywalls are rendered using Jetpack Compose. The SDK handles the full UI lifecycle, including product loading, plan selection, and purchase flow. Your app only needs to set the delegate and handle callbacks.
Next Steps