Subscriptions 101: learn how to get +23% app revenue Read more

Achats intégrés sous React native : implémentation simple. Tutoriel

Ivan Dorofeev

September 5, 2022

37 min read

Table of Contents

62fdf143e0d4c00ce1483eba jp android tutorial 1 configuration 10

Les cadres de développement d’applications multi-plateformes facilitent certainement la vie des développeurs, en leur permettant de créer des applications pour plusieurs plateformes à la fois. Il y a cependant quelques inconvénients. Par exemple, React Native ne dispose d’aucun outil prêt à l’emploi pour la mise en œuvre des achats intégrés (in-app purchases). Par conséquent, vous devrez inévitablement vous tourner vers des bibliothèques tierces. 

Quelles sont les options disponibles pour la mise en œuvre des achats intégrés ?

Les bibliothèques les plus populaires pour les abonnements (subscriptions) intégrés dans les applications React Native sont react-native-iap et expo-in-app-purchases. Je vais cependant parler de react-native-adapty,  car elle présente un certain nombre d’avantages par rapport aux autres bibliothèques :

  • Contrairement à ces derniers, il assure la validation des achats (purchase validation) sur le serveur.
  • Il prend en charge toutes les fonctionnalités récemment implémentées par les magasins d’applications, depuis les offres promotionnelles (promo offers) jusqu’aux  fonctionnalités de paiement anticipé (pay-upfront). Il est également rapide pour prendre en charge les nouvelles fonctionnalités à venir.
  • Le code finit par être plus clair et plus simple.
  • Vous pouvez modifier votre offre de produits et ajouter ou supprimer de nouvelles offres sans devoir passer par le cycle complet de publication. Il n’est pas nécessaire de publier des versions bêta et d’attendre l’approbation.

Il y a bien plus dans le SDK Adapty que ça. Vous disposez d’outils d’analyse intégrés pour tous les indicateurs clés, d’une analyse de cohorte (cohort analysis), d’une validation des achats sur serveur, d’un test AB pour les paywalls, de campagnes promotionnelles avec une segmentation flexible, d’intégrations d’outils d’analyse tiers, etc.

Dans cet article

Pour l’instant, nous allons parler du paramétrage des achats intégrés dans les applications React Native. Voici ce que nous allons couvrir aujourd’hui :

  1. Pourquoi Expo ne fonctionne pas pour les achats intégrés dans les applications React Native.
  2. Création d’un compte de développeur.
  3. Configuration de Adapty:
    Configuration de l’App Store
    Configuration de Play Store
  4. Ajout d’abonnements.
  5. Création d’un paywall.
  6. Installation de react-native-adapty.
  7. Un exemple d’application et le résultat. 

Dans ce guide, nous allons essayer de créer une application qui affiche des photos de chats pour les utilisateurs abonnés et propose une offre d’abonnement à tous les autres. 

Pourquoi Expo ne fonctionne pas pour les achats intégrés dans les applications React Native ?

Pour résumer brièvement la situation : Expo “managed” ne prend pas en charge les méthodes natives des magasins d’applications pour le traitement des achats (également connues sous le nom de kits de magasin). Vous devrez soit vous en tenir au RN pur, soit utiliser le flux de travail de Expo.

D’emblée, je dois décevoir ceux qui ont pensé à utiliser Expo : ça ne marchera pas. Expo est un cadre React Native qui facilite grandement le développement des applications. Leur flux de travail géré n’est cependant pas compatible avec le traitement des achats et des abonnements. Expo n’utilise pas de code natif dans ses méthodes et ses composants (les deux sont en JS uniquement), ce qui est requis pour les kits de magasin. Il n’y a aucun moyen d’implémenter les achats intégrés dans les boutiques mobiles avec JavaScript, vous devrez donc vous “éjecter“.

Création d’un compte de développeur

Tout d’abord, vous devrez configurer des comptes app store, ainsi que créer et configurer les achats et les abonnements pour iOS et Android. Cela ne devrait pas vous prendre plus de 20 minutes.

