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.
Files changed (81) hide show
  1. package/README.md +1 -0
  2. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +198 -36
  3. package/ios/HybridRnIap.swift +32 -2
  4. package/lib/module/hooks/useIAP.js +1 -6
  5. package/lib/module/hooks/useIAP.js.map +1 -1
  6. package/lib/module/index.js +228 -10
  7. package/lib/module/index.js.map +1 -1
  8. package/lib/module/types.js +116 -0
  9. package/lib/module/types.js.map +1 -1
  10. package/lib/typescript/plugin/src/withIAP.d.ts +6 -0
  11. package/lib/typescript/plugin/src/withIAP.d.ts.map +1 -1
  12. package/lib/typescript/src/hooks/useIAP.d.ts +5 -7
  13. package/lib/typescript/src/hooks/useIAP.d.ts.map +1 -1
  14. package/lib/typescript/src/index.d.ts +144 -2
  15. package/lib/typescript/src/index.d.ts.map +1 -1
  16. package/lib/typescript/src/specs/RnIap.nitro.d.ts +90 -8
  17. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  18. package/lib/typescript/src/types.d.ts +194 -10
  19. package/lib/typescript/src/types.d.ts.map +1 -1
  20. package/nitrogen/generated/android/c++/JBillingProgramAndroid.hpp +62 -0
  21. package/nitrogen/generated/android/c++/JExternalLinkLaunchModeAndroid.hpp +62 -0
  22. package/nitrogen/generated/android/c++/JExternalLinkTypeAndroid.hpp +62 -0
  23. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +88 -4
  24. package/nitrogen/generated/android/c++/JHybridRnIapSpec.hpp +4 -0
  25. package/nitrogen/generated/android/c++/JNitroBillingProgramAvailabilityResultAndroid.hpp +62 -0
  26. package/nitrogen/generated/android/c++/JNitroBillingProgramReportingDetailsAndroid.hpp +63 -0
  27. package/nitrogen/generated/android/c++/JNitroLaunchExternalLinkParamsAndroid.hpp +75 -0
  28. package/nitrogen/generated/android/c++/JNitroReceiptValidationAppleOptions.hpp +57 -0
  29. package/nitrogen/generated/android/c++/{JNitroReceiptValidationAndroidOptions.hpp → JNitroReceiptValidationGoogleOptions.hpp} +18 -14
  30. package/nitrogen/generated/android/c++/JNitroReceiptValidationHorizonOptions.hpp +65 -0
  31. package/nitrogen/generated/android/c++/JNitroReceiptValidationParams.hpp +19 -11
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/BillingProgramAndroid.kt +22 -0
  33. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalLinkLaunchModeAndroid.kt +22 -0
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/ExternalLinkTypeAndroid.kt +22 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/HybridRnIapSpec.kt +16 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroBillingProgramAvailabilityResultAndroid.kt +39 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroBillingProgramReportingDetailsAndroid.kt +39 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroLaunchExternalLinkParamsAndroid.kt +45 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationAppleOptions.kt +36 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/{NitroReceiptValidationAndroidOptions.kt → NitroReceiptValidationGoogleOptions.kt} +9 -6
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationHorizonOptions.kt +42 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroReceiptValidationParams.kt +7 -4
  43. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.cpp +16 -0
  44. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +141 -10
  45. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +27 -3
  46. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +57 -3
  47. package/nitrogen/generated/ios/swift/BillingProgramAndroid.swift +44 -0
  48. package/nitrogen/generated/ios/swift/ExternalLinkLaunchModeAndroid.swift +44 -0
  49. package/nitrogen/generated/ios/swift/ExternalLinkTypeAndroid.swift +44 -0
  50. package/nitrogen/generated/ios/swift/Func_void_NitroBillingProgramAvailabilityResultAndroid.swift +47 -0
  51. package/nitrogen/generated/ios/swift/Func_void_NitroBillingProgramReportingDetailsAndroid.swift +47 -0
  52. package/nitrogen/generated/ios/swift/HybridRnIapSpec.swift +4 -0
  53. package/nitrogen/generated/ios/swift/HybridRnIapSpec_cxx.swift +68 -0
  54. package/nitrogen/generated/ios/swift/NitroBillingProgramAvailabilityResultAndroid.swift +46 -0
  55. package/nitrogen/generated/ios/swift/NitroBillingProgramReportingDetailsAndroid.swift +46 -0
  56. package/nitrogen/generated/ios/swift/NitroLaunchExternalLinkParamsAndroid.swift +68 -0
  57. package/nitrogen/generated/ios/swift/NitroReceiptValidationAppleOptions.swift +35 -0
  58. package/nitrogen/generated/ios/swift/{NitroReceiptValidationAndroidOptions.swift → NitroReceiptValidationGoogleOptions.swift} +21 -10
  59. package/nitrogen/generated/ios/swift/NitroReceiptValidationHorizonOptions.swift +57 -0
  60. package/nitrogen/generated/ios/swift/NitroReceiptValidationParams.swift +46 -11
  61. package/nitrogen/generated/shared/c++/BillingProgramAndroid.hpp +80 -0
  62. package/nitrogen/generated/shared/c++/ExternalLinkLaunchModeAndroid.hpp +80 -0
  63. package/nitrogen/generated/shared/c++/ExternalLinkTypeAndroid.hpp +80 -0
  64. package/nitrogen/generated/shared/c++/HybridRnIapSpec.cpp +4 -0
  65. package/nitrogen/generated/shared/c++/HybridRnIapSpec.hpp +16 -0
  66. package/nitrogen/generated/shared/c++/NitroBillingProgramAvailabilityResultAndroid.hpp +80 -0
  67. package/nitrogen/generated/shared/c++/NitroBillingProgramReportingDetailsAndroid.hpp +81 -0
  68. package/nitrogen/generated/shared/c++/NitroLaunchExternalLinkParamsAndroid.hpp +95 -0
  69. package/nitrogen/generated/shared/c++/NitroReceiptValidationAppleOptions.hpp +75 -0
  70. package/nitrogen/generated/shared/c++/{NitroReceiptValidationAndroidOptions.hpp → NitroReceiptValidationGoogleOptions.hpp} +18 -14
  71. package/nitrogen/generated/shared/c++/NitroReceiptValidationHorizonOptions.hpp +83 -0
  72. package/nitrogen/generated/shared/c++/NitroReceiptValidationParams.hpp +22 -13
  73. package/openiap-versions.json +3 -3
  74. package/package.json +1 -1
  75. package/plugin/build/withIAP.d.ts +6 -0
  76. package/plugin/build/withIAP.js +46 -2
  77. package/plugin/src/withIAP.ts +67 -2
  78. package/src/hooks/useIAP.ts +8 -23
  79. package/src/index.ts +297 -14
  80. package/src/specs/RnIap.nitro.ts +125 -10
  81. 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 `NitroReceiptValidationAndroidOptions` to properly resolve imports.
