expo-iap 5.8.3 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/android/build.gradle +8 -13
  2. package/android/openiap-store.gradle +84 -8
  3. package/android/src/main/java/expo/modules/iap/ExpoIapHelper.kt +4 -3
  4. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +82 -38
  5. package/build/ExpoIapModule.d.ts +3 -1
  6. package/build/ExpoIapModule.d.ts.map +1 -1
  7. package/build/ExpoIapModule.js +19 -0
  8. package/build/ExpoIapModule.js.map +1 -1
  9. package/build/index.d.ts +1 -1
  10. package/build/index.d.ts.map +1 -1
  11. package/build/index.js +18 -19
  12. package/build/index.js.map +1 -1
  13. package/build/index.kepler.d.ts +0 -9
  14. package/build/index.kepler.d.ts.map +1 -1
  15. package/build/index.kepler.js +0 -9
  16. package/build/index.kepler.js.map +1 -1
  17. package/build/modules/android.d.ts +0 -14
  18. package/build/modules/android.d.ts.map +1 -1
  19. package/build/modules/android.js +0 -20
  20. package/build/modules/android.js.map +1 -1
  21. package/build/modules/ios.d.ts +0 -17
  22. package/build/modules/ios.d.ts.map +1 -1
  23. package/build/modules/ios.js +3 -21
  24. package/build/modules/ios.js.map +1 -1
  25. package/build/types.d.ts +38 -48
  26. package/build/types.d.ts.map +1 -1
  27. package/build/types.js +15 -0
  28. package/build/types.js.map +1 -1
  29. package/build/useIAP.d.ts +0 -5
  30. package/build/useIAP.d.ts.map +1 -1
  31. package/build/useIAP.js +4 -15
  32. package/build/useIAP.js.map +1 -1
  33. package/build/utils/availablePurchases.d.ts +1 -1
  34. package/build/utils/availablePurchases.d.ts.map +1 -1
  35. package/build/utils/availablePurchases.js +20 -6
  36. package/build/utils/availablePurchases.js.map +1 -1
  37. package/build/utils/restorePurchases.d.ts +2 -8
  38. package/build/utils/restorePurchases.d.ts.map +1 -1
  39. package/build/utils/restorePurchases.js +14 -15
  40. package/build/utils/restorePurchases.js.map +1 -1
  41. package/build/vega-adapter.d.ts.map +1 -1
  42. package/build/vega-adapter.js +3 -0
  43. package/build/vega-adapter.js.map +1 -1
  44. package/bun.lock +3 -2
  45. package/ios/ExpoIapModule.swift +22 -8
  46. package/ios/onside/OnsideIapModule.swift +1 -0
  47. package/openiap-versions.json +3 -3
  48. package/package.json +3 -2
  49. package/plugin/build/withIAP.d.ts +7 -2
  50. package/plugin/build/withIAP.js +66 -5
  51. package/plugin/build/withLocalOpenIAP.js +20 -9
  52. package/plugin/src/expoConfig.augmentation.d.ts +5 -2
  53. package/plugin/src/withIAP.ts +104 -10
  54. package/plugin/src/withLocalOpenIAP.ts +28 -9
  55. package/src/ExpoIapModule.ts +22 -2
  56. package/src/index.kepler.ts +0 -13
  57. package/src/index.ts +27 -31
  58. package/src/modules/android.ts +0 -23
  59. package/src/modules/ios.ts +7 -30
  60. package/src/types.ts +49 -48
  61. package/src/useIAP.ts +5 -22
  62. package/src/utils/availablePurchases.ts +28 -6
  63. package/src/utils/restorePurchases.ts +13 -15
  64. package/src/vega-adapter.ts +3 -0
@@ -55,7 +55,8 @@ apply from: project.file('openiap-android-sdk.gradle')
55
55
 
56
56
  // Store selection: explicit property > task flavor > connected debug device > play.
57
57
  apply from: project.file('openiap-store.gradle')
58
- def openiapFlavor = openIapResolveStore('expo-iap').store
58
+ def openiapSelection = openIapResolveStore('expo-iap')
59
+ def openiapFlavor = openiapSelection.store
59
60
 
60
61
  // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
61
62
  // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
@@ -86,7 +87,10 @@ android {
86
87
  versionCode = 1
87
88
  versionName = expoIapPackageVersion
88
89
  // When using local openiap-google with flavors, select the appropriate flavor
89
- missingDimensionStrategy "platform", openiapFlavor
90
+ missingDimensionStrategy "platform", openiapSelection.platform
91
+ }
92
+ testOptions {
93
+ unitTests.includeAndroidResources = true
90
94
  }
