Skip to main content

Expo IAP

Expo IAP is a powerful in-app purchase solution for Expo and React Native applications. It provides a unified API for handling in-app purchases across iOS and Android platforms with comprehensive error h### 📚 Guides

🛠️ Advanced Topicsnd modern TypeScript support.

✨ Features

  • 🔄 Cross-platform Support: Works seamlessly on both iOS and Android
  • 🎯 TypeScript First: Full TypeScript support with comprehensive type definitions
  • 🛡️ Centralized Error Handling: Unified error management with platform-specific error code mapping
  • 🎣 React Hooks: Modern React hooks API with useIAP
  • 📱 Expo Compatible: Built specifically for Expo with a seamless plugin experience
  • 🔍 Receipt Validation: Built-in receipt validation for both platforms
  • 💎 Products & Subscriptions: Support for both one-time purchases and subscriptions
  • 🚀 Performance Optimized: Efficient caching and minimal re-renders

🚀 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,
getProducts,
requestPurchase,
currentPurchase,
finishTransaction,
} = useIAP();

const productIds = ['your.product.id', 'your.premium.subscription'];
}

2. Fetch Products

Load your products when the store connects:

useEffect(() => {
if (connected) {
// Fetch your products
getProducts(productIds);
}
}, [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:

const handlePurchase = async (productId: string) => {
try {
await requestPurchase({
request: {sku: productId},
});
} catch (error) {
console.error('Purchase failed:', error);
}
};

5. Complete Transactions

Finish purchases when they complete:

useEffect(() => {
if (currentPurchase) {
const completePurchase = async () => {
try {
// Grant the purchase to user here
console.log('Purchase completed:', currentPurchase.id);

// Finish the transaction
await finishTransaction({
purchase: currentPurchase,
isConsumable: true, // Set based on your product type
});
} catch (error) {
console.error('Failed to complete purchase:', error);
}
};

completePurchase();
}
}, [currentPurchase]);

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,
getProducts,
requestPurchase,
currentPurchase,
finishTransaction,
} = useIAP();

const productIds = ['com.example.coins.pack1', 'com.example.premium'];

useEffect(() => {
if (connected) {
getProducts(productIds);
}
}, [connected]);

useEffect(() => {
if (currentPurchase) {
const completePurchase = async () => {
try {
console.log('Purchase completed:', currentPurchase.id);
await finishTransaction({
purchase: currentPurchase,
isConsumable: true,
});
} catch (error) {
console.error('Failed to complete purchase:', error);
}
};
completePurchase();
}
}, [currentPurchase]);

const handlePurchase = async (productId: string) => {
try {
await requestPurchase({
request: {sku: 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

PlatformSupportNotes
iOSStoreKit 1 & 2
AndroidGoogle Play Billing v5+
Expo Go⚠️Limited (requires custom development client)
Expo Dev ClientFull support
Bare React NativeFull support

🎯 What's Next?

📦 Setup & Configuration

🔧 Implementation

� Guides

�🛠️ Advanced Topics

🤝 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.