展示流程与付费墙 - React Native

展示流程与付费墙
流程 在 Flow Builder 中构建——在设备上原生渲染,无需 WebView
Paywall Builder 付费墙 所有现有的 Paywall Builder 内容

如果您已在 Flow Builder 中创建了流程或付费墙,则无需在移动端应用代码中手动处理其渲染逻辑来向用户展示它。此类流程已包含展示内容及展示方式的完整配置。

开始之前,请确认以下几点:

  1. 您已创建流程或付费墙
  2. 您已将其添加到版位
  3. 您已获取流程并准备好视图

本指南仅适用于流程和付费墙编辑工具付费墙,需要 SDK v4.0 或更高版本。展示流程的方式与远程配置付费墙有所不同。

Adapty React Native SDK 提供两种展示流程和付费墙的方式:

  • React 组件:嵌入式组件,可集成到应用的架构和导航体系中。

  • 模态弹窗

React 组件

要将流程嵌入到现有的组件树中,可以直接在 React Native 组件层级里使用 AdaptyFlowView 组件。嵌入式组件让你能够将其集成到应用的架构和导航系统中。

AdaptyFlowView 组件在渲染时会创建视图,此时会加载配置和图片。如需预加载,可在应用更早的位置针对同一流程调用 createFlowView。组件随后会复用缓存数据,无需等待下载即可完成渲染。


function MyFlow({ flow }) {
  const flowParams = useMemo(() => ({
    loadTimeoutMs: 3000,
  }), []);

  const onCloseButtonPress = useCallback<FlowEventHandlers['onCloseButtonPress']>(() => {}, []);
  const onProductSelected = useCallback<FlowEventHandlers['onProductSelected']>((productId) => {}, []);
  const onPurchaseStarted = useCallback<FlowEventHandlers['onPurchaseStarted']>((product) => {}, []);
  const onPurchaseCompleted = useCallback<FlowEventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []);
  const onPurchaseFailed = useCallback<FlowEventHandlers['onPurchaseFailed']>((error, product) => {}, []);
  const onRestoreStarted = useCallback<FlowEventHandlers['onRestoreStarted']>(() => {}, []);
  const onRestoreCompleted = useCallback<FlowEventHandlers['onRestoreCompleted']>((profile) => {}, []);
  const onRestoreFailed = useCallback<FlowEventHandlers['onRestoreFailed']>((error) => {}, []);
  const onAppeared = useCallback<FlowEventHandlers['onAppeared']>(() => {}, []);
  const onError = useCallback<FlowEventHandlers['onError']>((error) => {}, []);
  const onLoadingProductsFailed = useCallback<FlowEventHandlers['onLoadingProductsFailed']>((error) => {}, []);
  const onUrlPress = useCallback<FlowEventHandlers['onUrlPress']>((url) => {}, []);
  const onCustomAction = useCallback<FlowEventHandlers['onCustomAction']>((actionId) => {}, []);
  const onWebPaymentNavigationFinished = useCallback<FlowEventHandlers['onWebPaymentNavigationFinished']>(() => {}, []);

  return (
    <AdaptyFlowView
      flow={flow}
      params={flowParams}
      style={styles.flow}
      onCloseButtonPress={onCloseButtonPress}
      onProductSelected={onProductSelected}
      onPurchaseStarted={onPurchaseStarted}
      onPurchaseCompleted={onPurchaseCompleted}
      onPurchaseFailed={onPurchaseFailed}
      onRestoreStarted={onRestoreStarted}
      onRestoreCompleted={onRestoreCompleted}
      onRestoreFailed={onRestoreFailed}
      onAppeared={onAppeared}
      onError={onError}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onCustomAction={onCustomAction}
      onUrlPress={onUrlPress}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}

要将流程作为独立屏幕显示,请在由 createFlowView 方法创建的 view 上调用 view.present() 方法。每个 view 只能使用一次。如果需要再次显示该流程,请重新调用 createFlowView 创建一个新的 view 实例。

禁止在不重新创建的情况下复用同一个 view,否则将导致 AdaptyUIError.viewAlreadyPresented 错误。


const view = await createFlowView(flow);

// Optional: handle flow events (close, purchase, restore, etc)
// view.setEventHandlers({ ... });

try {
  await view.present();
} catch (error) {
  // handle the error
}

多次调用 setEventHandlers 会覆盖你之前设置的处理函数,替换掉这些特定事件的默认处理函数及先前设置的处理函数。

配置 iOS 呈现样式

通过向 present() 方法传递 iosPresentationStyle 参数,可以配置流程在 iOS 上的呈现方式。该参数接受 'full_screen'(默认值)或 'page_sheet' 两个值。

try {
  await view.present({ iosPresentationStyle: 'page_sheet' });
} catch (error) {
  // handle the error
}

使用开发者自定义计时器