Si vous n’avez toujours pas configuré votre compte de développeur et vos produits dans App Store Connect et/ou Google Play Console, consultez ces guides :

  • Pour iOS: lisez le guide depuis le début et jusqu’à la rubrique “Obtenir la liste de SKProduct”(Getting the list of SKProduct), car c’est là que nous commençons à parler des implémentations natives. 
  • Pour Android: lisez le guide depuis le début et jusqu’à la rubrique “Obtenir une liste de produits dans une application”.

Configurer Adapty

Pour react-native-adapty, vous devrez d’abord configurer votre tableau de bord Adapty. Cela ne prendra pas beaucoup de temps, mais vous obtiendrez tous les avantages énumérés ci-dessus qu’Adapty a sur le codage en dur.

À la troisième étape, vous serez invité à configurer l’App Store et Google Play.

61dd2e3df0eb779b1966c336 wwqbyclqbg22r4uxyue6f wty 8kx1lgeepf9 ctrv5mcvrihoe1upbhw 0jth wqwkhjc4az

Pour iOS, vous aurez besoin de :

  • Spécifiez l’ID du Bundle ;
  • Paramétrez les notifications du serveur (server notifications) App Store ;
  • Spécifiez le secret partagé (shared secret) App Store Connect. 

Ces champs sont obligatoires pour que les achats fonctionnent. 

Chaque champ dispose d’une indication “Découvrez comment”(Read how) qui contient des guides pratiques étape par étape. Consultez-les si vous avez des questions.

L’ID du Bundle est l’ID unique de votre application. Il doit correspondre à celui que vous avez spécifié dans Xcode, dans Targets > [App Name] > General :

61dd2e3d0bcefe3f92ed87c6 ofteg 29 hasv9sywd5gpm4vxtbwppkdlkqkatwvl3jprjkq3ffp3xzdcuett5qf uzavsew

Pour Android, les champs obligatoires sont le nom du paquet et le fichier clé du compte de service. Tous ces domaines ont également leurs propres conseils de lecture. Le nom du paquet fait sous Android ce que l’ID du Bundle fait sous iOS. Il doit correspondre à celui que vous avez spécifié dans votre code, qui se trouve dans le fichier /android/app/build.gradle dans android.defaultConfig.applicationId :

61dd2e3dad4d57621f800483 qbh6fhwgcmjiqsriilrm2ubc2xjswr8buchwntern5xn8fkxqxgep4hbrftvqyfu3rgx4dg x3c6iyvemvxarmvemiu

A la quatrième étape, vous serez invité à connecter SDK Adapty à votre application. Mais sautez cette étape pour l’instant, nous y reviendrons un peu plus tard.

Une fois que vous vous êtes inscrit, consultez l’onglet des paramètres et n’oubliez pas que c’est là que se trouve votre clé publique SDK. Vous aurez besoin de la clé plus tard.

61dd2e3d3fce0994fd3f0c38 0 z1xu4tx6yoskp5gysva4jz ksmpt59lwmlpt8mol5fmyuqn1gygyvmqf52rgz5drhlvtp2gekrd0pwygz8pydmejdeskwggxp5 f6kevd ftdziotbnzpcv6bsqgojjk9unzt

Ajout d’un abonnement

Adapty utilise des produits pour différents abonnements. Votre abonnement aux photos de chats peut être hebdomadaire, semestriel ou annuel. Chacune de ces options sera un produit Adapty distinct.

Spécifions dans le tableau de bord que nous avons un seul produit. Pour ce faire, allez dans Products & A/B Tests → Products et cliquez sur Create product. 

Ici, vous devrez spécifier le nom du produit, c’est-à-dire comment cet abonnement apparaîtra dans votre tableau de bord Adapty. 

Vous devrez également indiquer l’ID du produit App Store et l’ID du produit Play Store. Si vous le souhaitez, spécifiez également la période et le nom pour l’analyse. Cliquez sur Save.

61dd2e3d3abe166794791c09 fukrvgjrhor1rtmrrmvq4mar f3rdr2nqpbojb44aa5ipwcq 9bbq7ajpq085sroobeltpnw9rtdfah a0pqoqthyqbj8ayaahikjioylj1gszmlfh mhwlqowynrpaabjjkvoln

Création d’un paywall

