跳到正文
DEV Community· Famitha M A·· 5 小時前AI 評分58

Expo + RevenueCat:5 步設定訂閲並測試

Expo + RevenueCat: Subscriptions in 5 Steps (and How to Test Them)

AI 導讀

Expo + RevenueCat 訂閲整合可按 5 步設定,測試則先用 RevenueCat Test Store,再進行 Apple/Google 沙盒測試。

正文

Cover image for Expo + RevenueCat: Subscriptions in 5 Steps (and How to Test Them)

Famitha M A

TL;DR

  • Real purchases don't work in Expo Go. RevenueCat only runs a mock Preview API Mode there. Use a development build.
  • Configure the SDK once at startup with a per-platform public key.
  • Gate features on an entitlement (pro), never on product IDs.
  • purchasePackage throws when the user cancels. Check e.userCancelled and move on.
  • restorePurchases only from a button tap, never on launch.
  • Test with RevenueCat's Test Store first, then Apple/Google sandboxes before review.

1. Install and rebuild

npx expo install react-native-purchases react-native-purchases-ui expo-dev-client
eas build --profile development --platform all

Skip the rebuild and you'll get `new NativeEventEmitter()` requires a non-null argument, which RevenueCat's docs call out directly.

2. Configure once

// app/_layout.tsx
useEffect(() => {
  if (__DEV__) Purchases.setLogLevel(LOG_LEVEL.VERBOSE);
  Purchases.configure({
    apiKey: Platform.OS === 'ios'
      ? process.env.EXPO_PUBLIC_RC_APPLE_KEY!
      : process.env.EXPO_PUBLIC_RC_GOOGLE_KEY!,
  });
}, []);

Public SDK keys are fine in EXPO_PUBLIC_ vars. Secret keys never go in the app. If you have your own auth, call Purchases.logIn(userId) after sign-in.

3. Paywall

Lazy option, RevenueCat's prebuilt paywall, only shown if the user isn't already pro:

import RevenueCatUI, { PAYWALL_RESULT } from 'react-native-purchases-ui';

const result = await RevenueCatUI.presentPaywallIfNeeded({
  requiredEntitlementIdentifier: 'pro',
});

const hasPro = [
  PAYWALL_RESULT.PURCHASED,
  PAYWALL_RESULT.RESTORED,
  PAYWALL_RESULT.NOT_PRESENTED, // already subscribed
].includes(result);

Custom UI option:

const offerings = await Purchases.getOfferings();
const packages = offerings.current?.availablePackages ?? [];

async function buy(pkg: PurchasesPackage) {
  try {
    const { customerInfo } = await Purchases.purchasePackage(pkg);
    if (customerInfo.entitlements.active['pro']) unlock();
  } catch (e: any) {
    if (!e.userCancelled) showError(e);
  }
}

Render pkg.product.priceString, not a hard-coded "$4.99". Prices are per country.

4. One hook for access

export function useIsPro() {
  const [isPro, setIsPro] = useState(false);
  useEffect(() => {
    const update = (info: CustomerInfo) =>
      setIsPro(typeof info.entitlements.active['pro'] !== 'undefined');
    Purchases.getCustomerInfo().then(update).catch(() => {});
    Purchases.addCustomerInfoUpdateListener(update);
    return () => { Purchases.removeCustomerInfoUpdateListener(update); };
  }, []);
  return isPro;
}

The listener means renewals, expiries and restores update the UI live. Still check on your backend for anything valuable server-side.

If you're still building the screens that sit behind the paywall, RapidNative generates a plain Expo Router + TypeScript project from a prompt that you can export and drop this code into. (I work on it.)

5. Restore button

const info = await Purchases.restorePurchases();

RevenueCat's docs say to call this only from a user action, because it can pop an OS sign-in prompt. Put the button on the paywall and in settings.

Testing

Stage Use
Building RevenueCat Test Store key. No store accounts needed.
Pre-review Platform keys + Apple sandbox / Google Play testing
Production Platform keys. Never the Test Store key.

Android gotcha: products won't load unless a signed build is on a closed testing track, your account is a license tester, and you've opened the opt-in link. Sandbox renewals are fast (monthly every 5 min, yearly every 30 min, max six renewals), which is great for testing expiry.

Debug cheatsheet

Symptom Cause
Empty offerings Products not attached to the offering, or not active in the store
Nothing happens in Expo Go Preview API Mode. Use a dev build.
Android products missing Not on a testing track / not a license tester
Subscriber sees paywall Checking product IDs, or missing logIn

What's been your biggest subscriptions headache in React Native: store setup, sandbox testing, or syncing access with your backend?

來源:DEV Community · dev.to