Payments
RevenueCat Paywall, purchases, entitlements, and profile behavior in Expo.
RevenueCat integration
The Expo template uses the native RevenueCat packages:
react-native-purchasesreact-native-purchases-ui
It does not use expo-in-app-purchases or react-native-iap as the default integration.
pnpm add react-native-purchases react-native-purchases-uiThe packages are already present in the generated template; the command is only for a fresh custom app that is adopting the service.
Configuration
Set a public RevenueCat SDK key in mobile-app/.env:
EXPO_PUBLIC_REVENUECAT_API_KEY=test_xxxxx
# Prefer platform-specific keys for production:
EXPO_PUBLIC_REVENUECAT_IOS_API_KEY=appl_xxxxx
EXPO_PUBLIC_REVENUECAT_ANDROID_API_KEY=goog_xxxxxThe configured entitlement is currently Bowlerplate Pro. The default offering/product identifiers are lifetime, yearly, and monthly; configure the matching products and entitlement in RevenueCat before release.
Runtime behavior
- Account projects configure RevenueCat with the authenticated backend UUID so access can follow the user across devices.
- Guest projects use an anonymous RevenueCat customer and do not ask the BowlerKit API for entitlement data.
- Paywall, restore purchases, customer center, and entitlement checks run on native iOS/Android; web returns an unavailable result.
- Store purchase and restore actions require internet access even in Guest mode.
Showing a paywall
Use the existing service rather than calling the native SDK throughout the UI:
import { paywallService } from '@/services/paywall-service';
const outcome = await paywallService.showIfNeeded('Bowlerplate Pro');
if (outcome.granted) {
// Continue with the premium action.
}premiumAccess.require(...) is available for feature gates. Keep entitlement identifiers stable; changing them requires a RevenueCat migration.
AdMob integration
The same generated project also retains react-native-google-mobile-ads for Account and Guest. It is off by default (EXPO_PUBLIC_ADMOB_ENABLED=false) and the native config uses Google's test app IDs until you provide your own values. For an internal gallery build, EXPO_PUBLIC_ADMOB_GALLERY_PREVIEW=true exposes test inventory without enabling live ads.
To enable release ads, set EXPO_PUBLIC_ADMOB_ENABLED=true and provide all four validated values:
EXPO_PUBLIC_ADMOB_ANDROID_APP_ID=ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy
EXPO_PUBLIC_ADMOB_IOS_APP_ID=ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy
EXPO_PUBLIC_ADMOB_ANDROID_BANNER_ID=ca-app-pub-xxxxxxxxxxxxxxxx/yyyyyyyyyy
EXPO_PUBLIC_ADMOB_IOS_BANNER_ID=ca-app-pub-xxxxxxxxxxxxxxxx/yyyyyyyyyyapp.config.ts fails closed when any required ID is missing. Obtain consent and complete the platform policy/release review before showing live ads; an AdMob no-fill/error must not block app startup.
Release checklist
- Create Android/iOS products and the
Bowlerplate Proentitlement in RevenueCat. - Set platform-specific public RevenueCat SDK keys.
- Configure store credentials and tester accounts.
- Decide whether ads are enabled, configure consent, and provide every AdMob ID before a release build.
- Build a native preview/release binary; Expo Go cannot exercise every native purchase/ad path.
- Test purchase, restore, logout/login, anonymous Guest purchase, cross-device Account access, ad no-fill, and offline/error states.
Relevant files
services/paywall-service.tsconstants/monetization.tsstore/monetization-store.tsapp/profile/billing.tsxdocs/REVENUECAT.md