91
95
  lint {
92
96
  abortOnError = false
@@ -106,17 +110,8 @@ kotlin {
106
110
 
107
111
  dependencies {
108
112
  testImplementation "junit:junit:4.13.2"
113
+ testImplementation "org.robolectric:robolectric:4.16.1"
109
114
  testImplementation "org.json:json:20260719"
110
115
 
111
- // `api`, so an app's own native code can use dev.hyo.openiap through this module.
112
- if (findProject(":openiap-google") != null) {
113
- api project(":openiap-google")
114
- } else if (openiapFlavor == 'amazon') {
115
- api "io.github.hyochan.openiap:openiap-google-amazon:${googleVersionString}"
116
- } else if (openiapFlavor == 'horizon') {
117
- api "io.github.hyochan.openiap:openiap-google-horizon:${googleVersionString}"
118
- } else {
119
- // Fallback to published artifact when local project isn't linked
120
- api "io.github.hyochan.openiap:openiap-google:${googleVersionString}"
121
- }
116
+ openIapAddStoreDependencies('api', googleVersionString, ':openiap-google')
122
117
  }
@@ -16,10 +16,21 @@
16
16
  // builds another store (an abbreviation such as aHR) fails once the graph is ready.
17
17
 
18
18
  ext.openIapStoreAliases = [
19
- play: 'play', google: 'play', gplay: 'play', googleplay: 'play', 'google-play': 'play', gms: 'play',
20
- horizon: 'horizon', meta: 'horizon', quest: 'horizon',
21
- amazon: 'amazon', fire: 'amazon', fireos: 'amazon', 'fire-os': 'amazon',
22
- none: 'none', auto: 'auto',
19
+ 'play': 'play',
20
+ 'google': 'play',
21
+ 'gplay': 'play',
22
+ 'googleplay': 'play',
23
+ 'google-play': 'play',
24
+ 'gms': 'play',
25
+ 'horizon': 'horizon',
26
+ 'meta': 'horizon',
27
+ 'quest': 'horizon',
28
+ 'amazon': 'amazon',
29
+ 'fire': 'amazon',
30
+ 'fireos': 'amazon',
31
+ 'fire-os': 'amazon',
32
+ 'auto': 'auto',
33
+ 'none': 'none',
23
34
  ]
24
35
 
25
36
  ext.openIapNormalizeStore = { Object value, String label ->
@@ -28,10 +39,9 @@ ext.openIapNormalizeStore = { Object value, String label ->
28
39
  return null
29
40
  }
30
41
  def store = openIapStoreAliases[key]
31
- if (store == null) {
32
- throw new GradleException("${label}: unknown openiapStore '${value}'; use play, horizon, amazon, auto, or none where the library allows it")
33
- }
34
- return store
42
+ if (store != null) return store
43
+ if (key ==~ /[a-z][a-z0-9_]*/ && !(key in ['apple', 'unknown']) && project.findProperty('openiapProvider')) return key
44
+ throw new GradleException("${label}: unknown openiapStore '${value}'; use play, horizon, amazon, auto, or none where the library allows it, or supply openiapProvider for a lowercase community store id")
35
45
  }
36
46
 
37
47
  // assembleHorizonRelease -> [assemble, horizon, release]
@@ -327,6 +337,53 @@ ext.openIapDeviceStore = {
327
337
  return [store: store, reason: "device ${serial}, manufacturer ${manufacturer ?: 'unknown'}"]
328
338
  }
329
339
 
340
+ // Validates openiapProvider against the explicit store, for the plain path
341
+ // and the provider platform flavor alike. Returns the coordinates, or null
342
+ // when no provider is involved.
343
+ ext.openIapCheckProvider = { String label, String store, String explicit ->
344
+ def provider = project.findProperty('openiapProvider')?.toString()?.trim()
345
+ def official = store in ['play', 'horizon', 'amazon']
346
+ if (provider) {
347
+ if (official || store == 'none' || explicit == null) {
348
+ throw new GradleException("${label}: openiapProvider requires an explicit community openiapStore id")
349
+ }
350
+ if (!(provider ==~ /[A-Za-z0-9_.-]+:[A-Za-z0-9_.-]+:[0-9][A-Za-z0-9_.-]*/)) {
351
+ throw new GradleException("${label}: openiapProvider must be group:artifact:version with a fixed version")
352
+ }
353
+ // A snapshot in any letter case is a moving target, not a fixed version.
354
+ def providerVersion = provider.split(':')[2]
355
+ if (providerVersion.toLowerCase(Locale.ROOT).contains('snapshot')) {
356
+ throw new GradleException("${label}: openiapProvider must be group:artifact:version with a fixed version")
357
+ }
358
+ def module = provider.split(':').take(2).join(':')
359
+ if (module.startsWith('io.github.hyochan.openiap:openiap-')) {
360
+ throw new GradleException("${label}: openiapProvider must name a community provider, not an OpenIAP core or official artifact")
361
+ }
362
+ return provider
363
+ }
364
+ if (!official && store != 'none') {
365
+ throw new GradleException("${label}: openiapStore=${store} requires openiapProvider=group:artifact:version")
366
+ }
367
+ return null
368
+ }
369
+
370
+ // The provider platform flavor's coordinates. The pin selects that flavor
371
+ // only, so the task-flavor rule does not apply: each official flavor of the
372
+ // same app still links its own store.
373
+ ext.openIapResolveFlavorProvider = { String label ->
374
+ if (rootProject.ext.has('openIapProviderResolution')) {
375
+ return rootProject.ext.openIapProviderResolution
376
+ }
377
+ def explicit = openIapExplicitStore(label, false)
378
+ def provider = openIapCheckProvider(label, explicit ?: 'play', explicit)
379
+ def resolution = [store: explicit, provider: provider]
380
+ rootProject.ext.openIapProviderResolution = resolution
381
+ if (provider) {
382
+ logger.quiet("${label}: provider=${provider} for the provider platform flavor")
383
+ }
384
+ return resolution
385
+ }
386
+
330
387
  ext.openIapResolveStore = { String label, Map options = [:] ->
331
388
  def allowNone = options.allowNone == true
332
389
  if (rootProject.ext.has('openIapStoreResolution')) {
@@ -361,8 +418,27 @@ ext.openIapResolveStore = { String label, Map options = [:] ->
361
418
  if (resolution == null) {
362
419
  resolution = [store: 'play', source: 'default', reason: 'no store signal']
363
420
  }
421
+ resolution.official = resolution.store in ['play', 'horizon', 'amazon']
422
+ resolution.platform = resolution.official ? resolution.store : 'provider'
423
+ def provider = openIapCheckProvider(label, resolution.store, explicit)
424
+ if (provider) resolution.provider = provider
364
425
  rootProject.ext.openIapStoreResolution = resolution
365
426
  logger.quiet("openiap: store=${resolution.store} (source=${resolution.source}; ${resolution.reason})")
366
427
  openIapVerifyTaskGraph(label, resolution)
367
428
  return resolution
368
429
  }
430
+
431
+ // All framework wrappers use this dependency path, including embedded local builds.
432
+ ext.openIapAddStoreDependencies = { String configuration, String version, String localProject = ':openiap' ->
433
+ def resolution = openIapResolveStore('openiap', [allowNone: true])
434
+ if (resolution.store == 'none') return
435
+ if (resolution.provider) {
436
+ def core = findProject(':openiap-core')
437
+ dependencies.add(configuration, core != null ? core : "io.github.hyochan.openiap:openiap-core:${version}")
438
+ dependencies.add(configuration, resolution.provider)
439
+ } else {
440
+ def local = findProject(localProject)
441
+ def artifact = resolution.store == 'play' ? 'openiap-google' : "openiap-google-${resolution.store}"
442
+ dependencies.add(configuration, local != null ? local : "io.github.hyochan.openiap:${artifact}:${version}")
443
+ }
444
+ }
@@ -3,7 +3,8 @@ package expo.modules.iap
3
3
  import dev.hyo.openiap.AndroidSubscriptionOfferInput
4
4
  import dev.hyo.openiap.DeveloperBillingOptionParamsAndroid
5
5
  import dev.hyo.openiap.OpenIapError
6
- import dev.hyo.openiap.OpenIapModule
6
+ import dev.hyo.openiap.OpenIapProvider
7
+ import dev.hyo.openiap.OpenIapProtocol
7
8
  import dev.hyo.openiap.ProductQueryType
8
9
  import dev.hyo.openiap.SubscriptionProductReplacementParamsAndroid
9
10
  import dev.hyo.openiap.SubscriptionReplacementModeAndroid
@@ -301,7 +302,7 @@ object ExpoIapHelper {
301
302
  }
302
303
 
303
304
  fun setupListeners(
304
- openIap: OpenIapModule,
305
+ openIap: OpenIapProtocol,
305
306
  module: Module,
306
307
  scope: CoroutineScope,
307
308
  connectionReady: java.util.concurrent.atomic.AtomicBoolean,
@@ -458,7 +459,7 @@ object ExpoIapHelper {
458
459
  }
459
460
 
460
461
  fun cleanupListeners(
461
- openIap: OpenIapModule,
462
+ openIap: OpenIapProtocol,
462
463
  handles: ListenerHandles?,
463
464
  ) {
464
465
  if (handles == null) return
@@ -7,7 +7,8 @@ import dev.hyo.openiap.FetchProductsResultProducts
7
7
  import dev.hyo.openiap.FetchProductsResultSubscriptions
8
8
  import dev.hyo.openiap.InitConnectionConfig
9
9
  import dev.hyo.openiap.OpenIapError
10
- import dev.hyo.openiap.OpenIapModule
10
+ import dev.hyo.openiap.OpenIapProvider
11
+ import dev.hyo.openiap.OpenIapProtocol
11
12
  import dev.hyo.openiap.ProductQueryType
12
13
  import dev.hyo.openiap.ProductRequest
13
14
  import dev.hyo.openiap.Purchase
@@ -48,6 +49,7 @@ import dev.hyo.openiap.GetBillingChoiceInfoParamsAndroid as OpenIapGetBillingCho
48
49
  import dev.hyo.openiap.InAppMessageCategoryAndroid as OpenIapInAppMessageCategory
49
50
  import dev.hyo.openiap.InAppMessageParamsAndroid as OpenIapInAppMessageParams
50
51
  import dev.hyo.openiap.LaunchExternalLinkParamsAndroid as OpenIapLaunchExternalLinkParams
52
+ import dev.hyo.openiap.utils.redeemOfferCode
51
53
 
52
54
  internal suspend fun endExpoConnectionWithCleanup(
53
55
  endConnection: suspend () -> Boolean,
@@ -58,6 +60,18 @@ internal suspend fun endExpoConnectionWithCleanup(
58
60
  cleanup()
59
61
  }
60
62
 
63
+ internal suspend fun endExpoConnectionOrReset(
64
+ provider: Lazy<*>,
65
+ endConnection: suspend () -> Boolean,
66
+ cleanup: () -> Unit,
67
+ ): Boolean {
68
+ if (!provider.isInitialized()) {
69
+ cleanup()
70
+ return true
71
+ }
72
+ return endExpoConnectionWithCleanup(endConnection, cleanup)
73
+ }
74
+
61
75
  internal fun endConnectionErrorCode(error: Exception): String =
62
76
  (error as? OpenIapError)?.code ?: OpenIapError.ServiceDisconnected.CODE
63
77
 
@@ -118,9 +132,10 @@ class ExpoIapModule : Module() {
118
132
  private val currentActivity
119
133
  get() = appContext.activityProvider?.currentActivity ?: throw Exceptions.MissingActivity()
120
134
 
121
- private val openIap: OpenIapModule by lazy { OpenIapModule(context) }
135
+ private val openIapLazy: Lazy<OpenIapProtocol> = lazy { OpenIapProvider.create(context) }
136
+ private val openIap: OpenIapProtocol get() = openIapLazy.value
122
137
 
123
- // Pass openIap directly to OpenIapStore to avoid reflection-based module loading
138
+ // The bridge and store share one provider instance.
124
139
  private val openIapStore: OpenIapStore by lazy { OpenIapStore(openIap) }
125
140
  private var listenerHandles: ExpoIapHelper.ListenerHandles? = null
126
141
  private val pendingEvents = ConcurrentLinkedQueue<Pair<String, Map<String, Any?>>>()
@@ -185,9 +200,10 @@ class ExpoIapModule : Module() {
185
200
  // Clear any buffered events from a failed init
186
201
  pendingEvents.clear()
187
202
  ExpoIapLog.failure("initConnection", IllegalStateException("Failed to initialize connection"))
203
+ val error = OpenIapError.InitConnection.forProvider(context)
188
204
  promise.reject(
189
- OpenIapError.InitConnection.CODE,
190
- OpenIapError.InitConnection.message,
205
+ error.code,
206
+ error.message,
191
207
  null,
192
208
  )
193
209
  return@withLock
@@ -206,7 +222,7 @@ class ExpoIapModule : Module() {
206
222
  promise.resolve(true)
207
223
  } catch (e: Exception) {
208
224
  ExpoIapLog.failure("initConnection", e)
209
- promise.reject(OpenIapError.InitConnection.CODE, e.message, e)
225
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.InitConnection.CODE, e.message, e)
210
226
  }
211
227
  }
212
228
  }
@@ -217,10 +233,11 @@ class ExpoIapModule : Module() {
217
233
  scope.launch {
218
234
  connectionMutex.withLock {
219
235
  try {
220
- val result = endExpoConnectionWithCleanup(
236
+ val result = endExpoConnectionOrReset(
237
+ provider = openIapLazy,
221
238
  endConnection = { openIap.endConnection() },
222
239
  cleanup = {
223
- ExpoIapHelper.cleanupListeners(openIap, listenerHandles)
240
+ listenerHandles?.let { ExpoIapHelper.cleanupListeners(openIap, it) }
224
241
  listenerHandles = null
225
242
  PromiseUtils.rejectAllPendingPromises()
226
243
  connectionReady.set(false)
@@ -296,7 +313,7 @@ class ExpoIapModule : Module() {
296
313
  promise.resolve(payload)
297
314
  } catch (e: Exception) {
298
315
  ExpoIapLog.failure("getAvailableItems", e)
299
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
316
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, null)
300
317
  }
301
318
  }
302
319
  }
@@ -314,23 +331,20 @@ class ExpoIapModule : Module() {
314
331
  promise.resolve(null)
315
332
  } catch (e: Exception) {
316
333
  ExpoIapLog.failure("deepLinkToSubscriptionsAndroid", e)
317
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
334
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, null)
318
335
  }
319
336
  }
320
337
  }
321
338
 
322
- AsyncFunction("openRedeemOfferCodeAndroid") { promise: Promise ->
323
- ExpoIapLog.payload("openRedeemOfferCodeAndroid", null)
339
+ AsyncFunction("openRedeemOfferCode") { promise: Promise ->
340
+ ExpoIapLog.payload("openRedeemOfferCode", null)
324
341
  scope.launch {
325
342
  try {
326
- runCatching { currentActivity }.getOrNull()?.let(openIap::setActivity)
327
- val handler = openIap.mutationHandlers.openRedeemOfferCodeAndroid
328
- ?: throw OpenIapError.FeatureNotSupported()
329
- val launched = handler()
330
- ExpoIapLog.result("openRedeemOfferCodeAndroid", launched)
331
- promise.resolve(launched)
343
+ val purchase = redeemOfferCode(openIap, runCatching { currentActivity }.getOrNull())
344
+ ExpoIapLog.result("openRedeemOfferCode", purchase != null)
345
+ promise.resolve(purchase?.toJson())
332
346
  } catch (e: Exception) {
333
- ExpoIapLog.failure("openRedeemOfferCodeAndroid", e)
347
+ ExpoIapLog.failure("openRedeemOfferCode", e)
334
348
  val errorMap =
335
349
  if (e is OpenIapError) {
336
350
  ExpoIapHelper.serializeOpenIapError(e)
@@ -362,7 +376,7 @@ class ExpoIapModule : Module() {
362
376
  promise.resolve(code)
363
377
  } catch (e: Exception) {
364
378
  ExpoIapLog.failure("getStorefront", e)
365
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
379
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
366
380
  }
367
381
  }
368
382
  }
@@ -487,6 +501,30 @@ class ExpoIapModule : Module() {
487
501
  }
488
502
  }
489
503
 
504
+ AsyncFunction("restorePurchases") { promise: Promise ->
505
+ scope.launch {
506
+ try {
507
+ openIap.restorePurchases()
508
+ promise.resolve(true)
509
+ } catch (e: Exception) {
510
+ ExpoIapLog.failure("restorePurchases", e)
511
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, null)
512
+ }
513
+ }
514
+ }
515
+
516
+ AsyncFunction("finishTransaction") { purchase: Map<String, Any?>, isConsumable: Boolean, promise: Promise ->
517
+ scope.launch {
518
+ try {
519
+ openIap.finishTransaction(dev.hyo.openiap.PurchaseAndroid.fromJson(purchase), isConsumable)
520
+ promise.resolve(null)
521
+ } catch (e: Exception) {
522
+ ExpoIapLog.failure("finishTransaction", e)
523
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.DeveloperError.CODE, e.message, null)
524
+ }
525
+ }
526
+ }
527
+
490
528
  AsyncFunction("acknowledgePurchaseAndroid") { token: String, promise: Promise ->
491
529
  ExpoIapLog.payload("acknowledgePurchaseAndroid", mapOf("token" to token))
492
530
  scope.launch {
@@ -497,7 +535,7 @@ class ExpoIapModule : Module() {
497
535
  promise.resolve(response)
498
536
  } catch (e: Exception) {
499
537
  ExpoIapLog.failure("acknowledgePurchaseAndroid", e)
500
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
538
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, null)
501
539
  }
502
540
  }
503
541
  }
@@ -515,7 +553,7 @@ class ExpoIapModule : Module() {
515
553
  promise.resolve(response)
516
554
  } catch (e: Exception) {
517
555
  ExpoIapLog.failure("consumePurchaseAndroid", e)
518
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
556
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, null)
519
557
  }
520
558
  }
521
559
  }
@@ -542,7 +580,7 @@ class ExpoIapModule : Module() {
542
580
  promise.resolve(resultMap)
543
581
  } catch (e: Exception) {
544
582
  ExpoIapLog.failure("verifyPurchase", e)
545
- promise.reject(OpenIapError.VerificationFailed.CODE, e.message, e)
583
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.VerificationFailed.CODE, e.message, e)
546
584
  }
547
585
  }
548
586
  }
