Report: State of in-app subscriptions in the US 2023 Get a report

React native In-App-Käufe: Einfache Implementierung. Tutorial

Ivan Dorofeev

Updated: Mai 23, 2023

23 min read

Content

62fdf143e0d4c00ce1483eba jp android tutorial 1 configuration 10

Verschiedene plattformübergreifende App-Entwicklungs-Frameworks erleichtern Entwicklern das Leben, da sie Apps für mehrere Plattformen gleichzeitig erstellen können. Leider haben sie jedoch auch einige Nachteile. So hat React Native kein Tool zur Implementierung von In-App-Käufen (in-app purchases). Dies bedeutet also, dass Sie zwangsläufig Bibliotheken von Drittanbietern verwenden müssen. 

Wie können In-App-Käufe noch implementiert werden?

Zu den beliebten Bibliotheken für In-App-Abonnements in React Native-Apps zählen react-native-iap und expo-in-app-purchases. An dieser Stelle möchte ich aber über React-Native-Adapty sprechen, da es im Vergleich zu den anderen Bibliotheken einige Vorteile bietet:

  • Es bietet eine serverbasierte Kaufvalidierung.
  • Es unterstützt alle kürzlich von den App Stores implementierten Funktionen, von Werbeangeboten bis hin zu Vorauszahlungen.
  • Der Code ist klarer und einfacher.
  • Sie können ganze ohne das Durchlaufen des ganzen Veröffentlichungskreislaufs Ihr Produktangebot ändern und neue Angebote hinzufügen. Sie müssen keine Betaversionen veröffentlichen und auf Genehmigungen warten.

Adapty SDK bietet Ihnen aber noch viel mehr. Sie erhalten verschiedene Analysetools für alle wichtigen Kennzahlen, Kohortenanalysen, eine serverbasierte Kaufvalidierung, AB-Tests für Paywalls, Werbekampagnen mit flexibler Segmentierung, integrierte Analysetools von Drittanbietern und mehr.

Worauf ich zu sprechen kommen werde

Zuerst möchte ich über das Einrichten von In-App-Käufen in React Native-Apps sprechen. Dazu gehört Folgendes:

  1. Warum Expo nicht für In-App-Käufe in React Native-Apps funktioniert.
  2. Die Erstellung eines Entwicklerkontos
  3. Die Konfiguration von Adapty:
    Konfiguration im App Store
    Konfiguration im Play Store
  4. Das Hinzufügen von Abonnements.
  5. Die Erstellung einer Paywall.
  6. Die Installation von react-native-adapty.
  7. Eine Sample App und das Ergebnis.

In diesem Tutorial versuchen wir, eine App zu erstellen, in der Abonnenten Katzenbilder zu sehen bekomme. Alle anderen Nutzer sollen aufgefordert werden, ein Abonnement abzuschließen.

Warum Expo nicht für In-App-Käufe in React Native-Apps funktioniert

In wenigen Worten: Expo unterstützt nicht die nativen Methoden, die der App Store für die Kaufabwicklung anbietet (auch bekannt als Store-Kits). Sie müssen sich entweder an RN halten oder den Expo-Bare-Workflow verwenden.

Ich muss Sie gleich zu Beginn leider enttäuschen, falls auch Sie Expo verwenden wollten. Denn das wird nicht funktionieren. Natürlich ist Expo ein React Native-Framework, das die App-Entwicklung erheblich vereinfacht. Ihr verwalteter Workflow ist jedoch nicht mit der Kauf-/Abonnementverarbeitung kompatibel. Expo verwendet keinen nativen Code in seinen Methoden und Komponenten (beide sind JavaScript), was für Shop-Kits jedoch erforderlich ist. Es gibt keine Möglichkeit, In-App-Käufe in Stores mit JavaScript zu implementieren. Sie müssen diese Idee daher verwerfen und stattdessen einen “Eject“ wagen.

Die Erstellung eines Entwicklerkontos

Erst einmal müssen Sie App Store-Konten einrichten sowie Käufe und Abonnements für iOS und Android erstellen und konfigurieren. Das sollte kaum länger als 20 Minuten dauern.

Falls Sie Ihr Entwicklerkonto und Ihre Produkte noch nicht in App Store Connect und/oder in der Google Play Console konfiguriert haben, folgen Sie bitte diesen Schritten:

  • Für iOS: Lesen Sie das Tutorial von Anfang an bis zur Überschrift „Die Liste des SKProduct“, wo wir über die nativen Implementierungen sprechen.
  • Für Android: Lesen Sie das Tutorial von Anfang an bis zur Überschrift „Erstellen einer Produktliste in einer App“.

