2.8.0 Migration Guide - iOS Field Naming Convention Update
Breaking Changes
Version 2.8.0 introduces naming convention changes:
- iOS suffix convention: Fields with iOS suffixes now use uppercase
IOS
instead ofIos
- ID suffix convention: All fields ending with
ID
now useId
instead for consistency (e.g.,subscriptionGroupID
→subscriptionGroupId
,bundleID
→bundleId
)
Note: Android field names remain unchanged as they already follow the correct convention (e.g., autoRenewingAndroid
, purchaseTokenAndroid
).
What Changed
iOS Changes
Product Types
ProductIOS & ProductSubscriptionIOS:
displayName
- Product display nameisFamilyShareable
- Family sharing availabilityjsonRepresentation
- JSON representation of productintroductoryPriceNumberOfPeriodsIOS
- Introductory price period countintroductoryPriceSubscriptionPeriodIOS
- Introductory price periodintroductoryPriceAsAmountIOS
- Introductory price amountintroductoryPricePaymentModeIOS
- Introductory price payment modesubscriptionPeriodNumberIOS
- Subscription period numbersubscriptionPeriodUnitIOS
- Subscription period unit
SubscriptionInfo:
subscriptionGroupId
- Subscription group identifier (changed fromsubscriptionGroupID
)
Purchase Types
ProductPurchaseIOS includes these StoreKit 2 fields:
quantityIOS
,originalTransactionDateIOS
,originalTransactionIdentifierIOS
expirationDateIOS
,webOrderLineItemIdIOS
,environmentIOS
storefrontCountryCodeIOS
,appBundleIdIOS
,productTypeIOS
subscriptionGroupIdIOS
,isUpgradedIOS
,ownershipTypeIOS
reasonIOS
,reasonStringRepresentationIOS
,transactionReasonIOS
revocationDateIOS
,revocationReasonIOS
,offerIOS
priceIOS
,currencyIOS
,jwsRepresentationIOS
(deprecated)
AppTransactionIOS (iOS 16.0+):
appTransactionId
- App transaction identifier (changed fromappTransactionID
)bundleId
- Bundle identifier (changed frombundleID
)appId
- App identifier (changed fromappID
)appVersionId
- App version identifier (changed fromappVersionID
)
Breaking Changes - Field Renaming:
iOS Suffix Changes (Ios → IOS)
Old Field Name | New Field Name |
---|---|
quantityIos | quantityIOS |
originalTransactionDateIos | originalTransactionDateIOS |
originalTransactionIdentifierIos | originalTransactionIdentifierIOS |
appBundleIdIos | appBundleIdIOS |
productTypeIos | productTypeIOS |
subscriptionGroupIdIos | subscriptionGroupIdIOS |
webOrderLineItemIdIos | webOrderLineItemIdIOS |
expirationDateIos | expirationDateIOS |
isUpgradedIos | isUpgradedIOS |
ownershipTypeIos | ownershipTypeIOS |
revocationDateIos | revocationDateIOS |
revocationReasonIos | revocationReasonIOS |
transactionReasonIos | transactionReasonIOS |
environmentIos | environmentIOS |
storefrontCountryCodeIos | storefrontCountryCodeIOS |
reasonIos | reasonIOS |
offerIos | offerIOS |
priceIos | priceIOS |
currencyIos | currencyIOS |
jwsRepresentationIos | jwsRepresentationIOS |
reasonStringRepresentationIos | reasonStringRepresentationIOS |
ID Suffix Changes (ID → Id)
Old Field Name | New Field Name | Type/Context |
---|---|---|
subscriptionGroupID | subscriptionGroupId | SubscriptionInfo |
appTransactionID | appTransactionId | AppTransactionIOS |
bundleID | bundleId | AppTransactionIOS |
appID | appId | AppTransactionIOS |
appVersionID | appVersionId | AppTransactionIOS |
Function Parameter Changes
Function | Old Parameter | New Parameter |
---|---|---|
isEligibleForIntroOfferIOS | groupID | groupId |
Android Changes
Product Types
ProductAndroid & ProductSubscriptionAndroid:
name
- Product display nameoneTimePurchaseOfferDetails
- One-time purchase offer detailssubscriptionOfferDetails
- Subscription offer details array
Purchase Types
ProductPurchaseAndroid includes these Android Billing Library fields:
ids
- Array of product IDspurchaseTokenAndroid
- Android purchase token (deprecated, usepurchaseToken
)dataAndroid
- Purchase datasignatureAndroid
- Purchase signatureautoRenewingAndroid
- Auto-renewal statuspurchaseStateAndroid
- Purchase state enumisAcknowledgedAndroid
- Acknowledgment statuspackageNameAndroid
- App package namedeveloperPayloadAndroid
- Developer payloadobfuscatedAccountIdAndroid
- Obfuscated account IDobfuscatedProfileIdAndroid
- Obfuscated profile ID
Request Props
RequestPurchaseAndroidProps:
isOfferPersonalized
- For Android Billing V5 personalized pricing
No Breaking Changes - All Android fields maintain existing naming convention.
How to Migrate
Step 1: Update Field References
Search your codebase for any references to the old field names and update them:
// Before (2.7.x)
const purchase = await requestPurchase({sku: 'product-id'});
if (purchase.expirationDateIos) {
console.log('Expires:', purchase.expirationDateIos);
}
// After (2.8.0)
// Note: requestPurchase API signature has also changed in 2.8.0
const purchase = await requestPurchase({
request: {
ios: {sku: 'product-id'},
android: {skus: ['product-id']},
},
type: 'in-app',
});
if (purchase.expirationDateIOS) {
console.log('Expires:', purchase.expirationDateIOS);
}
Bug Fixes and Operational Notes
- iOS Hot Reload Fix (2.7.7): This version addresses critical failures during React Native's fast refresh on iOS, which could leave StoreKit in an inconsistent state. Previously, concurrent operations like
Promise.all([fetchProducts(...), getAvailablePurchases()])
would fail during hot reloads. The native module now cleans up its state duringinitConnection()
, validates connections withensureConnection()
across all public APIs (similar to Android), and properly manages StoreKit resources. No code changes are needed to benefit from this fix, butexpo-iap@2.7.7+
is required. - Metro bundling resolution (2.9.6): Fixed an ambiguous import in the hook implementation that could cause Metro to attempt to resolve
../../..
and crash. The import now explicitly targets./index
.
v2.7.3 Release — Google Play Billing Library v8.0.0 Support
We shipped v2.7.3 to add full support for Google Play Billing Library v8.0.0. This ensures compatibility with the latest Android billing features and improves error diagnostics.
What's New
- Upgraded Google Play Billing from v7.0.0 to v8.0.0
- Enhanced error handling with sub-response codes for better debugging
- API compatibility updates for new callback signatures
Key Changes
- Updated
queryProductDetailsAsync
callback to useQueryProductDetailsResult
- Improved
enablePendingPurchases
configuration - Removed deprecated, hardcoded Kotlin version constraints
Breaking Changes
Android Kotlin version requirement: Play Billing v8.0.0 requires Kotlin 2.0+. Because expo-modules-core
did not yet support Kotlin v2 at the time, you must set Kotlin 2.0.21 explicitly via expo-build-properties
:
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"android": {
"kotlinVersion": "2.0.21"
}
}
]
]
}
}
Installation
If you're upgrading to v2.7.3:
npx expo install expo-iap@2.7.3
Don't forget to add the expo-build-properties
configuration to your app.json if you haven't already.
Step 2: Update Type Imports and Declarations
Type names have also been updated to use uppercase IOS
:
// Before (2.7.x)
import {
ProductIOS,
ProductPurchaseIos,
ProductSubscriptionIOS,
ProductStatusIos,
} from 'expo-iap';
// After (2.8.0)
import {
ProductIOS,
ProductPurchaseIOS,
ProductSubscriptionIOS,
ProductStatusIOS,
} from 'expo-iap';
Note: The old type names are still available as deprecated aliases for backward compatibility, but we recommend updating to the new names.
Step 3: Update Type Checks
If you're using TypeScript and checking for iOS-specific fields:
// Before (2.7.x)
if ('expirationDateIos' in purchase) {
// Handle subscription
}
// After (2.8.0)
if ('expirationDateIOS' in purchase) {
// Handle subscription
}
Step 4: Update ID Field References
Update all ID field references to use Id
instead:
// Before (2.7.x)
const appTransaction = await getAppTransactionIOS();
console.log(appTransaction.bundleID);
console.log(appTransaction.appID);
// After (2.8.0)
const appTransaction = await getAppTransactionIOS();
console.log(appTransaction.bundleId);
console.log(appTransaction.appId);
Step 5: Update Subscription Helpers
If you're using the subscription helper functions:
// Before (2.7.x)
const subscription = {
expirationDateIos: purchase.expirationDateIos,
environmentIos: purchase.environmentIos,
};
// After (2.8.0)
const subscription = {
expirationDateIOS: purchase.expirationDateIOS,
environmentIOS: purchase.environmentIOS,
};
Quick Migration Script
You can use this regex find/replace pattern in your IDE to quickly update most occurrences:
Find Pattern (Regex):
\b(\w+)(Ios)\b
Replace Pattern:
$1IOS
⚠️ Note: Review each replacement carefully as this might affect non-field references.
Why This Change?
These changes align with widely-adopted naming conventions:
- iOS suffix: Acronyms at the end of identifiers are written in uppercase (e.g.,
dataIOS
,configIOS
) - ID suffix: The
Id
convention is more common in modern JavaScript/TypeScript codebases (e.g.,userId
,productId
,transactionId
)
This makes the codebase more consistent and follows best practices in the TypeScript/JavaScript ecosystem.
Need Help?
If you encounter any issues during migration:
- Check our GitHub Issues
- Join our Slack community
- Review the full documentation