@@ -566,7 +604,7 @@ class ExpoIapModule : Module() {
566
604
  promise.resolve(resultMap)
567
605
  } catch (e: Exception) {
568
606
  ExpoIapLog.failure("verifyPurchaseWithProvider", e)
569
- promise.reject(OpenIapError.VerificationFailed.CODE, e.message, e)
607
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.VerificationFailed.CODE, e.message, e)
570
608
  }
571
609
  }
572
610
  }
@@ -584,7 +622,7 @@ class ExpoIapModule : Module() {
584
622
  promise.resolve(result)
585
623
  } catch (e: Exception) {
586
624
  ExpoIapLog.failure("getActiveSubscriptions", e)
587
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
625
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
588
626
  }
589
627
  }
590
628
  }
@@ -601,7 +639,7 @@ class ExpoIapModule : Module() {
601
639
  promise.resolve(hasActive)
602
640
  } catch (e: Exception) {
603
641
  ExpoIapLog.failure("hasActiveSubscriptions", e)
604
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
642
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
605
643
  }
606
644
  }
607
645
  }
@@ -627,7 +665,7 @@ class ExpoIapModule : Module() {
627
665
  promise.resolve(response)
628
666
  } catch (e: Exception) {
629
667
  ExpoIapLog.failure("isBillingProgramAvailableAndroid", e)
630
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
668
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
631
669
  }
