expo-iap 2.7.4 → 2.7.5-rc.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.
@@ -11,10 +11,7 @@ import {
11
11
  Purchase,
12
12
  SubscriptionPurchase,
13
13
  } from '../ExpoIap.types';
14
- import type {
15
- ProductStatusIos,
16
- AppTransactionIOS,
17
- } from '../types/ExpoIapIos.types';
14
+ import type {ProductStatusIos, AppTransactionIOS} from '../types/ExpoIapIos.types';
18
15
 
19
16
  export type TransactionEvent = {
20
17
  transaction?: ProductPurchase;
@@ -38,6 +35,7 @@ export type TransactionEvent = {
38
35
  export const transactionUpdatedIos = (
39
36
  listener: (event: TransactionEvent) => void,
40
37
  ) => {
38
+
41
39
  const isProductPurchase = (item: unknown): item is ProductPurchase => {
42
40
  return (
43
41
  item != null &&
@@ -88,10 +86,10 @@ export function isProductIos<T extends {platform?: string}>(
88
86
  /**
89
87
  * Sync state with Appstore (iOS only)
90
88
  * https://developer.apple.com/documentation/storekit/appstore/3791906-sync
91
- *
89
+ *
92
90
  * @returns Promise resolving to null on success
93
91
  * @throws Error if called on non-iOS platform
94
- *
92
+ *
95
93
  * @platform iOS
96
94
  */
97
95
  export const syncIOS = (): Promise<null> => {
@@ -100,56 +98,50 @@ export const syncIOS = (): Promise<null> => {
100
98
 
101
99
  /**
102
100
  * Check if user is eligible for introductory offer
103
- *
101
+ *
104
102
  * @param groupID The subscription group ID
105
103
  * @returns Promise resolving to true if eligible
106
104
  * @throws Error if called on non-iOS platform
107
- *
105
+ *
108
106
  * @platform iOS
109
107
  */
110
- export const isEligibleForIntroOfferIOS = (
111
- groupID: string,
112
- ): Promise<boolean> => {
108
+ export const isEligibleForIntroOfferIOS = (groupID: string): Promise<boolean> => {
113
109
  return ExpoIapModule.isEligibleForIntroOffer(groupID);
114
110
  };
115
111
 
116
112
  /**
117
113
  * Get subscription status for a specific SKU
118
- *
114
+ *
119
115
  * @param sku The product SKU
120
116
  * @returns Promise resolving to array of subscription status
121
117
  * @throws Error if called on non-iOS platform
122
- *
118
+ *
123
119
  * @platform iOS
124
120
  */
125
- export const subscriptionStatusIOS = (
126
- sku: string,
127
- ): Promise<ProductStatusIos[]> => {
121
+ export const subscriptionStatusIOS = (sku: string): Promise<ProductStatusIos[]> => {
128
122
  return ExpoIapModule.subscriptionStatus(sku);
129
123
  };
130
124
 
131
125
  /**
132
126
  * Get current entitlement for a specific SKU
133
- *
127
+ *
134
128
  * @param sku The product SKU
135
129
  * @returns Promise resolving to current entitlement
136
130
  * @throws Error if called on non-iOS platform
137
- *
131
+ *
138
132
  * @platform iOS
139
133
  */
140
- export const currentEntitlementIOS = (
141
- sku: string,
142
- ): Promise<ProductPurchase> => {
134
+ export const currentEntitlementIOS = (sku: string): Promise<ProductPurchase> => {
143
135
  return ExpoIapModule.currentEntitlement(sku);
144
136
  };
145
137
 
146
138
  /**
147
139
  * Get latest transaction for a specific SKU
148
- *
140
+ *
149
141
  * @param sku The product SKU
150
142
  * @returns Promise resolving to latest transaction
151
143
  * @throws Error if called on non-iOS platform
152
- *
144
+ *
153
145
  * @platform iOS
154
146
  */
155
147
  export const latestTransactionIOS = (sku: string): Promise<ProductPurchase> => {
@@ -158,17 +150,15 @@ export const latestTransactionIOS = (sku: string): Promise<ProductPurchase> => {
158
150
 
159
151
  /**
160
152
  * Begin refund request for a specific SKU
161
- *
153
+ *
162
154
  * @param sku The product SKU
163
155
  * @returns Promise resolving to refund request status
164
156
  * @throws Error if called on non-iOS platform
165
- *
157
+ *
166
158
  * @platform iOS
167
159
  */
168
160
  type RefundRequestStatus = 'success' | 'userCancelled';
169
- export const beginRefundRequestIOS = (
170
- sku: string,
171
- ): Promise<RefundRequestStatus> => {
161
+ export const beginRefundRequestIOS = (sku: string): Promise<RefundRequestStatus> => {
172
162
  return ExpoIapModule.beginRefundRequest(sku);
173
163
  };
174
164
 
@@ -176,10 +166,10 @@ export const beginRefundRequestIOS = (
176
166
  * Shows the system UI for managing subscriptions.
177
167
  * When the user changes subscription renewal status, the system will emit events to
178
168
  * purchaseUpdatedListener and transactionUpdatedIos listeners.
179
- *
169
+ *
180
170
  * @returns Promise resolving to null on success
181
171
  * @throws Error if called on non-iOS platform
182
- *
172
+ *
183
173
  * @platform iOS
184
174
  */
185
175
  export const showManageSubscriptionsIOS = (): Promise<null> => {
@@ -207,7 +197,7 @@ export const getReceiptIOS = (): Promise<string> => {
207
197
  * @param sku The product's SKU (on iOS)
208
198
  * @returns Promise resolving to true if the transaction is verified
209
199
  * @throws Error if called on non-iOS platform
210
- *
200
+ *
211
201
  * @platform iOS
212
202
  */
213
203
  export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
@@ -221,7 +211,7 @@ export const isTransactionVerifiedIOS = (sku: string): Promise<boolean> => {
221
211
  * @param sku The product's SKU (on iOS)
222
212
  * @returns Promise resolving to JWS representation of the transaction
223
213
  * @throws Error if called on non-iOS platform
224
- *
214
+ *
225
215
  * @platform iOS
226
216
  */
227
217
  export const getTransactionJwsIOS = (sku: string): Promise<string> => {
@@ -258,12 +248,12 @@ export const validateReceiptIOS = async (
258
248
  /**
259
249
  * Present the code redemption sheet for offer codes (iOS only).
260
250
  * This allows users to redeem promotional codes for in-app purchases and subscriptions.
261
- *
251
+ *
262
252
  * Note: This only works on real devices, not simulators.
263
- *
253
+ *
264
254
  * @returns Promise resolving to true if the sheet was presented successfully
265
255
  * @throws Error if called on non-iOS platform or tvOS
266
- *
256
+ *
267
257
  * @platform iOS
268
258
  */
269
259
  export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
@@ -273,14 +263,14 @@ export const presentCodeRedemptionSheetIOS = (): Promise<boolean> => {
273
263
  /**
274
264
  * Get app transaction information (iOS 16.0+).
275
265
  * AppTransaction represents the initial purchase that unlocked the app.
276
- *
266
+ *
277
267
  * NOTE: This function requires:
278
268
  * - iOS 16.0 or later at runtime
279
269
  * - Xcode 15.0+ with iOS 16.0 SDK for compilation
280
- *
270
+ *
281
271
  * @returns Promise resolving to the app transaction information or null if not available
282
272
  * @throws Error if called on non-iOS platform, iOS version < 16.0, or compiled with older SDK
283
- *
273
+ *
284
274
  * @platform iOS
285
275
  * @since iOS 16.0
286
276
  */
@@ -288,6 +278,32 @@ export const getAppTransactionIOS = (): Promise<AppTransactionIOS | null> => {
288
278
  return ExpoIapModule.getAppTransaction();
289
279
  };
290
280
 
281
+ /**
282
+ * Get the promoted product details (iOS only).
283
+ * This is called after a promoted product event is received from the App Store.
284
+ *
285
+ * @returns Promise resolving to the promoted product details or null if none available
286
+ * @throws Error if called on non-iOS platform
287
+ *
288
+ * @platform iOS
289
+ */
290
+ export const getPromotedProductIOS = (): Promise<any | null> => {
291
+ return ExpoIapModule.getPromotedProduct();
292
+ };
293
+
294
+ /**
295
+ * Complete the purchase of a promoted product (iOS only).
296
+ * This should be called after showing your purchase UI for a promoted product.
297
+ *
298
+ * @returns Promise resolving when the purchase is initiated
299
+ * @throws Error if called on non-iOS platform or no promoted product is available
300
+ *
301
+ * @platform iOS
302
+ */
303
+ export const buyPromotedProductIOS = (): Promise<void> => {
304
+ return ExpoIapModule.buyPromotedProduct();
305
+ };
306
+
291
307
  // ============= DEPRECATED FUNCTIONS =============
292
308
  // These will be removed in version 3.0.0
293
309
 
@@ -295,9 +311,7 @@ export const getAppTransactionIOS = (): Promise<AppTransactionIOS | null> => {
295
311
  * @deprecated Use `syncIOS` instead. This function will be removed in version 3.0.0.
296
312
  */
297
313
  export const sync = (): Promise<null> => {
298
- console.warn(
299
- '`sync` is deprecated. Use `syncIOS` instead. This function will be removed in version 3.0.0.',
300
- );
314
+ console.warn('`sync` is deprecated. Use `syncIOS` instead. This function will be removed in version 3.0.0.');
301
315
  return syncIOS();
302
316
  };
303
317
 
@@ -305,21 +319,15 @@ export const sync = (): Promise<null> => {
305
319
  * @deprecated Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.
306
320
  */
307
321
  export const isEligibleForIntroOffer = (groupID: string): Promise<boolean> => {
308
- console.warn(
309
- '`isEligibleForIntroOffer` is deprecated. Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.',
310
- );
322
+ console.warn('`isEligibleForIntroOffer` is deprecated. Use `isEligibleForIntroOfferIOS` instead. This function will be removed in version 3.0.0.');
311
323
  return isEligibleForIntroOfferIOS(groupID);
312
324
  };
313
325
 
314
326
  /**
315
327
  * @deprecated Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.
316
328
  */
317
- export const subscriptionStatus = (
318
- sku: string,
319
- ): Promise<ProductStatusIos[]> => {
320
- console.warn(
321
- '`subscriptionStatus` is deprecated. Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.',
322
- );
329
+ export const subscriptionStatus = (sku: string): Promise<ProductStatusIos[]> => {
330
+ console.warn('`subscriptionStatus` is deprecated. Use `subscriptionStatusIOS` instead. This function will be removed in version 3.0.0.');
323
331
  return subscriptionStatusIOS(sku);
324
332
  };
325
333
 
@@ -327,9 +335,7 @@ export const subscriptionStatus = (
327
335
  * @deprecated Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.
328
336
  */
329
337
  export const currentEntitlement = (sku: string): Promise<ProductPurchase> => {
330
- console.warn(
331
- '`currentEntitlement` is deprecated. Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.',
332
- );
338
+ console.warn('`currentEntitlement` is deprecated. Use `currentEntitlementIOS` instead. This function will be removed in version 3.0.0.');
333
339
  return currentEntitlementIOS(sku);
334
340
  };
335
341
 
@@ -337,21 +343,15 @@ export const currentEntitlement = (sku: string): Promise<ProductPurchase> => {
337
343
  * @deprecated Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.
338
344
  */
339
345
  export const latestTransaction = (sku: string): Promise<ProductPurchase> => {
340
- console.warn(
341
- '`latestTransaction` is deprecated. Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.',
342
- );
346
+ console.warn('`latestTransaction` is deprecated. Use `latestTransactionIOS` instead. This function will be removed in version 3.0.0.');
343
347
  return latestTransactionIOS(sku);
344
348
  };
345
349
 
346
350
  /**
347
351
  * @deprecated Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.
348
352
  */
349
- export const beginRefundRequest = (
350
- sku: string,
351
- ): Promise<RefundRequestStatus> => {
352
- console.warn(
353
- '`beginRefundRequest` is deprecated. Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.',
354
- );
353
+ export const beginRefundRequest = (sku: string): Promise<RefundRequestStatus> => {
354
+ console.warn('`beginRefundRequest` is deprecated. Use `beginRefundRequestIOS` instead. This function will be removed in version 3.0.0.');
355
355
  return beginRefundRequestIOS(sku);
356
356
  };
357
357
 
@@ -359,9 +359,7 @@ export const beginRefundRequest = (
359
359
  * @deprecated Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.
360
360
  */
361
361
  export const showManageSubscriptions = (): Promise<null> => {
362
- console.warn(
363
- '`showManageSubscriptions` is deprecated. Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.',
364
- );
362
+ console.warn('`showManageSubscriptions` is deprecated. Use `showManageSubscriptionsIOS` instead. This function will be removed in version 3.0.0.');
365
363
  return showManageSubscriptionsIOS();
366
364
  };
367
365
 
@@ -369,9 +367,7 @@ export const showManageSubscriptions = (): Promise<null> => {
369
367
  * @deprecated Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.
370
368
  */
371
369
  export const getReceiptIos = (): Promise<string> => {
372
- console.warn(
373
- '`getReceiptIos` is deprecated. Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.',
374
- );
370
+ console.warn('`getReceiptIos` is deprecated. Use `getReceiptIOS` instead. This function will be removed in version 3.0.0.');
375
371
  return getReceiptIOS();
376
372
  };
377
373
 
@@ -379,9 +375,7 @@ export const getReceiptIos = (): Promise<string> => {
379
375
  * @deprecated Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.
380
376
  */
381
377
  export const isTransactionVerified = (sku: string): Promise<boolean> => {
382
- console.warn(
383
- '`isTransactionVerified` is deprecated. Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.',
384
- );
378
+ console.warn('`isTransactionVerified` is deprecated. Use `isTransactionVerifiedIOS` instead. This function will be removed in version 3.0.0.');
385
379
  return isTransactionVerifiedIOS(sku);
386
380
  };
387
381
 
@@ -389,9 +383,7 @@ export const isTransactionVerified = (sku: string): Promise<boolean> => {
389
383
  * @deprecated Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.
390
384
  */
391
385
  export const getTransactionJws = (sku: string): Promise<string> => {
392
- console.warn(
393
- '`getTransactionJws` is deprecated. Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.',
394
- );
386
+ console.warn('`getTransactionJws` is deprecated. Use `getTransactionJwsIOS` instead. This function will be removed in version 3.0.0.');
395
387
  return getTransactionJwsIOS(sku);
396
388
  };
397
389
 
@@ -406,9 +398,7 @@ export const validateReceiptIos = async (
406
398
  jwsRepresentation: string;
407
399
  latestTransaction?: ProductPurchase;
408
400
  }> => {
409
- console.warn(
410
- '`validateReceiptIos` is deprecated. Use `validateReceiptIOS` instead. This function will be removed in version 3.0.0.',
411
- );
401
+ console.warn('`validateReceiptIos` is deprecated. Use `validateReceiptIOS` instead. This function will be removed in version 3.0.0.');
412
402
  return validateReceiptIOS(sku);
413
403
  };
414
404
 
@@ -416,9 +406,7 @@ export const validateReceiptIos = async (
416
406
  * @deprecated Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.
417
407
  */
418
408
  export const presentCodeRedemptionSheet = (): Promise<boolean> => {
419
- console.warn(
420
- '`presentCodeRedemptionSheet` is deprecated. Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.',
421
- );
409
+ console.warn('`presentCodeRedemptionSheet` is deprecated. Use `presentCodeRedemptionSheetIOS` instead. This function will be removed in version 3.0.0.');
422
410
  return presentCodeRedemptionSheetIOS();
423
411
  };
424
412
 
@@ -426,8 +414,6 @@ export const presentCodeRedemptionSheet = (): Promise<boolean> => {
426
414
  * @deprecated Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.
427
415
  */
428
416
  export const getAppTransaction = (): Promise<AppTransactionIOS | null> => {
429
- console.warn(
430
- '`getAppTransaction` is deprecated. Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.',
431
- );
417
+ console.warn('`getAppTransaction` is deprecated. Use `getAppTransactionIOS` instead. This function will be removed in version 3.0.0.');
432
418
  return getAppTransactionIOS();
433
419
  };
package/src/useIap.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  initConnection,
10
10
  purchaseErrorListener,
11
11
  purchaseUpdatedListener,
12
+ promotedProductListenerIOS,
12
13
  getProducts,
13
14
  getAvailablePurchases,
14
15
  getPurchaseHistories,
@@ -17,7 +18,12 @@ import {
17
18
  requestPurchase as requestPurchaseInternal,
18
19
  requestProducts,
19
20
  } from './';
20
- import {syncIOS, validateReceiptIOS} from './modules/ios';
21
+ import {
22
+ syncIOS,
23
+ validateReceiptIOS,
24
+ getPromotedProductIOS,
25
+ buyPromotedProductIOS,
26
+ } from './modules/ios';
21
27
  import {validateReceiptAndroid} from './modules/android';
22
28
 
23
29
  // Types
@@ -36,12 +42,12 @@ import {
36
42
  type UseIap = {
37
43
  connected: boolean;
38
44
  products: Product[];
39
- promotedProductsIOS: ProductPurchase[];
40
45
  subscriptions: SubscriptionProduct[];
41
46
  purchaseHistories: ProductPurchase[];
42
47
  availablePurchases: ProductPurchase[];
43
48
  currentPurchase?: ProductPurchase;
44
49
  currentPurchaseError?: PurchaseError;
50
+ promotedProductIOS?: Product;
45
51
  clearCurrentPurchase: () => void;
46
52
  clearCurrentPurchaseError: () => void;
47
53
  finishTransaction: ({
@@ -73,6 +79,8 @@ type UseIap = {
73
79
  },
74
80
  ) => Promise<any>;
75
81
  restorePurchases: () => Promise<void>; // 구매 복원 함수 추가
82
+ getPromotedProductIOS: () => Promise<any | null>;
83
+ buyPromotedProductIOS: () => Promise<void>;
76
84
  };
77
85
 
78
86
  export interface UseIAPOptions {
@@ -82,14 +90,12 @@ export interface UseIAPOptions {
82
90
  onPurchaseError?: (error: PurchaseError) => void;
83
91
  onSyncError?: (error: Error) => void;
84
92
  shouldAutoSyncPurchases?: boolean; // New option to control auto-syncing
93
+ onPromotedProductIOS?: (product: Product) => void;
85
94
  }
86
95
 
87
96
  export function useIAP(options?: UseIAPOptions): UseIap {
88
97
  const [connected, setConnected] = useState<boolean>(false);
89
98
  const [products, setProducts] = useState<Product[]>([]);
90
- const [promotedProductsIOS, setPromotedProductsIOS] = useState<
91
- ProductPurchase[]
92
- >([]);
93
99
  const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);
94
100
  const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(
95
101
  [],
@@ -98,6 +104,7 @@ export function useIAP(options?: UseIAPOptions): UseIap {
98
104
  ProductPurchase[]
99
105
  >([]);
100
106
  const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();
107
+ const [promotedProductIOS, setPromotedProductIOS] = useState<Product>();
101
108
  const [currentPurchaseError, setCurrentPurchaseError] =
102
109
  useState<PurchaseError>();
103
110
 
@@ -374,27 +381,18 @@ export function useIAP(options?: UseIAPOptions): UseIap {
374
381
  );
375
382
 
376
383
  if (Platform.OS === 'ios') {
377
- // iOS promoted products are handled through regular purchase updates
378
- subscriptionsRef.current.promotedProductsIos = purchaseUpdatedListener(
379
- async (purchase: Purchase | SubscriptionPurchase) => {
380
- // Add to promoted products if it's a promoted transaction (avoid duplicates)
381
- setPromotedProductsIOS((prevProducts) =>
382
- mergeWithDuplicateCheck(
383
- prevProducts,
384
- [purchase as ProductPurchase],
385
- (product) => product.transactionId || product.id,
386
- ),
387
- );
388
-
389
- // Refresh subscription status if it's a subscription purchase
390
- if ('expirationDateIos' in purchase) {
391
- await refreshSubscriptionStatus(purchase.id);
384
+ // iOS promoted products listener
385
+ subscriptionsRef.current.promotedProductsIos =
386
+ promotedProductListenerIOS((product: Product) => {
387
+ setPromotedProductIOS(product);
388
+
389
+ if (optionsRef.current?.onPromotedProductIOS) {
390
+ optionsRef.current.onPromotedProductIOS(product);
392
391
  }
393
- },
394
- );
392
+ });
395
393
  }
396
394
  }
397
- }, [refreshSubscriptionStatus, mergeWithDuplicateCheck]);
395
+ }, [refreshSubscriptionStatus]);
398
396
 
399
397
  useEffect(() => {
400
398
  initIapWithSubscriptions();
@@ -412,13 +410,13 @@ export function useIAP(options?: UseIAPOptions): UseIap {
412
410
  return {
413
411
  connected,
414
412
  products,
415
- promotedProductsIOS,
416
413
  subscriptions,
417
414
  purchaseHistories,
418
415
  finishTransaction,
419
416
  availablePurchases,
420
417
  currentPurchase,
421
418
  currentPurchaseError,
419
+ promotedProductIOS,
422
420
  clearCurrentPurchase,
423
421
  clearCurrentPurchaseError,
424
422
  getAvailablePurchases: getAvailablePurchasesInternal,
@@ -429,5 +427,9 @@ export function useIAP(options?: UseIAPOptions): UseIap {
429
427
  requestPurchase: requestPurchaseWithReset,
430
428
  validateReceipt,
431
429
  restorePurchases,
430
+ getPromotedProductIOS:
431
+ Platform.OS === 'ios' ? getPromotedProductIOS : async () => null,
432
+ buyPromotedProductIOS:
433
+ Platform.OS === 'ios' ? buyPromotedProductIOS : async () => {},
432
434
  };
433
435
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=smartPurchase.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smartPurchase.d.ts","sourceRoot":"","sources":["../../src/utils/smartPurchase.ts"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=smartPurchase.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smartPurchase.js","sourceRoot":"","sources":["../../src/utils/smartPurchase.ts"],"names":[],"mappings":"","sourcesContent":[""]}
File without changes