Skip to main content

useIAP Hook

The useIAP hook is the main interface for interacting with in-app purchases in Expo IAP. It provides a comprehensive API for managing purchases, subscriptions, and error handling.

Import

import {useIAP} from 'expo-iap';

Basic Usage

const {
connected,
products,
subscriptions,
currentPurchase,
currentPurchaseError,
getProducts,
getSubscriptions,
requestPurchase,
validateReceipt,
} = useIAP({
onPurchaseSuccess: (purchase) => {
console.log('Purchase successful:', purchase);
},
onPurchaseError: (error) => {
console.error('Purchase failed:', error);
},
onSyncError: (error) => {
console.warn('Sync error:', error);
},
});

Configuration Options

useIAP(options)

ParameterTypeRequiredDescription
optionsUseIAPOptionsNoConfiguration object

UseIAPOptions

interface UseIAPOptions {
onPurchaseSuccess?: (purchase: Purchase) => void;
onPurchaseError?: (error: PurchaseError) => void;
onSyncError?: (error: Error) => void;
autoFinishTransactions?: boolean; // Default: true
}

Configuration Properties

onPurchaseSuccess

  • Type: (purchase: Purchase) => void
  • Description: Called when a purchase completes successfully
  • Example:
    onPurchaseSuccess: (purchase) => {
    // Grant user access to purchased content
    unlockFeature(purchase.productId);
    };

onPurchaseError

  • Type: (error: PurchaseError) => void
  • Description: Called when a purchase fails
  • Example:
    onPurchaseError: (error) => {
    if (error.code !== ErrorCode.E_USER_CANCELLED) {
    Alert.alert('Purchase Failed', error.message);
    }
    };

onSyncError

  • Type: (error: Error) => void
  • Description: Called when there's an error syncing with the store
  • Example:
    onSyncError: (error) => {
    console.warn('Store sync error:', error.message);
    };

autoFinishTransactions

  • Type: boolean
  • Default: true
  • Description: Whether to automatically finish transactions after successful purchases

Return Values

State Properties

connected

  • Type: boolean
  • Description: Whether the IAP service is connected and ready
  • Example:
    if (connected) {
    // Safe to make IAP calls
    getProducts(['product.id']);
    }

products

  • Type: Product[]
  • Description: Array of available products
  • Example:
    products.map((product) => <ProductItem key={product.id} product={product} />);

subscriptions

  • Type: SubscriptionProduct[]
  • Description: Array of available subscription products
  • Example:
    subscriptions.map((subscription) => (
    <SubscriptionItem key={subscription.id} subscription={subscription} />
    ));

currentPurchase

  • Type: Purchase | null
  • Description: Currently active purchase (if any)
  • Example:
    useEffect(() => {
    if (currentPurchase) {
    processPurchase(currentPurchase);
    }
    }, [currentPurchase]);

currentPurchaseError

  • Type: PurchaseError | null
  • Description: Current purchase error (if any)
  • Example:
    useEffect(() => {
    if (currentPurchaseError) {
    handlePurchaseError(currentPurchaseError);
    }
    }, [currentPurchaseError]);

Methods

getProducts

  • Type: (productIds: string[]) => Promise<Product[]>
  • Description: Fetch products from the store
  • Parameters:
    • productIds: Array of product IDs to fetch
  • Returns: Promise resolving to array of products
  • Example:
    const fetchProducts = async () => {
    try {
    const products = await getProducts([
    'com.app.premium',
    'com.app.coins_100',
    ]);
    console.log('Fetched products:', products);
    } catch (error) {
    console.error('Failed to fetch products:', error);
    }
    };

getSubscriptions

  • Type: (subscriptionIds: string[]) => Promise<SubscriptionProduct[]>
  • Description: Fetch subscription products from the store
  • Parameters:
    • subscriptionIds: Array of subscription IDs to fetch
  • Returns: Promise resolving to array of subscription products
  • Example:
    const fetchSubscriptions = async () => {
    try {
    const subs = await getSubscriptions([
    'com.app.premium_monthly',
    'com.app.premium_yearly',
    ]);
    console.log('Fetched subscriptions:', subs);
    } catch (error) {
    console.error('Failed to fetch subscriptions:', error);
    }
    };

