expo-iap 3.0.7 → 3.1.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 (52) hide show
  1. package/CLAUDE.md +14 -2
  2. package/CONTRIBUTING.md +19 -0
  3. package/README.md +18 -6
  4. package/android/build.gradle +24 -1
  5. package/android/src/main/java/expo/modules/iap/ExpoIapLog.kt +69 -0
  6. package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +190 -59
  7. package/build/index.d.ts +32 -111
  8. package/build/index.d.ts.map +1 -1
  9. package/build/index.js +198 -243
  10. package/build/index.js.map +1 -1
  11. package/build/modules/android.d.ts +7 -12
  12. package/build/modules/android.d.ts.map +1 -1
  13. package/build/modules/android.js +15 -12
  14. package/build/modules/android.js.map +1 -1
  15. package/build/modules/ios.d.ts +35 -36
  16. package/build/modules/ios.d.ts.map +1 -1
  17. package/build/modules/ios.js +101 -35
  18. package/build/modules/ios.js.map +1 -1
  19. package/build/types.d.ts +107 -82
  20. package/build/types.d.ts.map +1 -1
  21. package/build/types.js +1 -0
  22. package/build/types.js.map +1 -1
  23. package/build/useIAP.d.ts +7 -12
  24. package/build/useIAP.d.ts.map +1 -1
  25. package/build/useIAP.js +49 -23
  26. package/build/useIAP.js.map +1 -1
  27. package/build/utils/errorMapping.d.ts +32 -23
  28. package/build/utils/errorMapping.d.ts.map +1 -1
  29. package/build/utils/errorMapping.js +117 -22
  30. package/build/utils/errorMapping.js.map +1 -1
  31. package/ios/ExpoIap.podspec +3 -2
  32. package/ios/ExpoIapHelper.swift +96 -0
  33. package/ios/ExpoIapLog.swift +127 -0
  34. package/ios/ExpoIapModule.swift +218 -340
  35. package/openiap-versions.json +5 -0
  36. package/package.json +2 -2
  37. package/plugin/build/withIAP.js +6 -4
  38. package/plugin/src/withIAP.ts +14 -4
  39. package/scripts/update-types.mjs +20 -1
  40. package/src/index.ts +280 -356
  41. package/src/modules/android.ts +25 -23
  42. package/src/modules/ios.ts +138 -48
  43. package/src/types.ts +139 -91
  44. package/src/useIAP.ts +91 -58
  45. package/src/utils/errorMapping.ts +203 -23
  46. package/.copilot-instructions.md +0 -321
  47. package/.cursorrules +0 -321
  48. package/build/purchase-error.d.ts +0 -67
  49. package/build/purchase-error.d.ts.map +0 -1
  50. package/build/purchase-error.js +0 -166
  51. package/build/purchase-error.js.map +0 -1
  52. package/src/purchase-error.ts +0 -265
@@ -2,12 +2,22 @@ package expo.modules.iap
2
2
 
3
3
  import android.content.Context
4
4
  import android.util.Log
5
+ import dev.hyo.openiap.AndroidSubscriptionOfferInput
6
+ import dev.hyo.openiap.DeepLinkOptions
7
+ import dev.hyo.openiap.FetchProductsResultProducts
8
+ import dev.hyo.openiap.FetchProductsResultSubscriptions
5
9
  import dev.hyo.openiap.OpenIapError
6
10
  import dev.hyo.openiap.OpenIapModule
7
- import dev.hyo.openiap.models.DeepLinkOptions
8
- import dev.hyo.openiap.models.ProductRequest
9
- import dev.hyo.openiap.models.RequestPurchaseParams
10
- import dev.hyo.openiap.models.RequestSubscriptionAndroidProps
11
+ import dev.hyo.openiap.ProductQueryType
12
+ import dev.hyo.openiap.ProductRequest
13
+ import dev.hyo.openiap.Purchase
14
+ import dev.hyo.openiap.RequestPurchaseAndroidProps
15
+ import dev.hyo.openiap.RequestPurchaseProps
16
+ import dev.hyo.openiap.RequestPurchasePropsByPlatforms
17
+ import dev.hyo.openiap.RequestPurchaseResultPurchase
18
+ import dev.hyo.openiap.RequestPurchaseResultPurchases
19
+ import dev.hyo.openiap.RequestSubscriptionAndroidProps
20
+ import dev.hyo.openiap.RequestSubscriptionPropsByPlatforms
11
21
  import expo.modules.kotlin.Promise
12
22
  import expo.modules.kotlin.exception.Exceptions
