React Native and TypeScript
Configure the Fetch client and complete OpenIAP purchases after verification.
Download this chapterOn this page
The TypeScript package is @appvisionos/iap-client 0.5.0. It uses a Fetch-compatible transport and structurally accepts React Native IAP 15/OpenIAP purchase objects without adding the storefront library as a runtime dependency. It can also perform public runtime operations in other modern Fetch environments.
Integrate the source package
The package is private in the current preview and is not published to npm. Obtain the approved source checkout, run npm ci and npm run build inside sdk/typescript, then use your project's local-file/workspace package integration with that directory.
Keep the source build's generated dist available to the consuming application; the package's exports point there. Add and configure your chosen storefront library separately.
Configure the client and token cache
import { IAPClient, InMemoryCustomerTokenProvider } from '@appvisionos/iap-client';
const tokens = new InMemoryCustomerTokenProvider({
fetchToken: async (appUserId) => {
const response = await sessionApi.createIAPToken({ appUserId });
return { token: response.token, expiresAt: new Date(response.expires_at) };
},
});
const iap = new IAPClient({
baseUrl: 'https://api-staging.cherrysub.com',
publishableKey: 'iap_pk_REPLACE_WITH_YOUR_KEY',
customerTokenProvider: tokens.provider,
});sessionApi is your application's authenticated backend client. Its backend must derive the App User ID from verified session identity even if your client helper accepts an argument. See authentication.
Read the current presentation
const productSet = await iap.getCurrentProductSet(user.id);Handle null, filter packages to the device's store, preserve order, and load the storefront's product details and localized prices. CherrySub does not render a paywall or choose a Google offer for you.
Submit and complete an OpenIAP purchase
await iap.submitAndFinishOpenIAPPurchase(
user.id,
purchase,
() => finishTransaction({ purchase, isConsumable: false }),
'subscription', // Required for Google; omit for Apple.
);
const subscriber = await iap.getSubscriber(user.id);
const hasPro = subscriber.accessLevels.pro?.active === true;purchase, finishTransaction, and user come from your application/storefront integration. Choose isConsumable and Google's product type for the actual product; this example uses a non-consumable subscription completion policy.
The adapter accepts only purchaseState: 'purchased', uses the unified purchaseToken (StoreKit JWS or Google token), and rejects unsupported stores. It invokes your finish callback only after backend acceptance. Use submitOpenIAPPurchase for submit-only handling, or submitAppleTransaction / submitGooglePurchase with an application-owned bridge.
Errors and lifecycle
A 401 forces token refresh and one retry. Other statuses are not automatically replayed. Handle configuration, validation, transport, and response errors without showing premium access as a fallback.
The in-memory provider coalesces simultaneous refreshes and isolates caches by user. On logout, call tokens.invalidate(user.id), clear your own access cache, and revoke the token on the backend if required. Keep event IDs as decimal strings; they can exceed JavaScript's exact integer range.
Hook both the immediate purchase response and storefront update/recovery events into your verified completion flow. If the app exits before submission finishes, recover the unfinished store transaction on the next session rather than assuming it failed to charge.
Test
The package accepts an injected Fetch implementation for controlled transport tests. Run npm test in sdk/typescript for the source package's conformance checks, then perform real store-backed sandbox tests in the app. Verify pending/cancelled purchases, network interruption before completion, 401 refresh, invalid product types, restore ownership, and store refunds. Testing.