# expo-iap - Complete AI Reference > In-App Purchase solution for Expo and React Native supporting iOS StoreKit 2 and Android Play Billing 8.x This document provides comprehensive API documentation for AI assistants to help developers implement in-app purchases with expo-iap. ## Table of Contents 1. [Installation](#installation) 2. [useIAP Hook](#useiap-hook) 3. [Direct API Functions](#direct-api-functions) 4. [Types](#types) 5. [Error Handling](#error-handling) 6. [Platform-Specific APIs](#platform-specific-apis) 7. [Common Patterns](#common-patterns) 8. [Troubleshooting](#troubleshooting) --- ## Installation ### Package Installation ```bash npx expo install expo-iap ``` ### Required Configuration (Expo SDK 53+) ```json { "expo": { "plugins": [ "expo-iap", ["expo-build-properties", {"android": {"kotlinVersion": "2.2.0"}}] ] } } ``` ### With IAPKit API Key ```json { "expo": { "plugins": [ ["expo-iap", {"iapkitApiKey": "your_iapkit_api_key"}], ["expo-build-properties", {"android": {"kotlinVersion": "2.2.0"}}] ] } } ``` ### Prerequisites - Expo SDK 53+ or React Native 0.79+ - iOS 15+ for StoreKit 2 features - Android API 21+ - Custom development client (not Expo Go) --- ## useIAP Hook The primary interface for in-app purchases in React components. ### Import ```tsx import {useIAP} from 'expo-iap'; ``` ### Signature ```tsx function useIAP(options?: UseIAPOptions): UseIAPReturn; ``` ### Options ```tsx interface UseIAPOptions { onPurchaseSuccess?: (purchase: Purchase) => void; onPurchaseError?: (error: PurchaseError) => void; shouldAutoSyncPurchases?: boolean; onPromotedProductIOS?: (product: Product) => void; } ``` ### Return Value ```tsx interface UseIAPReturn { // ═══════════════════════════════════════════════════════════════ // STATE VARIABLES (read data from these) // ═══════════════════════════════════════════════════════════════ connected: boolean; products: Product[]; subscriptions: ProductSubscription[]; availablePurchases: Purchase[]; activeSubscriptions: ActiveSubscription[]; promotedProductIOS?: Product; // ═══════════════════════════════════════════════════════════════ // METHODS (return Promise, update state above) // ═══════════════════════════════════════════════════════════════ fetchProducts: (params: {skus: string[]; type: 'in-app' | 'subs'}) => Promise; requestPurchase: (props: RequestPurchaseProps) => Promise; finishTransaction: (params: {purchase: Purchase; isConsumable?: boolean}) => Promise; getAvailablePurchases: () => Promise; getActiveSubscriptions: (ids?: string[]) => Promise; restorePurchases: () => Promise; // ═══════════════════════════════════════════════════════════════ // METHODS THAT RETURN VALUES (exceptions to the pattern) // ═══════════════════════════════════════════════════════════════ hasActiveSubscriptions: (ids?: string[]) => Promise; verifyPurchase: (props: VerifyPurchaseProps) => Promise; verifyPurchaseWithProvider: (props: VerifyPurchaseWithProviderProps) => Promise; // iOS-specific getPromotedProductIOS: () => Promise; requestPurchaseOnPromotedProductIOS: () => Promise; } ``` ### Design Pattern **The hook follows React's state-driven pattern:** - Methods return `Promise` and update internal state - You must read data from state variables, not from method return values ```tsx // ✅ CORRECT: Call method, then read from state await fetchProducts({skus: ['product1'], type: 'in-app'}); console.log(products); // Read from state variable await getAvailablePurchases(); console.log(availablePurchases); // Read from state variable // ❌ WRONG: Don't expect return values from methods const result = await fetchProducts({...}); // result is void! const purchases = await getAvailablePurchases(); // purchases is void! ``` ### Important Behavior - **Auto-connects**: Calls `initConnection()` on mount, `endConnection()` on unmount - **Void-returning methods**: `fetchProducts`, `requestPurchase`, `getAvailablePurchases`, `getActiveSubscriptions` return `Promise` and update internal state - **Use callbacks**: Always handle purchases via `onPurchaseSuccess` and `onPurchaseError` ### Basic Example ```tsx import {useIAP, ErrorCode} from 'expo-iap'; function Store() { const { connected, products, fetchProducts, requestPurchase, finishTransaction, } = useIAP({ onPurchaseSuccess: async (purchase) => { // 1. Verify on your backend const isValid = await verifyOnServer(purchase); // 2. Grant entitlement if (isValid) { await grantPurchase(purchase); } // 3. Finish transaction await finishTransaction({ purchase, isConsumable: true, // true for consumables }); }, onPurchaseError: (error) => { if (error.code !== ErrorCode.UserCancelled) { Alert.alert('Purchase Failed', error.message); } }, }); useEffect(() => { if (connected) { fetchProducts({ skus: ['product_1', 'product_2'], type: 'in-app', }); } }, [connected]); const buy = (productId: string) => { requestPurchase({ request: { apple: {sku: productId}, google: {skus: [productId]}, }, }); }; return ( {products.map((product) => (