632
670
  }
633
671
  }
@@ -653,7 +691,7 @@ class ExpoIapModule : Module() {
653
691
  promise.resolve(result.toJson())
654
692
  } catch (e: Exception) {
655
693
  ExpoIapLog.failure("getBillingChoiceInfoAndroid", e)
656
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
694
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
657
695
  }
658
696
  }
659
697
  }
@@ -685,7 +723,7 @@ class ExpoIapModule : Module() {
685
723
  promise.resolve(response)
686
724
  } catch (e: Exception) {
687
725
  ExpoIapLog.failure("createBillingProgramReportingDetailsAndroid", e)
688
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
726
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
689
727
  }
690
728
  }
691
729
  }
@@ -719,7 +757,7 @@ class ExpoIapModule : Module() {
719
757
  promise.resolve(result.toJson())
720
758
  } catch (e: Exception) {
721
759
  ExpoIapLog.failure("showBillingProgramInformationDialogAndroid", e)
722
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
760
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
723
761
  }
724
762
  }
725
763
  }
@@ -754,7 +792,7 @@ class ExpoIapModule : Module() {
754
792
  promise.resolve(result.toJson())
755
793
  } catch (e: Exception) {
756
794
  ExpoIapLog.failure("showInAppMessagesAndroid", e)
757
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
795
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
758
796
  }
