Claude
Skills
Sign in
Back

integrating-revenuecat

Included with Lifetime
$97 forever

RevenueCat SDK setup, offerings, entitlements, and purchase flows for React Native Expo. Use when implementing in-app purchases, subscriptions, or paywalls.

Web Dev

What this skill does


# RevenueCat Integration Reference

## Plugin Tools

Extract product IDs and entitlements from your codebase:

```bash
# Scan codebase for RevenueCat identifiers
cd /path/to/expo-toolkit && npm run extract-product-ids

# Or specify directory
node tools/extract-product-ids.js /path/to/your/app
node tools/extract-product-ids.js /path/to/your/app --json
```

This extracts:
- Product IDs (e.g., `com.yourapp.premium.monthly`)
- Entitlement names (e.g., `premium`)
- Offering identifiers
- Package types (`$rc_monthly`, `$rc_annual`, etc.)

Use the output to cross-reference with RevenueCat Dashboard.

## RevenueCat Concepts

### Hierarchy

```
RevenueCat Account
└── Project (your app)
    └── Apps (iOS, Android, etc.)
        └── Products (from App Store Connect / Play Console)
            └── Offerings (groups of products)
                └── Packages (individual purchasable items)
                    └── Entitlements (what the user gets)
```

### Key Terms

| Term | Description |
|------|-------------|
| **Product** | An item in App Store Connect or Play Console |
| **Entitlement** | A feature/access level users can unlock |
| **Offering** | A group of packages to present to users |
| **Package** | A specific purchasable item with a product |
| **Customer** | A user identified by app user ID |

## SDK Installation

### For Expo (Managed Workflow)

```bash
npx expo install react-native-purchases
```

### For Expo (Prebuild/Bare)

```bash
npm install react-native-purchases
npx expo prebuild
```

### Plugin Configuration

Add to `app.config.js` or `app.json`:

```javascript
{
  "expo": {
    "plugins": [
      [
        "react-native-purchases",
        {
          "REVENUECAT_API_KEY": "appl_xxxxxxxx", // iOS key
          // Or for Android:
          // "REVENUECAT_API_KEY": "goog_xxxxxxxx"
        }
      ]
    ]
  }
}
```

**Multi-platform setup:**
```javascript
plugins: [
  [
    "react-native-purchases",
    {
      "REVENUECAT_API_KEY_IOS": "appl_xxxxxxxx",
      "REVENUECAT_API_KEY_ANDROID": "goog_xxxxxxxx"
    }
  ]
]
```

## SDK Initialisation

### Basic Setup

```typescript
import Purchases, { LOG_LEVEL } from 'react-native-purchases';
import { Platform } from 'react-native';

const API_KEY = Platform.select({
  ios: 'appl_xxxxxxxx',
  android: 'goog_xxxxxxxx',
});

export async function initPurchases() {
  if (__DEV__) {
    Purchases.setLogLevel(LOG_LEVEL.VERBOSE);
  }

  await Purchases.configure({ apiKey: API_KEY });
}
```

### With User Identification

```typescript
export async function initPurchases(userId?: string) {
  await Purchases.configure({ apiKey: API_KEY });

  if (userId) {
    await Purchases.logIn(userId);
  }
}
```

### App Initialisation

```typescript
// In App.tsx or root component
useEffect(() => {
  initPurchases();
}, []);
```

## Entitlements

### Setting Up Entitlements

In RevenueCat Dashboard:
1. Go to Project Settings → Entitlements
2. Create entitlement (e.g., "premium", "pro")
3. This represents what the user gets access to

### Checking Entitlements

```typescript
import Purchases from 'react-native-purchases';

export async function checkPremiumAccess(): Promise<boolean> {
  try {
    const customerInfo = await Purchases.getCustomerInfo();
    return customerInfo.entitlements.active['premium'] !== undefined;
  } catch (error) {
    console.error('Error checking entitlements:', error);
    return false;
  }
}
```

### Using a Hook

