react-native-iap 15.3.6 → 15.4.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 (83) hide show
  1. package/README.md +1 -1
  2. package/android/build.gradle +13 -5
  3. package/android/src/main/java/com/margelo/nitro/iap/HybridRnIap.kt +19 -6
  4. package/ios/HybridRnIap.swift +13 -1
  5. package/lib/module/index.js +56 -16
  6. package/lib/module/index.js.map +1 -1
  7. package/lib/module/index.kepler.js +170 -0
  8. package/lib/module/index.kepler.js.map +1 -0
  9. package/lib/module/types/amazon-devices-kepler/index.d.js +2 -0
  10. package/lib/module/types/amazon-devices-kepler/index.d.js.map +1 -0
  11. package/lib/module/types.js +5 -2
  12. package/lib/module/types.js.map +1 -1
  13. package/lib/module/utils/error.js +2 -2
  14. package/lib/module/utils/error.js.map +1 -1
  15. package/lib/module/utils/type-bridge.js +4 -1
  16. package/lib/module/utils/type-bridge.js.map +1 -1
  17. package/lib/module/vega-adapter.js +981 -0
  18. package/lib/module/vega-adapter.js.map +1 -0
  19. package/lib/module/vega.js +10 -0
  20. package/lib/module/vega.js.map +1 -0
  21. package/lib/module/vega.kepler.js +18 -0
  22. package/lib/module/vega.kepler.js.map +1 -0
  23. package/lib/typescript/src/index.d.ts +9 -2
  24. package/lib/typescript/src/index.d.ts.map +1 -1
  25. package/lib/typescript/src/index.kepler.d.ts +55 -0
  26. package/lib/typescript/src/index.kepler.d.ts.map +1 -0
  27. package/lib/typescript/src/specs/RnIap.nitro.d.ts +10 -1
  28. package/lib/typescript/src/specs/RnIap.nitro.d.ts.map +1 -1
  29. package/lib/typescript/src/types.d.ts +16 -3
  30. package/lib/typescript/src/types.d.ts.map +1 -1
  31. package/lib/typescript/src/utils/type-bridge.d.ts.map +1 -1
  32. package/lib/typescript/src/vega-adapter.d.ts +76 -0
  33. package/lib/typescript/src/vega-adapter.d.ts.map +1 -0
  34. package/lib/typescript/src/vega.d.ts +4 -0
  35. package/lib/typescript/src/vega.d.ts.map +1 -0
  36. package/lib/typescript/src/vega.kepler.d.ts +4 -0
  37. package/lib/typescript/src/vega.kepler.d.ts.map +1 -0
  38. package/nitrogen/generated/android/NitroIap+autolinking.cmake +1 -0
  39. package/nitrogen/generated/android/c++/JHybridRnIapSpec.cpp +5 -0
  40. package/nitrogen/generated/android/c++/JIapStore.hpp +3 -0
  41. package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitAmazonProps.hpp +71 -0
  42. package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithIapkitProps.hpp +9 -1
  43. package/nitrogen/generated/android/c++/JNitroVerifyPurchaseWithProviderProps.hpp +4 -0
  44. package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.cpp +26 -0
  45. package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.hpp +75 -0
  46. package/nitrogen/generated/android/c++/JVariant_NullType_NitroVerifyPurchaseWithIapkitProps.hpp +4 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/IapStore.kt +2 -1
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitAmazonProps.kt +44 -0
  49. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/NitroVerifyPurchaseWithIapkitProps.kt +5 -2
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/iap/Variant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.kt +53 -0
  51. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Bridge.hpp +47 -0
  52. package/nitrogen/generated/ios/NitroIap-Swift-Cxx-Umbrella.hpp +3 -0
  53. package/nitrogen/generated/ios/c++/HybridRnIapSpecSwift.hpp +3 -0
  54. package/nitrogen/generated/ios/swift/IapStore.swift +4 -0
  55. package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitAmazonProps.swift +103 -0
  56. package/nitrogen/generated/ios/swift/NitroVerifyPurchaseWithIapkitProps.swift +38 -1
  57. package/nitrogen/generated/ios/swift/Variant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.swift +18 -0
  58. package/nitrogen/generated/shared/c++/IapStore.hpp +4 -0
  59. package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitAmazonProps.hpp +94 -0
  60. package/nitrogen/generated/shared/c++/NitroVerifyPurchaseWithIapkitProps.hpp +8 -1
  61. package/openiap-versions.json +2 -2
  62. package/package.json +19 -12
  63. package/src/index.kepler.ts +311 -0
  64. package/src/index.ts +65 -19
  65. package/src/specs/RnIap.nitro.ts +13 -2
  66. package/src/types/amazon-devices-kepler/index.d.ts +14 -0
  67. package/src/types.ts +17 -3
  68. package/src/utils/error.ts +2 -2
  69. package/src/utils/type-bridge.ts +4 -1
  70. package/src/vega-adapter.ts +1503 -0
  71. package/src/vega.kepler.ts +21 -0
  72. package/src/vega.ts +10 -0
  73. package/android/src/test/java/com/margelo/nitro/iap/ListenerThreadSafetyTest.kt +0 -154
  74. package/android/src/test/java/com/margelo/nitro/iap/OpenIapExceptionTest.kt +0 -93
  75. package/android/src/test/java/com/margelo/nitro/iap/ProductQueryHelpersTest.kt +0 -140
  76. package/app.plugin.js +0 -1
  77. package/lib/typescript/plugin/src/withIAP.d.ts +0 -48
  78. package/lib/typescript/plugin/src/withIAP.d.ts.map +0 -1
  79. package/plugin/build/withIAP.d.ts +0 -47
  80. package/plugin/build/withIAP.js +0 -314
  81. package/plugin/src/withIAP.ts +0 -500
  82. package/plugin/tsconfig.json +0 -18
  83. package/plugin/tsconfig.tsbuildinfo +0 -1
