Use this page with AI
Copy this into your coding assistant and add your request.
Read https://openiap.dev/docs/updates/migration and https://openiap.dev/llms.txt. Follow the reading instructions, detailed reference, and linked guides relevant to my task before making changes.
Inspect my existing project and reuse its framework and conventions. Ask me for missing product decisions. Implement the requested behavior and run the applicable checks.
Show the working result, the commands and actual test results, and any remaining limitations. Keep your explanation brief.
My request: [describe what customers should be able to do]Migration
Find replacements for deprecated APIs and the package versions scheduled to remove them.
Scheduled removals#
These deprecated APIs, keys, and fields remain available through patch and minor releases. Each package removes its own confirmation helpers in its next major independently. Shared Gradle flags are removed together across the packages that read them. For store configuration, see How the Store Is Selected.
Keep new Expo app imports on expo-iap. The extracted purchase-flow-core and purchase-flow-confirmers modules are internal implementations.
Removal in Client Protocol 2.0, flutter_inapp_purchase 12.0, godot-iap 5.0, kmp-iap 5.0, and OpenIap.Maui 4.0
| Deprecated key | Migrate to |
|---|---|
createIapkitConfirmation / toIapkitVerificationProps / readIapkitVerification (Client Protocol, Flutter, KMP) | The SDK’s confirmWithIapkit; pass a vendor-independent confirmer for another service |
createDeviceConfirmationIOS (Client Protocol, Flutter, KMP) | The SDK’s confirmOnDeviceIOS |
IapkitConfirmationOptions / IapkitVerificationProps / IapkitVerificationResult from Client Protocol purchase-flow | The framework SDK’s IapkitConfirmationOptions / VerifyPurchaseWithProviderProps / VerifyPurchaseWithProviderResult |
OpenIap.Maui.IapkitConfirmation / DeviceConfirmationIOS | OpenIapClient.ConfirmWithIapkit / ConfirmOnDeviceIOS |
PurchaseFlow.create_iapkit_confirmation / to_iapkit_verification_props / read_iapkit_verification / IAPKIT_UNKNOWN_RETRY_MS (Godot) | GodotIapWrapper.confirm_with_iapkit; retry timing belongs to the confirmer |
PurchaseFlow.create_device_confirmation_ios (Godot) | GodotIapWrapper.confirm_on_device_ios |
Removal in openiap-google 5.0, react-native-iap 18.0, expo-iap 7.0, and flutter_inapp_purchase 12.0
| Deprecated key | Migrate to |
|---|---|
horizonEnabled=true | openiapStore=horizon, or no pin: a debug build follows the connected Quest |
openiapPlatform=none (flutter_inapp_purchase) | openiapStore=none |
Removal in expo-iap 7.0
| Deprecated key | Migrate to |
|---|---|
createIapkitConfirmation / createDeviceConfirmationIOS / toIapkitVerificationProps / readIapkitVerification from expo-iap/build/purchase-flow or expo-iap/src/purchase-flow | confirmWithIapkit / confirmOnDeviceIOS from expo-iap, or a vendor-independent confirmWithBackend callback |
IapkitConfirmationOptions / IapkitVerificationProps / IapkitVerificationResult from the Expo purchase-flow deep imports | IapkitConfirmationOptions / VerifyPurchaseWithProviderProps / VerifyPurchaseWithProviderResult from expo-iap; the root types remain supported |
modules.horizon / EXPO_IAP_HORIZON=1 | ORG_GRADLE_PROJECT_openiapStore=horizon in the EAS profile env; a local debug build follows the connected Quest |
EXPO_IAP_FIREOS=1 | modules.amazon.fireOS, which also turns Vega auto-detection off, or ORG_GRADLE_PROJECT_openiapStore=amazon in the EAS profile env |
EXPO_IAP_VEGA=1 | modules.amazon.vegaOS, or a root manifest.toml when Fire OS is not declared |
EXPO_IAP_ONSIDE=1 | modules.onside |
Removal in OpenIap.Maui 4.0
| Deprecated key | Migrate to |
|---|---|
OpenIapAndroidStore | OpenIapStore |
Removal in react-native-iap 18.0
| Deprecated key | Migrate to |
|---|---|
NitroProduct.originalPriceAndroid, originalPriceAmountMicrosAndroid, introductoryPriceValueAndroid, introductoryPriceCyclesAndroid, introductoryPricePeriodAndroid, subscriptionPeriodAndroid, freeTrialPeriodAndroid | subscriptionOffers and discountOffers on the product that fetchProducts returns |
Community providers and Client Protocol 1.0.0#
The October 2026 train adds community store providers. Each change below says who is affected and what to do; upgrade native and framework dependencies together (the release card lists the versions).
Replace the aliases removed in 1.0.0#
Client Protocol 1.0.0 removes the previously deprecated names below. Update imports, hook calls, mocks, and native bindings when upgrading to this release train.
| Removed | Use instead |
|---|---|
openRedeemOfferCodeAndroid / presentCodeRedemptionSheetIOS | openRedeemOfferCode(), which returns a purchase or null |
Godot open_redeem_offer_code_android / present_code_redemption_sheet_ios | await open_redeem_offer_code() |
Apple presentCodeRedemptionSheetResultIOS | openRedeemOfferCode() |
ObjC presentCodeRedemptionSheetIOSWithCompletion: | openRedeemOfferCodeWithCompletion: |
Horizon verification success | isValid |
OpenIapVersion.specVersion | OpenIapVersion.clientProtocolVersion |
A null redemption result can mean the store opened its UI without reporting a purchase immediately, or has no redemption flow. Observe purchase updates and refresh available purchases when the app resumes. React Native, Expo, Flutter, and MAUI now reject with a typed error when the Play redeem page cannot open; earlier versions resolved null. Catch that error. Godot emits purchase_error on failed or unsupported redemption while returning null.
Purchases and verification results require storeId#
What changed: storeId is required at construction on Purchase, PurchaseInput, and RequestVerifyPurchaseWithIapkitResult in TypeScript, Dart, Kotlin purchases, and the new Swift public initializers; C# and the Kotlin IAPKit result infer official ids instead. A community purchase uses store unknown with its own provider id; see Preserve store identity for the full rule. The unknown store spells IapStore.Unknown in Dart, Kotlin, and C#, IapStore.unknown in Swift, IapStore.UNKNOWN in GDScript, and 'unknown' in TypeScript.
Who is affected: apps that hand-build purchase, PurchaseInput, or verification values (fixtures, mocks, custom adapters, server-driven finish flows), and custom types that implement PurchaseCommon, which must add storeId. Passing SDK-returned purchases through needs no change. Swift apps are affected only through decoding and custom PurchaseCommon conformers: base had no public purchase initializer.
What to do: add storeId when constructing the value:
- Saved official JSON without it still decodes to the canonical id, but a community value needs an explicit valid id.
- A mismatched or invalid id fails to decode: TypeScript, Dart, Kotlin, and Swift decoders throw, GDScript
from_dictreturns null (null-check decoded values), and C# deserialization throwsJsonException. A hand-built C# record with a missing community id or a mismatched id throws wheneverStoreIdis read, includingEquals,GetHashCode, and serialization. store_iddefaults to""in GDScript.from_dictrejects a blank id for every store, so a hand-built object round-tripped outsidefinish_transactiondecodes to null. The Godot finish helpers stamp the connected provider's id onto blank finish inputs (a blank id for an official store is stripped instead), and GDScriptPurchaseInputhas nostore_id.
val purchase = PurchaseAndroid(
// ...existing fields,
store = IapStore.Google,
storeId = "play",
)An iOS purchase uses store apple with storeId apple.
KMP Store gains UNKNOWN#
What changed: the public Store enum has a new UNKNOWN case, returned for community-provider builds (the Android provider build and any non-Apple iOS provider) and on iOS when the Apple provider selection fails.
Who is affected: exhaustive when (getStore()) expressions without an else branch, which no longer compile.
What to do: add the UNKNOWN branch.
// kmpIAP is your KmpInAppPurchase instance.
when (kmpIAP.getStore()) {
Store.PLAY_STORE -> TODO("Play")
Store.AMAZON -> TODO("Amazon")
Store.APP_STORE -> TODO("App Store")
Store.HORIZON -> TODO("Horizon")
Store.UNKNOWN -> TODO("community provider")
// NONE is never returned; it only completes the exhaustive when.
Store.NONE -> TODO("no store")
}Kotlin IAPKit result is no longer a data class#
What changed: RequestVerifyPurchaseWithIapkitResult is now a plain class with a private primary constructor. The old 3-, 5-, and 6-argument constructors keep their signatures, but copy lost binary compatibility; component1-3 still exist, and equals/hashCode now compare all seven fields instead of three. PurchaseAndroid gains a required storeId parameter after store (after storefrontCountryCodeIOS on PurchaseIOS), so their constructor and copy signatures change and later componentN indexes shift.
Who is affected: Kotlin callers on OpenIAP Google 4.0.0 and KMP 4.0.0 that construct or copy purchases or results, or decode saved JSON.
What to do: recompile against 4.0.0 and pass storeId to purchase constructors. The result's copy keeps storeId while store is unchanged (a new store resets it to the official id, or throws for Unknown without storeId) and now preserves clientPayload, productId, and environment. A result built with IapStore.Unknown and no storeId still compiles but throws IllegalArgumentException. A purchase's copy keeps its storeId, so pass a matching storeId when you change store. JSON with a missing or unrecognized store value used to decode as Unknown and now throws unless it carries a valid community storeId.
Shared classes move to openiap-core#
What changed: the shared dev.hyo.openiap classes moved into the new openiap-core artifact, which the store artifacts depend on.
Who is affected: apps that link the openiap-google AAR by file.
What to do: add openiap-core next to it; Maven consumers get it transitively.
Apple requires tvOS 16#
What changed: the SwiftPM floor moves from tvOS 15 to tvOS 16. The CocoaPods deployment target already required 16.0.
Who is affected: tvOS apps on OpenIAP Apple 4.0.0 that build through SwiftPM with a 15.x deployment target.
What to do: raise the tvOS deployment target to 16.0.
Cancelled Apple operations throw CancellationError#
What changed: cancelling the calling task of requestPurchase or another StoreKit operation now throws CancellationError with no purchase-error event. Previously requestPurchase, fetchProducts, getPromotedProductIOS, and subscriptionStatusIOS wrapped the cancellation into an emitted PurchaseError, and syncIOS, restorePurchases, and openRedeemOfferCode threw it as a PurchaseError without an event. initConnection still returns false when endConnection interrupts its init task. The SwiftUI store's getAvailablePurchases and getActiveSubscriptions also throw CancellationError, without updating state, when the task is cancelled or endConnection completes while they wait; its initConnection throws it when its task is cancelled, even after connecting.
Who is affected: Swift apps that call OpenIAP from a task that can be cancelled, such as a SwiftUI .task.
What to do: catch CancellationError at the call site alongside the existing PurchaseError path; keep using the listener for real purchase failures. A user dismissing the purchase sheet still reports user-cancelled through the listener.
Flutter initConnection returns the native result on Apple#
What changed: on iOS and macOS initConnection() now returns the native Boolean, false when StoreKit cannot make payments; the plugins returned nil (read as true) before.
Who is affected: Flutter apps that treat the Apple init result as always true.
What to do: branch on the result and show the store as unavailable when it is false.
Android errors carry the provider's code#
What changed: the wrappers forward the provider's Android error code where they returned fixed codes:
- React Native: calls before init already reported
not-prepared; thrown init errors,verifyPurchase, andverifyPurchaseWithProvidernow forward the provider's code, and deep-link andendConnectionerrors arrive as OpenIAP errors. - Expo: Android calls that rejected every failure with
service-errornow forward the provider's code (not-preparedbefore init on Play and Horizon; Amazon queries work before init); thrown init errors and verification forward it too. - Flutter: calls that answered native failures with
service-errornow forward the provider's code (Dart-guarded calls still thrownot-preparedbefore init), and thrown init errors forward it instead ofinit-connection. - Godot: failure results now carry a
codefield (the provider's code, elseservice-error, orpurchase-verification-failedfor verification);verify_purchase_with_providerbefore init now reportsnot-prepared, asrestore_purchasesand the entitlement reads already did;init_connectionstill returns only a Boolean.
On Amazon, getStorefront and IAPKit verification without a userId now throw StoreConnectionFailure instead of InitConnection when the user-data request cannot start (code still init-connection; initConnection still returns false), so Kotlin callers matching by type must handle the new class.
Who is affected: apps that match literal Android error codes or messages.
What to do: handle specific codes such as not-prepared.
import {
ErrorCode,
getAvailablePurchases,
initConnection,
} from 'expo-iap';
import type { PurchaseError } from 'expo-iap';
async function loadPurchases() {
try {
return await getAvailablePurchases();
} catch (error) {
// Before: expo-iap rejected this Android call with service-error.
if ((error as PurchaseError).code !== ErrorCode.NotPrepared) throw error;
const connected = await initConnection();
if (!connected) throw error;
return getAvailablePurchases();
}
}Failed verification reports the native code#
What changed: Flutter reports the native error code from verifyPurchase and verifyPurchaseWithProvider on every platform instead of always purchase-verification-failed; KMP does the same for verify-with-provider on iOS, and Godot on iOS and Android. Generic Android failures in Flutter now report transaction-validation-failed.
Who is affected: apps that match purchase-verification-failed from these calls.
What to do: match the specific codes.
Android restore runs the provider first#
What changed: React Native, Expo, and Flutter run the provider's Android restore before querying ownership. On Horizon each owned purchase then reaches the purchase listeners; Play and Amazon restores deliver nothing by themselves. Godot runs the same restore but emits no purchase_updated signal on Horizon, and a failed restore still emits purchase_error: read owned purchases with get_available_purchases_result. Flutter restore failures on iOS and macOS now read Failed to restore purchases [code]: ... instead of Failed to sync iOS purchases [code]: ... (the code still forwards the native code).
Who is affected: Horizon apps with purchase listeners, and Flutter apps that match the Apple restore failure message.
What to do: make the purchase handler idempotent so a repeated delivery grants once, and keep reading owned purchases after the restore resolves.
import { purchaseUpdatedListener } from 'react-native-iap';
// Horizon redelivers every owned purchase on restore; grantOnce is your code
// and must grant at most once per token against your stored entitlements.
purchaseUpdatedListener((purchase) => {
if (purchase.purchaseState !== 'purchased') return;
void grantOnce(purchase.purchaseToken ?? purchase.id, purchase.productId);
});Godot purchase_error events change#
What changed: a failed Apple restore now emits one purchase_error carrying OpenIAP's code instead of two (sync-error then service-error); verify_purchase now emits purchase_error on failure instead of returning null silently (on Apple every failure, on Android when the native result carries a code). On Apple, get_storefront failures, the products_fetched failure payloads for fetch_products, and the failure results of the iOS-only methods now carry OpenIAP's code instead of service-error or no code.
Who is affected: Godot apps that count restore errors, call verify_purchase or get_storefront, or read failure codes from products_fetched.
What to do: handle one restore event, null-check the verify result alongside the signal, and match specific codes.
Godot export rejects an unknown Android store#
What changed: an unrecognized openiap/android_store value now fails the export instead of falling back to Play.
Who is affected: Godot Android builds with a misspelled store value.
What to do: fix the store value.
iOS subscription checks use the active flag#
What changed: hasActiveSubscriptions on iOS now answers from OpenIAP's active flag (expiration in the future): React Native and Flutter on every call (Flutter on iOS and macOS), KMP and MAUI on id-filtered calls (MAUI also on Mac Catalyst), as Expo, Godot, and native already did. A subscriber in billing grace reads inactive. Flutter hasActiveSubscriptions no longer checks init in Dart: before initConnection, iOS and macOS connect and answer instead of throwing not-prepared, Play and Horizon report the provider's not-prepared, and failures read Failed to check active subscriptions [code]: ... instead of Failed to get active subscriptions [code]: ...; match codes, not messages.
Who is affected: apps that grant access during billing grace, and Flutter apps that match the failure message or call it before initConnection.
What to do: to keep grace access, read the renewal info from getActiveSubscriptions instead of the boolean.
import { getActiveSubscriptions } from 'react-native-iap';
// hasActiveSubscriptions() is false in billing grace; use renewal info
// when grace keeps access.
const subs = await getActiveSubscriptions();
const inGrace = subs.some(
(sub) => (sub.renewalInfoIOS?.gracePeriodExpirationDate ?? 0) > Date.now(),
);2.x → 3.0#
The coordinated major train removes the previously deprecated, OpenIAP-owned compatibility surface. Use this catalog to update calls before upgrading.
Removal boundaries#
Native packages
| Package | Last compatible major | Removed in |
|---|---|---|
| openiap-apple | 2.x | 3.0.0 |
| openiap-google | 2.x | 3.0.0 |
Framework libraries
| Library | Last compatible major | Removed in |
|---|---|---|
| expo-iap | 4.x | 5.0.0 |
| react-native-iap | 15.x | 16.0.0 |
| flutter_inapp_purchase | 9.x | 10.0.0 |
| kmp-iap | 2.x | 3.0.0 |
| maui-iap | 1.x | 2.0.0 |
| godot-iap | 2.x | 3.0.0 |
IAPKit is a hosted service rather than a versioned framework library. Its scoped keys, client payloads, verification, and staged data migrations are unchanged by this SDK-only major train.
Generated Swift, Kotlin, TypeScript, Dart, GDScript, and C# contracts now expose only the canonical schema. The former declarations remain listed below solely as migration reference.
Raw JavaScript objects, plugin configuration, custom MethodChannel payloads, and direct Godot dictionaries must also use canonical keys. Removed aliases are rejected or ignored; they never override missing canonical input.
Flutter purchase payload compatibility#
PurchaseAndroid.dataAndroid is the only public, schema-defined field for Google Play's raw signed purchase JSON. originalJsonAndroid is not a public Purchase field and is never the preferred output key.
Flutter 9.x accepted the following legacy native or custom MethodChannel payload shapes. Flutter 10 accepts only the canonical forms in the middle column.
| Legacy Flutter 9.x input | Emit instead | Platform |
|---|---|---|
originalJsonAndroid | dataAndroid | Android |
purchaseStateAndroid | purchaseState | Android |
transactionStateIOS | purchaseState | iOS |
transactionReceipt | purchaseToken | iOS |
id used as a transactionId fallback | an explicit transactionId; keep id as the purchase identity | Android and iOS |
top-level { sku } for verifyPurchase / validateReceiptIOS | { apple: { sku } } | iOS and macOS |
The canonical id purchase identity is not deprecated. Flutter 10 requires an explicit transactionId.
Issue #248 and Android raw purchase JSON
- Before Flutter 9.6.1, issue #248 caused canonical
dataAndroidinput to be lost by the Dart compatibility converter. - The patch in PR #251 read
dataAndroidfirst and acceptedoriginalJsonAndroidonly as a temporary Flutter 9.x input fallback. If both keys exist,dataAndroidwins. - Flutter 10 removes that fallback. Custom native adapters, MethodChannel fixtures, and mocks must emit
dataAndroid.
See the canonical field reference in PurchaseAndroid.
Flutter 10 package-specific migrations#
In addition to the generated OpenIAP schema surfaces below, flutter_inapp_purchase 10.0.0 removes these Flutter-only compatibility APIs:
| Deprecated Flutter surface | Migrate to |
|---|---|
ReplacementMode / ReplaceMode | AndroidReplacementMode |
TypeInApp | ProductQueryType |
builder replacementMode | subscriptionProductReplacementParams |
builder useAlternativeBilling | InitConnectionConfig.enableBillingProgramAndroid |
purchaseUpdated | purchaseUpdatedListener |
PurchaseResult / purchaseError | PurchaseError / purchaseErrorListener |
ConnectionResult / connectionUpdated | the initConnection result |
requestPurchaseOnPromotedProductIOS | purchasePromoted, then requestPurchase |
Custom MethodChannel integrations
Applications normally use the Dart API and never call these internal channel names. Flutter 10 custom integrations must use the replacements:
| Legacy channel method | Migrate to |
|---|---|
getAvailableItemsByType | getAvailablePurchases |
getPurchaseHistoryByType | getAvailablePurchases for active purchases |
buyItemByType | requestPurchase |
acknowledgePurchase | finishTransaction or acknowledgePurchaseAndroid |
consumeProduct / consumePurchase | finishTransaction with isConsumable |
showInAppMessages | showInAppMessagesAndroid |
getAppTransaction | getAppTransactionIOS |
getSubscriptionStatus | subscriptionStatusIOS |
Custom MethodChannel payloads
The official Dart API emits the canonical forms below. Flutter 10 no longer normalizes the historical custom-channel inputs.
| Legacy payload shape | Emit instead |
|---|---|
product type 'inapp' | 'in-app' |
requestPurchase.request.ios / requestSubscription.request.ios | apple |
requestPurchase.request.android / requestSubscription.request.android | google |
productId / sku used as a product id | id |
discounts / subscription product metadata | discountOffers or subscriptionOffers, plus subscriptionGroupIdIOS when applicable |
subResponseCode | subResponseCodeAndroid |
fetchProducts skuArr / productIds | skus |
offerTokenArr | offerToken for one-time products or subscriptionOffers for subscriptions |
obfuscatedAccountIdAndroid / obfuscatedProfileIdAndroid | obfuscatedAccountId / obfuscatedProfileId |
purchaseTokenAndroid / token | purchaseToken |
finishTransaction transactionIdentifier | transactionId |
replacementModeAndroid / replacementMode | subscriptionProductReplacementParams |
unsuffixed deep-link sku / packageName | skuAndroid / packageNameAndroid |
numeric-indexed iOS SKU maps | { skus: [...] } |
Removed schema migration catalog#
The following OpenIAP-owned schema surfaces were removed by the package versions above.
Validation and storefront APIs
| Deprecated surface | Migrate to |
|---|---|
validateReceipt | verifyPurchase |
validateReceiptIOS | verifyPurchase |
getStorefrontIOS | getStorefront |
requestPurchaseOnPromotedProductIOS | the SDK's promoted-product listener or callback, then requestPurchase |
checkAlternativeBillingAvailabilityAndroid | isBillingProgramAvailableAndroid with the BillingProgramAndroid value your app is enrolled in |
showAlternativeBillingDialogAndroid | showBillingProgramInformationDialogAndroid (the in-app Billing Programs dialog); launchExternalLinkAndroid covers the external-link flows (External Offer, External Content Link, Billing Choice external links) |
createAlternativeBillingTokenAndroid | createBillingProgramReportingDetailsAndroid with the BillingProgramAndroid value your app is enrolled in |
Purchase and connection inputs
| Deprecated surface | Migrate to |
|---|---|
RequestPurchasePropsByPlatforms.ios / RequestSubscriptionPropsByPlatforms.ios | apple |
RequestPurchasePropsByPlatforms.android / RequestSubscriptionPropsByPlatforms.android | google |
useAlternativeBilling | enableBillingProgramAndroid in InitConnectionConfig |
alternativeBillingModeAndroid | enableBillingProgramAndroid |
RequestSubscriptionAndroidProps.replacementMode | subscriptionProductReplacementParams for item-level replacement (Play Billing 8.1+) |
Shared fields and errors
| Deprecated surface | Migrate to |
|---|---|
PurchaseCommon.platform / PurchaseInput.platform / PurchaseIOS.platform / PurchaseAndroid.platform | store |
willExpireSoon | daysUntilExpirationIOS |
presentCodeRedemptionSheetIOS Boolean result | nullable PurchaseIOS result: verified on Apple 27+ with Xcode 27+; null after the system sheet on iOS 15–26 and visionOS 1–26; Catalyst 16–26 fails with OpenIAP error code unknown and Catalyst 15 has no effect |
receipt-failed | purchase-verification-failed |
receipt-finished | purchase-verification-finished |
receipt-finished-failed | purchase-verification-finish-failed |
Offer and billing-program models
| Deprecated surface | Migrate to |
|---|---|
SubscriptionOfferIOS | SubscriptionOffer |
DiscountIOS / DiscountOfferIOS | SubscriptionOffer |
ProductAndroidOneTimePurchaseOfferDetail | DiscountOffer |
ProductSubscriptionAndroidOfferDetails | SubscriptionOffer |
ProductAndroid.oneTimePurchaseOfferDetailsAndroid | ProductAndroid.discountOffers |
ProductSubscriptionAndroid.oneTimePurchaseOfferDetailsAndroid | subscriptionOffers; one-time offer fields do not apply to subscriptions |
ProductSubscriptionAndroid.discountOffers | subscriptionOffers; one-time offer fields do not apply to subscriptions |
ProductAndroid.subscriptionOfferDetailsAndroid / ProductSubscriptionAndroid.subscriptionOfferDetailsAndroid | subscriptionOffers |
ProductIOS.subscriptionInfoIOS / ProductSubscriptionIOS.discountsIOS | subscriptionOffers |
ProductSubscriptionIOS.subscriptionInfoIOS | subscriptionOffers for offers and subscriptionGroupIdIOS for the group identifier |
AlternativeBillingModeAndroid | BillingProgramAndroid |
AlternativeBillingModeAndroid value 'user-choice' | BillingProgramAndroid value 'user-choice-billing' |
AlternativeBillingModeAndroid value 'alternative-only' | BillingProgramAndroid value 'external-offer' |
ExternalOfferAvailabilityResultAndroid | BillingProgramAvailabilityResultAndroid from isBillingProgramAvailableAndroid |
ExternalOfferReportingDetailsAndroid | BillingProgramReportingDetailsAndroid from createBillingProgramReportingDetailsAndroid |
Removed package-specific compatibility shims#
These public aliases and wrappers were package-local rather than GraphQL schema members. They are absent from the major versions named in each heading.
openiap-apple (OpenIAP 3.0)
| Deprecated package surface | Migrate to |
|---|---|
ReceiptValidationProps / ReceiptValidationResult / ReceiptValidationResultIOS | VerifyPurchaseProps / VerifyPurchaseResult / VerifyPurchaseResultIOS |
OpenIapErrorCode / OpenIapEvent / OpenIapPlatform | ErrorCode / IapEvent / IapPlatform |
getStorefrontIOSWithCompletion | getStorefrontWithCompletion |
requestPurchaseOnPromotedProductIOSWithCompletion | promotedProductListenerIOS followed by requestPurchase |
short requestSubscriptionWithSku(_:offer:completion:) overload | the extended overload with compactJWS, promotionalOfferJWS, winBackOfferId, and billingPlanType |
raw/custom purchase id used as a transactionId fallback | an explicit transactionId; keep id as the canonical purchase identity |
OpenIapStore.deepLinkToSubscriptionsIOS | deepLinkToSubscriptions |
OpenIapVersion.gqlVersion / OpenIapVersionInfo.gqlVersion | OpenIapVersion.clientProtocolVersion |
openiap-google (OpenIAP 3.0)
| Deprecated package surface | Migrate to |
|---|---|
ReceiptValidationProps / ReceiptValidationResult / ReceiptValidationResultIOS | VerifyPurchaseProps / VerifyPurchaseResult / VerifyPurchaseResultIOS |
AlternativeBillingMode | BillingProgramAndroid through InitConnectionConfig.enableBillingProgramAndroid |
Play OpenIapModule(context, AlternativeBillingMode, legacy listeners) | OpenIapModule(context), then register listeners and pass InitConnectionConfig.enableBillingProgramAndroid to initConnection |
Play OpenIapModule(context, enableAlternativeBilling) / OpenIapStore(context, enableAlternativeBilling) | construct normally, then pass InitConnectionConfig.enableBillingProgramAndroid to initConnection |
Play OpenIapStore(context, AlternativeBillingMode, userChoiceBillingListener) | OpenIapStore(context), then register listeners and pass InitConnectionConfig.enableBillingProgramAndroid to initConnection |
Amazon OpenIapModule(context, enableAlternativeBilling) | OpenIapModule(context); Amazon ignores the legacy option |
Amazon OpenIapModule(context, AlternativeBillingMode, legacy listeners) | OpenIapModule(context); Amazon ignores the legacy options, then register listeners with add/remove APIs |
Amazon OpenIapStore(context, AlternativeBillingMode, userChoiceBillingListener) | OpenIapStore(context); Amazon ignores the legacy options |
Horizon OpenIapModule / OpenIapStore constructors with AlternativeBillingMode or legacy listeners | OpenIapModule(context) / OpenIapStore(context); Horizon ignores the legacy options |
Horizon manifest keys com.meta.horizon.platform.ovr.OCULUS_APP_ID / com.meta.horizon.platform.ovr.HORIZON_APP_ID / com.oculus.vr.APP_ID | com.meta.horizon.platform.HORIZON_APP_ID |
setUserChoiceBillingListener / setDeveloperProvidedBillingListener | the corresponding add/remove listener APIs |
UserChoiceDetails / UserChoiceBillingListener | UserChoiceBillingDetails / OpenIapUserChoiceBillingListener |
DeveloperProvidedBillingDetails / DeveloperProvidedBillingListener | DeveloperProvidedBillingDetailsAndroid / OpenIapDeveloperProvidedBillingListener |
OpenIapStore.connectionStatus | OpenIapStore.isConnected |
OpenIapError.InvalidReceipt | OpenIapError.InvalidPurchaseVerification |
checkAlternativeBillingAvailability | isBillingProgramAvailable with BillingProgramAndroid.ExternalOffer |
showAlternativeBillingInformationDialog | launchExternalLink |
createAlternativeBillingReportingToken | createBillingProgramReportingDetails with BillingProgramAndroid.ExternalOffer |
OpenIapLog.d / i / w / e | debug / info / warn / error |
react-native-iap 16.0.0
| Deprecated package surface | Migrate to |
|---|---|
ProductTypeInput 'inapp' | 'in-app' |
request.ios / request.android | request.apple / request.google |
replacementMode | subscriptionProductReplacementParams |
useIAP().alternativeBillingModeAndroid | enableBillingProgramAndroid |
acknowledgePurchase | acknowledgePurchaseAndroid |
consumePurchase | consumePurchaseAndroid |
requestPromotedProductIOS | getPromotedProductIOS |
getReceiptIOS | getReceiptDataIOS |
requestPurchaseOnPromotedProductIOS | promotedProductListenerIOS, then requestPurchase |
useIAP().requestPurchaseOnPromotedProductIOS | onPromotedProductIOS, then requestPurchase |
expo-iap 5.0.0
| Deprecated package surface | Migrate to |
|---|---|
ProductTypeInput 'inapp' | 'in-app' |
request.ios / request.android | request.apple / request.google |
Android custom-channel skuArr | skus |
Android custom-channel offerTokenArr | subscriptionOffers for subscriptions |
replacementMode | subscriptionProductReplacementParams |
useIAP().alternativeBillingModeAndroid | enableBillingProgramAndroid |
acknowledgePurchase | acknowledgePurchaseAndroid |
consumePurchase | consumePurchaseAndroid |
getReceiptIOS | getReceiptDataIOS |
validateReceiptAndroid | verifyPurchase |
Android deep-link sku / packageName | skuAndroid / packageNameAndroid |
requestPurchaseOnPromotedProductIOS | promotedProductListenerIOS, then requestPurchase |
useIAP().requestPurchaseOnPromotedProductIOS | onPromotedProductIOS, then requestPurchase |
config.iosAlternativeBilling | config.ios.alternativeBilling |
config.horizonAppId / config.android.horizonAppId | config.android.horizon.appId |
config.android.amazon.fireOS / boolean config.android.amazon.vegaOS | config.modules.amazon.fireOS / config.modules.amazon.vegaOS |
godot-iap 3.0.0
| Deprecated package surface | Migrate to |
|---|---|
godot-iap get_storefront_ios | get_storefront |
godot-iap validate_receipt_ios / validate_receipt | verify_purchase |
godot-iap request_purchase_on_promoted_product_ios | promoted_product_ios, then request_purchase |
godot-iap check_alternative_billing_availability_android | is_billing_program_available_android with BillingProgramAndroid.EXTERNAL_OFFER |
godot-iap show_alternative_billing_dialog_android | launch_external_link_android |
godot-iap create_alternative_billing_token_android | create_billing_program_reporting_details_android with BillingProgramAndroid.EXTERNAL_OFFER |
flattened verify_purchase_with_provider IAPKit keys | keep provider at the top level and nest apiKey, baseUrl, includeClientPayload, apple, google, and amazon under iapkit |
ProductQueryType 'inapp' / 'in_app' | 'in-app' |
ProductQueryType 'subscription' | 'subs' |
raw request selector and ios / android purchase envelopes | requestPurchase or requestSubscription with apple / google |
raw offer_token | offerToken |
raw obfuscatedAccountIdAndroid / obfuscatedProfileIdAndroid / purchaseTokenAndroid | the corresponding unsuffixed Google request keys |
raw replacementModeAndroid / replacementMode | subscriptionProductReplacementParams |
raw skuArr / numeric-indexed iOS SKU maps | skus |
raw offerTokenArr | offerToken for a one-time product or subscriptionOffers for a subscription |
Android native requestPurchaseJson | requestPurchase |
iOS simple requestPurchase(sku:) / top-level sku request | requestPurchaseWithPayload using an apple request envelope |
kmp-iap 3.0.0
| Deprecated package surface | Migrate to |
|---|---|
kmp-iap requestPurchaseOnPromotedProductIOS | promotedProductListener, then requestPurchase |
kmp-iap getStorefrontIOS | getStorefront |
kmp-iap validateReceiptIOS / validateReceipt | verifyPurchase |
PurchaseRequestBuilder.ios / PurchaseRequestBuilder.android | PurchaseRequestBuilder.apple / PurchaseRequestBuilder.google |
AndroidOptionsBuilder.replacementMode | subscriptionProductReplacementParams |
generated RequestPurchasePropsByPlatforms.ios / .android and RequestSubscriptionPropsByPlatforms.ios / .android | apple / google |
generated RequestPurchaseProps.useAlternativeBilling / InitConnectionConfig.alternativeBillingModeAndroid | InitConnectionConfig.enableBillingProgramAndroid |
generated RequestSubscriptionAndroidProps.replacementMode | subscriptionProductReplacementParams |
OpenIap.Maui 2.0.0
| Deprecated package surface | Migrate to |
|---|---|
OpenIap.Maui Iap facade | OpenIapClient |
net9.0, net9.0-android, net9.0-ios, and net9.0-maccatalyst targets | the matching net10.0 target frameworks with the .NET 10 MAUI workload |
RequestPurchaseOnPromotedProductIOSAsync | PromotedProductIOS, then RequestPurchaseAsync |
What this schedule does not remove#
The schedule applies to OpenIAP-owned deprecated schema members and explicit compatibility shims. It does not automatically remove:
- redirects kept so existing documentation links continue to work;
- documentation that describes an upstream StoreKit or Play Billing legacy technology still supported by the stores; or
- StoreKit, Play Billing, Amazon, or Horizon response-shape normalization, including upstream names such as
productIdentifier,localizedPrice, and historical receipt payload labels; - internal React Native, Expo, KMP, or Godot recovery of native response fields that applications do not author;
- input normalization that accepts historical error-code spellings;
- safe fallbacks used when an operating-system version does not support a newer store API; or
- staged IAPKit storage migrations with their own retention and rollback requirements.
Check the release notes before every major upgrade for the final removal list and package-specific migration steps.