ÿÿ Hiển thị Flows & Paywalls trong React Native | Tài liệu Adapty

Hiển thị flows & paywalls - React Native

Hiển thị flows và paywalls
✦
Flow ÄÆ°á»£c tạo trong Flow Builder — render nguyên bản trên thiết bị, không cần WebView
✦
Paywall của Paywall Builder Toàn bộ nội dung Paywall Builder hiện có

Nếu bạn đã tạo má»™t flow hoặc paywall trong Flow Builder, bạn không cần lo lắng vá» việc render nó trong code ứng dụng để hiển thị cho ngưá»i dùng. Flow đó đã bao gồm cả ná»™i dung hiển thị lẫn cách thức hiển thị.

Trước khi bắt đầu, hãy đảm bảo rằng:

  1. Bạn đã tạo một flow hoặc paywall.
  2. Bạn đã thêm nó vào một placement.
  3. Bạn đã tải flow và chuẩn bị view.

Hướng dẫn này chỉ dành cho flow và paywall sử dụng Paywall Builder, yêu cầu SDK v4.0 trở lên. Quy trình hiển thị flow khác với paywall remote config.

Adapty React Native SDK cung cấp hai cách để hiển thị flow và paywall:

  • React component: Component nhúng cho phép tích hợp vào kiến trúc và hệ thống Ä‘iá»u hướng cá»§a ứng dụng.

  • Modal presentation

Component React

Äể nhúng má»™t flow vào cây component hiện có cá»§a bạn, hãy sá»­ dụng component AdaptyFlowView trá»±c tiếp trong phân cấp component React Native. Component nhúng này cho phép bạn tích hợp nó vào kiến trúc và hệ thống Ä‘iá»u hướng cá»§a ứng dụng.

Component AdaptyFlowView tạo view khi render, tức là khi cấu hình và hình ảnh được tải xuống. Äể tải trước, hãy gá»i createFlowView cho cùng má»™t flow ở phần trước cá»§a ứng dụng. Component sẽ tái sá»­ dụng dữ liệu đã cache và render mà không cần chá» tải xuống.


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={onRestorÿÿeFailed}
      onAppeared={onAppeared}
      onError={onError}
      onLoadingProductsFailed={onLoadingProductsFailed}
      onCustomAction={onCustomAction}
      onUrlPress={onUrlPress}
      onWebPaymentNavigationFinished={onWebPaymentNavigationFinished}
    />
  );
}

Äể hiển thị má»™t flow dưới dạng màn hình độc lập, hãy dùng phương thức view.present() trên view được tạo bởi phương thức createFlowView. Má»—i view chỉ có thể dùng má»™t lần. Nếu bạn cần hiển thị flow lại, hãy gá»i createFlowView thêm má»™t lần nữa để tạo má»™t view má»›i.

Không được tái sá»­ dụng cùng má»™t view mà không tạo lại. Äiá»u này sẽ dẫn đến lá»—i 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
}

Gá»i setEventHandlers nhiá»u lần sẽ ghi đè các handler bạn đã cung cấp, thay thế cả handler mặc định lẫn các handler đã thiết lập trước đó cho những sá»± kiện cụ thể đó.

Cấu hình kiểu hiển thị trên iOS

Cấu hình cách flow được hiển thị trên iOS bằng cách truyá»n tham số iosPresentationStyle vào phương thức present(). Tham số này chấp nhận các giá trị 'full_screen' (mặc định) hoặc 'page_sheet'.

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

Sử dụng timer do developer tự định nghĩa

Äể sá»­ dụng timer do developer tá»± định nghÄ©a trong ứng dụng mobile, hãy dùng timerId, trong ví dụ này là CUSTOM_TIMER_NY — Timer ID cá»§a timer mà bạn đã thiết lập trong Adapty dashboard. Äiá»u này đảm bảo ứng dụng cá»§a bạn tá»± động cập nhật timer vá»›i giá trị chính xác — ví dụ như 13d 09h 03m 34s (được tính bằng thá»i Ä‘iểm kết thúc cá»§a timer, chẳng hạn như Ngày đầu năm má»›i, trừ Ä‘i thá»i gian hiện tại).

