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
| Method | Signature | Description |
|---|
present | present(paywallId: string, context?: PaywallContext): Promise<void> | Present a paywall |
setDelegate | setDelegate(delegate: AppDNAPaywallDelegate): void | Set 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",
},
});
| Property | Type | Description |
|---|
placement | string | undefined | Where the paywall was triggered from |
customData | Record<string, unknown> | undefined | Custom 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:
| Event | Payload | Triggered 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:
| Event | Triggered When |
|---|
paywall_presented | A paywall is shown to the user |
paywall_dismissed | The user dismisses the paywall |
paywall_action | The user performs a custom action |
paywall_purchase_started | A purchase is initiated from a paywall |
paywall_purchase_completed | A purchase completes from a paywall |
paywall_purchase_failed | A 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.