From 1893b213e60f9cb58073e9967f5bd80b353f13a5 Mon Sep 17 00:00:00 2001
From: Hyo
kotlinVersion explicitly with expo-build-properties as
+ shown below.
npx expo prebuild).
-
- Xcode 27 builds must use the UIScene lifecycle. Regenerate with an
- Expo template that creates React Native from{' '}
- ExpoAppSceneDelegate, or migrate an older generated iOS
- host before building. Confirm that Info.plist contains a
- scene configuration and that AppDelegate.swift no longer
- creates UIWindow(frame: UIScreen.main.bounds). See the{' '}
+
Info.plist needs a scene configuration,
+ and AppDelegate.swift must no longer create{' '}
+ UIWindow(frame: UIScreen.main.bounds). Newer Expo
+ templates (based on ExpoAppSceneDelegate) generate this
+ correctly.
+ The expo-iap config plugin supports these options:
++ The expo-iap config plugin does two things: it wires your IAPKit + publishable key into the app for hosted{' '} + purchase verification, and it + enables optional store modules —{' '} + Onside (an iOS alternative + marketplace), Horizon OS{' '} + (Meta Quest), and Amazon{' '} + (Fire OS devices and the Vega OS runtime). All modules are off by + default; enable only the stores you ship to. +
- Use this page for the Expo plugin shape. Store-specific values, + Use this page for the Expo plugin shape. Store-specific values — required developer-console fields, supported targets, and artifact - rules live in Store Setup: + rules — live in each store's setup page linked above.
-
- Keep module enable flags under modules and
- platform-specific values under android or{' '}
- ios. For Amazon targets, use{' '}
- modules.amazon.fireOS and{' '}
- modules.amazon.vegaOS; use{' '}
- android.amazon.vegaOS only when Vega metadata must differ
- from the normal Expo app config.
+ Module enable flags live under modules; platform-specific
+ values live under android or ios. For
+ Amazon, modules.amazon.fireOS and{' '}
+ modules.amazon.vegaOS toggle each target; the separate{' '}
+ android.amazon.vegaOS block is only needed when your Vega
+ OS build requires different values (app id, artifacts) than your
+ regular Android config — see{' '}
+ Amazon Store Setup.
+ expo-iap and react-native-iap share the same OpenIAP API; they differ + only in tooling: +
npx expo install instead of{' '}
npm install
Replace react-native with react-native-tvos{' '}
in your package.json:
@@ -654,8 +634,8 @@ EXPO_TV=1 npx expo run:ios --device "Apple TV 4K (3rd generation)"`}
>
Warning: Expo SDK 52 (React Native 0.76.x) uses
Kotlin 1.9.x, which is incompatible with the current OpenIAP Android
- artifacts. Upgrading to SDK 53+ and setting Kotlin
- 2.2.0 is the recommended solution.
+ artifacts — upgrading to SDK 53+ is the recommended
+ fix (see Android Kotlin Version).
@@ -697,7 +677,12 @@ module.exports = function withBillingLibraryDowngrade(config) { -
minSdkVersion {ANDROID_SDK.minSdk}+ (see the platform
+ sections below)
+
- Add the following to your ios/Runner/Info.plist (iOS
- 14+):
+ Declaring itms-apps in ios/Runner/Info.plist{' '}
+ is only needed when your own code checks App Store links before
+ opening them (for example canLaunchUrl from url_launcher)
+ — the plugin itself does not require it:
missingDimensionStrategy{' '}
- configuration is required since v7.1.14 due to product flavor support
- for Meta Horizon OS and Fire OS. Keep this page focused on Flutter
- installation; use Store Setup for target-specific Android flavor
- details.
+ Note: The missingDimensionStrategy line
+ is required since v7.1.14 because the Android library ships product
+ flavors for alternative stores — Meta Horizon OS (Quest headsets) and
+ Amazon Fire OS. Apps shipping to Google Play always select the{' '}
+ play flavor shown above. Targeting Meta Quest or Amazon
+ devices instead? See{' '}
+ Horizon Store Setup or{' '}
+ Amazon Store Setup for the
+ flavor to select there.
dataAndroid. Flutter 10 does not accept the former
- custom-channel alias. Native adapters, MethodChannel fixtures, and
- mocks must emit dataAndroid. See{' '}
-
- Deprecations & 3.0 Migration
-
- .
-
+ The typical flow is initConnection → set up purchase
+ listeners → fetchProducts → requestPurchase{' '}
+ → finishTransaction. The snippets below cover each step;
+ the Purchase Guide shows the
+ full flow with receipt validation.
+
+ requestPurchase does not return the purchase. Results
+ arrive on the purchaseUpdatedListener stream you set up
+ in Basic Setup (unlike the callback-style
+ hooks in the React Native SDKs), so make sure both listeners are
+ active before you call it.
+
purchaseUpdatedListener and{' '}
- purchaseErrorListener listeners before calling{' '}
- requestPurchase.
- PurchaseState.Pending
+
+
+ The public Purchase field is dataAndroid. Flutter 10 does
+ not accept the former custom-channel alias. Native adapters,
+ MethodChannel fixtures, and mocks must emit dataAndroid.
+ See{' '}
+
+ Deprecations & 3.0 Migration
+
+ .
+
The zip includes pre-built binaries for both iOS and Android.
-
- Native Apple API availability is fixed when the pre-built{' '}
- GodotIap.framework is compiled. In particular, the
- verified Apple 27 offer-code result requires a framework built with
- Xcode 27 or later; a framework built with Xcode 26 still presents the
- legacy sheet and returns null, even when the app runs on
- Apple 27. The published godot-iap 3.0.0 iOS framework is built with
- Xcode 27 and its release workflow rejects an older artifact. Custom
- builds must use Xcode 27 to retain that result path.
-
- Release zips are intended for iOS export and Android. If you use a
- release or custom build that includes{' '}
- addons/godot-iap/bin/macos, and Godot reports that{' '}
- GodotIap.framework or{' '}
- SwiftGodotRuntime.framework is damaged on macOS, clear
- quarantine and repair the local ad-hoc signature:
-
- The checked-in macOS runtime frameworks are Apple Silicon (
- arm64) only. Custom source builds can override{' '}
- MACOS_ARCHS; make macos requests{' '}
- arm64 x86_64 by default, and generated metadata should
- only include architectures that the framework binaries actually
- contain. The default release zip does not include macOS runtime
- frameworks.
-
+ The checked-in macOS runtime frameworks are Apple Silicon (
+ arm64) only. Custom source builds can override{' '}
+ MACOS_ARCHS; make macos requests{' '}
+ arm64 x86_64 by default, and generated metadata should
+ only include architectures that the framework binaries actually
+ contain.
+
+ The pre-built iOS framework locks in Apple API availability at compile
+ time. One feature depends on this:{' '}
+
+ offer code redemption
+ {' '}
+ only returns a verified result when the framework was built with Xcode
+ 27 or later — a framework built with Xcode 26 falls back to the legacy
+ redemption sheet and returns null, even on devices
+ running the latest OS. The published godot-iap 3.0.0 framework is
+ built with Xcode 27. If you build from source and use offer codes,
+ build with Xcode 27 or later.
+
+ The default release zip does not include macOS runtime frameworks, so
+ most projects can skip this section. It applies only if you build from
+ source with macOS support or use a custom zip containing{' '}
+ addons/godot-iap/bin/macos. If Godot reports that{' '}
+ GodotIap.framework or{' '}
+ SwiftGodotRuntime.framework is damaged, clear quarantine
+ and repair the ad-hoc signature:
+
+ The recommended way to use GodotIap is to add{' '}
+ GodotIapWrapper as a child node:
+
GodotIapWrapper
+ @onready var iap = $GodotIapWrapper
+ Or create the wrapper node programmatically:
+
- Add GodotIapWrapper as a child node in your scene, then
- use this script:
+ With the GodotIapWrapper node from Scene Setup in place,
+ attach this script to confirm the plugin loads and the store connects:
- The recommended way to use GodotIap is to add{' '}
- GodotIapWrapper as a child node:
-
GodotIapWrapper
- @onready var iap = $GodotIapWrapper
- Or create the wrapper node programmatically:
-
+ The examples below use the iap reference created in Scene
+ Setup (@onready var iap = $GodotIapWrapper).
+
snake_case for all
+ function names (init_connection,{' '}
+ fetch_products, request_purchase). Return
+ types use Array for lists and Variant for
+ platform-specific single results.
+ finishTransaction{' '}
+ Critical: Always call finish_transaction{' '}
after verifying a purchase. On Android, unfinished purchases are
automatically refunded after 3 days.
@@ -519,13 +549,18 @@ func _on_purchase_error(error):
#
+ Query store metadata with a ProductRequest; results are
+ platform-typed (ProductAndroid on Android,{' '}
+ ProductIOS on iOS):
+
+ Configure both platforms in a single request — the plugin picks the + branch matching the store the game is running on, so one purchase call + works everywhere: +
snake_case for all
- function names (init_connection,{' '}
- fetch_products, request_purchase). Return
- types use Array for lists and Variant for
- platform-specific single results.
-
+ A crash with{' '}
+ Library not loaded: @rpath/GodotIap.framework/GodotIap{' '}
+ means the frameworks were not embedded — see{' '}
+ iOS: Xcode Framework Embedding and run{' '}
+ fix_ios_embed.sh.
+
- kmp-iap provides in-app purchase support for Kotlin
- Multiplatform projects. It supports Android natively and iOS via
- CocoaPods integration.
+ kmp-iap brings OpenIAP-compliant in-app purchases to Kotlin
+ Multiplatform projects. Android talks to Google Play Billing directly;
+ iOS links the OpenIAP StoreKit framework, added with either CocoaPods or
+ Swift Package Manager (see iOS Configuration{' '}
+ below). Requires iOS 15.0+ and the Android minSdk shown in{' '}
+ Android Configuration.
.xcworkspace (not .xcodeproj).
-
- Add to your iosApp/Info.plist:
+ Declaring itms-apps in iosApp/Info.plist is
+ only needed when your own code checks App Store links before opening
+ them — kmp-iap itself does not require it:
+ Results stream through Kotlin Flows: initialize the connection, attach
+ the purchase and error flows, then fetch and purchase. A successful{' '}
+ initConnection() followed by a non-empty{' '}
+ fetchProducts() result is the quickest way to confirm the
+ platform setup above is working.
+
Two patterns are supported:
++ Two patterns are supported. The connection, fetch, and purchase APIs + are suspend functions — call them from a coroutine scope: +
- KMP IAP uses Kotlin Flow for purchase events:
+ KMP IAP delivers purchase results through hot Kotlin{' '}
+ Flows — despite the names,{' '}
+ purchaseUpdatedListener and{' '}
+ purchaseErrorListener are Flows, not one-shot callbacks.
+ Collect both in a long-lived coroutine scope before requesting a
+ purchase; events are emitted as they occur.
+ Fetch products once the connection is up, then request a purchase —
+ the result arrives on purchaseUpdatedListener, not as a
+ return value.
+
+ Call endConnection() when the owning screen or scope is
+ disposed — not right after requestPurchase, or the
+ connection may close before the purchase result arrives.
+
- Package shape: apps reference only{' '}
- OpenIap.Maui. OpenIAP-owned Android and iOS binding
- outputs are flattened into that package. Google Billing, Play
- Services, Gson, AndroidX, and Kotlin Android libraries stay as normal
- NuGet dependencies so your app can deduplicate them with its own
- package graph.
-
Before you start: create the products in App Store @@ -73,9 +62,6 @@ function MauiSetup() {
+ Package shape: your app references a single
+ package, OpenIap.Maui. The OpenIAP-owned iOS and
+ Android bindings are bundled inside it, while shared dependencies
+ (Google Play Billing, Play Services, AndroidX, Kotlin, Gson) remain
+ ordinary NuGet dependencies so NuGet can deduplicate them with the
+ rest of your dependency graph.
+
Working from this monorepo before publishing? Use a
@@ -132,6 +129,20 @@ function MauiSetup() {
+ Building the Apple library from source requires Xcode 27; the + published package already embeds a prebuilt XCFramework, so NuGet + consumers do not need Xcode 27 — their normal toolchain is enough. +
+
+ Upgrading from OpenIap.Maui 1.x? OpenIap.Maui 2.x
+ supports .NET 10 only. Retarget every net9.0-* TFM to
+ the matching net10.0-* TFM and update the MAUI workload
+ before upgrading the package.
+
+ Configure the app project once per platform before calling any store + API. +
+ The typical flow is: initialize the connection, fetch products, + register purchase listeners, request a purchase, then finish the + verified transaction. +
- The MAUI package exposes the same IAPKit helper surface as the - JavaScript SDKs: create a kit client for status, entitlements, and - bind-user calls. These app-facing calls use the publishable key. Store - lifecycle webhooks flow into IAPKit only; there is no outbound webhook - stream in the MAUI package. + IAPKit is OpenIAP's hosted receipt-validation backend (see{' '} + Purchase Verification with IAPKit). + The MAUI package ships the same app-facing helper as the other OpenIAP + SDKs: create a kit client with your publishable key to read purchase + status and entitlements and to bind a purchase to a user. Store + lifecycle events (App Store Server Notifications, Google Play RTDN) + are delivered to IAPKit's backend, not to your app — the client reads + current state through these bounded calls rather than subscribing to a + webhook stream.
+ The example app builds against the in-repo Android library, so rebuild + the OpenIAP Android AARs once before the first run (published-package + consumers skip this step): +
+ Then run the example (its application id is{' '}
+ dev.hyo.martie; uninstalling first clears stale native
+ code):
+
- Upgrading from OpenIap.Maui 1.x? OpenIap.Maui 2.x
- supports .NET 10 only. Retarget every net9.0-* TFM to
- the matching net10.0-* TFM and update the MAUI workload
- before upgrading the package.
-
VS Code launch configurations are available in{' '}
libraries/maui-iap/.vscode/launch.json. The iOS device
@@ -406,6 +433,9 @@ dotnet build -t:Run -f net10.0-maccatalyst`}
#
+
+ Common failures and their causes, roughly in the order you hit them. +
If Google Play shows "This version of the application is not configured for billing through Google Play", the library reached - BillingClient correctly. The app build is not accepted by Play Billing - for that package, signing key, track, tester, or product setup yet. + BillingClient correctly - Play itself rejected the app build. Check + each of the following:
+
react-native-iap provides in-app purchase support for React
- Native apps using Nitro Modules. It supports StoreKit 2 on iOS and
- Google Play Billing {GOOGLE_PLAY_BILLING.version}+ on Android by
- default, with optional Horizon and Fire OS Android flavors.
+ Native apps using Nitro Modules, a high-performance native bridging
+ layer for React Native. It supports StoreKit 2 on iOS and Google Play
+ Billing {GOOGLE_PLAY_BILLING.version}+ on Android by default, with
+ optional build flavors for{' '}
+ Horizon OS (Meta Quest) and{' '}
+ Fire OS (Amazon Appstore).
react-native-iap v15+
- uses Nitro Modules and requires React Native 0.79+.
- It is designed for bare React Native CLI projects
- only. If you're using Expo, use{' '}
- expo-iap instead.
-
+ react-native-iap requires react-native-nitro-modules as a
+ peer dependency — install both together.
+
react-native-iap v15+ is built on{' '}
@@ -108,49 +104,12 @@ npm install react-native-iap`}
Native modules are automatically linked during your
app's build process
-
+ If you hit Swift 6 C++ interop errors in Nitro, see{' '}
+ Troubleshooting for the Swift 5.10 pin
+ workaround.
+ AnyMap.swift using{' '}
- cppPart.pointee.*), pin Swift 5.10 for the{' '}
- NitroModules pod as a temporary workaround:
- react-native-nitro-modules and nitro-codegen{' '}
- to latest, then pod install and do a clean build. If
- issues persist, share a minimal repro (package.json +{' '}
- Podfile) on{' '}
-
- GitHub Issues
-
- .
-
iOS
@@ -159,9 +118,6 @@ end`}
- Bare React Native does not use an Expo config plugin. For Vega OS, - keep Amazon Kepler packages in a Vega-only React Native target and - follow Store Setup for package, manifest, and supported-version - details. + Vega OS is Amazon's newer, non-Android operating system; its apps run + on the Kepler runtime. react-native-iap supports it through a separate + React Native for Vega target — it is not an Android build flavor, and + unlike expo-iap there is no config plugin to enable it. Keep the + Amazon Kepler packages in that Vega-only target so regular iOS and + Android builds are unaffected, and follow{' '} + Amazon Store Setup for package, + manifest, and supported-version details.
@@ -433,54 +392,6 @@ switch (error.code) {RCT-Folly/folly/Expected.h, add these defines to your{' '}
Podfile post_install block:
-
If you see errors in AnyMap.swift related to{' '}
- cppPart.pointee, see the{' '}
- Nitro Modules section above for the Swift
- 5.10 pin workaround.
+ cppPart.pointee, pin Swift 5.10 for the{' '}
+ NitroModules pod as a temporary workaround:
react-native-nitro-modules and nitro-codegen{' '}
+ to latest, then pod install and do a clean build. If
+ issues persist, share a minimal repro (package.json +{' '}
+ Podfile) on{' '}
+
+ GitHub Issues
+
+ .
+
- Amazon support has two separate targets. Fire OS is an Android Appstore
- build using the amazon Gradle flavor. Vega OS is a Kepler
- runtime target for React Native for Vega and compatible Expo Vega
- builds. Do not use fireOsEnabled as a Vega selector.
+ Amazon distributes apps to two different device families, and OpenIAP
+ treats them as two separate targets. Fire OS is Amazon's Android-based
+ OS (Fire TV, Fire tablets): an Amazon Appstore build is a regular
+ Android build that selects the amazon Gradle flavor. Vega
+ OS is Amazon's newer, non-Android OS: its apps run on the Kepler
+ JavaScript runtime and are built with React Native for Vega (or a
+ compatible Expo Vega build), so Vega is a separate project target rather
+ than an Android flavor.
+ OpenIAP selects each Amazon target through a different mechanism — a + Gradle flavor for Fire OS, a runtime-selected adapter for Vega OS: +
+| Target | +Runtime | +OpenIAP selection | +
|---|---|---|
| Fire OS | +Android / Amazon Appstore SDK | +
+ Android amazon flavor and{' '}
+ openiap-google-amazon.
+ |
+
| Vega OS | +Amazon Kepler JavaScript runtime | +
+ Runtime-selected kepler adapter in{' '}
+ expo-iap or react-native-iap.
+ |
+
Fire OS and Vega OS both validate through Amazon receipts, but their - app metadata is configured in different places. + app metadata is configured in different places. App Tester refers to + Amazon App Tester, Amazon's sideloaded sandbox app for exercising test + purchases on device.
| Target | -Runtime | -OpenIAP selection | -
|---|---|---|
| Fire OS | -Android / Amazon Appstore SDK | -
- Android amazon flavor and{' '}
- openiap-google-amazon.
- |
-
| Vega OS | -Amazon Kepler JavaScript runtime | -
- Runtime-selected kepler adapter in{' '}
- expo-iap or react-native-iap.
- |
-
+ The table below summarizes how each framework selects an Amazon + target; the Fire OS and{' '} + Vega OS sections that follow contain the full + configuration. +