Afficher les flows et paywalls - React Native

Display flows and paywalls
Flows Créés dans le Flow Builder — rendus nativement sur l'appareil, sans WebView
Paywalls du Paywall Builder Tout le contenu existant du Paywall Builder

Si vous avez créé un flow ou un paywall dans le Flow Builder, vous n’avez pas besoin de vous soucier de son rendu dans le code de votre application mobile pour l’afficher à l’utilisateur. Un tel flow contient à la fois ce qui doit être affiché et comment cela doit être affiché.

Avant de commencer, assurez-vous que :

  1. Vous avez créé un flow ou un paywall.
  2. Vous l’avez ajouté à un placement.
  3. Vous avez récupéré le flow et préparé la vue.

Ce guide concerne les flows et les paywalls Paywall Builder uniquement, qui nécessitent le SDK v4.0 ou une version ultérieure. Le processus de présentation des flows diffère pour les paywalls Remote Config.

Le SDK Adapty React Native propose deux façons de présenter les flows et les paywalls :

  • Composant React : un composant intégré vous permet de l’incorporer dans l’architecture et le système de navigation de votre application.

  • Présentation modale

Composant React

Pour intégrer un flow dans votre arborescence de composants existante, utilisez le composant AdaptyFlowView directement dans votre hiérarchie de composants React Native. Le composant intégré vous permet de l’incorporer dans l’architecture et le système de navigation de votre application.

Le composant AdaptyFlowView crée sa vue au moment du rendu, c’est-à-dire lorsque la configuration et les images sont chargées. Pour les précharger, appelez createFlowView pour le même flow plus tôt dans votre application. Le composant réutilise alors les données en cache et s’affiche sans attendre les téléchargements.


function MyFlow({ flow }) {
  const flowParams = useMemo(() => ({
    loadTimeoutMs: 3000,
    locale: 'en', // The localization to render the flow with
  }), []);

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

Pour afficher un flow en tant qu’écran autonome, utilisez la méthode view.present() sur la view créée par la méthode createFlowView. Chaque view ne peut être utilisée qu’une seule fois. Si vous avez besoin d’afficher le flow à nouveau, appelez createFlowView une fois de plus pour créer une nouvelle instance de view.

Réutiliser la même view sans la recréer est interdit. Cela entraînera une erreur 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
}

Appeler setEventHandlers plusieurs fois écrasera les gestionnaires que vous fournissez, remplaçant à la fois les gestionnaires par défaut et ceux précédemment définis pour ces événements spécifiques.

Configurer le style de présentation iOS

Configurez comment le flow est présenté sur iOS en passant le paramètre iosPresentationStyle à la méthode present(). Ce paramètre accepte les valeurs 'full_screen' (par défaut) ou 'page_sheet'.

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

Utiliser un minuteur défini par le développeur

Pour utiliser des minuteurs définis par le développeur dans votre application mobile, utilisez le timerId, dans cet exemple CUSTOM_TIMER_NY, le Timer ID du minuteur défini par le développeur que vous avez configuré dans l’Adapty Dashboard. Cela garantit que votre application met à jour dynamiquement le minuteur avec la valeur correcte — comme 13d 09h 03m 34s (calculée comme l’heure de fin du minuteur, par exemple le Jour de l’An, moins l’heure actuelle).

Dans cet exemple, CUSTOM_TIMER_NY est le Timer ID du minuteur défini par le développeur dans l’Adapty Dashboard. Le timerResolver permet à votre application de mettre à jour dynamiquement le minuteur avec la valeur correcte — par exemple 13j 09h 03m 34s (calculée comme la date de fin du minuteur, comme le Jour de l’An, moins l’heure actuelle).

Afficher une boîte de dialogue

Utilisez cette méthode à la place des boîtes de dialogue d’alerte natives lorsqu’une vue de flow est affichée sur Android. Sur Android, les alertes RN classiques apparaissent derrière la vue de flow, ce qui les rend invisibles pour les utilisateurs. Cette méthode garantit un affichage correct de la boîte de dialogue au-dessus du flow sur toutes les plateformes.

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
}

Remplacer un abonnement par un autre

Lorsqu’un utilisateur tente d’acheter un nouvel abonnement alors qu’un autre est déjà actif sur Android, vous pouvez contrôler la façon dont ce nouvel achat doit être traité en passant des paramètres de mise à jour d’abonnement lors de la création de la vue flow. Pour remplacer l’abonnement actuel par le nouveau, utilisez productPurchaseParams dans createFlowView avec les paramètres oldSubVendorProductId et 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 });

Si vous avez personnalisé un paywall avec le Paywall Builder, vous n’avez pas à vous soucier de son rendu dans le code de votre application mobile pour l’afficher à l’utilisateur. Un tel paywall contient à la fois ce qui doit être affiché et comment cela doit l’être.

