Skip to main content
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:
MethodDescription
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

PropertyTypeDescription
placementStringWhere the paywall was triggered (e.g., “settings”, “onboarding”)
experimentString?Experiment identifier for A/B testing
variantString?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:
ValueDescription
CTA_TAPPEDUser tapped the primary call-to-action button
FEATURE_SELECTEDUser selected a feature to view details
PLAN_CHANGEDUser switched between plan options
LINK_TAPPEDUser tapped a link (e.g., terms, privacy)
CUSTOMCustom action defined in the paywall config

Dismiss Reason

The DismissReason enum indicates how the paywall was closed:
ValueDescription
PURCHASEDPaywall dismissed after a successful purchase
DISMISSEDUser explicitly dismissed the paywall
TAPPED_OUTSIDEUser tapped outside the paywall to close it
PROGRAMMATICPaywall was dismissed programmatically

Auto-Tracked Events

The paywall module automatically tracks the following events:
EventTriggered When
paywall_viewPaywall is presented to the user
paywall_closePaywall is dismissed
purchase_startedUser 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