Die Konfiguration von Adapty

Für React-native-adapty müssen Sie zunächst Ihr Adapty-Dashboard konfigurieren. Dies nimmt nicht viel Zeit in Anspruch. Gleichzeitig genießen Sie alle oben genannten Vorteile von Adapty im Vergleich zur harten Codierung.

Im dritten Schritt werden Sie aufgefordert, die Konfigurationen für den App Store und Google Play vorzunehmen.

61dd2e3df0eb779b1966c336 wwqbyclqbg22r4uxyue6f wty 8kx1lgeepf9 ctrv5mcvrihoe1upbhw 0jth wqwkhjc4az

Für iOS müssen Sie:

  • Die Bundle ID angeben;
  • App Store Serverbenachrichtigungen einrichten;
  • Die gemeinsame Geheimnis für App Store Connect angeben.

All diese Felder sind erforderlich, damit die Käufe funktionieren. 

Jedes dieser Felder bietet weitere Informationen mit Schritt-für-Schritt-Anleitungen. Nutzen Sie diese, wenn Sie Fragen haben.

Die Bundle-ID ist die eindeutige ID Ihrer App. Sie muss mit der ID übereinstimmen, die Sie in Xcode angegeben haben: Targets > [App Name] > General:

61dd2e3d0bcefe3f92ed87c6 ofteg 29 hasv9sywd5gpm4vxtbwppkdlkqkatwvl3jprjkq3ffp3xzdcuett5qf uzavsew

Im Falle von Android sind die erforderlichen Felder der Package Name und die Service Account Key File. Auch diese Felder haben ihre eigenen Hinweise. Der Package Name macht in Android das, was die Bundle-ID in iOS macht. Er muss mit dem Namen übereinstimmen, den Sie in Ihrem Code angegeben haben. Prüfen Sie dies in der /android/app/build.gradle Datei in android.defaultConfig.applicationId:

61dd2e3dad4d57621f800483 qbh6fhwgcmjiqsriilrm2ubc2xjswr8buchwntern5xn8fkxqxgep4hbrftvqyfu3rgx4dg x3c6iyvemvxarmvemiu

Im vierten Schritt werden Sie dazu aufgefordert, Adapty SDK mit Ihrer App zu verbinden. Überspringen Sie diesen Schritt jedoch vorerst – wir werden etwas später darauf zurückkommen.

Nachdem Sie sich angemeldet haben, sehen Sie sich die Registerkarte „Einstellungen“ an. Beachten Sie bitte, dass hier Ihr öffentlicher SDK-Key zu finden ist. Diesen werden Sie später benötigen.

61dd2e3d3fce0994fd3f0c38 0 z1xu4tx6yoskp5gysva4jz ksmpt59lwmlpt8mol5fmyuqn1gygyvmqf52rgz5drhlvtp2gekrd0pwygz8pydmejdeskwggxp5 f6kevd ftdziotbnzpcv6bsqgojjk9unzt

Hinzufügen eines Abonnements

Adapty verwendet für die verschiedenen Abonnements Produkte. Ihr Abonnement für Katzenbilder kann wöchentlich, halbjährlich oder jährlich verlängert werden. Jede dieser Optionen ist ein separates Adapty-Produkt.

Im Dashboard geben Sie an, dass Sie ein Produkt haben. Gehen Sie dazu zu Produkte & A/B-Tests → Produkte und klicken Sie auf Produkt erstellen

Nun müssen Sie den Produktnamen angeben. Dies bestimmt, wie das Abonnement in Ihrem Adapty-Dashboard aussehen wird.

Zusätzlich müssen Sie die App Store Produkt-ID und die Play Store Produkt-ID angeben. Sie können auch den Zeitraum und den Namen für die Analyse angeben. Klicken Sie auf Speichern.

61dd2e3d3abe166794791c09 fukrvgjrhor1rtmrrmvq4mar f3rdr2nqpbojb44aa5ipwcq 9bbq7ajpq085sroobeltpnw9rtdfah a0pqoqthyqbj8ayaahikjioylj1gszmlfh mhwlqowynrpaabjjkvoln

Die Erstellung einer Paywall