requestPurchase

  • Type: (request: RequestPurchaseProps) => Promise<void>
  • Description: Initiate a purchase request
  • Parameters:
    • request: Purchase request configuration
  • Example:
    const buyProduct = async (productId: string) => {
    try {
    await requestPurchase({
    request: {sku: productId},
    });
    } catch (error) {
    console.error('Purchase request failed:', error);
    }
    };

validateReceipt

  • Type: (productId: string, params?: ValidationParams) => Promise<ValidationResult>
  • Description: Validate a purchase receipt
  • Parameters:
    • productId: ID of the product to validate
    • params: Optional validation parameters (Android)
  • Returns: Promise resolving to validation result
  • Example:
    const validatePurchase = async (productId: string) => {
    try {
    const result = await validateReceipt(productId);
    if (result.isValid) {
    console.log('Receipt is valid');
    // Grant access to content
    }
    } catch (error) {
    console.error('Validation failed:', error);
    }
    };

Platform-Specific Usage

iOS Example

const IOSPurchaseExample = () => {
const {connected, products, requestPurchase, validateReceipt} = useIAP({
onPurchaseSuccess: async (purchase) => {
// Validate receipt on iOS
const validation = await validateReceipt(purchase.productId);
if (validation.isValid) {
unlockContent(purchase.productId);
}
},
});

const buyProduct = (product: Product) => {
if (product.platform === 'ios') {
requestPurchase({
request: {sku: product.id},
});
}
};

return (
<View>
{products
.filter((p) => p.platform === 'ios')
.map((product) => (
<Button
key={product.id}
title={`${product.title} - ${product.displayPrice}`}
onPress={() => buyProduct(product)}
/>
))}
</View>
);
};

Android Example

const AndroidPurchaseExample = () => {
const {connected, products, requestPurchase} = useIAP({
onPurchaseSuccess: (purchase) => {
// Android purchases are automatically validated by Google Play
unlockContent(purchase.productId);
},
});

const buyProduct = (product: Product) => {
if (product.platform === 'android') {
requestPurchase({
request: {skus: [product.id]},
});
}
};

return (
<View>
{products
.filter((p) => p.platform === 'android')
.map((product) => (
<Button
key={product.id}
title={`${product.title} - ${product.oneTimePurchaseOfferDetails?.formattedPrice}`}
onPress={() => buyProduct(product)}
/>
))}
</View>
);
};

Error Handling

The useIAP hook integrates with the centralized error handling system:

const {requestPurchase} = useIAP({
onPurchaseError: (error) => {
// Error is automatically typed as PurchaseError
switch (error.code) {
case ErrorCode.E_USER_CANCELLED:
// Don't show error for user cancellation
break;
case ErrorCode.E_NETWORK_ERROR:
Alert.alert('Network Error', 'Please check your connection');
break;
case ErrorCode.E_ITEM_UNAVAILABLE:
Alert.alert(
'Item Unavailable',
'This item is not available for purchase',
);
break;
default:
Alert.alert('Purchase Failed', error.message);
}
},
});

Best Practices

  1. Always check connected before making IAP calls:

    useEffect(() => {
    if (connected) {
    getProducts(productIds);
    }
    }, [connected]);
  2. Handle loading states:

    const [loading, setLoading] = useState(false);

    const buyProduct = async (productId: string) => {
    setLoading(true);
    try {
    await requestPurchase({request: {sku: productId}});
    } finally {
    setLoading(false);
    }
    };
  3. Implement proper error handling:

    const handleError = (error: PurchaseError) => {
    // Log for debugging
    console.error('IAP Error:', error);

    // Show user-friendly message
    if (error.code !== ErrorCode.E_USER_CANCELLED) {
    Alert.alert('Purchase Failed', error.message);
    }
    };
  4. Cache products to avoid repeated fetches:

    const [productsLoaded, setProductsLoaded] = useState(false);

    useEffect(() => {
    if (connected && !productsLoaded) {
    getProducts(productIds).then(() => {
    setProductsLoaded(true);
    });
    }
    }, [connected, productsLoaded]);

See Also