expo-iap 3.1.2 → 3.1.4

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.
Files changed (34) hide show
  1. package/android/src/main/java/expo/modules/iap/ExpoIapHelper.kt +171 -0
  2. package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +4 -0
  3. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +50 -114
  4. package/build/types.d.ts +34 -34
  5. package/build/types.js +34 -34
  6. package/build/types.js.map +1 -1
  7. package/build/useIAP.d.ts +0 -2
  8. package/build/useIAP.d.ts.map +1 -1
  9. package/build/useIAP.js +6 -3
  10. package/build/useIAP.js.map +1 -1
  11. package/build/utils/errorMapping.d.ts +7 -8
  12. package/build/utils/errorMapping.d.ts.map +1 -1
  13. package/build/utils/errorMapping.js +83 -53
  14. package/build/utils/errorMapping.js.map +1 -1
  15. package/coverage/clover.xml +105 -96
  16. package/coverage/coverage-final.json +1 -1
  17. package/coverage/lcov-report/index.html +19 -19
  18. package/coverage/lcov-report/src/helpers/index.html +1 -1
  19. package/coverage/lcov-report/src/helpers/subscription.ts.html +1 -1
  20. package/coverage/lcov-report/src/index.html +1 -1
  21. package/coverage/lcov-report/src/index.ts.html +1 -1
  22. package/coverage/lcov-report/src/modules/android.ts.html +1 -1
  23. package/coverage/lcov-report/src/modules/index.html +1 -1
  24. package/coverage/lcov-report/src/modules/ios.ts.html +1 -1
  25. package/coverage/lcov-report/src/utils/errorMapping.ts.html +191 -134
  26. package/coverage/lcov-report/src/utils/index.html +19 -19
  27. package/coverage/lcov.info +215 -185
  28. package/ios/ExpoIapHelper.swift +99 -6
  29. package/ios/ExpoIapModule.swift +29 -97
  30. package/openiap-versions.json +1 -1
  31. package/package.json +1 -1
  32. package/src/types.ts +34 -34
  33. package/src/useIAP.ts +6 -4
  34. package/src/utils/errorMapping.ts +101 -82
@@ -23,30 +23,30 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">97.05% </span>
26
+ <span class="strong">94.59% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>99/102</span>
28
+ <span class='fraction'>105/111</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">85.07% </span>
33
+ <span class="strong">79.54% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>57/67</span>
35
+ <span class='fraction'>70/88</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">94.11% </span>
40
+ <span class="strong">88.23% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>16/17</span>
42
+ <span class='fraction'>15/17</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">97% </span>
47
+ <span class="strong">94.49% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>97/100</span>
49
+ <span class='fraction'>103/109</span>
50
50
  </div>
51
51
 
52
52
 
@@ -80,17 +80,17 @@
80
80
  </thead>
81
81
  <tbody><tr>
82
82
  <td class="file high" data-value="errorMapping.ts"><a href="errorMapping.ts.html">errorMapping.ts</a></td>
83
- <td data-value="97.05" class="pic high">
84
- <div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
83
+ <td data-value="94.59" class="pic high">
84
+ <div class="chart"><div class="cover-fill" style="width: 94%"></div><div class="cover-empty" style="width: 6%"></div></div>
85
85
  </td>
86
- <td data-value="97.05" class="pct high">97.05%</td>
87
- <td data-value="102" class="abs high">99/102</td>
88
- <td data-value="85.07" class="pct high">85.07%</td>
89
- <td data-value="67" class="abs high">57/67</td>
90
- <td data-value="94.11" class="pct high">94.11%</td>
91
- <td data-value="17" class="abs high">16/17</td>
92
- <td data-value="97" class="pct high">97%</td>
93
- <td data-value="100" class="abs high">97/100</td>
86
+ <td data-value="94.59" class="pct high">94.59%</td>
87
+ <td data-value="111" class="abs high">105/111</td>
88
+ <td data-value="79.54" class="pct medium">79.54%</td>
89
+ <td data-value="88" class="abs medium">70/88</td>
90
+ <td data-value="88.23" class="pct high">88.23%</td>
91
+ <td data-value="17" class="abs high">15/17</td>
92
+ <td data-value="94.49" class="pct high">94.49%</td>
93
+ <td data-value="109" class="abs high">103/109</td>
94
94
  </tr>