Nun müssen Sie eine Paywall entwerfen. Dabei handelt es sich um einen Screen, der den Zugriff des Nutzers auf Premium-Funktionen einschränkt und ihn dazu auffordert, ein Abonnement abzuschließen. Sie müssen das von Ihnen erstellte Produkt zu Ihrer Paywall hinzufügen. Klicken Sie dazu im selben Abschnitt auf Paywall erstellen (Produkte & A/B-Tests → Paywalls).

  • Wählen Sie einen Paywall-Namen aus, bei dem Sie und Ihr Team direkt erkennen, um welche Paywall es sich handelt.
  • Verwenden Sie die Paywall-ID, um sie in Ihrer App anzuzeigen. Für unsere Beispiel-App verwenden wir „cats_paywall“.
  • Wählen Sie in der Dropdown-Liste Produkt Ihr Abonnement aus.

Klicken Sie auf Speichern und Veröffentlichen.

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

Die Konfiguration ist nun abgeschlossen. Jetzt fügen Sie die Abhängigkeiten hinzu und schreiben den Code.

Installation von react-native-adapty

1. Fügen Sie als erstes die Abhängigkeit hinzu:

yarn add react-native-adapty

2. Installieren Sie iOS-Pods. Falls Sie noch nicht den CLI-Pod haben, empfehle ich Ihnen wärmstens, ihn herunterzuladen. Sie werden ihn in der iOS-Entwicklung sicherlich häufig brauchen.

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

3. Weil iOS React Native-Projekte in Obj-C geschrieben sind, müssen Sie einen Swift Bridging Header erstellen, damit dieser Obj-C Swift-Bibliotheken lesen kann. Öffnen Sie dazu einfach Ihr Xcode-Projekt und erstellen Sie eine neue Swift-Datei. Xcode fragt Sie, ob Sie einen Bridging-Header erstellen möchten. Genau das möchten Sie tun. Klicken Sie auf Erstellen.

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

4. Im Falle von Android stellen Sie bitte sicher, dass das Projekt (standardmäßig /android/build.gradle) das kotlin-gradle-plugin der Version 1.4.0 oder höher verwendet:

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

5. Für Android müssen Sie MultiDex aktivieren, das in der Konfigurationsdatei der App zu finden ist (standardmäßig /android/app/build.gradle).

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

Nun können Sie sich schon Ihrem Code widmen!

Abrufen der Produktliste in der App

react-native-adapty bietet Ihnen unzählige nützliche Dinge. Sie werden diese sicherlich früher oder später benötigen, weshalb Sie die Bibliothek ganz am Anfang Ihres Flows initialisieren sollten. Gehen Sie im Code Ihrer App so hoch wie möglich (Sie können dies auch direkt in der App.tsx tun) und starten Sie die Initialisierung:

// 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' });
  },[]);
  ...
}

An dieser Stelle ersetzen Sie MY_PUBLIC_KEY durch Ihren öffentlichen SDK-Key, den Sie in den Dashboard-Einstellungen finden. Tatsächlich kann die activateAdapty()-Methode mehr als einmal und an mehr als einer Stelle aufgerufen werden. Wir bleiben aber bei diesem Design.

Nun können Sie die Produkte abrufen, die Sie im Adapty-Dashboard hinzugefügt haben:

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

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

	return products;
}

Jetzt wird es interessant: Wir werden versuchen, eine kleine App zu bauen, mit der wir die Produkte unserer Paywalls durchsuchen und Einkäufe tätigen können.

Beispiel-App

Ich möchte mich von hier an kurz fassen, um die Basislogik nicht zu kompliziert zu machen. Ich werde in TypeScript programmieren, um Ihnen zu zeigen, welche Typen wo verwendet werden. Zum Testen verwende ich mein gutes altes iPhone 8. Denken Sie daran, dass der App Store ab iOS 14 die Verwendung von Store-Kits in Emulatoren verbietet. Sie können nur mit physischen Geräten testen.

App.tsx Root Component

1. Lassen Sie uns als erstes eine App.tsx Root Component erstellen, die einen Paywall-Button haben wird. Wir haben die Navigation bereits über React-Native-Navigation konfiguriert. Dies halte ich für viel besser als die React-Navigation-Option, die in den offiziellen Dokumenten empfohlen wird.

PROBLEM

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

Was passiert hier? Beim Mounten wird die Funktion fetchPaywalls() aufgerufen. Es aktiviert das SDK und speichert die Paywalls in dem Zustand, sodass der Nutzer nach dem Tippen auf den Button nicht auf das Abrufen warten muss. Es gibt nur einen Button in der Ansicht, die den Nutzer zur Paywall führen soll, die wir zuvor im Dashboard entworfen haben.

