Skip to main content
Version: 14.6

useIAP Hook

IapKit - In-App Purchase Validation Service

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

Import​

import {useIAP} from 'react-native-iap';

Important: Hook Behavior​

The useIAP hook follows React Hooks conventions and differs from calling functions directly from react-native-iap (index exports):

  • Automatic connection: Automatically calls initConnection on mount and endConnection on unmount.
  • Void-returning methods: Methods like fetchProducts, requestPurchase, getAvailablePurchases, etc. return Promise<void> in the hook. They do not resolve to data. Instead, they update internal state exposed by the hook: products, subscriptions, availablePurchases, etc.
  • Don't await for data: When using the hook, do not write const x = await fetchProducts(...). Call the method, then read the corresponding state from the hook.

Basic Usage​

const {
connected,
products,
subscriptions,
availablePurchases,
fetchProducts,
requestPurchase,
validateReceipt,
} = useIAP({
onPurchaseSuccess: (purchase) => {
// Validate on your backend, then finish the transaction
console.log('Purchase successful:', purchase);
},
onPurchaseError: (error) => {
console.error('Purchase failed:', error);
},
});

Configuration Options​

useIAP(options)​

ParameterTypeRequiredDescription
optionsUseIAPOptionsNoConfiguration object

UseIAPOptions​