Trong ví dụ này, CUSTOM_TIMER_NY là Timer ID cá»§a bá»™ đếm thá»i gian do developer định nghÄ©a mà bạn đã thiết lập trong Adapty dashboard. timerResolver đảm bảo ứng dụng cá»§a bạn cập nhật động bá»™ đếm vá»›i giá trị chính xác—chẳng hạn như 13d 09h 03m 34s (được tính bằng thá»i Ä‘iểm kết thúc cá»§a bá»™ đếm, ví dụ như Năm Má»›i, trừ Ä‘i thá»i gian hiện tại).

Hiển thị hộp thoại

Sá»­ dụng phương thức này thay cho các há»™p thoại alert mặc định khi má»™t flow view Ä‘ang được hiển thị trên Android. Trên Android, các alert thông thưá»ng cá»§a RN sẽ xuất hiện phía sau flow view, khiến ngưá»i dùng không nhìn thấy. Phương thức này đảm bảo há»™p thoại luôn hiển thị đúng vị trí, phía trên flow, trên tất cả các ná»n tảng.

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
}

Thay thế một gói đăng ký bằng gói khác

Khi ngưá»i dùng cố mua má»™t gói đăng ký má»›i trong khi đã có má»™t gói đăng ký Ä‘ang hoạt động trên Android, bạn có thể kiểm soát cách xá»­ lý giao dịch mua má»›i bằng cách truyá»n tham số cập nhật gói đăng ký khi tạo flow view. Äể thay thế gói đăng ký hiện tại bằng gói má»›i, hãy sá»­ dụng productPurchaseParams trong createFlowView vá»›i các tham số oldSubVendorProductId và prorationMode.


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 });

Nếu bạn đã tùy chỉnh paywall bằng Paywall Builder, bạn không cần lo lắng vá» việc render nó trong code ứng dụng di động để hiển thị cho ngưá»i dùng. Paywall đó đã bao gồm cả ná»™i dung hiển thị lẫn cách hiển thị.

Trước khi bắt đầu, hãy đảm bảo rằng:

  1. Bạn đã tạo một paywall.
  2. Bạn đã thêm paywall vào một placement.
  3. Bạn đã lấy paywall và chuẩn bị view.

Hướng dẫn này chỉ dành cho paywall mới trong Paywall Builder, yêu cầu SDK v3.0 trở lên. Quy trình hiển thị paywall khác nhau tùy theo phiên bản Paywall Builder được sử dụng và paywall sử dụng Remote Config.

Adapty React Native SDK cung cấp hai cách để hiển thị paywall:

  • React component: Component được nhúng vào cho phép bạn tích hợp nó vào kiến trúc và hệ thống Ä‘iá»u hướng cá»§a ứng dụng.

  • Modal presentation

React component

Cách tiếp cận React component yêu cầu SDK phiên bản 3.14.0 trở lên.

Äể nhúng má»™t paywall vào cây component hiện có cá»§a bạn, hãy sá»­ dụng component AdaptyPaywallView trá»±c tiếp trong hệ thống phân cấp component React Native. Component nhúng này cho phép bạn tích hợp nó vào kiến trúc và hệ thống Ä‘iá»u hướng cá»§a ứng dụng.

Trên Android, nếu paywall không kéo dài ra phía sau thanh trạng thái, một lớp phủ trực quan có thể xuất hiện ở phía trên. Chúng tôi khuyến nghị bạn tắt tính năng này cho các paywall của mình. Xem Lớp phủ trực quan ở đầu paywall (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}
    />
  );
}

Äể hiển thị paywall dưới dạng màn hình độc lập, hãy sá»­ dụng phương thức view.present() trên view được tạo bởi phương thức createPaywallView. Má»—i view chỉ có thể được sá»­ dụng má»™t lần. Nếu bạn cần hiển thị lại paywall, hãy gá»i createPaywallView thêm má»™t lần nữa để tạo má»™t view má»›i.

Không được phép tái sử dụng cùng một view mà không tạo lại. Việc này sẽ dẫn đến lỗi 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
}

