react-native-iap 13.0.4 → 14.0.0-rc.2

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 (381) hide show
  1. package/NitroIap.podspec +33 -0
  2. package/README.md +163 -51
  3. package/android/CMakeLists.txt +32 -0
  4. package/android/build.gradle +107 -138
  5. package/android/fix-prefab.gradle +51 -0
  6. package/android/gradle.properties +5 -13
  7. package/android/src/main/AndroidManifest.xml +1 -4
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +843 -0
  10. package/android/src/main/java/com/margelo/nitro/iap/NitroIapPackage.java +33 -0
  11. package/android/src/main/java/com/margelo/nitro/iap/Types.kt +173 -0
  12. package/ios/Bridge.h +8 -0
  13. package/ios/ErrorUtils.swift +153 -0
  14. package/ios/HybridRnIap.swift +1326 -0
  15. package/ios/ProductStore.swift +33 -0
  16. package/ios/reactnativeiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  17. package/ios/reactnativeiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  18. package/lib/module/helpers/subscription.js +56 -0
  19. package/lib/module/helpers/subscription.js.map +1 -0
  20. package/lib/module/hooks/useIAP.js +175 -123
  21. package/lib/module/hooks/useIAP.js.map +1 -1
  22. package/lib/module/index.js +1015 -9
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/package.json +1 -0
  25. package/lib/module/specs/RnIap.nitro.js +4 -0
  26. package/lib/{commonjs/modules/common.js.map → module/specs/RnIap.nitro.js.map} +1 -1
  27. package/lib/module/types.js +187 -0
  28. package/lib/module/types.js.map +1 -0
  29. package/lib/module/utils/error.js +80 -0
  30. package/lib/module/utils/error.js.map +1 -0
  31. package/lib/module/utils/type-bridge.js +202 -0
  32. package/lib/module/utils/type-bridge.js.map +1 -0
  33. package/lib/typescript/package.json +1 -0
  34. package/lib/typescript/plugin/src/withIAP.d.ts +2 -7
  35. package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
  36. package/lib/typescript/scripts/check-nitro-versions.d.ts +3 -0
  37. package/lib/typescript/scripts/check-nitro-versions.d.ts.map +1 -0
  38. package/lib/typescript/src/helpers/subscription.d.ts +14 -0
  39. package/lib/typescript/src/helpers/subscription.d.ts.map +1 -0
  40. package/lib/typescript/src/hooks/useIAP.d.ts +42 -26
  41. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  42. package/lib/typescript/src/index.d.ts +356 -8
  43. package/lib/typescript/src/index.d.ts.map +1 -1
  44. package/lib/typescript/src/specs/RnIap.nitro.d.ts +404 -0
  45. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -0
  46. package/lib/typescript/src/types.d.ts +544 -0
  47. package/lib/typescript/src/types.d.ts.map +1 -0
  48. package/lib/typescript/src/utils/error.d.ts +30 -0
  49. package/lib/typescript/src/utils/error.d.ts.map +1 -0
  50. package/lib/typescript/src/utils/type-bridge.d.ts +40 -0
  51. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -0
  52. package/nitro.json +17 -0
  53. package/nitrogen/generated/android/NitroIap+autolinking.cmake +80 -0
  54. package/nitrogen/generated/android/NitroIap+autolinking.gradle +27 -0
  55. package/nitrogen/generated/android/NitroIapOnLoad.cpp +50 -0
  56. package/nitrogen/generated/android/NitroIapOnLoad.hpp +25 -0
  57. package/nitrogen/generated/android/c++/JFunc_void_NitroProduct.hpp +78 -0
  58. package/nitrogen/generated/android/c++/JFunc_void_NitroPurchase.hpp +78 -0
  59. package/nitrogen/generated/android/c++/JFunc_void_NitroPurchaseResult.hpp +78 -0
  60. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +577 -0
  61. package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +93 -0
  62. package/nitrogen/generated/android/c++/JNitroAndroidReceiptValidationOptions.hpp +66 -0
  63. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesAndroidOptions.hpp +54 -0
  64. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesIosOptions.hpp +65 -0
  65. package/nitrogen/generated/android/c++/JNitroAvailablePurchasesOptions.hpp +62 -0
  66. package/nitrogen/generated/android/c++/JNitroFinishTransactionAndroidParams.hpp +58 -0
  67. package/nitrogen/generated/android/c++/JNitroFinishTransactionIosParams.hpp +53 -0
  68. package/nitrogen/generated/android/c++/JNitroFinishTransactionParams.hpp +62 -0
  69. package/nitrogen/generated/android/c++/JNitroProduct.hpp +154 -0
  70. package/nitrogen/generated/android/c++/JNitroPurchase.hpp +122 -0
  71. package/nitrogen/generated/android/c++/JNitroPurchaseRequest.hpp +66 -0
  72. package/nitrogen/generated/android/c++/JNitroPurchaseResult.hpp +70 -0
  73. package/nitrogen/generated/android/c++/JNitroReceiptValidationParams.hpp +60 -0
  74. package/nitrogen/generated/android/c++/JNitroReceiptValidationResultAndroid.hpp +122 -0
  75. package/nitrogen/generated/android/c++/JNitroReceiptValidationResultIOS.hpp +68 -0
  76. package/nitrogen/generated/android/c++/JNitroRequestPurchaseAndroid.hpp +115 -0
  77. package/nitrogen/generated/android/c++/JNitroRequestPurchaseIos.hpp +84 -0
  78. package/nitrogen/generated/android/c++/JNitroSubscriptionOffer.hpp +57 -0
  79. package/nitrogen/generated/android/c++/JNitroSubscriptionRenewalInfo.hpp +74 -0
  80. package/nitrogen/generated/android/c++/JNitroSubscriptionStatus.hpp +64 -0
  81. package/nitrogen/generated/android/c++/JVariant_Boolean_NitroPurchaseResult.cpp +26 -0
  82. package/nitrogen/generated/android/c++/JVariant_Boolean_NitroPurchaseResult.hpp +71 -0
  83. package/nitrogen/generated/android/c++/JVariant_NitroReceiptValidationResultIOS_NitroReceiptValidationResultAndroid.cpp +26 -0
  84. package/nitrogen/generated/android/c++/JVariant_NitroReceiptValidationResultIOS_NitroReceiptValidationResultAndroid.hpp +75 -0
  85. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Func_void_NitroProduct.kt +81 -0
  86. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Func_void_NitroPurchase.kt +81 -0
  87. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Func_void_NitroPurchaseResult.kt +81 -0
  88. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +198 -0
  89. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAndroidReceiptValidationOptions.kt +38 -0
  90. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesAndroidOptions.kt +29 -0
  91. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesIosOptions.kt +38 -0
  92. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroAvailablePurchasesOptions.kt +32 -0
  93. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroFinishTransactionAndroidParams.kt +32 -0
  94. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroFinishTransactionIosParams.kt +29 -0
  95. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroFinishTransactionParams.kt +32 -0
  96. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroIapOnLoad.kt +35 -0
  97. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroProduct.kt +104 -0
  98. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchase.kt +80 -0
  99. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseRequest.kt +32 -0
  100. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseResult.kt +41 -0
  101. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationParams.kt +32 -0
  102. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationResultAndroid.kt +80 -0
  103. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationResultIOS.kt +38 -0
  104. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseAndroid.kt +47 -0
  105. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseIos.kt +41 -0
  106. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroSubscriptionOffer.kt +32 -0
  107. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroSubscriptionRenewalInfo.kt +44 -0
  108. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroSubscriptionStatus.kt +35 -0
  109. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_Boolean_NitroPurchaseResult.kt +42 -0
  110. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NitroReceiptValidationResultIOS_NitroReceiptValidationResultAndroid.kt +42 -0
  111. package/nitrogen/generated/ios/NitroIap+autolinking.rb +60 -0
  112. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.cpp +152 -0
  113. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +1061 -0
  114. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +107 -0
  115. package/nitrogen/generated/ios/NitroIapAutolinking.mm +33 -0
  116. package/nitrogen/generated/ios/NitroIapAutolinking.swift +25 -0
  117. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.cpp +11 -0
  118. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +355 -0
  119. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  120. package/nitrogen/generated/ios/swift/Func_void_NitroProduct.swift +47 -0
  121. package/nitrogen/generated/ios/swift/Func_void_NitroPurchase.swift +47 -0
  122. package/nitrogen/generated/ios/swift/Func_void_NitroPurchaseResult.swift +47 -0
  123. package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
  124. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  125. package/nitrogen/generated/ios/swift/Func_void_std__optional_NitroProduct_.swift +54 -0
  126. package/nitrogen/generated/ios/swift/Func_void_std__optional_NitroPurchase_.swift +54 -0
  127. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +54 -0
  128. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__vector_NitroSubscriptionStatus__.swift +54 -0
  129. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  130. package/nitrogen/generated/ios/swift/Func_void_std__variant_NitroReceiptValidationResultIOS__NitroReceiptValidationResultAndroid_.swift +59 -0
  131. package/nitrogen/generated/ios/swift/Func_void_std__variant_bool__NitroPurchaseResult_.swift +59 -0
  132. package/nitrogen/generated/ios/swift/Func_void_std__vector_NitroProduct_.swift +47 -0
  133. package/nitrogen/generated/ios/swift/Func_void_std__vector_NitroPurchase_.swift +47 -0
  134. package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +78 -0
  135. package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +750 -0
  136. package/nitrogen/generated/ios/swift/NitroAndroidReceiptValidationOptions.swift +80 -0
  137. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesAndroidOptions.swift +54 -0
  138. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesIosOptions.swift +116 -0
  139. package/nitrogen/generated/ios/swift/NitroAvailablePurchasesOptions.swift +84 -0
  140. package/nitrogen/generated/ios/swift/NitroFinishTransactionAndroidParams.swift +58 -0
  141. package/nitrogen/generated/ios/swift/NitroFinishTransactionIosParams.swift +35 -0
  142. package/nitrogen/generated/ios/swift/NitroFinishTransactionParams.swift +84 -0
  143. package/nitrogen/generated/ios/swift/NitroProduct.swift +653 -0
  144. package/nitrogen/generated/ios/swift/NitroPurchase.swift +453 -0
  145. package/nitrogen/generated/ios/swift/NitroPurchaseRequest.swift +84 -0
  146. package/nitrogen/generated/ios/swift/NitroPurchaseResult.swift +117 -0
  147. package/nitrogen/generated/ios/swift/NitroReceiptValidationParams.swift +65 -0
  148. package/nitrogen/generated/ios/swift/NitroReceiptValidationResultAndroid.swift +258 -0
  149. package/nitrogen/generated/ios/swift/NitroReceiptValidationResultIOS.swift +87 -0
  150. package/nitrogen/generated/ios/swift/NitroRequestPurchaseAndroid.swift +225 -0
  151. package/nitrogen/generated/ios/swift/NitroRequestPurchaseIos.swift +161 -0
  152. package/nitrogen/generated/ios/swift/NitroSubscriptionOffer.swift +46 -0
  153. package/nitrogen/generated/ios/swift/NitroSubscriptionRenewalInfo.swift +152 -0
  154. package/nitrogen/generated/ios/swift/NitroSubscriptionStatus.swift +76 -0
  155. package/nitrogen/generated/ios/swift/Variant_Bool_NitroPurchaseResult.swift +18 -0
  156. package/nitrogen/generated/ios/swift/Variant_NitroReceiptValidationResultIOS_NitroReceiptValidationResultAndroid.swift +18 -0
  157. package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +50 -0
  158. package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +125 -0
  159. package/nitrogen/generated/shared/c++/NitroAndroidReceiptValidationOptions.hpp +80 -0
  160. package/nitrogen/generated/shared/c++/NitroAvailablePurchasesAndroidOptions.hpp +68 -0
  161. package/nitrogen/generated/shared/c++/NitroAvailablePurchasesIosOptions.hpp +79 -0
  162. package/nitrogen/generated/shared/c++/NitroAvailablePurchasesOptions.hpp +76 -0
  163. package/nitrogen/generated/shared/c++/NitroFinishTransactionAndroidParams.hpp +72 -0
  164. package/nitrogen/generated/shared/c++/NitroFinishTransactionIosParams.hpp +67 -0
  165. package/nitrogen/generated/shared/c++/NitroFinishTransactionParams.hpp +76 -0
  166. package/nitrogen/generated/shared/c++/NitroProduct.hpp +168 -0
  167. package/nitrogen/generated/shared/c++/NitroPurchase.hpp +136 -0
  168. package/nitrogen/generated/shared/c++/NitroPurchaseRequest.hpp +76 -0
  169. package/nitrogen/generated/shared/c++/NitroPurchaseResult.hpp +84 -0
  170. package/nitrogen/generated/shared/c++/NitroReceiptValidationParams.hpp +74 -0
  171. package/nitrogen/generated/shared/c++/NitroReceiptValidationResultAndroid.hpp +136 -0
  172. package/nitrogen/generated/shared/c++/NitroReceiptValidationResultIOS.hpp +82 -0
  173. package/nitrogen/generated/shared/c++/NitroRequestPurchaseAndroid.hpp +95 -0
  174. package/nitrogen/generated/shared/c++/NitroRequestPurchaseIos.hpp +85 -0
  175. package/nitrogen/generated/shared/c++/NitroSubscriptionOffer.hpp +71 -0
  176. package/nitrogen/generated/shared/c++/NitroSubscriptionRenewalInfo.hpp +88 -0
  177. package/nitrogen/generated/shared/c++/NitroSubscriptionStatus.hpp +78 -0
  178. package/package.json +144 -104
  179. package/plugin/build/src/withIAP.d.ts +3 -0
  180. package/plugin/build/src/withIAP.js +81 -0
  181. package/plugin/build/tsconfig.tsbuildinfo +1 -0
  182. package/plugin/build/withIAP.d.ts +2 -7
  183. package/plugin/build/withIAP.js +62 -51
  184. package/plugin/src/withIAP.ts +119 -0
  185. package/plugin/tsconfig.json +18 -0
  186. package/plugin/tsconfig.tsbuildinfo +1 -0
  187. package/src/helpers/subscription.ts +65 -0
  188. package/src/hooks/useIAP.ts +361 -222
  189. package/src/index.ts +1194 -9
  190. package/src/specs/RnIap.nitro.ts +501 -0
  191. package/src/types.ts +680 -0
  192. package/src/utils/error.ts +97 -0
  193. package/src/utils/type-bridge.ts +209 -0
  194. package/LICENSE +0 -21
  195. package/RNIap.podspec +0 -36
  196. package/android/src/amazon/AndroidManifest.xml +0 -14
  197. package/android/src/amazon/java/com/dooboolab/rniap/EventSender.kt +0 -10
  198. package/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxy.kt +0 -29
  199. package/android/src/amazon/java/com/dooboolab/rniap/PurchasingServiceProxyAmazonImpl.kt +0 -31
  200. package/android/src/amazon/java/com/dooboolab/rniap/RNIapActivityListener.kt +0 -55
  201. package/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonListener.kt +0 -325
  202. package/android/src/amazon/java/com/dooboolab/rniap/RNIapAmazonModule.kt +0 -244
  203. package/android/src/amazon/java/com/dooboolab/rniap/RNIapPackage.kt +0 -17
  204. package/android/src/amazon/java/com/dooboolab/rniap/modifysubscription/RNIapAmazonModifySubscriptionListener.kt +0 -88
  205. package/android/src/amazon/java/com/dooboolab/rniap/utils/Extensions.kt +0 -22
  206. package/android/src/main/java/com/dooboolab/rniap/PromiseUtils.kt +0 -62
  207. package/android/src/main/java/com/dooboolab/rniap/PromiseUtlis.kt +0 -43
  208. package/android/src/play/java/com/dooboolab/rniap/PlayUtils.kt +0 -114
  209. package/android/src/play/java/com/dooboolab/rniap/RNIapActivityListener.kt +0 -15
  210. package/android/src/play/java/com/dooboolab/rniap/RNIapModule.kt +0 -911
  211. package/android/src/play/java/com/dooboolab/rniap/RNIapPackage.kt +0 -17
  212. package/android/src/testAmazon/java/com/dooboolab/rniap/RNIapAmazonModuleTest.kt +0 -170
  213. package/app.plugin.js +0 -1
  214. package/ios/IapSerializationUtils.swift +0 -290
  215. package/ios/IapTypes.swift +0 -56
  216. package/ios/IapUtils.swift +0 -40
  217. package/ios/LatestPromiseKeeper.swift +0 -52
  218. package/ios/RNIapIos-Bridging-Header.h +0 -2
  219. package/ios/RNIapIos.m +0 -69
  220. package/ios/RNIapIos.swift +0 -868
  221. package/ios/RNIapIos.xcodeproj/project.pbxproj +0 -289
  222. package/ios/RNIapIosSk2.m +0 -126
  223. package/ios/RNIapIosSk2.swift +0 -1413
  224. package/ios/ThreadSafe.swift +0 -18
  225. package/lib/commonjs/eventEmitter.js +0 -195
  226. package/lib/commonjs/eventEmitter.js.map +0 -1
  227. package/lib/commonjs/hooks/index.js +0 -17
  228. package/lib/commonjs/hooks/index.js.map +0 -1
  229. package/lib/commonjs/hooks/useIAP.js +0 -203
  230. package/lib/commonjs/hooks/useIAP.js.map +0 -1
  231. package/lib/commonjs/hooks/withIAPContext.js +0 -95
  232. package/lib/commonjs/hooks/withIAPContext.js.map +0 -1
  233. package/lib/commonjs/iap.js +0 -836
  234. package/lib/commonjs/iap.js.map +0 -1
  235. package/lib/commonjs/index.js +0 -105
  236. package/lib/commonjs/index.js.map +0 -1
  237. package/lib/commonjs/internal/enhancedFetch.js +0 -26
  238. package/lib/commonjs/internal/enhancedFetch.js.map +0 -1
  239. package/lib/commonjs/internal/fillProductsWithAdditionalData.js +0 -50
  240. package/lib/commonjs/internal/fillProductsWithAdditionalData.js.map +0 -1
  241. package/lib/commonjs/internal/index.js +0 -39
  242. package/lib/commonjs/internal/index.js.map +0 -1
  243. package/lib/commonjs/internal/platform.js +0 -119
  244. package/lib/commonjs/internal/platform.js.map +0 -1
  245. package/lib/commonjs/modules/amazon.js +0 -50
  246. package/lib/commonjs/modules/amazon.js.map +0 -1
  247. package/lib/commonjs/modules/android.js +0 -92
  248. package/lib/commonjs/modules/android.js.map +0 -1
  249. package/lib/commonjs/modules/common.js +0 -2
  250. package/lib/commonjs/modules/index.js +0 -50
  251. package/lib/commonjs/modules/index.js.map +0 -1
  252. package/lib/commonjs/modules/ios.js +0 -160
  253. package/lib/commonjs/modules/ios.js.map +0 -1
  254. package/lib/commonjs/modules/iosSk2.js +0 -113
  255. package/lib/commonjs/modules/iosSk2.js.map +0 -1
  256. package/lib/commonjs/purchaseError.js +0 -170
  257. package/lib/commonjs/purchaseError.js.map +0 -1
  258. package/lib/commonjs/types/amazon.js +0 -2
  259. package/lib/commonjs/types/amazon.js.map +0 -1
  260. package/lib/commonjs/types/android.js +0 -67
  261. package/lib/commonjs/types/android.js.map +0 -1
  262. package/lib/commonjs/types/apple.js +0 -24
  263. package/lib/commonjs/types/apple.js.map +0 -1
  264. package/lib/commonjs/types/appleSk2.js +0 -137
  265. package/lib/commonjs/types/appleSk2.js.map +0 -1
  266. package/lib/commonjs/types/index.js +0 -67
  267. package/lib/commonjs/types/index.js.map +0 -1
  268. package/lib/commonjs/utils/errorMapping.js +0 -83
  269. package/lib/commonjs/utils/errorMapping.js.map +0 -1
  270. package/lib/commonjs/utils/typeGuards.js +0 -74
  271. package/lib/commonjs/utils/typeGuards.js.map +0 -1
  272. package/lib/module/eventEmitter.js +0 -185
  273. package/lib/module/eventEmitter.js.map +0 -1
  274. package/lib/module/hooks/index.js +0 -2
  275. package/lib/module/hooks/index.js.map +0 -1
  276. package/lib/module/hooks/withIAPContext.js +0 -86
  277. package/lib/module/hooks/withIAPContext.js.map +0 -1
  278. package/lib/module/iap.js +0 -805
  279. package/lib/module/iap.js.map +0 -1
  280. package/lib/module/internal/enhancedFetch.js +0 -19
  281. package/lib/module/internal/enhancedFetch.js.map +0 -1
  282. package/lib/module/internal/fillProductsWithAdditionalData.js +0 -43
  283. package/lib/module/internal/fillProductsWithAdditionalData.js.map +0 -1
  284. package/lib/module/internal/index.js +0 -4
  285. package/lib/module/internal/index.js.map +0 -1
  286. package/lib/module/internal/platform.js +0 -102
  287. package/lib/module/internal/platform.js.map +0 -1
  288. package/lib/module/modules/amazon.js +0 -42
  289. package/lib/module/modules/amazon.js.map +0 -1
  290. package/lib/module/modules/android.js +0 -81
  291. package/lib/module/modules/android.js.map +0 -1
  292. package/lib/module/modules/common.js +0 -2
  293. package/lib/module/modules/common.js.map +0 -1
  294. package/lib/module/modules/index.js +0 -5
  295. package/lib/module/modules/index.js.map +0 -1
  296. package/lib/module/modules/ios.js +0 -145
  297. package/lib/module/modules/ios.js.map +0 -1
  298. package/lib/module/modules/iosSk2.js +0 -96
  299. package/lib/module/modules/iosSk2.js.map +0 -1
  300. package/lib/module/purchaseError.js +0 -164
  301. package/lib/module/purchaseError.js.map +0 -1
  302. package/lib/module/types/amazon.js +0 -2
  303. package/lib/module/types/amazon.js.map +0 -1
  304. package/lib/module/types/android.js +0 -63
  305. package/lib/module/types/android.js.map +0 -1
  306. package/lib/module/types/apple.js +0 -17
  307. package/lib/module/types/apple.js.map +0 -1
  308. package/lib/module/types/appleSk2.js +0 -129
  309. package/lib/module/types/appleSk2.js.map +0 -1
  310. package/lib/module/types/index.js +0 -75
  311. package/lib/module/types/index.js.map +0 -1
  312. package/lib/module/utils/errorMapping.js +0 -75
  313. package/lib/module/utils/errorMapping.js.map +0 -1
  314. package/lib/module/utils/typeGuards.js +0 -61
  315. package/lib/module/utils/typeGuards.js.map +0 -1
  316. package/lib/typescript/src/eventEmitter.d.ts +0 -151
  317. package/lib/typescript/src/eventEmitter.d.ts.map +0 -1
  318. package/lib/typescript/src/hooks/index.d.ts +0 -2
  319. package/lib/typescript/src/hooks/index.d.ts.map +0 -1
  320. package/lib/typescript/src/hooks/withIAPContext.d.ts +0 -27
  321. package/lib/typescript/src/hooks/withIAPContext.d.ts.map +0 -1
  322. package/lib/typescript/src/iap.d.ts +0 -503
  323. package/lib/typescript/src/iap.d.ts.map +0 -1
  324. package/lib/typescript/src/internal/enhancedFetch.d.ts +0 -6
  325. package/lib/typescript/src/internal/enhancedFetch.d.ts.map +0 -1
  326. package/lib/typescript/src/internal/fillProductsWithAdditionalData.d.ts +0 -7
  327. package/lib/typescript/src/internal/fillProductsWithAdditionalData.d.ts.map +0 -1
  328. package/lib/typescript/src/internal/index.d.ts +0 -4
  329. package/lib/typescript/src/internal/index.d.ts.map +0 -1
  330. package/lib/typescript/src/internal/platform.d.ts +0 -27
  331. package/lib/typescript/src/internal/platform.d.ts.map +0 -1
  332. package/lib/typescript/src/modules/amazon.d.ts +0 -55
  333. package/lib/typescript/src/modules/amazon.d.ts.map +0 -1
  334. package/lib/typescript/src/modules/android.d.ts +0 -74
  335. package/lib/typescript/src/modules/android.d.ts.map +0 -1
  336. package/lib/typescript/src/modules/common.d.ts +0 -14
  337. package/lib/typescript/src/modules/common.d.ts.map +0 -1
  338. package/lib/typescript/src/modules/index.d.ts +0 -5
  339. package/lib/typescript/src/modules/index.d.ts.map +0 -1
  340. package/lib/typescript/src/modules/ios.d.ts +0 -117
  341. package/lib/typescript/src/modules/ios.d.ts.map +0 -1
  342. package/lib/typescript/src/modules/iosSk2.d.ts +0 -140
  343. package/lib/typescript/src/modules/iosSk2.d.ts.map +0 -1
  344. package/lib/typescript/src/purchaseError.d.ts +0 -133
  345. package/lib/typescript/src/purchaseError.d.ts.map +0 -1
  346. package/lib/typescript/src/types/amazon.d.ts +0 -33
  347. package/lib/typescript/src/types/amazon.d.ts.map +0 -1
  348. package/lib/typescript/src/types/android.d.ts +0 -66
  349. package/lib/typescript/src/types/android.d.ts.map +0 -1
  350. package/lib/typescript/src/types/apple.d.ts +0 -27
  351. package/lib/typescript/src/types/apple.d.ts.map +0 -1
  352. package/lib/typescript/src/types/appleSk2.d.ts +0 -129
  353. package/lib/typescript/src/types/appleSk2.d.ts.map +0 -1
  354. package/lib/typescript/src/types/index.d.ts +0 -302
  355. package/lib/typescript/src/types/index.d.ts.map +0 -1
  356. package/lib/typescript/src/utils/errorMapping.d.ts +0 -29
  357. package/lib/typescript/src/utils/errorMapping.d.ts.map +0 -1
  358. package/lib/typescript/src/utils/typeGuards.d.ts +0 -53
  359. package/lib/typescript/src/utils/typeGuards.d.ts.map +0 -1
  360. package/src/eventEmitter.ts +0 -212
  361. package/src/hooks/index.ts +0 -1
  362. package/src/hooks/withIAPContext.tsx +0 -179
  363. package/src/iap.ts +0 -1046
  364. package/src/internal/enhancedFetch.ts +0 -25
  365. package/src/internal/fillProductsWithAdditionalData.ts +0 -47
  366. package/src/internal/index.ts +0 -3
  367. package/src/internal/platform.ts +0 -135
  368. package/src/modules/amazon.ts +0 -94
  369. package/src/modules/android.ts +0 -179
  370. package/src/modules/common.ts +0 -16
  371. package/src/modules/index.ts +0 -4
  372. package/src/modules/ios.ts +0 -229
  373. package/src/modules/iosSk2.ts +0 -194
  374. package/src/purchaseError.ts +0 -196
  375. package/src/types/amazon.ts +0 -40
  376. package/src/types/android.ts +0 -89
  377. package/src/types/apple.ts +0 -44
  378. package/src/types/appleSk2.ts +0 -275
  379. package/src/types/index.ts +0 -379
  380. package/src/utils/errorMapping.ts +0 -88
  381. package/src/utils/typeGuards.ts +0 -90