95
95
 
96
96
  </tbody>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2025-09-22T14:13:03.978Z
104
+ at 2025-09-24T13:03:48.683Z
105
105
  </div>
106
106
  <script src="../../prettify.js"></script>
107
107
  <script>
@@ -719,34 +719,34 @@ BRH:34
719
719
  end_of_record
720
720
  TN:
721
721
  SF:src/utils/errorMapping.ts
722
- FN:29,(anonymous_0)
723
- FN:32,(anonymous_1)
724
- FN:94,(anonymous_2)
725
- FN:97,(anonymous_3)
726
- FN:110,(anonymous_4)
727
- FN:130,(anonymous_5)
728
- FN:138,(anonymous_6)
729
- FN:145,(anonymous_7)
730
- FN:161,(anonymous_8)
731
- FN:179,(anonymous_9)
732
- FN:189,(anonymous_10)
733
- FN:212,(anonymous_11)
734
- FN:231,extractCode
735
- FN:243,isUserCancelledError
736
- FN:247,isNetworkError
737
- FN:260,isRecoverableError
738
- FN:276,getUserFriendlyErrorMessage
722
+ FN:10,(anonymous_0)
723
+ FN:14,(anonymous_1)
724
+ FN:41,(anonymous_2)
725
+ FN:92,(anonymous_3)
726
+ FN:113,(anonymous_4)
727
+ FN:135,(anonymous_5)
728
+ FN:141,(anonymous_6)
729
+ FN:151,(anonymous_7)
730
+ FN:174,(anonymous_8)
731
+ FN:195,(anonymous_9)
732
+ FN:204,(anonymous_10)
733
+ FN:222,(anonymous_11)
734
+ FN:250,extractCode
735
+ FN:262,isUserCancelledError
736
+ FN:266,isNetworkError
737
+ FN:279,isRecoverableError
738
+ FN:295,getUserFriendlyErrorMessage
739
739
  FNF:17
740
- FNH:16
741
- FNDA:207,(anonymous_0)
742
- FNDA:2,(anonymous_1)
743
- FNDA:102,(anonymous_2)
740
+ FNH:15
741
+ FNDA:30,(anonymous_0)
742
+ FNDA:45,(anonymous_1)
743
+ FNDA:1,(anonymous_2)
744
744
  FNDA:4,(anonymous_3)
745
745
  FNDA:1,(anonymous_4)
746
746
  FNDA:0,(anonymous_5)
747
- FNDA:3,(anonymous_6)
748
- FNDA:13,(anonymous_7)
749
- FNDA:32,(anonymous_8)
747
+ FNDA:7,(anonymous_6)
748
+ FNDA:0,(anonymous_7)
749
+ FNDA:58,(anonymous_8)
750
750
  FNDA:1,(anonymous_9)
751
751
  FNDA:2,(anonymous_10)
752
752
  FNDA:37,(anonymous_11)
@@ -757,173 +757,203 @@ FNDA:4,isRecoverableError
757
757
  FNDA:24,getUserFriendlyErrorMessage
758
758
  DA:7,3
759
759
  DA:8,3
760
- DA:29,3
761
- DA:30,207
762
- DA:32,3
763
- DA:33,2
764
- DA:37,3
765
- DA:88,3
766
- DA:93,3
767
- DA:94,102
768
- DA:97,3
769
- DA:100,4
760
+ DA:10,3
761
+ DA:11,30
762
+ DA:12,20
763
+ DA:14,45
764
+ DA:17,10
765
+ DA:41,3
766
+ DA:42,1
767
+ DA:46,3
768
+ DA:85,3
769
+ DA:90,3
770
+ DA:92,3
771
+ DA:95,4
770
772
  DA:101,4
771
- DA:102,4
772
- DA:103,4
773
773
  DA:104,4
774
774
  DA:105,4
775
775
  DA:106,4
776
776
  DA:107,4
777
- DA:110,3
778
- DA:114,1
779
- DA:115,1
780
- DA:119,1
781
- DA:129,3
782
- DA:131,0
783
- DA:132,0
784
- DA:142,3
785
- DA:143,2
786
- DA:144,1
787
- DA:145,13
777
+ DA:108,4
778
+ DA:109,4
779
+ DA:110,4
780
+ DA:113,3
781
+ DA:117,1
782
+ DA:118,1
783
+ DA:124,1
784
+ DA:134,3
785
+ DA:136,0
786
+ DA:145,7
787
+ DA:146,1
788
788
  DA:147,1
