expo-iap 2.7.6 → 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.6",
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,31 +80,29 @@ 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
  };
@@ -112,7 +114,7 @@ export function initConnection(): Promise<boolean> {
112
114
 
113
115
  export const getProducts = async (skus: string[]): Promise<Product[]> => {
114
116
  console.warn(
115
- "`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.',
116
118
  );
117
119
  if (!skus?.length) {
118
120
  return Promise.reject(new Error('"skus" is required'));
@@ -146,7 +148,7 @@ export const getSubscriptions = async (
146
148
  skus: string[],
147
149
  ): Promise<SubscriptionProduct[]> => {
148
150
  console.warn(
149
- "`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.',
150
152
  );
151
153
  if (!skus?.length) {
152
154
  return Promise.reject(new Error('"skus" is required'));
@@ -270,7 +272,7 @@ export const getPurchaseHistory = ({
270
272
  onlyIncludeActiveItems?: boolean;
271
273
  } = {}): Promise<ProductPurchase[]> => {
272
274
  console.warn(
273
- '`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.",
274
276
  );
275
277
  return getPurchaseHistories({
276
278
  alsoPublishToEventListener,
@@ -320,8 +322,9 @@ export const getAvailablePurchases = ({
320
322
  ),
321
323
  android: async () => {
322
324
  const products = await ExpoIapModule.getAvailableItemsByType('inapp');
323
- const subscriptions =
324
- await ExpoIapModule.getAvailableItemsByType('subs');
325
+ const subscriptions = await ExpoIapModule.getAvailableItemsByType(
326
+ 'subs',
327
+ );
325
328
  return products.concat(subscriptions);
326
329
  },
327
330
  }) || (() => Promise.resolve([]))
@@ -340,6 +343,7 @@ const offerToRecordIos = (
340
343
  };
341
344
  };
342
345
 
346
+
343
347
  // Define discriminated union with explicit type parameter
344
348
  // Using legacy types internally for backward compatibility
345
349
  type PurchaseRequest =
@@ -369,8 +373,7 @@ const normalizeRequestProps = (
369
373
  if (platform === 'ios') {
370
374
  return {
371
375
  sku: request.sku || (request.skus?.[0] ?? ''),
372
- andDangerouslyFinishTransactionAutomaticallyIOS:
373
- request.andDangerouslyFinishTransactionAutomaticallyIOS,
376
+ andDangerouslyFinishTransactionAutomaticallyIOS: request.andDangerouslyFinishTransactionAutomaticallyIOS,
374
377
  appAccountToken: request.appAccountToken,
375
378
  quantity: request.quantity,
376
379
  withOffer: request.withOffer,
@@ -382,7 +385,7 @@ const normalizeRequestProps = (
382
385
  obfuscatedProfileIdAndroid: request.obfuscatedProfileIdAndroid,
383
386
  isOfferPersonalized: request.isOfferPersonalized,
384
387
  };
385
-
388
+
386
389
  // Add subscription-specific fields if present
387
390
  if ('subscriptionOffers' in request && request.subscriptionOffers) {
388
391
  androidRequest.subscriptionOffers = request.subscriptionOffers;
@@ -393,7 +396,7 @@ const normalizeRequestProps = (
393
396
  if ('replacementModeAndroid' in request) {
394
397
  androidRequest.replacementModeAndroid = request.replacementModeAndroid;
395
398
  }
396
-
399
+
397
400
  return androidRequest;
398
401
  }
399
402
  }
@@ -404,11 +407,11 @@ const normalizeRequestProps = (
404
407
 
405
408
  /**
406
409
  * Request a purchase for products or subscriptions.
407
- *
410
+ *
408
411
  * @param requestObj - Purchase request configuration
409
412
  * @param requestObj.request - Platform-specific purchase parameters
410
413
  * @param requestObj.type - Type of purchase: 'inapp' for products (default) or 'subs' for subscriptions
411
- *
414
+ *
412
415
  * @example
413
416
  * ```typescript
414
417
  * // Product purchase
@@ -419,12 +422,12 @@ const normalizeRequestProps = (
419
422
  * },
420
423
  * type: 'inapp'
421
424
  * });
422
- *
425
+ *
423
426
  * // Subscription purchase
424
427
  * await requestPurchase({
425
428
  * request: {
426
429
  * ios: { sku: subscriptionId },
427
- * android: {
430
+ * android: {
428
431
  * skus: [subscriptionId],
429
432
  * subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
430
433
  * }
@@ -446,7 +449,7 @@ export const requestPurchase = (
446
449
 
447
450
  if (Platform.OS === 'ios') {
448
451
  const normalizedRequest = normalizeRequestProps(request, 'ios');
449
-
452
+
450
453
  if (!normalizedRequest?.sku) {
451
454
  throw new Error(
452
455
  'Invalid request for iOS. The `sku` property is required and must be a string.',
@@ -479,7 +482,7 @@ export const requestPurchase = (
479
482
 
480
483
  if (Platform.OS === 'android') {
481
484
  const normalizedRequest = normalizeRequestProps(request, 'android');
482
-
485
+
483
486
  if (!normalizedRequest?.skus?.length) {
484
487
  throw new Error(
485
488
  'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
@@ -543,7 +546,7 @@ export const requestPurchase = (
543
546
 
544
547
  /**
545
548
  * @deprecated Use `requestPurchase({ request, type: 'subs' })` instead. This method will be removed in version 3.0.0.
546
- *
549
+ *
547
550
  * @example
548
551
  * ```typescript
549
552
  * // Old way (deprecated)
@@ -552,12 +555,12 @@ export const requestPurchase = (
552
555
  * // or for Android
553
556
  * skus: [subscriptionId],
554
557
  * });
555
- *
558
+ *
556
559
  * // New way (recommended)
557
560
  * await requestPurchase({
558
561
  * request: {
559
562
  * ios: { sku: subscriptionId },
560
- * android: {
563
+ * android: {
561
564
  * skus: [subscriptionId],
562
565
  * subscriptionOffers: [{ sku: subscriptionId, offerToken: 'token' }]
563
566
  * }
@@ -622,16 +625,16 @@ export const finishTransaction = ({
622
625
 
623
626
  /**
624
627
  * Retrieves the current storefront information from iOS App Store
625
- *
628
+ *
626
629
  * @returns Promise resolving to the storefront country code
627
630
  * @throws Error if called on non-iOS platform
628
- *
631
+ *
629
632
  * @example
630
633
  * ```typescript
631
634
  * const storefront = await getStorefrontIOS();
632
635
  * console.log(storefront); // 'US'
633
636
  * ```
634
- *
637
+ *
635
638
  * @platform iOS
636
639
  */
637
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
  };