react-native-iap 16.4.0 → 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.
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +200 -142
- package/android/src/main/java/com/margelo/nitro/iap/TokenizedListenerRegistry.kt +0 -1
- package/ios/HybridRnIap.swift +832 -165
- package/ios/RnIapHelper.swift +1 -1
- package/lib/module/hooks/useIAP.js +48 -32
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +206 -152
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.kepler.js +1 -0
- package/lib/module/index.kepler.js.map +1 -1
- package/lib/module/types.js +1 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/error.js +5 -4
- package/lib/module/utils/error.js.map +1 -1
- package/lib/module/vega-adapter.js +4 -1
- package/lib/module/vega-adapter.js.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts +31 -15
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +28 -14
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.kepler.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +10 -0
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +11 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/error.d.ts.map +1 -1
- package/lib/typescript/src/vega-adapter.d.ts.map +1 -1
- package/nitrogen/generated/android/NitroIap+autolinking.cmake +1 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JNitroPurchaseRequest.hpp +7 -1
- package/nitrogen/generated/android/c++/JNitroPurchaseRequestType.hpp +58 -0
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitHorizonProps.hpp +66 -0
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitProps.hpp +8 -1
- package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithProviderProps.hpp +3 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.hpp +74 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitProps.hpp +3 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseRequest.kt +8 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroPurchaseRequestType.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitHorizonProps.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitProps.kt +7 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.kt +62 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +65 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +6 -0
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +6 -0
- package/nitrogen/generated/ios/swift/NitroPurchaseRequest.swift +13 -2
- package/nitrogen/generated/ios/swift/NitroPurchaseRequestType.swift +40 -0
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitHorizonProps.swift +66 -0
- package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitProps.swift +38 -1
- package/nitrogen/generated/ios/swift/Variant_NullType_NitroVerifyPurchaseWithIapkitHorizonProps.swift +30 -0
- package/nitrogen/generated/shared/c++/NitroPurchaseRequest.hpp +9 -2
- package/nitrogen/generated/shared/c++/NitroPurchaseRequestType.hpp +76 -0
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitHorizonProps.hpp +90 -0
- package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitProps.hpp +8 -1
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/src/hooks/useIAP.ts +96 -58
- package/src/index.kepler.ts +1 -0
- package/src/index.ts +248 -195
- package/src/specs/RnIap.nitro.ts +12 -0
- package/src/types.ts +12 -2
- package/src/utils/error.ts +7 -3
- 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
|
}
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
package/src/hooks/useIAP.ts
CHANGED
|
@@ -83,10 +83,12 @@ type UseIap = {
|
|
|
83
83
|
*
|
|
84
84
|
* @example
|
|
85
85
|
* ```ts
|
|
86
|
-
* purchaseUpdatedListener(
|
|
87
|
-
*
|
|
88
|
-
*
|
|
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
|
-
*
|
|
116
|
-
*
|
|
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
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* if (
|
|
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
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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 = (
|
|
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
|
-
|
|
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
|
-
|
|
449
|
+
mergeByKey(
|
|
437
450
|
prevProducts,
|
|
438
451
|
newProducts,
|
|
439
452
|
(product: Product) => product.id,
|
|
440
453
|
),
|
|
441
454
|
);
|
|
442
455
|
setSubscriptions((prevSubscriptions: ProductSubscription[]) =>
|
|
443
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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 (
|
|
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) {
|
package/src/index.kepler.ts
CHANGED