react-native-iap 16.4.1 → 16.5.0

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 (63) hide show
  1. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +200 -142
  2. package/android/src/main/java/com/margelo/nitro/iap/TokenizedListenerRegistry.kt +0 -1
  3. package/ios/HybridRnIap.swift +832 -165
  4. package/ios/RnIapHelper.swift +1 -1
  5. package/lib/module/hooks/useIAP.js +48 -32
  6. package/lib/module/hooks/useIAP.js.map +1 -1
  7. package/lib/module/index.js +206 -152
  8. package/lib/module/index.js.map +1 -1
  9. package/lib/module/index.kepler.js +1 -0
  10. package/lib/module/index.kepler.js.map +1 -1
  11. package/lib/module/types.js +1 -0
  12. package/lib/module/types.js.map +1 -1
  13. package/lib/module/utils/error.js +5 -4
  14. package/lib/module/utils/error.js.map +1 -1
  15. package/lib/module/vega-adapter.js +4 -1
  16. package/lib/module/vega-adapter.js.map +1 -1
  17. package/lib/typescript/src/hooks/useIAP.d.ts +31 -15
  18. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  19. package/lib/typescript/src/index.d.ts +28 -14
  20. package/lib/typescript/src/index.d.ts.map +1 -1
  21. package/lib/typescript/src/index.kepler.d.ts.map +1 -1
  22. package/lib/typescript/src/specs/RnIap.nitro.d.ts +10 -0
  23. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  24. package/lib/typescript/src/types.d.ts +11 -2
  25. package/lib/typescript/src/types.d.ts.map +1 -1
  26. package/lib/typescript/src/utils/error.d.ts.map +1 -1
  27. package/lib/typescript/src/vega-adapter.d.ts.map +1 -1
  28. package/nitrogen/generated/android/NitroIap+autolinking.cmake +1 -0
  29. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +9 -0
  30. package/nitrogen/generated/android/c++/JNitroPurchaseRequest.hpp +7 -1
  31. package/nitrogen/generated/android/c++/JNitroPurchaseRequestType.hpp +58 -0
  32. package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitHorizonProps.hpp +66 -0
  33. package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitProps.hpp +8 -1
  34. package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithProviderProps.hpp +3 -0
  35. package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.cpp +26 -0
  36. package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.hpp +74 -0
  37. package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitProps.hpp +3 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseRequest.kt +8 -3
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseRequestType.kt +23 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitHorizonProps.kt +56 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitProps.kt +7 -2
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.kt +62 -0
  43. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +65 -0
  44. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +6 -0
  45. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +6 -0
  46. package/nitrogen/generated/ios/swift/NitroPurchaseRequest.swift +13 -2
  47. package/nitrogen/generated/ios/swift/NitroPurchaseRequestType.swift +40 -0
  48. package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitHorizonProps.swift +66 -0
  49. package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitProps.swift +38 -1
  50. package/nitrogen/generated/ios/swift/Variant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.swift +30 -0
  51. package/nitrogen/generated/shared/c++/NitroPurchaseRequest.hpp +9 -2
  52. package/nitrogen/generated/shared/c++/NitroPurchaseRequestType.hpp +76 -0
  53. package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitHorizonProps.hpp +90 -0
  54. package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitProps.hpp +8 -1
  55. package/openiap-versions.json +3 -3
  56. package/package.json +1 -1
  57. package/src/hooks/useIAP.ts +96 -58
  58. package/src/index.kepler.ts +1 -0
  59. package/src/index.ts +248 -195
  60. package/src/specs/RnIap.nitro.ts +12 -0
  61. package/src/types.ts +12 -2
  62. package/src/utils/error.ts +7 -3
  63. package/src/vega-adapter.ts +9 -5
@@ -28,15 +28,18 @@
28
28
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
29
  #endif
30
30
 
31
+ // Forward declaration of `NitroPurchaseRequestType` to properly resolve imports.
32
+ namespace margelo::nitro::iap { enum class NitroPurchaseRequestType; }
31
33
  // Forward declaration of `NitroRequestPurchaseIos` to properly resolve imports.