Vous devez maintenant concevoir un paywall, c’est-à-dire un écran qui limite l’accès de l’utilisateur aux fonctions premium et lui propose une offre d’abonnement. Vous devrez ajouter le produit que vous avez créé à votre paywall. Pour ce faire, cliquez sur Create paywall dans la même section (Products & A/B Tests → Paywalls).

  • Choisissez un nom de paywall tel que vous et votre équipe pourrez facilement déduire, simplement en regardant le nom, de quel paywall il s’agit.
  • Vous utiliserez l’ID du paywall pour afficher ce paywall dans votre application. Pour notre exemple d’application, nous utiliserons “cats_paywall”.
  • Dans le menu déroulant ” Product “, sélectionnez votre abonnement.

Cliquez sur Save & publish.

61dd2e3ddced150628dcbffb uw2xexcu4rprox7 esl56chynz2mu1iqtae1xlweot55m7lqpugfjxtzp kyg vtawjwjhcpkmuno ysa i el8xnv2v 6seczhpmdhy7xsswndb1 2kbuu9bfhqltew h4yq5tj

C’est tout pour la configuration. Maintenant, nous allons ajouter les dépendances et écrire le code.

Installation de react-native-adapty

1. Tout d’abord, ajoutez la dépendance :

yarn add react-native-adapty

2. Installez les pods iOS. Si vous n’avez pas encore le pod CLI, je vous recommande vivement de le télécharger. Vous en aurez certainement beaucoup besoin dans le développement iOS.

#pods get installed into the native iOS project, which, by default, is the /ios folderpod install --project-directory=ios

3. Les projets iOS React Native étant écrits en Obj-C, vous devrez créer un en-tête de pontage Swift pour qu’Obj-C puisse lire les bibliothèques Swift. Pour ce faire, il suffit d’ouvrir votre projet Xcode et de créer un nouveau fichier Swift. Xcode vous demandera si vous voulez créer un en-tête de pontage, ce qui est exactement ce que vous voulez. Cliquez sur Create.

61dd2e3da26af882142a7518 o2nycy3aeotxdr5m bcojaezt3hnj uzz 1jim7k25r9bn2jedf yhyvhgwx2qlequduyul0hwxxpjixeoe
61dd2e3ef99ba01c6f90aa08 rldljwkg131iukgbpxmwbjwu5q0m mhqxje3s9i1hljldxeau b8ehfimaw2ttxqq

4. Pour Android, assurez-vous que le projet-/android/build.gradle par défaut- utilise le kotlin-gradle-plugin de la version 1.4.0 ou supérieure :

... buildscript { 
  ... dependencies { 
    ... classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0" 
  } 
} ...

5. Pour Android, vous devez activer multiDex, qui se trouve dans le fichier de configuration de l’application (/android/app/build.gradle par défaut).

... 
android { 
  ... defaultConfig { 
    ... multiDexEnabled true 
  } 
}

Voilà, vous êtes prêt et vous pouvez v̶o̶u̶s̶ ̶r̶e̶p̶o̶s̶e̶r̶ commencer à coder ! 

Récupération de la liste des produits dans l’application

Il y a des tonnes de choses utiles qui se passent sous le capot de react-native-adapty. Vous en aurez certainement besoin, tôt ou tard, c’est pourquoi vous devez initialiser la bibliothèque au tout début de votre flux. Allez aussi loin que possible dans le code de votre application (vous pouvez aussi le faire directement dans le App.tsx) et lancez l’initialisation :

// import the method
import { activateAdapty } from 'react-native-adapty';

// We’ve had this App component in our app’s root
const App: React.FC = () => {
  ...
// we’re invoking it once in a root component on mount
  useEffect(() => {
    activateAdapty({ sdkKey: 'MY_PUBLIC_KEY' });
  },[]);
  ...
}

Ici, remplacez MY_PUBLIC_KEY par votre clé publique SDK qui se trouve dans les paramètres du tableau de bord. En fait, la méthode activateAdapty() peut être invoquée plus d’une fois et à plus d’un endroit, mais nous nous en tiendrons à cette conception.

Maintenant, nous pouvons récupérer les produits que nous avons ajoutés dans le tableau de bord Adapty :