27
- namespace margelo::nitro::iap { struct NitroReceiptValidationAndroidOptions; }
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 <string>
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::string sku SWIFT_PRIVATE;
41
- std::optional<NitroReceiptValidationAndroidOptions> androidOptions SWIFT_PRIVATE;
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::string sku, std::optional<NitroReceiptValidationAndroidOptions> androidOptions): sku(sku), androidOptions(androidOptions) {}
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::string>::fromJSI(runtime, obj.getProperty(runtime, "sku")),
59
- JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationAndroidOptions>>::fromJSI(runtime, obj.getProperty(runtime, "androidOptions"))
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, "sku", JSIConverter<std::string>::toJSI(runtime, arg.sku));
65
- obj.setProperty(runtime, "androidOptions", JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationAndroidOptions>>::toJSI(runtime, arg.androidOptions));
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::string>::canConvert(runtime, obj.getProperty(runtime, "sku"))) return false;
77
- if (!JSIConverter<std::optional<margelo::nitro::iap::NitroReceiptValidationAndroidOptions>>::canConvert(runtime, obj.getProperty(runtime, "androidOptions"))) return false;
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
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "apple": "1.3.0",
3
- "google": "1.3.11",
4
- "gql": "1.3.1"
2
+ "apple": "1.3.2",
3
+ "google": "1.3.14",
4
+ "gql": "1.3.4"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-iap",
3
- "version": "14.5.1-rc.1",
3
+ "version": "14.6.0-rc.2",
4
4
  "description": "React Native In-App Purchases module for iOS and Android using Nitro",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -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;
@@ -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;
@@ -125,7 +125,10 @@ const modifyAppBuildGradle = (gradle: string): string => {
125
125
  return modified;
126
126
  };
127
127
 
128
- const withIapAndroid: ConfigPlugin = (config) => {
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;
@@ -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
- sku: string,
76
- androidOptions?: {
77
- packageName: string;
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
- sku: string,
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
- * @param sku - Product SKU
1339
- * @param androidOptions - Android-specific validation options (required for Android)
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 {sku, androidOptions} = options;
1363
+ const {apple, google, horizon} = options;
1346
1364
  try {
1347
- const normalizedAndroidOptions =
1348
- androidOptions != null
1349
- ? {
1350
- ...androidOptions,
1351
- isSub:
1352
- androidOptions.isSub == null
1353
- ? undefined
1354
- : Boolean(androidOptions.isSub),
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
- : undefined;
1395
+ }
1396
+ }
1397
+ }
1357
1398
 
1358
1399
  const params: NitroReceiptValidationParams = {
1359
- sku,
1360
- androidOptions: normalizedAndroidOptions,
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
  // ------------------------------