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;
shouldAutoSyncPurchases?: boolean; // Controls auto sync behavior inside the hook
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