import { adapty } from 'react-native-adapty';

async function getProducts() {
	const {paywalls, products} = await adapty.paywalls.getPaywalls();

	return products;
}

Maintenant, passons à la pratique : Nous essaierons de créer une petite application où l’on pourra parcourir les produits de nos paywalls et faire des achats.

Exemple d’application

Je vais faire court à partir de maintenant pour éviter de rendre la logique de base trop compliquée. Je coderai également en TypeScript pour vous montrer quels types sont utilisés et où. Pour les tests, je vais utiliser mon bon vieil iPhone 8. N’oubliez pas qu’à partir d’iOS 14, l’App Store interdit l’utilisation de kits de magasin dans des émulateurs : vous ne pouvez tester qu’à l’aide d’appareils physiques.

Composant racine App.tsx

1. Tout d’abord, créons un composant racine App.tsx qui comportera un bouton d’affichage du paywall. Nous avons déjà configuré la navigation via react-native-navigation nous pensons qu’elle est bien meilleure que l’option react-navigation recommandée dans la documentation officielle.

PROBLÈME

import React, { useEffect, useState } from "react";
import { Button, StyleSheet, View } from "react-native";
import { adapty, activateAdapty, AdaptyPaywall } from "react-native-adapty";

export const App: React.FC = () => {
  const [paywalls, setPaywalls] = useState<AdaptyPaywall[]>([]);

  useEffect(() => {
    async function fetchPaywalls(): Promise<void> {
      await activateAdapty({ sdkKey: "MY_PUBLIC_KEY" });

      const result = await adapty.paywalls.getPaywalls();
      setPaywalls(result.paywalls);
    }

    fetchPaywalls();
  }, []);

  return (
    <View style={styles.container}>
      <Button
        title="Show the paywall"
        onPress={() => {
          const paywall = paywalls.find(
            (paywall) => paywall.developerId === "cats_paywall"
          );

          if (!paywall) {
            return alert("There is no such paywall");
          }
					// Switching to a paywall...
        }}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: "center", justifyContent: "center" },
});

Que se passe-t-il ici ? Lors du montage, la fonction fetchPaywalls() est invoquée. Il active le SDK et enregistre les paywalls en l’état afin que l’utilisateur n’ait pas à attendre pour les récupérer après avoir appuyé sur le bouton. Il n’y a qu’un seul bouton dans la vue qui est censé amener l’utilisateur au paywall que nous avons précédemment conçu dans le tableau de bord.

En fait, il est possible de récupérer les paywalls ici même, sans les sauvegarder en l’état. Par défaut, adapty.paywalls.getPaywalls() ira les chercher dans le stockage du cache (après les avoir mis en cache au lancement), ce qui signifie que vous n’aurez pas à attendre que la méthode communique avec le serveur.

Voici le résultat :

react native in-app purchases tutorial payall

Un composant  paywall

2. Écrivons un composant paywall dans le même fichier.

// there are more imports here
import React, { useEffect, useState } from "react";
import {
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  PlatformColor,
} from "react-native";
import {
  adapty,
  activateAdapty,
  AdaptyPaywall,
  AdaptyProduct,
} from "react-native-adapty";
import { Navigation } from "react-native-navigation";

// ...

interface PaywallProps {
  paywall: AdaptyPaywall;
  onRequestBuy: (product: AdaptyProduct) => void | Promise<void>;
}
export const Paywall: React.FC<PaywallProps> = ({ paywall, onRequestBuy }) => {
  const [isLoading, setIsLoading] = useState<boolean>(false);

  return (
    <SafeAreaView style={styles.container}>
      {paywall.products.map((product) => (
        <View key={product.vendorProductId}>
          <Text>{product.localizedTitle}</Text>
          <Button
            title={`Buy for за ${product.localizedPrice}`}
            disabled={isLoading}
            onPress={async () => {
              try {
                setIsLoading(true);
                await onRequestBuy(product);
              } catch (error) {
                alert("Error occured :(");
              } finally {
                setIsLoading(false);
              }
            }}
          />
        </View>
      ))}
    </SafeAreaView>
  );
};

