ScaleRocket/Mobile

RevenueCat Setup

Set up RevenueCat for in-app purchases and subscriptions on iOS and Android — from account creation to sandbox testing.

RevenueCat Setup

RevenueCat handles in-app purchases and subscriptions for both iOS and Android. It wraps Apple's StoreKit and Google Play Billing into a single SDK so you don't have to deal with receipt validation, subscription status tracking, or platform differences.

Why not Stripe? Apple and Google require that digital goods sold within apps use their in-app purchase systems. Stripe can only be used for physical goods or services performed outside the app.

1. Create a RevenueCat Account

  1. Go to app.revenuecat.com and sign up
  2. Create a new Project (e.g., "My App")
  3. Add your platforms:
    • Apple App Store — you'll need your App Store Connect credentials
    • Google Play Store — you'll need your Google Play Console service account

Apple App Store Configuration

  1. In RevenueCat, go to Project Settings > Apps > App Store App
  2. Enter your Bundle ID (must match app.jsonios.bundleIdentifier)
  3. Add your App Store Connect Shared Secret:
    • Go to App Store Connect → Your App → General > App Information > App-Specific Shared Secret
    • Copy the secret and paste it in RevenueCat

Google Play Store Configuration

  1. In RevenueCat, go to Project Settings > Apps > Play Store App
  2. Enter your Package Name (must match app.jsonandroid.package)
  3. Upload your Service Account JSON key:
    • In Google Play ConsoleSetup > API Access
    • Create a service account with Financial Data permissions
    • Download the JSON key and upload it to RevenueCat

2. Set Up Products

In App Store Connect (iOS)

  1. Go to App Store Connect → Your App → Monetization > Subscriptions
  2. Create a Subscription Group (e.g., "Premium")
  3. Add subscription products:
    • Monthly — e.g., com.yourapp.monthly at $9.99/month
    • Annual — e.g., com.yourapp.annual at $79.99/year
  4. Fill in display name, description, and pricing for each product
  5. Submit for review (products must be approved before they work in sandbox)

In Google Play Console (Android)

  1. Go to Google Play Console → Your App → Monetize > Products > Subscriptions
  2. Create subscription products with the same IDs as iOS:
    • Monthlycom.yourapp.monthly
    • Annualcom.yourapp.annual
  3. Set pricing and billing period for each product
  4. Activate the products

3. Configure RevenueCat Products

Back in the RevenueCat dashboard:

  1. Go to Products and add each product ID from both platforms
  2. Create an Entitlement (e.g., "pro") — this represents what the user unlocks
  3. Attach your products to the entitlement
  4. Create an Offering (e.g., "default") — this is what you display to users
  5. Add Packages to the offering (Monthly, Annual) and link them to your products

4. Install the SDK

npx expo install react-native-purchases

Initialize RevenueCat

Get your API keys from the RevenueCat dashboard (Project Settings > Apps > API Keys):

// lib/revenuecat.ts
import Purchases from "react-native-purchases";
import { Platform } from "react-native";

export async function initRevenueCat() {
  Purchases.configure({
    apiKey: Platform.OS === "ios"
      ? "appl_your_ios_api_key"
      : "goog_your_android_api_key",
  });
}

Call this in your root layout:

// app/_layout.tsx
useEffect(() => {
  initRevenueCat();
}, []);

Identify Users

Link RevenueCat to your authenticated user so subscription status syncs across devices:

// After successful login
const { data: { user } } = await supabase.auth.getUser();
if (user) {
  await Purchases.logIn(user.id);
}
// After successful login
const userId = useConvexAuth().userId;
if (userId) {
  await Purchases.logIn(userId);
}

5. Set Up Webhooks

RevenueCat webhooks notify your backend when a subscription changes (renewal, cancellation, expiry, etc.).

  1. Create a Supabase Edge Function to handle webhook events:
supabase functions new revenuecat-webhook
  1. In the RevenueCat dashboard, go to Integrations > Webhooks
  2. Set the webhook URL to: https://your-project.supabase.co/functions/v1/revenuecat-webhook
  3. Add an authorization header for security
  4. Your Edge Function should update the user's subscription status in your database:
