Welcome to Ray's Blog

Stay Hungry Stay Foolish - Steve Jobs

0%

Integrating RevenueCat IAP in React Native + Expo Application (Simple IOS Demo)

React Native In-App Purchases with RevenueCat

This guide outlines how to integrate and manage in-app subscriptions in a React Native application using RevenueCat’s react-native-purchases SDK, with real-world code, usage, and debugging insights.

Table of Contents

Installation

1
2
npm install --save react-native-purchases
npx pod-install # (for iOS)

Enable In-App Purchase capability in Xcode and add billing permission in AndroidManifest.xml:

1
<uses-permission android:name="com.android.vending.BILLING" />

SDK Configuration

1
2
3
4
5
6
import Purchases from 'react-native-purchases';

Purchases.configure({
apiKey: 'your_revenuecat_api_key',
appUserID: null,
});

Fetching Offerings

1
2
3
4
5
const fetchIapList = async () => {
const offerings = await Purchases.getOfferings();
const currentOffering = offerings.current;
return currentOffering?.availablePackages || [];
};

Making Purchases

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const startAppleIap = async (selectedPackage) => {
const { customerInfo } = await Purchases.purchasePackage(selectedPackage);
const isActive = isEntitlementActive(customerInfo);

if (isActive) {
Alert.alert('Purchase Success');
} else {
Alert.alert('Purchase Failed');
}
};

const isEntitlementActive = (customerInfo) => {
return !!customerInfo.entitlements.active['your_entitlement_identifier'];
};

Restoring Purchases

1
2
3
4
5
const onRestore = async () => {
const customerInfo = await Purchases.restorePurchases();
const isActive = isEntitlementActive(customerInfo);
Alert.alert(isActive ? 'Restore Success' : 'Restore Failed');
};

Checking Subscription Status

1
2
3
4
const checkSubscription = async () => {
const customerInfo = await Purchases.getCustomerInfo();
return !!customerInfo.entitlements.active['your_entitlement_identifier'];
};

Listening for Subscription Updates

1
2
3
4
5
6
7
8
9
useEffect(() => {
const listener = (info) => {
const isSubscribed =
!!info.entitlements.active['your_entitlement_identifier'];
setIsSubscribed(isSubscribed);
};
Purchases.addCustomerInfoUpdateListener(listener);
return () => Purchases.removeCustomerInfoUpdateListener(listener);
}, []);

Real Trouble Example: Subscription Flag Always True

Symptom

After calling restorePurchases(), customerInfo.entitlements.active['your_entitlement_identifier'] always returns a non-undefined value — even when the user didn’t subscribe.

Cause

In real-world scenarios (especially on iOS), when a user cancels a subscription, they still retain access to the content until the end of the current billing period. This means RevenueCat will still report the entitlement as active until the subscription period officially ends. As a result, during a restore operation, the isSubscribed flag may appear true even though the user has already canceled the subscription. This is expected behavior in accordance with App Store billing policies.

1
2
3
const isActive = config.entitlements.some(
(key) => !!customerInfo.entitlements.active[key]
);

Also log clearly to debug:

1
console.log('Active entitlements:', customerInfo.entitlements.active);

Best Practices

  • Always refresh with getCustomerInfo() after a restore
  • Use listeners to stay updated on real-time changes
  • Clean up listeners in useEffect
  • Use real devices for all IAP tests

References