// A new key
const styles = StyleSheet.create({
  container: { flex: 1, alignItems: "center", justifyContent: "center" },
  paywallContainer: {
    flex: 1,
    alignItems: "center",
    justifyContent: "space-evenly",
    backgroundColor: PlatformColor("secondarySystemBackground"),
  },
});

Ici, nous allons simplement mapper les produits du paywall et afficher un bouton d’achat à côté de chaque produit.

Enregistrement de l’écran

3. Pour voir à quoi cela ressemble, enregistrons cet écran dans react-native-navigation. Si vous utilisez un autre système de navigation, passez cette étape. Mon fichier racine index.js ressemble à ceci :

import "react-native-gesture-handler";
import { Navigation } from "react-native-navigation";

import { App, Paywall } from "./App";

Navigation.registerComponent("Home", () => App);
Navigation.registerComponent("Paywall", () => Paywall);

Navigation.events().registerAppLaunchedListener(() => {
  Navigation.setRoot({
    root: { stack: { children: [{ component: { name: "Home" } }] } },
  });
});

Bouton “Afficher le paywall”.

4. Maintenant, il ne nous reste plus qu’à attribuer une action au bouton “Afficher le paywall”. Dans notre cas, il s’agit d’une modale via Navigation.

Navigation.showModal<PaywallProps>({
    component: {
      name: "Paywall",
      passProps: {
        paywall,
        onRequestBuy: async (product) => {
          const purchase = await adapty.purchases.makePurchase(product);
          // Doing everything we need
          console.log("purchase", purchase);
        },
      },
    },
  });

L’intégralité du fichier App.tsx :

import React, { useEffect, useState } from "react";
import {
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  PlatformColor,
} from "react-native";
import {
  adapty,
  activateAdapty,
  AdaptyPaywall,
  AdaptyProduct,
} from "react-native-adapty";
import { Navigation } from "react-native-navigation";
export const App: React.FC = () => {
  const [paywalls, setPaywalls] = useState<AdaptyPaywall[]>([]);
  useEffect(() => {
    async function fetchPaywalls(): Promise<void> {
      await activateAdapty({
        sdkKey: "MY_PUBLIC_KEY",
      });

      const result = await adapty.paywalls.getPaywalls();
      setPaywalls(result.paywalls);
    }

    fetchPaywalls();
  }, []);

  return (
    <View style={styles.container}>
      <Button
        title="Show paywall"
        onPress={() => {
          const paywall = paywalls.find(
            (paywall) => paywall.developerId === "cats_paywall"
          );

          if (!paywall) {
            return alert("There is no such paywall");
          }

          Navigation.showModal<PaywallProps>({
            component: {
              name: "Paywall",
              passProps: {
                paywall,
                onRequestBuy: async (product) => {
                  const purchase = await adapty.purchases.makePurchase(product);
                  // Doing everything we need
                  console.log("purchase", purchase);
                },
              },
            },
          });
        }}
      />
    </View>
  );
};

interface PaywallProps {
  paywall: AdaptyPaywall;
  onRequestBuy: (product: AdaptyProduct) => void | Promise<void>;
}
export const Paywall: React.FC<PaywallProps> = ({ paywall, onRequestBuy }) => {
  const [isLoading, setIsLoading] = useState<boolean>(false);

  return (
    <SafeAreaView style={styles.paywallContainer}>
      {paywall.products.map((product) => (
        <View key={product.vendorProductId}>
          <Text>{product.localizedTitle}</Text>
          <Button
            title={`Buy for ${product.localizedPrice}`}
            disabled={isLoading}
            onPress={async () => {
              try {
                setIsLoading(true);
                await onRequestBuy(product);
              } catch (error) {
                alert("Error occured :(");
              } finally {
                setIsLoading(false);
              }
            }}
          />
        </View>
      ))}
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: "center", justifyContent: "center" },
  paywallContainer: {
    flex: 1,
    alignItems: "center",
    justifyContent: "space-evenly",
    backgroundColor: PlatformColor("secondarySystemBackground"),
  },
});

6203b7f5d4192527a3d05ac8 react native tutorial 2 cropped

C’est ça ! Maintenant, vous pouvez afficher ces paywalls à vos utilisateurs.

