Skip to main content

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:

  1. Better caching: Products and subscriptions are cached more efficiently
  2. Reduced re-renders: The hook is optimized to minimize unnecessary re-renders
  3. Memory management: Better cleanup of listeners and connections

Getting Help

If you encounter issues during migration:

  1. Check the troubleshooting guide
  2. Review the FAQ
  3. 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:

  1. Review the new features available in expo-iap
  2. Consider implementing enhanced error handling
  3. Explore performance optimizations
  4. Update your testing procedures if needed