要在移动应用中使用开发者自定义计时器,请使用 timerId,本例中为 CUSTOM_TIMER_NY,即你在 Adapty 看板中设置的开发者自定义计时器的 Timer ID。它能确保应用动态更新计时器并显示正确的值,例如 13d 09h 03m 34s(计算方式为计时器的结束时间(如元旦)减去当前时间)。

在此示例中,CUSTOM_TIMER_NY 是你在 Adapty 看板中设置的开发者自定义计时器的 Timer IDtimerResolver 会确保你的应用动态更新计时器,显示正确的值——例如 13d 09h 03m 34s(由计时器的结束时间(如元旦)减去当前时间计算得出)。

显示对话框

在 Android 上展示流程视图时,请使用此方法代替原生 alert 对话框。在 Android 上,普通 RN 弹窗会显示在流程视图后面,导致用户看不到。此方法可确保对话框在所有平台上都能正确显示在流程上方。

try {
  const action = await view.showDialog({
    title: 'Close paywall?',
    content: 'You will lose access to exclusive offers.',
    primaryActionTitle: 'Stay',
    secondaryActionTitle: 'Close',
  });

  if (action === 'secondary') {
    // User confirmed - close the flow
    await view.dismiss();
  }
  // If primary - do nothing, user stays
} catch (error) {
  // handle error
}

用新订阅替换旧订阅

当用户在 Android 上已有活跃订阅的情况下尝试购买新订阅时,你可以通过在创建 flow 视图时传入订阅更新参数来控制新购买的处理方式。若要用新订阅替换当前订阅,请在 createFlowView 中使用 productPurchaseParams,并传入 oldSubVendorProductIdprorationMode 参数。


const productPurchaseParams = flow.paywalls
  .flatMap((variation) => variation.productIdentifiers)
  .map((productId) => {
    let params = {};
    if (Platform.OS === 'android') {
      params.android = {
        subscriptionUpdateParams: {
          oldSubVendorProductId: 'PRODUCT_ID_OF_THE_CURRENT_ACTIVE_SUBSCRIPTION',
          prorationMode: 'with_time_proration',
        },
      };
    }
    return { productId, params };
  });

const view = await createFlowView(flow, { productPurchaseParams });

如果你使用付费墙编辑工具自定义了付费墙,就无需在移动应用代码中手动渲染它来向用户展示。这类付费墙已包含展示内容和展示方式的完整配置。

开始之前,请确认:

  1. 你已创建付费墙
  2. 你已将付费墙添加到版位
  3. 你已获取付费墙并准备好视图

本指南仅适用于新版付费墙编辑工具付费墙,需要 SDK v3.0 或更高版本。不同版本付费墙编辑工具设计的付费墙以及远程配置付费墙的展示流程有所不同。

Adapty React Native SDK 提供两种展示付费墙的方式:

  • React 组件:嵌入式组件,可集成到应用的架构和导航系统中。

  • 模态展示

React 组件

React 组件方式需要 SDK 3.14.0 或更高版本。

要将付费墙嵌入到现有的组件树中,可以直接在 React Native 组件层级中使用 AdaptyPaywallView 组件。嵌入式组件允许你将其集成到应用的架构和导航系统中。

在 Android 上,如果付费墙未延伸到状态栏后方,其顶部可能会出现视觉遮罩。我们建议您为付费墙关闭此效果。请参阅付费墙顶部的视觉遮罩(Android)