// supabase/functions/revenuecat-webhook/index.ts
Deno.serve(async (req) => {
  const body = await req.json();
  const event = body.event;
  const appUserId = event.app_user_id;

  // Update subscription status based on event type
  // event.type: INITIAL_PURCHASE, RENEWAL, CANCELLATION, EXPIRATION, etc.
  await supabase.from("profiles").update({
    plan: event.type === "EXPIRATION" ? "free" : "pro",
    revenuecat_app_user_id: appUserId,
  }).eq("id", appUserId);

  return new Response("ok");
});
  1. Create a Convex HTTP action to handle webhook events
  2. In the RevenueCat dashboard, go to Integrations > Webhooks
  3. Set the webhook URL to: https://your-project.convex.site/revenuecat-webhook
  4. Your HTTP action should update the user's subscription status:
// convex/http.ts
http.route({
  path: "/revenuecat-webhook",
  method: "POST",
  handler: async (ctx, req) => {
    const body = await req.json();
    const event = body.event;
    const appUserId = event.app_user_id;

    // Update subscription status based on event type
    await ctx.runMutation(internal.subscriptions.updateStatus, {
      userId: appUserId,
      plan: event.type === "EXPIRATION" ? "free" : "pro",
    });

    return new Response("ok");
  },
});

Webhook Event Types

EventWhen it fires
INITIAL_PURCHASEFirst subscription purchase
RENEWALSubscription renewed
CANCELLATIONUser cancelled (still active until period ends)
EXPIRATIONSubscription expired
BILLING_ISSUEPayment failed
PRODUCT_CHANGEUser changed plan (upgrade/downgrade)

6. Test in Sandbox Mode

iOS Sandbox Testing

  1. In App Store Connect, go to Users and Access > Sandbox > Test Accounts
  2. Create a sandbox tester account (use a real email you can access)
  3. On your test device: Settings > App Store > Sandbox Account — sign in with the sandbox account
  4. Run your development build — purchases will use the sandbox environment automatically
  5. Sandbox subscriptions renew at accelerated rates:
    • 1 week → 3 minutes
    • 1 month → 5 minutes
    • 1 year → 1 hour

Android Sandbox Testing

  1. In Google Play Console, go to Setup > License Testing
  2. Add tester Gmail addresses
  3. Upload an APK/AAB to any track (internal testing works)
  4. Testers can make purchases without being charged

Verify in RevenueCat

  1. Make a test purchase on your device
  2. Check the RevenueCat dashboard → Customers to see the purchase
  3. Verify your webhook endpoint received the event
  4. Confirm your database was updated with the correct subscription status

RevenueCat Paywall

Build a paywall screen to display your offerings:

function PaywallScreen() {
  const [offerings, setOfferings] = useState<PurchasesOfferings | null>(null);

  useEffect(() => {
    Purchases.getOfferings().then(setOfferings);
  }, []);

  return (
    <ScreenLayout>
      <Text style={styles.title}>Upgrade to Pro</Text>
      <Text style={styles.subtitle}>Unlock all features</Text>

      {offerings?.current?.availablePackages.map((pkg) => (
        <TouchableOpacity key={pkg.identifier} onPress={() => purchaseSubscription(pkg)}>
          <Text>{pkg.product.title}</Text>
          <Text>{pkg.product.priceString}/month</Text>
        </TouchableOpacity>
      ))}

      <Button title="Restore Purchases" variant="ghost" onPress={restorePurchases} />
    </ScreenLayout>
  );
}

Restore Purchases

Required by Apple — users must be able to restore purchases on new devices:

async function restorePurchases() {
  try {
    const customerInfo = await Purchases.restorePurchases();
    const isPro = customerInfo.entitlements.active["pro"] !== undefined;
    Alert.alert(isPro ? "Restored!" : "No active subscription found");
  } catch (error) {
    Alert.alert("Error", "Could not restore purchases");
  }
}

On this page