expo-iap 2.5.4-rc.1 → 2.6.1

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.
@@ -1,13 +1,21 @@
1
+ // External dependencies
1
2
  import {Platform} from 'react-native';
3
+
4
+ // Internal modules
2
5
  import {purchaseUpdatedListener} from '..';
6
+ import ExpoIapModule from '../ExpoIapModule';
7
+
8
+ // Types
3
9
  import {
4
10
  ProductPurchase,
5
11
  PurchaseError,
6
12
  Purchase,
7
13
  SubscriptionPurchase,
8
14
  } from '../ExpoIap.types';
9
- import type {ProductStatusIos} from '../types/ExpoIapIos.types';
10
- import ExpoIapModule from '../ExpoIapModule';
15
+ import type {
16
+ ProductStatusIos,
17
+ AppTransactionIOS,
18
+ } from '../types/ExpoIapIos.types';
11
19
 
12
20
  export type TransactionEvent = {
13
21
  transaction?: ProductPurchase;
@@ -85,49 +93,136 @@ export function isProductIos<T extends {platform?: string}>(
85
93
  /**
86
94
  * Sync state with Appstore (iOS only)
87
95
  * https://developer.apple.com/documentation/storekit/appstore/3791906-sync
96
+ *
97
+ * @returns Promise resolving to null on success
98
+ * @throws Error if called on non-iOS platform
99
+ *
100
+ * @platform iOS
88
101
  */
89
- export const sync = (): Promise<null> => ExpoIapModule.sync();
102
+ export const syncIOS = (): Promise<null> => {
103
+ if (Platform.OS !== 'ios') {
104
+ throw new Error('syncIOS: This method is only available on iOS');
105
+ }
106
+ return ExpoIapModule.sync();
107
+ };
90
108
 
91
109
  /**
110
+ * Check if user is eligible for introductory offer
111
+ *
112
+ * @param groupID The subscription group ID
113
+ * @returns Promise resolving to true if eligible
114
+ * @throws Error if called on non-iOS platform
92
115
  *
116
+ * @platform iOS
93
117
  */
94
- export const isEligibleForIntroOffer = (groupID: string): Promise<boolean> =>
95
- ExpoIapModule.isEligibleForIntroOffer(groupID);
118
+ export const isEligibleForIntroOfferIOS = (
119
+ groupID: string,
120
+ ): Promise<boolean> => {
121
+ if (Platform.OS !== 'ios') {
122
+ throw new Error(
123
+ 'isEligibleForIntroOfferIOS: This method is only available on iOS',
124
+ );
125
+ }
126
+ return ExpoIapModule.isEligibleForIntroOffer(groupID);
127
+ };
96
128
 
97
129
  /**
130
+ * Get subscription status for a specific SKU
131
+ *
132
+ * @param sku The product SKU
133
+ * @returns Promise resolving to array of subscription status
134
+ * @throws Error if called on non-iOS platform
98
135
  *
136
+ * @platform iOS
99
137
  */
100
-
101
- export const subscriptionStatus = (sku: string): Promise<ProductStatusIos[]> =>
102
- ExpoIapModule.subscriptionStatus(sku);
138
+ export const subscriptionStatusIOS = (
139
+ sku: string,
140
+ ): Promise<ProductStatusIos[]> => {
141
+ if (Platform.OS !== 'ios') {
142
+ throw new Error(
143
+ 'subscriptionStatusIOS: This method is only available on iOS',
144
+ );
145
+ }
146
+ return ExpoIapModule.subscriptionStatus(sku);
147
+ };
103
148
 
104
149
  /**
150
+ * Get current entitlement for a specific SKU
151
+ *
152
+ * @param sku The product SKU
153
+ * @returns Promise resolving to current entitlement
154
+ * @throws Error if called on non-iOS platform
105
155
  *
156
+ * @platform iOS
106
157
  */
107
- export const currentEntitlement = (sku: string): Promise<ProductPurchase> =>
108
- ExpoIapModule.currentEntitlement(sku);
158
+ export const currentEntitlementIOS = (
159
+ sku: string,
160
+ ): Promise<ProductPurchase> => {
161
+ if (Platform.OS !== 'ios') {
162
+ throw new Error(
163
+ 'currentEntitlementIOS: This method is only available on iOS',
164
+ );
165
+ }
166
+ return ExpoIapModule.currentEntitlement(sku);
167
+ };
109
168
 
110
169
  /**
170
+ * Get latest transaction for a specific SKU
171
+ *
172
+ * @param sku The product SKU
173
+ * @returns Promise resolving to latest transaction
174
+ * @throws Error if called on non-iOS platform
111
175
  *
176
+ * @platform iOS
112
177
  */
113
- export const latestTransaction = (sku: string): Promise<ProductPurchase> =>
114
- ExpoIapModule.latestTransaction(sku);
178
+ export const latestTransactionIOS = (sku: string): Promise<ProductPurchase> => {
179
+ if (Platform.OS !== 'ios') {
180
+ throw new Error(
181
+ 'latestTransactionIOS: This method is only available on iOS',
182
+ );
183
+ }
184
+ return ExpoIapModule.latestTransaction(sku);
185
+ };
115
186
 
116
187
  /**
188
+ * Begin refund request for a specific SKU
189
+ *
190
+ * @param sku The product SKU
191
+ * @returns Promise resolving to refund request status
192
+ * @throws Error if called on non-iOS platform
117
193
  *
194
+ * @platform iOS
118
195
  */
119
196
  type RefundRequestStatus = 'success' | 'userCancelled';
120
- export const beginRefundRequest = (sku: string): Promise<RefundRequestStatus> =>
121
- ExpoIapModule.beginRefundRequest(sku);
197
+ export const beginRefundRequestIOS = (
198
+ sku: string,
199
+ ): Promise<RefundRequestStatus> => {
200
+ if (Platform.OS !== 'ios') {
201
+ throw new Error(
202
+ 'beginRefundRequestIOS: This method is only available on iOS',
203
+ );
204
+ }
205
+ return ExpoIapModule.beginRefundRequest(sku);
206
+ };
122
207
 
123
208
  /**
124
209
  * Shows the system UI for managing subscriptions.
125
210
  * When the user changes subscription renewal status, the system will emit events to
126
211
  * purchaseUpdatedListener and transactionUpdatedIos listeners.
127
- * @returns {Promise<null>}
212
+ *
213
+ * @returns Promise resolving to null on success
214
+ * @throws Error if called on non-iOS platform
215
+ *
216
+ * @platform iOS
128
217
  */
129
- export const showManageSubscriptions = (): Promise<null> =>
130
- ExpoIapModule.showManageSubscriptions();
218
+ export const showManageSubscriptionsIOS = (): Promise<null> => {
219
+ if (Platform.OS !== 'ios') {
220
+ throw new Error(
221
+ 'showManageSubscriptionsIOS: This method is only available on iOS',
222
+ );
223
+ }
224
+ return ExpoIapModule.showManageSubscriptions();
225
+ };
131
226
 
132
227
  /**
133
228
  * Get the receipt data from the iOS device.
@@ -139,7 +234,7 @@ export const showManageSubscriptions = (): Promise<null> =>
139
234
  *
140
235
  * @returns {Promise<string>} Base64 encoded receipt data
141
236
  */
142
- export const getReceiptIos = (): Promise<string> => {
237
+ export const getReceiptIOS = (): Promise<string> => {
143
238
  if (Platform.OS !== 'ios') {
144
239
  throw new Error('This method is only available on iOS');
145
240
  }
@@ -150,12 +245,17 @@ export const getReceiptIos = (): Promise<string> => {
150
245
  * Check if a transaction is verified through StoreKit 2.
151
246
  * StoreKit 2 performs local verification of transaction JWS signatures.
152
247
  *
153
- * @param {string} sku The product's SKU (on iOS)
154
- * @returns {Promise<boolean>} True if the transaction is verified
248
+ * @param sku The product's SKU (on iOS)
249
+ * @returns Promise resolving to true if the transaction is verified
250
+ * @throws Error if called on non-iOS platform
251
+ *
252
+ * @platform iOS
155
253
  */
156
- export const isTransactionVerified = (sku: string): Promise<boolean> => {
254
+ export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
157
255
  if (Platform.OS !== 'ios') {
158
- throw new Error('This method is only available on iOS');
256
+ throw new Error(
257
+ 'isTransactionVerifiedIOS: This method is only available on iOS',
258
+ );
159
259
  }
160
260
  return ExpoIapModule.isTransactionVerified(sku);
161
261
  };
@@ -164,12 +264,17 @@ export const isTransactionVerified = (sku: string): Promise<boolean> => {
164
264
  * Get the JWS representation of a purchase for server-side verification.
165
265
  * The JWS (JSON Web Signature) can be verified on your server using Apple's public keys.
166
266
  *
167
- * @param {string} sku The product's SKU (on iOS)
168
- * @returns {Promise<string>} JWS representation of the transaction
267
+ * @param sku The product's SKU (on iOS)
268
+ * @returns Promise resolving to JWS representation of the transaction
269
+ * @throws Error if called on non-iOS platform
270
+ *
271
+ * @platform iOS
169
272
  */
170
- export const getTransactionJws = (sku: string): Promise<string> => {
273
+ export const getTransactionJwsIOS = (sku: string): Promise<string> => {
171
274
  if (Platform.OS !== 'ios') {
172
- throw new Error('This method is only available on iOS');
275
+ throw new Error(
276
+ 'getTransactionJwsIOS: This method is only available on iOS',
277
+ );
173
278
  }
174
279
  return ExpoIapModule.getTransactionJws(sku);
175
280
  };
@@ -189,7 +294,7 @@ export const getTransactionJws = (sku: string): Promise<string> => {
189
294
  * latestTransaction?: ProductPurchase;
190
295
  * }>}
191
296
  */
192
- export const validateReceiptIos = async (
297
+ export const validateReceiptIOS = async (
193
298
  sku: string,
194
299
  ): Promise<{
195
300
  isValid: boolean;
@@ -201,6 +306,188 @@ export const validateReceiptIos = async (
201
306
  throw new Error('This method is only available on iOS');
202
307
  }
203
308
 
204
- const result = await ExpoIapModule.validateReceiptIos(sku);
309
+ const result = await ExpoIapModule.validateReceiptIOS(sku);
205
310
  return result;
206
311
  };
312
+
313
+ /**
314
+ * Present the code redemption sheet for offer codes (iOS only).
315
+ * This allows users to redeem promotional codes for in-app purchases and subscriptions.
316
+ *
317
+ * Note: This only works on real devices, not simulators.
318
+ *
319
+ * @returns Promise resolving to true if the sheet was presented successfully
320
+ * @throws Error if called on non-iOS platform or tvOS
321
+ *
322
+ * @platform iOS
323
+ */
324
+ export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
325
+ if (Platform.OS !== 'ios') {
326
+ throw new Error(
327
+ 'presentCodeRedemptionSheetIOS: This method is only available on iOS',
328
+ );
329
+ }
330
+ return ExpoIapModule.presentCodeRedemptionSheet();
331
+ };
332
+
333
+ /**
334
+ * Get app transaction information (iOS 16.0+).
335
+ * AppTransaction represents the initial purchase that unlocked the app.
336
+ *
337
+ * @returns Promise resolving to the app transaction information or null if not available
338
+ * @throws Error if called on non-iOS platform or iOS version < 16.0
339
+ *
340
+ * @platform iOS
341
+ */
342
+ export const getAppTransactionIOS = (): Promise<AppTransactionIOS | null> => {
343
+ if (Platform.OS !== 'ios') {
344
+ throw new Error(
345
+ 'getAppTransactionIOS: This method is only available on iOS',
346
+ );
347
+ }
348
+ return ExpoIapModule.getAppTransaction();
349
+ };
350
+
351
+ // ============= DEPRECATED FUNCTIONS =============
352
+ // These will be removed in version 3.0.0
353
+
354
+ /**
355
+ * @deprecated Use `syncIOS` instead. This function will be removed in version 3.0.0.
356
+ */
357
+ export const sync = (): Promise<null> => {
358
+ console.warn(
359
+ '`sync` is deprecated. Use `syncIOS` instead. This function will be removed in version 3.0.0.',
360
+ );
361
+ return syncIOS();
362
+ };
363
+
364
+ /**
365
+ * @deprecated Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.
366
+ */
367
+ export const isEligibleForIntroOffer = (groupID: string): Promise<boolean> => {
368
+ console.warn(
369
+ '`isEligibleForIntroOffer` is deprecated. Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.',
370
+ );
371
+ return isEligibleForIntroOfferIOS(groupID);
372
+ };
373
+
374
+ /**
375
+ * @deprecated Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.
376
+ */
377
+ export const subscriptionStatus = (
378
+ sku: string,
379
+ ): Promise<ProductStatusIos[]> => {
380
+ console.warn(
381
+ '`subscriptionStatus` is deprecated. Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.',
382
+ );
383
+ return subscriptionStatusIOS(sku);
384
+ };
385
+
386
+ /**
387
+ * @deprecated Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.
388
+ */
389
+ export const currentEntitlement = (sku: string): Promise<ProductPurchase> => {
390
+ console.warn(
391
+ '`currentEntitlement` is deprecated. Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.',
392
+ );
393
+ return currentEntitlementIOS(sku);
394
+ };
395
+
396
+ /**
397
+ * @deprecated Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.
398
+ */
399
+ export const latestTransaction = (sku: string): Promise<ProductPurchase> => {
400
+ console.warn(
401
+ '`latestTransaction` is deprecated. Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.',
402
+ );
403
+ return latestTransactionIOS(sku);
404
+ };
405
+
406
+ /**
407
+ * @deprecated Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.
408
+ */
409
+ export const beginRefundRequest = (
410
+ sku: string,
411
+ ): Promise<RefundRequestStatus> => {
412
+ console.warn(
413
+ '`beginRefundRequest` is deprecated. Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.',
414
+ );
415
+ return beginRefundRequestIOS(sku);
416
+ };
417
+
418
+ /**
419
+ * @deprecated Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.
420
+ */
421
+ export const showManageSubscriptions = (): Promise<null> => {
422
+ console.warn(
423
+ '`showManageSubscriptions` is deprecated. Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.',
424
+ );
425
+ return showManageSubscriptionsIOS();
426
+ };
427
+
428
+ /**
429
+ * @deprecated Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.
430
+ */
431
+ export const getReceiptIos = (): Promise<string> => {
432
+ console.warn(
433
+ '`getReceiptIos` is deprecated. Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.',
434
+ );
435
+ return getReceiptIOS();
436
+ };
437
+
438
+ /**
439
+ * @deprecated Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.
440
+ */
441
+ export const isTransactionVerified = (sku: string): Promise<boolean> => {
442
+ console.warn(
443
+ '`isTransactionVerified` is deprecated. Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.',
444
+ );
445
+ return isTransactionVerifiedIOS(sku);
446
+ };
447
+
448
+ /**
449
+ * @deprecated Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.
450
+ */
451
+ export const getTransactionJws = (sku: string): Promise<string> => {
452
+ console.warn(
453
+ '`getTransactionJws` is deprecated. Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.',
454
+ );
455
+ return getTransactionJwsIOS(sku);
456
+ };
457
+
458
+ /**
459
+ * @deprecated Use `validateReceiptIOS` instead. This function will be removed in version 3.0.0.
460
+ */
461
+ export const validateReceiptIos = async (
462
+ sku: string,
463
+ ): Promise<{
464
+ isValid: boolean;
465
+ receiptData: string;
466
+ jwsRepresentation: string;
467
+ latestTransaction?: ProductPurchase;
468
+ }> => {
469
+ console.warn(
470
+ '`validateReceiptIos` is deprecated. Use `validateReceiptIOS` instead. This function will be removed in version 3.0.0.',
471
+ );
472
+ return validateReceiptIOS(sku);
473
+ };
474
+
475
+ /**
476
+ * @deprecated Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.
477
+ */
478
+ export const presentCodeRedemptionSheet = (): Promise<boolean> => {
479
+ console.warn(
480
+ '`presentCodeRedemptionSheet` is deprecated. Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.',
481
+ );
482
+ return presentCodeRedemptionSheetIOS();
483
+ };
484
+
485
+ /**
486
+ * @deprecated Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.
487
+ */
488
+ export const getAppTransaction = (): Promise<AppTransactionIOS | null> => {
489
+ console.warn(
490
+ '`getAppTransaction` is deprecated. Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.',
491
+ );
492
+ return getAppTransactionIOS();
493
+ };
@@ -30,7 +30,7 @@ export type ProductIos = ProductBase & {
30
30
  displayName: string;
31
31
  isFamilyShareable: boolean;
32
32
  jsonRepresentation: string;
33
- subscription: SubscriptionInfo;
33
+ subscription?: SubscriptionInfo;
34
34
  introductoryPriceNumberOfPeriodsIOS?: string;
35
35
  introductoryPriceSubscriptionPeriodIOS?: SubscriptionIosPeriod;
36
36
  };
@@ -140,3 +140,11 @@ export type ProductPurchaseIos = PurchaseBase & {
140
140
  currencyIos?: string;
141
141
  jwsRepresentationIos?: string;
142
142
  };
143
+
144
+ export type AppTransactionIOS = {
145
+ appTransactionID: string;
146
+ originalAppAccountToken?: string;
147
+ originalPurchaseDate: number;
148
+ deviceVerification: string;
149
+ deviceVerificationNonce: string;
150
+ };
package/src/useIap.ts CHANGED
@@ -1,3 +1,9 @@
1
+ // External dependencies
2
+ import {useCallback, useEffect, useState, useRef} from 'react';
3
+ import {Platform} from 'react-native';
4
+ import {EventSubscription} from 'expo-modules-core';
5
+
6
+ // Internal modules
1
7
  import {
2
8
  endConnection,
3
9
  initConnection,
@@ -5,14 +11,15 @@ import {
5
11
  purchaseUpdatedListener,
6
12
  getProducts,
7
13
  getAvailablePurchases,
8
- getPurchaseHistory,
14
+ getPurchaseHistories,
9
15
  finishTransaction as finishTransactionInternal,
10
16
  getSubscriptions,
11
17
  requestPurchase as requestPurchaseInternal,
12
18
  } from './';
13
- import {sync, validateReceiptIos} from './modules/ios';
19
+ import {syncIOS, validateReceiptIOS} from './modules/ios';
14
20
  import {validateReceiptAndroid} from './modules/android';
15
- import {useCallback, useEffect, useState, useRef} from 'react';
21
+
22
+ // Types
16
23
  import {
17
24
  Product,
18
25
  ProductPurchase,
@@ -22,8 +29,6 @@ import {
22
29
  SubscriptionProduct,
23
30
  SubscriptionPurchase,
24
31
  } from './ExpoIap.types';
25
- import {Platform} from 'react-native';
26
- import {EventSubscription} from 'expo-modules-core';
27
32
 
28
33
  type UseIap = {
29
34
  connected: boolean;
@@ -182,7 +187,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
182
187
  }, []);
183
188
 
184
189
  const getPurchaseHistoriesInternal = useCallback(async (): Promise<void> => {
185
- setPurchaseHistories(await getPurchaseHistory());
190
+ setPurchaseHistories(await getPurchaseHistories());
186
191
  }, []);
187
192
 
188
193
  const finishTransaction = useCallback(
@@ -248,7 +253,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
248
253
  const restorePurchases = useCallback(async (): Promise<void> => {
249
254
  try {
250
255
  if (Platform.OS === 'ios') {
251
- await sync().catch((error) => {
256
+ await syncIOS().catch((error) => {
252
257
  if (optionsRef.current?.onSyncError) {
253
258
  optionsRef.current.onSyncError(error);
254
259
  } else {
@@ -273,7 +278,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
273
278
  },
274
279
  ) => {
275
280
  if (Platform.OS === 'ios') {
276
- return await validateReceiptIos(sku);
281
+ return await validateReceiptIOS(sku);
277
282
  } else if (Platform.OS === 'android') {
278
283
  if (
279
284
  !androidOptions ||