openiap
Unified in-app purchase SDK for Expo and React Native apps, conforming to the OpenIAP spec across iOS and Android.
Repository Health
Technical Analysis
expo-iap is an Expo Module that gives React Native and Expo apps a single, type-safe API for in-app purchases and subscriptions across iOS and Android. Instead of hand-rolling separate StoreKit and Play Billing integrations, developers call one set of functions — fetchProducts, requestPurchase, finishTransaction, getActiveSubscriptions — and the library routes each call to native StoreKit 2 (iOS) or Play Billing 9.1 (Android) code underneath.
The package conforms to the OpenIAP specification, a vendor-neutral protocol shared with react-native-iap, flutter_inapp_purchase, kmp-iap, and godot-iap. Types, error codes, and purchase flows are generated from a single GraphQL schema, so switching between these sibling libraries — or debugging a purchase error — follows the same mental model regardless of platform. A useIAP() React hook wraps connection lifecycle and event listeners for common cases, while the lower-level functions remain available for custom flows.
What You Get
- A unified fetchProducts/requestPurchase/finishTransaction API that abstracts over StoreKit 2 and Play Billing 9.1
- A useIAP() React hook managing connection lifecycle, purchase listeners, and subscription state
- OpenIAP-generated TypeScript types and error codes shared with react-native-iap, flutter_inapp_purchase, kmp-iap, and godot-iap
- Structured PurchaseError objects with platform-aware error codes instead of raw native exceptions
- An Expo config plugin (app.plugin.js) for automatic native project configuration
- Support for Amazon Appstore purchases via the Onside/Kepler integration alongside Apple and Google
Common Use Cases
- Mobile app monetization — Expo/React Native teams selling consumables, non-consumables, or subscriptions add one dependency instead of separate iOS/Android purchase code
- Cross-platform receipt verification — apps call verifyPurchase/verifyPurchaseWithProvider to validate purchases server-side using OpenIAP-standardized payloads
- Migrating from react-native-iap — Expo-first projects moving off the older bridge-based library adopt expo-iap for tighter Expo Modules integration while keeping the same OpenIAP types
- Restoring purchases across devices — restorePurchases and getAvailablePurchases let apps recover a user’s entitlements after reinstall or a device change
Under The Hood
Architecture index.ts exposes the OpenIAP-conformant query/mutation surface (fetchProducts, requestPurchase, finishTransaction, initConnection, getActiveSubscriptions, verifyPurchase) as thin typed wrappers around ExpoIapModule, the generated Expo native-module binding (ExpoIapModule.ts / getNativeModule), which in turn delegates to platform-specific native code — Swift/StoreKit 2 under ios/ and Kotlin/Play Billing under android/src/main/java. An emitter-based event system (OpenIapEvent, purchaseUpdatedListener, purchaseErrorListener, userChoiceBillingListenerAndroid) sits on top of the native bridge, and useIAP.ts composes those primitives into a single React hook that manages connection lifecycle, listener subscription/cleanup via refs and effects, and derived subscription state — so consumers can choose the low-level functional API or the hook without the two diverging.
Tech Stack The package is TypeScript-first, built on Expo Modules Core (expo-module-scripts, expo-modules-core) with peer dependencies on expo, react, and react-native, plus a dedicated Amazon Appstore integration (@amazon-devices/keplerscript-appstore-iap-lib). Native code is Swift on iOS (ExpoIapModule.swift, StoreKit 2) and Kotlin on Android, generated/maintained in lockstep with sibling libraries from a shared OpenIAP GraphQL schema. Bun drives builds and scripts (bun.lock, expo-module build/clean), TypeScript compiles the plugin separately (tsc —build plugin), and a dedicated GitHub Actions workflow (ci-expo-iap.yml) runs CI scoped to this package within the openiap monorepo.
Code Quality The src/tests directory holds a comprehensive suite (17 test files) covering the public hook (useIAP.test.tsx), error mapping, OpenIAP conformance, native module lifecycle, and edge cases like discriminated-union product responses — run via ts-jest with coverage collection configured in jest.config.js. Errors are handled explicitly through createPurchaseError/createPurchaseErrorFromNativeException, which normalize native StoreKit/Play Billing failures into a typed PurchaseError with a stable ErrorCode enum, rather than letting raw native exceptions leak through. The lint pipeline (lint:ci) chains TypeScript’s noEmit type-check, ESLint, Prettier, and ktlint for the Kotlin side, all enforced in CI.
What Makes It Unique Rather than shipping an isolated IAP wrapper, expo-iap is one of several sibling libraries (react-native-iap, flutter_inapp_purchase, kmp-iap, godot-iap) that all implement the same OpenIAP specification — a vendor-neutral protocol with types, error codes, and purchase-flow semantics generated from a single GraphQL schema and versioned centrally (openiap-versions.json). That shared-spec approach, backed by Meta and Amazon as sponsors, is aimed squarely at reducing the fragmentation that otherwise exists between per-platform, per-framework in-app-purchase implementations, and at making purchase-handling code easier for both humans and AI coding assistants to generate consistently.