interface UseIAPOptions {
onPurchaseSuccess?: (purchase: Purchase) => void;
onPurchaseError?: (error: PurchaseError) => void;
onPromotedProductIOS?: (product: Product) => void; // iOS promoted products
}

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.UserCancelled) {
    Alert.alert('Purchase Failed', 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
    fetchProducts({skus: ['product.id'], type: 'in-app'});
    }

products​

  • Type: Product[]

  • Description: Array of available products

  • Example:

    products.map((product) => <ProductItem key={product.id} product={product} />);

subscriptions​

  • Type: ProductSubscription[]

  • Description: Array of available subscription products

  • Example:

    subscriptions.map((subscription) => (
    <SubscriptionItem key={subscription.id} subscription={subscription} />
    ));

currentPurchaseError​

  • Type: PurchaseError | null

  • Description: Current purchase error (if any)

  • Example:

    useEffect(() => {
    if (currentPurchaseError) {
    handlePurchaseError(currentPurchaseError);
    }
    }, [currentPurchaseError]);

availablePurchases​

  • Type: Purchase[]

  • Description: Array of available purchases (restorable items)

  • Example:

    availablePurchases.map((purchase) => (
    <RestorableItem key={purchase.id} purchase={purchase} />
    ));

promotedProductIOS​

  • Type: Product | undefined

  • Description: The promoted product details (iOS only)

  • Example:

    useEffect(() => {
    if (promotedProductIOS) {
    // Handle promoted product
    handlePromotedProduct(promotedProductIOS);
    }
    }, [promotedProductIOS]);

Methods​

fetchProducts​

  • Type: (params: { skus: string[]; type?: 'in-app' | 'subs' }) => Promise<void>

  • Description: Fetch products or subscriptions and update products / subscriptions state. In the hook this returns void (no data result), by design.

  • Do not await for data: Call it, then consume products / subscriptions state from the hook.

  • Example:

    useEffect(() => {
    if (!connected) return;
    // In hook: returns void, updates state
    fetchProducts({
    skus: ['com.app.premium', 'com.app.coins_100'],
    type: 'in-app',
    });
    fetchProducts({skus: ['com.app.premium_monthly'], type: 'subs'});
    }, [connected, fetchProducts]);

    // Later in render/effects
    products.forEach((p) => console.log('product', p.id));
    subscriptions.forEach((s) => console.log('sub', s.id));

requestPurchase​

  • Type: (request: RequestPurchaseProps) => Promise<void>

  • Description: Initiate a purchase request

  • Parameters:

    • request: Purchase request configuration
  • Example:

    const buyProduct = async (productId: string) => {
    try {
    // In hook: returns void. Listen via callbacks.
    await requestPurchase({
    request: {
    apple: {sku: productId},
    google: {skus: [productId]},
    },
    type: 'in-app',
    });
    } catch (error) {
    console.error('Purchase request failed:', error);
    }
    };

Subscription Offers​

When purchasing subscriptions, you need to specify the pricing plan (offer) for each platform:

Android Subscription Offers​

Android requires subscriptionOffers array containing offer tokens from fetchProducts(). Each offer token represents a specific pricing plan (base plan, introductory offer, etc.).

const buySubscription = async (subscriptionId: string) => {
// 1) Fetch subscription products first
await fetchProducts({skus: [subscriptionId], type: 'subs'});

// 2) Find the subscription and build offers
const subscription = subscriptions.find((s) => s.id === subscriptionId);
if (!subscription) return;

const subscriptionOffers = (
subscription.subscriptionOfferDetailsAndroid ?? []
).map((offer) => ({
sku: subscriptionId,
offerToken: offer.offerToken,
}));

// 3) Request purchase with offers
await requestPurchase({
request: {
apple: {sku: subscriptionId},
google: {
skus: [subscriptionId],
// Only include subscriptionOffers when offers are available
...(subscriptionOffers.length > 0 && {subscriptionOffers}),
},
},
type: 'subs',
});
};

Note: subscriptionOffers should only be included when subscription offers are available from fetchProducts(). Without offers, Android purchases will fail.

iOS Subscription Offers​

iOS uses withOffer for promotional discounts configured in App Store Connect. This is optional and only needed for special promotional pricing.

const buySubscriptionWithOffer = async (
subscriptionId: string,
discountOffer?: DiscountOfferInputIOS,
) => {
await requestPurchase({
request: {
apple: {
sku: subscriptionId,
// Optional: apply promotional offer
...(discountOffer && {withOffer: discountOffer}),
},
google: {skus: [subscriptionId]},
},
type: 'subs',
});
};

Subscription helpers (hook)​

  • getActiveSubscriptions(subscriptionIds?) => Promise<ActiveSubscription[]>

    • Returns active subscription info and also updates activeSubscriptions state.

    • Exception to the hook’s void-return design: this method returns data for convenience.

    • Example:

      const {getActiveSubscriptions, activeSubscriptions} = useIAP();

      useEffect(() => {
      if (!connected) return;
      (async () => {
      const subs = await getActiveSubscriptions(['premium_monthly']);
      console.log('Subs from return:', subs.length);
      console.log('Subs from state:', activeSubscriptions.length);
      })();
      }, [connected]);
  • hasActiveSubscriptions(subscriptionIds?) => Promise<boolean>

    • Boolean convenience check to see if any active subscriptions exist (optionally filtered by IDs).

Removed in v2.9.0: purchaseHistories state and getPurchaseHistories() method. Use getAvailablePurchases() and availablePurchases instead.

getAvailablePurchases​

  • Type: () => Promise<void>

  • Description: Fetch available purchases (restorable items) from the store

  • Example:

    const restorePurchases = async () => {
    try {
    // Updates `availablePurchases` state; do not expect a return value
    await getAvailablePurchases();
    // Read from state afterwards
    console.log('Available purchases count:', availablePurchases.length);
    } catch (error) {
    console.error('Failed to fetch available purchases:', error);
    }
    };

validateReceipt (Deprecated)​

⚠️ Deprecated: Use verifyPurchase instead. This function will be removed in a future version.

  • Type: (options: VerifyPurchaseProps) => Promise<VerifyPurchaseResult>
  • Description: Validate a purchase receipt using platform-specific verification
  • Parameters:
    • options (object): Platform-specific verification parameters
      • apple? (object): Apple App Store verification options
        • sku (string): Product SKU to validate
      • google? (object): Google Play Store verification options
        • sku (string): Product SKU to validate
        • accessToken (string): Google OAuth2 access token for API authentication
        • packageName (string): Android package name (e.g., com.example.app)
        • purchaseToken (string): Purchase token from the purchase response
        • isSub? (boolean): Whether this is a subscription purchase
      • horizon? (object): Meta Horizon verification options
        • sku (string): SKU for the add-on item
        • accessToken (string): Meta API access token
        • userId (string): User ID to verify purchase for
  • Returns: Promise resolving to platform-specific validation result

Important Platform Differences:

  • iOS: Only requires apple.sku - uses StoreKit 2's built-in verification

  • Android: Requires all google parameters - calls Google Play Developer API

  • Horizon: Requires all horizon parameters - calls Meta's S2S API

  • Example:

    const validatePurchase = async (purchase: Purchase) => {
    try {
    // Cross-platform validation
    const result = await validateReceipt({
    // iOS options (only needs SKU)
    apple: {
    sku: purchase.productId,
    },
    // Android options (requires all parameters)
    google: {
    sku: purchase.productId,
    packageName: purchase.packageNameAndroid ?? '',
    purchaseToken: purchase.purchaseToken ?? '',
    accessToken: 'your-google-api-access-token',
    isSub: false, // Set to true for subscriptions
    },
    });

    console.log('Validation result:', result);
    return result;
    } catch (error) {
    console.error('Validation failed:', error);
    throw error;
    }
    };

    // Platform-specific examples
    const validateIOSOnly = async (sku: string) => {
    const result = await validateReceipt({
    apple: {sku},
    });
    return result;
    };

    const validateAndroidOnly = async (purchase: Purchase) => {
    const result = await validateReceipt({
    google: {
    sku: purchase.productId,
    packageName: purchase.packageNameAndroid!,
    purchaseToken: purchase.purchaseToken!,
    accessToken: 'your-google-api-access-token',
    isSub: false,
    },
    });
    return result;
    };

getPromotedProductIOS​

  • Type: () => Promise<any | null>

  • Description: Get the promoted product details (iOS only)

  • Example:

    const handlePromotedProduct = async () => {
    const promotedProduct = await getPromotedProductIOS();
    if (promotedProduct) {
    console.log('Promoted product:', promotedProduct);
    // Show custom purchase UI
    }
    };

requestPurchaseOnPromotedProductIOS​

  • Type: () => Promise<boolean>

  • Description: Complete the purchase of a promoted product (iOS only)

    Deprecated: In StoreKit 2, promoted products can be purchased directly via the standard requestPurchase() flow. Use promotedProductListenerIOS to receive the product ID when a user taps a promoted product, then call requestPurchase() with the received SKU directly.

  • Example:

    // Recommended approach (StoreKit 2)
    promotedProductListenerIOS(async (product) => {
    await requestPurchase({
    request: { apple: { sku: product.id } },
    type: 'in-app',
    });
    });

    // Legacy approach (deprecated)
    const completePurchase = async () => {
    try {
    const success = await requestPurchaseOnPromotedProductIOS();
    if (success) {
    console.log('Promoted product purchase completed');
    }
    } catch (error) {
    console.error('Failed to purchase promoted product:', 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) => {
requestPurchase({
request: {
apple: {sku: product.id},
google: {skus: [product.id]},
},
type: 'in-app',
});
};

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) => {
requestPurchase({
request: {
apple: {sku: product.id},
google: {skus: [product.id]},
},
type: 'in-app',
});
};

return (
<View>
{products
.filter((p) => p.platform === 'android')
.map((product) => (
<Button
key={product.id}
title={`${product.title} - ${product.displayPrice}`}
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.UserCancelled:
// Don't show error for user cancellation
break;
case ErrorCode.NetworkError:
Alert.alert('Network Error', 'Please check your connection');
break;
case ErrorCode.ItemUnavailable:
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) {
    fetchProducts({skus: productIds, type: 'in-app'});
    }
    }, [connected, fetchProducts]);
  2. Handle loading states:

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

    const buyProduct = async (productId: string) => {
    setLoading(true);
    try {
    await requestPurchase({
    request: {
    apple: {sku: productId},
    google: {skus: [productId]},
    },
    type: 'in-app',
    });
    } 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.UserCancelled) {
    Alert.alert('Purchase Failed', error.message);
    }
    };

Handle App Store promoted products when users tap on them in the App Store:

const PromotedProductExample = () => {
const {promotedProductIOS, requestPurchaseOnPromotedProductIOS} = useIAP({
onPromotedProductIOS: (product) => {
console.log('Promoted product detected:', product);
},
onPurchaseSuccess: (purchase) => {
// Recommended: handle success via callback
},
onPurchaseError: (error) => {
// Recommended: handle errors via callback
},
});

useEffect(() => {
if (promotedProductIOS) {
handlePromotedProduct();
}
}, [promotedProductIOS]);

const handlePromotedProduct = async () => {
try {
// Show your custom purchase UI
const confirmed = await showPurchaseConfirmation(promotedProductIOS);

if (confirmed) {
// Complete the promoted purchase
await requestPurchaseOnPromotedProductIOS();
}
} catch (error) {
console.error('Error handling promoted product:', error);
}
};

const showPurchaseConfirmation = async (product: any) => {
return new Promise((resolve) => {
Alert.alert(
'Purchase Product',
`Would you like to purchase ${product.localizedTitle} for ${product.price}?`,
[
{text: 'Cancel', onPress: () => resolve(false), style: 'cancel'},
{text: 'Buy', onPress: () => resolve(true)},
],
);
});
};

return <View>{/* Your regular store UI */}</View>;
};

See Also​