react-native-iap 14.6.1 → 14.6.3-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/HybridRnIap.swift +89 -46
- package/ios/RnIapHelper.swift +36 -3
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/reactnativeiap.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/lib/module/index.js +32 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/index.d.ts +16 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +7 -0
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +23 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JNitroRequestPurchaseIos.hpp +7 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroRequestPurchaseIos.kt +6 -3
- package/nitrogen/generated/ios/swift/NitroRequestPurchaseIos.swift +31 -1
- package/nitrogen/generated/shared/c++/NitroRequestPurchaseIos.hpp +6 -2
- package/openiap-versions.json +4 -3
- package/package.json +1 -1
- package/plugin/build/src/withIAP.d.ts +3 -0
- package/plugin/build/src/withIAP.js +81 -0
- package/plugin/build/tsconfig.tsbuildinfo +1 -0
- package/plugin/tsconfig.tsbuildinfo +1 -1
- package/src/index.ts +67 -39
- package/src/specs/RnIap.nitro.ts +7 -0
- package/src/types.ts +23 -2
|
@@ -43,6 +43,8 @@ namespace margelo::nitro::iap {
|
|
|
43
43
|
jni::local_ref<jni::JDouble> quantity = this->getFieldValue(fieldQuantity);
|
|
44
44
|
static const auto fieldWithOffer = clazz->getField<jni::JMap<jni::JString, jni::JString>>("withOffer");
|
|
45
45
|
jni::local_ref<jni::JMap<jni::JString, jni::JString>> withOffer = this->getFieldValue(fieldWithOffer);
|
|
46
|
+
static const auto fieldAdvancedCommerceData = clazz->getField<jni::JString>("advancedCommerceData");
|
|
47
|
+
jni::local_ref<jni::JString> advancedCommerceData = this->getFieldValue(fieldAdvancedCommerceData);
|
|
46
48
|
return NitroRequestPurchaseIos(
|
|
47
49
|
sku->toStdString(),
|
|
48
50
|
andDangerouslyFinishTransactionAutomatically != nullptr ? std::make_optional(static_cast<bool>(andDangerouslyFinishTransactionAutomatically->value())) : std::nullopt,
|
|
@@ -55,7 +57,8 @@ namespace margelo::nitro::iap {
|
|
|
55
57
|
__map.emplace(__entry.first->toStdString(), __entry.second->toStdString());
|
|
56
58
|
}
|
|
57
59
|
return __map;
|
|
58
|
-
}()) : std::nullopt
|
|
60
|
+
}()) : std::nullopt,
|
|
61
|
+
advancedCommerceData != nullptr ? std::make_optional(advancedCommerceData->toStdString()) : std::nullopt
|
|
59
62
|
);
|
|
60
63
|
}
|
|
61
64
|
|
|
@@ -65,7 +68,7 @@ namespace margelo::nitro::iap {
|
|
|
65
68
|
*/
|
|
66
69
|
[[maybe_unused]]
|
|
67
70
|
static jni::local_ref<JNitroRequestPurchaseIos::javaobject> fromCpp(const NitroRequestPurchaseIos& value) {
|
|
68
|
-
using JSignature = JNitroRequestPurchaseIos(jni::alias_ref<jni::JString>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JMap<jni::JString, jni::JString
|
|
71
|
+
using JSignature = JNitroRequestPurchaseIos(jni::alias_ref<jni::JString>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JString>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JMap<jni::JString, jni::JString>>, jni::alias_ref<jni::JString>);
|
|
69
72
|
static const auto clazz = javaClassStatic();
|
|
70
73
|
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
|
71
74
|
return create(
|
|
@@ -80,7 +83,8 @@ namespace margelo::nitro::iap {
|
|
|
80
83
|
__map->put(jni::make_jstring(__entry.first), jni::make_jstring(__entry.second));
|
|
81
84
|
}
|
|
82
85
|
return __map;
|
|
83
|
-
}() : nullptr
|
|
86
|
+
}() : nullptr,
|
|
87
|
+
value.advancedCommerceData.has_value() ? jni::make_jstring(value.advancedCommerceData.value()) : nullptr
|
|
84
88
|
);
|
|
85
89
|
}
|
|
86
90
|
};
|
|
@@ -31,7 +31,10 @@ data class NitroRequestPurchaseIos(
|
|
|
31
31
|
val quantity: Double?,
|
|
32
32
|
@DoNotStrip
|
|
33
33
|
@Keep
|
|
34
|
-
val withOffer: Map<String, String
|
|
34
|
+
val withOffer: Map<String, String>?,
|
|
35
|
+
@DoNotStrip
|
|
36
|
+
@Keep
|
|
37
|
+
val advancedCommerceData: String?
|
|
35
38
|
) {
|
|
36
39
|
private companion object {
|
|
37
40
|
/**
|
|
@@ -41,8 +44,8 @@ data class NitroRequestPurchaseIos(
|
|
|
41
44
|
@Keep
|
|
42
45
|
@Suppress("unused")
|
|
43
46
|
@JvmStatic
|
|
44
|
-
private fun fromCpp(sku: String, andDangerouslyFinishTransactionAutomatically: Boolean?, appAccountToken: String?, quantity: Double?, withOffer: Map<String, String
|
|
45
|
-
return NitroRequestPurchaseIos(sku, andDangerouslyFinishTransactionAutomatically, appAccountToken, quantity, withOffer)
|
|
47
|
+
private fun fromCpp(sku: String, andDangerouslyFinishTransactionAutomatically: Boolean?, appAccountToken: String?, quantity: Double?, withOffer: Map<String, String>?, advancedCommerceData: String?): NitroRequestPurchaseIos {
|
|
48
|
+
return NitroRequestPurchaseIos(sku, andDangerouslyFinishTransactionAutomatically, appAccountToken, quantity, withOffer, advancedCommerceData)
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
}
|
|
@@ -18,7 +18,7 @@ public extension NitroRequestPurchaseIos {
|
|
|
18
18
|
/**
|
|
19
19
|
* Create a new instance of `NitroRequestPurchaseIos`.
|
|
20
20
|
*/
|
|
21
|
-
init(sku: String, andDangerouslyFinishTransactionAutomatically: Bool?, appAccountToken: String?, quantity: Double?, withOffer: Dictionary<String, String
|
|
21
|
+
init(sku: String, andDangerouslyFinishTransactionAutomatically: Bool?, appAccountToken: String?, quantity: Double?, withOffer: Dictionary<String, String>?, advancedCommerceData: String?) {
|
|
22
22
|
self.init(std.string(sku), { () -> bridge.std__optional_bool_ in
|
|
23
23
|
if let __unwrappedValue = andDangerouslyFinishTransactionAutomatically {
|
|
24
24
|
return bridge.create_std__optional_bool_(__unwrappedValue)
|
|
@@ -49,6 +49,12 @@ public extension NitroRequestPurchaseIos {
|
|
|
49
49
|
} else {
|
|
50
50
|
return .init()
|
|
51
51
|
}
|
|
52
|
+
}(), { () -> bridge.std__optional_std__string_ in
|
|
53
|
+
if let __unwrappedValue = advancedCommerceData {
|
|
54
|
+
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
55
|
+
} else {
|
|
56
|
+
return .init()
|
|
57
|
+
}
|
|
52
58
|
}())
|
|
53
59
|
}
|
|
54
60
|
|
|
@@ -165,4 +171,28 @@ public extension NitroRequestPurchaseIos {
|
|
|
165
171
|
}()
|
|
166
172
|
}
|
|
167
173
|
}
|
|
174
|
+
|
|
175
|
+
var advancedCommerceData: String? {
|
|
176
|
+
@inline(__always)
|
|
177
|
+
get {
|
|
178
|
+
return { () -> String? in
|
|
179
|
+
if bridge.has_value_std__optional_std__string_(self.__advancedCommerceData) {
|
|
180
|
+
let __unwrapped = bridge.get_std__optional_std__string_(self.__advancedCommerceData)
|
|
181
|
+
return String(__unwrapped)
|
|
182
|
+
} else {
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
}()
|
|
186
|
+
}
|
|
187
|
+
@inline(__always)
|
|
188
|
+
set {
|
|
189
|
+
self.__advancedCommerceData = { () -> bridge.std__optional_std__string_ in
|
|
190
|
+
if let __unwrappedValue = newValue {
|
|
191
|
+
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
192
|
+
} else {
|
|
193
|
+
return .init()
|
|
194
|
+
}
|
|
195
|
+
}()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
168
198
|
}
|
|
@@ -41,10 +41,11 @@ namespace margelo::nitro::iap {
|
|
|
41
41
|
std::optional<std::string> appAccountToken SWIFT_PRIVATE;
|
|
42
42
|
std::optional<double> quantity SWIFT_PRIVATE;
|
|
43
43
|
std::optional<std::unordered_map<std::string, std::string>> withOffer SWIFT_PRIVATE;
|
|
44
|
+
std::optional<std::string> advancedCommerceData SWIFT_PRIVATE;
|
|
44
45
|
|
|
45
46
|
public:
|
|
46
47
|
NitroRequestPurchaseIos() = default;
|
|
47
|
-
explicit NitroRequestPurchaseIos(std::string sku, std::optional<bool> andDangerouslyFinishTransactionAutomatically, std::optional<std::string> appAccountToken, std::optional<double> quantity, std::optional<std::unordered_map<std::string, std::string>> withOffer): sku(sku), andDangerouslyFinishTransactionAutomatically(andDangerouslyFinishTransactionAutomatically), appAccountToken(appAccountToken), quantity(quantity), withOffer(withOffer) {}
|
|
48
|
+
explicit NitroRequestPurchaseIos(std::string sku, std::optional<bool> andDangerouslyFinishTransactionAutomatically, std::optional<std::string> appAccountToken, std::optional<double> quantity, std::optional<std::unordered_map<std::string, std::string>> withOffer, std::optional<std::string> advancedCommerceData): sku(sku), andDangerouslyFinishTransactionAutomatically(andDangerouslyFinishTransactionAutomatically), appAccountToken(appAccountToken), quantity(quantity), withOffer(withOffer), advancedCommerceData(advancedCommerceData) {}
|
|
48
49
|
};
|
|
49
50
|
|
|
50
51
|
} // namespace margelo::nitro::iap
|
|
@@ -61,7 +62,8 @@ namespace margelo::nitro {
|
|
|
61
62
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, "andDangerouslyFinishTransactionAutomatically")),
|
|
62
63
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "appAccountToken")),
|
|
63
64
|
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, "quantity")),
|
|
64
|
-
JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, "withOffer"))
|
|
65
|
+
JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::fromJSI(runtime, obj.getProperty(runtime, "withOffer")),
|
|
66
|
+
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, "advancedCommerceData"))
|
|
65
67
|
);
|
|
66
68
|
}
|
|
67
69
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroRequestPurchaseIos& arg) {
|
|
@@ -71,6 +73,7 @@ namespace margelo::nitro {
|
|
|
71
73
|
obj.setProperty(runtime, "appAccountToken", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.appAccountToken));
|
|
72
74
|
obj.setProperty(runtime, "quantity", JSIConverter<std::optional<double>>::toJSI(runtime, arg.quantity));
|
|
73
75
|
obj.setProperty(runtime, "withOffer", JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::toJSI(runtime, arg.withOffer));
|
|
76
|
+
obj.setProperty(runtime, "advancedCommerceData", JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.advancedCommerceData));
|
|
74
77
|
return obj;
|
|
75
78
|
}
|
|
76
79
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -86,6 +89,7 @@ namespace margelo::nitro {
|
|
|
86
89
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "appAccountToken"))) return false;
|
|
87
90
|
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, "quantity"))) return false;
|
|
88
91
|
if (!JSIConverter<std::optional<std::unordered_map<std::string, std::string>>>::canConvert(runtime, obj.getProperty(runtime, "withOffer"))) return false;
|
|
92
|
+
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, "advancedCommerceData"))) return false;
|
|
89
93
|
return true;
|
|
90
94
|
}
|
|
91
95
|
};
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const config_plugins_1 = require("expo/config-plugins");
|
|
4
|
+
const pkg = require('../../package.json');
|
|
5
|
+
// Global flag to prevent duplicate logs
|
|
6
|
+
let hasLoggedPluginExecution = false;
|
|
7
|
+
const addLineToGradle = (content, anchor, lineToAdd, offset = 1) => {
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
const index = lines.findIndex((line) => line.match(anchor));
|
|
10
|
+
if (index === -1) {
|
|
11
|
+
console.warn(`Anchor "${anchor}" not found in build.gradle. Appending to end.`);
|
|
12
|
+
lines.push(lineToAdd);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
lines.splice(index + offset, 0, lineToAdd);
|
|
16
|
+
}
|
|
17
|
+
return lines.join('\n');
|
|
18
|
+
};
|
|
19
|
+
const modifyAppBuildGradle = (gradle) => {
|
|
20
|
+
let modified = gradle;
|
|
21
|
+
// Add billing library dependencies to app-level build.gradle
|
|
22
|
+
const billingDep = ` implementation "com.android.billingclient:billing-ktx:8.0.0"`;
|
|
23
|
+
const gmsDep = ` implementation "com.google.android.gms:play-services-base:18.1.0"`;
|
|
24
|
+
let hasAddedDependency = false;
|
|
25
|
+
if (!modified.includes(billingDep)) {
|
|
26
|
+
modified = addLineToGradle(modified, /dependencies\s*{/, billingDep);
|
|
27
|
+
hasAddedDependency = true;
|
|
28
|
+
}
|
|
29
|
+
if (!modified.includes(gmsDep)) {
|
|
30
|
+
modified = addLineToGradle(modified, /dependencies\s*{/, gmsDep, 1);
|
|
31
|
+
hasAddedDependency = true;
|
|
32
|
+
}
|
|
33
|
+
// Log only once and only if we actually added dependencies
|
|
34
|
+
if (hasAddedDependency && !hasLoggedPluginExecution) {
|
|
35
|
+
console.log('🛠️ react-native-iap: Added billing dependencies to build.gradle');
|
|
36
|
+
}
|
|
37
|
+
return modified;
|
|
38
|
+
};
|
|
39
|
+
const withIapAndroid = (config) => {
|
|
40
|
+
// Add IAP dependencies to app build.gradle
|
|
41
|
+
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
42
|
+
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
|
|
43
|
+
return config;
|
|
44
|
+
});
|
|
45
|
+
config = (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
46
|
+
const manifest = config.modResults;
|
|
47
|
+
if (!manifest.manifest['uses-permission']) {
|
|
48
|
+
manifest.manifest['uses-permission'] = [];
|
|
49
|
+
}
|
|
50
|
+
const permissions = manifest.manifest['uses-permission'];
|
|
51
|
+
const billingPerm = { $: { 'android:name': 'com.android.vending.BILLING' } };
|
|
52
|
+
const alreadyExists = permissions.some((p) => p.$['android:name'] === 'com.android.vending.BILLING');
|
|
53
|
+
if (!alreadyExists) {
|
|
54
|
+
permissions.push(billingPerm);
|
|
55
|
+
if (!hasLoggedPluginExecution) {
|
|
56
|
+
console.log('✅ Added com.android.vending.BILLING to AndroidManifest.xml');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (!hasLoggedPluginExecution) {
|
|
61
|
+
console.log('ℹ️ com.android.vending.BILLING already exists in AndroidManifest.xml');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return config;
|
|
65
|
+
});
|
|
66
|
+
return config;
|
|
67
|
+
};
|
|
68
|
+
const withIAP = (config, _props) => {
|
|
69
|
+
try {
|
|
70
|
+
const result = withIapAndroid(config);
|
|
71
|
+
// Set flag after first execution to prevent duplicate logs
|
|
72
|
+
hasLoggedPluginExecution = true;
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
config_plugins_1.WarningAggregator.addWarningAndroid('react-native-iap', `react-native-iap plugin encountered an error: ${error}`);
|
|
77
|
+
console.error('react-native-iap plugin error:', error);
|
|
78
|
+
return config;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withIAP, pkg.name, pkg.version);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/withiap.ts"],"version":"5.9.2"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["./src/
|
|
1
|
+
{"root":["./src/withiap.ts"],"version":"5.9.2"}
|
package/src/index.ts
CHANGED
|
@@ -1068,14 +1068,17 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1068
1068
|
}
|
|
1069
1069
|
|
|
1070
1070
|
if (Platform.OS === 'ios') {
|
|
1071
|
-
|
|
1071
|
+
// Support both 'apple' (recommended) and 'ios' (deprecated) fields
|
|
1072
|
+
const iosRequest = perPlatformRequest.apple ?? perPlatformRequest.ios;
|
|
1072
1073
|
if (!iosRequest?.sku) {
|
|
1073
1074
|
throw new Error(
|
|
1074
1075
|
'Invalid request for iOS. The `sku` property is required.',
|
|
1075
1076
|
);
|
|
1076
1077
|
}
|
|
1077
1078
|
} else if (Platform.OS === 'android') {
|
|
1078
|
-
|
|
1079
|
+
// Support both 'google' (recommended) and 'android' (deprecated) fields
|
|
1080
|
+
const androidRequest =
|
|
1081
|
+
perPlatformRequest.google ?? perPlatformRequest.android;
|
|
1079
1082
|
if (!androidRequest?.skus?.length) {
|
|
1080
1083
|
throw new Error(
|
|
1081
1084
|
'Invalid request for Android. The `skus` property is required and must be a non-empty array.',
|
|
@@ -1087,10 +1090,12 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1087
1090
|
|
|
1088
1091
|
const unifiedRequest: NitroPurchaseRequest = {};
|
|
1089
1092
|
|
|
1090
|
-
|
|
1093
|
+
// Support both 'apple' (recommended) and 'ios' (deprecated) fields
|
|
1094
|
+
const iosRequestSource = perPlatformRequest.apple ?? perPlatformRequest.ios;
|
|
1095
|
+
if (Platform.OS === 'ios' && iosRequestSource) {
|
|
1091
1096
|
const iosRequest = isSubs
|
|
1092
|
-
? (
|
|
1093
|
-
: (
|
|
1097
|
+
? (iosRequestSource as RequestSubscriptionIosProps)
|
|
1098
|
+
: (iosRequestSource as RequestPurchaseIosProps);
|
|
1094
1099
|
|
|
1095
1100
|
const iosPayload: NonNullable<NitroPurchaseRequest['ios']> = {
|
|
1096
1101
|
sku: iosRequest.sku,
|
|
@@ -1117,14 +1122,20 @@ export const requestPurchase: MutationField<'requestPurchase'> = async (
|
|
|
1117
1122
|
if (offerRecord) {
|
|
1118
1123
|
iosPayload.withOffer = offerRecord;
|
|
1119
1124
|
}
|
|
1125
|
+
if (iosRequest.advancedCommerceData) {
|
|
1126
|
+
iosPayload.advancedCommerceData = iosRequest.advancedCommerceData;
|
|
1127
|
+
}
|
|
1120
1128
|
|
|
1121
1129
|
unifiedRequest.ios = iosPayload;
|
|
1122
1130
|
}
|
|
1123
1131
|
|
|
1124
|
-
|
|
1132
|
+
// Support both 'google' (recommended) and 'android' (deprecated) fields
|
|
1133
|
+
const androidRequestSource =
|
|
1134
|
+
perPlatformRequest.google ?? perPlatformRequest.android;
|
|
1135
|
+
if (Platform.OS === 'android' && androidRequestSource) {
|
|
1125
1136
|
const androidRequest = isSubs
|
|
1126
|
-
? (
|
|
1127
|
-
: (
|
|
1137
|
+
? (androidRequestSource as RequestSubscriptionAndroidProps)
|
|
1138
|
+
: (androidRequestSource as RequestPurchaseAndroidProps);
|
|
1128
1139
|
|
|
1129
1140
|
const androidPayload: NonNullable<NitroPurchaseRequest['android']> = {
|
|
1130
1141
|
skus: androidRequest.skus,
|
|
@@ -1601,43 +1612,60 @@ export const presentCodeRedemptionSheetIOS: MutationField<
|
|
|
1601
1612
|
|
|
1602
1613
|
/**
|
|
1603
1614
|
* Buy promoted product on iOS
|
|
1615
|
+
* @deprecated In StoreKit 2, promoted products can be purchased directly via the standard `requestPurchase()` flow.
|
|
1616
|
+
* Use `promotedProductListenerIOS` to receive the product ID when a user taps a promoted product,
|
|
1617
|
+
* then call `requestPurchase()` with the received SKU directly.
|
|
1618
|
+
*
|
|
1619
|
+
* @example
|
|
1620
|
+
* ```typescript
|
|
1621
|
+
* // Recommended approach
|
|
1622
|
+
* promotedProductListenerIOS(async (product) => {
|
|
1623
|
+
* await requestPurchase({
|
|
1624
|
+
* request: { apple: { sku: product.id } },
|
|
1625
|
+
* type: 'in-app'
|
|
1626
|
+
* });
|
|
1627
|
+
* });
|
|
1628
|
+
* ```
|
|
1629
|
+
*
|
|
1604
1630
|
* @returns Promise<boolean> - true when the request triggers successfully
|
|
1605
1631
|
* @platform iOS
|
|
1606
1632
|
*/
|
|
1607
|
-
export const requestPurchaseOnPromotedProductIOS
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
);
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
try {
|
|
1617
|
-
await IAP.instance.buyPromotedProductIOS();
|
|
1618
|
-
const pending = await IAP.instance.getPendingTransactionsIOS();
|
|
1619
|
-
const latest = pending.find((purchase) => purchase != null);
|
|
1620
|
-
if (!latest) {
|
|
1621
|
-
throw new Error('No promoted purchase available after request');
|
|
1633
|
+
export const requestPurchaseOnPromotedProductIOS =
|
|
1634
|
+
async (): Promise<boolean> => {
|
|
1635
|
+
if (Platform.OS !== 'ios') {
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
'requestPurchaseOnPromotedProductIOS is only available on iOS',
|
|
1638
|
+
);
|
|
1622
1639
|
}
|
|
1623
1640
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1641
|
+
try {
|
|
1642
|
+
await IAP.instance.buyPromotedProductIOS();
|
|
1643
|
+
const pending = await IAP.instance.getPendingTransactionsIOS();
|
|
1644
|
+
const latest = pending.find((purchase) => purchase != null);
|
|
1645
|
+
if (!latest) {
|
|
1646
|
+
throw new Error('No promoted purchase available after request');
|
|
1647
|
+
}
|
|
1628
1648
|
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1649
|
+
const converted = convertNitroPurchaseToPurchase(latest);
|
|
1650
|
+
if (converted.platform !== 'ios') {
|
|
1651
|
+
throw new Error('Promoted purchase result not available for iOS');
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
return true;
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
RnIapConsole.error(
|
|
1657
|
+
'[requestPurchaseOnPromotedProductIOS] Failed:',
|
|
1658
|
+
error,
|
|
1659
|
+
);
|
|
1660
|
+
const parsedError = parseErrorStringToJsonObj(error);
|
|
1661
|
+
throw createPurchaseError({
|
|
1662
|
+
code: parsedError.code,
|
|
1663
|
+
message: parsedError.message,
|
|
1664
|
+
responseCode: parsedError.responseCode,
|
|
1665
|
+
debugMessage: parsedError.debugMessage,
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1641
1669
|
|
|
1642
1670
|
/**
|
|
1643
1671
|
* Clear unfinished transactions on iOS
|
package/src/specs/RnIap.nitro.ts
CHANGED
|
@@ -102,6 +102,13 @@ export interface NitroRequestPurchaseIos {
|
|
|
102
102
|
appAccountToken?: RequestPurchaseIosProps['appAccountToken'];
|
|
103
103
|
quantity?: RequestPurchaseIosProps['quantity'];
|
|
104
104
|
withOffer?: Record<string, string> | null;
|
|
105
|
+
/**
|
|
106
|
+
* Advanced commerce data for StoreKit 2's Product.PurchaseOption.custom API.
|
|
107
|
+
* Used to pass attribution data (campaign tokens, affiliate IDs) during purchases.
|
|
108
|
+
* Data is formatted as JSON: {"signatureInfo": {"token": "<value>"}}
|
|
109
|
+
* @platform iOS
|
|
110
|
+
*/
|
|
111
|
+
advancedCommerceData?: RequestPurchaseIosProps['advancedCommerceData'];
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
export interface NitroRequestPurchaseAndroid {
|
package/src/types.ts
CHANGED
|
@@ -358,8 +358,15 @@ export interface Mutation {
|
|
|
358
358
|
presentExternalPurchaseNoticeSheetIOS: Promise<ExternalPurchaseNoticeResultIOS>;
|
|
359
359
|
/** Initiate a purchase flow; rely on events for final state */
|
|
360
360
|
requestPurchase?: Promise<(Purchase | Purchase[] | null)>;
|
|
361
|
-
/**
|
|
362
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Purchase the promoted product surfaced by the App Store.
|
|
363
|
+
*
|
|
364
|
+
* @deprecated Use promotedProductListenerIOS to receive the productId,
|
|
365
|
+
* then call requestPurchase with that SKU instead. In StoreKit 2,
|
|
366
|
+
* promoted products can be purchased directly via the standard purchase flow.
|
|
367
|
+
* @deprecated Use promotedProductListenerIOS + requestPurchase instead
|
|
368
|
+
*/
|
|
369
|
+
requestPurchaseOnPromotedProductIOS: boolean;
|
|
363
370
|
/** Restore completed purchases across platforms */
|
|
364
371
|
restorePurchases: Promise<void>;
|
|
365
372
|
/**
|
|
@@ -889,6 +896,13 @@ export interface RequestPurchaseAndroidProps {
|
|
|
889
896
|
}
|
|
890
897
|
|
|
891
898
|
export interface RequestPurchaseIosProps {
|
|
899
|
+
/**
|
|
900
|
+
* Advanced commerce data token (iOS 15+).
|
|
901
|
+
* Used with StoreKit 2's Product.PurchaseOption.custom API for passing
|
|
902
|
+
* campaign tokens, affiliate IDs, or other attribution data.
|
|
903
|
+
* The data is formatted as JSON: {"signatureInfo": {"token": "<value>"}}
|
|
904
|
+
*/
|
|
905
|
+
advancedCommerceData?: (string | null);
|
|
892
906
|
/** Auto-finish transaction (dangerous) */
|
|
893
907
|
andDangerouslyFinishTransactionAutomatically?: (boolean | null);
|
|
894
908
|
/** App account token for user tracking */
|
|
@@ -964,6 +978,13 @@ export interface RequestSubscriptionAndroidProps {
|
|
|
964
978
|
}
|
|
965
979
|
|
|
966
980
|
export interface RequestSubscriptionIosProps {
|
|
981
|
+
/**
|
|
982
|
+
* Advanced commerce data token (iOS 15+).
|
|
983
|
+
* Used with StoreKit 2's Product.PurchaseOption.custom API for passing
|
|
984
|
+
* campaign tokens, affiliate IDs, or other attribution data.
|
|
985
|
+
* The data is formatted as JSON: {"signatureInfo": {"token": "<value>"}}
|
|
986
|
+
*/
|
|
987
|
+
advancedCommerceData?: (string | null);
|
|
967
988
|
andDangerouslyFinishTransactionAutomatically?: (boolean | null);
|
|
968
989
|
appAccountToken?: (string | null);
|
|
969
990
|
quantity?: (number | null);
|