Si vous souhaitez tester votre abonnement iOS dans un sandbox, vous devrez créer votre propre compte de testeur sandbox. N’oubliez pas que les abonnements sandbox sont rapidement invalidés pour faciliter les tests. Pour Android, vous n’avez pas besoin de comptes supplémentaires et vous pouvez même effectuer des tests dans un émulateur.

Vérifier si l’utilisateur a des abonnements actifs

Nous devons encore décider où stocker les données d’abonnement actif pour permettre à l’utilisateur final d’accéder à son contenu premium. Adapty nous aidera également dans ce domaine, car il enregistre tous les achats associés à l’utilisateur. Faisons comme suit : si l’utilisateur n’a pas d’abonnement, il sera invité à cliquer sur un bouton “paywall”. S’ils le font, on leur montrera une photo de chat. 

Étant donné que les données de l’abonnement actif sont extraites soit du serveur, soit du stockage en cache, vous aurez besoin d’un chargeur. Par souci de simplicité, ajoutons les états isLoading et isPremium.

// ...
export const App: React.FC = () => {
	const [isLoading, setIsLoading] = useState<boolean>(true);
	const [isPremium, setIsPremium] = useState<boolean>(false);
  const [paywalls, setPaywalls] = useState<AdaptyPaywall[]>([]);

	useEffect(() => {
    async function fetchPaywalls(): Promise<void> {
      try {
        await activateAdapty({
          sdkKey: "MY_PUBLIC_KEY",
        });

        const profile = await adapty.purchases.getInfo();
        const isSubscribed = profile.accessLevels.premium.isActive;
        setIsPremium(isSubscribed);

        if (!isSubscribed) {
          const result = await adapty.paywalls.getPaywalls();
          setPaywalls(result.paywalls);
        }
      } finally {
        setIsLoading(false);
      }
    }

    fetchPaywalls();
  }, []);

  // ...
}
// ...

Voici ce qui a changé : nous avons ajouté deux drapeaux à l’état. L’ensemble du contenu de fetchPaywalls() est maintenant enveloppé dans un bloc try-catch afin que le code atteigne setIsLoading(false) dans tous les scénarios possibles. Pour vérifier si l’utilisateur a un abonnement actif, nous récupérons le profil de l’utilisateur (qui contient toutes ses données d’abonnement) et voyons la valeur de profile.accessLevels.premium.isActive. Vous pouvez utiliser autant de niveaux d’accès (accessLevels) — qui sont essentiellement des niveaux d’abonnement, tels que Gold ou Premium—que vous le souhaitez, mais conservons la valeur par défaut pour le moment. Adapty créera automatiquement le niveau d’accès premium, et pour la plupart des applications, cela sera suffisant. isActive restera vrai tant qu’il y aura un abonnement actif avec ce niveau d’accès.

A partir de là, tout semble assez simple. Si l’utilisateur a le statut d’abonné premium, il n’est pas nécessaire de récupérer les paywalls : il suffit de désactiver le chargeur et d’afficher le contenu. 

export const App: React.FC = () => {
// ...
const renderContent = (): React.ReactNode => {
  if (isLoading) {
    return <Text>Loading...</Text>;
  }

  if (isPremium) {
    return (
      <Image
        source={{
          url: "https://25.media.tumblr.com/tumblr_lugj06ZSgX1r4xjo2o1_500.gif",
          width: Dimensions.get("window").width * 0.8,
          height: Dimensions.get("window").height * 0.8,
        }}
      />
    );
  }

  return (
    <Button
      title="Show paywall"
      onPress={() => {
        const paywall = paywalls.find(
          (paywall) => paywall.developerId === "cats_paywall"
        );

        if (!paywall) {
          return alert("There is no such paywall");
        }

        Navigation.showModal<PaywallProps>({
          component: {
            name: "Paywall",
            passProps: {
	            paywall,
		          onRequestBuy: async (product) => {
		            const purchase = await adapty.purchases.makePurchase(product);
	              const isSubscribed =
		              purchase.purchaserInfo.accessLevels?.premium.isActive;
                setIsPremium(isSubscribed);
                Navigation.dismissAllModals();
              },
            },
          },
        });
      }}
    />
  );
};

return <View style={styles.container}>{renderContent()}</View>;
};

