Expo IAP
Expo IAP is a powerful in-app purchase solution for Expo and React Native applications that conforms to the Open IAP specification. It provides a unified API for handling in-app purchases across iOS and Android platforms with comprehensive error handling and modern TypeScript support.
If you're shipping an app with expo-iap, we’d love to hear about it—please share your product and feedback in Who’s using Expo IAP?. Community stories help us keep improving the ecosystem.
Sponsors & Community Support
We're building the OpenIAP ecosystem—defining the spec at openiap.dev, maintaining OpenIAP for the shared type system, and shipping native SDKs such as openiap-apple and openiap-google. These modules power expo-iap, flutter_inapp_purchase, kmp-iap, and react-native-iap. After simplifying fragmented APIs, the next milestone is a streamlined purchase flow: initConnection → fetchProducts → requestPurchase → (server receipt validation) → finishTransaction
.
Your sponsorship keeps this work moving—ensuring more developers across platforms, OS, and frameworks can implement IAPs without headaches while we expand to additional plugins and payment systems. Sponsors receive shout-outs in each release and, depending on tier, can request tailored support. If you’re interested—or have rollout feedback to share—you can view sponsorship options at openiap.dev/sponsors.
📚 Guides
- Installation: Complete guide to implementing in-app purchases
- Purchase Lifecycle: Understanding connection management and best practices
- Purchase Implementation: Detailed purchase flow and event handling
- FAQ: Frequently asked questions and solutions
- Support: Getting help and community resources
🚀 Quick Start
Installation
Install the package using your favorite package manager:
npm install expo-iap
1. Basic Setup
First, import and initialize the IAP hook:
import {useIAP} from 'expo-iap';
function MyStore() {
const {
connected,
products,
fetchProducts,
requestPurchase,
finishTransaction,
} = useIAP({
onPurchaseSuccess: async (purchase) => {
console.log('Purchase successful:', purchase);
// IMPORTANT: Verify receipt on your backend before finishing transaction
const isValid = await verifyReceiptOnServer(purchase);
if (isValid) {
await finishTransaction({purchase, isConsumable: true});
}
},
onPurchaseError: (error) => {
console.error('Purchase failed:', error);
},
});
const productIds = ['your.product.id', 'your.premium.subscription'];
}
2. Fetch Products
Load your products when the store connects:
useEffect(() => {
if (connected) {
// Fetch your products
fetchProducts({skus: productIds, type: 'in-app'});
}
}, [connected]);
3. Display Products
Show available products to users:
return (
<View>
<Text>Store Status: {connected ? 'Connected' : 'Connecting...'}</Text>
{products.map((product) => (
<View key={product.id} style={styles.productItem}>
<Text style={styles.productTitle}>{product.title}</Text>
<Text style={styles.productPrice}>{product.displayPrice}</Text>
<Button title="Buy Now" onPress={() => handlePurchase(product.id)} />
</View>
))}
</View>
);
4. Handle Purchases
Process purchase requests with our new platform-specific API (v2.7.0+):
const handlePurchase = async (productId: string) => {
try {
await requestPurchase({
request: {
ios: {
sku: productId,
},
android: {
skus: [productId],
},
},
});
} catch (error) {
console.error('Purchase failed:', error);
}
};
No more Platform.OS checks! The new API automatically handles platform differences. iOS can only purchase one product at a time, while Android supports purchasing multiple products in a single transaction.
5. Complete Transactions
Finish purchases in the success callback:
const {connected, products, fetchProducts, requestPurchase, finishTransaction} =
useIAP({
onPurchaseSuccess: async (purchase) => {
try {
console.log('Purchase completed:', purchase.id);
// IMPORTANT: Verify receipt on your backend before finishing transaction
const isValid = await verifyReceiptOnServer(purchase);
if (!isValid) {
console.error('Receipt validation failed');
return;
}
// Grant the purchase to user here
await grantPurchaseToUser(purchase);
// Finish the transaction
await finishTransaction({
purchase,
isConsumable: true, // Set based on your product type
});
} catch (error) {
console.error('Failed to complete purchase:', error);
}
},
onPurchaseError: (error) => {
console.error('Purchase failed:', error);
},
});
Complete Basic Example
Here's a complete working example:
import React, {useEffect} from 'react';
import {View, Text, Button, StyleSheet} from 'react-native';
import {useIAP} from 'expo-iap';
export default function SimpleStore() {
const {
connected,
products,
fetchProducts,
requestPurchase,
finishTransaction,
} = useIAP({
onPurchaseSuccess: async (purchase) => {
try {
console.log('Purchase completed:', purchase.id);
// IMPORTANT: Verify receipt on your backend before finishing transaction
const isValid = await verifyReceiptOnServer(purchase);
if (!isValid) {
console.error('Receipt validation failed');
return;
}
// Grant purchase to user
await grantPurchaseToUser(purchase);
// Finish the transaction
await finishTransaction({
purchase,
isConsumable: true,
});
} catch (error) {
console.error('Failed to complete purchase:', error);
}
},
onPurchaseError: (error) => {
console.error('Purchase failed:', error);
},
});
const productIds = ['com.example.coins.pack1', 'com.example.premium'];
useEffect(() => {
if (connected) {
fetchProducts({skus: productIds, type: 'in-app'});
}
}, [connected]);
const handlePurchase = async (productId: string) => {
try {
await requestPurchase({
request: {
ios: {
sku: productId,
},
android: {
skus: [productId],
},
},
});
} catch (error) {
console.error('Purchase failed:', error);
}
};
return (
<View style={styles.container}>
<Text style={styles.status}>
Store: {connected ? 'Connected ✅' : 'Connecting...'}
</Text>
{products.map((product) => (
<View key={product.id} style={styles.product}>
<Text style={styles.title}>{product.title}</Text>
<Text style={styles.price}>{product.displayPrice}</Text>
<Button title="Buy Now" onPress={() => handlePurchase(product.id)} />
</View>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {padding: 20},
status: {fontSize: 16, marginBottom: 20},
product: {
padding: 15,
marginVertical: 5,
backgroundColor: '#f0f0f0',
borderRadius: 8,
},
title: {fontSize: 16, fontWeight: 'bold'},
price: {fontSize: 14, color: '#666', marginVertical: 5},
});
🏗️ Architecture
Expo IAP is built with a modern architecture that emphasizes:
- Type Safety: Comprehensive TypeScript definitions for all APIs
- Error Resilience: Centralized error handling with meaningful error codes
- Platform Abstraction: Unified API that handles platform differences internally
- Performance: Optimized for minimal bundle size and runtime performance
📱 Platform Support
Platform | Support | Notes |
---|---|---|
iOS | ✅ | StoreKit 1 & 2 (StoreKit 2 requires iOS 15+) |
Android | ✅ | Google Play Billing v5+ |
Expo Go | ⚠️ | Limited (requires custom development client) |
Expo Dev Client | ✅ | Full support |
Bare React Native | ✅ | Full support |
🎯 What's Next?
📦 Setup & Configuration
- Installation Guide: Install and configure Expo IAP
- iOS Setup: App Store Connect and Xcode configuration
- Android Setup: Google Play Console setup
🔧 Implementation
- API Reference: Complete useIAP hook documentation
- Purchase Flow Example: Simple product purchase flow
- Available Purchases Example: Restore and list prior purchases
📚 Guides
- Installation: Complete guide to implementing in-app purchases
- Purchase Lifecycle: Understanding connection management and best practices
- Purchase Implementation: Detailed purchase flow and event handling
- FAQ: Frequently asked questions and solutions
- Support: Getting help and community resources
🛠️ Advanced Topics
- Receipt Validation: Secure purchase validation
- Error Handling: Comprehensive error management
- Subscriptions Flow Example: Handle recurring subscriptions
- Troubleshooting: Common issues and solutions
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.