Purchase Flow
This example walks through a clean purchase flow using expo-iap with the useIAP hook and the new platform‑specific request shape. It mirrors the working sample in example/app/purchase-flow.tsx.
View the full example source:
- GitHub: example/app/purchase-flow.tsx
Flow Overview
-
Initialize:
useIAPmanages the store connection lifecycle -
Load products:
fetchProducts({ skus, type: 'in-app' }) -
Start purchase:
requestPurchase({ request: { apple: { sku }, google: { skus: [sku] } }, type: 'in-app' }) -
Receive callbacks:
onPurchaseSuccess/onPurchaseError(fromuseIAP) -
Validate server‑side: send receipt/JWS or token to your backend
-
Finish transaction:
finishTransaction({ purchase, isConsumable })
Connect → Fetch Products → Request Purchase → Server Validate → Finish Transaction
Key Concepts
1. Connection Management
useIAPautomatically opens/closes the connection- Exposes
connected, convenient for showing loading states
2. Product Loading
- Load in‑app products or subscriptions (set
type) - Handle and log failed product fetches
3. Purchase Flow
- Start purchases via unified request shape (no
Platform.OSbranching) - Use
onPurchaseSuccess/onPurchaseErrorfromuseIAP - Always call
finishTransactionafter server validation
4. Purchase Verification
- Perform verification on your backend (never only on device)
- iOS: verify the JWS token; Android: verify purchase token + package name
5. User Experience
- Provide clear states for loading, success, and error
- Show subscription management/deep‑links when appropriate
Platform Differences
Purchase Request Parameters
Use the modern, platform‑specific request container (v2.7.0+). This avoids manual Platform.OS checks:
await requestPurchase({
request: {
apple: {sku: productId, quantity: 1},
google: {skus: [productId]},
},
type: 'in-app',
});
Notes:
- Keep
andDangerouslyFinishTransactionAutomaticallyoff (default) to validate first.
Key iOS Options
appAccountToken: set per user to correlate receipts on your backendquantity: purchase quantity for iOS (consumables)
Purchase Object Properties
Purchase objects have different properties on iOS and Android. When accessing platform-specific properties, TypeScript type casting is required:
// Unified fields
const token = purchase.purchaseToken; // iOS JWS or Android token
// Android-only helpers
// const pkg = (purchase as PurchaseAndroid).packageNameAndroid;
Purchase Verification
Purchase verification (aka receipt validation) requires different approaches:
- iOS: verify JWS token on your server against Apple
- Android: verify token and package name against Google Play Developer API
Usage
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import PurchaseFlow from './purchase-flow';
export default function App() {
return (
<NavigationContainer>
<PurchaseFlow />
</NavigationContainer>
);
}
Customization
You can customize this example by:
- Styling: Modify the
stylesobject to match your app's design - Product IDs: Update
PRODUCT_IDSwith your actual product IDs - Validation: Implement proper server-side purchase verification
- Error Handling: Add more specific error handling for your use case
- Features: Add features like purchase restoration, subscription management, etc.
IAPKit Server Verification
IAPKit is a server-side receipt verification service that simplifies purchase validation. The example app includes built-in support for IAPKit verification.
Setup
-
Get your API key from IAPKit Dashboard
-
Configure environment variable in your project:
# .env or app.config.ts
EXPO_PUBLIC_IAPKIT_API_KEY=your_iapkit_api_key_here
- Select IAPKit verification in the example app by tapping the "Purchase Verification" button and selecting "☁️ IAPKit (Server)"
How It Works
When IAPKit verification is enabled, after a successful purchase:
import {useIAP, type VerifyPurchaseWithProviderProps} from 'expo-iap';
import {Platform, Alert} from 'react-native';
function PurchaseWithIAPKit() {
const {verifyPurchaseWithProvider, finishTransaction} = useIAP({
onPurchaseSuccess: async (purchase) => {
const apiKey = process.env.EXPO_PUBLIC_IAPKIT_API_KEY;
if (!apiKey) {
console.error('EXPO_PUBLIC_IAPKIT_API_KEY not configured');
return;
}
const jwsOrToken = purchase.purchaseToken ?? '';
const verifyRequest: VerifyPurchaseWithProviderProps = {
provider: 'iapkit',
iapkit: {
apiKey,
apple: {
jws: jwsOrToken, // iOS: JWS token from StoreKit 2
},
google: {
purchaseToken: jwsOrToken, // Android: purchase token
},
},
};
try {
const result = await verifyPurchaseWithProvider(verifyRequest);
if (result.iapkit) {
const iapkitResult = result.iapkit;
if (iapkitResult.isValid) {
// Purchase is valid - grant entitlements
console.log('Purchase verified:', iapkitResult.state);
await finishTransaction({
purchase,
isConsumable: true, // or false for non-consumables
});
Alert.alert('Success', 'Purchase verified and completed!');
} else {
Alert.alert(
'Verification Failed',
'Purchase could not be verified',
);
}
}
} catch (error) {
console.error('IAPKit verification failed:', error);
}
},
});
// ... rest of component
}
Verification Response
IAPKit returns a standardized response:
import type {IapkitPurchaseState, IapkitStore} from 'expo-iap';
export interface RequestVerifyPurchaseWithIapkitResult {
/** Whether the purchase is valid (not falsified). */
isValid: boolean;
/** The current state of the purchase. */
state: IapkitPurchaseState;
store: IapkitStore;
}
// Available states:
type IapkitPurchaseState =
| 'entitled'
| 'pending-acknowledgment'
| 'pending'
| 'canceled'
| 'expired'
| 'ready-to-consume'
| 'consumed'
| 'unknown'
| 'inauthentic';
// Available stores:
type IapkitStore = 'apple' | 'google';
Verification Methods
The example app supports three verification methods:
| Method | Description | Use Case |
|---|---|---|
| None (Skip) | Skip verification | Testing/Development |
| Local (Device) | Verify with Apple/Google directly | Simple validation |
| IAPKit (Server) | Server-side verification via IAPKit | Production recommended |
Benefits of IAPKit
- Unified API: Same verification flow for iOS and Android
- Server-side validation: More secure than client-only validation
- Fraud detection: Built-in fraud prevention
- Webhook support: Real-time purchase notifications
- Dashboard: Monitor purchases and analytics
For more information, visit IAPKit Documentation.
Next Steps
- Implement proper purchase verification
- Add purchase restoration
- Handle subscription management
- Add comprehensive error handling