789
789
  DA:148,1
790
- DA:153,2
791
- DA:156,5
792
- DA:160,1
793
- DA:161,32
794
- DA:163,1
795
- DA:164,1
796
- DA:169,1
797
- DA:172,34
798
- DA:173,0
799
- DA:177,1
800
- DA:183,1
801
- DA:184,1
802
- DA:187,1
803
- DA:193,2
804
- DA:194,2
805
- DA:198,1
806
- DA:200,1
807
- DA:210,3
808
- DA:212,3
809
- DA:213,37
810
- DA:214,1
811
- DA:217,36
812
- DA:218,29
813
- DA:221,7
814
- DA:222,4
815
- DA:223,4
816
- DA:224,3
817
- DA:228,4
818
- DA:232,38
819
- DA:233,28
820
- DA:236,10
821
- DA:237,9
822
- DA:240,1
823
- DA:243,3
824
- DA:244,5
825
- DA:247,3
826
- DA:248,5
827
- DA:256,5
828
- DA:257,5
829
- DA:260,3
830
- DA:261,4
831
- DA:272,4
832
- DA:273,4
833
- DA:276,3
834
- DA:277,24
835
- DA:279,24
836
- DA:281,2
837
- DA:283,1
838
- DA:285,1
839
- DA:287,1
840
- DA:289,1
841
- DA:291,1
842
- DA:293,1
843
- DA:295,1
844
- DA:297,1
845
- DA:299,1
846
- DA:301,1
847
- DA:303,1
848
- DA:305,1
849
- DA:307,1
850
- DA:309,1
851
- DA:311,1
852
- DA:313,2
853
- DA:315,1
854
- DA:317,1
855
- DA:319,3
790
+ DA:149,1
791
+ DA:150,0
792
+ DA:151,0
793
+ DA:153,0
794
+ DA:154,0
795
+ DA:160,7
796
+ DA:164,7
797
+ DA:168,12
798
+ DA:169,12
799
+ DA:173,3
800
+ DA:174,58
801
+ DA:176,3
802
+ DA:177,3
803
+ DA:182,4
804
+ DA:185,101
805
+ DA:189,3
806
+ DA:193,1
807
+ DA:199,1
808
+ DA:202,1
809
+ DA:208,2
810
+ DA:220,3
811
+ DA:222,3
812
+ DA:223,37
813
+ DA:224,1
814
+ DA:227,36
815
+ DA:228,26
816
+ DA:231,10
817
+ DA:232,10
818
+ DA:233,2
819
+ DA:236,8
820
+ DA:237,4
821
+ DA:238,4
822
+ DA:239,0
823
+ DA:241,4
824
+ DA:242,4
825
+ DA:243,2
826
+ DA:247,6
827
+ DA:251,38
828
+ DA:252,28
829
+ DA:255,10
830
+ DA:256,9
831
+ DA:259,1
832
+ DA:262,3
833
+ DA:263,5
834
+ DA:266,3
835
+ DA:267,5
836
+ DA:275,5
837
+ DA:276,5
838
+ DA:279,3
839
+ DA:280,4
840
+ DA:291,4
841
+ DA:292,4
842
+ DA:295,3
843
+ DA:296,24
844
+ DA:298,24
845
+ DA:300,2
846
+ DA:302,1
847
+ DA:304,1
848
+ DA:306,1
849
+ DA:308,1
850
+ DA:310,1
851
+ DA:312,1
852
+ DA:314,1
853
+ DA:316,1
854
+ DA:318,1
856
855
  DA:320,1