@@ -0,0 +1,1326 @@
1
+ import Foundation
2
+ import NitroModules
3
+ import StoreKit
4
+ #if os(iOS) || os(tvOS)
5
+ import UIKit
6
+ #endif
7
+
8
+ @available(iOS 15.0, *)
9
+ class HybridRnIap: HybridRnIapSpec {
10
+ // MARK: - Properties
11
+
12
+ private var updateListenerTask: Task<Void, Never>?
13
+ private var isInitialized: Bool = false
14
+ private var productStore: ProductStore?
15
+ private var transactions: [String: Transaction] = [:]
16
+ private var paymentObserver: PaymentObserver?
17
+
18
+ // Promoted products
19
+ private var promotedProduct: Product?
20
+ private var promotedPayment: SKPayment?
21
+ private var promotedSKProduct: SKProduct?
22
+
23
+ // Event listeners
24
+ private var purchaseUpdatedListeners: [(NitroPurchase) -> Void] = []
25
+ private var purchaseErrorListeners: [(NitroPurchaseResult) -> Void] = []
26
+ private var promotedProductListeners: [(NitroProduct) -> Void] = []
27
+
28
+ // MARK: - Initialization
29
+
30
+ override init() {
31
+ super.init()
32
+ }
33
+
34
+ deinit {
35
+ updateListenerTask?.cancel()
36
+ }
37
+
38
+ // MARK: - Public Methods (Cross-platform)
39
+
40
+ func initConnection() throws -> Promise<Bool> {
41
+ return Promise.async {
42
+ // Clean up any existing state first (important for hot reload)
43
+ self.cleanupExistingState()
44
+
45
+ // StoreKit 2 doesn't require explicit connection initialization
46
+ // Just verify that the store is available
47
+ let canMakePayments = SKPaymentQueue.canMakePayments()
48
+ if canMakePayments {
49
+ self.isInitialized = true
50
+ self.productStore = ProductStore()
51
+
52
+ // Set up PaymentObserver for promoted products
53
+ if self.paymentObserver == nil {
54
+ self.paymentObserver = PaymentObserver(iapModule: self)
55
+ SKPaymentQueue.default().add(self.paymentObserver!)
56
+ }
57
+ } else {
58
+ let errorJson = ErrorUtils.createErrorJson(
59
+ code: IapErrorCode.iapNotAvailable,
60
+ message: "In-app purchases are not available on this device"
61
+ )
62
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
63
+ }
64
+ return canMakePayments
65
+ }
66
+ }
67
+
68
+ func endConnection() throws -> Promise<Bool> {
69
+ return Promise.async {
70
+ self.cleanupExistingState()
71
+ return true
72
+ }
73
+ }
74
+
75
+ func fetchProducts(skus: [String], type: String) throws -> Promise<[NitroProduct]> {
76
+ return Promise.async {
77
+ do {
78
+ try self.ensureConnection()
79
+ print("[RnIap] fetchProducts called with skus: \(skus), type: \(type)")
80
+
81
+ // Fetch products from StoreKit 2
82
+ let storeProducts = try await StoreKit.Product.products(for: Set(skus))
83
+ print("[RnIap] StoreKit returned \(storeProducts.count) products")
84
+
85
+ // Store products in ProductStore
86
+ if let productStore = self.productStore {
87
+ await productStore.addProducts(storeProducts)
88
+ }
89
+
90
+ // Convert StoreKit products to NitroProduct
91
+ let nitroProducts = storeProducts.map { storeProduct in
92
+ print("[RnIap] Converting product: \(storeProduct.id)")
93
+ return self.convertToNitroProduct(storeProduct, type: type)
94
+ }
95
+
96
+ print("[RnIap] Returning \(nitroProducts.count) NitroProducts")
97
+ return nitroProducts
98
+ } catch {
99
+ print("[RnIap] Error fetching products: \(error)")
100
+ let errorJson = ErrorUtils.createErrorJson(from: error)
101
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
102
+ }
103
+ }
104
+ }
105
+
106
+ func requestPurchase(request: NitroPurchaseRequest) throws -> Promise<Void> {
107
+ return Promise.async {
108
+ // iOS implementation
109
+ guard let iosRequest = request.ios else {
110
+ // No iOS request, send error event
111
+ let error = self.createPurchaseErrorResult(
112
+ code: IapErrorCode.userError,
113
+ message: "No iOS request provided"
114
+ )
115
+ self.sendPurchaseError(error)
116
+ return
117
+ }
118
+
119
+ do {
120
+ try self.ensureConnection()
121
+ guard let productStore = self.productStore else {
122
+ let error = self.createPurchaseErrorResult(
123
+ code: IapErrorCode.notPrepared,
124
+ message: "Product store not initialized",
125
+ productId: iosRequest.sku
126
+ )
127
+ self.sendPurchaseError(error)
128
+ return
129
+ }
130
+
131
+ // Get product from store or fetch if not cached
132
+ var product: Product? = await productStore.getProduct(productID: iosRequest.sku)
133
+
134
+ if product == nil {
135
+ // Try fetching from StoreKit if not in cache
136
+ let products = try await Product.products(for: [iosRequest.sku])
137
+ guard let fetchedProduct = products.first else {
138
+ let error = self.createPurchaseErrorResult(
139
+ code: IapErrorCode.itemUnavailable,
140
+ message: "Invalid product ID: \(iosRequest.sku)",
141
+ productId: iosRequest.sku
142
+ )
143
+ self.sendPurchaseError(error)
144
+ return
145
+ }
146
+ // Store for future use
147
+ await productStore.addProduct(fetchedProduct)
148
+ product = fetchedProduct
149
+ }
150
+
151
+ // Purchase the product - this will send events internally
152
+ try await self.purchaseProductWithEvents(
153
+ product!,
154
+ sku: iosRequest.sku,
155
+ andDangerouslyFinishTransactionAutomatically: iosRequest.andDangerouslyFinishTransactionAutomatically ?? false,
156
+ appAccountToken: iosRequest.appAccountToken,
157
+ quantity: iosRequest.quantity,
158
+ withOffer: iosRequest.withOffer
159
+ )
160
+ } catch {
161
+ // Map StoreKit errors to proper error codes
162
+ if let nsError = error as NSError? {
163
+ var errorCode = IapErrorCode.purchaseError
164
+ var errorMessage = error.localizedDescription
165
+
166
+ switch nsError.domain {
167
+ case "SKErrorDomain":
168
+ switch nsError.code {
169
+ case 0: // SKError.unknown
170
+ errorCode = IapErrorCode.unknown
171
+ case 1: // SKError.clientInvalid
172
+ errorCode = IapErrorCode.serviceError
173
+ case 2: // SKError.paymentCancelled
174
+ errorCode = IapErrorCode.userCancelled
175
+ errorMessage = "User cancelled the purchase"
176
+ case 3: // SKError.paymentInvalid
177
+ errorCode = IapErrorCode.userError
178
+ case 4: // SKError.paymentNotAllowed
179
+ errorCode = IapErrorCode.userError
180
+ errorMessage = "Payment not allowed"
181
+ case 5: // SKError.storeProductNotAvailable
182
+ errorCode = IapErrorCode.itemUnavailable
183
+ case 6: // SKError.cloudServicePermissionDenied
184
+ errorCode = IapErrorCode.serviceError
185
+ case 7: // SKError.cloudServiceNetworkConnectionFailed
186
+ errorCode = IapErrorCode.networkError
187
+ case 8: // SKError.cloudServiceRevoked
188
+ errorCode = IapErrorCode.serviceError
189
+ default:
190
+ errorCode = IapErrorCode.purchaseError
191
+ }
192
+ case "NSURLErrorDomain":
193
+ errorCode = IapErrorCode.networkError
194
+ errorMessage = "Network error: \(error.localizedDescription)"
195
+ default:
196
+ if error.localizedDescription.lowercased().contains("network") {
197
+ errorCode = IapErrorCode.networkError
198
+ } else if error.localizedDescription.lowercased().contains("cancelled") {
199
+ errorCode = IapErrorCode.userCancelled
200
+ }
201
+ }
202
+
203
+ let error = self.createPurchaseErrorResult(
204
+ code: errorCode,
205
+ message: errorMessage,
206
+ productId: iosRequest.sku
207
+ )
208
+ self.sendPurchaseError(error)
209
+ } else {
210
+ let error = self.createPurchaseErrorResult(
211
+ code: IapErrorCode.purchaseError,
212
+ message: error.localizedDescription,
213
+ productId: iosRequest.sku
214
+ )
215
+ self.sendPurchaseError(error)
216
+ }
217
+ }
218
+ }
219
+ }
220
+
221
+ func getAvailablePurchases(options: NitroAvailablePurchasesOptions?) throws -> Promise<[NitroPurchase]> {
222
+ return Promise.async {
223
+ try self.ensureConnection()
224
+
225
+ // Support both new IOS suffixed and deprecated parameters
226
+ let _ = options?.ios?.alsoPublishToEventListenerIOS ?? options?.ios?.alsoPublishToEventListener ?? false
227
+ let onlyIncludeActiveItems = options?.ios?.onlyIncludeActiveItemsIOS ?? options?.ios?.onlyIncludeActiveItems ?? false
228
+
229
+ var purchases: [NitroPurchase] = []
230
+
231
+ // Get all transactions
232
+ for await verification in Transaction.currentEntitlements {
233
+ switch verification {
234
+ case .verified(let transaction):
235
+ // Get JWS representation from verification result
236
+ let jwsRepresentation = verification.jwsRepresentation
237
+
238
+ if onlyIncludeActiveItems {
239
+ // Only include active subscriptions and non-consumables
240
+ if transaction.productType == .nonConsumable ||
241
+ (transaction.productType == .autoRenewable && transaction.revocationDate == nil) {
242
+ if let products = try? await StoreKit.Product.products(for: [transaction.productID]),
243
+ let product = products.first {
244
+ purchases.append(self.convertToNitroPurchase(transaction, product: product, jwsRepresentation: jwsRepresentation))
245
+ }
246
+ }
247
+ } else {
248
+ // Include all transactions
249
+ if let products = try? await StoreKit.Product.products(for: [transaction.productID]),
250
+ let product = products.first {
251
+ purchases.append(self.convertToNitroPurchase(transaction, product: product, jwsRepresentation: jwsRepresentation))
252
+ }
253
+ }
254
+ case .unverified:
255
+ continue
256
+ }
257
+ }
258
+
259
+ return purchases
260
+ }
261
+ }
262
+
263
+ func finishTransaction(params: NitroFinishTransactionParams) throws -> Promise<Variant_Bool_NitroPurchaseResult> {
264
+ return Promise.async {
265
+ // iOS implementation
266
+ guard let iosParams = params.ios else {
267
+ // No iOS params, return success
268
+ return .first(true)
269
+ }
270
+
271
+ try self.ensureConnection()
272
+
273
+ // Find the transaction
274
+ for await verification in Transaction.all {
275
+ switch verification {
276
+ case .verified(let transaction):
277
+ if String(transaction.id) == iosParams.transactionId {
278
+ await transaction.finish()
279
+ return .first(true)
280
+ }
281
+ case .unverified:
282
+ continue
283
+ }
284
+ }
285
+
286
+ let errorJson = ErrorUtils.createErrorJson(
287
+ code: IapErrorCode.itemUnavailable,
288
+ message: "Transaction not found: \(iosParams.transactionId)"
289
+ )
290
+ throw NSError(domain: "RnIap", code: 0, userInfo: [NSLocalizedDescriptionKey: errorJson])
291
+ }
292
+ }
293
+
294
+ func validateReceipt(params: NitroReceiptValidationParams) throws -> Promise<Variant_NitroReceiptValidationResultIOS_NitroReceiptValidationResultAndroid> {
295
+ return Promise.async {
296
+ do {
297
+ // Get the app receipt data
298
+ guard let receiptURL = Bundle.main.appStoreReceiptURL,
299
+ let receiptData = try? Data(contentsOf: receiptURL) else {
300
+ let errorJson = ErrorUtils.createErrorJson(
301
+ code: IapErrorCode.receiptFailed,
302
+ message: "App receipt not found or could not be read"
303
+ )
304
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
305
+ }
306
+
307
+ let receiptDataBase64 = receiptData.base64EncodedString()
308
+
309
+ // For StoreKit 2, we can use Transaction.currentEntitlements or Transaction.all
310
+ // to get the latest transaction for the specified SKU
311
+ var latestTransaction: NitroPurchase? = nil
312
+
313
+ // Find the latest transaction for the specified SKU
314
+ for await verificationResult in Transaction.currentEntitlements {
315
+ switch verificationResult {
316
+ case .verified(let transaction):
317
+ if transaction.productID == params.sku {
318
+ // Fetch the product details for this transaction
319
+ if let products = try? await StoreKit.Product.products(for: [transaction.productID]),
320
+ let product = products.first {
321
+ latestTransaction = self.convertToNitroPurchase(transaction, product: product, jwsRepresentation: nil)
322
+ }
323
+ break
324
+ }
325
+ case .unverified(_, let verificationError):
326
+ // Handle unverified transactions if needed
327
+ print("Unverified transaction for SKU \(params.sku): \(verificationError)")
328
+ }
329
+ }
330
+
331
+ // For StoreKit 2, the receipt is always considered valid if we can read it
332
+ // and the transaction verification passed
333
+ let isValid = latestTransaction != nil
334
+
335
+ // Generate JWS representation (simplified for now)
336
+ let jwsRepresentation = receiptDataBase64 // In a real implementation, this would be the actual JWS
337
+
338
+ let result = NitroReceiptValidationResultIOS(
339
+ isValid: isValid,
340
+ receiptData: receiptDataBase64,
341
+ jwsRepresentation: jwsRepresentation,
342
+ latestTransaction: latestTransaction
343
+ )
344
+ return Variant_NitroReceiptValidationResultIOS_NitroReceiptValidationResultAndroid.first(result)
345
+
346
+ } catch {
347
+ let errorJson = ErrorUtils.createErrorJson(
348
+ code: IapErrorCode.receiptFailed,
349
+ message: "Receipt validation failed: \(error.localizedDescription)"
350
+ )
351
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
352
+ }
353
+ }
354
+ }
355
+
356
+ // MARK: - iOS-specific Public Methods
357
+
358
+ func getStorefrontIOS() throws -> Promise<String> {
359
+ return Promise.async {
360
+ // Get the current storefront from StoreKit 2
361
+ if let storefront = await Storefront.current {
362
+ // Return the country code (e.g., "USA", "GBR", "KOR")
363
+ return storefront.countryCode
364
+ } else {
365
+ // If no storefront is available, throw an error
366
+ let errorJson = ErrorUtils.createErrorJson(
367
+ code: IapErrorCode.unknown,
368
+ message: "Unable to retrieve storefront information"
369
+ )
370
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
371
+ }
372
+ }
373
+ }
374
+
375
+ func getAppTransactionIOS() throws -> Promise<String?> {
376
+ return Promise.async {
377
+ if #available(iOS 16.0, *) {
378
+ #if compiler(>=5.7)
379
+ let verificationResult = try await AppTransaction.shared
380
+
381
+ let appTransaction: AppTransaction
382
+ switch verificationResult {
383
+ case .verified(let verified):
384
+ appTransaction = verified
385
+ case .unverified(_, _):
386
+ return nil
387
+ }
388
+
389
+ var result: [String: Any?] = [
390
+ "bundleId": appTransaction.bundleID,
391
+ "appVersion": appTransaction.appVersion,
392
+ "originalAppVersion": appTransaction.originalAppVersion,
393
+ "originalPurchaseDate": appTransaction.originalPurchaseDate.timeIntervalSince1970 * 1000,
394
+ "deviceVerification": appTransaction.deviceVerification.base64EncodedString(),
395
+ "deviceVerificationNonce": appTransaction.deviceVerificationNonce.uuidString,
396
+ "environment": appTransaction.environment.rawValue,
397
+ "signedDate": appTransaction.signedDate.timeIntervalSince1970 * 1000,
398
+ "appId": appTransaction.appID,
399
+ "appVersionId": appTransaction.appVersionID,
400
+ "preorderDate": appTransaction.preorderDate.map { $0.timeIntervalSince1970 * 1000 }
401
+ ]
402
+
403
+ // iOS 18.4+ properties - only compile with Xcode 16.4+ (Swift 6.1+)
404
+ // This prevents build failures on Xcode 16.3 and below
405
+ #if swift(>=6.1)
406
+ if #available(iOS 18.4, *) {
407
+ result["appTransactionId"] = appTransaction.appTransactionID
408
+ result["originalPlatform"] = appTransaction.originalPlatform.rawValue
409
+ }
410
+ #endif
411
+
412
+ // Convert dictionary to JSON string
413
+ let jsonData = try JSONSerialization.data(withJSONObject: result, options: [])
414
+ return String(data: jsonData, encoding: .utf8)
415
+ #else
416
+ let errorJson = ErrorUtils.createErrorJson(
417
+ code: IapErrorCode.unknown,
418
+ message: "getAppTransaction requires Xcode 15.0+ with iOS 16.0 SDK for compilation"
419
+ )
420
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
421
+ #endif
422
+ } else {
423
+ let errorJson = ErrorUtils.createErrorJson(
424
+ code: IapErrorCode.unknown,
425
+ message: "getAppTransaction requires iOS 16.0 or later"
426
+ )
427
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
428
+ }
429
+ }
430
+ }
431
+
432
+ func requestPromotedProductIOS() throws -> Promise<NitroProduct?> {
433
+ return Promise.async {
434
+ // Return the stored promoted product if available
435
+ if let product = self.promotedProduct {
436
+ // Convert Product to NitroProduct
437
+ return self.convertProductToNitroProduct(product, type: "inapp")
438
+ } else if let skProduct = self.promotedSKProduct {
439
+ // Convert SKProduct to NitroProduct (backward compatibility)
440
+ // First get the StoreKit 2 Product
441
+ if let products = try? await StoreKit.Product.products(for: [skProduct.productIdentifier]),
442
+ let product = products.first {
443
+ return self.convertProductToNitroProduct(product, type: "inapp")
444
+ }
445
+ }
446
+ return nil
447
+ }
448
+ }
449
+
450
+ func buyPromotedProductIOS() throws -> Promise<Void> {
451
+ return Promise.async {
452
+ // Check if we have a promoted payment to process
453
+ guard let payment = self.promotedPayment else {
454
+ let errorJson = ErrorUtils.createErrorJson(
455
+ code: IapErrorCode.itemUnavailable,
456
+ message: "No promoted product available"
457
+ )
458
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
459
+ }
460
+
461
+ // Add the deferred payment to the queue
462
+ SKPaymentQueue.default().add(payment)
463
+
464
+ // Clear the promoted product data
465
+ self.promotedPayment = nil
466
+ self.promotedProduct = nil
467
+ }
468
+ }
469
+
470
+ func presentCodeRedemptionSheetIOS() throws -> Promise<Bool> {
471
+ return Promise.async {
472
+ // Present the App Store's code redemption sheet
473
+ #if !targetEnvironment(simulator)
474
+ await MainActor.run {
475
+ SKPaymentQueue.default().presentCodeRedemptionSheet()
476
+ }
477
+ return true
478
+ #else
479
+ // Not available on simulator
480
+ let errorJson = ErrorUtils.createErrorJson(
481
+ code: IapErrorCode.itemUnavailable,
482
+ message: "Code redemption sheet is not available on simulator"
483
+ )
484
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
485
+ #endif
486
+ }
487
+ }
488
+
489
+ func clearTransactionIOS() throws -> Promise<Void> {
490
+ return Promise.async {
491
+ // Clear all unfinished transactions
492
+ for await result in Transaction.unfinished {
493
+ do {
494
+ let transaction = try self.checkVerified(result)
495
+ await transaction.finish()
496
+ self.transactions.removeValue(forKey: String(transaction.id))
497
+ } catch {
498
+ print("Failed to finish transaction: \(error.localizedDescription)")
499
+ }
500
+ }
501
+ }
502
+ }
503
+
504
+ // Additional iOS-only functions for feature parity with expo-iap
505
+
506
+ func subscriptionStatusIOS(sku: String) throws -> Promise<[NitroSubscriptionStatus]?> {
507
+ return Promise.async {
508
+ try self.ensureConnection()
509
+
510
+ // Get the product from the store
511
+ guard let products = try? await StoreKit.Product.products(for: [sku]),
512
+ let product = products.first,
513
+ let subscription = product.subscription else {
514
+ return []
515
+ }
516
+
517
+ // Get subscription status
518
+ let statuses = try await subscription.status
519
+
520
+ return statuses.map { status in
521
+ // Get renewal info if available
522
+ var renewalInfo: NitroSubscriptionRenewalInfo? = nil
523
+ switch status.renewalInfo {
524
+ case .verified(let info):
525
+ renewalInfo = NitroSubscriptionRenewalInfo(
526
+ autoRenewStatus: info.willAutoRenew,
527
+ autoRenewPreference: info.autoRenewPreference,
528
+ expirationReason: info.expirationReason.map { Double($0.rawValue) },
529
+ gracePeriodExpirationDate: info.gracePeriodExpirationDate?.timeIntervalSince1970,
530
+ currentProductID: info.currentProductID,
531
+ platform: "ios"
532
+ )
533
+ case .unverified:
534
+ renewalInfo = nil
535
+ }
536
+
537
+ return NitroSubscriptionStatus(
538
+ state: Double(status.state.rawValue),
539
+ platform: "ios",
540
+ renewalInfo: renewalInfo
541
+ )
542
+ }
543
+ }
544
+ }
545
+
546
+ func currentEntitlementIOS(sku: String) throws -> Promise<NitroPurchase?> {
547
+ return Promise.async {
548
+ try self.ensureConnection()
549
+
550
+ // Get the product from the store
551
+ guard let products = try? await StoreKit.Product.products(for: [sku]),
552
+ let product = products.first else {
553
+ let errorJson = ErrorUtils.createErrorJson(
554
+ code: IapErrorCode.itemUnavailable,
555
+ message: "Can't find product for sku \(sku)"
556
+ )
557
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
558
+ }
559
+
560
+ // Get current entitlement
561
+ guard let entitlement = await product.currentEntitlement else {
562
+ let errorJson = ErrorUtils.createErrorJson(
563
+ code: IapErrorCode.itemUnavailable,
564
+ message: "Can't find entitlement for sku \(sku)"
565
+ )
566
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
567
+ }
568
+
569
+ switch entitlement {
570
+ case .verified(let transaction):
571
+ return self.convertToNitroPurchase(transaction, product: product, jwsRepresentation: entitlement.jwsRepresentation)
572
+ case .unverified:
573
+ let errorJson = ErrorUtils.createErrorJson(
574
+ code: IapErrorCode.transactionValidationFailed,
575
+ message: "Failed to verify transaction for sku \(sku)"
576
+ )
577
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
578
+ }
579
+ }
580
+ }
581
+
582
+ func latestTransactionIOS(sku: String) throws -> Promise<NitroPurchase?> {
583
+ return Promise.async {
584
+ try self.ensureConnection()
585
+
586
+ // Get the product from the store
587
+ guard let products = try? await StoreKit.Product.products(for: [sku]),
588
+ let product = products.first else {
589
+ let errorJson = ErrorUtils.createErrorJson(
590
+ code: IapErrorCode.itemUnavailable,
591
+ message: "Can't find product for sku \(sku)"
592
+ )
593
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
594
+ }
595
+
596
+ // Get latest transaction
597
+ guard let latestTransaction = await product.latestTransaction else {
598
+ let errorJson = ErrorUtils.createErrorJson(
599
+ code: IapErrorCode.itemUnavailable,
600
+ message: "Can't find latest transaction for sku \(sku)"
601
+ )
602
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
603
+ }
604
+
605
+ switch latestTransaction {
606
+ case .verified(let transaction):
607
+ return self.convertToNitroPurchase(transaction, product: product, jwsRepresentation: latestTransaction.jwsRepresentation)
608
+ case .unverified:
609
+ let errorJson = ErrorUtils.createErrorJson(
610
+ code: IapErrorCode.transactionValidationFailed,
611
+ message: "Failed to verify transaction for sku \(sku)"
612
+ )
613
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
614
+ }
615
+ }
616
+ }
617
+
618
+ func getPendingTransactionsIOS() throws -> Promise<[NitroPurchase]> {
619
+ return Promise.async {
620
+ // Return the transactions we're currently tracking
621
+ var pendingPurchases: [NitroPurchase] = []
622
+
623
+ for (_, transaction) in self.transactions {
624
+ // Get product info for each transaction
625
+ if let products = try? await StoreKit.Product.products(for: [transaction.productID]),
626
+ let product = products.first {
627
+ pendingPurchases.append(self.convertToNitroPurchase(transaction, product: product))
628
+ }
629
+ }
630
+
631
+ return pendingPurchases
632
+ }
633
+ }
634
+
635
+ func syncIOS() throws -> Promise<Bool> {
636
+ return Promise.async {
637
+ do {
638
+ // Sync with the App Store
639
+ try await AppStore.sync()
640
+ return true
641
+ } catch {
642
+ let errorJson = ErrorUtils.createErrorJson(
643
+ code: IapErrorCode.syncError,
644
+ message: "Error synchronizing with the AppStore: \(error.localizedDescription)"
645
+ )
646
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
647
+ }
648
+ }
649
+ }
650
+
651
+ func showManageSubscriptionsIOS() throws -> Promise<Bool> {
652
+ return Promise.async {
653
+ #if !os(tvOS)
654
+ // Get the active window scene
655
+ guard let windowScene = await MainActor.run(body: {
656
+ UIApplication.shared.connectedScenes
657
+ .compactMap({ $0 as? UIWindowScene })
658
+ .first(where: { $0.activationState == .foregroundActive })
659
+ }) else {
660
+ let errorJson = ErrorUtils.createErrorJson(
661
+ code: IapErrorCode.serviceError,
662
+ message: "Cannot find active window scene"
663
+ )
664
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
665
+ }
666
+
667
+ // Show the subscription management UI
668
+ try await AppStore.showManageSubscriptions(in: windowScene)
669
+ return true
670
+ #else
671
+ let errorJson = ErrorUtils.createErrorJson(
672
+ code: IapErrorCode.itemUnavailable,
673
+ message: "This method is not available on tvOS"
674
+ )
675
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
676
+ #endif
677
+ }
678
+ }
679
+
680
+ func isEligibleForIntroOfferIOS(groupID: String) throws -> Promise<Bool> {
681
+ return Promise.async {
682
+ return await Product.SubscriptionInfo.isEligibleForIntroOffer(for: groupID)
683
+ }
684
+ }
685
+
686
+ func getReceiptDataIOS() throws -> Promise<String> {
687
+ return Promise.async {
688
+ if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
689
+ FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
690
+ do {
691
+ let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
692
+ return receiptData.base64EncodedString(options: [])
693
+ } catch {
694
+ let errorJson = ErrorUtils.createErrorJson(
695
+ code: IapErrorCode.receiptFailed,
696
+ message: "Error reading receipt data: \(error.localizedDescription)"
697
+ )
698
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
699
+ }
700
+ } else {
701
+ let errorJson = ErrorUtils.createErrorJson(
702
+ code: IapErrorCode.receiptFailed,
703
+ message: "App Store receipt not found"
704
+ )
705
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
706
+ }
707
+ }
708
+ }
709
+
710
+ func isTransactionVerifiedIOS(sku: String) throws -> Promise<Bool> {
711
+ return Promise.async {
712
+ try self.ensureConnection()
713
+
714
+ // Get the product from the store
715
+ guard let products = try? await StoreKit.Product.products(for: [sku]),
716
+ let product = products.first,
717
+ let result = await product.latestTransaction else {
718
+ return false
719
+ }
720
+
721
+ // Check if transaction is verified
722
+ switch result {
723
+ case .verified:
724
+ return true
725
+ case .unverified:
726
+ return false
727
+ }
728
+ }
729
+ }
730
+
731
+ func getTransactionJwsIOS(sku: String) throws -> Promise<String?> {
732
+ return Promise.async {
733
+ try self.ensureConnection()
734
+
735
+ // Get the product from the store
736
+ guard let products = try? await StoreKit.Product.products(for: [sku]),
737
+ let product = products.first,
738
+ let result = await product.latestTransaction else {
739
+ let errorJson = ErrorUtils.createErrorJson(
740
+ code: IapErrorCode.itemUnavailable,
741
+ message: "Can't find transaction for sku \(sku)"
742
+ )
743
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
744
+ }
745
+
746
+ return result.jwsRepresentation
747
+ }
748
+ }
749
+
750
+ func beginRefundRequestIOS(sku: String) throws -> Promise<String?> {
751
+ return Promise.async {
752
+ #if !os(tvOS)
753
+ if #available(macOS 12.0, *) {
754
+ // Find the latest transaction for the SKU
755
+ var latestTransaction: Transaction? = nil
756
+
757
+ for await result in Transaction.currentEntitlements {
758
+ switch result {
759
+ case .verified(let transaction):
760
+ if transaction.productID == sku {
761
+ latestTransaction = transaction
762
+ break
763
+ }
764
+ case .unverified(_, _):
765
+ continue
766
+ }
767
+ }
768
+
769
+ guard let transaction = latestTransaction else {
770
+ let errorJson = ErrorUtils.createErrorJson(
771
+ code: IapErrorCode.itemUnavailable,
772
+ message: "Can't find transaction for SKU \(sku)"
773
+ )
774
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
775
+ }
776
+
777
+ // Begin refund request
778
+ do {
779
+ // Get the active window scene
780
+ guard let windowScene = await MainActor.run(body: {
781
+ UIApplication.shared.connectedScenes
782
+ .compactMap({ $0 as? UIWindowScene })
783
+ .first(where: { $0.activationState == .foregroundActive })
784
+ }) else {
785
+ let errorJson = ErrorUtils.createErrorJson(
786
+ code: IapErrorCode.serviceError,
787
+ message: "Cannot find active window scene"
788
+ )
789
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
790
+ }
791
+
792
+ let refundStatus = try await transaction.beginRefundRequest(in: windowScene)
793
+
794
+ // Convert refund status to string
795
+ switch refundStatus {
796
+ case .success:
797
+ return "success"
798
+ case .userCancelled:
799
+ return "userCancelled"
800
+ @unknown default:
801
+ return "unknown"
802
+ }
803
+ } catch {
804
+ let errorJson = ErrorUtils.createErrorJson(
805
+ code: IapErrorCode.serviceError,
806
+ message: "Failed to begin refund request: \(error.localizedDescription)"
807
+ )
808
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
809
+ }
810
+ } else {
811
+ // Refund request is only available on iOS 15+
812
+ let errorJson = ErrorUtils.createErrorJson(
813
+ code: IapErrorCode.itemUnavailable,
814
+ message: "Refund request requires iOS 15.0 or later"
815
+ )
816
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
817
+ }
818
+ #else
819
+ // Not available on tvOS
820
+ let errorJson = ErrorUtils.createErrorJson(
821
+ code: IapErrorCode.itemUnavailable,
822
+ message: "Refund request is not available on tvOS"
823
+ )
824
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
825
+ #endif
826
+ }
827
+ }
828
+
829
+ func addPromotedProductListenerIOS(listener: @escaping (NitroProduct) -> Void) throws {
830
+ promotedProductListeners.append(listener)
831
+
832
+ // If we already have a promoted product, notify the new listener immediately
833
+ if let product = promotedProduct {
834
+ let nitroProduct = convertProductToNitroProduct(product, type: "inapp")
835
+ listener(nitroProduct)
836
+ }
837
+ }
838
+
839
+ func removePromotedProductListenerIOS(listener: @escaping (NitroProduct) -> Void) throws {
840
+ // Note: In Swift, comparing closures is not straightforward, so we'll clear all listeners
841
+ // In a real implementation, you might want to use a unique identifier for each listener
842
+ promotedProductListeners.removeAll()
843
+ }
844
+
845
+ // MARK: - Event Listener Methods
846
+
847
+ func addPurchaseUpdatedListener(listener: @escaping (NitroPurchase) -> Void) throws {
848
+ purchaseUpdatedListeners.append(listener)
849
+ }
850
+
851
+ func addPurchaseErrorListener(listener: @escaping (NitroPurchaseResult) -> Void) throws {
852
+ purchaseErrorListeners.append(listener)
853
+ }
854
+
855
+ func removePurchaseUpdatedListener(listener: @escaping (NitroPurchase) -> Void) throws {
856
+ // Note: This is a limitation of Swift closures - we can't easily remove by reference
857
+ // For now, we'll just clear all listeners when requested
858
+ purchaseUpdatedListeners.removeAll()
859
+ }
860
+
861
+ func removePurchaseErrorListener(listener: @escaping (NitroPurchaseResult) -> Void) throws {
862
+ // Note: This is a limitation of Swift closures - we can't easily remove by reference
863
+ // For now, we'll just clear all listeners when requested
864
+ purchaseErrorListeners.removeAll()
865
+ }
866
+
867
+ // MARK: - Private Helper Methods
868
+
869
+ private func purchaseProduct(
870
+ _ product: Product,
871
+ sku: String,
872
+ andDangerouslyFinishTransactionAutomatically: Bool,
873
+ appAccountToken: String?,
874
+ quantity: Double?,
875
+ withOffer: [String: String]?
876
+ ) async throws -> NitroPurchase {
877
+ // Prepare purchase options
878
+ var options: Set<Product.PurchaseOption> = []
879
+
880
+ // Add quantity if specified
881
+ if let quantity = quantity, quantity > 0 {
882
+ options.insert(.quantity(Int(quantity)))
883
+ }
884
+
885
+ // Add promotional offer if provided
886
+ if let offerID = withOffer?["identifier"],
887
+ let keyID = withOffer?["keyIdentifier"],
888
+ let nonce = withOffer?["nonce"],
889
+ let signature = withOffer?["signature"],
890
+ let timestamp = withOffer?["timestamp"],
891
+ let uuidNonce = UUID(uuidString: nonce),
892
+ let signatureData = Data(base64Encoded: signature),
893
+ let timestampInt = Int(timestamp) {
894
+ options.insert(
895
+ .promotionalOffer(
896
+ offerID: offerID,
897
+ keyID: keyID,
898
+ nonce: uuidNonce,
899
+ signature: signatureData,
900
+ timestamp: timestampInt
901
+ )
902
+ )
903
+ }
904
+
905
+ // Add app account token if provided
906
+ if let appAccountToken = appAccountToken,
907
+ let appAccountUUID = UUID(uuidString: appAccountToken) {
908
+ options.insert(.appAccountToken(appAccountUUID))
909
+ }
910
+
911
+ // Get window scene for iOS 17+ purchase confirmation
912
+ let windowScene = await currentWindowScene()
913
+
914
+ // Perform the purchase
915
+ let result: Product.PurchaseResult
916
+ #if swift(>=5.9)
917
+ if #available(iOS 17.0, tvOS 17.0, *) {
918
+ if let windowScene = windowScene {
919
+ result = try await product.purchase(confirmIn: windowScene, options: options)
920
+ } else {
921
+ result = try await product.purchase(options: options)
922
+ }
923
+ } else {
924
+ #if !os(visionOS)
925
+ result = try await product.purchase(options: options)
926
+ #endif
927
+ }
928
+ #elseif !os(visionOS)
929
+ result = try await product.purchase(options: options)
930
+ #endif
931
+
932
+ switch result {
933
+ case .success(let verification):
934
+ let transaction = try checkVerified(verification)
935
+
936
+ // Store transaction if not auto-finishing
937
+ if !andDangerouslyFinishTransactionAutomatically {
938
+ self.transactions[String(transaction.id)] = transaction
939
+ }
940
+
941
+ // Get JWS representation
942
+ let jwsRepresentation = verification.jwsRepresentation
943
+
944
+ // Create purchase object
945
+ let purchase = self.convertToNitroPurchase(
946
+ transaction,
947
+ product: product,
948
+ jwsRepresentation: jwsRepresentation
949
+ )
950
+
951
+ // Finish transaction if requested
952
+ if andDangerouslyFinishTransactionAutomatically {
953
+ await transaction.finish()
954
+ }
955
+
956
+ return purchase
957
+
958
+ case .userCancelled:
959
+ let errorJson = ErrorUtils.createErrorJson(
960
+ code: IapErrorCode.userCancelled,
961
+ message: "User cancelled the purchase",
962
+ productId: sku
963
+ )
964
+ throw NSError(domain: "RnIap", code: 1, userInfo: [NSLocalizedDescriptionKey: errorJson])
965
+
966
+ case .pending:
967
+ let errorJson = ErrorUtils.createErrorJson(
968
+ code: IapErrorCode.deferredPayment,
969
+ message: "The payment was deferred",
970
+ productId: sku
971
+ )
972
+ throw NSError(domain: "RnIap", code: 2, userInfo: [NSLocalizedDescriptionKey: errorJson])
973
+
974
+ @unknown default:
975
+ let errorJson = ErrorUtils.createErrorJson(
976
+ code: IapErrorCode.unknown,
977
+ message: "Unknown purchase result",
978
+ productId: sku
979
+ )
980
+ throw NSError(domain: "RnIap", code: 0, userInfo: [NSLocalizedDescriptionKey: errorJson])
981
+ }
982
+ }
983
+
984
+ private func purchaseProductWithEvents(
985
+ _ product: Product,
986
+ sku: String,
987
+ andDangerouslyFinishTransactionAutomatically: Bool,
988
+ appAccountToken: String?,
989
+ quantity: Double?,
990
+ withOffer: [String: String]?
991
+ ) async throws {
992
+ // Prepare purchase options
993
+ var options: Set<Product.PurchaseOption> = []
994
+
995
+ // Add quantity if specified
996
+ if let quantity = quantity, quantity > 0 {
997
+ options.insert(.quantity(Int(quantity)))
998
+ }
999
+
1000
+ // Add promotional offer if provided
1001
+ if let offerID = withOffer?["identifier"],
1002
+ let keyID = withOffer?["keyIdentifier"],
1003
+ let nonce = withOffer?["nonce"],
1004
+ let signature = withOffer?["signature"],
1005
+ let timestamp = withOffer?["timestamp"],
1006
+ let uuidNonce = UUID(uuidString: nonce),
1007
+ let signatureData = Data(base64Encoded: signature),
1008
+ let timestampInt = Int(timestamp) {
1009
+ options.insert(
1010
+ .promotionalOffer(
1011
+ offerID: offerID,
1012
+ keyID: keyID,
1013
+ nonce: uuidNonce,
1014
+ signature: signatureData,
1015
+ timestamp: timestampInt
1016
+ )
1017
+ )
1018
+ }
1019
+
1020
+ // Add app account token if provided
1021
+ if let appAccountToken = appAccountToken,
1022
+ let appAccountUUID = UUID(uuidString: appAccountToken) {
1023
+ options.insert(.appAccountToken(appAccountUUID))
1024
+ }
1025
+
1026
+ // Get window scene for iOS 17+ purchase confirmation
1027
+ let windowScene = await currentWindowScene()
1028
+
1029
+ // Perform the purchase
1030
+ let result: Product.PurchaseResult
1031
+ #if swift(>=5.9)
1032
+ if #available(iOS 17.0, tvOS 17.0, *) {
1033
+ if let windowScene = windowScene {
1034
+ result = try await product.purchase(confirmIn: windowScene, options: options)
1035
+ } else {
1036
+ result = try await product.purchase(options: options)
1037
+ }
1038
+ } else {
1039
+ #if !os(visionOS)
1040
+ result = try await product.purchase(options: options)
1041
+ #endif
1042
+ }
1043
+ #elseif !os(visionOS)
1044
+ result = try await product.purchase(options: options)
1045
+ #endif
1046
+
1047
+ switch result {
1048
+ case .success(let verification):
1049
+ let transaction = try checkVerified(verification)
1050
+
1051
+ // Store transaction if not auto-finishing
1052
+ if !andDangerouslyFinishTransactionAutomatically {
1053
+ self.transactions[String(transaction.id)] = transaction
1054
+ }
1055
+
1056
+ // Get JWS representation
1057
+ let jwsRepresentation = verification.jwsRepresentation
1058
+
1059
+ // Create purchase object
1060
+ let purchase = self.convertToNitroPurchase(
1061
+ transaction,
1062
+ product: product,
1063
+ jwsRepresentation: jwsRepresentation
1064
+ )
1065
+
1066
+ // Finish transaction if requested
1067
+ if andDangerouslyFinishTransactionAutomatically {
1068
+ await transaction.finish()
1069
+ }
1070
+
1071
+ // Send purchase update event
1072
+ self.sendPurchaseUpdate(purchase)
1073
+
1074
+ case .userCancelled:
1075
+ let error = self.createPurchaseErrorResult(
1076
+ code: IapErrorCode.userCancelled,
1077
+ message: "User cancelled the purchase",
1078
+ productId: sku
1079
+ )
1080
+ self.sendPurchaseError(error)
1081
+
1082
+ case .pending:
1083
+ let error = self.createPurchaseErrorResult(
1084
+ code: IapErrorCode.deferredPayment,
1085
+ message: "The payment was deferred",
1086
+ productId: sku
1087
+ )
1088
+ self.sendPurchaseError(error)
1089
+
1090
+ @unknown default:
1091
+ let error = self.createPurchaseErrorResult(
1092
+ code: IapErrorCode.unknown,
1093
+ message: "Unknown purchase result",
1094
+ productId: sku
1095
+ )
1096
+ self.sendPurchaseError(error)
1097
+ }
1098
+ }
1099
+
1100
+ private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
1101
+ switch result {
1102
+ case .verified(let safe):
1103
+ return safe
1104
+ case .unverified(_, let error):
1105
+ let errorJson = ErrorUtils.createErrorJson(
1106
+ code: IapErrorCode.transactionValidationFailed,
1107
+ message: "Transaction verification failed: \(error)",
1108
+ underlyingError: error
1109
+ )
1110
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
1111
+ }
1112
+ }
1113
+
1114
+ private func ensureConnection() throws {
1115
+ guard isInitialized else {
1116
+ let errorJson = ErrorUtils.createErrorJson(
1117
+ code: IapErrorCode.notPrepared,
1118
+ message: "Connection not initialized. Call initConnection() first."
1119
+ )
1120
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
1121
+ }
1122
+
1123
+ guard SKPaymentQueue.canMakePayments() else {
1124
+ let errorJson = ErrorUtils.createErrorJson(
1125
+ code: IapErrorCode.iapNotAvailable,
1126
+ message: "In-app purchases are not available on this device"
1127
+ )
1128
+ throw NSError(domain: "RnIap", code: -1, userInfo: [NSLocalizedDescriptionKey: errorJson])
1129
+ }
1130
+ }
1131
+
1132
+ @MainActor
1133
+ private func currentWindowScene() async -> UIWindowScene? {
1134
+ #if os(iOS) || os(tvOS)
1135
+ // Find the active window scene
1136
+ for scene in UIApplication.shared.connectedScenes {
1137
+ if let windowScene = scene as? UIWindowScene,
1138
+ windowScene.activationState == .foregroundActive {
1139
+ return windowScene
1140
+ }
1141
+ }
1142
+
1143
+ // Fallback to first window scene if no active one found
1144
+ for scene in UIApplication.shared.connectedScenes {
1145
+ if let windowScene = scene as? UIWindowScene {
1146
+ return windowScene
1147
+ }
1148
+ }
1149
+ #endif
1150
+ return nil
1151
+ }
1152
+
1153
+ private func convertToNitroProduct(_ storeProduct: StoreKit.Product, type: String) -> NitroProduct {
1154
+ var product = NitroProduct()
1155
+
1156
+ // Basic fields
1157
+ product.id = storeProduct.id
1158
+ product.title = storeProduct.displayName
1159
+ product.description = storeProduct.description
1160
+ product.type = type
1161
+ product.displayName = storeProduct.displayName
1162
+ product.displayPrice = storeProduct.displayPrice
1163
+ product.platform = "ios"
1164
+
1165
+ // Price and currency - priceFormatStyle.currencyCode is not optional
1166
+ product.currency = storeProduct.priceFormatStyle.currencyCode
1167
+
1168
+ // Convert Decimal price to Double - price is not optional
1169
+ product.price = NSDecimalNumber(decimal: storeProduct.price).doubleValue
1170
+
1171
+ // iOS specific fields
1172
+ product.isFamilyShareable = storeProduct.isFamilyShareable
1173
+ product.jsonRepresentation = storeProduct.jsonRepresentation.base64EncodedString()
1174
+
1175
+ // Subscription information
1176
+ if let subscription = storeProduct.subscription {
1177
+ // Subscription period - value is Int, need to convert to Double
1178
+ product.subscriptionPeriodUnitIOS = getPeriodString(subscription.subscriptionPeriod.unit)
1179
+ product.subscriptionPeriodNumberIOS = Double(subscription.subscriptionPeriod.value)
1180
+
1181
+ // Introductory offer
1182
+ if let introOffer = subscription.introductoryOffer {
1183
+ product.introductoryPriceIOS = introOffer.displayPrice
1184
+ product.introductoryPriceAsAmountIOS = NSDecimalNumber(decimal: introOffer.price).doubleValue
1185
+ product.introductoryPricePaymentModeIOS = String(introOffer.paymentMode.rawValue)
1186
+ product.introductoryPriceNumberOfPeriodsIOS = Double(introOffer.periodCount)
1187
+ product.introductoryPriceSubscriptionPeriodIOS = getPeriodString(introOffer.period.unit)
1188
+ }
1189
+ }
1190
+
1191
+ return product
1192
+ }
1193
+
1194
+ private func convertProductToNitroProduct(_ product: Product, type: String) -> NitroProduct {
1195
+ return convertToNitroProduct(product, type: type)
1196
+ }
1197
+
1198
+ private func getPeriodString(_ unit: StoreKit.Product.SubscriptionPeriod.Unit) -> String {
1199
+ switch unit {
1200
+ case .day: return "DAY"
1201
+ case .week: return "WEEK"
1202
+ case .month: return "MONTH"
1203
+ case .year: return "YEAR"
1204
+ @unknown default: return ""
1205
+ }
1206
+ }
1207
+
1208
+ private func convertToNitroPurchase(_ transaction: Transaction, product: StoreKit.Product, jwsRepresentation: String? = nil) -> NitroPurchase {
1209
+ var purchase = NitroPurchase()
1210
+
1211
+ // Basic fields
1212
+ purchase.id = String(transaction.id)
1213
+ purchase.productId = transaction.productID
1214
+ purchase.transactionDate = transaction.purchaseDate.timeIntervalSince1970 * 1000 // Convert to milliseconds
1215
+ purchase.platform = "ios"
1216
+
1217
+ // iOS specific fields
1218
+ purchase.quantityIOS = Double(transaction.purchasedQuantity)
1219
+
1220
+ // originalID is not optional in StoreKit 2
1221
+ purchase.originalTransactionIdentifierIOS = String(transaction.originalID)
1222
+
1223
+ // originalPurchaseDate is not optional
1224
+ purchase.originalTransactionDateIOS = transaction.originalPurchaseDate.timeIntervalSince1970 * 1000
1225
+
1226
+ if let appAccountToken = transaction.appAccountToken {
1227
+ purchase.appAccountToken = appAccountToken.uuidString
1228
+ }
1229
+
1230
+ // Store the JWS representation as purchaseToken for verification
1231
+ // JWS is passed from VerificationResult
1232
+ if let jws = jwsRepresentation {
1233
+ purchase.purchaseToken = jws
1234
+ }
1235
+
1236
+ return purchase
1237
+ }
1238
+
1239
+ private func sendPurchaseUpdate(_ purchase: NitroPurchase) {
1240
+ for listener in purchaseUpdatedListeners {
1241
+ listener(purchase)
1242
+ }
1243
+ }
1244
+
1245
+ private func sendPurchaseError(_ error: NitroPurchaseResult) {
1246
+ for listener in purchaseErrorListeners {
1247
+ listener(error)
1248
+ }
1249
+ }
1250
+
1251
+ private func createPurchaseErrorResult(code: String, message: String, productId: String? = nil) -> NitroPurchaseResult {
1252
+ var result = NitroPurchaseResult()
1253
+ result.responseCode = 0
1254
+ result.code = code
1255
+ result.message = message
1256
+ result.purchaseToken = productId
1257
+ return result
1258
+ }
1259
+
1260
+ private func cleanupExistingState() {
1261
+ // Cancel transaction listener if any
1262
+ updateListenerTask?.cancel()
1263
+ updateListenerTask = nil
1264
+ isInitialized = false
1265
+ productStore = nil
1266
+ transactions.removeAll()
1267
+
1268
+ // Remove payment observer if exists
1269
+ if let observer = paymentObserver {
1270
+ SKPaymentQueue.default().remove(observer)
1271
+ paymentObserver = nil
1272
+ }
1273
+
1274
+ // Clear promoted products
1275
+ promotedProduct = nil
1276
+ promotedPayment = nil
1277
+ promotedSKProduct = nil
1278
+
1279
+ // Clear event listeners
1280
+ purchaseUpdatedListeners.removeAll()
1281
+ purchaseErrorListeners.removeAll()
1282
+ promotedProductListeners.removeAll()
1283
+ }
1284
+
1285
+ // Method called by PaymentObserver when a promoted product is received
1286
+ func handlePromotedProduct(payment: SKPayment, product: SKProduct) {
1287
+ self.promotedPayment = payment
1288
+ self.promotedSKProduct = product
1289
+
1290
+ // Convert SKProduct to StoreKit 2 Product and then to NitroProduct
1291
+ Task {
1292
+ if let products = try? await StoreKit.Product.products(for: [product.productIdentifier]),
1293
+ let storeKit2Product = products.first {
1294
+ self.promotedProduct = storeKit2Product
1295
+
1296
+ // Notify all promoted product listeners
1297
+ let nitroProduct = self.convertProductToNitroProduct(storeKit2Product, type: "inapp")
1298
+ for listener in self.promotedProductListeners {
1299
+ listener(nitroProduct)
1300
+ }
1301
+ }
1302
+ }
1303
+ }
1304
+ }
1305
+
1306
+ // PaymentObserver for handling promoted products
1307
+ @available(iOS 15.0, *)
1308
+ class PaymentObserver: NSObject, SKPaymentTransactionObserver {
1309
+ weak var iapModule: HybridRnIap?
1310
+
1311
+ init(iapModule: HybridRnIap) {
1312
+ self.iapModule = iapModule
1313
+ }
1314
+
1315
+ // Required by SKPaymentTransactionObserver protocol but not used
1316
+ func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
1317
+ // We don't handle transactions here as StoreKit 2 handles them in HybridRnIap
1318
+ }
1319
+
1320
+ // Handle promoted products from App Store
1321
+ func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
1322
+ iapModule?.handlePromotedProduct(payment: payment, product: product)
1323
+ // Return false to defer the payment
1324
+ return false
1325
+ }
1326
+ }