package/README.md CHANGED
@@ -105,7 +105,7 @@ Before installing React Native IAP, make sure you have:
105
105
 
106
106
  #### Android Configuration
107
107
 
108
- **Kotlin Version Requirement:** This library requires Kotlin 2.0+. Configure your project's Kotlin version:
108
+ **Kotlin Version Requirement:** This library requires Kotlin 2.2+. Configure your project's Kotlin version:
109
109
 
110
110
  In your root `android/build.gradle`:
111
111
 
@@ -107,8 +107,13 @@ def getExtOrIntegerDefault(name) {
107
107
  return getExtOrDefault(name).toString().toInteger()
108
108
  }
109
109
 
110
- // Read horizonEnabled from gradle.properties, default to false (play)
110
+ // Read store flags from gradle.properties, default to play
111
111
  def horizonEnabled = project.findProperty('horizonEnabled')?.toBoolean() ?: false
112
+ def fireOsEnabled = project.findProperty('fireOsEnabled')?.toBoolean() ?: false
113
+ if (horizonEnabled && fireOsEnabled) {
114
+ throw new GradleException("react-native-iap: horizonEnabled and fireOsEnabled cannot both be true")
115
+ }
116
+ def openiapFlavor = fireOsEnabled ? 'amazon' : (horizonEnabled ? 'horizon' : 'play')
112
117
 
