Skip to main content
The AppDNA paywall module lets you present server-driven paywalls in your React Native app. Paywalls are configured in the AppDNA Console and rendered by the native layer — no client-side UI code required. Purchase handling is integrated with the Billing module.

Present a Paywall

Use the top-level static method to present a paywall by its ID:
import { AppDNA } from '@appdna/react-native-sdk';

await AppDNA.presentPaywall("premium_paywall", { placement: "settings" });
Paywalls are configured in the AppDNA Console under Monetization > Paywalls. The SDK fetches the paywall definition from the server and renders it using native UI components, including product information from the App Store or Google Play.

Paywall Module

Access the paywall module through the AppDNA.paywall property for advanced usage:
const paywall = AppDNA.paywall;

Module Methods

MethodSignatureDescription
presentpresent(paywallId: string, context?: PaywallContext): Promise<void>Present a paywall
setDelegatesetDelegate(delegate: AppDNAPaywallDelegate): voidSet a delegate for lifecycle callbacks

PaywallContext

Pass additional context to customize the paywall presentation:
await AppDNA.paywall.present("premium_paywall", {
  placement: "settings",
  customData: {
    featureRequested: "advanced_analytics",
    source: "feature_gate",
  },
});
PropertyTypeDescription
placementstring | undefinedWhere the paywall was triggered from
customDataRecord<string, unknown> | undefinedCustom data for targeting and analytics

AppDNAPaywallDelegate

Set a delegate to receive paywall lifecycle callbacks:
interface AppDNAPaywallDelegate {
  onPaywallPresented(paywallId: string): void;
  onPaywallAction(paywallId: string, action: string): void;
  onPaywallPurchaseStarted(paywallId: string, productId: string): void;
  onPaywallPurchaseCompleted(
    paywallId: string,
    productId: string,
    transaction: Record<string, unknown>
  ): void;
  onPaywallPurchaseFailed(paywallId: string, error: Error): void;
  onPaywallDismissed(paywallId: string): void;
}

Example Implementation

AppDNA.paywall.setDelegate({
  onPaywallPresented(paywallId) {
    console.log(`Paywall presented: ${paywallId}`);
  },

  onPaywallAction(paywallId, action) {
    console.log(`Paywall action: ${action} on ${paywallId}`);
    // Handle custom actions (e.g., "terms_of_service", "privacy_policy")
    if (action === "terms_of_service") {
      openURL("https://example.com/terms");
    }
  },

  onPaywallPurchaseStarted(paywallId, productId) {
    console.log(`Purchase started: ${productId} from ${paywallId}`);
  },

  onPaywallPurchaseCompleted(paywallId, productId, transaction) {
    console.log(`Purchase completed: ${productId}`);
    unlockPremiumFeatures();
  },

  onPaywallPurchaseFailed(paywallId, error) {
    console.log(`Purchase failed: ${error.message}`);
  },

  onPaywallDismissed(paywallId) {
    console.log(`Paywall dismissed: ${paywallId}`);
  },
});

NativeEventEmitter Events

The SDK emits the following paywall events through React Native’s NativeEventEmitter:
EventPayloadTriggered When
onPaywallPresented{ paywallId: string }A paywall is presented to the user
onPaywallAction{ paywallId: string, action: string }The user performs a custom action
onPaywallPurchaseStarted{ paywallId: string, productId: string }A purchase is initiated from the paywall
onPaywallPurchaseCompleted{ paywallId: string, productId: string, transaction: object }A purchase completes successfully
onPaywallPurchaseFailed{ paywallId: string, error: string }A purchase fails with an error
onPaywallDismissed{ paywallId: string }The user dismisses the paywall
You do not need to subscribe to NativeEventEmitter events directly. The delegate interface handles event bridging for you. Use the native events only if you need custom low-level handling.

Auto-Tracked Events

The SDK automatically tracks the following paywall-related events:
EventTriggered When
paywall_presentedA paywall is shown to the user
paywall_dismissedThe user dismisses the paywall
paywall_actionThe user performs a custom action
paywall_purchase_startedA purchase is initiated from a paywall
paywall_purchase_completedA purchase completes from a paywall
paywall_purchase_failedA purchase fails from a paywall

Full Example

import { useEffect } from 'react';
import { AppDNA } from '@appdna/react-native-sdk';

function PremiumFeatureScreen() {
  useEffect(() => {
    AppDNA.paywall.setDelegate({
      onPaywallPresented(paywallId) {
        console.log("Paywall shown:", paywallId);
      },
      onPaywallPurchaseCompleted(paywallId, productId, transaction) {
        console.log("Purchased:", productId);
        refreshUserAccess();
      },
      onPaywallPurchaseFailed(paywallId, error) {
        showError(error.message);
      },
      onPaywallDismissed(paywallId) {
        console.log("Paywall dismissed");
      },
      onPaywallAction(paywallId, action) {
        handleCustomAction(action);
      },
      onPaywallPurchaseStarted(paywallId, productId) {
        showLoadingIndicator();
      },
    });
  }, []);

  async function showPaywall() {
    await AppDNA.paywall.present("premium_paywall", {
      placement: "feature_gate",
      customData: {
        feature: "advanced_analytics",
      },
    });
  }

  return (
    <View>
      <Text>This feature requires a premium subscription.</Text>
      <Button title="Upgrade to Premium" onPress={showPaywall} />
    </View>
  );
}
Always set the delegate before calling present() to ensure you receive the onPaywallPresented callback. If a purchase completes, the billing module’s onEntitlementsChanged listener will also fire.