expo-iap 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +1 -1
- package/.eslintrc.js +1 -0
- package/.prettierignore +1 -0
- package/CHANGELOG.md +6 -0
- package/CLAUDE.md +1 -1
- package/CONTRIBUTING.md +10 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +12 -12
- package/android/src/main/java/expo/modules/iap/PromiseUtils.kt +2 -2
- package/build/index.d.ts +28 -14
- package/build/index.d.ts.map +1 -1
- package/build/index.js +36 -15
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts +3 -4
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +2 -4
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +2 -3
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +1 -3
- package/build/modules/ios.js.map +1 -1
- package/build/purchase-error.d.ts +8 -10
- package/build/purchase-error.d.ts.map +1 -1
- package/build/purchase-error.js +4 -2
- package/build/purchase-error.js.map +1 -1
- package/build/types.d.ts +159 -204
- package/build/types.d.ts.map +1 -1
- package/build/types.js +1 -59
- package/build/types.js.map +1 -1
- package/build/useIAP.d.ts +5 -8
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js.map +1 -1
- package/ios/ExpoIap.podspec +1 -1
- package/ios/ExpoIapModule.swift +103 -89
- package/package.json +2 -1
- package/plugin/build/withIAP.js +4 -5
- package/plugin/src/withIAP.ts +4 -5
- package/scripts/update-types.mjs +61 -0
- package/src/index.ts +77 -29
- package/src/modules/android.ts +5 -7
- package/src/modules/ios.ts +3 -5
- package/src/purchase-error.ts +13 -16
- package/src/types.ts +183 -216
- package/src/useIAP.ts +10 -10
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
Purchase,
|
|
23
23
|
ErrorCode,
|
|
24
24
|
RequestPurchaseProps,
|
|
25
|
+
RequestPurchasePropsByPlatforms,
|
|
25
26
|
RequestSubscriptionPropsByPlatforms,
|
|
26
27
|
ProductSubscription,
|
|
27
28
|
PurchaseAndroid,
|
|
@@ -68,6 +69,58 @@ export const emitter = (ExpoIapModule || NativeModulesProxy.ExpoIap) as {
|
|
|
68
69
|
) => void;
|
|
69
70
|
};
|
|
70
71
|
|
|
72
|
+
/**
|
|
73
|
+
* TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.
|
|
74
|
+
*/
|
|
75
|
+
export type ProductTypeInput = 'inapp' | 'in-app' | 'subs';
|
|
76
|
+
export type InAppTypeInput = Exclude<ProductTypeInput, 'subs'>;
|
|
77
|
+
|
|
78
|
+
type PurchaseRequestInApp = {
|
|
79
|
+
request: RequestPurchasePropsByPlatforms;
|
|
80
|
+
type?: InAppTypeInput;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type PurchaseRequestSubscription = {
|
|
84
|
+
request: RequestSubscriptionPropsByPlatforms;
|
|
85
|
+
type: 'subs';
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type PurchaseRequestInput =
|
|
89
|
+
| PurchaseRequestInApp
|
|
90
|
+
| PurchaseRequestSubscription;
|
|
91
|
+
|
|
92
|
+
export type PurchaseRequest =
|
|
93
|
+
| {
|
|
94
|
+
request: RequestPurchaseProps;
|
|
95
|
+
type?: InAppTypeInput;
|
|
96
|
+
}
|
|
97
|
+
| {
|
|
98
|
+
request: RequestSubscriptionPropsByPlatforms;
|
|
99
|
+
type: 'subs';
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const normalizeProductType = (type?: ProductTypeInput) => {
|
|
103
|
+
if (type === 'inapp') {
|
|
104
|
+
console.warn(
|
|
105
|
+
"expo-iap: 'inapp' product type is deprecated and will be removed in v3.1.0. Use 'in-app' instead.",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!type || type === 'inapp' || type === 'in-app') {
|
|
110
|
+
return {
|
|
111
|
+
canonical: 'in-app' as const,
|
|
112
|
+
native: 'inapp' as const,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (type === 'subs') {
|
|
116
|
+
return {
|
|
117
|
+
canonical: 'subs' as const,
|
|
118
|
+
native: 'subs' as const,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw new Error(`Unsupported product type: ${type}`);
|
|
122
|
+
};
|
|
123
|
+
|
|
71
124
|
export const purchaseUpdatedListener = (
|
|
72
125
|
listener: (event: Purchase) => void,
|
|
73
126
|
) => {
|
|
@@ -146,14 +199,14 @@ export async function endConnection(): Promise<boolean> {
|
|
|
146
199
|
*
|
|
147
200
|
* @param params - Product fetch configuration
|
|
148
201
|
* @param params.skus - Array of product SKUs to fetch
|
|
149
|
-
* @param params.type - Type of products: '
|
|
202
|
+
* @param params.type - Type of products: 'in-app' for regular products (default) or 'subs' for subscriptions
|
|
150
203
|
*
|
|
151
204
|
* @example
|
|
152
205
|
* ```typescript
|
|
153
206
|
* // Regular products
|
|
154
207
|
* const products = await fetchProducts({
|
|
155
208
|
* skus: ['product1', 'product2'],
|
|
156
|
-
* type: '
|
|
209
|
+
* type: 'in-app'
|
|
157
210
|
* });
|
|
158
211
|
*
|
|
159
212
|
* // Subscriptions
|
|
@@ -165,10 +218,10 @@ export async function endConnection(): Promise<boolean> {
|
|
|
165
218
|
*/
|
|
166
219
|
export const fetchProducts = async ({
|
|
167
220
|
skus,
|
|
168
|
-
type
|
|
221
|
+
type,
|
|
169
222
|
}: {
|
|
170
223
|
skus: string[];
|
|
171
|
-
type?:
|
|
224
|
+
type?: ProductTypeInput;
|
|
172
225
|
}): Promise<Product[] | ProductSubscription[]> => {
|
|
173
226
|
if (!skus?.length) {
|
|
174
227
|
throw new PurchaseError({
|
|
@@ -177,8 +230,10 @@ export const fetchProducts = async ({
|
|
|
177
230
|
});
|
|
178
231
|
}
|
|
179
232
|
|
|
233
|
+
const {canonical, native} = normalizeProductType(type);
|
|
234
|
+
|
|
180
235
|
if (Platform.OS === 'ios') {
|
|
181
|
-
const rawItems = await ExpoIapModule.fetchProducts({skus, type});
|
|
236
|
+
const rawItems = await ExpoIapModule.fetchProducts({skus, type: native});
|
|
182
237
|
|
|
183
238
|
const filteredItems = rawItems.filter((item: unknown) => {
|
|
184
239
|
if (!isProductIOS(item)) {
|
|
@@ -193,13 +248,13 @@ export const fetchProducts = async ({
|
|
|
193
248
|
return isValid;
|
|
194
249
|
});
|
|
195
250
|
|
|
196
|
-
return
|
|
251
|
+
return canonical === 'in-app'
|
|
197
252
|
? (filteredItems as Product[])
|
|
198
253
|
: (filteredItems as ProductSubscription[]);
|
|
199
254
|
}
|
|
200
255
|
|
|
201
256
|
if (Platform.OS === 'android') {
|
|
202
|
-
const items = await ExpoIapModule.fetchProducts(
|
|
257
|
+
const items = await ExpoIapModule.fetchProducts(native, skus);
|
|
203
258
|
const filteredItems = items.filter((item: unknown) => {
|
|
204
259
|
if (!isProductAndroid(item)) return false;
|
|
205
260
|
return (
|
|
@@ -211,7 +266,7 @@ export const fetchProducts = async ({
|
|
|
211
266
|
);
|
|
212
267
|
});
|
|
213
268
|
|
|
214
|
-
return
|
|
269
|
+
return canonical === 'in-app'
|
|
215
270
|
? (filteredItems as Product[])
|
|
216
271
|
: (filteredItems as ProductSubscription[]);
|
|
217
272
|
}
|
|
@@ -284,22 +339,13 @@ const offerToRecordIOS = (
|
|
|
284
339
|
};
|
|
285
340
|
};
|
|
286
341
|
|
|
287
|
-
// Define discriminated union with explicit type parameter
|
|
288
|
-
type PurchaseRequest =
|
|
289
|
-
| {
|
|
290
|
-
request: RequestPurchaseProps;
|
|
291
|
-
type?: 'inapp';
|
|
292
|
-
}
|
|
293
|
-
| {
|
|
294
|
-
request: RequestSubscriptionPropsByPlatforms;
|
|
295
|
-
type: 'subs';
|
|
296
|
-
};
|
|
297
|
-
|
|
298
342
|
/**
|
|
299
343
|
* Helper to normalize request props to platform-specific format
|
|
300
344
|
*/
|
|
301
345
|
const normalizeRequestProps = (
|
|
302
|
-
request:
|
|
346
|
+
request:
|
|
347
|
+
| RequestPurchasePropsByPlatforms
|
|
348
|
+
| RequestSubscriptionPropsByPlatforms,
|
|
303
349
|
platform: 'ios' | 'android',
|
|
304
350
|
): any => {
|
|
305
351
|
// Platform-specific format - directly return the appropriate platform data
|
|
@@ -311,7 +357,7 @@ const normalizeRequestProps = (
|
|
|
311
357
|
*
|
|
312
358
|
* @param requestObj - Purchase request configuration
|
|
313
359
|
* @param requestObj.request - Platform-specific purchase parameters
|
|
314
|
-
* @param requestObj.type - Type of purchase: '
|
|
360
|
+
* @param requestObj.type - Type of purchase: 'in-app' for products (default) or 'subs' for subscriptions
|
|
315
361
|
*
|
|
316
362
|
* @example
|
|
317
363
|
* ```typescript
|
|
@@ -321,7 +367,7 @@ const normalizeRequestProps = (
|
|
|
321
367
|
* ios: { sku: productId },
|
|
322
368
|
* android: { skus: [productId] }
|
|
323
369
|
* },
|
|
324
|
-
* type: '
|
|
370
|
+
* type: 'in-app'
|
|
325
371
|
* });
|
|
326
372
|
*
|
|
327
373
|
* // Subscription purchase
|
|
@@ -338,9 +384,11 @@ const normalizeRequestProps = (
|
|
|
338
384
|
* ```
|
|
339
385
|
*/
|
|
340
386
|
export const requestPurchase = (
|
|
341
|
-
requestObj:
|
|
387
|
+
requestObj: PurchaseRequestInput,
|
|
342
388
|
): Promise<Purchase | Purchase[] | void> => {
|
|
343
|
-
const {request, type
|
|
389
|
+
const {request, type} = requestObj;
|
|
390
|
+
const {canonical, native} = normalizeProductType(type);
|
|
391
|
+
const isInAppPurchase = canonical === 'in-app';
|
|
344
392
|
|
|
345
393
|
if (Platform.OS === 'ios') {
|
|
346
394
|
const normalizedRequest = normalizeRequestProps(request, 'ios');
|
|
@@ -369,7 +417,7 @@ export const requestPurchase = (
|
|
|
369
417
|
withOffer: offer,
|
|
370
418
|
});
|
|
371
419
|
|
|
372
|
-
return
|
|
420
|
+
return purchase as Purchase;
|
|
373
421
|
})();
|
|
374
422
|
}
|
|
375
423
|
|
|
@@ -382,7 +430,7 @@ export const requestPurchase = (
|
|
|
382
430
|
);
|
|
383
431
|
}
|
|
384
432
|
|
|
385
|
-
if (
|
|
433
|
+
if (isInAppPurchase) {
|
|
386
434
|
const {
|
|
387
435
|
skus,
|
|
388
436
|
obfuscatedAccountIdAndroid,
|
|
@@ -392,7 +440,7 @@ export const requestPurchase = (
|
|
|
392
440
|
|
|
393
441
|
return (async () => {
|
|
394
442
|
return ExpoIapModule.requestPurchase({
|
|
395
|
-
type:
|
|
443
|
+
type: native,
|
|
396
444
|
skuArr: skus,
|
|
397
445
|
purchaseToken: undefined,
|
|
398
446
|
replacementMode: -1,
|
|
@@ -404,7 +452,7 @@ export const requestPurchase = (
|
|
|
404
452
|
})();
|
|
405
453
|
}
|
|
406
454
|
|
|
407
|
-
if (
|
|
455
|
+
if (canonical === 'subs') {
|
|
408
456
|
const {
|
|
409
457
|
skus,
|
|
410
458
|
obfuscatedAccountIdAndroid,
|
|
@@ -417,7 +465,7 @@ export const requestPurchase = (
|
|
|
417
465
|
|
|
418
466
|
return (async () => {
|
|
419
467
|
return ExpoIapModule.requestPurchase({
|
|
420
|
-
type:
|
|
468
|
+
type: native,
|
|
421
469
|
skuArr: skus,
|
|
422
470
|
purchaseToken,
|
|
423
471
|
replacementMode: replacementModeAndroid,
|
package/src/modules/android.ts
CHANGED
|
@@ -6,18 +6,16 @@ import ExpoIapModule from '../ExpoIapModule';
|
|
|
6
6
|
|
|
7
7
|
// Types
|
|
8
8
|
import type {ReceiptValidationResultAndroid, VoidResult} from '../types';
|
|
9
|
-
import {Platform as PurchasePlatform} from '../types';
|
|
10
9
|
|
|
11
10
|
// Type guards
|
|
12
|
-
export function isProductAndroid<
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export function isProductAndroid<T extends {platform?: string}>(
|
|
12
|
+
item: unknown,
|
|
13
|
+
): item is T & {platform: 'android'} {
|
|
15
14
|
return (
|
|
16
15
|
item != null &&
|
|
17
16
|
typeof item === 'object' &&
|
|
18
17
|
'platform' in item &&
|
|
19
|
-
(
|
|
20
|
-
(item as any).platform === PurchasePlatform.Android)
|
|
18
|
+
(item as any).platform === 'android'
|
|
21
19
|
);
|
|
22
20
|
}
|
|
23
21
|
|
|
@@ -73,7 +71,7 @@ export const deepLinkToSubscriptionsAndroid = async ({
|
|
|
73
71
|
* @param {string} params.productId - product id for your in app product.
|
|
74
72
|
* @param {string} params.productToken - token for your purchase (called 'token' in the API documentation).
|
|
75
73
|
* @param {string} params.accessToken - OAuth access token with androidpublisher scope. Required for authentication.
|
|
76
|
-
* @param {boolean} params.isSub - whether this is subscription or
|
|
74
|
+
* @param {boolean} params.isSub - whether this is subscription or in-app. `true` for subscription.
|
|
77
75
|
* @returns {Promise<ReceiptAndroid>}
|
|
78
76
|
*/
|
|
79
77
|
export const validateReceiptAndroid = async ({
|
package/src/modules/ios.ts
CHANGED
|
@@ -13,7 +13,6 @@ import type {
|
|
|
13
13
|
ReceiptValidationResultIOS,
|
|
14
14
|
} from '../types';
|
|
15
15
|
import type {PurchaseError} from '../purchase-error';
|
|
16
|
-
import {Platform as PurchasePlatform} from '../types';
|
|
17
16
|
import {Linking} from 'react-native';
|
|
18
17
|
|
|
19
18
|
export type TransactionEvent = {
|
|
@@ -24,15 +23,14 @@ export type TransactionEvent = {
|
|
|
24
23
|
// Listeners
|
|
25
24
|
|
|
26
25
|
// Type guards
|
|
27
|
-
export function isProductIOS<T extends {platform?: string
|
|
26
|
+
export function isProductIOS<T extends {platform?: string}>(
|
|
28
27
|
item: unknown,
|
|
29
|
-
): item is T & {platform:
|
|
28
|
+
): item is T & {platform: 'ios'} {
|
|
30
29
|
return (
|
|
31
30
|
item != null &&
|
|
32
31
|
typeof item === 'object' &&
|
|
33
32
|
'platform' in item &&
|
|
34
|
-
(
|
|
35
|
-
(item as any).platform === PurchasePlatform.Ios)
|
|
33
|
+
(item as any).platform === 'ios'
|
|
36
34
|
);
|
|
37
35
|
}
|
|
38
36
|
|
package/src/purchase-error.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import {NATIVE_ERROR_CODES} from './ExpoIapModule';
|
|
2
|
-
import {ErrorCode,
|
|
3
|
-
|
|
4
|
-
/** Platform identifiers supported by {@link PurchaseError}. */
|
|
5
|
-
export type PurchaseErrorPlatform = Platform | 'ios' | 'android';
|
|
2
|
+
import {ErrorCode, IapPlatform} from './types';
|
|
6
3
|
|
|
7
4
|
/** Properties used to construct a {@link PurchaseError}. */
|
|
8
5
|
export interface PurchaseErrorProps {
|
|
@@ -11,7 +8,7 @@ export interface PurchaseErrorProps {
|
|
|
11
8
|
debugMessage?: string;
|
|
12
9
|
code?: ErrorCode;
|
|
13
10
|
productId?: string;
|
|
14
|
-
platform?:
|
|
11
|
+
platform?: IapPlatform;
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
/** Shape of raw platform error objects coming from native modules. */
|
|
@@ -26,10 +23,10 @@ type PlatformErrorData = {
|
|
|
26
23
|
const toStandardizedCode = (errorCode: ErrorCode): string =>
|
|
27
24
|
errorCode.startsWith('E_') ? errorCode : `E_${errorCode}`;
|
|
28
25
|
|
|
29
|
-
const normalizePlatform = (
|
|
30
|
-
platform
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
const normalizePlatform = (platform: IapPlatform): 'ios' | 'android' =>
|
|
27
|
+
typeof platform === 'string' && platform.toLowerCase() === 'ios'
|
|
28
|
+
? 'ios'
|
|
29
|
+
: 'android';
|
|
33
30
|
|
|
34
31
|
const OPENIAP_ERROR_CODE_SET: Set<string> = new Set(
|
|
35
32
|
Object.values(ErrorCode).map((code) => toStandardizedCode(code)),
|
|
@@ -103,7 +100,7 @@ export class PurchaseError extends Error {
|
|
|
103
100
|
public debugMessage?: string;
|
|
104
101
|
public code?: ErrorCode;
|
|
105
102
|
public productId?: string;
|
|
106
|
-
public platform?:
|
|
103
|
+
public platform?: IapPlatform;
|
|
107
104
|
|
|
108
105
|
constructor(
|
|
109
106
|
message: string,
|
|
@@ -111,7 +108,7 @@ export class PurchaseError extends Error {
|
|
|
111
108
|
debugMessage?: string,
|
|
112
109
|
code?: ErrorCode,
|
|
113
110
|
productId?: string,
|
|
114
|
-
platform?:
|
|
111
|
+
platform?: IapPlatform,
|
|
115
112
|
);
|
|
116
113
|
constructor(props: PurchaseErrorProps);
|
|
117
114
|
constructor(
|
|
@@ -120,7 +117,7 @@ export class PurchaseError extends Error {
|
|
|
120
117
|
debugMessage?: string,
|
|
121
118
|
code?: ErrorCode,
|
|
122
119
|
productId?: string,
|
|
123
|
-
platform?:
|
|
120
|
+
platform?: IapPlatform,
|
|
124
121
|
) {
|
|
125
122
|
super(
|
|
126
123
|
typeof messageOrProps === 'string'
|
|
@@ -150,7 +147,7 @@ export class PurchaseError extends Error {
|
|
|
150
147
|
*/
|
|
151
148
|
static fromPlatformError(
|
|
152
149
|
errorData: PlatformErrorData,
|
|
153
|
-
platform:
|
|
150
|
+
platform: IapPlatform,
|
|
154
151
|
): PurchaseError {
|
|
155
152
|
const normalizedPlatform = normalizePlatform(platform);
|
|
156
153
|
|
|
@@ -197,7 +194,7 @@ export const ErrorCodeUtils = {
|
|
|
197
194
|
*/
|
|
198
195
|
fromPlatformCode: (
|
|
199
196
|
platformCode: string | number,
|
|
200
|
-
_platform:
|
|
197
|
+
_platform: IapPlatform,
|
|
201
198
|
): ErrorCode => {
|
|
202
199
|
if (typeof platformCode === 'string' && platformCode.startsWith('E_')) {
|
|
203
200
|
if (OPENIAP_ERROR_CODE_SET.has(platformCode)) {
|
|
@@ -241,7 +238,7 @@ export const ErrorCodeUtils = {
|
|
|
241
238
|
*/
|
|
242
239
|
toPlatformCode: (
|
|
243
240
|
errorCode: ErrorCode,
|
|
244
|
-
_platform:
|
|
241
|
+
_platform: IapPlatform,
|
|
245
242
|
): string | number => {
|
|
246
243
|
const standardized = toStandardizedCode(errorCode);
|
|
247
244
|
const native = (NATIVE_ERROR_CODES as Record<string, string | number>)[
|
|
@@ -254,7 +251,7 @@ export const ErrorCodeUtils = {
|
|
|
254
251
|
*/
|
|
255
252
|
isValidForPlatform: (
|
|
256
253
|
errorCode: ErrorCode,
|
|
257
|
-
platform:
|
|
254
|
+
platform: IapPlatform,
|
|
258
255
|
): boolean => {
|
|
259
256
|
const standardized = toStandardizedCode(errorCode);
|
|
260
257
|
if (
|