32
34
  namespace margelo::nitro::iap { struct NitroRequestPurchaseIos; }
33
35
  // Forward declaration of `NitroRequestPurchaseAndroid` to properly resolve imports.
34
36
  namespace margelo::nitro::iap { struct NitroRequestPurchaseAndroid; }
35
37
 
38
+ #include "NitroPurchaseRequestType.hpp"
39
+ #include <optional>
36
40
  #include <NitroModules/Null.hpp>
37
41
  #include "NitroRequestPurchaseIos.hpp"
38
42
  #include <variant>
39
- #include <optional>
40
43
  #include "NitroRequestPurchaseAndroid.hpp"
41
44
 
42
45
  namespace margelo::nitro::iap {
@@ -46,12 +49,13 @@ namespace margelo::nitro::iap {
46
49
  */
47
50
  struct NitroPurchaseRequest final {
48
51
  public:
52
+ std::optional<NitroPurchaseRequestType> type SWIFT_PRIVATE;
49
53
  std::optional<std::variant<nitro::NullType, NitroRequestPurchaseIos>> apple SWIFT_PRIVATE;
50
54
  std::optional<std::variant<nitro::NullType, NitroRequestPurchaseAndroid>> google SWIFT_PRIVATE;
51
55
 
52
56
  public:
53
57
  NitroPurchaseRequest() = default;
54
- explicit NitroPurchaseRequest(std::optional<std::variant<nitro::NullType, NitroRequestPurchaseIos>> apple, std::optional<std::variant<nitro::NullType, NitroRequestPurchaseAndroid>> google): apple(apple), google(google) {}
58
+ explicit NitroPurchaseRequest(std::optional<NitroPurchaseRequestType> type, std::optional<std::variant<nitro::NullType, NitroRequestPurchaseIos>> apple, std::optional<std::variant<nitro::NullType, NitroRequestPurchaseAndroid>> google): type(type), apple(apple), google(google) {}
55
59
 
56
60
  public:
57
61
  // NitroPurchaseRequest is not equatable because these properties are not equatable: google
@@ -67,12 +71,14 @@ namespace margelo::nitro {
67
71
  static inline margelo::nitro::iap::NitroPurchaseRequest fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
68
72
  jsi::Object obj = arg.asObject(runtime);
69
73
  return margelo::nitro::iap::NitroPurchaseRequest(
74
+ JSIConverter<std::optional<margelo::nitro::iap::NitroPurchaseRequestType>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "type"))),
70
75
  JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroRequestPurchaseIos>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "apple"))),
71
76
  JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroRequestPurchaseAndroid>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "google")))
72
77
  );
73
78
  }
74
79
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroPurchaseRequest& arg) {
75
80
  jsi::Object obj(runtime);
81
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "type"), JSIConverter<std::optional<margelo::nitro::iap::NitroPurchaseRequestType>>::toJSI(runtime, arg.type));
76
82
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "apple"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroRequestPurchaseIos>>>::toJSI(runtime, arg.apple));
77
83
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "google"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroRequestPurchaseAndroid>>>::toJSI(runtime, arg.google));
78
84
  return obj;
@@ -85,6 +91,7 @@ namespace margelo::nitro {
85
91
  if (!nitro::isPlainObject(runtime, obj)) {
86
92
  return false;
87
93
  }
94
+ if (!JSIConverter<std::optional<margelo::nitro::iap::NitroPurchaseRequestType>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "type")))) return false;
88
95
  if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroRequestPurchaseIos>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "apple")))) return false;
89
96
  if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroRequestPurchaseAndroid>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "google")))) return false;
90
97
  return true;
