First of all you should create a RevenueCat account for free (e.g. using your Github account) and then create a new project in your account, under which you can add your iOS and Android apps and their respective bundle IDs.
To add IAP to your app you of course need a native app, so make sure you have set up an according app ID for iOS and Android before you continue.
You will also need to to work on your app in App Store Connect for iOS as well as the Google Developer Console for Android where you can add In-App purchases and subscriptions, so make sure you got all the credentials for those accounts.
We are not covering the full process of adding offerings and items to RevenueCat, but you can find a great guide in the RevenueCat getting started guide.
The steps are:
Create the products in App Store Connect and the Google Developer Console
Add the products to RevenueCat
Create entitlements in RevenueCat and add the products to them
Define offerings in RevenueCat and add the products to them
We need to load our products and offerings from RevenueCat when the React Native app starts, so we have a RevenueCatProvider that will do this for us.
We also want to be able to purchase items and restore purchases, so we have these methods as well to the provider as well.
To make sure all of these functions are available to our React app, we create a RevenueCatContext and export it as a useRevenueCat hook to be used in our app.
At this point you also need your RevenueCat API keys, which you can find in your RevenueCat project under API Keys. You will need to add them to the .env file.
import{ createContext, useContext, useEffect, useState }from'react';import{Platform}from'react-native';importPurchases,{LOG_LEVEL,PurchasesPackage}from'react-native-purchases';import{CustomerInfo}from'react-native-purchases';// Use your RevenueCat API keysconstAPIKeys={ apple: process.env.REVENUE_CAT_APPLE_KEY, google: process.env.REVENUE_CAT_GOOGLE_KEY};interfaceRevenueCatProps{ purchasePackage?:(pack:PurchasesPackage)=>Promise<void>; restorePermissions?:()=>Promise<CustomerInfo>; user:UserState; packages:PurchasesPackage[];}exportinterfaceUserState{ pro:boolean; subscriptionType:'monthly'|'annual'|null;// Added to track subscription type}constRevenueCatContext=createContext<RevenueCatProps |null>(null);// Export context for easy usageexportconstuseRevenueCat=()=>{returnuseContext(RevenueCatContext)asRevenueCatProps;};// Provide RevenueCat functions to our appexportconstRevenueCatProvider=({ children }:any)=>{const[user, setUser]=useState<UserState>({ pro:false, subscriptionType:null});const[packages, setPackages]=useState<PurchasesPackage[]>([]);const[isReady, setIsReady]=useState(false);useEffect(()=>{constinit=async()=>{if(Platform.OS==='android'){awaitPurchases.configure({ apiKey:APIKeys.google||'undefined'});}else{awaitPurchases.configure({ apiKey:APIKeys.apple||'undefined'});}setIsReady(true);// Use more logging during debug if want!Purchases.setLogLevel(LOG_LEVEL.DEBUG);// Listen for customer updatesPurchases.addCustomerInfoUpdateListener(async(info)=>{updateCustomerInformation(info);});// Load all offerings and the user object with entitlementsawaitloadOfferings();};init();},[]);// Load all offerings a user can (currently) purchaseconstloadOfferings=async()=>{const offerings =awaitPurchases.getOfferings();if(offerings.current){setPackages(offerings.current.availablePackages);}};// Update user state based on previous purchasesconstupdateCustomerInformation=async(customerInfo:CustomerInfo)=>{const newUser:UserState={...user, pro:false, subscriptionType:null};if(customerInfo?.entitlements.active['PRO_MONTHLY']){ newUser.pro=true; newUser.subscriptionType='monthly';}if(customerInfo?.entitlements.active['PRO_ANNUAL']){ newUser.pro=true; newUser.subscriptionType='annual';}setUser(newUser);};// Purchase a packageconstpurchasePackage=async(pack:PurchasesPackage)=>{try{const purchase =awaitPurchases.purchasePackage(pack);if(purchase.customerInfo.entitlements.active['PRO_MONTHLY']){setUser({...user, pro:true, subscriptionType:'monthly'});}elseif(purchase.customerInfo.entitlements.active['PRO_ANNUAL']){setUser({...user, pro:true, subscriptionType:'annual'});}}catch(e:any){if(!e.userCancelled){alert(e);}}};// Restore previous purchasesconstrestorePermissions=async()=>{const customer =awaitPurchases.restorePurchases();return customer;};const value ={ restorePermissions, user, packages, purchasePackage};// Return empty fragment if provider is not ready (Purchase not yet initialised)if(!isReady)return<></>;return<RevenueCatContext.Providervalue={value}>{children}</RevenueCatContext.Provider>;};
As you can see in the code we have 2 entitlements PRO_MONTHLY and PRO_ANNUAL set up. You can add more entitlements in RevenueCat and then add them to the updateCustomerInformation function.
Also note that we have 2 subscription types monthly and annual in the UserState object.
If you have done everything correctly, you should now be able to see your subscription options in the Paywall screen and purchase them.
To access the user subscription state in your app, you can use the useRevenueCat hook like this:
example.tsx
import{View,Text}from'react-native';importReactfrom'react';import{UserState}from'@/providers/RevenueCatProvider';interfaceUserProps{ user:UserState;}constPage=({ user }:UserProps)=>{return(<View>{user.pro?(<Text>You are a Pro user with a {user.subscriptionType} subscription</Text>):(<Text>You are not a Pro user</Text>)}</View>);};exportdefaultPage;
This is an example of how you can use the useRevenueCat hook to access the user subscription state in your app.