react-native-iap 14.5.1-rc.1 → 14.6.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +198 -36
- package/ios/HybridRnIap.swift +32 -2
- package/lib/module/hooks/useIAP.js +1 -6
- package/lib/module/hooks/useIAP.js.map +1 -1
- package/lib/module/index.js +228 -10
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +116 -0
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/plugin/src/withIAP.d.ts +6 -0
- package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useIAP.d.ts +5 -7
- package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +144 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/RnIap.nitro.d.ts +90 -8
- package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +194 -10
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JBillingProgramAndroid.hpp +62 -0
- package/nitrogen/generated/android/c++/JExternalLinkLaunchModeAndroid.hpp +62 -0
- package/nitrogen/generated/android/c++/JExternalLinkTypeAndroid.hpp +62 -0
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +88 -4
- package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/JNitroBillingProgramAvailabilityResultAndroid.hpp +62 -0
- package/nitrogen/generated/android/c++/JNitroBillingProgramReportingDetailsAndroid.hpp +63 -0
- package/nitrogen/generated/android/c++/JNitroLaunchExternalLinkParamsAndroid.hpp +75 -0
- package/nitrogen/generated/android/c++/JNitroReceiptValidationAppleOptions.hpp +57 -0
- package/nitrogen/generated/android/c++/{JNitroReceiptValidationAndroidOptions.hpp → JNitroReceiptValidationGoogleOptions.hpp} +18 -14
- package/nitrogen/generated/android/c++/JNitroReceiptValidationHorizonOptions.hpp +65 -0
- package/nitrogen/generated/android/c++/JNitroReceiptValidationParams.hpp +19 -11
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/BillingProgramAndroid.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalLinkLaunchModeAndroid.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalLinkTypeAndroid.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +16 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroBillingProgramAvailabilityResultAndroid.kt +39 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroBillingProgramReportingDetailsAndroid.kt +39 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroLaunchExternalLinkParamsAndroid.kt +45 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationAppleOptions.kt +36 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/{NitroReceiptValidationAndroidOptions.kt → NitroReceiptValidationGoogleOptions.kt} +9 -6
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationHorizonOptions.kt +42 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationParams.kt +7 -4
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.cpp +16 -0
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +141 -10
- package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +27 -3
- package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +57 -3
- package/nitrogen/generated/ios/swift/BillingProgramAndroid.swift +44 -0
- package/nitrogen/generated/ios/swift/ExternalLinkLaunchModeAndroid.swift +44 -0
- package/nitrogen/generated/ios/swift/ExternalLinkTypeAndroid.swift +44 -0
- package/nitrogen/generated/ios/swift/Func_void_NitroBillingProgramAvailabilityResultAndroid.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_NitroBillingProgramReportingDetailsAndroid.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +4 -0
- package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +68 -0
- package/nitrogen/generated/ios/swift/NitroBillingProgramAvailabilityResultAndroid.swift +46 -0
- package/nitrogen/generated/ios/swift/NitroBillingProgramReportingDetailsAndroid.swift +46 -0
- package/nitrogen/generated/ios/swift/NitroLaunchExternalLinkParamsAndroid.swift +68 -0
- package/nitrogen/generated/ios/swift/NitroReceiptValidationAppleOptions.swift +35 -0
- package/nitrogen/generated/ios/swift/{NitroReceiptValidationAndroidOptions.swift → NitroReceiptValidationGoogleOptions.swift} +21 -10
- package/nitrogen/generated/ios/swift/NitroReceiptValidationHorizonOptions.swift +57 -0
- package/nitrogen/generated/ios/swift/NitroReceiptValidationParams.swift +46 -11
- package/nitrogen/generated/shared/c++/BillingProgramAndroid.hpp +80 -0
- package/nitrogen/generated/shared/c++/ExternalLinkLaunchModeAndroid.hpp +80 -0
- package/nitrogen/generated/shared/c++/ExternalLinkTypeAndroid.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +16 -0
- package/nitrogen/generated/shared/c++/NitroBillingProgramAvailabilityResultAndroid.hpp +80 -0
- package/nitrogen/generated/shared/c++/NitroBillingProgramReportingDetailsAndroid.hpp +81 -0
- package/nitrogen/generated/shared/c++/NitroLaunchExternalLinkParamsAndroid.hpp +95 -0
- package/nitrogen/generated/shared/c++/NitroReceiptValidationAppleOptions.hpp +75 -0
- package/nitrogen/generated/shared/c++/{NitroReceiptValidationAndroidOptions.hpp → NitroReceiptValidationGoogleOptions.hpp} +18 -14
- package/nitrogen/generated/shared/c++/NitroReceiptValidationHorizonOptions.hpp +83 -0
- package/nitrogen/generated/shared/c++/NitroReceiptValidationParams.hpp +22 -13
- package/openiap-versions.json +3 -3
- package/package.json +1 -1
- package/plugin/build/withIAP.d.ts +6 -0
- package/plugin/build/withIAP.js +46 -2
- package/plugin/src/withIAP.ts +67 -2
- package/src/hooks/useIAP.ts +8 -23
- package/src/index.ts +297 -14
- package/src/specs/RnIap.nitro.ts +125 -10
- package/src/types.ts +207 -10
|
@@ -23,12 +23,17 @@
|
|
|
23
23
|
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
24
|
#endif
|
|
25
25
|
|
|
26
|
-
// Forward declaration of `
|
|
27
|
-
namespace margelo::nitro::iap { struct
|
|
26
|
+
// Forward declaration of `NitroReceiptValidationAppleOptions` to properly resolve imports.
|
|
27
|
+
namespace margelo::nitro::iap { struct NitroReceiptValidationAppleOptions; }
|
|
28
|
+
// Forward declaration of `NitroReceiptValidationGoogleOptions` to properly resolve imports.
|
|
29
|
+
namespace margelo::nitro::iap { struct NitroReceiptValidationGoogleOptions; }
|
|
30
|
+
// Forward declaration of `NitroReceiptValidationHorizonOptions` to properly resolve imports.
|
|
31
|
+
namespace margelo::nitro::iap { struct NitroReceiptValidationHorizonOptions; }
|
|
28
32
|
|
|
29
|
-
#include
|
|
30
|
-
#include "NitroReceiptValidationAndroidOptions.hpp"
|
|
33
|
+
#include "NitroReceiptValidationAppleOptions.hpp"
|
|
31
34
|
#include <optional>
|
|
35
|
+
#include "NitroReceiptValidationGoogleOptions.hpp"
|
|
36
|
+
#include "NitroReceiptValidationHorizonOptions.hpp"
|
|
32
37
|
|
|
33
38
|
namespace margelo::nitro::iap {
|
|
34
39
|
|
|
@@ -37,12 +42,13 @@ namespace margelo::nitro::iap {
|
|
|
37
42
|
*/
|
|
38
43
|
struct NitroReceiptValidationParams {
|
|
39
44
|
public:
|
|
40
|
-
std::
|
|
41
|
-
std::optional<
|
|
45
|
+
std::optional<NitroReceiptValidationAppleOptions> apple SWIFT_PRIVATE;
|
|
46
|
+
std::optional<NitroReceiptValidationGoogleOptions> google SWIFT_PRIVATE;
|
|
47
|
+
std::optional<NitroReceiptValidationHorizonOptions> horizon SWIFT_PRIVATE;
|
|
42
48
|
|
|
43
49
|
public:
|
|
44
50
|
NitroReceiptValidationParams() = default;
|
|
45
|
-
explicit NitroReceiptValidationParams(std::
|
|
51
|
+
explicit NitroReceiptValidationParams(std::optional<NitroReceiptValidationAppleOptions> apple, std::optional<NitroReceiptValidationGoogleOptions> google, std::optional<NitroReceiptValidationHorizonOptions> horizon): apple(apple), google(google), horizon(horizon) {}
|
|
46
52
|
};
|
|
47
53
|
|
|
48
54
|
} // namespace margelo::nitro::iap
|
|
@@ -55,14 +61,16 @@ namespace margelo::nitro {
|
|
|
55
61
|
static inline margelo::nitro::iap::NitroReceiptValidationParams fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
56
62
|
jsi::Object obj = arg.asObject(runtime);
|
|
57
63
|
return margelo::nitro::iap::NitroReceiptValidationParams(
|
|
58
|
-
JSIConverter<std::
|
|
59
|
-
JSIConverter<std::optional<margelo::nitro::iap::
|
|
64
|
+
JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationAppleOptions>>::fromJSI(runtime, obj.getProperty(runtime, "apple")),
|
|
65
|
+
JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationGoogleOptions>>::fromJSI(runtime, obj.getProperty(runtime, "google")),
|
|
66
|
+
JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationHorizonOptions>>::fromJSI(runtime, obj.getProperty(runtime, "horizon"))
|
|
60
67
|
);
|
|
61
68
|
}
|
|
62
69
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::iap::NitroReceiptValidationParams& arg) {
|
|
63
70
|
jsi::Object obj(runtime);
|
|
64
|
-
obj.setProperty(runtime, "
|
|
65
|
-
obj.setProperty(runtime, "
|
|
71
|
+
obj.setProperty(runtime, "apple", JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationAppleOptions>>::toJSI(runtime, arg.apple));
|
|
72
|
+
obj.setProperty(runtime, "google", JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationGoogleOptions>>::toJSI(runtime, arg.google));
|
|
73
|
+
obj.setProperty(runtime, "horizon", JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationHorizonOptions>>::toJSI(runtime, arg.horizon));
|
|
66
74
|
return obj;
|
|
67
75
|
}
|
|
68
76
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -73,8 +81,9 @@ namespace margelo::nitro {
|
|
|
73
81
|
if (!nitro::isPlainObject(runtime, obj)) {
|
|
74
82
|
return false;
|
|
75
83
|
}
|
|
76
|
-
if (!JSIConverter<std::
|
|
77
|
-
if (!JSIConverter<std::optional<margelo::nitro::iap::
|
|
84
|
+
if (!JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationAppleOptions>>::canConvert(runtime, obj.getProperty(runtime, "apple"))) return false;
|
|
85
|
+
if (!JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationGoogleOptions>>::canConvert(runtime, obj.getProperty(runtime, "google"))) return false;
|
|
86
|
+
if (!JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationHorizonOptions>>::canConvert(runtime, obj.getProperty(runtime, "horizon"))) return false;
|
|
78
87
|
return true;
|
|
79
88
|
}
|
|
80
89
|
};
|
package/openiap-versions.json
CHANGED
package/package.json
CHANGED
|
@@ -31,6 +31,12 @@ type IapPluginProps = {
|
|
|
31
31
|
* @platform ios
|
|
32
32
|
*/
|
|
33
33
|
iosAlternativeBilling?: IosAlternativeBillingConfig;
|
|
34
|
+
/**
|
|
35
|
+
* IAPKit API key for purchase verification.
|
|
36
|
+
* This key will be added to AndroidManifest.xml (as meta-data) and Info.plist.
|
|
37
|
+
* Get your API key from https://iapkit.com
|
|
38
|
+
*/
|
|
39
|
+
iapkitApiKey?: string;
|
|
34
40
|
};
|
|
35
41
|
type IapPluginCallable = {
|
|
36
42
|
(config: ExpoConfig): ExpoConfig;
|
package/plugin/build/withIAP.js
CHANGED
|
@@ -86,7 +86,7 @@ const modifyAppBuildGradle = (gradle) => {
|
|
|
86
86
|
}
|
|
87
87
|
return modified;
|
|
88
88
|
};
|
|
89
|
-
const withIapAndroid = (config) => {
|
|
89
|
+
const withIapAndroid = (config, props) => {
|
|
90
90
|
// Add OpenIAP dependency to app build.gradle
|
|
91
91
|
config = (0, config_plugins_1.withAppBuildGradle)(config, (config) => {
|
|
92
92
|
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
|
|
@@ -111,6 +111,29 @@ const withIapAndroid = (config) => {
|
|
|
111
111
|
console.log('ℹ️ com.android.vending.BILLING already exists in AndroidManifest.xml');
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
// Add IAPKit API key as meta-data if provided
|
|
115
|
+
if (props?.iapkitApiKey) {
|
|
116
|
+
const application = manifest.manifest.application;
|
|
117
|
+
const app = application?.[0];
|
|
118
|
+
if (app) {
|
|
119
|
+
if (!app['meta-data']) {
|
|
120
|
+
app['meta-data'] = [];
|
|
121
|
+
}
|
|
122
|
+
const metaDataKey = 'dev.iapkit.API_KEY';
|
|
123
|
+
const existingMetaData = app['meta-data'].find((m) => m.$?.['android:name'] === metaDataKey);
|
|
124
|
+
if (!existingMetaData) {
|
|
125
|
+
app['meta-data'].push({
|
|
126
|
+
$: {
|
|
127
|
+
'android:name': metaDataKey,
|
|
128
|
+
'android:value': props.iapkitApiKey,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
if (!hasLoggedPluginExecution) {
|
|
132
|
+
console.log('✅ Added IAPKit API key to AndroidManifest.xml');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
114
137
|
return config;
|
|
115
138
|
});
|
|
116
139
|
return config;
|
|
@@ -247,14 +270,35 @@ end
|
|
|
247
270
|
return config;
|
|
248
271
|
});
|
|
249
272
|
};
|
|
273
|
+
/** Add IAPKit API key to iOS Info.plist */
|
|
274
|
+
const withIapkitApiKeyIOS = (config, apiKey) => {
|
|
275
|
+
if (!apiKey) {
|
|
276
|
+
return config;
|
|
277
|
+
}
|
|
278
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
279
|
+
const plist = config.modResults;
|
|
280
|
+
const plistKey = 'IAPKitAPIKey';
|
|
281
|
+
if (!plist[plistKey]) {
|
|
282
|
+
plist[plistKey] = apiKey;
|
|
283
|
+
if (!hasLoggedPluginExecution) {
|
|
284
|
+
console.log('✅ Added IAPKit API key to Info.plist');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return config;
|
|
288
|
+
});
|
|
289
|
+
};
|
|
250
290
|
const withIAP = (config, props) => {
|
|
251
291
|
try {
|
|
252
|
-
let result = withIapAndroid(config);
|
|
292
|
+
let result = withIapAndroid(config, { iapkitApiKey: props?.iapkitApiKey });
|
|
253
293
|
result = withIapIosFollyWorkaround(result, props);
|
|
254
294
|
// Add iOS alternative billing configuration if provided
|
|
255
295
|
if (props?.iosAlternativeBilling) {
|
|
256
296
|
result = withIosAlternativeBilling(result, props.iosAlternativeBilling);
|
|
257
297
|
}
|
|
298
|
+
// Add IAPKit API key to iOS Info.plist if provided
|
|
299
|
+
if (props?.iapkitApiKey) {
|
|
300
|
+
result = withIapkitApiKeyIOS(result, props.iapkitApiKey);
|
|
301
|
+
}
|
|
258
302
|
// Set flag after first execution to prevent duplicate logs
|
|
259
303
|
hasLoggedPluginExecution = true;
|
|
260
304
|
return result;
|
package/plugin/src/withIAP.ts
CHANGED
|
@@ -125,7 +125,10 @@ const modifyAppBuildGradle = (gradle: string): string => {
|
|
|
125
125
|
return modified;
|
|
126
126
|
};
|
|
127
127
|
|
|
128
|
-
const withIapAndroid: ConfigPlugin = (
|
|
128
|
+
const withIapAndroid: ConfigPlugin<{iapkitApiKey?: string} | undefined> = (
|
|
129
|
+
config,
|
|
130
|
+
props,
|
|
131
|
+
) => {
|
|
129
132
|
// Add OpenIAP dependency to app build.gradle
|
|
130
133
|
config = withAppBuildGradle(config, (config) => {
|
|
131
134
|
config.modResults.contents = modifyAppBuildGradle(
|
|
@@ -161,6 +164,34 @@ const withIapAndroid: ConfigPlugin = (config) => {
|
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
// Add IAPKit API key as meta-data if provided
|
|
168
|
+
if (props?.iapkitApiKey) {
|
|
169
|
+
const application = manifest.manifest.application;
|
|
170
|
+
const app = application?.[0];
|
|
171
|
+
if (app) {
|
|
172
|
+
if (!app['meta-data']) {
|
|
173
|
+
app['meta-data'] = [];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const metaDataKey = 'dev.iapkit.API_KEY';
|
|
177
|
+
const existingMetaData = (
|
|
178
|
+
app['meta-data'] as {$?: {'android:name'?: string}}[]
|
|
179
|
+
).find((m) => m.$?.['android:name'] === metaDataKey);
|
|
180
|
+
|
|
181
|
+
if (!existingMetaData) {
|
|
182
|
+
app['meta-data'].push({
|
|
183
|
+
$: {
|
|
184
|
+
'android:name': metaDataKey,
|
|
185
|
+
'android:value': props.iapkitApiKey,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
if (!hasLoggedPluginExecution) {
|
|
189
|
+
console.log('✅ Added IAPKit API key to AndroidManifest.xml');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
164
195
|
return config;
|
|
165
196
|
});
|
|
166
197
|
|
|
@@ -329,6 +360,12 @@ type IapPluginProps = {
|
|
|
329
360
|
* @platform ios
|
|
330
361
|
*/
|
|
331
362
|
iosAlternativeBilling?: IosAlternativeBillingConfig;
|
|
363
|
+
/**
|
|
364
|
+
* IAPKit API key for purchase verification.
|
|
365
|
+
* This key will be added to AndroidManifest.xml (as meta-data) and Info.plist.
|
|
366
|
+
* Get your API key from https://iapkit.com
|
|
367
|
+
*/
|
|
368
|
+
iapkitApiKey?: string;
|
|
332
369
|
};
|
|
333
370
|
|
|
334
371
|
const withIapIosFollyWorkaround: ConfigPlugin<IapPluginProps | undefined> = (
|
|
@@ -389,14 +426,42 @@ end
|
|
|
389
426
|
});
|
|
390
427
|
};
|
|
391
428
|
|
|
429
|
+
/** Add IAPKit API key to iOS Info.plist */
|
|
430
|
+
const withIapkitApiKeyIOS: ConfigPlugin<string | undefined> = (
|
|
431
|
+
config,
|
|
432
|
+
apiKey,
|
|
433
|
+
) => {
|
|
434
|
+
if (!apiKey) {
|
|
435
|
+
return config;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return withInfoPlist(config, (config) => {
|
|
439
|
+
const plist = config.modResults;
|
|
440
|
+
const plistKey = 'IAPKitAPIKey';
|
|
441
|
+
|
|
442
|
+
if (!plist[plistKey]) {
|
|
443
|
+
plist[plistKey] = apiKey;
|
|
444
|
+
if (!hasLoggedPluginExecution) {
|
|
445
|
+
console.log('✅ Added IAPKit API key to Info.plist');
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return config;
|
|
450
|
+
});
|
|
451
|
+
};
|
|
452
|
+
|
|
392
453
|
const withIAP: ConfigPlugin<IapPluginProps | undefined> = (config, props) => {
|
|
393
454
|
try {
|
|
394
|
-
let result = withIapAndroid(config);
|
|
455
|
+
let result = withIapAndroid(config, {iapkitApiKey: props?.iapkitApiKey});
|
|
395
456
|
result = withIapIosFollyWorkaround(result, props);
|
|
396
457
|
// Add iOS alternative billing configuration if provided
|
|
397
458
|
if (props?.iosAlternativeBilling) {
|
|
398
459
|
result = withIosAlternativeBilling(result, props.iosAlternativeBilling);
|
|
399
460
|
}
|
|
461
|
+
// Add IAPKit API key to iOS Info.plist if provided
|
|
462
|
+
if (props?.iapkitApiKey) {
|
|
463
|
+
result = withIapkitApiKeyIOS(result, props.iapkitApiKey);
|
|
464
|
+
}
|
|
400
465
|
// Set flag after first execution to prevent duplicate logs
|
|
401
466
|
hasLoggedPluginExecution = true;
|
|
402
467
|
return result;
|
package/src/hooks/useIAP.ts
CHANGED
|
@@ -71,16 +71,13 @@ type UseIap = {
|
|
|
71
71
|
requestPurchase: (
|
|
72
72
|
params: RequestPurchaseProps,
|
|
73
73
|
) => Promise<RequestPurchaseResult | null>;
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated Use `verifyPurchase` instead. This function will be removed in a future version.
|
|
76
|
+
*/
|
|
74
77
|
validateReceipt: (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
productToken: string;
|
|
79
|
-
accessToken: string;
|
|
80
|
-
isSub?: boolean;
|
|
81
|
-
},
|
|
82
|
-
) => Promise<any>;
|
|
83
|
-
/** Verify purchase with the configured providers (alias for validateReceipt) */
|
|
78
|
+
options: VerifyPurchaseProps,
|
|
79
|
+
) => Promise<VerifyPurchaseResult>;
|
|
80
|
+
/** Verify purchase with the configured providers */
|
|
84
81
|
verifyPurchase: (
|
|
85
82
|
options: VerifyPurchaseProps,
|
|
86
83
|
) => Promise<VerifyPurchaseResult>;
|
|
@@ -311,20 +308,8 @@ export function useIAP(options?: UseIapOptions): UseIap {
|
|
|
311
308
|
// No local restorePurchases; use the top-level helper via returned API
|
|
312
309
|
|
|
313
310
|
const validateReceipt = useCallback(
|
|
314
|
-
async (
|
|
315
|
-
|
|
316
|
-
androidOptions?: {
|
|
317
|
-
packageName: string;
|
|
318
|
-
productToken: string;
|
|
319
|
-
accessToken: string;
|
|
320
|
-
isSub?: boolean;
|
|
321
|
-
},
|
|
322
|
-
) => {
|
|
323
|
-
return validateReceiptInternal({
|
|
324
|
-
sku,
|
|
325
|
-
androidOptions,
|
|
326
|
-
});
|
|
327
|
-
},
|
|
311
|
+
async (options: VerifyPurchaseProps): Promise<VerifyPurchaseResult> =>
|
|
312
|
+
validateReceiptInternal(options),
|
|
328
313
|
[],
|
|
329
314
|
);
|
|
330
315
|
|
package/src/index.ts
CHANGED
|
@@ -1335,29 +1335,94 @@ export const consumePurchaseAndroid: MutationField<
|
|
|
1335
1335
|
|
|
1336
1336
|
/**
|
|
1337
1337
|
* Validate receipt on both iOS and Android platforms
|
|
1338
|
-
* @
|
|
1339
|
-
* @param
|
|
1338
|
+
* @deprecated Use `verifyPurchase` instead. This function will be removed in a future version.
|
|
1339
|
+
* @param options - Platform-specific verification options
|
|
1340
|
+
* @param options.apple - Apple App Store verification options (iOS)
|
|
1341
|
+
* @param options.google - Google Play verification options (Android)
|
|
1342
|
+
* @param options.horizon - Meta Horizon (Quest) verification options
|
|
1340
1343
|
* @returns Promise<VerifyPurchaseResultIOS | VerifyPurchaseResultAndroid> - Platform-specific receipt validation result
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* ```typescript
|
|
1347
|
+
* // Use verifyPurchase instead:
|
|
1348
|
+
* const result = await verifyPurchase({
|
|
1349
|
+
* apple: { sku: 'premium_monthly' },
|
|
1350
|
+
* google: {
|
|
1351
|
+
* sku: 'premium_monthly',
|
|
1352
|
+
* packageName: 'com.example.app',
|
|
1353
|
+
* purchaseToken: 'token...',
|
|
1354
|
+
* accessToken: 'oauth_token...',
|
|
1355
|
+
* isSub: true
|
|
1356
|
+
* }
|
|
1357
|
+
* });
|
|
1358
|
+
* ```
|
|
1341
1359
|
*/
|
|
1342
1360
|
export const validateReceipt: MutationField<'validateReceipt'> = async (
|
|
1343
1361
|
options,
|
|
1344
1362
|
) => {
|
|
1345
|
-
const {
|
|
1363
|
+
const {apple, google, horizon} = options;
|
|
1346
1364
|
try {
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1365
|
+
// Validate required fields based on platform
|
|
1366
|
+
if (Platform.OS === 'ios') {
|
|
1367
|
+
if (!apple?.sku) {
|
|
1368
|
+
throw new Error('Missing required parameter: apple.sku');
|
|
1369
|
+
}
|
|
1370
|
+
} else if (Platform.OS === 'android') {
|
|
1371
|
+
// Horizon verification path (e.g., Meta Quest) - skip Google validation
|
|
1372
|
+
if (horizon?.sku) {
|
|
1373
|
+
// Validate all required Horizon fields
|
|
1374
|
+
if (!horizon.userId || !horizon.accessToken) {
|
|
1375
|
+
throw new Error(
|
|
1376
|
+
'Missing required Horizon parameters: userId and accessToken are required when horizon.sku is provided',
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
// Horizon verification will be handled by native layer
|
|
1380
|
+
} else if (!google) {
|
|
1381
|
+
throw new Error('Missing required parameter: google options');
|
|
1382
|
+
} else {
|
|
1383
|
+
const requiredFields: (keyof typeof google)[] = [
|
|
1384
|
+
'sku',
|
|
1385
|
+
'accessToken',
|
|
1386
|
+
'packageName',
|
|
1387
|
+
'purchaseToken',
|
|
1388
|
+
];
|
|
1389
|
+
for (const field of requiredFields) {
|
|
1390
|
+
if (!google[field]) {
|
|
1391
|
+
throw new Error(
|
|
1392
|
+
`Missing or empty required parameter: google.${field}`,
|
|
1393
|
+
);
|
|
1355
1394
|
}
|
|
1356
|
-
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1357
1398
|
|
|
1358
1399
|
const params: NitroReceiptValidationParams = {
|
|
1359
|
-
sku
|
|
1360
|
-
|
|
1400
|
+
apple: apple?.sku
|
|
1401
|
+
? {
|
|
1402
|
+
sku: apple.sku,
|
|
1403
|
+
}
|
|
1404
|
+
: null,
|
|
1405
|
+
google:
|
|
1406
|
+
google?.sku &&
|
|
1407
|
+
google.accessToken &&
|
|
1408
|
+
google.packageName &&
|
|
1409
|
+
google.purchaseToken
|
|
1410
|
+
? {
|
|
1411
|
+
sku: google.sku,
|
|
1412
|
+
accessToken: google.accessToken,
|
|
1413
|
+
packageName: google.packageName,
|
|
1414
|
+
purchaseToken: google.purchaseToken,
|
|
1415
|
+
isSub: google.isSub == null ? undefined : Boolean(google.isSub),
|
|
1416
|
+
}
|
|
1417
|
+
: null,
|
|
1418
|
+
horizon:
|
|
1419
|
+
horizon?.sku && horizon.userId && horizon.accessToken
|
|
1420
|
+
? {
|
|
1421
|
+
sku: horizon.sku,
|
|
1422
|
+
userId: horizon.userId,
|
|
1423
|
+
accessToken: horizon.accessToken,
|
|
1424
|
+
}
|
|
1425
|
+
: null,
|
|
1361
1426
|
};
|
|
1362
1427
|
|
|
1363
1428
|
const nitroResult = await IAP.instance.validateReceipt(params);
|
|
@@ -2153,6 +2218,224 @@ export const createAlternativeBillingTokenAndroid: MutationField<
|
|
|
2153
2218
|
}
|
|
2154
2219
|
};
|
|
2155
2220
|
|
|
2221
|
+
// ------------------------------
|
|
2222
|
+
// Billing Programs API (Android 8.2.0+)
|
|
2223
|
+
// ------------------------------
|
|
2224
|
+
|
|
2225
|
+
/**
|
|
2226
|
+
* Billing program type for external content links and external offers.
|
|
2227
|
+
* @platform Android
|
|
2228
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2229
|
+
*/
|
|
2230
|
+
export type BillingProgramAndroid =
|
|
2231
|
+
| 'unspecified'
|
|
2232
|
+
| 'external-content-link'
|
|
2233
|
+
| 'external-offer';
|
|
2234
|
+
|
|
2235
|
+
/**
|
|
2236
|
+
* Launch mode for external link flow.
|
|
2237
|
+
* @platform Android
|
|
2238
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2239
|
+
*/
|
|
2240
|
+
export type ExternalLinkLaunchModeAndroid =
|
|
2241
|
+
| 'unspecified'
|
|
2242
|
+
| 'launch-in-external-browser-or-app'
|
|
2243
|
+
| 'caller-will-launch-link';
|
|
2244
|
+
|
|
2245
|
+
/**
|
|
2246
|
+
* Link type for external link flow.
|
|
2247
|
+
* @platform Android
|
|
2248
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2249
|
+
*/
|
|
2250
|
+
export type ExternalLinkTypeAndroid =
|
|
2251
|
+
| 'unspecified'
|
|
2252
|
+
| 'link-to-digital-content-offer'
|
|
2253
|
+
| 'link-to-app-download';
|
|
2254
|
+
|
|
2255
|
+
/**
|
|
2256
|
+
* Parameters for launching an external link (Android 8.2.0+).
|
|
2257
|
+
*/
|
|
2258
|
+
export interface LaunchExternalLinkParamsAndroid {
|
|
2259
|
+
/** The billing program (external-content-link or external-offer) */
|
|
2260
|
+
billingProgram: BillingProgramAndroid;
|
|
2261
|
+
/** The external link launch mode */
|
|
2262
|
+
launchMode: ExternalLinkLaunchModeAndroid;
|
|
2263
|
+
/** The type of the external link */
|
|
2264
|
+
linkType: ExternalLinkTypeAndroid;
|
|
2265
|
+
/** The URI where the content will be accessed from */
|
|
2266
|
+
linkUri: string;
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
/**
|
|
2270
|
+
* Result of checking billing program availability (Android 8.2.0+).
|
|
2271
|
+
*/
|
|
2272
|
+
export interface BillingProgramAvailabilityResultAndroid {
|
|
2273
|
+
/** The billing program that was checked */
|
|
2274
|
+
billingProgram: BillingProgramAndroid;
|
|
2275
|
+
/** Whether the billing program is available for the user */
|
|
2276
|
+
isAvailable: boolean;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
/**
|
|
2280
|
+
* Reporting details for external transactions (Android 8.2.0+).
|
|
2281
|
+
*/
|
|
2282
|
+
export interface BillingProgramReportingDetailsAndroid {
|
|
2283
|
+
/** The billing program that the reporting details are associated with */
|
|
2284
|
+
billingProgram: BillingProgramAndroid;
|
|
2285
|
+
/** External transaction token used to report transactions to Google */
|
|
2286
|
+
externalTransactionToken: string;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
/**
|
|
2290
|
+
* Enable a billing program before initConnection (Android only).
|
|
2291
|
+
* Must be called BEFORE initConnection() to configure the BillingClient.
|
|
2292
|
+
*
|
|
2293
|
+
* @param program - The billing program to enable (external-content-link or external-offer)
|
|
2294
|
+
* @platform Android
|
|
2295
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2296
|
+
*
|
|
2297
|
+
* @example
|
|
2298
|
+
* ```typescript
|
|
2299
|
+
* // Enable external offers before connecting
|
|
2300
|
+
* enableBillingProgramAndroid('external-offer');
|
|
2301
|
+
* await initConnection();
|
|
2302
|
+
* ```
|
|
2303
|
+
*/
|
|
2304
|
+
export const enableBillingProgramAndroid = (
|
|
2305
|
+
program: BillingProgramAndroid,
|
|
2306
|
+
): void => {
|
|
2307
|
+
if (Platform.OS !== 'android') {
|
|
2308
|
+
RnIapConsole.warn(
|
|
2309
|
+
'enableBillingProgramAndroid is only supported on Android',
|
|
2310
|
+
);
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
try {
|
|
2314
|
+
IAP.instance.enableBillingProgramAndroid(program as any);
|
|
2315
|
+
} catch (error) {
|
|
2316
|
+
RnIapConsole.error('Failed to enable billing program:', error);
|
|
2317
|
+
}
|
|
2318
|
+
};
|
|
2319
|
+
|
|
2320
|
+
/**
|
|
2321
|
+
* Check if a billing program is available for this user/device (Android only).
|
|
2322
|
+
*
|
|
2323
|
+
* @param program - The billing program to check
|
|
2324
|
+
* @returns Promise with availability result
|
|
2325
|
+
* @platform Android
|
|
2326
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2327
|
+
*
|
|
2328
|
+
* @example
|
|
2329
|
+
* ```typescript
|
|
2330
|
+
* const result = await isBillingProgramAvailableAndroid('external-offer');
|
|
2331
|
+
* if (result.isAvailable) {
|
|
2332
|
+
* // External offers are available for this user
|
|
2333
|
+
* }
|
|
2334
|
+
* ```
|
|
2335
|
+
*/
|
|
2336
|
+
export const isBillingProgramAvailableAndroid = async (
|
|
2337
|
+
program: BillingProgramAndroid,
|
|
2338
|
+
): Promise<BillingProgramAvailabilityResultAndroid> => {
|
|
2339
|
+
if (Platform.OS !== 'android') {
|
|
2340
|
+
throw new Error('Billing Programs API is only supported on Android');
|
|
2341
|
+
}
|
|
2342
|
+
try {
|
|
2343
|
+
const result = await IAP.instance.isBillingProgramAvailableAndroid(
|
|
2344
|
+
program as any,
|
|
2345
|
+
);
|
|
2346
|
+
return {
|
|
2347
|
+
billingProgram: result.billingProgram as unknown as BillingProgramAndroid,
|
|
2348
|
+
isAvailable: result.isAvailable,
|
|
2349
|
+
};
|
|
2350
|
+
} catch (error) {
|
|
2351
|
+
RnIapConsole.error('Failed to check billing program availability:', error);
|
|
2352
|
+
throw error;
|
|
2353
|
+
}
|
|
2354
|
+
};
|
|
2355
|
+
|
|
2356
|
+
/**
|
|
2357
|
+
* Create billing program reporting details for external transactions (Android only).
|
|
2358
|
+
* Used to get the external transaction token needed for reporting to Google.
|
|
2359
|
+
*
|
|
2360
|
+
* @param program - The billing program to create reporting details for
|
|
2361
|
+
* @returns Promise with reporting details including external transaction token
|
|
2362
|
+
* @platform Android
|
|
2363
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2364
|
+
*
|
|
2365
|
+
* @example
|
|
2366
|
+
* ```typescript
|
|
2367
|
+
* const details = await createBillingProgramReportingDetailsAndroid('external-offer');
|
|
2368
|
+
* // Use details.externalTransactionToken to report the transaction
|
|
2369
|
+
* await fetch('/api/report-external-transaction', {
|
|
2370
|
+
* method: 'POST',
|
|
2371
|
+
* body: JSON.stringify({ token: details.externalTransactionToken })
|
|
2372
|
+
* });
|
|
2373
|
+
* ```
|
|
2374
|
+
*/
|
|
2375
|
+
export const createBillingProgramReportingDetailsAndroid = async (
|
|
2376
|
+
program: BillingProgramAndroid,
|
|
2377
|
+
): Promise<BillingProgramReportingDetailsAndroid> => {
|
|
2378
|
+
if (Platform.OS !== 'android') {
|
|
2379
|
+
throw new Error('Billing Programs API is only supported on Android');
|
|
2380
|
+
}
|
|
2381
|
+
try {
|
|
2382
|
+
const result =
|
|
2383
|
+
await IAP.instance.createBillingProgramReportingDetailsAndroid(
|
|
2384
|
+
program as any,
|
|
2385
|
+
);
|
|
2386
|
+
return {
|
|
2387
|
+
billingProgram: result.billingProgram as unknown as BillingProgramAndroid,
|
|
2388
|
+
externalTransactionToken: result.externalTransactionToken,
|
|
2389
|
+
};
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
RnIapConsole.error(
|
|
2392
|
+
'Failed to create billing program reporting details:',
|
|
2393
|
+
error,
|
|
2394
|
+
);
|
|
2395
|
+
throw error;
|
|
2396
|
+
}
|
|
2397
|
+
};
|
|
2398
|
+
|
|
2399
|
+
/**
|
|
2400
|
+
* Launch external link for external offers or app download (Android only).
|
|
2401
|
+
*
|
|
2402
|
+
* @param params - Parameters for launching the external link
|
|
2403
|
+
* @returns Promise<boolean> - true if user accepted, false otherwise
|
|
2404
|
+
* @platform Android
|
|
2405
|
+
* @since Google Play Billing Library 8.2.0+
|
|
2406
|
+
*
|
|
2407
|
+
* @example
|
|
2408
|
+
* ```typescript
|
|
2409
|
+
* const success = await launchExternalLinkAndroid({
|
|
2410
|
+
* billingProgram: 'external-offer',
|
|
2411
|
+
* launchMode: 'launch-in-external-browser-or-app',
|
|
2412
|
+
* linkType: 'link-to-digital-content-offer',
|
|
2413
|
+
* linkUri: 'https://your-website.com/purchase'
|
|
2414
|
+
* });
|
|
2415
|
+
* if (success) {
|
|
2416
|
+
* console.log('User accepted external link');
|
|
2417
|
+
* }
|
|
2418
|
+
* ```
|
|
2419
|
+
*/
|
|
2420
|
+
export const launchExternalLinkAndroid = async (
|
|
2421
|
+
params: LaunchExternalLinkParamsAndroid,
|
|
2422
|
+
): Promise<boolean> => {
|
|
2423
|
+
if (Platform.OS !== 'android') {
|
|
2424
|
+
throw new Error('Billing Programs API is only supported on Android');
|
|
2425
|
+
}
|
|
2426
|
+
try {
|
|
2427
|
+
return await IAP.instance.launchExternalLinkAndroid({
|
|
2428
|
+
billingProgram: params.billingProgram as any,
|
|
2429
|
+
launchMode: params.launchMode as any,
|
|
2430
|
+
linkType: params.linkType as any,
|
|
2431
|
+
linkUri: params.linkUri,
|
|
2432
|
+
});
|
|
2433
|
+
} catch (error) {
|
|
2434
|
+
RnIapConsole.error('Failed to launch external link:', error);
|
|
2435
|
+
throw error;
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
|
|
2156
2439
|
// ------------------------------
|
|
2157
2440
|
// iOS External Purchase
|
|
2158
2441
|
// ------------------------------
|