@@ -0,0 +1,76 @@
1
+ ///
2
+ /// NitroPurchaseRequestType.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/NitroHash.hpp>)
11
+ #include <NitroModules/NitroHash.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
16
+ #include <NitroModules/JSIConverter.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
21
+ #include <NitroModules/NitroDefines.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+
26
+ namespace margelo::nitro::iap {
27
+
28
+ /**
29
+ * An enum which can be represented as a JavaScript union (NitroPurchaseRequestType).
30
+ */
31
+ enum class NitroPurchaseRequestType {
32
+ IN_APP SWIFT_NAME(inApp) = 0,
33
+ SUBS SWIFT_NAME(subs) = 1,
34
+ } CLOSED_ENUM;
35
+
36
+ } // namespace margelo::nitro::iap
37
+
38
+ namespace margelo::nitro {
39
+
40
+ // C++ NitroPurchaseRequestType <> JS NitroPurchaseRequestType (union)
41
+ template <>
42
+ struct JSIConverter<margelo::nitro::iap::NitroPurchaseRequestType> final {
43
+ static inline margelo::nitro::iap::NitroPurchaseRequestType fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
44
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
45
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
46
+ case hashString("in-app"): return margelo::nitro::iap::NitroPurchaseRequestType::IN_APP;
47
+ case hashString("subs"): return margelo::nitro::iap::NitroPurchaseRequestType::SUBS;
48
+ default: [[unlikely]]
49
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum NitroPurchaseRequestType - invalid value!");
50
+ }
51
+ }
52
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::iap::NitroPurchaseRequestType arg) {
53
+ switch (arg) {
54
+ case margelo::nitro::iap::NitroPurchaseRequestType::IN_APP: return JSIConverter<std::string>::toJSI(runtime, "in-app");
55
+ case margelo::nitro::iap::NitroPurchaseRequestType::SUBS: return JSIConverter<std::string>::toJSI(runtime, "subs");
56
+ default: [[unlikely]]
57
+ throw std::invalid_argument("Cannot convert NitroPurchaseRequestType to JS - invalid value: "
58
+ + std::to_string(static_cast<int>(arg)) + "!");
59
+ }
60
+ }
61
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
62
+ if (!value.isString()) {
63
+ return false;
64
+ }
65
+ std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
66
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
67
+ case hashString("in-app"):
68
+ case hashString("subs"):
69
+ return true;
70
+ default:
71
+ return false;
72
+ }
73
+ }
74
+ };
75
+
76
+ } // namespace margelo::nitro
@@ -0,0 +1,90 @@
1
+ ///
2
+ /// NitroVerifyPurchaseWithIapkitHorizonProps.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
11
+ #include <NitroModules/JSIConverter.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
16
+ #include <NitroModules/NitroDefines.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/JSIHelpers.hpp>)
21
+ #include <NitroModules/JSIHelpers.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+ #if __has_include(<NitroModules/PropNameIDCache.hpp>)
26
+ #include <NitroModules/PropNameIDCache.hpp>
27
+ #else
28
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
+ #endif
30
+
31
+
32
+
33
+ #include <string>
34
+ #include <NitroModules/Null.hpp>
35
+ #include <variant>
36
+ #include <optional>
37
+
38
+ namespace margelo::nitro::iap {
39
+
40
+ /**
41
+ * A struct which can be represented as a JavaScript object (NitroVerifyPurchaseWithIapkitHorizonProps).
42
+ */
43
+ struct NitroVerifyPurchaseWithIapkitHorizonProps final {
44
+ public:
45
+ std::string sku SWIFT_PRIVATE;
46
+ std::optional<std::variant<nitro::NullType, std::string>> userId SWIFT_PRIVATE;
47
+
48
+ public:
49
+ NitroVerifyPurchaseWithIapkitHorizonProps() = default;
50
+ explicit NitroVerifyPurchaseWithIapkitHorizonProps(std::string sku, std::optional<std::variant<nitro::NullType, std::string>> userId): sku(sku), userId(userId) {}
51
+
52
+ public:
53
+ friend bool operator==(const NitroVerifyPurchaseWithIapkitHorizonProps& lhs, const NitroVerifyPurchaseWithIapkitHorizonProps& rhs) = default;
54
+ };
55
+
56
+ } // namespace margelo::nitro::iap
57
+
58
+ namespace margelo::nitro {
59
+
60
+ // C++ NitroVerifyPurchaseWithIapkitHorizonProps <> JS NitroVerifyPurchaseWithIapkitHorizonProps (object)
61
+ template <>
62
+ struct JSIConverter<margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps> final {
63
+ static inline margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
64
+ jsi::Object obj = arg.asObject(runtime);
65
+ return margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps(
66
+ JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "sku"))),
67
+ JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userId")))
68
+ );
69
+ }
70
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps& arg) {
71
+ jsi::Object obj(runtime);
72
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "sku"), JSIConverter<std::string>::toJSI(runtime, arg.sku));
73
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "userId"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.userId));
74
+ return obj;
75
+ }
76
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
77
+ if (!value.isObject()) {
78
+ return false;
79
+ }
80
+ jsi::Object obj = value.getObject(runtime);
81
+ if (!nitro::isPlainObject(runtime, obj)) {
82
+ return false;
83
+ }
84
+ if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "sku")))) return false;
85
+ if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "userId")))) return false;
86
+ return true;
87
+ }
88
+ };
89
+
90
+ } // namespace margelo::nitro
@@ -34,6 +34,8 @@ namespace margelo::nitro::iap { struct NitroVerifyPurchaseWithIapkitAmazonProps;
34
34
  namespace margelo::nitro::iap { struct NitroVerifyPurchaseWithIapkitAppleProps; }
35
35
  // Forward declaration of `NitroVerifyPurchaseWithIapkitGoogleProps` to properly resolve imports.
36
36
  namespace margelo::nitro::iap { struct NitroVerifyPurchaseWithIapkitGoogleProps; }
37
+ // Forward declaration of `NitroVerifyPurchaseWithIapkitHorizonProps` to properly resolve imports.
38
+ namespace margelo::nitro::iap { struct NitroVerifyPurchaseWithIapkitHorizonProps; }
37
39
 
38
40
  #include <NitroModules/Null.hpp>
39
41
  #include <string>
@@ -42,6 +44,7 @@ namespace margelo::nitro::iap { struct NitroVerifyPurchaseWithIapkitGoogleProps;
42
44
  #include "NitroVerifyPurchaseWithIapkitAmazonProps.hpp"
43
45
  #include "NitroVerifyPurchaseWithIapkitAppleProps.hpp"
44
46
  #include "NitroVerifyPurchaseWithIapkitGoogleProps.hpp"
47
+ #include "NitroVerifyPurchaseWithIapkitHorizonProps.hpp"
45
48
 
46
49
  namespace margelo::nitro::iap {
47
50
 
@@ -55,11 +58,12 @@ namespace margelo::nitro::iap {
55
58
  std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitAppleProps>> apple SWIFT_PRIVATE;
56
59
  std::optional<std::variant<nitro::NullType, std::string>> baseUrl SWIFT_PRIVATE;
57
60
  std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitGoogleProps>> google SWIFT_PRIVATE;
61
+ std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitHorizonProps>> horizon SWIFT_PRIVATE;
58
62
  std::optional<std::variant<nitro::NullType, bool>> includeClientPayload SWIFT_PRIVATE;
59
63
 
60
64
  public:
61
65
  NitroVerifyPurchaseWithIapkitProps() = default;
62
- explicit NitroVerifyPurchaseWithIapkitProps(std::optional<std::variant<nitro::NullType, std::string>> apiKey, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitAmazonProps>> amazon, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitAppleProps>> apple, std::optional<std::variant<nitro::NullType, std::string>> baseUrl, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitGoogleProps>> google, std::optional<std::variant<nitro::NullType, bool>> includeClientPayload): apiKey(apiKey), amazon(amazon), apple(apple), baseUrl(baseUrl), google(google), includeClientPayload(includeClientPayload) {}
66
+ explicit NitroVerifyPurchaseWithIapkitProps(std::optional<std::variant<nitro::NullType, std::string>> apiKey, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitAmazonProps>> amazon, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitAppleProps>> apple, std::optional<std::variant<nitro::NullType, std::string>> baseUrl, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitGoogleProps>> google, std::optional<std::variant<nitro::NullType, NitroVerifyPurchaseWithIapkitHorizonProps>> horizon, std::optional<std::variant<nitro::NullType, bool>> includeClientPayload): apiKey(apiKey), amazon(amazon), apple(apple), baseUrl(baseUrl), google(google), horizon(horizon), includeClientPayload(includeClientPayload) {}
63
67
 
64
68
  public:
65
69
  friend bool operator==(const NitroVerifyPurchaseWithIapkitProps& lhs, const NitroVerifyPurchaseWithIapkitProps& rhs) = default;
@@ -80,6 +84,7 @@ namespace margelo::nitro {
80
84
  JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitAppleProps>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "apple"))),
81
85
  JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "baseUrl"))),
