Purchases
tip
For complete working examples, see Purchase Flow and Subscription Flow.
Key Concepts
- Event-driven: Purchases are handled through callbacks, not promises
- Asynchronous: Purchases may complete after your app is closed
- Validation required: Always validate receipts on your server
- Finish transactions: Always call
finishTransaction()after validation
Recommended: useIAP Hook
const {products, requestPurchase, finishTransaction} = useIAP({
onPurchaseSuccess: async (purchase) => {
// Validate on your server, then finish
await finishTransaction({purchase, isConsumable: true});
},
onPurchaseError: (error) => {
if (error.code !== ErrorCode.UserCancelled) {
Alert.alert('Error', error.message);
}
},
});
Direct Listeners
useEffect(() => {
initConnection().then(() => {
const purchaseUpdate = purchaseUpdatedListener((purchase) => {
handlePurchaseUpdate(purchase);
});
const purchaseError = purchaseErrorListener((error) => {
console.log('purchaseErrorListener', error);
});
return () => {
purchaseUpdate.remove();
purchaseError.remove();
};
});
}, []);
Request Purchase
Products (In-App)
await requestPurchase({
request: {
ios: {sku: 'product_id'},
android: {skus: ['product_id']},
},
type: 'in-app',
});
Subscriptions
const subscription = subscriptions.find((s) => s.id === 'sub_id');
await requestPurchase({
request: {
ios: {sku: 'sub_id'},
android: {
skus: ['sub_id'],
subscriptionOffers:
subscription?.subscriptionOfferDetailsAndroid?.map((offer) => ({
sku: 'sub_id',
offerToken: offer.offerToken,
})) || [],
},
},
type: 'subs',
});
Platform Differences
| Feature | iOS | Android |
|---|---|---|
| SKU format | Single sku | Array skus |
| Subscriptions | Simple SKU | Requires subscriptionOffers |
| Expiration | expirationDateIOS | autoRenewingAndroid |
Finish Transaction
// Consumable (can be purchased again)
await finishTransaction({purchase, isConsumable: true});
// Non-consumable or subscription
await finishTransaction({purchase, isConsumable: false});
Error Handling
import {ErrorCode} from 'react-native-iap';
if (error.code === ErrorCode.UserCancelled) return;
if (error.code === ErrorCode.NetworkError) showRetry();
if (error.code === ErrorCode.ItemUnavailable) showUnavailable();
Purchase Verification
Always validate on your server:
// Send to your server for validation
const isValid = await yourAPI.validateReceipt({
purchaseToken: purchase.purchaseToken,
productId: purchase.productId,
});
if (isValid) {
await finishTransaction({purchase});
}
