expo-iap 2.7.5 → 2.7.7-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.
@@ -229,6 +229,9 @@ public class ExpoIapModule: Module {
229
229
  private var paymentObserver: PaymentObserver?
230
230
  private var promotedPayment: SKPayment?
231
231
  private var promotedProduct: SKProduct?
232
+
233
+ // Add a flag to track initialization state
234
+ private var isInitialized = false
232
235
 
233
236
  public func definition() -> ModuleDefinition {
234
237
  Name("ExpoIap")
@@ -250,6 +253,10 @@ public class ExpoIapModule: Module {
250
253
  }
251
254
 
252
255
  Function("initConnection") { () -> Bool in
256
+ // Clean up any existing state first (important for hot reload)
257
+ self.cleanupExistingState()
258
+
259
+ // Initialize fresh state
253
260
  self.productStore = ProductStore()
254
261
 
255
262
  // Set up PaymentObserver for promoted products
@@ -258,6 +265,7 @@ public class ExpoIapModule: Module {
258
265
  SKPaymentQueue.default().add(self.paymentObserver!)
259
266
  }
260
267
 
268
+ self.isInitialized = true
261
269
  return AppStore.canMakePayments
262
270
  }
263
271
 
@@ -352,10 +360,10 @@ public class ExpoIapModule: Module {
352
360
  }
353
361
 
354
362
  AsyncFunction("getItems") { (skus: [String]) -> [[String: Any?]?] in
355
- guard let productStore = self.productStore else {
356
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.notPrepared)
357
- }
358
-
363
+ try self.ensureConnection()
364
+
365
+ let productStore = self.productStore!
366
+
359
367
  do {
360
368
  let fetchedProducts = try await Product.products(for: skus)
361
369
  await productStore.performOnActor { isolatedStore in
@@ -367,48 +375,26 @@ public class ExpoIapModule: Module {
367
375
  return products.map { serializeProduct($0) }.compactMap { $0 }
368
376
  } catch {
369
377
  print("Error fetching items: \(error)")
370
- if error is Exception {
371
- throw error
372
- }
373
378
  throw error
374
379
  }
375
380
  }
376
381
 
377
382
  AsyncFunction("endConnection") { () -> Bool in
378
- guard let productStore = self.productStore else {
379
- return false
380
- }
381
- await productStore.removeAll()
382
- self.transactions.removeAll()
383
- self.productStore = nil
384
- self.removeTransactionObserver()
385
-
386
- // Remove PaymentObserver
387
- if let observer = self.paymentObserver {
388
- SKPaymentQueue.default().remove(observer)
389
- self.paymentObserver = nil
390
- }
391
-
383
+ self.cleanupExistingState()
392
384
  return true
393
385
  }
394
386
 
395
387
  AsyncFunction("getAvailableItems") {
396
388
  (alsoPublishToEventListener: Bool, onlyIncludeActiveItems: Bool) -> [[String: Any?]?] in
389
+
390
+ try self.ensureConnection()
391
+
397
392
  var purchasedItemsSerialized: [[String: Any?]] = []
398
393
 
399
394
  func addTransaction(transaction: Transaction, jwsRepresentationIos: String? = nil) {
400
- // Debug: Log JWS representation
401
- logDebug("getAvailableItems JWS: \(jwsRepresentationIos != nil ? "exists" : "nil")")
402
- if let jws = jwsRepresentationIos {
403
- logDebug("getAvailableItems JWS length: \(jws.count)")
404
- }
405
-
406
395
  let serialized = serializeTransaction(transaction, jwsRepresentationIos: jwsRepresentationIos)
407
396
  purchasedItemsSerialized.append(serialized)
408
397
 
409
- // Debug: Check if jwsRepresentationIos is included in serialized result
410
- logDebug("getAvailableItems serialized includes JWS: \(serialized["jwsRepresentationIos"] != nil)")
411
-
412
398
  if alsoPublishToEventListener {
413
399
  self.sendEvent(IapEvent.PurchaseUpdated, serialized)
414
400
  }
@@ -476,9 +462,9 @@ public class ExpoIapModule: Module {
476
462
  sku: String, andDangerouslyFinishTransactionAutomatically: Bool,
477
463
  appAccountToken: String?, quantity: Int, discountOffer: [String: String]?
478
464
  ) -> [String: Any?]? in
479
- guard let productStore = self.productStore else {
480
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
481
- }
465
+
466
+ try self.ensureConnection()
467
+ let productStore = self.productStore!
482
468
 
483
469
  let product: Product? = await productStore.getProduct(productID: sku)
484
470
  if let product = product {
@@ -572,9 +558,8 @@ public class ExpoIapModule: Module {
572
558
  }
573
559
 
574
560
  AsyncFunction("subscriptionStatus") { (sku: String) -> [[String: Any?]?]? in
575
- guard let productStore = self.productStore else {
576
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
577
- }
561
+ try self.ensureConnection()
562
+ let productStore = self.productStore!
578
563
 
579
564
  do {
580
565
  let product = await productStore.getProduct(productID: sku)
@@ -593,9 +578,8 @@ public class ExpoIapModule: Module {
593
578
  }
594
579
 
595
580
  AsyncFunction("currentEntitlement") { (sku: String) -> [String: Any?]? in
596
- guard let productStore = self.productStore else {
597
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
598
- }
581
+ try self.ensureConnection()
582
+ let productStore = self.productStore!
599
583
 
600
584
  if let product = await productStore.getProduct(productID: sku) {
601
585
  if let result = await product.currentEntitlement {
@@ -619,9 +603,8 @@ public class ExpoIapModule: Module {
619
603
  }
620
604
 
621
605
  AsyncFunction("latestTransaction") { (sku: String) -> [String: Any?]? in
622
- guard let productStore = self.productStore else {
623
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
624
- }
606
+ try self.ensureConnection()
607
+ let productStore = self.productStore!
625
608
 
626
609
  if let product = await productStore.getProduct(productID: sku) {
627
610
  if let result = await product.latestTransaction {
@@ -716,7 +699,10 @@ public class ExpoIapModule: Module {
716
699
 
717
700
  AsyncFunction("beginRefundRequest") { (sku: String) -> String? in
718
701
  #if !os(tvOS)
719
- guard let product = await self.productStore?.getProduct(productID: sku),
702
+ try self.ensureConnection()
703
+ let productStore = self.productStore!
704
+
705
+ guard let product = await productStore.getProduct(productID: sku),
720
706
  let result = await product.latestTransaction
721
707
  else {
722
708
  throw Exception(name: "ExpoIapModule", description: "Can't find product or transaction for sku \(sku)", code: IapErrorCode.itemUnavailable)
@@ -752,9 +738,8 @@ public class ExpoIapModule: Module {
752
738
  }
753
739
 
754
740
  AsyncFunction("isTransactionVerified") { (sku: String) -> Bool in
755
- guard let productStore = self.productStore else {
756
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
757
- }
741
+ try self.ensureConnection()
742
+ let productStore = self.productStore!
758
743
 
759
744
  if let product = await productStore.getProduct(productID: sku),
760
745
  let result = await product.latestTransaction {
@@ -770,9 +755,8 @@ public class ExpoIapModule: Module {
770
755
  }
771
756
 
772
757
  AsyncFunction("getTransactionJws") { (sku: String) -> String? in
773
- guard let productStore = self.productStore else {
774
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
775
- }
758
+ try self.ensureConnection()
759
+ let productStore = self.productStore!
776
760
 
777
761
  if let product = await productStore.getProduct(productID: sku),
778
762
  let result = await product.latestTransaction {
@@ -783,9 +767,8 @@ public class ExpoIapModule: Module {
783
767
  }
784
768
 
785
769
  AsyncFunction("validateReceiptIOS") { (sku: String) -> [String: Any] in
786
- guard let productStore = self.productStore else {
787
- throw Exception(name: "ExpoIapModule", description: "Connection not initialized", code: IapErrorCode.serviceError)
788
- }
770
+ try self.ensureConnection()
771
+ let productStore = self.productStore!
789
772
 
790
773
  // Get receipt data
791
774
  var receiptData: String = ""
@@ -824,6 +807,58 @@ public class ExpoIapModule: Module {
824
807
  }
825
808
  }
826
809
 
810
+ // Similar to Android's ensureConnection pattern
811
+ private func ensureConnection() throws {
812
+ guard isInitialized else {
813
+ throw Exception(
814
+ name: "ExpoIapModule",
815
+ description: "Connection not initialized. Call initConnection() first.",
816
+ code: IapErrorCode.notPrepared
817
+ )
818
+ }
819
+
820
+ guard productStore != nil else {
821
+ throw Exception(
822
+ name: "ExpoIapModule",
823
+ description: "Product store not available",
824
+ code: IapErrorCode.notPrepared
825
+ )
826
+ }
827
+ }
828
+
829
+ private func cleanupExistingState() {
830
+ // Cancel any existing tasks
831
+ updateListenerTask?.cancel()
832
+ updateListenerTask = nil
833
+
834
+ subscriptionPollingTask?.cancel()
835
+ subscriptionPollingTask = nil
836
+
837
+ // Clear collections
838
+ transactions.removeAll()
839
+ pollingSkus.removeAll()
840
+
841
+ // Reset promoted products
842
+ promotedPayment = nil
843
+ promotedProduct = nil
844
+
845
+ // Remove existing payment observer if any
846
+ if let observer = paymentObserver {
847
+ SKPaymentQueue.default().remove(observer)
848
+ paymentObserver = nil
849
+ }
850
+
851
+ // Clear product store
852
+ if let store = productStore {
853
+ Task {
854
+ await store.removeAll()
855
+ }
856
+ }
857
+ productStore = nil
858
+
859
+ isInitialized = false
860
+ }
861
+
827
862
  private func addTransactionObserver() {
828
863
  if updateListenerTask == nil {
829
864
  updateListenerTask = listenForTransactions()
@@ -996,6 +1031,11 @@ public class ExpoIapModule: Module {
996
1031
  sendEvent(IapEvent.PromotedProductIOS, productData)
997
1032
  }
998
1033
  }
1034
+
1035
+ // Ensure cleanup when module is deallocated
1036
+ deinit {
1037
+ cleanupExistingState()
1038
+ }
999
1039
  }
1000
1040
 
1001
1041
  // PaymentObserver for handling promoted products
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "self:">
6
+ </FileRef>
7
+ </Workspace>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>IDEDidComputeMac32BitWarning</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.7.5",
3
+ "version": "2.7.7-rc.1",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1 +1 @@
1
- {"root":["./src/withIAP.ts"],"version":"5.8.3"}
1
+ {"root":["./src/withiap.ts"],"version":"5.8.3"}
@@ -365,8 +365,7 @@ export interface AndroidRequestPurchaseProps {
365
365
  /**
366
366
  * Android-specific subscription request parameters
367
367
  */
368
- export interface AndroidRequestSubscriptionProps
369
- extends AndroidRequestPurchaseProps {
368
+ export interface AndroidRequestSubscriptionProps extends AndroidRequestPurchaseProps {
370
369
  readonly purchaseTokenAndroid?: string;
371
370
  readonly replacementModeAndroid?: number;
372
371
  readonly subscriptionOffers: {
@@ -409,36 +408,28 @@ export type RequestSubscriptionProps = PlatformRequestSubscriptionProps;
409
408
  * Includes both unified and old platform-specific formats
410
409
  * @deprecated Use RequestPurchaseProps with platform-specific structure instead
411
410
  */
412
- export type LegacyRequestPurchasePropsAll =
413
- | UnifiedRequestPurchaseProps
414
- | LegacyRequestPurchaseProps;
411
+ export type LegacyRequestPurchasePropsAll = UnifiedRequestPurchaseProps | LegacyRequestPurchaseProps;
415
412
 
416
413
  /**
417
414
  * Legacy request subscription parameters (deprecated)
418
415
  * Includes both unified and old platform-specific formats
419
416
  * @deprecated Use RequestSubscriptionProps with platform-specific structure instead
420
417
  */
421
- export type LegacyRequestSubscriptionPropsAll =
422
- | UnifiedRequestSubscriptionProps
423
- | LegacyRequestSubscriptionProps;
418
+ export type LegacyRequestSubscriptionPropsAll = UnifiedRequestSubscriptionProps | LegacyRequestSubscriptionProps;
424
419
 
425
420
  /**
426
421
  * All supported request purchase parameters
427
422
  * Used internally for backward compatibility
428
423
  * @internal
429
424
  */
430
- export type RequestPurchasePropsWithLegacy =
431
- | RequestPurchaseProps
432
- | LegacyRequestPurchasePropsAll;
425
+ export type RequestPurchasePropsWithLegacy = RequestPurchaseProps | LegacyRequestPurchasePropsAll;
433
426
 
434
427
  /**
435
428
  * All supported request subscription parameters
436
429
  * Used internally for backward compatibility
437
430
  * @internal
438
431
  */
439
- export type RequestSubscriptionPropsWithLegacy =
440
- | RequestSubscriptionProps
441
- | LegacyRequestSubscriptionPropsAll;
432
+ export type RequestSubscriptionPropsWithLegacy = RequestSubscriptionProps | LegacyRequestSubscriptionPropsAll;
442
433
 
443
434
  // ============================================================================
444
435
  // Type Guards and Utility Functions
@@ -446,19 +437,19 @@ export type RequestSubscriptionPropsWithLegacy =
446
437
 
447
438
  // Type guards to check which API style is being used
448
439
  export function isPlatformRequestProps(
449
- props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
440
+ props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
450
441
  ): props is PlatformRequestPurchaseProps | PlatformRequestSubscriptionProps {
451
442
  return 'ios' in props || 'android' in props;
452
443
  }
453
444
 
454
445
  export function isUnifiedRequestProps(
455
- props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
446
+ props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
456
447
  ): props is UnifiedRequestPurchaseProps | UnifiedRequestSubscriptionProps {
457
448
  return 'sku' in props || 'skus' in props;
458
449
  }
459
450
 
460
451
  export function isLegacyRequestProps(
461
- props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy,
452
+ props: RequestPurchasePropsWithLegacy | RequestSubscriptionPropsWithLegacy
462
453
  ): props is LegacyRequestPurchaseProps | LegacyRequestSubscriptionProps {
463
454
  return 'productId' in props || 'productIds' in props;
464
455
  }
package/src/index.ts CHANGED
@@ -21,8 +21,12 @@ import {
21
21
  isPlatformRequestProps,
22
22
  isUnifiedRequestProps,
23
23
  } from './ExpoIap.types';
24
- import {ProductPurchaseAndroid} from './types/ExpoIapAndroid.types';
25
- import {PaymentDiscount} from './types/ExpoIapIos.types';
24
+ import {
25
+ ProductPurchaseAndroid,
26
+ } from './types/ExpoIapAndroid.types';
27
+ import {
28
+ PaymentDiscount,
29
+ } from './types/ExpoIapIos.types';
26
30
 
27
31
  // Export all types
28
32
  export * from './ExpoIap.types';
@@ -76,42 +80,41 @@ export const purchaseErrorListener = (
76
80
  /**
77
81
  * iOS-only listener for App Store promoted product events.
78
82
  * This fires when a user taps on a promoted product in the App Store.
79
- *
83
+ *
80
84
  * @param listener - Callback function that receives the promoted product details
81
85
  * @returns EventSubscription that can be used to unsubscribe
82
- *
86
+ *
83
87
  * @example
84
88
  * ```typescript
85
89
  * const subscription = promotedProductListenerIOS((product) => {
86
90
  * console.log('Promoted product:', product);
87
91
  * // Handle the promoted product
88
92
  * });
89
- *
93
+ *
90
94
  * // Later, clean up
91
95
  * subscription.remove();
92
96
  * ```
93
- *
97
+ *
94
98
  * @platform iOS
95
99
  */
96
100
  export const promotedProductListenerIOS = (
97
101
  listener: (product: Product) => void,
98
102
  ) => {
99
103
  if (Platform.OS !== 'ios') {
100
- console.warn(
101
- 'promotedProductListenerIOS: This listener is only available on iOS',
102
- );
103
- return {remove: () => {}};
104
+ console.warn('promotedProductListenerIOS: This listener is only available on iOS');
105
+ return { remove: () => {} };
104
106
  }
105
107
  return emitter.addListener(IapEvent.PromotedProductIOS, listener);
106
108
  };
107
109
 
108
- export function initConnection() {
109
- return ExpoIapModule.initConnection();
110
+ export function initConnection(): Promise<boolean> {
111
+ const result = ExpoIapModule.initConnection();
112
+ return Promise.resolve(result);
110
113
  }
111
114
 
112
115
  export const getProducts = async (skus: string[]): Promise<Product[]> => {
113
116
  console.warn(
114
- "`getProducts` is deprecated. Use `requestProducts({ skus, type: 'inapp' })` instead. This function will be removed in version 3.0.0.",
117
+ '`getProducts` is deprecated. Use `requestProducts({ skus, type: \'inapp\' })` instead. This function will be removed in version 3.0.0.',
115
118
  );
116
119
  if (!skus?.length) {
117
120
  return Promise.reject(new Error('"skus" is required'));
@@ -145,7 +148,7 @@ export const getSubscriptions = async (
145
148
  skus: string[],
146
149
  ): Promise<SubscriptionProduct[]> => {
147
150
  console.warn(
148
- "`getSubscriptions` is deprecated. Use `requestProducts({ skus, type: 'subs' })` instead. This function will be removed in version 3.0.0.",
151
+ '`getSubscriptions` is deprecated. Use `requestProducts({ skus, type: \'subs\' })` instead. This function will be removed in version 3.0.0.',
149
152
  );
150
153
  if (!skus?.length) {
151
154
  return Promise.reject(new Error('"skus" is required'));
@@ -269,7 +272,7 @@ export const getPurchaseHistory = ({
269
272
  onlyIncludeActiveItems?: boolean;
270
273
  } = {}): Promise<ProductPurchase[]> => {
271
274
  console.warn(
272
- '`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.',
275
+ "`getPurchaseHistory` is deprecated. Use `getPurchaseHistories` instead. This function will be removed in version 3.0.0.",
273
276
  );
274
277
  return getPurchaseHistories({
275
278
  alsoPublishToEventListener,
@@ -319,8 +322,9 @@ export const getAvailablePurchases = ({
319
322
  ),
320
323
  android: async () => {
321
324
  const products = await ExpoIapModule.getAvailableItemsByType('inapp');
322
- const subscriptions =
323
- await ExpoIapModule.getAvailableItemsByType('subs');
325
+ const subscriptions = await ExpoIapModule.getAvailableItemsByType(
326
+ 'subs',
327
+ );
324
328
  return products.concat(subscriptions);
325
329
  },
326
330
  }) || (() => Promise.resolve([]))
@@ -339,6 +343,7 @@ const offerToRecordIos = (
339
343
  };
340
344
  };
341
345
 
346
+
342
347
  // Define discriminated union with explicit type parameter
343
348
  // Using legacy types internally for backward compatibility
344
349
  type PurchaseRequest =
@@ -368,8 +373,7 @@ const normalizeRequestProps = (
368
373
  if (platform === 'ios') {
369
374
  return {
370
375
  sku: request.sku || (request.skus?.[0] ?? ''),
371
- andDangerouslyFinishTransactionAutomaticallyIOS:
372
- request.andDangerouslyFinishTransactionAutomaticallyIOS,
376
+ andDangerouslyFinishTransactionAutomaticallyIOS: request.andDangerouslyFinishTransactionAutomaticallyIOS,
373
377
  appAccountToken: request.appAccountToken,
374
378
  quantity: request.quantity,
375
379
  withOffer: request.withOffer,
@@ -381,7 +385,7 @@ const normalizeRequestProps = (
381
385
  obfuscatedProfileIdAndroid: request.obfuscatedProfileIdAndroid,
382
386
  isOfferPersonalized: request.isOfferPersonalized,
383
387
  };
384
-
388
+
385
389
  // Add subscription-specific fields if present
386
390
  if ('subscriptionOffers' in request && request.subscriptionOffers) {
387
391
  androidRequest.subscriptionOffers = request.subscriptionOffers;
@@ -392,7 +396,7 @@ const normalizeRequestProps = (
392
396
  if ('replacementModeAndroid' in request) {
393
397
  androidRequest.replacementModeAndroid = request.replacementModeAndroid;
394
398
  }
395
-
399
+
396
400
  return androidRequest;
397
401
  }
398
402
  }
@@ -403,11 +407,11 @@ const normalizeRequestProps = (
403
407
 
404
408
  /**
405
409
  * Request a purchase for products or subscriptions.
406
- *
410
+ *
407
411
  * @param requestObj - Purchase request configuration
408
412
  * @param requestObj.request - Platform-specific purchase parameters
409
413
  * @param requestObj.type - Type of purchase: 'inapp' for products (default) or 'subs' for subscriptions
410
- *
414
+ *
411
415
  * @example
412
416
  * ```typescript
413
417
  * // Product purchase
@@ -418,12 +422,12 @@ const normalizeRequestProps = (
418
422
  * },
419
423
  * type: 'inapp'
420
424
  * });
421
- *
425
+ *
422
426
  * // Subscription purchase
423
427
  * await requestPurchase({
424
428
  * request: {
425
429
  * ios: { sku: subscriptionId },
426
- * android: {
430
+ * android: {
427
431
  * skus: [subscriptionId],
428
432
  * subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
429
433
  * }
@@ -445,7 +449,7 @@ export const requestPurchase = (
445
449
 
446
450
  if (Platform.OS === 'ios') {
447
451
  const normalizedRequest = normalizeRequestProps(request, 'ios');
448
-
452
+
449
453
  if (!normalizedRequest?.sku) {
450
454
  throw new Error(
451
455
  'Invalid request for iOS. The `sku` property is required and must be a string.',
@@ -478,7 +482,7 @@ export const requestPurchase = (
478
482
 
479
483
  if (Platform.OS === 'android') {
480
484
  const normalizedRequest = normalizeRequestProps(request, 'android');
481
-
485
+
482
486
  if (!normalizedRequest?.skus?.length) {
483
487
  throw new Error(
484
488
  'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
@@ -542,7 +546,7 @@ export const requestPurchase = (
542
546
 
543
547
  /**
544
548
  * @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0.
545
- *
549
+ *
546
550
  * @example
547
551
  * ```typescript
548
552
  * // Old way (deprecated)
@@ -551,12 +555,12 @@ export const requestPurchase = (
551
555
  * // or for Android
552
556
  * skus: [subscriptionId],
553
557
  * });
554
- *
558
+ *
555
559
  * // New way (recommended)
556
560
  * await requestPurchase({
557
561
  * request: {
558
562
  * ios: { sku: subscriptionId },
559
- * android: {
563
+ * android: {
560
564
  * skus: [subscriptionId],
561
565
  * subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
562
566
  * }
@@ -621,16 +625,16 @@ export const finishTransaction = ({
621
625
 
622
626
  /**
623
627
  * Retrieves the current storefront information from iOS App Store
624
- *
628
+ *
625
629
  * @returns Promise resolving to the storefront country code
626
630
  * @throws Error if called on non-iOS platform
627
- *
631
+ *
628
632
  * @example
629
633
  * ```typescript
630
634
  * const storefront = await getStorefrontIOS();
631
635
  * console.log(storefront); // 'US'
632
636
  * ```
633
- *
637
+ *
634
638
  * @platform iOS
635
639
  */
636
640
  export const getStorefrontIOS = (): Promise<string> => {
@@ -102,9 +102,11 @@ export const acknowledgePurchaseAndroid = ({
102
102
  * Open the Google Play Store to redeem offer codes (Android only).
103
103
  * Note: Google Play does not provide a direct API to redeem codes within the app.
104
104
  * This function opens the Play Store where users can manually enter their codes.
105
- *
105
+ *
106
106
  * @returns {Promise<void>}
107
107
  */
108
108
  export const openRedeemOfferCodeAndroid = async (): Promise<void> => {
109
- return Linking.openURL(`https://play.google.com/redeem?code=`);
109
+ return Linking.openURL(
110
+ `https://play.google.com/redeem?code=`
111
+ );
110
112
  };