13
23
  import expo.modules.kotlin.modules.Module
@@ -18,6 +28,7 @@ import kotlinx.coroutines.Job
18
28
  import kotlinx.coroutines.launch
19
29
  import kotlinx.coroutines.sync.Mutex
20
30
  import kotlinx.coroutines.sync.withLock
31
+ import java.util.Locale
21
32
  import java.util.concurrent.ConcurrentLinkedQueue
22
33
  import java.util.concurrent.atomic.AtomicBoolean
23
34
 
@@ -59,7 +70,20 @@ class ExpoIapModule : Module() {
59
70
  pendingEvents.add(name to payload)
60
71
  }
61
72
 
62
- // Mapping helpers now provided by openiap-google (toJSON helpers)
73
+ private fun parseProductQueryType(rawType: String?): ProductQueryType {
74
+ val normalized =
75
+ rawType
76
+ ?.trim()
77
+ ?.lowercase(Locale.US)
78
+ ?.replace("-", "")
79
+ ?.replace("_", "")
80
+
81
+ return when (normalized) {
82
+ "subs" -> ProductQueryType.Subs
83
+ "all" -> ProductQueryType.All
84
+ else -> ProductQueryType.InApp
85
+ }
86
+ }
63
87
 
64
88
  override fun definition() =
65
89
  ModuleDefinition {
@@ -72,6 +96,7 @@ class ExpoIapModule : Module() {
72
96
  Events(EVENT_PURCHASE_UPDATED, EVENT_PURCHASE_ERROR)
73
97
 
74
98
  AsyncFunction("initConnection") { promise: Promise ->
99
+ ExpoIapLog.payload("initConnection", null)
75
100
  scope.launch {
76
101
  connectionMutex.withLock {
77
102
  try {
@@ -82,6 +107,7 @@ class ExpoIapModule : Module() {
82
107
 
83
108
  // If already connected, short-circuit
84
109
  if (connectionReady.get()) {
110
+ ExpoIapLog.result("initConnection", true)
85
111
  promise.resolve(true)
86
112
  return@withLock
87
113
  }
@@ -90,8 +116,9 @@ class ExpoIapModule : Module() {
90
116
  if (!listenersAttached) {
91
117
  listenersAttached = true
92
118
  openIap.addPurchaseUpdateListener { p ->
93
- runCatching { emitOrQueue(EVENT_PURCHASE_UPDATED, p.toJSON()) }
94
- .onFailure { Log.e(TAG, "Failed to buffer/send PURCHASE_UPDATED", it) }
119
+ runCatching {
120
+ emitOrQueue(EVENT_PURCHASE_UPDATED, p.toJson())
121
+ }.onFailure { Log.e(TAG, "Failed to buffer/send PURCHASE_UPDATED", it) }
95
122
  }
96
123
  openIap.addPurchaseErrorListener { e ->
97
124
  runCatching { emitOrQueue(EVENT_PURCHASE_ERROR, e.toJSON()) }
@@ -104,6 +131,10 @@ class ExpoIapModule : Module() {
104
131
  if (!ok) {
105
132
  // Clear any buffered events from a failed init
106
133
  pendingEvents.clear()
134
+ ExpoIapLog.failure(
135
+ "initConnection",
136
+ IllegalStateException("Failed to initialize connection"),
137
+ )
107
138
  promise.reject(OpenIapError.InitConnection.CODE, "Failed to initialize connection", null)
108
139
  return@withLock
109
140
  }
@@ -117,8 +148,10 @@ class ExpoIapModule : Module() {
117
148
  .onFailure { Log.e(TAG, "Failed to flush buffered event: ${ev.first}", it) }
118
149
  }
119
150
 
151
+ ExpoIapLog.result("initConnection", true)
120
152
  promise.resolve(true)
121
153
  } catch (e: Exception) {
154
+ ExpoIapLog.failure("initConnection", e)
122
155
  promise.reject(OpenIapError.InitConnection.CODE, e.message, e)
123
156
  }
124
157
  }
@@ -126,35 +159,54 @@ class ExpoIapModule : Module() {
126
159
  }
127
160
 
128
161
  AsyncFunction("endConnection") { promise: Promise ->
162
+ ExpoIapLog.payload("endConnection", null)
129
163
  scope.launch {
130
164
  connectionMutex.withLock {
131
165
  runCatching { openIap.endConnection() }
132
166
  // Reset connection state and clear any buffered events
133
167
  connectionReady.set(false)
134
168
  pendingEvents.clear()
169
+ ExpoIapLog.result("endConnection", true)
135
170
  promise.resolve(true)
136
171
  }
137
172
  }
138
173
  }
139
174
 
140
175
  AsyncFunction("fetchProducts") { type: String, skuArr: Array<String>, promise: Promise ->
176
+ ExpoIapLog.payload(
177
+ "fetchProductsAndroid",
178
+ mapOf("type" to type, "skus" to skuArr.toList()),
179
+ )
141
180
  scope.launch {
142
181
  try {
143
- val reqType = ProductRequest.ProductRequestType.fromString(type)
144
- val products = openIap.fetchProducts(ProductRequest(skuArr.toList(), reqType))
145
- promise.resolve(products.map { it.toJSON() })
182
+ val queryType = parseProductQueryType(type)
183
+ val request = ProductRequest(skuArr.toList(), queryType)
184
+ val result = openIap.fetchProducts(request)
185
+ val payload =
186
+ when (result) {
187
+ is FetchProductsResultProducts -> result.value.orEmpty().map { it.toJson() }
188
+ is FetchProductsResultSubscriptions -> result.value.orEmpty().map { it.toJson() }
189
+ else -> emptyList<Map<String, Any?>>()
190
+ }
191
+ ExpoIapLog.result("fetchProductsAndroid", payload)
192
+ promise.resolve(payload)
146
193
  } catch (e: Exception) {
194
+ ExpoIapLog.failure("fetchProductsAndroid", e)
147
195
  promise.reject(OpenIapError.QueryProduct.CODE, e.message, null)
148
196
  }
149
197
  }
150
198
  }
151
199
 
152
200
  AsyncFunction("getAvailableItems") { promise: Promise ->
201
+ ExpoIapLog.payload("getAvailableItemsAndroid", null)
153
202
  scope.launch {
154
203
  try {
155
204
  val purchases = openIap.getAvailablePurchases(null)
156
- promise.resolve(purchases.map { it.toJSON() })
205
+ val payload = purchases.map { it.toJson() }
206
+ ExpoIapLog.result("getAvailableItemsAndroid", payload)
207
+ promise.resolve(payload)
157
208
  } catch (e: Exception) {
209
+ ExpoIapLog.failure("getAvailableItemsAndroid", e)
158
210
  promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
159
211
  }
160
212
  }
@@ -164,11 +216,22 @@ class ExpoIapModule : Module() {
164
216
  AsyncFunction("deepLinkToSubscriptionsAndroid") { params: Map<String, Any?>, promise: Promise ->
165
217
  val sku = (params["sku"] ?: params["skuAndroid"]) as? String
166
218
  val packageName = (params["packageName"] ?: params["packageNameAndroid"]) as? String
219
+ ExpoIapLog.payload(
220
+ "deepLinkToSubscriptionsAndroid",
221
+ mapOf("sku" to sku, "packageName" to packageName),
222
+ )
167
223
  scope.launch {
168
224
  try {
169
- openIap.deepLinkToSubscriptions(DeepLinkOptions(sku, packageName))
225
+ openIap.deepLinkToSubscriptions(
226
+ DeepLinkOptions(
227
+ packageNameAndroid = packageName,
228
+ skuAndroid = sku,
229
+ ),
230
+ )
231
+ ExpoIapLog.result("deepLinkToSubscriptionsAndroid", true)
170
232
  promise.resolve(null)
171
233
  } catch (e: Exception) {
234
+ ExpoIapLog.failure("deepLinkToSubscriptionsAndroid", e)
172
235
  promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
173
236
  }
174
237
  }
@@ -176,18 +239,22 @@ class ExpoIapModule : Module() {
176
239
 
177
240
  // Get Google Play storefront country code (Android)
178
241
  AsyncFunction("getStorefrontAndroid") { promise: Promise ->
242
+ ExpoIapLog.payload("getStorefrontAndroid", null)
179
243
  scope.launch {
180
244
  try {
181
245
  val code = openIap.getStorefront()
246
+ ExpoIapLog.result("getStorefrontAndroid", code)
182
247
  promise.resolve(code)
183
248
  } catch (e: Exception) {
249
+ ExpoIapLog.failure("getStorefrontAndroid", e)
184
250
  promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, e)
185
251
  }
186
252
  }
187
253
  }
188
254
 
189
255
  AsyncFunction("requestPurchase") { params: Map<String, Any?>, promise: Promise ->
190
- val type = params["type"] as String
256
+ ExpoIapLog.payload("requestPurchaseAndroid", params)
257
+ val type = params["type"] as? String
191
258
  val skus: List<String> =
192
259
  (params["skus"] as? List<*>)?.filterIsInstance<String>()
193
260
  ?: (params["skuArr"] as? List<*>)?.filterIsInstance<String>()
@@ -199,7 +266,7 @@ class ExpoIapModule : Module() {
199
266
  val isOfferPersonalized = params["isOfferPersonalized"] as? Boolean ?: false
200
267
  val offerTokenArr =
201
268
  (params["offerTokenArr"] as? List<*>)?.filterIsInstance<String>() ?: emptyList()
202
- val subscriptionOffersParam =
269
+ val explicitSubscriptionOffers =
203
270
  (params["subscriptionOffers"] as? List<*>)?.mapNotNull { rawOffer ->
204
271
  val offerMap = rawOffer as? Map<*, *> ?: return@mapNotNull null
205
272
  val sku = offerMap["sku"] as? String
@@ -207,67 +274,123 @@ class ExpoIapModule : Module() {
207
274
  if (sku.isNullOrEmpty() || offerToken.isNullOrEmpty()) {
208
275
  null
209
276
  } else {
210
- RequestSubscriptionAndroidProps.SubscriptionOffer(sku = sku, offerToken = offerToken)
277
+ AndroidSubscriptionOfferInput(offerToken = offerToken, sku = sku)
211
278
  }
212
279
  } ?: emptyList()
280
+ val purchaseToken =
281
+ (params["purchaseTokenAndroid"] ?: params["purchaseToken"]) as? String
282
+ val replacementMode =
283
+ (params["replacementModeAndroid"] ?: params["replacementMode"]) as? Number
213
284
 
214
- PromiseUtils.addPromiseForKey(PromiseUtils.PROMISE_BUY_ITEM, promise)
215
- scope.launch {
216
- try {
217
- openIap.setActivity(currentActivity)
218
- val reqType = ProductRequest.ProductRequestType.fromString(type)
219
- val subscriptionOffers =
220
- if (reqType == ProductRequest.ProductRequestType.Subs) {
221
- when {
222
- subscriptionOffersParam.isNotEmpty() -> subscriptionOffersParam
223
- offerTokenArr.isNotEmpty() ->
224
- skus.zip(offerTokenArr).mapNotNull { (sku, token) ->
225
- if (token.isNotEmpty()) {
226
- RequestSubscriptionAndroidProps.SubscriptionOffer(
227
- sku = sku,
228
- offerToken = token,
229
- )
230
- } else {
231
- null
232
- }
233
- }
234
- else -> emptyList()
235
- }
285
+ val productType =
286
+ when (parseProductQueryType(type)) {
287
+ ProductQueryType.Subs -> ProductQueryType.Subs
288
+ else -> ProductQueryType.InApp
289
+ }
290
+
291
+ val fallbackOffers =
292
+ if (explicitSubscriptionOffers.isEmpty() && offerTokenArr.isNotEmpty()) {
293
+ skus.zip(offerTokenArr).mapNotNull { (sku, token) ->
294
+ if (token.isNotEmpty()) {
295
+ AndroidSubscriptionOfferInput(offerToken = token, sku = sku)
236
296
  } else {
237
- emptyList()
297
+ null
238
298
  }
239
- val result =
240
- openIap.requestPurchase(
241
- RequestPurchaseParams(
242
- skus = skus,
299
+ }
300
+ } else {
301
+ emptyList()
302
+ }
303
+
304
+ val subscriptionOffers =
305
+ (explicitSubscriptionOffers.ifEmpty { fallbackOffers })
306
+ .takeIf { it.isNotEmpty() }
307
+
308
+ val requestProps =
309
+ when (productType) {
310
+ ProductQueryType.Subs -> {
311
+ val android =
312
+ RequestSubscriptionAndroidProps(
313
+ isOfferPersonalized = isOfferPersonalized,
243
314
  obfuscatedAccountIdAndroid = obfuscatedAccountId,
244
315
  obfuscatedProfileIdAndroid = obfuscatedProfileId,
245
- isOfferPersonalized = isOfferPersonalized,
316
+ purchaseTokenAndroid = purchaseToken,
317
+ replacementModeAndroid = replacementMode?.toInt(),
318
+ skus = skus,
246
319
  subscriptionOffers = subscriptionOffers,
247
- ),
248
- reqType,
320
+ )
321
+ RequestPurchaseProps(
322
+ request =
323
+ RequestPurchaseProps.Request.Subscription(
324
+ RequestSubscriptionPropsByPlatforms(android = android),
325
+ ),
326
+ type = ProductQueryType.Subs,
249
327
  )
250
- result.forEach { p ->
251
- try {
252
- emitOrQueue(EVENT_PURCHASE_UPDATED, p.toJSON())
253
- } catch (ex: Exception) {
254
- Log.e(TAG, "Failed to send PURCHASE_UPDATED event (requestPurchase)", ex)
328
+ }
329
+
330
+ else -> {
331
+ val android =
332
+ RequestPurchaseAndroidProps(
333
+ isOfferPersonalized = isOfferPersonalized,
334
+ obfuscatedAccountIdAndroid = obfuscatedAccountId,
335
+ obfuscatedProfileIdAndroid = obfuscatedProfileId,
336
+ skus = skus,
337
+ )
338
+ RequestPurchaseProps(
339
+ request =
340
+ RequestPurchaseProps.Request.Purchase(
341
+ RequestPurchasePropsByPlatforms(android = android),
342
+ ),
343
+ type = ProductQueryType.InApp,
344
+ )
345
+ }
346
+ }
347
+
348
+ PromiseUtils.addPromiseForKey(PromiseUtils.PROMISE_BUY_ITEM, promise)
349
+ scope.launch {
350
+ try {
351
+ openIap.setActivity(currentActivity)
352
+ val result = openIap.requestPurchase(requestProps)
353
+ val purchases =
354
+ when (result) {
355
+ is RequestPurchaseResultPurchases -> result.value.orEmpty()
356
+ is RequestPurchaseResultPurchase -> result.value?.let(::listOf).orEmpty()
357
+ else -> emptyList()
358
+ }
359
+ ExpoIapLog.result(
360
+ "requestPurchaseAndroid",
361
+ purchases.map { it.toJson() },
362
+ )
363
+ purchases.forEach { purchase ->
364
+ runCatching {
365
+ emitOrQueue(EVENT_PURCHASE_UPDATED, purchase.toJson())
366
+ }.onFailure { ex ->
367
+ Log.e(
368
+ TAG,
369
+ "Failed to send PURCHASE_UPDATED event (requestPurchase)",
370
+ ex,
371
+ )
255
372
  }
256
373
  }
257
- PromiseUtils.resolvePromisesForKey(PromiseUtils.PROMISE_BUY_ITEM, result.map { it.toJSON() })
374
+ PromiseUtils.resolvePromisesForKey(
375
+ PromiseUtils.PROMISE_BUY_ITEM,
376
+ purchases.map { it.toJson() },
377
+ )
258
378
  } catch (e: Exception) {
379
+ ExpoIapLog.failure("requestPurchaseAndroid", e)
259
380
  val errorMap =
260
381
  mapOf(
261
382
  "code" to OpenIapError.PurchaseFailed.CODE,
262
383
  "message" to (e.message ?: "Purchase failed"),
263
384
  "platform" to "android",
264
385
  )
265
- try {
266
- emitOrQueue(EVENT_PURCHASE_ERROR, errorMap)
267
- } catch (ex: Exception) {
268
- Log.e(TAG, "Failed to send PURCHASE_ERROR event (requestPurchase)", ex)
269
- }
270
- // Reject and clear any pending promises for this purchase flow
386
+ runCatching { emitOrQueue(EVENT_PURCHASE_ERROR, errorMap) }
387
+ .onFailure { ex ->
388
+ Log.e(
389
+ TAG,
390
+ "Failed to send PURCHASE_ERROR event (requestPurchase)",
391
+ ex,
392
+ )
393
+ }
271
394
  PromiseUtils.rejectPromisesForKey(
272
395
  PromiseUtils.PROMISE_BUY_ITEM,
273
396
  OpenIapError.PurchaseFailed.CODE,
@@ -279,11 +402,15 @@ class ExpoIapModule : Module() {
279
402
  }
280
403
 
281
404
  AsyncFunction("acknowledgePurchaseAndroid") { token: String, promise: Promise ->
405
+ ExpoIapLog.payload("acknowledgePurchaseAndroid", mapOf("token" to token))
282
406
  scope.launch {
283
407
  try {
284
408
  openIap.acknowledgePurchaseAndroid(token)
285
- promise.resolve(mapOf("responseCode" to 0))
409
+ val response = mapOf("responseCode" to 0)
410
+ ExpoIapLog.result("acknowledgePurchaseAndroid", response)
411
+ promise.resolve(response)
286
412
  } catch (e: Exception) {
413
+ ExpoIapLog.failure("acknowledgePurchaseAndroid", e)
287
414
  promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
288
415
  }
289
416
  }
@@ -291,11 +418,15 @@ class ExpoIapModule : Module() {
291
418
 
292
419
  // New name: consumePurchaseAndroid
293
420
  AsyncFunction("consumePurchaseAndroid") { token: String, promise: Promise ->
421
+ ExpoIapLog.payload("consumePurchaseAndroid", mapOf("token" to token))
294
422
  scope.launch {
295
423
  try {
296
424
  openIap.consumePurchaseAndroid(token)
297
- promise.resolve(mapOf("responseCode" to 0, "purchaseToken" to token))
425
+ val response = mapOf("responseCode" to 0, "purchaseToken" to token)
426
+ ExpoIapLog.result("consumePurchaseAndroid", response)
427
+ promise.resolve(response)
298
428
  } catch (e: Exception) {
429
+ ExpoIapLog.failure("consumePurchaseAndroid", e)
299
430
  promise.reject(OpenIapError.ServiceUnavailable.CODE, e.message, null)
300
431
  }
301
432
  }
package/build/index.d.ts CHANGED
@@ -1,17 +1,14 @@
1
- import { Product, Purchase, RequestPurchaseProps, RequestPurchasePropsByPlatforms, RequestSubscriptionPropsByPlatforms, ProductSubscription, VoidResult, ReceiptValidationResult } from './types';
2
- import { PurchaseError } from './purchase-error';
1
+ import type { MutationField, Product, ProductQueryType, Purchase, QueryField } from './types';
2
+ import { type PurchaseError } from './utils/errorMapping';
3
3
  export * from './types';
4
- export { ErrorCodeUtils, ErrorCodeMapping } from './purchase-error';
5
4
  export * from './modules/android';
6
5
  export * from './modules/ios';
7
6
  export { getActiveSubscriptions, hasActiveSubscriptions, } from './helpers/subscription';
8
- export declare const PI: any;
9
7
  export declare enum OpenIapEvent {
10
8
  PurchaseUpdated = "purchase-updated",
11
9
  PurchaseError = "purchase-error",
12
10
  PromotedProductIOS = "promoted-product-ios"
13
11
  }
14
- export declare function setValueAsync(value: string): any;
15
12
  type ExpoIapEventPayloads = {
16
13
  [OpenIapEvent.PurchaseUpdated]: Purchase;
17
14
  [OpenIapEvent.PurchaseError]: PurchaseError;
@@ -28,24 +25,7 @@ export declare const emitter: ExpoIapEmitter;
28
25
  /**
29
26
  * TODO(v3.1.0): Remove legacy 'inapp' alias once downstream apps migrate to 'in-app'.
30
27
  */
31
- export type ProductTypeInput = 'inapp' | 'in-app' | 'subs';
32
- export type InAppTypeInput = Exclude<ProductTypeInput, 'subs'>;
33
- type PurchaseRequestInApp = {
34
- request: RequestPurchasePropsByPlatforms;
35
- type?: InAppTypeInput;
36
- };
37
- type PurchaseRequestSubscription = {
38
- request: RequestSubscriptionPropsByPlatforms;
39
- type: 'subs';
40
- };
41
- export type PurchaseRequestInput = PurchaseRequestInApp | PurchaseRequestSubscription;
42
- export type PurchaseRequest = {
43
- request: RequestPurchaseProps;
44
- type?: InAppTypeInput;
45
- } | {
46
- request: RequestSubscriptionPropsByPlatforms;
47
- type: 'subs';
48
- };
28
+ export type ProductTypeInput = ProductQueryType | 'inapp';
49
29
  export declare const purchaseUpdatedListener: (listener: (event: Purchase) => void) => {
50
30
  remove: () => void;
51
31
  };
@@ -75,55 +55,18 @@ export declare const purchaseErrorListener: (listener: (error: PurchaseError) =>
75
55
  export declare const promotedProductListenerIOS: (listener: (product: Product) => void) => {
76
56
  remove: () => void;
77
57
  };
78
- export declare function initConnection(): Promise<boolean>;
79
- export declare function endConnection(): Promise<boolean>;
58
+ export declare const initConnection: MutationField<'initConnection'>;
59
+ export declare const endConnection: MutationField<'endConnection'>;
80
60
  /**
81
61
  * Fetch products with unified API (v2.7.0+)
82
62
  *
83
- * @param params - Product fetch configuration
84
- * @param params.skus - Array of product SKUs to fetch
85
- * @param params.type - Type of products: 'in-app' for regular products (default) or 'subs' for subscriptions
86
- *
87
- * @example
88
- * ```typescript
89
- * // Regular products
90
- * const products = await fetchProducts({
91
- * skus: ['product1', 'product2'],
92
- * type: 'in-app'
93
- * });
94
- *
95
- * // Subscriptions
96
- * const subscriptions = await fetchProducts({
97
- * skus: ['sub1', 'sub2'],
98
- * type: 'subs'
99
- * });
100
- * ```
63
+ * @param request - Product fetch configuration
64
+ * @param request.skus - Array of product SKUs to fetch
65
+ * @param request.type - Product query type: 'in-app', 'subs', or 'all'
101
66
  */
102
- export declare const fetchProducts: ({ skus, type, }: {
103
- skus: string[];
104
- type?: ProductTypeInput;
105
- }) => Promise<Product[] | ProductSubscription[]>;
106
- export declare const getAvailablePurchases: ({ alsoPublishToEventListenerIOS, onlyIncludeActiveItemsIOS, }?: {
107
- alsoPublishToEventListenerIOS?: boolean;
108
- onlyIncludeActiveItemsIOS?: boolean;
109
- }) => Promise<Purchase[]>;
110
- /**
111
- * Restore completed transactions (cross-platform behavior)
112
- *
113
- * - iOS: perform a lightweight sync to refresh transactions and ignore sync errors,
114
- * then fetch available purchases to surface restored items to the app.
115
- * - Android: simply fetch available purchases (restoration happens via query).
116
- *
117
- * This helper returns the restored/available purchases so callers can update UI/state.
118
- *
119
- * @param options.alsoPublishToEventListenerIOS - iOS only: whether to also publish to the event listener
120
- * @param options.onlyIncludeActiveItemsIOS - iOS only: whether to only include active items
121
- * @returns Promise resolving to the list of available/restored purchases
122
- */
123
- export declare const restorePurchases: (options?: {
124
- alsoPublishToEventListenerIOS?: boolean;
125
- onlyIncludeActiveItemsIOS?: boolean;
126
- }) => Promise<Purchase[]>;
67
+ export declare const fetchProducts: QueryField<'fetchProducts'>;
68
+ export declare const getAvailablePurchases: QueryField<'getAvailablePurchases'>;
69
+ export declare const getStorefront: QueryField<'getStorefrontIOS'>;
127
70
  /**
128
71
  * Request a purchase for products or subscriptions.
129
72
  *
@@ -155,48 +98,19 @@ export declare const restorePurchases: (options?: {
155
98
  * });
156
99
  * ```
157
100
  */
158
- export declare const requestPurchase: (requestObj: PurchaseRequestInput) => Promise<Purchase | Purchase[] | void>;
159
- export declare const finishTransaction: ({ purchase, isConsumable, }: {
160
- purchase: Purchase;
161
- isConsumable?: boolean;
162
- }) => Promise<VoidResult | boolean>;
101
+ export declare const requestPurchase: MutationField<'requestPurchase'>;
102
+ export declare const finishTransaction: MutationField<'finishTransaction'>;
163
103
  /**
164
- * Retrieves the current storefront information from iOS App Store
165
- *
166
- * @returns Promise resolving to the storefront country code
167
- * @throws Error if called on non-iOS platform
168
- *
169
- * @example
170
- * ```typescript
171
- * const storefront = await getStorefrontIOS();
172
- * console.log(storefront); // 'US'
173
- * ```
174
- *
175
- * @platform iOS
176
- */
177
- export declare const getStorefrontIOS: () => Promise<string>;
178
- /**
179
- * Gets the storefront country code from the underlying native store.
180
- * Returns a two-letter country code such as 'US', 'KR', or empty string on failure.
104
+ * Restore completed transactions (cross-platform behavior)
181
105
  *
182
- * @platform ios
183
- * @platform android
184
- */
185
- export declare const getStorefront: () => Promise<string>;
186
- /**
187
- * Internal receipt validation function (NOT RECOMMENDED for production use)
106
+ * - iOS: perform a lightweight sync to refresh transactions and ignore sync errors,
107
+ * then fetch available purchases to surface restored items to the app.
108
+ * - Android: simply fetch available purchases (restoration happens via query).
188
109
  *
189
- * WARNING: This function performs client-side validation which is NOT secure.
190
- * For production apps, always validate receipts on your secure server:
191
- * - iOS: Send receipt data to Apple's verification endpoint from your server
192
- * - Android: Use Google Play Developer API with service account credentials
110
+ * This helper triggers the refresh flows but does not return the purchases; consumers should
111
+ * call `getAvailablePurchases` or rely on hook state to inspect the latest items.
193
112
  */
194
- export declare const validateReceipt: (sku: string, androidOptions?: {
195
- packageName: string;
196
- productToken: string;
197
- accessToken: string;
198
- isSub?: boolean;
199
- }) => Promise<ReceiptValidationResult>;
113
+ export declare const restorePurchases: MutationField<'restorePurchases'>;
200
114
  /**
201
115
  * Deeplinks to native interface that allows users to manage their subscriptions
202
116
  * @param options.skuAndroid - Required for Android to locate specific subscription (ignored on iOS)
@@ -215,10 +129,17 @@ export declare const validateReceipt: (sku: string, androidOptions?: {
215
129
  * packageNameAndroid: 'com.example.app'
216
130
  * });
217
131
  */
218
- export declare const deepLinkToSubscriptions: (options: {
219
- skuAndroid?: string;
220
- packageNameAndroid?: string;
221
- }) => Promise<void>;
132
+ export declare const deepLinkToSubscriptions: MutationField<'deepLinkToSubscriptions'>;
133
+ /**
134
+ * Internal receipt validation function (NOT RECOMMENDED for production use)
135
+ *
136
+ * WARNING: This function performs client-side validation which is NOT secure.
137
+ * For production apps, always validate receipts on your secure server:
138
+ * - iOS: Send receipt data to Apple's verification endpoint from your server
139
+ * - Android: Use Google Play Developer API with service account credentials
140
+ */
141
+ export declare const validateReceipt: MutationField<'validateReceipt'>;
222
142
  export * from './useIAP';
223
- export * from './utils/errorMapping';
143
+ export { ErrorCodeUtils, ErrorCodeMapping, createPurchaseError, createPurchaseErrorFromPlatform, } from './utils/errorMapping';
144
+ export type { PurchaseError as ExpoPurchaseError, PurchaseErrorProps, } from './utils/errorMapping';
224
145
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAIV,aAAa,EAGb,OAAO,EACP,gBAAgB,EAEhB,QAAQ,EAER,UAAU,EAOX,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAsB,KAAK,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAG7E,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAG9B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAGhC,oBAAY,YAAY;IACtB,eAAe,qBAAqB;IACpC,aAAa,mBAAmB;IAChC,kBAAkB,yBAAyB;CAC5C;AAED,KAAK,oBAAoB,GAAG;IAC1B,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC;IACzC,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IAC5C,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC5C,CAAC;AAEF,KAAK,oBAAoB,CAAC,CAAC,SAAS,YAAY,IAAI,CAClD,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAC7B,IAAI,CAAC;AAEV,KAAK,cAAc,GAAG;IACpB,WAAW,CAAC,CAAC,SAAS,YAAY,EAChC,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAChC;QAAC,MAAM,EAAE,MAAM,IAAI,CAAA;KAAC,CAAC;IACxB,cAAc,CAAC,CAAC,SAAS,YAAY,EACnC,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAChC,IAAI,CAAC;CACT,CAAC;AAGF,eAAO,MAAM,OAAO,EACa,cAAc,CAAC;AAEhD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG,OAAO,CAAC;AA+C1D,eAAO,MAAM,uBAAuB,GAClC,UAAU,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI;YA9DvB,MAAM,IAAI;CAyEvB,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI;YA5E5B,MAAM,IAAI;CAsFvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,0BAA0B,GACrC,UAAU,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI;YA7GxB,MAAM,IAAI;CAsHvB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAAa,CAAC,gBAAgB,CAC3B,CAAC;AAEjC,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,eAAe,CAC1B,CAAC;AAEhC;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,EAAE,UAAU,CAAC,eAAe,CA6DrD,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,UAAU,CAC5C,uBAAuB,CAoBxB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,UAAU,CAAC,kBAAkB,CASxD,CAAC;AA+BF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,iBAAiB,CAkI5D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,aAAa,CAAC,mBAAmB,CA+BhE,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAAa,CAAC,kBAAkB,CAS9D,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,EAAE,aAAa,CACjD,yBAAyB,CAa1B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC,iBAAiB,CA8B5D,CAAC;AAEF,cAAc,UAAU,CAAC;AACzB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,+BAA+B,GAChC,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,aAAa,IAAI,iBAAiB,EAClC,kBAAkB,GACnB,MAAM,sBAAsB,CAAC"}