expo-iap 3.0.7 → 3.0.8

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.
@@ -5,7 +5,11 @@ import {Linking} from 'react-native';
5
5
  import ExpoIapModule from '../ExpoIapModule';
6
6
 
7
7
  // Types
8
- import type {ReceiptValidationResultAndroid, VoidResult} from '../types';
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}>(
@@ -22,25 +26,24 @@ export function isProductAndroid<T extends {platform?: string}>(
22
26
  /**
23
27
  * Deep link to subscriptions screen on Android.
24
28
  * @param {Object} params - The parameters object
25
- * @param {string} params.sku - The product's SKU (on Android)
26
- * @param {string} params.packageName - The package name of your Android app (e.g., 'com.example.app')
29
+ * @param {string} params.skuAndroid - The product's SKU (on Android)
30
+ * @param {string} params.packageNameAndroid - The package name of your Android app (e.g., 'com.example.app')
27
31
  * @returns {Promise<void>}
28
32
  *
29
33
  * @example
30
34
  * ```typescript
31
35
  * await deepLinkToSubscriptionsAndroid({
32
- * sku: 'subscription_id',
33
- * packageName: 'com.example.app'
36
+ * skuAndroid: 'subscription_id',
37
+ * packageNameAndroid: 'com.example.app'
34
38
  * });
35
39
  * ```
36
40
  */
37
- export const deepLinkToSubscriptionsAndroid = async ({
38
- sku,
39
- packageName,
40
- }: {
41
- sku?: string;
42
- packageName?: string;
43
- }): Promise<void> => {
41
+ export const deepLinkToSubscriptionsAndroid = async (
42
+ options?: DeepLinkOptions | null,
43
+ ): Promise<void> => {
44
+ const sku = options?.skuAndroid ?? undefined;
45
+ const packageName = options?.packageNameAndroid ?? undefined;
46
+
44
47
  // Prefer native deep link implementation via OpenIAP module
45
48
  if (ExpoIapModule?.deepLinkToSubscriptionsAndroid) {
46
49
  return (ExpoIapModule as any).deepLinkToSubscriptionsAndroid({
@@ -117,28 +120,26 @@ export const validateReceiptAndroid = async ({
117
120
  * @param {string} params.token - The product's token (on Android)
118
121
  * @returns {Promise<VoidResult | void>}
119
122
  */
120
- export const acknowledgePurchaseAndroid = async ({
121
- token,
122
- }: {
123
- token: string;
124
- }): Promise<VoidResult | boolean | void> => {
125
- const result = await ExpoIapModule.acknowledgePurchaseAndroid(token);
123
+ export const acknowledgePurchaseAndroid: MutationField<
124
+ 'acknowledgePurchaseAndroid'
125
+ > = async (purchaseToken) => {
126
+ const result = await ExpoIapModule.acknowledgePurchaseAndroid(purchaseToken);
126
127
 
127
- if (typeof result === 'boolean' || typeof result === 'undefined') {
128
+ if (typeof result === 'boolean') {
128
129
  return result;
129
130
  }
130
131
 
131
132
  if (result && typeof result === 'object') {
132
133
  const record = result as Record<string, unknown>;
133
134
  if (typeof record.success === 'boolean') {
134
- return {success: record.success};
135
+ return record.success;
135
136
  }
136
137
  if (typeof record.responseCode === 'number') {
137
- return {success: record.responseCode === 0};
138
+ return record.responseCode === 0;
138
139
  }
139
140
  }
140
141
 
141
- return {success: true};
142
+ return true;
142
143
  };
143
144
 
144
145
  /**
@@ -6,14 +6,18 @@ import ExpoIapModule from '../ExpoIapModule';
6
6
 
7
7
  // Types
8
8
  import type {
9
- Product,
9
+ MutationField,
10
+ ProductIOS,
10
11
  Purchase,
11
- SubscriptionStatusIOS,
12
- AppTransaction,
12
+ PurchaseIOS,
13
+ QueryField,
14
+ ReceiptValidationProps,
13
15
  ReceiptValidationResultIOS,
16
+ SubscriptionStatusIOS,
14
17
  } from '../types';
15
18
  import type {PurchaseError} from '../purchase-error';
16
19
  import {Linking} from 'react-native';
20
+ import {normalizePurchaseId, normalizePurchaseList} from '../utils/purchase';
17
21
 
18
22
  export type TransactionEvent = {
19
23
  transaction?: Purchase;
@@ -44,8 +48,8 @@ export function isProductIOS<T extends {platform?: string}>(
44
48
  *
45
49
  * @platform iOS
46
50
  */
47
- export const syncIOS = (): Promise<null> => {
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
- groupId: string,
62
- ): Promise<boolean> => {
63
- return ExpoIapModule.isEligibleForIntroOfferIOS(groupId);
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
- sku: string,
77
- ): Promise<SubscriptionStatusIOS[]> => {
78
- return ExpoIapModule.subscriptionStatusIOS(sku);
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 = (sku: string): Promise<Purchase> => {
91
- return ExpoIapModule.currentEntitlementIOS(sku);
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 normalizePurchaseId((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 = (sku: string): Promise<Purchase> => {
104
- return ExpoIapModule.latestTransactionIOS(sku);
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 normalizePurchaseId((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
- type RefundRequestStatus = 'success' | 'userCancelled';
117
- export const beginRefundRequestIOS = (
118
- sku: string,
119
- ): Promise<RefundRequestStatus> => {
120
- return ExpoIapModule.beginRefundRequestIOS(sku);
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 = (): Promise<Purchase[]> => {
133
- return ExpoIapModule.showManageSubscriptionsIOS();
158
+ export const showManageSubscriptionsIOS: MutationField<
159
+ 'showManageSubscriptionsIOS'
160
+ > = async () => {
161
+ const purchases = await ExpoIapModule.showManageSubscriptionsIOS();
162
+ return normalizePurchaseList((purchases ?? []) as PurchaseIOS[]);
134
163
  };
135
164
 
136
165
  /**
@@ -143,10 +172,12 @@ export const showManageSubscriptionsIOS = (): Promise<Purchase[]> => {
143
172
  *
144
173
  * @returns {Promise<string>} Base64 encoded receipt data
145
174
  */
146
- export const getReceiptIOS = (): Promise<string> => {
175
+ export const getReceiptDataIOS: QueryField<'getReceiptDataIOS'> = async () => {
147
176
  return ExpoIapModule.getReceiptDataIOS();
148
177
  };
149
178
 
179
+ export const getReceiptIOS = getReceiptDataIOS;
180
+
150
181
  /**
151
182
  * Check if a transaction is verified through StoreKit 2.
152
183
  * StoreKit 2 performs local verification of transaction JWS signatures.
@@ -157,7 +188,12 @@ export const getReceiptIOS = (): Promise<string> => {
157
188
  *
158
189
  * @platform iOS
159
190
  */
160
- export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
191
+ export const isTransactionVerifiedIOS: QueryField<
192
+ 'isTransactionVerifiedIOS'
193
+ > = async (sku) => {
194
+ if (!sku) {
195
+ throw new Error('isTransactionVerifiedIOS requires a SKU');
196
+ }
161
197
  return ExpoIapModule.isTransactionVerifiedIOS(sku);
162
198
  };
163
199
 
@@ -171,8 +207,14 @@ export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
171
207
  *
172
208
  * @platform iOS
173
209
  */
174
- export const getTransactionJwsIOS = (sku: string): Promise<string> => {
175
- return ExpoIapModule.getTransactionJwsIOS(sku);
210
+ export const getTransactionJwsIOS: QueryField<'getTransactionJwsIOS'> = async (
211
+ sku,
212
+ ) => {
213
+ if (!sku) {
214
+ throw new Error('getTransactionJwsIOS requires a SKU');
215
+ }
216
+ const jws = await ExpoIapModule.getTransactionJwsIOS(sku);
217
+ return jws ?? '';
176
218
  };
177
219
 
178
220
  /**
@@ -190,13 +232,34 @@ export const getTransactionJwsIOS = (sku: string): Promise<string> => {
190
232
  * latestTransaction?: Purchase;
191
233
  * }>}
192
234
  */
193
- export const validateReceiptIOS = async (
194
- sku: string,
195
- ): Promise<ReceiptValidationResultIOS> => {
196
- const result = await ExpoIapModule.validateReceiptIOS(sku);
197
- return result;
235
+ const validateReceiptIOSImpl = async (
236
+ props: ReceiptValidationProps | string,
237
+ ) => {
238
+ const sku =
239
+ typeof props === 'string' ? props : (props as ReceiptValidationProps)?.sku;
240
+
241
+ if (!sku) {
242
+ throw new Error('validateReceiptIOS requires a SKU');
243
+ }
244
+
245
+ const result = (await ExpoIapModule.validateReceiptIOS(
246
+ sku,
247
+ )) as ReceiptValidationResultIOS;
248
+ const normalizedLatest = normalizePurchaseId(
249
+ result.latestTransaction ?? undefined,
250
+ );
251
+ if (normalizedLatest === result.latestTransaction) {
252
+ return result;
253
+ }
254
+ return {
255
+ ...result,
256
+ latestTransaction: normalizedLatest ?? null,
257
+ };
198
258
  };
199
259
 
260
+ export const validateReceiptIOS =
261
+ validateReceiptIOSImpl as QueryField<'validateReceiptIOS'>;
262
+
200
263
  /**
201
264
  * Present the code redemption sheet for offer codes (iOS only).
202
265
  * This allows users to redeem promotional codes for in-app purchases and subscriptions.
@@ -208,8 +271,10 @@ export const validateReceiptIOS = async (
208
271
  *
209
272
  * @platform iOS
210
273
  */
211
- export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
212
- return ExpoIapModule.presentCodeRedemptionSheetIOS();
274
+ export const presentCodeRedemptionSheetIOS: MutationField<
275
+ 'presentCodeRedemptionSheetIOS'
276
+ > = async () => {
277
+ return Boolean(await ExpoIapModule.presentCodeRedemptionSheetIOS());
213
278
  };
214
279
 
215
280
  /**
@@ -226,8 +291,10 @@ export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
226
291
  * @platform iOS
227
292
  * @since iOS 16.0
228
293
  */
229
- export const getAppTransactionIOS = (): Promise<AppTransaction | null> => {
230
- return ExpoIapModule.getAppTransactionIOS();
294
+ export const getAppTransactionIOS: QueryField<
295
+ 'getAppTransactionIOS'
296
+ > = async () => {
297
+ return (await ExpoIapModule.getAppTransactionIOS()) ?? null;
231
298
  };
232
299
 
233
300
  /**
@@ -240,8 +307,11 @@ export const getAppTransactionIOS = (): Promise<AppTransaction | null> => {
240
307
  *
241
308
  * @platform iOS
242
309
  */
243
- export const getPromotedProductIOS = (): Promise<Product | null> => {
244
- return ExpoIapModule.getPromotedProductIOS();
310
+ export const getPromotedProductIOS: QueryField<
311
+ 'getPromotedProductIOS'
312
+ > = async () => {
313
+ const product = await ExpoIapModule.getPromotedProductIOS();
314
+ return (product ?? null) as ProductIOS | null;
245
315
  };
246
316
 
247
317
  /**
@@ -253,8 +323,11 @@ export const getPromotedProductIOS = (): Promise<Product | null> => {
253
323
  *
254
324
  * @platform iOS
255
325
  */
256
- export const requestPurchaseOnPromotedProductIOS = (): Promise<void> => {
257
- return ExpoIapModule.requestPurchaseOnPromotedProductIOS();
326
+ export const requestPurchaseOnPromotedProductIOS: MutationField<
327
+ 'requestPurchaseOnPromotedProductIOS'
328
+ > = async () => {
329
+ await ExpoIapModule.requestPurchaseOnPromotedProductIOS();
330
+ return true;
258
331
  };
259
332
 
260
333
  /**
@@ -263,8 +336,11 @@ export const requestPurchaseOnPromotedProductIOS = (): Promise<void> => {
263
336
  * @returns Promise resolving to array of pending transactions
264
337
  * @platform iOS
265
338
  */
266
- export const getPendingTransactionsIOS = (): Promise<any[]> => {
267
- return ExpoIapModule.getPendingTransactionsIOS();
339
+ export const getPendingTransactionsIOS: QueryField<
340
+ 'getPendingTransactionsIOS'
341
+ > = async () => {
342
+ const transactions = await ExpoIapModule.getPendingTransactionsIOS();
343
+ return normalizePurchaseList((transactions ?? []) as PurchaseIOS[]);
268
344
  };
269
345
 
270
346
  /**
@@ -273,8 +349,10 @@ export const getPendingTransactionsIOS = (): Promise<any[]> => {
273
349
  * @returns Promise resolving when transaction is cleared
274
350
  * @platform iOS
275
351
  */
276
- export const clearTransactionIOS = (): Promise<void> => {
277
- return ExpoIapModule.clearTransactionIOS();
352
+ export const clearTransactionIOS: MutationField<
353
+ 'clearTransactionIOS'
354
+ > = async () => {
355
+ return Boolean(await ExpoIapModule.clearTransactionIOS());
278
356
  };
279
357
 
280
358
  /**