Es ist sogar möglich, die Paywalls direkt hier abzurufen, ohne sie zu speichern. Standardmäßig holt adapty.paywalls.getPaywalls() sie aus dem Cache (nachdem sie beim Start abgefangen wurden). Das bedeutet, dass Sie nicht warten müssen, bis die Methode mit dem Server kommuniziert.

Hier ist das Ergebnis:

react native in-app purchases tutorial payall

Eine paywall component

2. Schreiben Sie in derselben Datei eine Paywall Component.

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

Hier ordnen wir einfach die Produkte der Paywall zu und zeigen neben jedem Produkt einen Kaufen-Button an.

Registrierung des Screens

3. Registrieren Sie jetzt diesen Screen in React-Native-Navigation. Wenn Sie eine andere Navigation verwenden, überspringen Sie diesen Schritt. Meine root index.js-Datei sieht so aus:

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" } }] } },
  });
});

„Paywall anzeigen” Button

4. Nun müssen wir nur noch den Button „Paywall anzeigen“ eine Aktion zuweisen. In unserem Fall wird ein Modal über die Navigation aufgerufen.

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

Die gesamte App.tsx Datei:

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

Das ist alles! Jetzt können Sie die Paywalls Ihren Nutzern anzeigen.

Falls Sie Ihr iOS-Abonnement in einer Sandbox testen möchten, müssen Sie Ihr eigenes Sandbox-Testkonto erstellen. Beachten Sie, dass Sandbox-Abonnements schnell ungültig werden, um das Testen zu vereinfachen. Für Android benötigen Sie keine zusätzlichen Konten. Sie können sogar Tests in einem Emulator durchführen.

Subscribe to Adapty newsletter

Get fresh paywall ideas, subscription insights, and mobile app news every month!

Überprüfung, ob der Benutzer aktive Abonnements hat

Nun müssen Sie noch entscheiden, wo aktive die Abonnement-Daten gespeichert werden sollen, um dem Endbenutzer Zugriff auf seine Premium-Inhalte zu gewähren. Adapty hilft uns dabei, da es alle mit dem Nutzer assoziierten Käufe speichert. So geht’s: Wenn der Nutzer kein Abonnement hat, wird er mit einem Paywall-Button aufgefordert, ein Abonnement abzuschließen. Verfügt er über ein Abonnement, zeigen wir ihm ein Katzenbild. 

Weil die aktiven Abonnement-Daten entweder vom Server oder vom Cache abgerufen werden, benötigen Sie einen Loader. Der Einfachheit halber fügen Sie isLoading und isPremium hinzu.

// ...
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();
  }, []);

  // ...
}
// ...

Folgendes hat sich geändert: Wir haben dem Status zwei Flags hinzugefügt. Der gesamte Inhalt von fetchPaywalls() ist jetzt in einen try-catch Block eingeschlossen, sodass der Code in jedem möglichen Szenario setIsLoading(false) erhält. Um zu überprüfen, ob der Nutzer ein aktives Abonnement hat, rufen wir das Profil des Benutzers ab (das alle seine Abonnement-Daten enthält) und sehen den Wert profile.accessLevels.premium.isActive. Sie können so viele Zugriffsebenen (accessLevels) verwenden, wie Sie möchten. Sie sind im Grunde nur Abonnement-Stufen wie Gold oder Premium. Lassen Sie uns vorerst den Standardwert beibehalten. Adapty erstellt automatisch die Premium-Zugriffsebene. Für die meisten Apps wird dies ausreichen. isActive bleibt wahr, solange es ein aktives Abonnement mit dieser Zugriffsebene gibt.

Der Rest ist relativ einfach. Wenn der Nutzer den Premium-Abonnementstatus hat, müssen die Paywalls nicht abgerufen werden. Deaktivieren Sie einfach den Loader und zeigen Sie den Inhalt an. 

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

Fügen Sie noch eine Funktion hinzu, die den Inhalt sowie etwas Logik zu onRequestBuy rendert: nämlich das Aktualisieren des Status von isPremium und das Schließen des Modals.

Und schließlich das Endergebnis:

Die gesamte Datei:

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

Fazit

Sie haben jetzt eine gut aussehende und äußerst nützliche Abonnement-App entwickelt. Die Abonnenten sehen Katzen und alle anderen erhalten stattdessen Paywalls. Dank dieses Tutorials sollten Sie alles gelernt haben, um in Zukunft In-App-Käufe in Ihrer App zu implementieren. Wenn Sie mehr über Shop-Kits erfahren möchten, dürfen Sie sich auf weitere Tutorials freuen. Vielen Dank!