Skip to content

Commit 823a754

Browse files
committed
docs: clarify that transactionReceipt is now purchaseToken
1 parent 55dedae commit 823a754

File tree

16 files changed

+68
-111
lines changed

16 files changed

+68
-111
lines changed

docs/blog/2025-09-13-v3.0.0.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ import AdFitTopFixed from "@site/src/uis/AdFitTopFixed";
3232

3333
## 💥 Breaking Changes
3434

35+
- Removed fields
36+
37+
- Removed `transactionReceipt` (use `purchaseToken` on all platforms). On iOS, `purchaseToken` contains the JWS representation previously accessed via receipts.
38+
- Removed `jwsRepresentationIOS` in favor of the unified `purchaseToken`.
39+
3540
- Removed functions
3641

3742
- `getProducts`, `getSubscriptions`, `requestProducts`, `requestSubscription`
@@ -107,7 +112,7 @@ const restored = await restorePurchases({
107112
import {finishTransaction, type Purchase} from 'expo-iap';
108113

109114
function getToken(p: Purchase) {
110-
return p.purchaseToken; // iOS JWS or Android token
115+
return p.purchaseToken; // iOS: JWS, Android: purchaseToken
111116
}
112117

113118
await finishTransaction({purchase: somePurchase, isConsumable: true});

docs/docs/api/methods/core-methods.md

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ import AdFitTopFixed from "@site/src/uis/AdFitTopFixed";
1313
This section covers the core methods available in expo-iap for managing in-app purchases.
1414

1515
Note: expo-iap aligns with the OpenIAP API surface. For canonical cross-SDK API docs, see:
16-
- https://www.openiap.dev/docs/apis
1716

18-
17+
- [OpenIAP APIs](https://www.openiap.dev/docs/apis)
1918

2019
## Unified APIs
2120

@@ -73,10 +72,6 @@ const cleanup = async () => {
7372

7473
**Note:** When using the `useIAP` hook, connection cleanup is automatic.
7574

76-
77-
78-
79-
8075
## fetchProducts()
8176

8277
Fetches product or subscription information from the store.
@@ -125,8 +120,6 @@ const loadSubscriptions = async () => {
125120

126121
[**Product Interface**](../types.md#product)
127122

128-
129-
130123
## requestPurchase()
131124

132125
Initiates a purchase request for products or subscriptions.
@@ -189,8 +182,6 @@ const buySubscription = async (subscriptionId: string, subscription?: any) => {
189182
};
190183
```
191184

192-
193-
194185
### Detailed Platform Examples
195186

196187
#### iOS Only
@@ -244,8 +235,6 @@ For subscription status checks after a purchase or when listing entitlements:
244235
- iOS: Check `expirationDateIOS` to determine if the subscription is still active
245236
- Android: Check `autoRenewingAndroid` to see if auto‑renewal has been canceled
246237

247-
248-
249238
## finishTransaction()
250239

251240
Completes a purchase transaction. Must be called after successful receipt validation.
@@ -318,8 +307,6 @@ const restorePurchases = async () => {
318307

319308
**Returns:** `Promise<Purchase[]>`
320309

321-
322-
323310
## deepLinkToSubscriptions()
324311

325312
Opens the platform-specific subscription management UI.
@@ -460,7 +447,6 @@ interface Purchase {
460447
id: string; // Transaction identifier
461448
productId: string;
462449
transactionDate: number;
463-
transactionReceipt: string;
464450
purchaseToken?: string; // Unified token (iOS JWS or Android token)
465451

466452
// iOS-specific properties
@@ -488,7 +474,6 @@ interface Purchase {
488474

489475
The following iOS‑only helpers expose StoreKit and App Store specific capabilities. Most day‑to‑day flows are covered by the cross‑platform Core Methods above; use these only when you need iOS features.
490476

491-
492477
### clearTransactionIOS()
493478

494479
Clears all pending transactions from the iOS payment queue. Useful if your app previously crashed or missed finishing transactions.
@@ -565,7 +550,7 @@ Checks if the user is eligible for an introductory offer for a subscription grou
565550
import {isEligibleForIntroOfferIOS, fetchProducts} from 'expo-iap';
566551

567552
// Example: derive group ID from a fetched subscription product
568-
const [sub] = await fetchProducts({ skus: ['your_sub_sku'], type: 'subs' });
553+
const [sub] = await fetchProducts({skus: ['your_sub_sku'], type: 'subs'});
569554
const groupId = sub?.subscriptionInfoIOS?.subscriptionGroupId ?? '';
570555
const eligible = groupId ? await isEligibleForIntroOfferIOS(groupId) : false;
571556
```
@@ -710,7 +695,10 @@ const fetchAppTransaction = async () => {
710695
const appTransaction = await getAppTransactionIOS();
711696
if (appTransaction) {
712697
console.log('App Transaction ID:', appTransaction.appTransactionId);
713-
console.log('Original Purchase Date:', new Date(appTransaction.originalPurchaseDate));
698+
console.log(
699+
'Original Purchase Date:',
700+
new Date(appTransaction.originalPurchaseDate),
701+
);
714702
console.log('Device Verification:', appTransaction.deviceVerification);
715703
}
716704
} catch (error) {
@@ -748,17 +736,19 @@ Acknowledge a non‑consumable purchase or subscription on Android.
748736
```ts
749737
import {acknowledgePurchaseAndroid} from 'expo-iap';
750738

751-
await acknowledgePurchaseAndroid({ token: purchase.purchaseToken! });
739+
await acknowledgePurchaseAndroid({token: purchase.purchaseToken!});
752740
```
753741

754742
Notes:
743+
755744
- finishTransaction() calls this automatically when `isConsumable` is false. You typically do not need to call it directly.
756745

757746
#### consumePurchaseAndroid
758747

759748
Consume a purchase (consumables only). This marks an item as consumed so it can be purchased again.
760749

761750
Notes:
751+
762752
- finishTransaction() calls Android consumption automatically when `isConsumable` is true.
763753
- A direct JS helper is not exposed; consumption is handled internally via the native module.
764754

docs/docs/api/types.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import AdFitTopFixed from "@site/src/uis/AdFitTopFixed";
77
This page contains the TypeScript types and interfaces used throughout the expo-iap library.
88

99
Note: expo-iap aligns closely with the OpenIAP type schema. For canonical definitions and cross-SDK parity, see OpenIAP Types:
10-
- https://www.openiap.dev/docs/types
10+
11+
- [OpenIAP Types](https://www.openiap.dev/docs/types)
1112

1213
## Core Types
1314

@@ -75,7 +76,6 @@ interface Purchase {
7576
id: string; // Transaction identifier
7677
productId: string;
7778
transactionDate: number;
78-
transactionReceipt: string;
7979
purchaseToken?: string; // Unified token (iOS JWS or Android token)
8080
}
8181
```
@@ -156,7 +156,7 @@ The following sections describe the complete TypeScript surface for expo‑iap.
156156
### Core
157157

158158
```ts
159-
export type ChangeEventPayload = { value: string };
159+
export type ChangeEventPayload = {value: string};
160160

161161
// iOS detailed product types
162162
export enum ProductTypeIOS {
@@ -195,19 +195,18 @@ export type PurchaseCommon = {
195195
productId: string;
196196
ids?: string[];
197197
transactionDate: number;
198-
transactionReceipt: string;
199198
purchaseToken?: string;
200199
platform?: string;
201200
quantity: number;
202201
purchaseState: PurchaseState;
203202
isAutoRenewing: boolean;
204203
};
205204

206-
export type ProductSubscriptionCommon = ProductCommon & { type: 'subs' };
205+
export type ProductSubscriptionCommon = ProductCommon & {type: 'subs'};
207206

208207
// Platform tags
209-
export type IosPlatform = { platform: 'ios' };
210-
export type AndroidPlatform = { platform: 'android' };
208+
export type IosPlatform = {platform: 'ios'};
209+
export type AndroidPlatform = {platform: 'android'};
211210
```
212211

213212
### iOS
@@ -220,7 +219,7 @@ type SubscriptionOffer = {
220219
displayPrice: string;
221220
id: string;
222221
paymentMode: PaymentMode;
223-
period: { unit: SubscriptionIosPeriod; value: number };
222+
period: {unit: SubscriptionIosPeriod; value: number};
224223
periodCount: number;
225224
price: number;
226225
type: 'introductory' | 'promotional';
@@ -230,7 +229,7 @@ type SubscriptionInfo = {
230229
introductoryOffer?: SubscriptionOffer;
231230
promotionalOffers?: SubscriptionOffer[];
232231
subscriptionGroupId: string;
233-
subscriptionPeriod: { unit: SubscriptionIosPeriod; value: number };
232+
subscriptionPeriod: {unit: SubscriptionIosPeriod; value: number};
234233
};
235234

236235
export type Discount = {
@@ -285,7 +284,7 @@ export type PurchaseIOS = PurchaseCommon & {
285284
transactionReasonIOS?: 'PURCHASE' | 'RENEWAL' | string;
286285
revocationDateIOS?: number;
287286
revocationReasonIOS?: string;
288-
offerIOS?: { id: string; type: string; paymentMode: string };
287+
offerIOS?: {id: string; type: string; paymentMode: string};
289288
currencyCodeIOS?: string;
290289
currencySymbolIOS?: string;
291290
countryCodeIOS?: string;
@@ -337,7 +336,7 @@ type PricingPhaseAndroid = {
337336
recurrenceMode: number;
338337
};
339338

340-
type PricingPhasesAndroid = { pricingPhaseList: PricingPhaseAndroid[] };
339+
type PricingPhasesAndroid = {pricingPhaseList: PricingPhaseAndroid[]};
341340

342341
type ProductSubscriptionAndroidOfferDetail = {
343342
basePlanId: string;
@@ -395,7 +394,7 @@ export type ProductPurchase =
395394
| (PurchaseIOS & IosPlatform);
396395

397396
export type SubscriptionPurchase =
398-
| (PurchaseAndroid & AndroidPlatform & { autoRenewingAndroid: boolean })
397+
| (PurchaseAndroid & AndroidPlatform & {autoRenewingAndroid: boolean})
399398
| (PurchaseIOS & IosPlatform);
400399

401400
export type Purchase =
@@ -430,7 +429,7 @@ export interface RequestSubscriptionAndroidProps
430429
extends RequestPurchaseAndroidProps {
431430
readonly purchaseTokenAndroid?: string;
432431
readonly replacementModeAndroid?: number;
433-
readonly subscriptionOffers: { sku: string; offerToken: string }[];
432+
readonly subscriptionOffers: {sku: string; offerToken: string}[];
434433
}
435434

436435
export interface RequestPurchasePropsByPlatforms {
@@ -549,7 +548,9 @@ export interface IapContext {
549548
type?: 'inapp' | 'subs';
550549
}): Promise<Purchase | Purchase[] | void>;
551550

552-
finishTransaction(params: FinishTransactionParams): Promise<PurchaseResult | boolean>;
551+
finishTransaction(
552+
params: FinishTransactionParams,
553+
): Promise<PurchaseResult | boolean>;
553554

554555
getAvailablePurchases(options?: PurchaseOptions): Promise<Purchase[]>;
555556

docs/docs/examples/purchase-flow.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This example walks through a clean purchase flow using expo-iap with the `useIAP
1414

1515
View the full example source:
1616

17-
- GitHub: https://github.com/hyochan/expo-iap/blob/main/example/app/purchase-flow.tsx
17+
- GitHub: [example/app/purchase-flow.tsx](https://github.com/hyochan/expo-iap/blob/main/example/app/purchase-flow.tsx)
1818

1919
## Flow Overview
2020

@@ -36,7 +36,7 @@ View the full example source:
3636

3737
`finishTransaction({ purchase, isConsumable })`
3838

39-
```
39+
```txt
4040
Connect → Fetch Products → Request Purchase → Server Validate → Finish Transaction
4141
```
4242

@@ -77,14 +77,15 @@ Use the modern, platform‑specific request container (v2.7.0+). This avoids man
7777
```tsx
7878
await requestPurchase({
7979
request: {
80-
ios: { sku: productId, quantity: 1 },
81-
android: { skus: [productId] },
80+
ios: {sku: productId, quantity: 1},
81+
android: {skus: [productId]},
8282
},
8383
type: 'inapp',
8484
});
8585
```
8686

8787
Notes:
88+
8889
- Keep `andDangerouslyFinishTransactionAutomatically` off (default) to validate first.
8990

9091
### Key iOS Options
@@ -99,7 +100,6 @@ Purchase objects have different properties on iOS and Android. When accessing pl
99100
```tsx
100101
// Unified fields
101102
const token = purchase.purchaseToken; // iOS JWS or Android token
102-
const receipt = purchase.transactionReceipt; // Raw receipt (iOS), useful for debugging
103103

104104
// Android-only helpers
105105
// const pkg = (purchase as PurchaseAndroid).packageNameAndroid;

docs/docs/examples/subscription-flow.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ sidebar_position: 2
55
---
66

77
<!-- This document was renamed from subscription-manager.md to subscription-flow.md -->
8+
89
import AdFitTopFixed from "@site/src/uis/AdFitTopFixed";
910

1011
# Subscriptions Flow
@@ -15,7 +16,7 @@ This example walks through a practical subscriptions flow with expo-iap. It mirr
1516

1617
View the full example source:
1718

18-
- GitHub: https://github.com/hyochan/expo-iap/blob/main/example/app/subscription-flow.tsx
19+
- GitHub: [example/app/subscription-flow.tsx](https://github.com/hyochan/expo-iap/blob/main/example/app/subscription-flow.tsx)
1920

2021
## Important: Platform-Specific Subscription Properties
2122

@@ -44,7 +45,7 @@ When checking subscription status, different platforms provide different propert
4445

4546
View the full example source:
4647

47-
- GitHub: https://github.com/hyochan/expo-iap/blob/main/example/app/subscription-flow.tsx
48+
- GitHub: [example/app/subscription-flow.tsx](https://github.com/hyochan/expo-iap/blob/main/example/app/subscription-flow.tsx)
4849

4950
```tsx
5051
import React, {useEffect, useState} from 'react';
@@ -221,11 +222,9 @@ export default function SubscriptionManager() {
221222
'Content-Type': 'application/json',
222223
},
223224
body: JSON.stringify({
224-
receipt: purchase.transactionReceipt,
225225
productId: purchase.productId,
226-
// Platform-specific fields
227226
purchaseToken: purchase.purchaseToken, // Unified field (iOS: JWS, Android: purchaseToken)
228-
transactionId: purchase.transactionId, // iOS
227+
packageName: (purchase as PurchaseAndroid)?.packageNameAndroid, // Will be needed in android
229228
}),
230229
},
231230
);
@@ -710,7 +709,7 @@ await requestPurchase({
710709

711710
Subscription validation requires different approaches:
712711

713-
- **iOS**: Send `transactionReceipt` to Apple's validation servers
712+
- **iOS**: Send `purchaseToken` which was ~~`transactionReceipt`~~ to Apple's validation servers
714713
- **Android**: Send `purchaseToken` and `packageName` to Google Play validation
715714

716715
This is handled in the `validateSubscriptionStatus` function with platform-specific logic.

docs/docs/guides/getting-started.md

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ const productIds = [
147147

148148
useEffect(() => {
149149
if (connected) {
150-
fetchProducts({ skus: productIds, type: 'inapp' });
150+
fetchProducts({skus: productIds, type: 'inapp'});
151151
}
152152
}, [connected, fetchProducts]);
153153
```
@@ -188,32 +188,18 @@ The `useIAP` hook automatically handles purchase updates. When a purchase is suc
188188
```tsx
189189
useEffect(() => {
190190
if (currentPurchase) {
191-
// Platform-specific validation
191+
// Unified validation (iOS/Android)
192192
const validateAndFinish = async () => {
193193
try {
194-
if (Platform.OS === 'ios') {
195-
// iOS: Simple validation
196-
await validateReceiptOnServer({
197-
receiptData: currentPurchase.transactionReceipt,
198-
productId: currentPurchase.productId,
199-
});
200-
} else if (Platform.OS === 'android') {
201-
// Android: Check required parameters first
202-
const purchaseToken = currentPurchase.purchaseToken;
203-
const packageName = currentPurchase.packageNameAndroid;
204-
205-
if (!purchaseToken || !packageName) {
206-
throw new Error(
207-
'Android validation requires packageName and purchaseToken',
208-
);
209-
}
210-
211-
await validateReceiptOnServer({
212-
packageName,
213-
purchaseToken,
214-
productId: currentPurchase.productId,
215-
});
216-
}
194+
const purchaseToken = currentPurchase.purchaseToken;
195+
const productId = currentPurchase.productId;
196+
const packageName = currentPurchase.packageNameAndroid; // iOS: undefined
197+
198+
await validateReceiptOnServer({
199+
productId,
200+
purchaseToken,
201+
...(packageName ? {packageName} : {}),
202+
});
217203

218204
// If validation successful, finish the transaction
219205
await finishTransaction({purchase: currentPurchase});

0 commit comments

Comments
 (0)