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';import Purchases, { LOG_LEVEL, PurchasesPackage } from 'react-native-purchases';import { CustomerInfo } from 'react-native-purchases';// Use your RevenueCat API keysconst APIKeys = { apple: process.env.REVENUE_CAT_APPLE_KEY, google: process.env.REVENUE_CAT_GOOGLE_KEY};interface RevenueCatProps { purchasePackage?: (pack: PurchasesPackage) => Promise<void>; restorePermissions?: () => Promise<CustomerInfo>; user: UserState; packages: PurchasesPackage[];}export interface UserState { pro: boolean; subscriptionType: 'monthly' | 'annual' | null; // Added to track subscription type}const RevenueCatContext = createContext<RevenueCatProps | null>(null);// Export context for easy usageexport const useRevenueCat = () => { return useContext(RevenueCatContext) as RevenueCatProps;};// Provide RevenueCat functions to our appexport const RevenueCatProvider = ({ children }: any) => { const [user, setUser] = useState<UserState>({ pro: false, subscriptionType: null }); const [packages, setPackages] = useState<PurchasesPackage[]>([]); const [isReady, setIsReady] = useState(false); useEffect(() => { const init = async () => { if (Platform.OS === 'android') { await Purchases.configure({ apiKey: APIKeys.google || 'undefined' }); } else { await Purchases.configure({ apiKey: APIKeys.apple || 'undefined' }); } setIsReady(true); // Use more logging during debug if want! Purchases.setLogLevel(LOG_LEVEL.DEBUG); // Listen for customer updates Purchases.addCustomerInfoUpdateListener(async (info) => { updateCustomerInformation(info); }); // Load all offerings and the user object with entitlements await loadOfferings(); }; init(); }, []); // Load all offerings a user can (currently) purchase const loadOfferings = async () => { const offerings = await Purchases.getOfferings(); if (offerings.current) { setPackages(offerings.current.availablePackages); } }; // Update user state based on previous purchases const updateCustomerInformation = 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 package const purchasePackage = async (pack: PurchasesPackage) => { try { const purchase = await Purchases.purchasePackage(pack); if (purchase.customerInfo.entitlements.active['PRO_MONTHLY']) { setUser({ ...user, pro: true, subscriptionType: 'monthly' }); } else if (purchase.customerInfo.entitlements.active['PRO_ANNUAL']) { setUser({ ...user, pro: true, subscriptionType: 'annual' }); } } catch (e: any) { if (!e.userCancelled) { alert(e); } } }; // Restore previous purchases const restorePermissions = async () => { const customer = await Purchases.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.Provider value={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
Copy
import { View, Text } from 'react-native';import React from 'react';import { UserState } from '@/providers/RevenueCatProvider'; interface UserProps { user: UserState;}const Page = ({ 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> );};export default Page;
This is an example of how you can use the useRevenueCat hook to access the user subscription state in your app.