Avant de commencer, assurez-vous que :

  1. Vous avez créé un paywall.
  2. Vous avez ajouté le paywall à un placement.
  3. Vous avez récupéré le paywall et préparé la vue.

Ce guide concerne uniquement les paywalls du nouveau Paywall Builder, qui nécessitent le SDK v3.0 ou une version ultérieure. La procédure de présentation des paywalls diffère selon la version du Paywall Builder utilisée pour les concevoir et selon les paywalls avec Remote Config.

Le SDK Adapty React Native propose deux façons de présenter les paywalls :

  • Composant React : un composant intégré que vous pouvez incorporer à l’architecture et au système de navigation de votre application.

  • Présentation modale

Composant React

L’approche par composant React nécessite le SDK 3.14.0 ou une version ultérieure.

Pour intégrer un paywall dans votre arborescence de composants existante, utilisez directement le composant AdaptyPaywallView dans la hiérarchie de composants React Native. Le composant intégré vous permet de l’incorporer dans l’architecture et le système de navigation de votre application.

Sur Android, si le paywall ne s’étend pas derrière la barre de statut, un overlay visuel peut apparaître en haut. Nous vous recommandons de le désactiver pour vos paywalls. Voir Overlay visuel en haut du 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}
    />
  );
}

Pour afficher un paywall en tant qu’écran autonome, utilisez la méthode view.present() sur la view créée par la méthode createPaywallView. Chaque view ne peut être utilisée qu’une seule fois. Si vous avez besoin d’afficher le paywall à nouveau, appelez createPaywallView une nouvelle fois pour créer une nouvelle instance de view.

Réutiliser la même view sans la recréer est interdit. Cela provoquera une erreur 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
}

Appeler setEventHandlers plusieurs fois remplacera les gestionnaires que vous fournissez, en écrasant à la fois les gestionnaires par défaut et ceux précédemment définis pour ces événements spécifiques.

Configurer le style de présentation iOS

Configurez la façon dont le paywall est présenté sur iOS en passant le paramètre iosPresentationStyle à la méthode present(). Ce paramètre accepte les valeurs 'full_screen' (par défaut) ou 'page_sheet'.

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

Utiliser un minuteur défini par le développeur

Pour utiliser des minuteurs définis par le développeur dans votre application mobile, utilisez le timerId, dans cet exemple CUSTOM_TIMER_NY, le Timer ID du minuteur défini par le développeur que vous avez configuré dans l’Adapty Dashboard. Cela permet à votre application de mettre à jour dynamiquement le minuteur avec la valeur correcte — par exemple 13d 09h 03m 34s (calculée comme la date de fin du minuteur, par exemple le Jour de l’An, moins l’heure actuelle).

Dans cet exemple, CUSTOM_TIMER_NY est le Timer ID du timer défini par le développeur que vous avez configuré dans l’Adapty Dashboard. Le timerResolver permet à votre application de mettre à jour dynamiquement le timer avec la valeur correcte — par exemple 13d 09h 03m 34s (calculée comme la date de fin du timer, comme le Jour de l’An, moins l’heure actuelle).

Afficher une boîte de dialogue

Utilisez cette méthode à la place des boîtes de dialogue d’alerte natives lorsqu’une vue de paywall est affichée sur Android. Sur Android, les alertes RN classiques apparaissent derrière la vue du paywall, ce qui les rend invisibles pour les utilisateurs. Cette méthode garantit un affichage correct de la boîte de dialogue au-dessus du paywall sur toutes les plateformes.

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
}

Remplacer un abonnement par un autre

Lorsqu’un utilisateur tente d’acheter un nouvel abonnement alors qu’un autre abonnement est actif sur Android, vous pouvez contrôler la façon dont le nouvel achat doit être traité en passant des paramètres de mise à jour d’abonnement lors de la création de la vue paywall. Pour remplacer l’abonnement actuel par le nouveau, utilisez productPurchaseParams dans createPaywallView avec les paramètres oldSubVendorProductId et 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 });

Résolution des problèmes

Superposition visuelle en haut du paywall (Android)

Ce paramètre est pris en charge à partir du SDK React Native 3.15.5 et n’est disponible que dans les projets React Native en mode bare.

Si vous utilisez un workflow géré par Expo, vous ne pouvez pas ajouter cette ressource Android directement. Pour appliquer ce paramètre, vous devez créer un plugin de configuration Expo personnalisé qui ajoute la ressource Android correspondante et l’enregistrer dans app.config.js. Cela est nécessaire car Expo gère le projet Android natif à votre place.

Si AdaptyPaywallView ne s’étend pas derrière la barre de statut, une superposition visuelle peut quand même apparaître en haut. Pour la supprimer, ajoutez la ressource booléenne suivante à votre application :

  1. Accédez à android/app/src/main/res/values. S’il n’existe pas de fichier bools.xml, créez-le.

  2. Ajoutez la ressource suivante :

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

Notez que ces modifications s’appliquent globalement à tous les paywalls de votre application.