function MyPaywall({ paywall }) {
  const paywallParams = useMemo(() => ({
    loadTimeoutMs: 3000,
  }), []);

  const onCloseButtonPress = useCallback<EventHandlers['onCloseButtonPress']>(() => {}, []);
  const onProductSelected = useCallback<EventHandlers['onProductSelected']>((productId) => {}, []);
  const onPurchaseStarted = useCallback<EventHandlers['onPurchaseStarted']>((product) => {}, []);
  const onPurchaseCompleted = useCallback<EventHandlers['onPurchaseCompleted']>((purchaseResult, product) => {}, []);
  const onPurchaseFailed = useCallback<EventHandlers['onPurchaseFailed']>((error, product) => {}, []);
  const onRestoreStarted = useCallback<EventHandlers['onRestoreStarted']>(() => {}, []);
  const onRestoreCompleted = useCallback<EventHandlers['onRestoreCompleted']>((profile) => {}, []);
  const onRestoreFailed = useCallback<EventHandlers['onRestoreFailed']>((error) => {}, []);
  const onPaywallShown = useCallback<EventHandlers['onPaywallShown']>(() => {}, []);
  const onRenderingFailed = useCallback<EventHandlers['onRenderingFailed']>((error) => {}, []);
  const onLoadingProductsFailed = useCallback<EventHandlers['onLoadingProductsFailed']>((error) => {}, []);
  const onUrlPress = useCallback<EventHandlers['onUrlPress']>((url) => {}, []);
  const onCustomAction = useCallback<EventHandlers['onCustomAction']>((actionId) => {}, []);
  const onWebPaymentNavigationFinished = useCallback<EventHandlers['onWebPaymentNavigationFinished']>(() => {}, []);

  return (
    <AdaptyPaywallView
      paywall={paywall}
      params={paywallParams}
      style={styles.paywall}
      onCloseButtonPress={onCloseButtonPress}
      onProductSelected={onProductSelected}
      onPurchaseStarted={onPurchaseStarted}
      onPurchaseCompleted={onPurchaseCompleted}
      onPurchaseFailed={onPurchaseFailed}
      onRestoreStarted={onRestoreStarted}
      onRestoreCompleted={onRestoreCompleted}
      onRestoreFailed={onRestoreFailed}
      onPaywallShown={onPaywallShown}
      onRenderingFailed={onRenderingFailed}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onCustomAction={onCustomAction}
      onUrlPress={onUrlPress}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}

要将付费墙作为独立页面展示,请在由 createPaywallView 方法创建的 view 上调用 view.present() 方法。每个 view 只能使用一次。如果需要再次展示付费墙,请重新调用 createPaywallView 创建一个新的 view 实例。

禁止在不重新创建的情况下复用同一个 view,否则会触发 AdaptyUIError.viewAlreadyPresented 错误。


const view = await createPaywallView(paywall);

// Optional: handle paywall events (close, purchase, restore, etc)
// view.setEventHandlers({ ... });

try {
  await view.present();
} catch (error) {
  // handle the error
}

多次调用 setEventHandlers 会覆盖你之前设置的处理器,包括默认处理器和针对特定事件已设置的处理器。

配置 iOS 展示样式

通过向 present() 方法传入 iosPresentationStyle 参数,可以配置付费墙在 iOS 上的展示方式。该参数接受 'full_screen'(默认值)或 'page_sheet' 两个值。

try {
  await view.present({ iosPresentationStyle: 'page_sheet' });
} catch (error) {
  // handle the error
}

使用开发者自定义计时器

要在移动应用中使用开发者自定义计时器,请使用 timerId——在本示例中为 CUSTOM_TIMER_NY,即你在 Adapty 看板中设置的开发者自定义计时器的 Timer ID。这样可以确保应用动态地以正确的值更新计时器——例如 13d 09h 03m 34s(由计时器的结束时间(如元旦)减去当前时间计算得出)。

在此示例中,CUSTOM_TIMER_NY 是你在 Adapty 看板中设置的开发者自定义计时器的 Timer IDtimerResolver 会确保你的应用动态更新计时器,显示正确的值——例如 13d 09h 03m 34s(由计时器的结束时间(如元旦)减去当前时间得出)。

显示对话框

在 Android 上展示付费墙视图时,请使用此方法替代原生的 alert 对话框。在 Android 上,普通的 RN alert 会出现在付费墙视图的后面,导致用户看不到。此方法可确保对话框在所有平台上都能正确显示在付费墙的上层。

try {
  const action = await view.showDialog({
    title: 'Close paywall?',
    content: 'You will lose access to exclusive offers.',
    primaryActionTitle: 'Stay',
    secondaryActionTitle: 'Close',
  });

  if (action === 'secondary') {
    // User confirmed - close the paywall
    await view.dismiss();
  }
  // If primary - do nothing, user stays
} catch (error) {
  // handle error
}

用新订阅替换旧订阅

当用户在 Android 上已有有效订阅时尝试购买新订阅,你可以在创建付费墙视图时传入订阅更新参数,控制新购买的处理方式。如需将当前订阅替换为新订阅,请在 createPaywallView 中使用 productPurchaseParams,并传入 oldSubVendorProductIdprorationMode 参数。


const productPurchaseParams = paywall.productIdentifiers.map((productId) => {
  let params = {};
  if (Platform.OS === 'android') {
    params.android = {
      subscriptionUpdateParams: {
        oldSubVendorProductId: 'PRODUCT_ID_OF_THE_CURRENT_ACTIVE_SUBSCRIPTION',
        prorationMode: 'with_time_proration',
      },
    };
  }
  return { productId, params };
});

const view = await createPaywallView(paywall, { productPurchaseParams });

故障排查

付费墙顶部的视觉遮罩层(Android)

此设置从 React Native SDK 3.15.5 起支持,且仅适用于纯 React Native 项目。

如果你使用的是 Expo 托管工作流,则无法直接添加此 Android 资源。要应用此设置,必须创建一个自定义 Expo config plugin,将相应的 Android 资源添加其中,并在 app.config.js 中注册。这是必要的,因为 Expo 会替你管理原生 Android 项目。

如果 AdaptyPaywallView 没有延伸到状态栏后面,其顶部可能仍会出现视觉遮罩。要去除该遮罩,请在你的应用中添加以下布尔资源:

  1. 前往 android/app/src/main/res/values。如果不存在 bools.xml 文件,请新建一个。

  2. 添加以下资源:

<resources>
    <bool name="adapty_paywall_enable_safe_area_paddings">false</bool>
</resources>

请注意,该更改会全局应用于应用中的所有付费墙。