759
797
  }
760
798
  }
@@ -802,18 +840,24 @@ class ExpoIapModule : Module() {
802
840
  promise.resolve(result)
803
841
  } catch (e: Exception) {
804
842
  ExpoIapLog.failure("launchExternalLinkAndroid", e)
805
- promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
843
+ promise.reject((e as? OpenIapError)?.code ?: OpenIapError.ServiceUnavailable.CODE, e.message, e)
806
844
  }
807
845
  }
808
846
  }
809
847
 
810
848
  OnDestroy {
811
- ExpoIapHelper.cleanupListeners(openIap, listenerHandles)
812
- listenerHandles = null
813
- connectionReady.set(false)
814
- pendingEvents.clear()
815
- PromiseUtils.rejectAllPendingPromises()
816
- job.cancel()
849
+ try {
850
+ listenerHandles?.let { ExpoIapHelper.cleanupListeners(openIap, it) }
851
+ } finally {
852
+ listenerHandles = null
853
+ connectionReady.set(false)
854
+ pendingEvents.clear()
855
+ try {
856
+ PromiseUtils.rejectAllPendingPromises()
857
+ } finally {
858
+ job.cancel()
859
+ }
860
+ }
817
861
  }
818
862
  }
819
863
 
@@ -3,6 +3,7 @@ import type { BillingProgramAndroid, BillingProgramReportingDetailsAndroid, Deve
3
3
  /** Members read from the raw module; every store module provides them. */
4
4
  type NativeEventModule = {
5
5
  ERROR_CODES?: Record<string, unknown>;
6
+ HAS_EXPLICIT_STORE_PROVIDER?: boolean;
6
7
  addListener<T>(eventName: string, listener: (payload: T) => void): EventSubscription | undefined;
7
8
  removeListener?<T>(eventName: string, listener: (payload: T) => void): void;
8
9
  setPurchaseUpdatedListenerOptions?(options?: PurchaseUpdatedListenerOptions | null): Promise<void>;
@@ -17,7 +18,7 @@ type MutationFields<K extends keyof Mutation> = {
17
18
  * Native surface behind the default export. Results the wrappers decode stay
18
19
  * `unknown`; the rest match the generated operation signatures.
19
20
  */
20
- export type ExpoIapNativeModule = NativeEventModule & QueryFields<'canPresentExternalPurchaseNoticeIOS' | 'currentEntitlementIOS' | 'getActiveSubscriptions' | 'getAppTransactionIOS' | 'getBillingChoiceInfoAndroid' | 'getExternalPurchaseCustomLinkTokenIOS' | 'getPromotedProductIOS' | 'getReceiptDataIOS' | 'getTransactionJwsIOS' | 'hasActiveSubscriptions' | 'isEligibleForExternalPurchaseCustomLinkIOS' | 'isEligibleForIntroOfferIOS' | 'isTransactionVerifiedIOS' | 'latestTransactionIOS'> & MutationFields<'beginRefundRequestIOS' | 'clearTransactionIOS' | 'endConnection' | 'initConnection' | 'isBillingProgramAvailableAndroid' | 'launchExternalLinkAndroid' | 'openRedeemOfferCodeAndroid' | 'presentCodeRedemptionSheetIOS' | 'presentExternalPurchaseLinkIOS' | 'presentExternalPurchaseNoticeSheetIOS' | 'showBillingProgramInformationDialogAndroid' | 'showExternalPurchaseCustomLinkNoticeIOS' | 'showInAppMessagesAndroid' | 'syncIOS' | 'verifyPurchase' | 'verifyPurchaseWithProvider'> & {
21
+ export type ExpoIapNativeModule = NativeEventModule & QueryFields<'canPresentExternalPurchaseNoticeIOS' | 'currentEntitlementIOS' | 'getActiveSubscriptions' | 'getAppTransactionIOS' | 'getBillingChoiceInfoAndroid' | 'getExternalPurchaseCustomLinkTokenIOS' | 'getPromotedProductIOS' | 'getReceiptDataIOS' | 'getTransactionJwsIOS' | 'hasActiveSubscriptions' | 'isEligibleForExternalPurchaseCustomLinkIOS' | 'isEligibleForIntroOfferIOS' | 'isTransactionVerifiedIOS' | 'latestTransactionIOS'> & MutationFields<'beginRefundRequestIOS' | 'clearTransactionIOS' | 'endConnection' | 'initConnection' | 'isBillingProgramAvailableAndroid' | 'launchExternalLinkAndroid' | 'openRedeemOfferCode' | 'presentExternalPurchaseLinkIOS' | 'presentExternalPurchaseNoticeSheetIOS' | 'showBillingProgramInformationDialogAndroid' | 'showExternalPurchaseCustomLinkNoticeIOS' | 'showInAppMessagesAndroid' | 'syncIOS' | 'verifyPurchase' | 'verifyPurchaseWithProvider'> & {
21
22
  USING_ONSIDE_SDK: boolean;
22
23
  USING_VEGA_SDK: boolean;
23
24
  fetchProducts(request: {
@@ -30,6 +31,7 @@ export type ExpoIapNativeModule = NativeEventModule & QueryFields<'canPresentExt
30
31
  getAllTransactionsIOS(): Promise<unknown>;
31
32
  getPendingTransactionsIOS(): Promise<unknown>;
32
33
  showManageSubscriptionsIOS(): Promise<unknown>;
34
+ deepLinkToSubscriptionsIOS(): Promise<boolean>;
33
35
  requestPurchase(request: object): Promise<unknown>;
34
36
  finishTransaction(purchase: PurchaseInput, isConsumable: boolean | null): Promise<boolean>;
35
37
  /** Internal: the once-per-install flag behind the first-purchase notice. */
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpoIapModule.d.ts","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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"}
@@ -4,8 +4,27 @@ import { getVegaIapModule, isVegaOS } from './vega';
4
4
  const ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';
5
5
  let cached = null;
6
6
  let onsideModuleUnavailable = false;
7
+ let explicitStoreProvider;
7
8
  function getResolved() {
8
9
  function shouldUseOnsideModule() {
10
+ if (installedFromOnside === true ||
11
+ typeof installedFromOnside === 'string') {
12
+ if (explicitStoreProvider === undefined) {
13
+ try {
14
+ const module = cached?.name === 'ExpoIap'
15
+ ? cached.module
16
+ : requireNativeModule('ExpoIap');
17
+ explicitStoreProvider = module.HAS_EXPLICIT_STORE_PROVIDER === true;
18
+ }
19
+ catch (error) {
20
+ if (!isMissingModuleError(error, 'ExpoIap'))
21
+ throw error;
22
+ explicitStoreProvider = false;
23
+ }
24
+ }
25
+ if (explicitStoreProvider)
26
+ return false;
27
+ }
9
28
  if (installedFromOnside === true) {
10
29
  return true;
11
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n requireNativeModule,\n UnavailabilityError,\n type EventSubscription,\n} from 'expo-modules-core';\nimport {installedFromOnside} from './onside';\nimport type {\n BillingProgramAndroid,\n BillingProgramReportingDetailsAndroid,\n DeveloperBillingTypeAndroid,\n Mutation,\n MutationField,\n ProductQueryType,\n PurchaseInput,\n PurchaseOptions,\n PurchaseUpdatedListenerOptions,\n Query,\n QueryField,\n SubscriptionStatusIOS,\n} from './types';\nimport {getVegaIapModule, isVegaOS} from './vega';\n\ntype NativeIapModuleName = 'ExpoIapVega' | 'ExpoIapOnside' | 'ExpoIap';\nconst ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';\n\n/** Members read from the raw module; every store module provides them. */\ntype NativeEventModule = {\n ERROR_CODES?: Record<string, unknown>;\n addListener<T>(\n eventName: string,\n listener: (payload: T) => void,\n ): EventSubscription | undefined;\n removeListener?<T>(eventName: string, listener: (payload: T) => void): void;\n setPurchaseUpdatedListenerOptions?(\n options?: PurchaseUpdatedListenerOptions | null,\n ): Promise<void>;\n};\n\ntype QueryFields<K extends keyof Query> = {[P in K]: QueryField<P>};\ntype MutationFields<K extends keyof Mutation> = {[P in K]: MutationField<P>};\n\n/**\n * Native surface behind the default export. Results the wrappers decode stay\n * `unknown`; the rest match the generated operation signatures.\n */\nexport type ExpoIapNativeModule = NativeEventModule &\n QueryFields<\n | 'canPresentExternalPurchaseNoticeIOS'\n | 'currentEntitlementIOS'\n | 'getActiveSubscriptions'\n | 'getAppTransactionIOS'\n | 'getBillingChoiceInfoAndroid'\n | 'getExternalPurchaseCustomLinkTokenIOS'\n | 'getPromotedProductIOS'\n | 'getReceiptDataIOS'\n | 'getTransactionJwsIOS'\n | 'hasActiveSubscriptions'\n | 'isEligibleForExternalPurchaseCustomLinkIOS'\n | 'isEligibleForIntroOfferIOS'\n | 'isTransactionVerifiedIOS'\n | 'latestTransactionIOS'\n > &\n MutationFields<\n | 'beginRefundRequestIOS'\n | 'clearTransactionIOS'\n | 'endConnection'\n | 'initConnection'\n | 'isBillingProgramAvailableAndroid'\n | 'launchExternalLinkAndroid'\n | 'openRedeemOfferCodeAndroid'\n | 'presentCodeRedemptionSheetIOS'\n | 'presentExternalPurchaseLinkIOS'\n | 'presentExternalPurchaseNoticeSheetIOS'\n | 'showBillingProgramInformationDialogAndroid'\n | 'showExternalPurchaseCustomLinkNoticeIOS'\n | 'showInAppMessagesAndroid'\n | 'syncIOS'\n | 'verifyPurchase'\n | 'verifyPurchaseWithProvider'\n > & {\n USING_ONSIDE_SDK: boolean;\n USING_VEGA_SDK: boolean;\n fetchProducts(request: {\n skus: string[];\n type: ProductQueryType;\n }): Promise<unknown[]>;\n fetchProducts(type: ProductQueryType, skus: string[]): Promise<unknown[]>;\n getAvailableItems(\n alsoPublishToEventListenerIOS: boolean,\n onlyIncludeActiveItemsIOS: boolean,\n ): Promise<unknown>;\n getAvailableItems(options: PurchaseOptions): Promise<unknown>;\n getAllTransactionsIOS(): Promise<unknown>;\n getPendingTransactionsIOS(): Promise<unknown>;\n showManageSubscriptionsIOS(): Promise<unknown>;\n requestPurchase(request: object): Promise<unknown>;\n finishTransaction(\n purchase: PurchaseInput,\n isConsumable: boolean | null,\n ): Promise<boolean>;\n /** Internal: the once-per-install flag behind the first-purchase notice. */\n claimFirstPurchaseNotice(): Promise<boolean>;\n acknowledgePurchaseAndroid(purchaseToken: string): Promise<unknown>;\n consumePurchaseAndroid(purchaseToken: string): Promise<unknown>;\n createBillingProgramReportingDetailsAndroid(\n program: BillingProgramAndroid,\n developerBillingType: DeveloperBillingTypeAndroid | null,\n ): Promise<BillingProgramReportingDetailsAndroid>;\n subscriptionStatusIOS(sku: string): Promise<SubscriptionStatusIOS[] | null>;\n requestReceiptRefreshIOS(): Promise<string>;\n deepLinkToSubscriptionsAndroid?(options: {\n skuAndroid?: string;\n packageNameAndroid?: string;\n }): Promise<void> | void;\n getStorefront?(): Promise<string> | string;\n restorePurchases?(): Promise<boolean>;\n };\n\ntype ResolvedNativeModule = {\n module: NativeEventModule;\n name: NativeIapModuleName;\n};\n\nlet cached: ResolvedNativeModule | null = null;\nlet onsideModuleUnavailable = false;\n\nfunction getResolved(): ResolvedNativeModule {\n function shouldUseOnsideModule(): boolean {\n if (installedFromOnside === true) {\n return true;\n }\n\n if (typeof installedFromOnside !== 'string') {\n return false;\n }\n\n const normalized = installedFromOnside.trim().toLowerCase();\n return normalized === 'true' || normalized === ONSIDE_MARKETPLACE_ID;\n }\n\n function getExpectedModuleName(): NativeIapModuleName {\n if (isVegaOS()) {\n return 'ExpoIapVega';\n }\n\n return shouldUseOnsideModule() ? 'ExpoIapOnside' : 'ExpoIap';\n }\n\n function resolveNativeModule(): ResolvedNativeModule {\n if (isVegaOS()) {\n const vegaModule = getVegaIapModule();\n if (!vegaModule) {\n throw new UnavailabilityError(\n 'expo-iap',\n '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.',\n );\n }\n return {module: vegaModule, name: 'ExpoIapVega'};\n }\n\n if (shouldUseOnsideModule()) {\n if (onsideModuleUnavailable) {\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n try {\n return {\n module: requireNativeModule<NativeEventModule>('ExpoIapOnside'),\n name: 'ExpoIapOnside',\n };\n } catch (error) {\n if (!isMissingModuleError(error, 'ExpoIapOnside')) {\n throw error;\n }\n onsideModuleUnavailable = true;\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n }\n\n return {\n module: requireNativeModule<NativeEventModule>('ExpoIap'),\n name: 'ExpoIap',\n };\n }\n\n const expectedName = getExpectedModuleName();\n if (!cached || cached.name !== expectedName) {\n cached = resolveNativeModule();\n }\n return cached;\n}\n\nfunction isMissingModuleError(error: unknown, moduleName: string): boolean {\n if (error instanceof UnavailabilityError) {\n return true;\n }\n\n if (error instanceof Error) {\n return error.message.includes(`Cannot find native module '${moduleName}'`);\n }\n\n return false;\n}\n\nexport const NATIVE_ERROR_CODES: Record<string, unknown> = new Proxy(\n {} as Record<string, unknown>,\n {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const errorCodes: Record<string, unknown> =\n getResolved().module.ERROR_CODES || {};\n return errorCodes[prop];\n },\n },\n);\n\n/**\n * Returns the raw native module, not the Proxy. Use it for addListener: a JSI\n * HostObject needs the real module as `this`, and a Proxy throws \"native state\n * unsupported on Proxy\" on New Architecture / Hermes.\n */\nexport function getNativeModule(): NativeEventModule {\n return getResolved().module;\n}\n\n// The Proxy forwards every member to the lazily resolved store module.\nexport default new Proxy({} as ExpoIapNativeModule, {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const resolved = getResolved();\n if (prop === 'USING_ONSIDE_SDK') {\n return resolved.name === 'ExpoIapOnside';\n }\n if (prop === 'USING_VEGA_SDK') {\n return resolved.name === 'ExpoIapVega';\n }\n\n const value: unknown = Reflect.get(resolved.module, prop);\n if (value !== undefined || resolved.name !== 'ExpoIapOnside') {\n return value;\n }\n\n return () => {\n throw new UnavailabilityError(\n 'expo-iap',\n `The Onside marketplace does not support ${String(\n prop,\n )}. The call was not routed through Apple StoreKit.`,\n );\n };\n },\n});\n"]}
1
+ {"version":3,"file":"ExpoIapModule.js","sourceRoot":"","sources":["../src/ExpoIapModule.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n requireNativeModule,\n UnavailabilityError,\n type EventSubscription,\n} from 'expo-modules-core';\nimport {installedFromOnside} from './onside';\nimport type {\n BillingProgramAndroid,\n BillingProgramReportingDetailsAndroid,\n DeveloperBillingTypeAndroid,\n Mutation,\n MutationField,\n ProductQueryType,\n PurchaseInput,\n PurchaseOptions,\n PurchaseUpdatedListenerOptions,\n Query,\n QueryField,\n SubscriptionStatusIOS,\n} from './types';\nimport {getVegaIapModule, isVegaOS} from './vega';\n\ntype NativeIapModuleName = 'ExpoIapVega' | 'ExpoIapOnside' | 'ExpoIap';\nconst ONSIDE_MARKETPLACE_ID = 'com.onside.marketplace-app';\n\n/** Members read from the raw module; every store module provides them. */\ntype NativeEventModule = {\n ERROR_CODES?: Record<string, unknown>;\n HAS_EXPLICIT_STORE_PROVIDER?: boolean;\n addListener<T>(\n eventName: string,\n listener: (payload: T) => void,\n ): EventSubscription | undefined;\n removeListener?<T>(eventName: string, listener: (payload: T) => void): void;\n setPurchaseUpdatedListenerOptions?(\n options?: PurchaseUpdatedListenerOptions | null,\n ): Promise<void>;\n};\n\ntype QueryFields<K extends keyof Query> = {[P in K]: QueryField<P>};\ntype MutationFields<K extends keyof Mutation> = {[P in K]: MutationField<P>};\n\n/**\n * Native surface behind the default export. Results the wrappers decode stay\n * `unknown`; the rest match the generated operation signatures.\n */\nexport type ExpoIapNativeModule = NativeEventModule &\n QueryFields<\n | 'canPresentExternalPurchaseNoticeIOS'\n | 'currentEntitlementIOS'\n | 'getActiveSubscriptions'\n | 'getAppTransactionIOS'\n | 'getBillingChoiceInfoAndroid'\n | 'getExternalPurchaseCustomLinkTokenIOS'\n | 'getPromotedProductIOS'\n | 'getReceiptDataIOS'\n | 'getTransactionJwsIOS'\n | 'hasActiveSubscriptions'\n | 'isEligibleForExternalPurchaseCustomLinkIOS'\n | 'isEligibleForIntroOfferIOS'\n | 'isTransactionVerifiedIOS'\n | 'latestTransactionIOS'\n > &\n MutationFields<\n | 'beginRefundRequestIOS'\n | 'clearTransactionIOS'\n | 'endConnection'\n | 'initConnection'\n | 'isBillingProgramAvailableAndroid'\n | 'launchExternalLinkAndroid'\n | 'openRedeemOfferCode'\n | 'presentExternalPurchaseLinkIOS'\n | 'presentExternalPurchaseNoticeSheetIOS'\n | 'showBillingProgramInformationDialogAndroid'\n | 'showExternalPurchaseCustomLinkNoticeIOS'\n | 'showInAppMessagesAndroid'\n | 'syncIOS'\n | 'verifyPurchase'\n | 'verifyPurchaseWithProvider'\n > & {\n USING_ONSIDE_SDK: boolean;\n USING_VEGA_SDK: boolean;\n fetchProducts(request: {\n skus: string[];\n type: ProductQueryType;\n }): Promise<unknown[]>;\n fetchProducts(type: ProductQueryType, skus: string[]): Promise<unknown[]>;\n getAvailableItems(\n alsoPublishToEventListenerIOS: boolean,\n onlyIncludeActiveItemsIOS: boolean,\n ): Promise<unknown>;\n getAvailableItems(options: PurchaseOptions): Promise<unknown>;\n getAllTransactionsIOS(): Promise<unknown>;\n getPendingTransactionsIOS(): Promise<unknown>;\n showManageSubscriptionsIOS(): Promise<unknown>;\n deepLinkToSubscriptionsIOS(): Promise<boolean>;\n requestPurchase(request: object): Promise<unknown>;\n finishTransaction(\n purchase: PurchaseInput,\n isConsumable: boolean | null,\n ): Promise<boolean>;\n /** Internal: the once-per-install flag behind the first-purchase notice. */\n claimFirstPurchaseNotice(): Promise<boolean>;\n acknowledgePurchaseAndroid(purchaseToken: string): Promise<unknown>;\n consumePurchaseAndroid(purchaseToken: string): Promise<unknown>;\n createBillingProgramReportingDetailsAndroid(\n program: BillingProgramAndroid,\n developerBillingType: DeveloperBillingTypeAndroid | null,\n ): Promise<BillingProgramReportingDetailsAndroid>;\n subscriptionStatusIOS(sku: string): Promise<SubscriptionStatusIOS[] | null>;\n requestReceiptRefreshIOS(): Promise<string>;\n deepLinkToSubscriptionsAndroid?(options: {\n skuAndroid?: string;\n packageNameAndroid?: string;\n }): Promise<void> | void;\n getStorefront?(): Promise<string> | string;\n restorePurchases?(): Promise<boolean>;\n };\n\ntype ResolvedNativeModule = {\n module: NativeEventModule;\n name: NativeIapModuleName;\n};\n\nlet cached: ResolvedNativeModule | null = null;\nlet onsideModuleUnavailable = false;\nlet explicitStoreProvider: boolean | undefined;\n\nfunction getResolved(): ResolvedNativeModule {\n function shouldUseOnsideModule(): boolean {\n if (\n installedFromOnside === true ||\n typeof installedFromOnside === 'string'\n ) {\n if (explicitStoreProvider === undefined) {\n try {\n const module =\n cached?.name === 'ExpoIap'\n ? cached.module\n : requireNativeModule<NativeEventModule>('ExpoIap');\n explicitStoreProvider = module.HAS_EXPLICIT_STORE_PROVIDER === true;\n } catch (error) {\n if (!isMissingModuleError(error, 'ExpoIap')) throw error;\n explicitStoreProvider = false;\n }\n }\n if (explicitStoreProvider) return false;\n }\n if (installedFromOnside === true) {\n return true;\n }\n\n if (typeof installedFromOnside !== 'string') {\n return false;\n }\n\n const normalized = installedFromOnside.trim().toLowerCase();\n return normalized === 'true' || normalized === ONSIDE_MARKETPLACE_ID;\n }\n\n function getExpectedModuleName(): NativeIapModuleName {\n if (isVegaOS()) {\n return 'ExpoIapVega';\n }\n\n return shouldUseOnsideModule() ? 'ExpoIapOnside' : 'ExpoIap';\n }\n\n function resolveNativeModule(): ResolvedNativeModule {\n if (isVegaOS()) {\n const vegaModule = getVegaIapModule();\n if (!vegaModule) {\n throw new UnavailabilityError(\n 'expo-iap',\n '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.',\n );\n }\n return {module: vegaModule, name: 'ExpoIapVega'};\n }\n\n if (shouldUseOnsideModule()) {\n if (onsideModuleUnavailable) {\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n try {\n return {\n module: requireNativeModule<NativeEventModule>('ExpoIapOnside'),\n name: 'ExpoIapOnside',\n };\n } catch (error) {\n if (!isMissingModuleError(error, 'ExpoIapOnside')) {\n throw error;\n }\n onsideModuleUnavailable = true;\n throw new UnavailabilityError(\n 'expo-iap',\n 'The Onside marketplace build does not contain ExpoIapOnside. Rebuild with ios.onside.enabled instead of routing purchases through Apple StoreKit.',\n );\n }\n }\n\n return {\n module: requireNativeModule<NativeEventModule>('ExpoIap'),\n name: 'ExpoIap',\n };\n }\n\n const expectedName = getExpectedModuleName();\n if (!cached || cached.name !== expectedName) {\n cached = resolveNativeModule();\n }\n return cached;\n}\n\nfunction isMissingModuleError(error: unknown, moduleName: string): boolean {\n if (error instanceof UnavailabilityError) {\n return true;\n }\n\n if (error instanceof Error) {\n return error.message.includes(`Cannot find native module '${moduleName}'`);\n }\n\n return false;\n}\n\nexport const NATIVE_ERROR_CODES: Record<string, unknown> = new Proxy(\n {} as Record<string, unknown>,\n {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const errorCodes: Record<string, unknown> =\n getResolved().module.ERROR_CODES || {};\n return errorCodes[prop];\n },\n },\n);\n\n/**\n * Returns the raw native module, not the Proxy. Use it for addListener: a JSI\n * HostObject needs the real module as `this`, and a Proxy throws \"native state\n * unsupported on Proxy\" on New Architecture / Hermes.\n */\nexport function getNativeModule(): NativeEventModule {\n return getResolved().module;\n}\n\n// The Proxy forwards every member to the lazily resolved store module.\nexport default new Proxy({} as ExpoIapNativeModule, {\n get(target, prop) {\n if (typeof prop === 'symbol') return Reflect.get(target, prop);\n const resolved = getResolved();\n if (prop === 'USING_ONSIDE_SDK') {\n return resolved.name === 'ExpoIapOnside';\n }\n if (prop === 'USING_VEGA_SDK') {\n return resolved.name === 'ExpoIapVega';\n }\n\n const value: unknown = Reflect.get(resolved.module, prop);\n if (value !== undefined || resolved.name !== 'ExpoIapOnside') {\n return value;\n }\n\n return () => {\n throw new UnavailabilityError(\n 'expo-iap',\n `The Onside marketplace does not support ${String(\n prop,\n )}. The call was not routed through Apple StoreKit.`,\n );\n };\n },\n});\n"]}
package/build/index.d.ts CHANGED
@@ -327,7 +327,7 @@ export declare const finishTransaction: MutationField<'finishTransaction'>;
327
327
  * `getAvailablePurchases` or from hook state.
328
328
  *
329
329
  * - iOS: sync (or Onside restore when OnsideKit is active), then fetch available purchases.
330
- * - Android: fetch available purchases; the query itself restores them.
330
+ * - Android: run the provider's restore, then fetch available purchases.
331
331
  *
332
332
  * @see {@link https://openiap.dev/docs/apis/restore-purchases}
333
333
  */