113
118
  def resolveOpenIapGoogleBuildFile() {
114
119
  def candidates = [
@@ -155,9 +160,8 @@ android {
155
160
  // Ship consumer keep rules so Nitro HybridObjects aren't stripped in app release builds
156
161
  consumerProguardFiles 'consumer-rules.pro'
157
162
 
158
- // Use horizonEnabled to determine platform flavor
159
- def flavor = horizonEnabled ? 'horizon' : 'play'
160
- missingDimensionStrategy "platform", flavor
163
+ // Use explicit store flags to determine platform flavor
164
+ missingDimensionStrategy "platform", openiapFlavor
161
165
 
162
166
  externalNativeBuild {
163
167
  cmake {
@@ -251,7 +255,9 @@ dependencies {
251
255
  }
252
256
 
253
257
  // Google Play Services
254
- implementation "com.google.android.gms:play-services-base:$playServicesBaseVersion"
258
+ if (!fireOsEnabled && !horizonEnabled) {
259
+ implementation "com.google.android.gms:play-services-base:$playServicesBaseVersion"
260
+ }
255
261
 
256
262
  // Kotlin coroutines
257
263
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
@@ -261,6 +267,8 @@ dependencies {
261
267
  def localGoogleProject = findProject(':openiap')
262
268
  if (localGoogleProject != null) {
263
269
  implementation project(':openiap')
270
+ } else if (fireOsEnabled) {
271
+ implementation "io.github.hyochan.openiap:openiap-google-amazon:${googleVersionString}"
264
272
  } else if (horizonEnabled) {
265
273
  implementation "io.github.hyochan.openiap:openiap-google-horizon:${googleVersionString}"
266
274
  } else {
@@ -43,13 +43,13 @@ import dev.hyo.openiap.ExternalLinkLaunchModeAndroid as OpenIapExternalLinkLaunc
43
43
  import dev.hyo.openiap.ExternalLinkTypeAndroid as OpenIapExternalLinkType
44
44
  import dev.hyo.openiap.listener.OpenIapDeveloperProvidedBillingListener
45
45
  import dev.hyo.openiap.store.OpenIapStore
46
+ import java.util.Locale
46
47
  import kotlin.coroutines.cancellation.CancellationException
47
48
  import kotlinx.coroutines.Dispatchers
48
49
  import kotlinx.coroutines.withContext
49
50
  import kotlinx.coroutines.CompletableDeferred
50
51
  import org.json.JSONArray
51
52
  import org.json.JSONObject
52
- import java.util.Locale
53
53
 
54
54
  /**
55
55
  * Custom exception for OpenIAP errors that only includes the error JSON without stack traces.
@@ -575,7 +575,7 @@ class HybridRnIap : HybridRnIapSpec() {
575
575
  mapOf("type" to androidOptions?.type?.name, "includeSuspended" to includeSuspended)
576
576
  )
577
577
 
578
- val typeName = androidOptions?.type?.name?.lowercase()
578
+ val typeName = androidOptions?.type?.name?.lowercase(java.util.Locale.ROOT)
579
579
  val normalizedType = when (typeName) {
580
580
  "inapp" -> {
581
581
  RnIapLog.warn("getAvailablePurchases received legacy type 'inapp'; forwarding as 'in-app'")
@@ -1279,6 +1279,7 @@ class HybridRnIap : HybridRnIapSpec() {
1279
1279
  dev.hyo.openiap.IapStore.Apple -> IapStore.APPLE
1280
1280
  dev.hyo.openiap.IapStore.Google -> IapStore.GOOGLE
1281
1281
  dev.hyo.openiap.IapStore.Horizon -> IapStore.HORIZON
1282
+ dev.hyo.openiap.IapStore.Amazon -> IapStore.AMAZON
1282
1283
  dev.hyo.openiap.IapStore.Unknown -> IapStore.UNKNOWN
1283
1284
  }
1284
1285
  }
@@ -1469,19 +1470,27 @@ class HybridRnIap : HybridRnIapSpec() {
1469
1470
  return Promise.async {
1470
1471
  try {
1471
1472
  // Convert Nitro enum to string (e.g., IAPKIT -> "iapkit")
1472
- val providerString = params.provider.name.lowercase()
1473
+ val providerString = params.provider.name.lowercase(java.util.Locale.ROOT)
1473
1474
  RnIapLog.payload("verifyPurchaseWithProvider", mapOf("provider" to providerString))
1474
1475
 
1475
1476
  // Build the props map for OpenIAP - use string value for provider
1476
1477
  val propsMap = mutableMapOf<String, Any?>("provider" to providerString)
1477
1478
  (params.iapkit as? Variant_NullType_NitroVerifyPurchaseWithIapkitProps.Second)?.value?.let { iapkit ->
1478
1479
  val iapkitMap = mutableMapOf<String, Any?>()
1479
- // Use provided apiKey, or fallback to AndroidManifest meta-data (set by config plugin)
1480
+ // Use provided apiKey, or fallback to host app AndroidManifest meta-data.
1480
1481
  val apiKey = iapkit.apiKey.unwrapString() ?: getIapkitApiKeyFromManifest()
1481
1482
  apiKey?.let { iapkitMap["apiKey"] = it }
1482
1483
  (iapkit.google as? Variant_NullType_NitroVerifyPurchaseWithIapkitGoogleProps.Second)?.value?.let { google ->
1483
1484
  iapkitMap["google"] = mapOf("purchaseToken" to google.purchaseToken)
1484
1485
  }
1486
+ (iapkit.amazon as? Variant_NullType_NitroVerifyPurchaseWithIapkitAmazonProps.Second)?.value?.let { amazon ->
1487
+ val amazonMap = mutableMapOf<String, Any?>(
1488
+ "receiptId" to amazon.receiptId
1489
+ )
1490
+ amazon.userId.unwrapString()?.let { amazonMap["userId"] = it }
1491
+ amazon.sandbox.unwrapBool()?.let { amazonMap["sandbox"] = it }
1492
+ iapkitMap["amazon"] = amazonMap
1493
+ }
1485
1494
  (iapkit.apple as? Variant_NullType_NitroVerifyPurchaseWithIapkitAppleProps.Second)?.value?.let { apple ->
1486
1495
  iapkitMap["apple"] = mapOf("jws" to apple.jws)
1487
1496
  }
@@ -1609,6 +1618,7 @@ class HybridRnIap : HybridRnIapSpec() {
1609
1618
  // Alternative Billing (Android)
1610
1619
  // -------------------------------------------------------------------------
1611
1620
 
1621
+ @Suppress("DEPRECATION")
1612
1622
  override fun checkAlternativeBillingAvailabilityAndroid(): Promise<Boolean> {
1613
1623
  return Promise.async {
1614
1624
  RnIapLog.payload("checkAlternativeBillingAvailabilityAndroid", null)
@@ -1626,6 +1636,7 @@ class HybridRnIap : HybridRnIapSpec() {
1626
1636
  }
1627
1637
  }
1628
1638
 
1639
+ @Suppress("DEPRECATION")
1629
1640
  override fun showAlternativeBillingDialogAndroid(): Promise<Boolean> {
1630
1641
  return Promise.async {
1631
1642
  RnIapLog.payload("showAlternativeBillingDialogAndroid", null)
@@ -1647,6 +1658,7 @@ class HybridRnIap : HybridRnIapSpec() {
1647
1658
  }
1648
1659
  }
1649
1660
 
1661
+ @Suppress("DEPRECATION")
1650
1662
  override fun createAlternativeBillingTokenAndroid(sku: Variant_NullType_String?): Promise<Variant_NullType_String> {
1651
1663
  return Promise.async {
1652
1664
  val skuValue = sku.unwrapString()
@@ -2000,6 +2012,7 @@ class HybridRnIap : HybridRnIapSpec() {
2000
2012
  "APPLE" -> IapStore.APPLE
2001
2013
  "GOOGLE" -> IapStore.GOOGLE
2002
2014
  "HORIZON" -> IapStore.HORIZON
2015
+ "AMAZON" -> IapStore.AMAZON
2003
2016
  else -> IapStore.UNKNOWN
2004
2017
  }
2005
2018
  }
@@ -2012,8 +2025,8 @@ class HybridRnIap : HybridRnIapSpec() {
2012
2025
  }
2013
2026
 
2014
2027
  /**
2015
- * Read IAPKit API key from AndroidManifest.xml meta-data (set by config plugin).
2016
- * Config plugin sets: <meta-data android:name="dev.iapkit.API_KEY" android:value="..." />
2028
+ * Read IAPKit API key from AndroidManifest.xml meta-data.
2029
+ * Host app sets: <meta-data android:name="dev.iapkit.API_KEY" android:value="..." />
2017
2030
  */
2018
2031
  private fun getIapkitApiKeyFromManifest(): String? {
2019
2032
  return try {
@@ -434,7 +434,7 @@ class HybridRnIap: HybridRnIapSpec {
434
434
  var propsDict: [String: Any] = ["provider": params.provider.stringValue]
435
435
  if case .second(let iapkit) = params.iapkit {
436
436
  var iapkitDict: [String: Any] = [:]
437
- // Use provided apiKey, or fallback to Info.plist IAPKitAPIKey (set by config plugin)
437
+ // Use provided apiKey, or fallback to the host app's Info.plist IAPKitAPIKey.
438
438
  if case .second(let apiKey) = iapkit.apiKey {
439
439
  iapkitDict["apiKey"] = apiKey
440
440
  } else if let plistApiKey = Bundle.main.object(forInfoDictionaryKey: "IAPKitAPIKey") as? String {
@@ -446,6 +446,18 @@ class HybridRnIap: HybridRnIapSpec {
446
446
  if case .second(let google) = iapkit.google {
447
447
  iapkitDict["google"] = ["purchaseToken": google.purchaseToken]
448
448
  }
449
+ if case .second(let amazon) = iapkit.amazon {
450
+ var amazonDict: [String: Any] = [
451
+ "receiptId": amazon.receiptId
452
+ ]
453
+ if case .second(let sandbox) = amazon.sandbox {
454
+ amazonDict["sandbox"] = sandbox
455
+ }
456
+ if case .second(let userId) = amazon.userId {
457
+ amazonDict["userId"] = userId
458
+ }
459
+ iapkitDict["amazon"] = amazonDict
460
+ }
449
461
  propsDict["iapkit"] = iapkitDict
450
462
  }
451
463
  // Use JSONSerialization + JSONDecoder like expo-iap does
@@ -15,6 +15,7 @@ import { normalizeErrorCodeFromNative, createPurchaseError, DUPLICATE_PURCHASE_C
15
15
  import { RnIapConsole } from "./utils/debug.js";
16
16
  import { getSuccessFromPurchaseVariant } from "./utils/purchase.js";
17
17
  import { parseAppTransactionPayload } from "./utils.js";
18
+ import { getVegaIapModule, isVegaOS } from "./vega.js";
18
19
 
19
20
  // ------------------------------
20
21
  // Billing Programs API (Android 8.2.0+)
@@ -28,6 +29,7 @@ import { parseAppTransactionPayload } from "./utils.js";
28
29
 
29
30
  export * from "./types.js";
30
31
  export * from "./utils/error.js";
32
+ export * from "./vega.js";
31
33
  const LEGACY_INAPP_WARNING = "[react-native-iap] `type: 'inapp'` is deprecated and will be removed in a future major version. Use 'in-app' instead.";
32
34
  const toErrorMessage = error => {
33
35
  if (typeof error === 'object' && error !== null && 'message' in error && error.message != null) {
@@ -63,6 +65,10 @@ let iapRef = null;
63
65
  */
64
66
  export const isNitroReady = () => {
65
67
  if (iapRef) return true;
68
+ if (isVegaOS()) {
69
+ iapRef = getVegaIapModule();
70
+ return Boolean(iapRef);
71
+ }
66
72
  try {
67
73
  iapRef = NitroModules.createHybridObject('RnIap');
68
74
  return true;
@@ -93,9 +99,20 @@ export const isMacOS = () => {
93
99
  export const isStandardIOS = () => {
94
100
  return Platform.OS === 'ios' && !isTVOS() && !isMacOS();
95
101
  };
102
+ const isAndroidStoreRuntime = () => {
103
+ return Platform.OS === 'android' || isVegaOS();
104
+ };
96
105
  const IAP = {
97
106
  get instance() {
98
107
  if (iapRef) return iapRef;
108
+ if (isVegaOS()) {
109
+ const vegaModule = getVegaIapModule();
110
+ if (!vegaModule) {
111
+ throw new Error('Amazon Vega IAP module is unavailable. Install @amazon-devices/keplerscript-appstore-iap-lib in the Vega app target and build with the React Native for Vega kepler platform.');
112
+ }
113
+ iapRef = vegaModule;
114
+ return iapRef;
115
+ }
99
116
 
100
117
  // Attempt to create the HybridObject and map common Nitro/JSI readiness errors
101
118
  try {
@@ -507,7 +524,7 @@ function tryAttachSubscriptionBillingIssueNative() {
507
524
  } catch (e) {
508
525
  const msg = toErrorMessage(e);
509
526
  if (msg.includes('Nitro runtime not installed')) {
510
- RnIapConsole.warn('[subscriptionBillingIssueListener] Nitro not ready yet; will retry on next registration after initConnection()');
527
+ RnIapConsole.warn('[subscriptionBillingIssueListener] Nitro not ready yet; will retry after initConnection()');
511
528
  } else {
512
529
  throw e;
513
530
  }
@@ -597,7 +614,7 @@ export const fetchProducts = async request => {
597
614
 
598
615
  // item.type === 'subs' case
599
616
  // For Android, check if subscription items have actual offers
600
- if (Platform.OS === 'android' && item.platform === 'android' && item.type === 'subs') {
617
+ if (isAndroidStoreRuntime() && item.platform === 'android' && item.type === 'subs') {
601
618
  // TypeScript now knows this is ProductSubscriptionAndroid
602
619
  const hasSubscriptionOffers = item.subscriptionOfferDetailsAndroid && Array.isArray(item.subscriptionOfferDetailsAndroid) && item.subscriptionOfferDetailsAndroid.length > 0;
603
620
  RnIapConsole.debug(`[fetchProducts] ${item.id}: type=${item.type}, hasOffers=${hasSubscriptionOffers}`);
@@ -683,9 +700,22 @@ export const getAvailablePurchases = async options => {
683
700
  RnIapConsole.warn(`[getAvailablePurchases] Some purchases failed validation: ${nitroPurchases.length - validPurchases.length} invalid`);
684
701
  }
685
702
  return validPurchases.map(convertNitroPurchaseToPurchase);
686
- } else if (Platform.OS === 'android') {
687
- // For Android, we need to call twice for inapp and subs
703
+ } else if (isAndroidStoreRuntime()) {
688
704
  const includeSuspended = Boolean(options?.includeSuspendedAndroid ?? false);
705
+ if (isVegaOS()) {
706
+ const nitroPurchases = await IAP.instance.getAvailablePurchases({
707
+ android: {
708
+ includeSuspended
709
+ }
710
+ });
711
+ const validPurchases = nitroPurchases.filter(validateNitroPurchase);
712
+ if (validPurchases.length !== nitroPurchases.length) {
713
+ RnIapConsole.warn(`[getAvailablePurchases] Some Vega purchases failed validation: ${nitroPurchases.length - validPurchases.length} invalid`);
714
+ }
715
+ return validPurchases.map(convertNitroPurchaseToPurchase);
716
+ }
717
+
718
+ // For Android Play/Horizon/Fire OS, query in-app items and subscriptions separately.
689
719
  const inappNitroPurchases = await IAP.instance.getAvailablePurchases({
690
720
  android: {
691
721
  type: 'inapp',
@@ -777,8 +807,8 @@ export const getStorefrontIOS = async () => {
777
807
  * @see {@link https://openiap.dev/docs/apis/get-storefront}
778
808
  */
779
809
  export const getStorefront = async () => {
780
- if (Platform.OS !== 'ios' && Platform.OS !== 'android') {
781
- RnIapConsole.warn('[getStorefront] Storefront lookup is only supported on iOS and Android.');
810
+ if (Platform.OS !== 'ios' && !isAndroidStoreRuntime()) {
811
+ RnIapConsole.warn('[getStorefront] Storefront lookup is only supported on iOS, Android, and Vega OS.');
782
812
  return '';
783
813
  }
784
814
  const hasUnifiedMethod = typeof IAP.instance.getStorefront === 'function';
@@ -1171,7 +1201,11 @@ export const getTransactionJwsIOS = async sku => {
1171
1201
  */
1172
1202
  export const initConnection = async config => {
1173
1203
  try {
1174
- return await IAP.instance.initConnection(config);
1204
+ const result = await IAP.instance.initConnection(config);
1205
+ if (subscriptionBillingIssueJsListeners.size > 0) {
1206
+ tryAttachSubscriptionBillingIssueNative();
1207
+ }
1208
+ return result;
1175
1209
  } catch (error) {
1176
1210
  const parsedError = parseErrorAndLogIfNeeded('Failed to initialize IAP connection:', error);
1177
1211
  throw createPurchaseError({
@@ -1274,7 +1308,7 @@ export const requestPurchase = async request => {
1274
1308
  if (!iosRequest?.sku) {
1275
1309
  throw new Error('Invalid request for iOS. The `sku` property is required.');
1276
1310
  }
1277
- } else if (Platform.OS === 'android') {
1311
+ } else if (isAndroidStoreRuntime()) {
1278
1312
  // Support both 'google' (recommended) and 'android' (deprecated) fields
1279
1313
  const androidRequest = perPlatformRequest.google ?? perPlatformRequest.android;
1280
1314
  if (!androidRequest?.skus?.length) {
@@ -1328,7 +1362,7 @@ export const requestPurchase = async request => {
1328
1362
 
1329
1363
  // Support both 'google' (recommended) and 'android' (deprecated) fields
1330
1364
  const androidRequestSource = perPlatformRequest.google ?? perPlatformRequest.android;
1331
- if (Platform.OS === 'android' && androidRequestSource) {
1365
+ if (isAndroidStoreRuntime() && androidRequestSource) {
1332
1366
  const androidRequest = isSubs ? androidRequestSource : androidRequestSource;
1333
1367
  const androidPayload = {
1334
1368
  skus: androidRequest.skus
@@ -1421,7 +1455,7 @@ export const finishTransaction = async args => {
1421
1455
  transactionId: purchase.id
1422
1456
  }
1423
1457
  };
1424
- } else if (Platform.OS === 'android') {
1458
+ } else if (isAndroidStoreRuntime()) {
1425
1459
  const token = purchase.purchaseToken ?? undefined;
1426
1460
  if (!token) {
1427
1461
  throw new Error('purchaseToken required to finish Android transaction');
@@ -1471,8 +1505,8 @@ export const finishTransaction = async args => {
1471
1505
  */
1472
1506
  export const acknowledgePurchaseAndroid = async purchaseToken => {
1473
1507
  try {
1474
- if (Platform.OS !== 'android') {
1475
- throw new Error('acknowledgePurchaseAndroid is only available on Android');
1508
+ if (!isAndroidStoreRuntime()) {
1509
+ throw new Error('acknowledgePurchaseAndroid is only available on Android and Vega OS');
1476
1510
  }
1477
1511
  const result = await IAP.instance.finishTransaction({
1478
1512
  android: {
@@ -1506,8 +1540,8 @@ export const acknowledgePurchaseAndroid = async purchaseToken => {
1506
1540
  */
1507
1541
  export const consumePurchaseAndroid = async purchaseToken => {
1508
1542
  try {
1509
- if (Platform.OS !== 'android') {
1510
- throw new Error('consumePurchaseAndroid is only available on Android');
1543
+ if (!isAndroidStoreRuntime()) {
1544
+ throw new Error('consumePurchaseAndroid is only available on Android and Vega OS');
1511
1545
  }
1512
1546
  const result = await IAP.instance.finishTransaction({
1513
1547
  android: {
@@ -1699,8 +1733,14 @@ export const validateReceiptIOS = async options => {
1699
1733
  * provider: 'iapkit',
1700
1734
  * iapkit: {
1701
1735
  * apiKey: 'your-api-key',
1702
- * apple: { jws: purchase.purchaseToken },
1703
- * google: { purchaseToken: purchase.purchaseToken },
1736
+ * // Choose exactly one store payload.
1737
+ * // apple: { jws: purchase.purchaseToken },
1738
+ * // google: { purchaseToken: purchase.purchaseToken },
1739
+ * amazon: {
1740
+ * userId: amazonUserId,
1741
+ * receiptId: purchase.purchaseToken,
1742
+ * sandbox: __DEV__,
1743
+ * },
1704
1744
  * },
1705
1745
  * });
1706
1746
  * ```