One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Purchases

StoreKit 2 products, purchases, entitlements, and transaction updates on iOS

One.Purchases uses StoreKit 2 in a One native iOS build. Configure product IDs in App Store Connect for an app you distribute, or select a local StoreKit configuration in an Xcode test plan while developing. The API needs no Info.plist usage string. Apple’s StoreKit testing guide explains local products and transactions.

In this example, grantContentOnce is app code that verifies any server-owned access and persists delivery by transaction ID before the transaction is finished.

import { One } from 'one'
const purchases = One.Purchases
const handling = new Set<string>()
async function deliver(transaction: { id: string; productId: string; jws: string }) {
if (handling.has(transaction.id)) return
handling.add(transaction.id)
try {
// persist delivery by transaction id so a relaunch or restore stays idempotent.
await grantContentOnce(transaction.id, transaction.productId, transaction.jws)
await purchases.finishTransaction(transaction.id)
} finally {
handling.delete(transaction.id)
}
}
const remove = purchases.addTransactionListener(async (update) => {
if (update.status === 'verified' && update.transaction) await deliver(update.transaction)
})
for (const transaction of await purchases.getUnfinishedTransactions()) await deliver(transaction)
const products = await purchases.getProducts(['com.example.app.premium'])
// Show products[0].displayPrice in your purchase UI.
const result = await purchases.purchase('com.example.app.premium')
if (result.status === 'purchased' && result.transaction) await deliver(result.transaction)
// Keep the listener for your app session; call remove() when its owner unmounts.

getProducts accepts 1 to 100 distinct IDs, returns configured products in the requested order, and omits IDs StoreKit does not find. A product includes its localized name, description, display price, optional currency code, and type. purchase starts StoreKit’s system flow from the active app. It returns purchased with a locally verified transaction, cancelled, or pending. The optional second argument is an app account token UUID, sent to StoreKit with the purchase.

Each transaction has a decimal string ID and original ID because StoreKit uses 64-bit integers that JavaScript numbers cannot always represent. It also has the product ID and type, purchase time in milliseconds, optional expiration and revocation times, and its signed JWS. Verify the JWS with Apple on your server before granting server-owned access. A transaction is only delivered as purchased after StoreKit’s local verification succeeds. An unverified update has status: 'unverified' and an error, with no grantable transaction.

Start the update listener at app startup. On startup and after a pending purchase, call getUnfinishedTransactions() to recover transactions that arrived before the listener. Deliver content before calling finishTransaction(id). That method accepts only an unfinished, verified transaction. A finished transaction leaves the unfinished list; a nonconsumable entitlement remains available. getCurrentEntitlements() returns active nonconsumables and subscriptions. It does not include consumables, so use the unfinished list and your own delivery record for those. Call sync() from an explicit Restore Purchases action to ask App Store to sync the user’s transaction history; then read entitlements again. Apple documents the entitlement scope.

Invalid IDs and account tokens reject with E_PURCHASE_INPUT. Missing products reject with E_PURCHASE_PRODUCT; a purchase attempted while the app is inactive rejects with E_PURCHASE_INACTIVE. Product loading, purchase flow, unknown results, unverified transactions, finish of an ID not in StoreKit’s unfinished list, and restore failures use E_PURCHASE_PRODUCTS, E_PURCHASE_FAILED, E_PURCHASE_RESULT, E_PURCHASE_UNVERIFIED, E_PURCHASE_NOT_UNFINISHED, and E_PURCHASE_SYNC respectively. Purchases are implemented on iOS. On Android and web, getProducts() resolves [] and transaction listeners return a remover that does nothing. Purchase, entitlement, unfinished transaction, finish, and restore calls reject with Purchases.<verb> needs an iOS or Android build; these calls never report a verified purchase without native support.

Edit this page on GitHub.