2.7.0 - New Platform API & Google Play Billing v8
We're excited to announce expo-iap v2.7.0! This release includes a cleaner platform-specific API for handling purchases and full support for Google Play Billing Library v8.0.0.
๐ฏ New Platform-Specific APIโ
The Problemโ
Previously, developers had to write conditional logic to handle platform differences:
// Old approach - requires platform checks
if (Platform.OS === 'ios') {
await requestPurchase({
request: {sku: productId},
});
} else {
await requestPurchase({
request: {skus: [productId]},
});
}
This approach had several issues:
- Required manual platform checks
- Easy to miss platform-specific parameters
- TypeScript couldn't provide proper platform-specific type hints
The Solution: Platform-Specific Parametersโ
The new API introduces a cleaner structure with explicit platform parameters:
// New approach - clear platform separation
await requestPurchase({
request: {
ios: {
sku: productId,
appAccountToken: 'user-123',
},
android: {
skus: [productId],
obfuscatedAccountIdAndroid: 'user-123',
},
},
});
Key Benefitsโ
- Better Type Safety: TypeScript now provides accurate autocompletion for each platform's specific parameters
- Clearer Code Structure: Platform-specific logic is clearly separated
- Backward Compatibility: The old API still works, so you can migrate at your own pace
Migration Examplesโ
Basic Product Purchaseโ
Before:
const buyProduct = async (productId: string) => {
if (Platform.OS === 'ios') {
await requestPurchase({
request: {sku: productId},
});
} else {
await requestPurchase({
request: {skus: [productId]},
});
}
};
After:
const buyProduct = async (productId: string) => {
await requestPurchase({
request: {
ios: {sku: productId},
android: {skus: [productId]},
},
});
};
Subscription Purchaseโ
Before:
const buySubscription = async (subId: string) => {
if (Platform.OS === 'ios') {
await requestPurchase({
request: {
sku: subId,
appAccountToken: 'user-123',
},
type: 'subs',
});
} else {
const subscription = subscriptions.find((s) => s.id === subId);
const offer = subscription?.subscriptionOfferDetails?.[0];
await requestPurchase({
request: {
skus: [subId],
subscriptionOffers: [
{
sku: subId,
offerToken: offer?.offerToken || '',
},
],
},
type: 'subs',
});
}
};
After:
const buySubscription = async (subId: string) => {
const subscription = subscriptions.find((s) => s.id === subId);
await requestPurchase({
request: {
ios: {
sku: subId,
appAccountToken: 'user-123',
},
android: {
skus: [subId],
subscriptionOffers:
subscription?.subscriptionOfferDetails?.map((offer) => ({
sku: subId,
offerToken: offer.offerToken,
})) || [],
},
},
type: 'subs',
});
};
Platform-Specific Parametersโ
iOS Parametersโ
{
ios: {
sku: string; // Required: Product SKU
andDangerouslyFinishTransactionAutomatically?: boolean;
appAccountToken?: string; // For server validation
quantity?: number; // For bulk purchases
withOffer?: PaymentDiscount; // For discounts
}
}
Android Parametersโ
{
android: {
skus: string[]; // Required: Product SKUs
obfuscatedAccountIdAndroid?: string; // User identifier
obfuscatedProfileIdAndroid?: string; // Profile identifier
isOfferPersonalized?: boolean; // For personalized pricing
// For subscriptions only:
subscriptionOffers?: Array<{
sku: string;
offerToken: string;
}>;
purchaseTokenAndroid?: string; // For upgrades/downgrades
replacementModeAndroid?: number; // Proration mode
}
}
๐ Deprecation Notice: requestSubscriptionโ
Starting from v2.7.0, requestSubscription
is deprecated in favor of using requestPurchase
with type: 'subs'
. This unifies our API and makes the codebase cleaner.
Migration example:
// Old way (deprecated)
await requestSubscription({
sku: subscriptionId,
skus: [subscriptionId],
});
// New way (recommended)
await requestPurchase({
request: {
ios: {sku: subscriptionId},
android: {skus: [subscriptionId]},
},
type: 'subs',
});
๐ค Google Play Billing Library v8.0.0 Supportโ
We've updated to Google Play Billing Library v8.0.0 to meet Google Play's latest requirements. For more details on the migration, see Google's official migration guide.
Key Changesโ
1. Updated Dependenciesโ
The Android module now uses the latest Google Play Billing Library:
implementation "com.android.billingclient:billing-ktx:8.0.0"
2. Removed Deprecated Methodsโ
getPurchaseHistory is no longer available on Android
Google Play Billing Library v8 has removed the queryPurchaseHistoryAsync()
method. The getPurchaseHistories()
function will now return an empty array on Android with a console warning:
// Before v8
const history = await getPurchaseHistories(); // Returns purchase history
// After v8
const history = await getPurchaseHistories(); // Returns [] on Android with warning
// Use getAvailablePurchases() instead for active purchases
3. Automatic Service Reconnectionโ
The library now includes automatic service reconnection support, improving reliability when the billing service disconnects unexpectedly.
4. Sub-Response Codesโ
The library now provides more detailed error information through sub-response codes:
// Error object now includes sub-response codes
{
responseCode: 6, // ERROR
debugMessage: "Error processing purchase",
subResponseCode: 1, // PAYMENT_DECLINED_DUE_TO_INSUFFICIENT_FUNDS
subResponseMessage: "Payment declined due to insufficient funds"
}
Breaking Changes Summaryโ
getPurchaseHistory()
removed - UsegetAvailablePurchases()
insteadquerySkuDetailsAsync()
removed - Already migrated toqueryProductDetailsAsync()
enablePendingPurchases()
signature changed - Now requiresPendingPurchasesParams
queryPurchasesAsync(skuType)
removed - UsequeryPurchasesAsync(QueryPurchasesParams)
instead
Migration Guide for getPurchaseHistoryโ
If you're using getPurchaseHistory()
or getPurchaseHistories()
on Android:
// Old approach
const history = await getPurchaseHistories();
// New approach - use getAvailablePurchases for active purchases
const activePurchases = await getAvailablePurchases();
๐ Best Practicesโ
-
Handle Platform Availability: Not all parameters need to be set for both platforms
await requestPurchase({
request: {
ios: {sku: productId},
android: {skus: [productId]},
},
}); -
Use TypeScript: Let TypeScript guide you with autocompletion
await requestPurchase({
request: {
ios: {
// TypeScript will show iOS-specific options
},
android: {
// TypeScript will show Android-specific options
},
},
}); -
Gradual Migration: The old API still works, migrate at your own pace
๐ฆ Upgradingโ
To upgrade to version 2.7.0:
npm install expo-iap@2.7.0
# or
yarn add expo-iap@2.7.0
# or
bun add expo-iap@2.7.0
Requirementsโ
- Android Gradle Plugin 4.0 or higher
- Kotlin 1.6 or higher
- JVM target 17 (automatically configured)
- Google Play's latest billing requirements (deadline: August 31, 2025)
๐ Benefitsโ
- Cleaner Code: No more Platform.OS checks in your purchase logic
- Better Type Safety: Platform-specific TypeScript hints
- Future-Proof: Compliance with Google Play's latest requirements
- Improved Reliability: Automatic service reconnection on Android
- Enhanced Error Handling: Detailed sub-response codes
๐ Documentationโ
- Check our updated documentation
- View complete examples
- Join our community discussions
๐ฌ Feedbackโ
If you encounter any issues with this update, please open an issue on our GitHub repository.
Happy coding! ๐