857
- DA:325,2
858
- LF:100
859
- LH:97
860
- BRDA:30,0,0,0
861
- BRDA:30,0,1,207
862
- BRDA:33,1,0,1
863
- BRDA:33,1,1,1
864
- BRDA:33,2,0,2
865
- BRDA:33,2,1,2
866
- BRDA:115,3,0,1
867
- BRDA:115,3,1,0
868
- BRDA:120,4,0,1
869
- BRDA:120,4,1,0
870
- BRDA:133,5,0,0
871
- BRDA:133,5,1,0
872
- BRDA:142,6,0,2
873
- BRDA:142,7,0,3
874
- BRDA:142,7,1,2
875
- BRDA:143,8,0,1
876
- BRDA:147,9,0,1
877
- BRDA:154,10,0,2
878
- BRDA:154,10,1,0
879
- BRDA:156,11,0,1
880
- BRDA:157,12,0,5
881
- BRDA:157,12,1,1
882
- BRDA:163,13,0,1
883
- BRDA:172,14,0,0
884
- BRDA:187,15,0,1
885
- BRDA:187,15,1,0
886
- BRDA:187,15,2,0
887
- BRDA:194,16,0,1
888
- BRDA:213,17,0,1
889
- BRDA:217,18,0,29
890
- BRDA:221,19,0,4
891
- BRDA:223,20,0,3
892
- BRDA:232,21,0,28
893
- BRDA:236,22,0,9
894
- BRDA:236,23,0,10
895
- BRDA:236,23,1,9
896
- BRDA:236,23,2,9
897
- BRDA:257,24,0,5
898
- BRDA:257,24,1,4
899
- BRDA:273,25,0,4
900
- BRDA:273,25,1,4
901
- BRDA:279,26,0,2
902
- BRDA:279,26,1,1
903
- BRDA:279,26,2,1
904
- BRDA:279,26,3,1
905
- BRDA:279,26,4,1
906
- BRDA:279,26,5,1
907
- BRDA:279,26,6,1
908
- BRDA:279,26,7,1
909
- BRDA:279,26,8,1
910
- BRDA:279,26,9,1
911
- BRDA:279,26,10,1
912
- BRDA:279,26,11,1
913
- BRDA:279,26,12,1
914
- BRDA:279,26,13,1
915
- BRDA:279,26,14,1
916
- BRDA:279,26,15,1
917
- BRDA:279,26,16,2
918
- BRDA:279,26,17,1
919
- BRDA:279,26,18,1
920
- BRDA:279,26,19,3
921
- BRDA:319,27,0,1
922
- BRDA:319,28,0,3
923
- BRDA:319,28,1,3
924
- BRDA:319,28,2,2
925
- BRDA:321,29,0,1
926
- BRDA:321,29,1,0
927
- BRF:67
928
- BRH:57
856
+ DA:322,1
857
+ DA:324,1
858
+ DA:326,1
859
+ DA:328,1
860
+ DA:330,1
861
+ DA:332,2
862
+ DA:334,1
863
+ DA:336,1
864
+ DA:338,3
865
+ DA:339,1
866
+ DA:344,2
867
+ LF:109
868
+ LH:103
869
+ BRDA:11,0,0,20
870
+ BRDA:11,0,1,10
871
+ BRDA:42,1,0,1
872
+ BRDA:42,1,1,0
873
+ BRDA:42,2,0,1
874
+ BRDA:42,2,1,1
875
+ BRDA:95,3,0,4
876
+ BRDA:95,3,1,0
877
+ BRDA:96,4,0,4
878
+ BRDA:96,4,1,0
879
+ BRDA:96,5,0,4
880
+ BRDA:96,5,1,0
881
+ BRDA:97,6,0,4
882
+ BRDA:97,6,1,1
883
+ BRDA:102,7,0,4
884
+ BRDA:102,7,1,0
885
+ BRDA:118,8,0,1
886
+ BRDA:118,8,1,0
887
+ BRDA:119,9,0,1
888
+ BRDA:119,9,1,0
889
+ BRDA:119,10,0,1
890
+ BRDA:119,10,1,0
891
+ BRDA:125,11,0,1
892
+ BRDA:125,11,1,0
893
+ BRDA:137,12,0,0
894
+ BRDA:137,12,1,0
895
+ BRDA:145,13,0,1
896
+ BRDA:145,14,0,7
897
+ BRDA:145,14,1,6
898
+ BRDA:149,15,0,0
899
+ BRDA:153,16,0,0
900
+ BRDA:160,17,0,6
901
+ BRDA:160,17,1,1
902
+ BRDA:165,18,0,7
903
+ BRDA:165,18,1,0
904
+ BRDA:168,19,0,9
905
+ BRDA:168,19,1,3
906
+ BRDA:169,20,0,3
907
+ BRDA:170,21,0,12
908
+ BRDA:170,21,1,3
909
+ BRDA:176,22,0,3
910
+ BRDA:185,23,0,3
911
+ BRDA:186,24,0,101
912
+ BRDA:186,24,1,98
913
+ BRDA:202,25,0,1
914
+ BRDA:202,25,1,0
915
+ BRDA:202,25,2,0
916
+ BRDA:223,26,0,1
917
+ BRDA:227,27,0,26
918
+ BRDA:232,28,0,2
919
+ BRDA:236,29,0,4
920
+ BRDA:238,30,0,0
921
+ BRDA:242,31,0,2
922
+ BRDA:251,32,0,28
923
+ BRDA:255,33,0,9
924
+ BRDA:255,34,0,10
925
+ BRDA:255,34,1,9
926
+ BRDA:255,34,2,9
927
+ BRDA:276,35,0,5
928
+ BRDA:276,35,1,4
929
+ BRDA:292,36,0,4
930
+ BRDA:292,36,1,4
931
+ BRDA:298,37,0,2
932
+ BRDA:298,37,1,1
933
+ BRDA:298,37,2,1
934
+ BRDA:298,37,3,1
935
+ BRDA:298,37,4,1
936
+ BRDA:298,37,5,1
937
+ BRDA:298,37,6,1
938
+ BRDA:298,37,7,1
939
+ BRDA:298,37,8,1
940
+ BRDA:298,37,9,1
941
+ BRDA:298,37,10,1
942
+ BRDA:298,37,11,1
943
+ BRDA:298,37,12,1
944
+ BRDA:298,37,13,1
945
+ BRDA:298,37,14,1
946
+ BRDA:298,37,15,1
947
+ BRDA:298,37,16,2
948
+ BRDA:298,37,17,1
949
+ BRDA:298,37,18,1
950
+ BRDA:298,37,19,3
951
+ BRDA:338,38,0,1
952
+ BRDA:338,39,0,3
953
+ BRDA:338,39,1,3
954
+ BRDA:338,39,2,2
955
+ BRDA:340,40,0,1
956
+ BRDA:340,40,1,0
957
+ BRF:88
958
+ BRH:70
929
959
  end_of_record
