Migration from react-native-iap
This guide helps you migrate from react-native-iap
to expo-iap
. While the APIs are similar, there are some key differences and improvements in expo-iap
.
Key Differences
Package Installation
# Before (react-native-iap)
npm install react-native-iap
# After (expo-iap)
npm install expo-iap
Hook Usage
react-native-iap:
import {useIAP, withIAPContext} from 'react-native-iap';
// Had to wrap app with context
const AppWithIAP = withIAPContext(App);
function App() {
const {connected, products, getProducts} = useIAP();
// ...
}
expo-iap:
import {useIAP} from 'expo-iap';
// No context wrapper needed
function App() {
const {connected, products, getProducts} = useIAP();
// Connection and listeners are automatically managed
}
Error Handling
react-native-iap:
try {
await requestPurchase({sku: 'product_id'});
} catch (error) {
console.error(error.code, error.message);
}
expo-iap:
import {IAPError} from 'expo-iap';
try {
await requestPurchase({sku: 'product_id'});
} catch (error) {
if (error instanceof IAPError) {
// Enhanced error handling with better typing
console.error(error.code, error.message, error.platform);
}
}
Step-by-Step Migration
1. Update Dependencies
Remove react-native-iap and install expo-iap:
npm uninstall react-native-iap
npm install expo-iap
# For iOS
cd ios && pod install && cd ..
2. Update Imports
Replace all react-native-iap imports:
// Before
import {
initConnection,
getProducts,
requestPurchase,
useIAP,
withIAPContext,
} from 'react-native-iap';
// After
import {
initConnection,
getProducts,
requestPurchase,
useIAP,
// withIAPContext not needed
} from 'expo-iap';
3. Remove Context Wrapper
Before:
import {withIAPContext} from 'react-native-iap';
const App = () => {
return <YourAppContent />;
};
export default withIAPContext(App);
After:
// No wrapper needed
const App = () => {
return <YourAppContent />;
};
export default App;
4. Update Hook Usage
The useIAP
hook signature is mostly the same, but with better TypeScript support:
Before:
const {
connected,
products,
subscriptions,
purchaseHistory,
availablePurchases,
currentPurchase,
currentPurchaseError,
finishTransaction,
getProducts,
getSubscriptions,
} = useIAP();
After:
const {
connected,
products,
subscriptions,
purchaseHistory,
availablePurchases,
currentPurchase,
currentPurchaseError,
finishTransaction,
getProducts,
getSubscriptions,
// Additional methods and better typing
} = useIAP();
5. Update Error Handling
Enhance error handling with the new error types:
Before:
useEffect(() => {
if (currentPurchaseError) {
console.error('Purchase error:', currentPurchaseError);
}
}, [currentPurchaseError]);
After:
import {IAPError} from 'expo-iap';
useEffect(() => {
if (currentPurchaseError) {
if (currentPurchaseError instanceof IAPError) {
switch (currentPurchaseError.code) {
case 'E_USER_CANCELLED':
// Handle user cancellation
break;
case 'E_NETWORK_ERROR':
// Handle network error
break;
default:
console.error('Purchase error:', currentPurchaseError);
}
}
}
}, [currentPurchaseError]);
API Changes
Method Signatures
Most method signatures remain the same, but with improved TypeScript definitions:
// Both libraries have the same signature
await getProducts({skus: ['product1', 'product2']});
await requestPurchase({sku: 'product_id'});
await finishTransaction({purchase});
New Methods
expo-iap includes some additional utility methods:
import {validateReceiptIos, validateReceiptAndroid} from 'expo-iap';
// Platform-specific receipt validation helpers
const isValidIos = await validateReceiptIos({
receiptBody: purchase.transactionReceipt,
password: 'your_shared_secret',
});
const isValidAndroid = await validateReceiptAndroid({
packageName: 'com.yourapp',
productId: purchase.productId,
productToken: purchase.purchaseToken,
accessToken: 'your_access_token',
});
Testing Migration
1. Test Basic Functionality
Create a simple test to ensure basic functionality works:
import {useIAP} from 'expo-iap';
export default function MigrationTest() {
const {connected, getProducts} = useIAP();
useEffect(() => {
if (connected) {
console.log('✅ Connection successful');
getProducts({skus: ['test_product']})
.then((products) => {
console.log('✅ Products fetched:', products.length);
})
.catch((error) => {
console.error('❌ Product fetch failed:', error);
});
}
}, [connected, getProducts]);
return (
<View>
<Text>Migration Test</Text>
<Text>Connected: {connected ? '✅' : '❌'}</Text>
</View>
);
}
2. Test Purchase Flow
Test the complete purchase flow:
const testPurchaseFlow = async () => {
try {
// 1. Fetch products
const products = await getProducts({skus: ['test_product']});
console.log('✅ Products fetched');
// 2. Request purchase
await requestPurchase({sku: 'test_product'});
console.log('✅ Purchase requested');
// 3. Purchase handling will be automatic with useIAP
} catch (error) {
console.error('❌ Purchase flow failed:', error);
}
};
3. Test Error Scenarios
Ensure error handling transitions properly:
const testErrorHandling = () => {
// Test with invalid product ID
getProducts({skus: ['invalid_product']})
.then((products) => {
if (products.length === 0) {
console.log('✅ Empty products handled correctly');
}
})
.catch((error) => {
console.log('✅ Error handled:', error.code);
});
};
Common Migration Issues
1. Context Not Working
If you're getting context errors:
// ❌ This is no longer needed
import {withIAPContext} from 'expo-iap';
// ✅ Just use the hook directly
import {useIAP} from 'expo-iap';
2. TypeScript Errors
Update your TypeScript types if you were using custom interfaces:
// Before
import {Product, Purchase} from 'react-native-iap';
// After
import {Product, Purchase} from 'expo-iap';
3. Purchase Listeners
If you were using manual listeners, consider switching to the hook:
Before:
import {purchaseUpdatedListener} from 'react-native-iap';
useEffect(() => {
const subscription = purchaseUpdatedListener((purchase) => {
// Handle purchase
});
return () => subscription.remove();
}, []);
After (using hook):
const {currentPurchase} = useIAP();
useEffect(() => {
if (currentPurchase) {
// Handle purchase automatically
}
}, [currentPurchase]);
Performance Improvements
expo-iap includes several performance improvements:
- Better caching: Products and subscriptions are cached more efficiently
- Reduced re-renders: The hook is optimized to minimize unnecessary re-renders
- Memory management: Better cleanup of listeners and connections
Getting Help
If you encounter issues during migration:
- Check the troubleshooting guide
- Review the FAQ
- Create a GitHub issue with:
- Your previous react-native-iap version
- The migration step where you got stuck
- Error messages and relevant code
Next Steps
After successful migration:
- Review the new features available in expo-iap
- Consider implementing enhanced error handling
- Explore performance optimizations
- Update your testing procedures if needed