expo-iap 3.0.7 → 3.1.0
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/CLAUDE.md +14 -2
- package/CONTRIBUTING.md +19 -0
- package/README.md +18 -6
- package/android/build.gradle +24 -1
- package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +69 -0
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +190 -59
- package/build/index.d.ts +32 -111
- package/build/index.d.ts.map +1 -1
- package/build/index.js +198 -243
- package/build/index.js.map +1 -1
- package/build/modules/android.d.ts +7 -12
- package/build/modules/android.d.ts.map +1 -1
- package/build/modules/android.js +15 -12
- package/build/modules/android.js.map +1 -1
- package/build/modules/ios.d.ts +35 -36
- package/build/modules/ios.d.ts.map +1 -1
- package/build/modules/ios.js +101 -35
- package/build/modules/ios.js.map +1 -1
- package/build/types.d.ts +107 -82
- package/build/types.d.ts.map +1 -1
- package/build/types.js +1 -0
- package/build/types.js.map +1 -1
- package/build/useIAP.d.ts +7 -12
- package/build/useIAP.d.ts.map +1 -1
- package/build/useIAP.js +49 -23
- package/build/useIAP.js.map +1 -1
- package/build/utils/errorMapping.d.ts +32 -23
- package/build/utils/errorMapping.d.ts.map +1 -1
- package/build/utils/errorMapping.js +117 -22
- package/build/utils/errorMapping.js.map +1 -1
- package/ios/ExpoIap.podspec +3 -2
- package/ios/ExpoIapHelper.swift +96 -0
- package/ios/ExpoIapLog.swift +127 -0
- package/ios/ExpoIapModule.swift +218 -340
- package/openiap-versions.json +5 -0
- package/package.json +2 -2
- package/plugin/build/withIAP.js +6 -4
- package/plugin/src/withIAP.ts +14 -4
- package/scripts/update-types.mjs +20 -1
- package/src/index.ts +280 -356
- package/src/modules/android.ts +25 -23
- package/src/modules/ios.ts +138 -48
- package/src/types.ts +139 -91
- package/src/useIAP.ts +91 -58
- package/src/utils/errorMapping.ts +203 -23
- package/.copilot-instructions.md +0 -321
- package/.cursorrules +0 -321
- package/build/purchase-error.d.ts +0 -67
- package/build/purchase-error.d.ts.map +0 -1
- package/build/purchase-error.js +0 -166
- package/build/purchase-error.js.map +0 -1
- package/src/purchase-error.ts +0 -265
package/src/modules/android.ts
CHANGED
|
@@ -5,7 +5,11 @@ import {Linking} from 'react-native';
|
|
|
5
5
|
import ExpoIapModule from '../ExpoIapModule';
|
|
6
6
|
|
|
7
7
|
// Types
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
DeepLinkOptions,
|
|
10
|
+
MutationField,
|
|
11
|
+
ReceiptValidationResultAndroid,
|
|
12
|
+
} from '../types';
|
|
9
13
|
|
|
10
14
|
// Type guards
|
|
11
15
|
export function isProductAndroid<T extends {platform?: string}>(
|
|
@@ -15,32 +19,32 @@ export function isProductAndroid<T extends {platform?: string}>(
|
|
|
15
19
|
item != null &&
|
|
16
20
|
typeof item === 'object' &&
|
|
17
21
|
'platform' in item &&
|
|
18
|
-
(item as any).platform === '
|
|
22
|
+
typeof (item as any).platform === 'string' &&
|
|
23
|
+
(item as any).platform.toLowerCase() === 'android'
|
|
19
24
|
);
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
/**
|
|
23
28
|
* Deep link to subscriptions screen on Android.
|
|
24
29
|
* @param {Object} params - The parameters object
|
|
25
|
-
* @param {string} params.
|
|
26
|
-
* @param {string} params.
|
|
30
|
+
* @param {string} params.skuAndroid - The product's SKU (on Android)
|
|
31
|
+
* @param {string} params.packageNameAndroid - The package name of your Android app (e.g., 'com.example.app')
|
|
27
32
|
* @returns {Promise<void>}
|
|
28
33
|
*
|
|
29
34
|
* @example
|
|
30
35
|
* ```typescript
|
|
31
36
|
* await deepLinkToSubscriptionsAndroid({
|
|
32
|
-
*
|
|
33
|
-
*
|
|
37
|
+
* skuAndroid: 'subscription_id',
|
|
38
|
+
* packageNameAndroid: 'com.example.app'
|
|
34
39
|
* });
|
|
35
40
|
* ```
|
|
36
41
|
*/
|
|
37
|
-
export const deepLinkToSubscriptionsAndroid = async (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}): Promise<void> => {
|
|
42
|
+
export const deepLinkToSubscriptionsAndroid = async (
|
|
43
|
+
options?: DeepLinkOptions | null,
|
|
44
|
+
): Promise<void> => {
|
|
45
|
+
const sku = options?.skuAndroid ?? undefined;
|
|
46
|
+
const packageName = options?.packageNameAndroid ?? undefined;
|
|
47
|
+
|
|
44
48
|
// Prefer native deep link implementation via OpenIAP module
|
|
45
49
|
if (ExpoIapModule?.deepLinkToSubscriptionsAndroid) {
|
|
46
50
|
return (ExpoIapModule as any).deepLinkToSubscriptionsAndroid({
|
|
@@ -117,28 +121,26 @@ export const validateReceiptAndroid = async ({
|
|
|
117
121
|
* @param {string} params.token - The product's token (on Android)
|
|
118
122
|
* @returns {Promise<VoidResult | void>}
|
|
119
123
|
*/
|
|
120
|
-
export const acknowledgePurchaseAndroid
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}): Promise<VoidResult | boolean | void> => {
|
|
125
|
-
const result = await ExpoIapModule.acknowledgePurchaseAndroid(token);
|
|
124
|
+
export const acknowledgePurchaseAndroid: MutationField<
|
|
125
|
+
'acknowledgePurchaseAndroid'
|
|
126
|
+
> = async (purchaseToken) => {
|
|
127
|
+
const result = await ExpoIapModule.acknowledgePurchaseAndroid(purchaseToken);
|
|
126
128
|
|
|
127
|
-
if (typeof result === 'boolean'
|
|
129
|
+
if (typeof result === 'boolean') {
|
|
128
130
|
return result;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
if (result && typeof result === 'object') {
|
|
132
134
|
const record = result as Record<string, unknown>;
|
|
133
135
|
if (typeof record.success === 'boolean') {
|
|
134
|
-
return
|
|
136
|
+
return record.success;
|
|
135
137
|
}
|
|
136
138
|
if (typeof record.responseCode === 'number') {
|
|
137
|
-
return
|
|
139
|
+
return record.responseCode === 0;
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
141
|
-
return
|
|
143
|
+
return true;
|
|
142
144
|
};
|
|
143
145
|
|
|
144
146
|
/**
|
package/src/modules/ios.ts
CHANGED
|
@@ -6,14 +6,17 @@ import ExpoIapModule from '../ExpoIapModule';
|
|
|
6
6
|
|
|
7
7
|
// Types
|
|
8
8
|
import type {
|
|
9
|
-
|
|
9
|
+
MutationField,
|
|
10
|
+
ProductIOS,
|
|
10
11
|
Purchase,
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
PurchaseIOS,
|
|
13
|
+
QueryField,
|
|
14
|
+
ReceiptValidationProps,
|
|
13
15
|
ReceiptValidationResultIOS,
|
|
16
|
+
SubscriptionStatusIOS,
|
|
14
17
|
} from '../types';
|
|
15
|
-
import type {PurchaseError} from '../
|
|
16
|
-
import {Linking} from 'react-native';
|
|
18
|
+
import type {PurchaseError} from '../utils/errorMapping';
|
|
19
|
+
import {Linking, Platform} from 'react-native';
|
|
17
20
|
|
|
18
21
|
export type TransactionEvent = {
|
|
19
22
|
transaction?: Purchase;
|
|
@@ -30,7 +33,8 @@ export function isProductIOS<T extends {platform?: string}>(
|
|
|
30
33
|
item != null &&
|
|
31
34
|
typeof item === 'object' &&
|
|
32
35
|
'platform' in item &&
|
|
33
|
-
(item as any).platform === '
|
|
36
|
+
typeof (item as any).platform === 'string' &&
|
|
37
|
+
(item as any).platform.toLowerCase() === 'ios'
|
|
34
38
|
);
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -44,8 +48,8 @@ export function isProductIOS<T extends {platform?: string}>(
|
|
|
44
48
|
*
|
|
45
49
|
* @platform iOS
|
|
46
50
|
*/
|
|
47
|
-
export const syncIOS = ()
|
|
48
|
-
return ExpoIapModule.syncIOS();
|
|
51
|
+
export const syncIOS: MutationField<'syncIOS'> = async () => {
|
|
52
|
+
return Boolean(await ExpoIapModule.syncIOS());
|
|
49
53
|
};
|
|
50
54
|
|
|
51
55
|
/**
|
|
@@ -57,10 +61,13 @@ export const syncIOS = (): Promise<null> => {
|
|
|
57
61
|
*
|
|
58
62
|
* @platform iOS
|
|
59
63
|
*/
|
|
60
|
-
export const isEligibleForIntroOfferIOS
|
|
61
|
-
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
+
export const isEligibleForIntroOfferIOS: QueryField<
|
|
65
|
+
'isEligibleForIntroOfferIOS'
|
|
66
|
+
> = async (groupID) => {
|
|
67
|
+
if (!groupID) {
|
|
68
|
+
throw new Error('isEligibleForIntroOfferIOS requires a groupID');
|
|
69
|
+
}
|
|
70
|
+
return ExpoIapModule.isEligibleForIntroOfferIOS(groupID);
|
|
64
71
|
};
|
|
65
72
|
|
|
66
73
|
/**
|
|
@@ -72,10 +79,14 @@ export const isEligibleForIntroOfferIOS = (
|
|
|
72
79
|
*
|
|
73
80
|
* @platform iOS
|
|
74
81
|
*/
|
|
75
|
-
export const subscriptionStatusIOS
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
|
|
82
|
+
export const subscriptionStatusIOS: QueryField<
|
|
83
|
+
'subscriptionStatusIOS'
|
|
84
|
+
> = async (sku) => {
|
|
85
|
+
if (!sku) {
|
|
86
|
+
throw new Error('subscriptionStatusIOS requires a SKU');
|
|
87
|
+
}
|
|
88
|
+
const status = await ExpoIapModule.subscriptionStatusIOS(sku);
|
|
89
|
+
return (status ?? []) as SubscriptionStatusIOS[];
|
|
79
90
|
};
|
|
80
91
|
|
|
81
92
|
/**
|
|
@@ -87,8 +98,14 @@ export const subscriptionStatusIOS = (
|
|
|
87
98
|
*
|
|
88
99
|
* @platform iOS
|
|
89
100
|
*/
|
|
90
|
-
export const currentEntitlementIOS
|
|
91
|
-
|
|
101
|
+
export const currentEntitlementIOS: QueryField<
|
|
102
|
+
'currentEntitlementIOS'
|
|
103
|
+
> = async (sku) => {
|
|
104
|
+
if (!sku) {
|
|
105
|
+
throw new Error('currentEntitlementIOS requires a SKU');
|
|
106
|
+
}
|
|
107
|
+
const purchase = await ExpoIapModule.currentEntitlementIOS(sku);
|
|
108
|
+
return (purchase ?? null) as PurchaseIOS | null;
|
|
92
109
|
};
|
|
93
110
|
|
|
94
111
|
/**
|
|
@@ -100,8 +117,14 @@ export const currentEntitlementIOS = (sku: string): Promise<Purchase> => {
|
|
|
100
117
|
*
|
|
101
118
|
* @platform iOS
|
|
102
119
|
*/
|
|
103
|
-
export const latestTransactionIOS
|
|
104
|
-
|
|
120
|
+
export const latestTransactionIOS: QueryField<'latestTransactionIOS'> = async (
|
|
121
|
+
sku,
|
|
122
|
+
) => {
|
|
123
|
+
if (!sku) {
|
|
124
|
+
throw new Error('latestTransactionIOS requires a SKU');
|
|
125
|
+
}
|
|
126
|
+
const transaction = await ExpoIapModule.latestTransactionIOS(sku);
|
|
127
|
+
return (transaction ?? null) as PurchaseIOS | null;
|
|
105
128
|
};
|
|
106
129
|
|
|
107
130
|
/**
|
|
@@ -113,11 +136,14 @@ export const latestTransactionIOS = (sku: string): Promise<Purchase> => {
|
|
|
113
136
|
*
|
|
114
137
|
* @platform iOS
|
|
115
138
|
*/
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
|
|
139
|
+
export const beginRefundRequestIOS: MutationField<
|
|
140
|
+
'beginRefundRequestIOS'
|
|
141
|
+
> = async (sku) => {
|
|
142
|
+
if (!sku) {
|
|
143
|
+
throw new Error('beginRefundRequestIOS requires a SKU');
|
|
144
|
+
}
|
|
145
|
+
const status = await ExpoIapModule.beginRefundRequestIOS(sku);
|
|
146
|
+
return status ?? null;
|
|
121
147
|
};
|
|
122
148
|
|
|
123
149
|
/**
|
|
@@ -129,8 +155,11 @@ export const beginRefundRequestIOS = (
|
|
|
129
155
|
*
|
|
130
156
|
* @platform iOS
|
|
131
157
|
*/
|
|
132
|
-
export const showManageSubscriptionsIOS
|
|
133
|
-
|
|
158
|
+
export const showManageSubscriptionsIOS: MutationField<
|
|
159
|
+
'showManageSubscriptionsIOS'
|
|
160
|
+
> = async () => {
|
|
161
|
+
const purchases = await ExpoIapModule.showManageSubscriptionsIOS();
|
|
162
|
+
return (purchases ?? []) as PurchaseIOS[];
|
|
134
163
|
};
|
|
135
164
|
|
|
136
165
|
/**
|
|
@@ -143,10 +172,34 @@ export const showManageSubscriptionsIOS = (): Promise<Purchase[]> => {
|
|
|
143
172
|
*
|
|
144
173
|
* @returns {Promise<string>} Base64 encoded receipt data
|
|
145
174
|
*/
|
|
146
|
-
export const
|
|
175
|
+
export const getReceiptDataIOS: QueryField<'getReceiptDataIOS'> = async () => {
|
|
147
176
|
return ExpoIapModule.getReceiptDataIOS();
|
|
148
177
|
};
|
|
149
178
|
|
|
179
|
+
export const getReceiptIOS = getReceiptDataIOS;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Retrieves the current storefront information from the iOS App Store.
|
|
183
|
+
*
|
|
184
|
+
* @returns Promise resolving to the storefront country code
|
|
185
|
+
* @throws Error if called on non-iOS platform
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const storefront = await getStorefrontIOS();
|
|
190
|
+
* console.log(storefront); // 'US'
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* @platform iOS
|
|
194
|
+
*/
|
|
195
|
+
export const getStorefrontIOS: QueryField<'getStorefrontIOS'> = async () => {
|
|
196
|
+
if (Platform.OS !== 'ios') {
|
|
197
|
+
console.warn('getStorefrontIOS: This method is only available on iOS');
|
|
198
|
+
return '';
|
|
199
|
+
}
|
|
200
|
+
return ExpoIapModule.getStorefrontIOS();
|
|
201
|
+
};
|
|
202
|
+
|
|
150
203
|
/**
|
|
151
204
|
* Check if a transaction is verified through StoreKit 2.
|
|
152
205
|
* StoreKit 2 performs local verification of transaction JWS signatures.
|
|
@@ -157,7 +210,12 @@ export const getReceiptIOS = (): Promise<string> => {
|
|
|
157
210
|
*
|
|
158
211
|
* @platform iOS
|
|
159
212
|
*/
|
|
160
|
-
export const isTransactionVerifiedIOS
|
|
213
|
+
export const isTransactionVerifiedIOS: QueryField<
|
|
214
|
+
'isTransactionVerifiedIOS'
|
|
215
|
+
> = async (sku) => {
|
|
216
|
+
if (!sku) {
|
|
217
|
+
throw new Error('isTransactionVerifiedIOS requires a SKU');
|
|
218
|
+
}
|
|
161
219
|
return ExpoIapModule.isTransactionVerifiedIOS(sku);
|
|
162
220
|
};
|
|
163
221
|
|
|
@@ -171,8 +229,14 @@ export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
|
|
|
171
229
|
*
|
|
172
230
|
* @platform iOS
|
|
173
231
|
*/
|
|
174
|
-
export const getTransactionJwsIOS
|
|
175
|
-
|
|
232
|
+
export const getTransactionJwsIOS: QueryField<'getTransactionJwsIOS'> = async (
|
|
233
|
+
sku,
|
|
234
|
+
) => {
|
|
235
|
+
if (!sku) {
|
|
236
|
+
throw new Error('getTransactionJwsIOS requires a SKU');
|
|
237
|
+
}
|
|
238
|
+
const jws = await ExpoIapModule.getTransactionJwsIOS(sku);
|
|
239
|
+
return jws ?? '';
|
|
176
240
|
};
|
|
177
241
|
|
|
178
242
|
/**
|
|
@@ -190,13 +254,24 @@ export const getTransactionJwsIOS = (sku: string): Promise<string> => {
|
|
|
190
254
|
* latestTransaction?: Purchase;
|
|
191
255
|
* }>}
|
|
192
256
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
const
|
|
197
|
-
|
|
257
|
+
const validateReceiptIOSImpl = async (
|
|
258
|
+
props: ReceiptValidationProps | string,
|
|
259
|
+
) => {
|
|
260
|
+
const sku =
|
|
261
|
+
typeof props === 'string' ? props : (props as ReceiptValidationProps)?.sku;
|
|
262
|
+
|
|
263
|
+
if (!sku) {
|
|
264
|
+
throw new Error('validateReceiptIOS requires a SKU');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return (await ExpoIapModule.validateReceiptIOS(
|
|
268
|
+
sku,
|
|
269
|
+
)) as ReceiptValidationResultIOS;
|
|
198
270
|
};
|
|
199
271
|
|
|
272
|
+
export const validateReceiptIOS =
|
|
273
|
+
validateReceiptIOSImpl as QueryField<'validateReceiptIOS'>;
|
|
274
|
+
|
|
200
275
|
/**
|
|
201
276
|
* Present the code redemption sheet for offer codes (iOS only).
|
|
202
277
|
* This allows users to redeem promotional codes for in-app purchases and subscriptions.
|
|
@@ -208,8 +283,10 @@ export const validateReceiptIOS = async (
|
|
|
208
283
|
*
|
|
209
284
|
* @platform iOS
|
|
210
285
|
*/
|
|
211
|
-
export const presentCodeRedemptionSheetIOS
|
|
212
|
-
|
|
286
|
+
export const presentCodeRedemptionSheetIOS: MutationField<
|
|
287
|
+
'presentCodeRedemptionSheetIOS'
|
|
288
|
+
> = async () => {
|
|
289
|
+
return Boolean(await ExpoIapModule.presentCodeRedemptionSheetIOS());
|
|
213
290
|
};
|
|
214
291
|
|
|
215
292
|
/**
|
|
@@ -226,8 +303,10 @@ export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
|
|
|
226
303
|
* @platform iOS
|
|
227
304
|
* @since iOS 16.0
|
|
228
305
|
*/
|
|
229
|
-
export const getAppTransactionIOS
|
|
230
|
-
|
|
306
|
+
export const getAppTransactionIOS: QueryField<
|
|
307
|
+
'getAppTransactionIOS'
|
|
308
|
+
> = async () => {
|
|
309
|
+
return (await ExpoIapModule.getAppTransactionIOS()) ?? null;
|
|
231
310
|
};
|
|
232
311
|
|
|
233
312
|
/**
|
|
@@ -240,8 +319,11 @@ export const getAppTransactionIOS = (): Promise<AppTransaction | null> => {
|
|
|
240
319
|
*
|
|
241
320
|
* @platform iOS
|
|
242
321
|
*/
|
|
243
|
-
export const getPromotedProductIOS
|
|
244
|
-
|
|
322
|
+
export const getPromotedProductIOS: QueryField<
|
|
323
|
+
'getPromotedProductIOS'
|
|
324
|
+
> = async () => {
|
|
325
|
+
const product = await ExpoIapModule.getPromotedProductIOS();
|
|
326
|
+
return (product ?? null) as ProductIOS | null;
|
|
245
327
|
};
|
|
246
328
|
|
|
247
329
|
/**
|
|
@@ -253,8 +335,11 @@ export const getPromotedProductIOS = (): Promise<Product | null> => {
|
|
|
253
335
|
*
|
|
254
336
|
* @platform iOS
|
|
255
337
|
*/
|
|
256
|
-
export const requestPurchaseOnPromotedProductIOS
|
|
257
|
-
|
|
338
|
+
export const requestPurchaseOnPromotedProductIOS: MutationField<
|
|
339
|
+
'requestPurchaseOnPromotedProductIOS'
|
|
340
|
+
> = async () => {
|
|
341
|
+
await ExpoIapModule.requestPurchaseOnPromotedProductIOS();
|
|
342
|
+
return true;
|
|
258
343
|
};
|
|
259
344
|
|
|
260
345
|
/**
|
|
@@ -263,8 +348,11 @@ export const requestPurchaseOnPromotedProductIOS = (): Promise<void> => {
|
|
|
263
348
|
* @returns Promise resolving to array of pending transactions
|
|
264
349
|
* @platform iOS
|
|
265
350
|
*/
|
|
266
|
-
export const getPendingTransactionsIOS
|
|
267
|
-
|
|
351
|
+
export const getPendingTransactionsIOS: QueryField<
|
|
352
|
+
'getPendingTransactionsIOS'
|
|
353
|
+
> = async () => {
|
|
354
|
+
const transactions = await ExpoIapModule.getPendingTransactionsIOS();
|
|
355
|
+
return (transactions ?? []) as PurchaseIOS[];
|
|
268
356
|
};
|
|
269
357
|
|
|
270
358
|
/**
|
|
@@ -273,8 +361,10 @@ export const getPendingTransactionsIOS = (): Promise<any[]> => {
|
|
|
273
361
|
* @returns Promise resolving when transaction is cleared
|
|
274
362
|
* @platform iOS
|
|
275
363
|
*/
|
|
276
|
-
export const clearTransactionIOS
|
|
277
|
-
|
|
364
|
+
export const clearTransactionIOS: MutationField<
|
|
365
|
+
'clearTransactionIOS'
|
|
366
|
+
> = async () => {
|
|
367
|
+
return Boolean(await ExpoIapModule.clearTransactionIOS());
|
|
278
368
|
};
|
|
279
369
|
|
|
280
370
|
/**
|