82
86
  JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitGoogleProps>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "google"))),
87
+ JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "horizon"))),
83
88
  JSIConverter<std::optional<std::variant<nitro::NullType, bool>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "includeClientPayload")))
84
89
  );
85
90
  }
@@ -90,6 +95,7 @@ namespace margelo::nitro {
90
95
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "apple"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitAppleProps>>>::toJSI(runtime, arg.apple));
91
96
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "baseUrl"), JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::toJSI(runtime, arg.baseUrl));
92
97
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "google"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitGoogleProps>>>::toJSI(runtime, arg.google));
98
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "horizon"), JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps>>>::toJSI(runtime, arg.horizon));
93
99
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "includeClientPayload"), JSIConverter<std::optional<std::variant<nitro::NullType, bool>>>::toJSI(runtime, arg.includeClientPayload));
94
100
  return obj;
95
101
  }
@@ -106,6 +112,7 @@ namespace margelo::nitro {
106
112
  if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitAppleProps>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "apple")))) return false;
107
113
  if (!JSIConverter<std::optional<std::variant<nitro::NullType, std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "baseUrl")))) return false;
108
114
  if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitGoogleProps>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "google")))) return false;
115
+ if (!JSIConverter<std::optional<std::variant<nitro::NullType, margelo::nitro::iap::NitroVerifyPurchaseWithIapkitHorizonProps>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "horizon")))) return false;
109
116
  if (!JSIConverter<std::optional<std::variant<nitro::NullType, bool>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "includeClientPayload")))) return false;
