Skip to content
Skip to content
Goodspeed

BUILT INTO EVERY GOODSPEED APP

In-App Purchases

RevenueCat handles subscription and one-time purchase flows on both iOS and Android. Entitlement checking, paywall screens, receipt validation, and restore-purchases are all included and controlled by a single config flag.

  • Tier: Common
  • Status: Config-toggled
  • Config: features.inAppPurchases.enabled

WHY IT MATTERS

Most mobile app projects spend weeks plumbing the same infrastructure before writing a single line of product code. In-App Purchases is one of those cross-cutting concerns that every app eventually needs but almost none get right the first time. Permissions are handled incorrectly, tokens expire silently, or the feature breaks after an OS update nobody tested against.

Goodspeed solves this by shipping in-app purchases as a production-grade, tested implementation inside every generated app. The code follows the patterns in the GAS template - the same 246-feature catalog that powers every app we build. Controlled by `features.inAppPurchases.enabled` in gas.config.ts. You own the code from day one, can read every line, and can hire any React Native developer to extend it. The build pipeline verifies the feature compiles and routes resolve before the app lands in your repository, so you are not the one catching the integration error at 2 am before launch.

HOW IT IS WIRED

Real code from the GAS template

The excerpt below is lifted verbatim from lib/revenuecat.ts in the gas-template repository. This is the code your generated app gets, not pseudocode, not a description of intent.

// lib/revenuecat.ts — initialization + purchase
export async function initRevenueCat(): Promise<void> {
  if (isWeb || !Purchases) return;
  if (!gasConfig.features.inAppPurchases.enabled) return;

  const apiKey =
    Platform.OS === 'ios' ? REVENUECAT_APPLE_KEY : REVENUECAT_GOOGLE_KEY;
  if (!apiKey) return;

  if (__DEV__ && LOG_LEVEL) Purchases.setLogLevel(LOG_LEVEL.DEBUG);
  await Purchases.configure({ apiKey });
  captureEvent('revenuecat_initialized', { platform: Platform.OS });
}

export async function purchasePackage(
  pkg: import('react-native-purchases').PurchasesPackage,
): Promise<{ entitlementActive: boolean }> {
  if (isWeb || !Purchases) return { entitlementActive: false };
  const { customerInfo } = await Purchases.purchasePackage(pkg);
  const active = Object.keys(customerInfo.entitlements.active).length > 0;
  captureEvent('iap_purchase', { packageId: pkg.identifier, active });
  return { entitlementActive: active };
}

Source: goodspeed-apps/gas-template lib/revenuecat.ts

HONEST LIMITS

When In-App Purchases is the wrong choice

Free tools, enterprise apps billed outside the mobile stores, and B2B apps where the purchase happens on a web portal should disable in-app purchases entirely. Apple and Google take a 15-30% cut, which is not viable for all pricing models.

Tier: Common · Config-toggled

  1. Evaluate your use case

    Check whether in-app purchases aligns with your target audience, platform constraints, and regulatory environment before enabling it.

  2. Audit the config

    The `features.inAppPurchases.enabled` flag controls this feature. Set it to false in gas.config.ts to disable the feature entirely with no residual code paths.

  3. Seek alternatives

    If the built-in implementation does not fit, the generated codebase is standard React Native + Expo code. Any library in the Expo ecosystem can replace the default.

APPS USING THIS FEATURE

Apps built with In-App Purchases

These apps were generated by Goodspeed and use in-app purchases as a core part of their experience. Each link goes to the full app marketing page.

CAPABILITIES

In-App Purchases capability breakdown

Concrete dimensions of what the built-in in-app purchases implementation covers. These reflect the actual template code, not a marketing summary.

ItemDescriptionStrength
ProvidersRevenueCat abstracts Apple StoreKit and Google Play Billing behind one API call.iOS + Android
EntitlementsEntitlement status is fetched via getCustomerInfo(). The paywall reads from offerings returned by getOfferings().RevenueCat
Offerings cacheOfferings are cached in memory for 5 minutes to avoid redundant network round-trips.5-min TTL cache
RestoreRestore purchases calls Purchases.restorePurchases() and re-checks entitlements.Built in
Web supportAll IAP functions are no-ops on web. The paywall never renders on non-native platforms.No-op on web

GET IT BUILT INTO YOUR APP

Score your idea and get in-app purchases wired in from day one