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
One section per coordinated major train. Each train lists the versions that drop the previously deprecated, OpenIAP-owned compatibility surface and the canonical call to use instead.
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 throws StoreKitError.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-apple 3.5.0; specVersion stays as a deprecated alias until client protocol 1.0.0) |
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.