BowlerKitBowlerKit
Features

Payments

10/11/2026

RevenueCat Paywall, purchases, entitlements, and profile behavior in Expo.

RevenueCat integration

The Expo template uses the native RevenueCat packages:

  • react-native-purchases
  • react-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-ui

The 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_xxxxx

The 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/yyyyyyyyyy

app.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

  1. Create Android/iOS products and the Bowlerplate Pro entitlement in RevenueCat.
  2. Set platform-specific public RevenueCat SDK keys.
  3. Configure store credentials and tester accounts.
  4. Decide whether ads are enabled, configure consent, and provide every AdMob ID before a release build.
  5. Build a native preview/release binary; Expo Go cannot exercise every native purchase/ad path.
  6. Test purchase, restore, logout/login, anonymous Guest purchase, cross-device Account access, ad no-fill, and offline/error states.

Relevant files

  • services/paywall-service.ts
  • constants/monetization.ts
  • store/monetization-store.ts
  • app/profile/billing.tsx
  • docs/REVENUECAT.md