110
117
  return true;
111
118
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "spec": "3.3.1",
3
- "google": "3.4.0",
4
- "apple": "3.3.1"
2
+ "spec": "3.4.0",
3
+ "google": "3.5.0",
4
+ "apple": "3.4.0"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "16.4.1",
3
+ "version": "16.5.0",
4
4
  "description": "React Native In-App Purchases module for iOS and Android using Nitro",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -83,10 +83,12 @@ type UseIap = {
83
83
  *
84
84
  * @example
85
85
  * ```ts
86
- * purchaseUpdatedListener(async (purchase) => {
87
- * if (await verifyOnServer(purchase)) {
88
- * await finishTransaction({ purchase, isConsumable: false });
89
- * }
86
+ * purchaseUpdatedListener((purchase) => {
87
+ * void verifyOnServer(purchase)
88
+ * .then((verified) => verified
89
+ * ? finishTransaction({ purchase, isConsumable: false })
90
+ * : undefined)
91
+ * .catch((error) => console.warn('Transaction finalization failed:', error));
90
92
  * });
91
93
  * ```
92
94
  *
@@ -109,18 +111,25 @@ type UseIap = {
109
111
  *
110
112
  * @example
111
113
  * ```ts
112
- * const { availablePurchases, getAvailablePurchases, finishTransaction } = useIAP();
114
+ * const { connected, availablePurchases, getAvailablePurchases, finishTransaction } = useIAP();
113
115
  *
114
116
  * useEffect(() => {
115
- * void getAvailablePurchases();
116
- * }, [getAvailablePurchases]);
117
+ * if (!connected) return;
118
+ * void getAvailablePurchases().catch((error) => {
119
+ * console.warn('Purchase restore failed', error);
120
+ * });
121
+ * }, [connected, getAvailablePurchases]);
117
122
  *
118
123
  * useEffect(() => {
119
- * for (const p of availablePurchases) {
120
- * void verifyOnServer(p).then((ok) => {
121
- * if (ok) finishTransaction({ purchase: p, isConsumable: false });
122
- * });
123
- * }
124
+ * void (async () => {
125
+ * for (const p of availablePurchases) {
126
+ * if (await verifyOnServer(p)) {
127
+ * await finishTransaction({ purchase: p, isConsumable: false });
128
+ * }
129
+ * }
130
+ * })().catch((error) => {
131
+ * console.warn('Restored purchase processing failed', error);
132
+ * });
124
133
  * }, [availablePurchases, finishTransaction]);
125
134
  * ```
126
135
  *
@@ -139,14 +148,17 @@ type UseIap = {
139
148
  *
140
149
  * @example
141
150
  * ```ts
142
- * const { products, fetchProducts } = useIAP();
151
+ * const { connected, products, fetchProducts } = useIAP();
143
152
  *
144
153
  * useEffect(() => {
154
+ * if (!connected) return;
145
155
  * void fetchProducts({
146
156
  * skus: ['com.app.coins_100', 'com.app.premium'],
147
157
  * type: 'in-app',
158
+ * }).catch((error) => {
159
+ * console.warn('Product fetch failed', error);
148
160
  * });
149
- * }, [fetchProducts]);
161
+ * }, [connected, fetchProducts]);
150
162
  *
151
163
  * // Render `products` directly from hook state.
152
164
  * ```
@@ -169,10 +181,14 @@ type UseIap = {
169
181
  * - `type: 'subs'` — same shape, plus `request.google.subscriptionOffers: [{ sku, offerToken }]`.
170
182
  * @returns Promise that resolves when the request is dispatched; results land in the
171
183
  * hook's `onPurchaseSuccess` / `onPurchaseError` callbacks.
172
- * @throws Synchronous rejection from the store (e.g. `E_NOT_PREPARED`, validation failure).
184
+ * @throws Invalid arguments or failures that prevent dispatch. Store outcomes are event-based;
185
+ * on Android, `not-prepared` is delivered through `onPurchaseError`.
173
186
  *
174
187
  * @example
175
188
  * ```ts
189
+ * const { connected, requestPurchase } = useIAP();
190
+ * if (!connected) return;
191
+ *
176
192
  * await requestPurchase({
177
193
  * request: {
178
194
  * apple: { sku: 'com.app.premium' },
@@ -309,6 +325,18 @@ export interface UseIapOptions {
309
325
  billingChoiceScreenTypeAndroid?: BillingChoiceScreenTypeAndroid;
310
326
  }
311
327
 
328
+ function mergeByKey<T>(
329
+ existingItems: T[],
330
+ newItems: T[],
331
+ getKey: (item: T) => string,
332
+ ): T[] {
333
+ const merged = new Map(
334
+ existingItems.map((item) => [getKey(item), item] as const),
335
+ );
336
+ newItems.forEach((item) => merged.set(getKey(item), item));
337
+ return Array.from(merged.values());
338
+ }
339
+
312
340
  /**
313
341
  * React Hook for managing In-App Purchases.
314
342
  * See documentation at https://react-native-iap.hyo.dev/docs/hooks/useIAP
@@ -326,27 +354,6 @@ export function useIAP(options?: UseIapOptions): UseIap {
326
354
  const optionsRef = useRef<UseIapOptions | undefined>(options);
327
355
  const connectedRef = useRef<boolean>(false);
328
356
 
329
- // Helper function to merge arrays with duplicate checking
330
- const mergeWithDuplicateCheck = useCallback(
331
- <T>(
332
- existingItems: T[],
333
- newItems: T[],
334
- getKey: (item: T) => string,
335
- ): T[] => {
336
- const merged = [...existingItems];
337
- newItems.forEach((newItem) => {
338
- const isDuplicate = merged.some(
339
- (existingItem) => getKey(existingItem) === getKey(newItem),
340
- );
341
- if (!isDuplicate) {
342
- merged.push(newItem);
343
- }
344
- });
345
- return merged;
346
- },
347
- [],
348
- );
349
-
350
357
  useEffect(() => {
351
358
  optionsRef.current = options;
352
359
  }, [options]);
@@ -367,6 +374,8 @@ export function useIAP(options?: UseIapOptions): UseIap {
367
374
  // Track if component is mounted to prevent listener leaks on early unmount
368
375
  const isMountedRef = useRef<boolean>(false);
369
376
  const initializationGenerationRef = useRef(0);
377
+ const productRequestGenerationRef = useRef(0);
378
+ const productRequestOwnersRef = useRef(new Map<string, number>());
370
379
 
371
380
  const subscriptionsRefState = useRef<ProductSubscription[]>([]);
372
381
 
@@ -388,12 +397,11 @@ export function useIAP(options?: UseIapOptions): UseIap {
388
397
  skus: string[];
389
398
  type?: ProductQueryType | null;
390
399
  }): Promise<void> => {
391
- if (!connectedRef.current) {
392
- RnIapConsole.warn(
393
- '[useIAP] fetchProducts called before connection; skipping',
394
- );
395
- return;
400
+ const generation = ++productRequestGenerationRef.current;
401
+ for (const sku of params.skus) {
402
+ productRequestOwnersRef.current.set(sku, generation);
396
403
  }
404
+
397
405
  try {
398
406
  const requestType = params.type ?? 'in-app';
399
407
  RnIapConsole.debug('[useIAP] Calling fetchProducts with:', {
@@ -408,13 +416,18 @@ export function useIAP(options?: UseIapOptions): UseIap {
408
416
  '[useIAP] fetchProducts count:',
409
417
  result?.length ?? 0,
410
418
  );
411
- const items = (result ?? []) as (Product | ProductSubscription)[];
419
+ const items = (
420
+ (result ?? []) as (Product | ProductSubscription)[]
421
+ ).filter(
422
+ (item) => productRequestOwnersRef.current.get(item.id) === generation,
423
+ );
424
+ if (items.length === 0) return;
412
425
 
413
426
  // fetchProducts already returns properly filtered results based on type
414
427
  if (requestType === 'subs') {
415
428
  // All items are already subscriptions
416
429
  setSubscriptions((prevSubscriptions: ProductSubscription[]) =>
417
- mergeWithDuplicateCheck(
430
+ mergeByKey(
418
431
  prevSubscriptions,
419
432
  items as ProductSubscription[],
420
433
  (subscription: ProductSubscription) => subscription.id,
@@ -433,14 +446,14 @@ export function useIAP(options?: UseIapOptions): UseIap {
433
446
  );
434
447
 
435
448
  setProducts((prevProducts: Product[]) =>
436
- mergeWithDuplicateCheck(
449
+ mergeByKey(
437
450
  prevProducts,
438
451
  newProducts,
439
452
  (product: Product) => product.id,
440
453
  ),
441
454
  );
442
455
  setSubscriptions((prevSubscriptions: ProductSubscription[]) =>
443
- mergeWithDuplicateCheck(
456
+ mergeByKey(
444
457
  prevSubscriptions,
445
458
  newSubscriptions,
446
459
  (subscription: ProductSubscription) => subscription.id,
@@ -451,7 +464,7 @@ export function useIAP(options?: UseIapOptions): UseIap {
451
464
 
452
465
  // For 'in-app' type, all items are already products
453
466
  setProducts((prevProducts: Product[]) =>
454
- mergeWithDuplicateCheck(
467
+ mergeByKey(
455
468
  prevProducts,
456
469
  items as Product[],
457
470
  (product: Product) => product.id,
@@ -460,9 +473,16 @@ export function useIAP(options?: UseIapOptions): UseIap {
460
473
  } catch (error) {
461
474
  RnIapConsole.error('Error fetching products:', error);
462
475
  invokeOnError(error);
476
+ throw error;
477
+ } finally {
478
+ for (const sku of params.skus) {
479
+ if (productRequestOwnersRef.current.get(sku) === generation) {
480
+ productRequestOwnersRef.current.delete(sku);
481
+ }
482
+ }
463
483
  }
464
484
  },
465
- [mergeWithDuplicateCheck, invokeOnError],
485
+ [invokeOnError],
466
486
  );
467
487
 
468
488
  const getAvailablePurchasesInternal = useCallback(
@@ -609,19 +629,27 @@ export function useIAP(options?: UseIapOptions): UseIap {
609
629
  if (!subscriptionsRef.current.purchaseUpdate) {
610
630
  subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(
611
631
  async (purchase: Purchase) => {
632
+ // Deliver first so store refresh latency cannot delay verification
633
+ // and transaction finalization in the host app.
634
+ try {
635
+ const callbackResult =
636
+ optionsRef.current?.onPurchaseSuccess?.(purchase);
637
+ void Promise.resolve(callbackResult).catch((e) => {
638
+ RnIapConsole.error('[useIAP] onPurchaseSuccess failed:', e);
639
+ });
640
+ } catch (e) {
641
+ RnIapConsole.error('[useIAP] onPurchaseSuccess failed:', e);
642
+ }
643
+
612
644
  try {
613
645
  await getActiveSubscriptionsInternal();
614
- await getAvailablePurchasesInternal();
615
646
  } catch (e) {
616
647
  RnIapConsole.warn('[useIAP] post-purchase refresh failed:', e);
617
648
  }
618
-
619
- // Deliver even if the hook unmounted while the refresh was pending:
620
- // onPurchaseSuccess is where apps call finishTransaction, and a
621
- // dropped event has no in-session redelivery. Internal setState is
622
- // mount-guarded inside the refresh helpers.
623
- if (optionsRef.current?.onPurchaseSuccess) {
624
- optionsRef.current.onPurchaseSuccess(purchase);
649
+ try {
650
+ await getAvailablePurchasesInternal();
651
+ } catch (e) {
652
+ RnIapConsole.warn('[useIAP] post-purchase refresh failed:', e);
625
653
  }
626
654
  },
627
655
  optionsRef.current?.purchaseUpdatedListenerOptions,
@@ -657,7 +685,6 @@ export function useIAP(options?: UseIapOptions): UseIap {
657
685
 
658
686
  if (
659
687
  Platform.OS === 'android' &&
660
- optionsRef.current?.onUserChoiceBillingAndroid &&
661
688
  !subscriptionsRef.current.userChoiceBillingAndroid
662
689
  ) {
663
690
  subscriptionsRef.current.userChoiceBillingAndroid =
@@ -670,7 +697,6 @@ export function useIAP(options?: UseIapOptions): UseIap {
670
697
 
671
698
  if (
672
699
  Platform.OS === 'android' &&
673
- optionsRef.current?.onDeveloperProvidedBillingAndroid &&
674
700
  !subscriptionsRef.current.developerProvidedBillingAndroid
675
701
  ) {
676
702
  subscriptionsRef.current.developerProvidedBillingAndroid =
@@ -757,16 +783,22 @@ export function useIAP(options?: UseIapOptions): UseIap {
757
783
  );
758
784
 
759
785
  const reconnect = useCallback(async (): Promise<boolean> => {
786
+ const generation = ++initializationGenerationRef.current;
760
787
  const config = buildAndroidConfig();
761
788
 
762
789
  try {
763
790
  const result = await initConnection(config);
764
791
 
765
- if (!isMountedRef.current) {
792
+ if (
793
+ !isMountedRef.current ||
794
+ initializationGenerationRef.current !== generation
795
+ ) {
766
796
  return false;
767
797
  }
768
798
 
769
799
  if (result) {
800
+ // endConnection invalidates these hook-owned subscription handles.
801
+ cleanupListeners();
770
802
  registerListeners();
771
803
  setConnected(true);
772
804
  return true;
@@ -775,6 +807,12 @@ export function useIAP(options?: UseIapOptions): UseIap {
775
807
  setConnected(false);
776
808
  return false;
777
809
  } catch (error) {
810
+ if (
811
+ !isMountedRef.current ||
812
+ initializationGenerationRef.current !== generation
813
+ ) {
814
+ return false;
815
+ }
778
816
  RnIapConsole.error('[useIAP] reconnect failed:', error);
779
817
  cleanupListeners();
780
818
  if (isMountedRef.current) {
@@ -157,6 +157,7 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
157
157
  }
158
158
 
159
159
  const nitroRequest: NitroPurchaseRequest = {
160
+ type,
160
161
  google: androidRequest,
161
162
  };
162
163
  const result = await getModule().requestPurchase(nitroRequest);