Gá»i setEventHandlers nhiá»u lần sẽ ghi đè các handler bạn đã cung cấp, thay thế cả handler mặc định lẫn các handler đã được thiết lập trước đó cho những sá»± kiện cụ thể đó.

Cấu hình kiểu hiển thị trên iOS

Cấu hình cách paywall được hiển thị trên iOS bằng cách truyá»n tham số iosPresentationStyle vào phương thức present(). Tham số này chấp nhận các giá trị 'full_screen' (mặc định) hoặc 'page_sheet'.

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

Sử dụng timer do lập trình viên định nghĩa

Äể sá»­ dụng timer do lập trình viên định nghÄ©a trong ứng dụng di động, hãy dùng timerId, trong ví dụ này là CUSTOM_TIMER_NY — Timer ID cá»§a timer do lập trình viên định nghÄ©a mà bạn đã thiết lập trên Adapty dashboard. Äiá»u này đảm bảo ứng dụng cập nhật động giá trị cá»§a timer vá»›i giá trị chính xác — chẳng hạn 13d 09h 03m 34s (được tính bằng thá»i Ä‘iểm kết thúc cá»§a timer, ví dụ như ngày đầu năm má»›i, trừ Ä‘i thá»i gian hiện tại).

Trong ví dụ này, CUSTOM_TIMER_NY là Timer ID cá»§a timer do developer định nghÄ©a mà bạn đã cài đặt trong Adapty dashboard. timerResolver đảm bảo ứng dụng cá»§a bạn cập nhật động timer vá»›i giá trị chính xác—chẳng hạn như 13d 09h 03m 34s (được tính bằng thá»i Ä‘iểm kết thúc cá»§a timer, như Ngày Äầu Năm Má»›i, trừ Ä‘i thá»i gian hiện tại).

Hiển thị dialog

Sá»­ dụng phương thức này thay cho các há»™p thoại alert thông thưá»ng khi má»™t paywall view Ä‘ang hiển thị trên Android. Trên Android, các alert RN thông thưá»ng sẽ xuất hiện phía sau paywall view, khiến ngưá»i dùng không thể nhìn thấy chúng. Phương thức này đảm bảo dialog được hiển thị đúng cách, phía trên paywall trên má»i ná»n tảng.

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
}

Thay thế một gói đăng ký bằng gói khác

Khi ngưá»i dùng cố mua má»™t gói đăng ký má»›i trong khi Ä‘ang có gói đăng ký khác Ä‘ang hoạt động trên Android, bạn có thể kiểm soát cách xá»­ lý giao dịch mua má»›i bằng cách truyá»n các tham số cập nhật gói đăng ký khi tạo paywall view. Äể thay thế gói đăng ký hiện tại bằng gói má»›i, hãy sá»­ dụng productPurchaseParams trong createPaywallView vá»›i các tham số oldSubVendorProductId và prorationMode.


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 });

Khắc phục sự cố

Lớp phủ hình ảnh ở đầu paywall (Android)

Cài đặt này được hỗ trợ từ React Native SDK 3.15.5 trở lên và chỉ khả dụng trong các dự án React Native thuần (bare).

Nếu bạn Ä‘ang sá»­ dụng Expo managed workflow, bạn không thể thêm trá»±c tiếp Android resource này. Äể áp dụng cài đặt này, bạn cần tạo má»™t Expo config plugin tùy chỉnh để thêm Android resource tương ứng và đăng ký nó trong app.config.js. Äiá»u này là bắt buá»™c vì Expo quản lý native Android project thay cho bạn.

Nếu AdaptyPaywallView không mở rá»™ng ra phía sau thanh trạng thái, má»™t lá»›p phá»§ trá»±c quan vẫn có thể xuất hiện ở phía trên. Äể xóa nó, hãy thêm tài nguyên boolean sau vào ứng dụng cá»§a bạn:

  1. Truy cập vào android/app/src/main/res/values. Nếu chưa có file bools.xml, hãy tạo mới.

  2. Thêm tài nguyên sau:

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

Lưu ý rằng các thay đổi này áp dụng toàn cục cho tất cả các paywall trong ứng dụng của bạn.

ÿÿÿÿ