```typescript
import { useEffect, useState } from 'react';
import Purchases, { CustomerInfo } from 'react-native-purchases';

export function usePremiumStatus() {
  const [isPremium, setIsPremium] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const checkStatus = async () => {
      try {
        const customerInfo = await Purchases.getCustomerInfo();
        setIsPremium(customerInfo.entitlements.active['premium'] !== undefined);
      } catch (error) {
        console.error(error);
      } finally {
        setLoading(false);
      }
    };

    checkStatus();

    // Listen for changes
    const listener = Purchases.addCustomerInfoUpdateListener((info: CustomerInfo) => {
      setIsPremium(info.entitlements.active['premium'] !== undefined);
    });

    return () => listener.remove();
  }, []);

  return { isPremium, loading };
}
```

## Offerings and Products

### Fetching Offerings

```typescript
import Purchases, { PurchasesOffering } from 'react-native-purchases';

export async function getOfferings(): Promise<PurchasesOffering | null> {
  try {
    const offerings = await Purchases.getOfferings();
    return offerings.current;
  } catch (error) {
    console.error('Error fetching offerings:', error);
    return null;
  }
}
```

### Displaying Products

```typescript
const offering = await getOfferings();

if (offering) {
  offering.availablePackages.forEach(pkg => {
    console.log('Package:', pkg.identifier);
    console.log('Product:', pkg.product.title);
    console.log('Price:', pkg.product.priceString);
    console.log('Description:', pkg.product.description);
  });
}
```

### Package Types

| Type | Description |
|------|-------------|
| `$rc_monthly` | Monthly subscription |
| `$rc_annual` | Annual subscription |
| `$rc_weekly` | Weekly subscription |
| `$rc_lifetime` | Lifetime (one-time) purchase |
| Custom | Your own identifier |

## Making Purchases

### Purchase Flow

```typescript
import Purchases, { PurchasesPackage } from 'react-native-purchases';

export async function purchasePackage(pkg: PurchasesPackage): Promise<boolean> {
  try {
    const { customerInfo } = await Purchases.purchasePackage(pkg);

    if (customerInfo.entitlements.active['premium']) {
      return true;
    }
    return false;
  } catch (error: any) {
    if (error.userCancelled) {
      // User cancelled, not an error
      return false;
    }
    throw error;
  }
}
```

### Complete Paywall Component

```typescript
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, ActivityIndicator, StyleSheet } from 'react-native';
import Purchases, { PurchasesOffering, PurchasesPackage } from 'react-native-purchases';

export function Paywall({ onPurchase }: { onPurchase: () => void }) {
  const [offering, setOffering] = useState<PurchasesOffering | null>(null);
  const [loading, setLoading] = useState(true);
  const [purchasing, setPurchasing] = useState(false);

  useEffect(() => {
    const fetchOfferings = async () => {
      try {
        const offerings = await Purchases.getOfferings();
        setOffering(offerings.current);
      } catch (error) {
        console.error(error);
      } finally {
        setLoading(false);
      }
    };
    fetchOfferings();
  }, []);

  const handlePurchase = async (pkg: PurchasesPackage) => {
    setPurchasing(true);
    try {
      const { customerInfo } = await Purchases.purchasePackage(pkg);
      if (customerInfo.entitlements.active['premium']) {
        onPurchase();
      }
    } catch (error: any) {
      if (!error.userCancelled) {
        console.error('Purchase error:', error);
      }
    } finally {
      setPurchasing(false);
    }
  };

  const handleRestore = async () => {
    setPurchasing(true);
    try {
      const customerInfo = await Purchases.restorePurchases();
      if (customerInfo.entitlements.active['premium']) {
        onPurchase();
      }
    } catch (error) {
      console.error('Restore error:', error);
    } finally {
      setPurchasing(false);
    }
  };

  if (loading) {
    return <ActivityIndicator size="large" />;
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Upgrade to Premium</Text>

      {offering?.availablePackages.map(pkg => (
        <TouchableOpacity
          key={pkg.identifier}
          style={styles.package}
          onPress={() => handlePurchase(pkg)}
          disabled={purchasing}
        >
          <Text style={styles.packageTitle}>{pkg.product.title}</Text>
          <Text style=

Related in Web Dev