@@ -2,6 +2,8 @@ import Foundation
2
2
  import OpenIAP
3
3
 
4
4
  enum ExpoIapHelper {
5
+ private static var listeners: [Subscription] = []
6
+
5
7
  static func sanitizeDictionary(_ dictionary: [String: Any?]) -> [String: Any] {
6
8
  var result: [String: Any] = [:]
7
9
  for (key, value) in dictionary {
@@ -26,7 +28,8 @@ enum ExpoIapHelper {
26
28
  }
27
29
 
28
30
  static func parseProductQueryType(_ rawValue: String?) -> ProductQueryType {
29
- guard let raw = rawValue?.trimmingCharacters(in: .whitespacesAndNewlines), !raw.isEmpty else {
31
+ guard let raw = rawValue?.trimmingCharacters(in: .whitespacesAndNewlines), !raw.isEmpty
32
+ else {
30
33
  return .all
31
34
  }
32
35
  switch raw.lowercased() {
@@ -56,14 +59,17 @@ enum ExpoIapHelper {
56
59
  return try OpenIapSerialization.productRequest(skus: indexedSkus, type: .all)
57
60
  }
58
61
 
59
- if let request = try? OpenIapSerialization.decode(object: payload, as: ProductRequest.self) {
62
+ if let request = try? OpenIapSerialization.decode(object: payload, as: ProductRequest.self)
63
+ {
60
64
  return request
61
65
  }
62
66
 
63
67
  throw PurchaseError.emptySkuList()
64
68
  }
65
69
 
66
- static func decodeRequestPurchaseProps(from payload: [String: Any]) throws -> RequestPurchaseProps {
70
+ static func decodeRequestPurchaseProps(from payload: [String: Any]) throws
71
+ -> RequestPurchaseProps
72
+ {
67
73
  if payload["requestPurchase"] != nil || payload["requestSubscription"] != nil {
68
74
  return try OpenIapSerialization.decode(object: payload, as: RequestPurchaseProps.self)
69
75
  }
@@ -80,17 +86,104 @@ enum ExpoIapHelper {
80
86
  case .all:
81
87
  break
82
88
  }
83
- return try OpenIapSerialization.decode(object: normalized, as: RequestPurchaseProps.self)
89
+ return try OpenIapSerialization.decode(
90
+ object: normalized, as: RequestPurchaseProps.self)
84
91
  }
85
92
 
86
93
  if payload["sku"] != nil {
87
94
  let normalized: [String: Any] = [
88
95
  "type": ProductQueryType.inApp.rawValue,
89
- "requestPurchase": ["ios": payload]
96
+ "requestPurchase": ["ios": payload],
90
97
  ]
91
- return try OpenIapSerialization.decode(object: normalized, as: RequestPurchaseProps.self)
98
+ return try OpenIapSerialization.decode(
99
+ object: normalized, as: RequestPurchaseProps.self)
92
100
  }
93
101
 
94
102
  throw PurchaseError.make(code: .developerError, message: "Invalid request payload")
95
103
  }
104
+
105
+ static func setupListeners(
106
+ module: ExpoIapModule,
107
+ purchaseUpdated: @escaping (Purchase) -> Void,
108
+ purchaseError: @escaping (PurchaseError) -> Void,
109
+ promotedProduct: @escaping (String) async -> Void
110
+ ) {
111
+ // Clean up any existing listeners first
112
+ cleanupListeners()
113
+
114
+ let purchaseUpdatedSub = OpenIapModule.shared.purchaseUpdatedListener { purchase in
115
+ Task { @MainActor in
116
+ purchaseUpdated(purchase)
117
+ }
118
+ }
119
+
120
+ let purchaseErrorSub = OpenIapModule.shared.purchaseErrorListener { error in
121
+ Task { @MainActor in
122
+ purchaseError(error)
123
+ }
124
+ }
125
+
126
+ let promotedProductSub = OpenIapModule.shared.promotedProductListenerIOS { productId in
127
+ Task { @MainActor in
128
+ await promotedProduct(productId)
129
+ }
130
+ }
131
+
132
+ listeners = [purchaseUpdatedSub, purchaseErrorSub, promotedProductSub]
133
+ }
134
+
135
+ static func cleanupListeners() {
136
+ // Clear subscriptions to prevent memory leaks
137
+ // Subscription deinit will automatically call onRemove closure
138
+ listeners.removeAll()
139
+ }
140
+
141
+ static func setupStore(module: ExpoIapModule) {
142
+ setupListeners(
143
+ module: module,
144
+ purchaseUpdated: { [weak module] purchase in
145
+ guard let module else { return }
146
+ let payload = sanitizeDictionary(OpenIapSerialization.purchase(purchase))
147
+ module.sendEvent(OpenIapEvent.purchaseUpdated.rawValue, payload)
148
+ },
149
+ purchaseError: { [weak module] error in
150
+ guard let module else { return }
151
+ let payload = sanitizeDictionary(OpenIapSerialization.encode(error))
152
+ module.sendEvent(OpenIapEvent.purchaseError.rawValue, payload)
153
+ },
154
+ promotedProduct: { [weak module] productId in
155
+ guard let module else { return }
156
+ do {
157
+ if let product = try await OpenIapModule.shared.getPromotedProductIOS() {
158
+ let sanitized = sanitizeDictionary(OpenIapSerialization.encode(product))
159
+ module.sendEvent(OpenIapEvent.promotedProductIos.rawValue, sanitized)
160
+ return
161
+ }
162
+ } catch {
163
+ ExpoIapLog.failure("promotedProductListenerIOS", error: error)
164
+ }
165
+
166
+ module.sendEvent(
167
+ OpenIapEvent.promotedProductIos.rawValue,
168
+ ["productId": productId]
169
+ )
170
+ }
171
+ )
172
+ }
173
+
174
+ static func cleanupStore() async {
175
+ cleanupListeners()
176
+ _ = try? await OpenIapModule.shared.endConnection()
177
+ }
178
+
179
+ static func ensureConnection(isInitialized: Bool) async throws {
180
+ try await MainActor.run {
181
+ guard isInitialized else {
182
+ throw PurchaseError.make(
183
+ code: .initConnection,
184
+ message: "Connection not initialized. Call initConnection() first."
185
+ )
186
+ }
187
+ }
188
+ }
96
189
  }