Ici, nous ajoutons une fonction qui rend le contenu ainsi qu’une certaine logique à onRequestBuy : à savoir, la mise à jour de l’état de isPremium et la fermeture de la modale.

Voilà le résultat final :

Le dossier entier :

import React, { useEffect, useState } from "react";
import {
  Button,
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  PlatformColor,
  Image,
  Dimensions,
} from "react-native";
import {
  adapty,
  activateAdapty,
  AdaptyPaywall,
  AdaptyProduct,
} from "react-native-adapty";
import { Navigation } from "react-native-navigation";

export const App: React.FC = () => {
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [isPremium, setIsPremium] = useState<boolean>(false);
  const [paywalls, setPaywalls] = useState<AdaptyPaywall[]>([]);

  useEffect(() => {
    async function fetchPaywalls(): Promise<void> {
      try {
        await activateAdapty({
          sdkKey: "MY_PUBLIC_KEY",
        });

        const profile = await adapty.purchases.getInfo();
        const isSubscribed = profile.accessLevels.premium.isActive;
        setIsPremium(isSubscribed);

        if (!isSubscribed) {
          const result = await adapty.paywalls.getPaywalls();
          setPaywalls(result.paywalls);
        }
      } finally {
        setIsLoading(false);
      }
    }

    fetchPaywalls();
  }, []);

  const renderContent = (): React.ReactNode => {
    if (isLoading) {
      return <Text>Loading...</Text>;
    }

    if (isPremium) {
      return (
        <Image
          source={{
            uri: "https://25.media.tumblr.com/tumblr_lugj06ZSgX1r4xjo2o1_500.gif",
            width: Dimensions.get("window").width * 0.8,
            height: Dimensions.get("window").height * 0.8,
          }}
        />
      );
    }

    return (
      <Button
        title="Show a paywall"
        onPress={() => {
          const paywall = paywalls.find(
            (paywall) => paywall.developerId === "cats_paywall"
          );

          if (!paywall) {
            return alert("There is no such a paywall");
          }

          Navigation.showModal<PaywallProps>({
            component: {
              name: "Paywall",
              passProps: {
                paywall,
                onRequestBuy: async (product) => {
                  const purchase = await adapty.purchases.makePurchase(product);
                  const isSubscribed =
                    purchase.purchaserInfo.accessLevels?.premium.isActive;
                  setIsPremium(isSubscribed);
                  Navigation.dismissAllModals();
                },
              },
            },
          });
        }}
      />
    );
  };

  return <View style={styles.container}>{renderContent()}</View>;
};
interface PaywallProps {
  paywall: AdaptyPaywall;
  onRequestBuy: (product: AdaptyProduct) => void | Promise<void>;
}
export const Paywall: React.FC<PaywallProps> = ({ paywall, onRequestBuy }) => {
  const [isLoading, setIsLoading] = useState<boolean>(false);

  return (
    <SafeAreaView style={styles.paywallContainer}>
      {paywall.products.map((product) => (
        <View key={product.vendorProductId}>
          <Text>{product.localizedTitle}</Text>
          <Button
            title={`Buy for ${product.localizedPrice}`}
            disabled={isLoading}
            onPress={async () => {
              try {
                setIsLoading(true);
                await onRequestBuy(product);
              } catch (error) {
                alert("An error occured :(");
              } finally {
                setIsLoading(false);
              }
            }}
          />
        </View>
      ))}
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, alignItems: "center", justifyContent: "center" },
  paywallContainer: {
    flex: 1,
    alignItems: "center",
    justifyContent: "space-evenly",
    backgroundColor: PlatformColor("secondarySystemBackground"),
  },
});

En résumé

Nous avons fini par créer une application d’abonnement très jolie et extrêmement utile. Ceux qui paient verront des chats, et tous les autres auront des paywalls à la place. Ce guide devrait vous avoir appris tout ce dont vous pourriez avoir besoin pour mettre en place des achats intégrés dans votre application. Et pour ceux qui ont hâte d’approfondir les kits de magasin, restez à l’écoute pour en savoir plus. Merci !