Skip to content

Apple & StoreKit

Goal: Tie App Store purchases, renewals, and refunds to a BitEasy install.

Requirement: A stable installId generated once per device install. Must be a valid UUID — StoreKit 2 only accepts UUID values for appAccountToken. Passing a non-UUID string will cause the purchase to fail silently.


Apple never sends device IDs or custom metadata in their server notifications. To link purchases to installs, the client must inject appAccountToken into each purchase.

BitEasy first learns the install ID when your app calls the /claim endpoint.


Step 1: Generate & persist a stable Install ID

Section titled “Step 1: Generate & persist a stable Install ID”

Generate a UUID the first time the app is opened and store it permanently on the device. This must be a valid UUID (e.g. 550e8400-e29b-41d4-a716-446655440000) — Apple’s StoreKit 2 requires appAccountToken to be a UUID type. A non-UUID value will prevent the purchase sheet from appearing.

import AsyncStorage from '@react-native-async-storage/async-storage';
import { v4 as uuidv4 } from 'uuid';
const INSTALL_KEY = 'biteasy_install_id';
export async function getStableInstallId() {
let id = await AsyncStorage.getItem(INSTALL_KEY);
if (!id) {
id = uuidv4();
await AsyncStorage.setItem(INSTALL_KEY, id);
}
return id;
}
  • Stored once via AsyncStorage
  • Survives restarts; clears only if user manually wipes data or uninstalls
  • No regeneration ensures deterministic attribution

BitEasy learns the install ID the first time a user claims a referral code:

await fetch('https://api.biteasy.com/api/v1/claim', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
appId,
installId: await getStableInstallId(),
code,
}),
});

Note: This is just an example. You may also pass installId when using the JS SDK.


Step 3: Pass the Install ID inside purchase calls

Section titled “Step 3: Pass the Install ID inside purchase calls”

To link the purchase back to the claim, you must pass the install ID as the appAccountToken.

let installId = getStableInstallId()
guard let uuid = UUID(uuidString: installId) else { return }
let result = try await product.purchase(
options: [.appAccountToken(uuid)]
)
let payment = SKMutablePayment(product: product)
payment.applicationUsername = installId
queue.add(payment)

Verified against latest react-native-iap (v13+) API surface.

Subscriptions:

import * as RNIap from 'react-native-iap';
await RNIap.requestSubscription({
sku: 'your_sku_here',
appAccountToken: await getStableInstallId(),
});

One-time purchases:

await RNIap.requestPurchase({
sku: 'your_sku_here',
appAccountToken: await getStableInstallId(),
});

Restore purchases:

await RNIap.restorePurchases({
appAccountToken: await getStableInstallId(),
});

If you do NOT pass appAccountToken, App Store Server Notifications cannot identify who made the purchase. If you DO pass it, Apple embeds it in:

  • Initial trial or paid purchase
  • Renewals
  • Billing retries
  • Refunds / Revokes

That’s it! BitEasy will automatically attribute the purchase to the install ID.