expo-iap 2.2.4-rc.2 → 2.2.5-rc.1

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.
@@ -32,6 +32,7 @@ class ExpoIapModule :
32
32
  const val E_INIT_CONNECTION = "E_INIT_CONNECTION"
33
33
  const val E_QUERY_PRODUCT = "E_QUERY_PRODUCT"
34
34
  const val EMPTY_SKU_LIST = "EMPTY_SKU_LIST"
35
+ private const val PROMISE_BUY_ITEM = "PROMISE_BUY_ITEM"
35
36
  }
36
37
 
37
38
  object IapEvent {
@@ -63,6 +64,7 @@ class ExpoIapModule :
63
64
  error["code"] = errorData.code
64
65
  error["message"] = errorData.message
65
66
  sendEvent(IapEvent.PURCHASE_ERROR, error.toMap())
67
+ PromiseUtils.rejectPromisesWithBillingError(PROMISE_BUY_ITEM, responseCode)
66
68
  return
67
69
  }
68
70
 
@@ -92,6 +94,7 @@ class ExpoIapModule :
92
94
  promiseItems.add(item.toMap())
93
95
  sendEvent(IapEvent.PURCHASE_UPDATED, item.toMap())
94
96
  }
97
+ PromiseUtils.resolvePromisesForKey(PROMISE_BUY_ITEM, promiseItems)
95
98
  } else {
96
99
  val result =
97
100
  mutableMapOf<String, Any?>(
@@ -101,6 +104,7 @@ class ExpoIapModule :
101
104
  "The purchases are null. This is a normal behavior if you have requested DEFERRED proration. If not please report an issue.",
102
105
  )
103
106
  sendEvent(IapEvent.PURCHASE_UPDATED, result.toMap())
107
+ PromiseUtils.resolvePromisesForKey(PROMISE_BUY_ITEM, result)
104
108
  }
105
109
  }
106
110
 
@@ -301,22 +305,25 @@ class ExpoIapModule :
301
305
  val isOfferPersonalized = params["isOfferPersonalized"] as? Boolean ?: false
302
306
 
303
307
  if (currentActivity == null) {
304
- throw Exception("getCurrentActivity returned null")
308
+ promise.reject("E_UNKNOWN", "getCurrentActivity returned null", null)
309
+ return@AsyncFunction
305
310
  }
306
311
 
307
312
  ensureConnection(promise) { billingClient ->
313
+ PromiseUtils.addPromiseForKey(PROMISE_BUY_ITEM, promise)
314
+
308
315
  if (type == BillingClient.ProductType.SUBS && skuArr.size != offerTokenArr.size) {
309
- val debugMessage =
310
- "The number of skus (${skuArr.size}) must match: the number of offerTokens (${offerTokenArr.size}) for Subscriptions"
316
+ val debugMessage = "The number of skus (${skuArr.size}) must match: the number of offerTokens (${offerTokenArr.size}) for Subscriptions"
311
317
  sendEvent(
312
318
  IapEvent.PURCHASE_ERROR,
313
319
  mapOf(
314
320
  "debugMessage" to debugMessage,
315
321
  "code" to "E_SKU_OFFER_MISMATCH",
316
322
  "message" to debugMessage,
317
- ),
323
+ )
318
324
  )
319
- throw Exception(debugMessage)
325
+ promise.reject("E_SKU_OFFER_MISMATCH", debugMessage, null)
326
+ return@ensureConnection
320
327
  }
321
328
 
322
329
  val productParamsList =
@@ -334,7 +341,8 @@ class ExpoIapModule :
334
341
  "productId" to sku,
335
342
  ),
336
343
  )
337
- throw Exception(debugMessage)
344
+ promise.reject("E_SKU_NOT_FOUND", debugMessage, null)
345
+ return@ensureConnection
338
346
  }
339
347
 
340
348
  val productDetailParams =
@@ -378,7 +386,6 @@ class ExpoIapModule :
378
386
  }
379
387
  subscriptionUpdateParams.setSubscriptionReplacementMode(mode)
380
388
  }
381
-
382
389
  builder.setSubscriptionUpdateParams(subscriptionUpdateParams.build())
383
390
  }
384
391
 
@@ -389,14 +396,10 @@ class ExpoIapModule :
389
396
  val billingResult = billingClient.launchBillingFlow(currentActivity, flowParams)
390
397
 
391
398
  if (billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
392
- promise.reject(
393
- "Billing Error",
394
- billingResult.debugMessage,
395
- null,
396
- )
399
+ val errorData = PlayUtils.getBillingResponseData(billingResult.responseCode)
400
+ promise.reject(errorData.code, billingResult.debugMessage, null)
401
+ return@ensureConnection
397
402
  }
398
-
399
- promise.resolve(true)
400
403
  }
401
404
  }
402
405
 
@@ -29,6 +29,76 @@ object PromiseUtils {
29
29
  const val E_DEVELOPER_ERROR = "E_DEVELOPER_ERROR"
30
30
  const val E_BILLING_RESPONSE_JSON_PARSE_ERROR = "E_BILLING_RESPONSE_JSON_PARSE_ERROR"
31
31
  const val E_CONNECTION_CLOSED = "E_CONNECTION_CLOSED"
32
+
33
+ fun addPromiseForKey(
34
+ key: String,
35
+ promise: Promise,
36
+ ) {
37
+ promises.getOrPut(key) { mutableListOf() }.add(promise)
38
+ }
39
+
40
+ fun resolvePromisesForKey(
41
+ key: String,
42
+ value: Any?,
43
+ ) {
44
+ promises[key]?.forEach { promise ->
45
+ promise.safeResolve(value)
46
+ }
47
+ promises.remove(key)
48
+ }
49
+
50
+ fun rejectAllPendingPromises() {
51
+ promises.flatMap { it.value }.forEach { promise ->
52
+ promise.safeReject(E_CONNECTION_CLOSED, "Connection has been closed", null)
53
+ }
54
+ promises.clear()
55
+ }
56
+
57
+ fun rejectPromisesForKey(
58
+ key: String,
59
+ code: String,
60
+ message: String?,
61
+ err: Exception?,
62
+ ) {
63
+ promises[key]?.forEach { promise ->
64
+ promise.safeReject(code, message, err)
65
+ }
66
+ promises.remove(key)
67
+ }
68
+ }
69
+
70
+ const val TAG = "IapPromises"
71
+
72
+ fun Promise.safeResolve(value: Any?) {
73
+ try {
74
+ this.resolve(value)
75
+ } catch (e: RuntimeException) {
76
+ Log.d(TAG, "Already consumed ${e.message}")
77
+ }
78
+ }
79
+
80
+ fun Promise.safeReject(message: String) = this.safeReject(message, null, null)
81
+
82
+ fun Promise.safeReject(
83
+ code: String,
84
+ message: String?,
85
+ ) = this.safeReject(code, message, null)
86
+
87
+ fun Promise.safeReject(
88
+ code: String,
89
+ throwable: Throwable?,
90
+ ) = this.safeReject(code, null, throwable)
91
+
92
+ fun Promise.safeReject(
93
+ code: String,
94
+ message: String?,
95
+ throwable: Throwable?,
96
+ ) {
97
+ try {
98
+ this.reject(code, message, throwable)
99
+ } catch (e: RuntimeException) {
100
+ Log.d(TAG, "Already consumed ${e.message}")
101
+ }
32
102
  }
33
103
 
34
104
  object PlayUtils {
@@ -1 +1 @@
1
- {"version":3,"file":"useIap.d.ts","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,EAEpB,MAAM,iBAAiB,CAAC;AAKzB,KAAK,UAAU,GAAG;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mBAAmB,EAAE,eAAe,EAAE,CAAC;IACvC,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,kBAAkB,EAAE,eAAe,EAAE,CAAC;IACtC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,aAAa,CAAC;IACrC,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,YAAY,EACZ,uBAAuB,GACxB,EAAE;QACD,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,CAAC;AAEF,wBAAgB,MAAM,IAAI,UAAU,CAsInC"}
1
+ {"version":3,"file":"useIap.d.ts","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,OAAO,EACP,eAAe,EACf,QAAQ,EACR,aAAa,EACb,cAAc,EACd,mBAAmB,EAEpB,MAAM,iBAAiB,CAAC;AAKzB,KAAK,UAAU,GAAG;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,mBAAmB,EAAE,eAAe,EAAE,CAAC;IACvC,aAAa,EAAE,mBAAmB,EAAE,CAAC;IACrC,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,kBAAkB,EAAE,eAAe,EAAE,CAAC;IACtC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,aAAa,CAAC;IACrC,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,YAAY,EACZ,uBAAuB,GACxB,EAAE;QACD,QAAQ,EAAE,QAAQ,CAAC;QACnB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;KAClC,KAAK,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,qBAAqB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,CAAC;AAEF,wBAAgB,MAAM,IAAI,UAAU,CAmInC"}
package/build/useIap.js CHANGED
@@ -1,4 +1,4 @@
1
- import { endConnection, initConnection, purchaseErrorListener, purchaseUpdatedListener, transactionUpdatedIos, getProducts, getAvailablePurchases, getPurchaseHistory, getSubscriptions, } from './';
1
+ import { endConnection, initConnection, purchaseErrorListener, purchaseUpdatedListener, transactionUpdatedIos, getProducts, getAvailablePurchases, getPurchaseHistory, finishTransaction as finishTransactionInternal, getSubscriptions, } from './';
2
2
  import { useCallback, useEffect, useState, useRef } from 'react';
3
3
  import { Platform } from 'react-native';
4
4
  export function useIAP() {
@@ -24,12 +24,11 @@ export function useIAP() {
24
24
  const requestPurchaseHistories = useCallback(async () => {
25
25
  setPurchaseHistories(await getPurchaseHistory());
26
26
  }, []);
27
- const finishTransaction = useCallback(async ({ purchase, isConsumable, developerPayloadAndroid, }) => {
27
+ const finishTransaction = useCallback(async ({ purchase, isConsumable, }) => {
28
28
  try {
29
- return await finishTransaction({
29
+ return await finishTransactionInternal({
30
30
  purchase,
31
31
  isConsumable,
32
- developerPayloadAndroid,
33
32
  });
34
33
  }
35
34
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACrB,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,IAAI,CAAC;AACZ,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,OAAO,CAAC;AAY/D,OAAO,EAAC,QAAQ,EAAC,MAAM,cAAc,CAAC;AA0BtC,MAAM,UAAU,MAAM;IACpB,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC3D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAY,EAAE,CAAC,CAAC;IACxD,MAAM,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,QAAQ,CAE5D,EAAE,CAAC,CAAC;IACN,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAwB,EAAE,CAAC,CAAC;IAC9E,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CACxD,EAAE,CACH,CAAC;IACF,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,GAAG,QAAQ,CAE1D,EAAE,CAAC,CAAC;IACN,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,EAAmB,CAAC;IAC1E,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GACnD,QAAQ,EAAiB,CAAC;IAE5B,2BAA2B;IAC3B,MAAM,gBAAgB,GAAG,MAAM,CAI5B,EAAE,CAAC,CAAC;IAEP,MAAM,eAAe,GAAG,WAAW,CAAC,KAAK,EAAE,IAAc,EAAiB,EAAE;QAC1E,WAAW,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,oBAAoB,GAAG,WAAW,CACtC,KAAK,EAAE,IAAc,EAAiB,EAAE;QACtC,gBAAgB,CAAC,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,yBAAyB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACtE,qBAAqB,CAAC,MAAM,qBAAqB,EAAE,CAAC,CAAC;IACvD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,wBAAwB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACrE,oBAAoB,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC;IACnD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,iBAAiB,GAAG,WAAW,CACnC,KAAK,EAAE,EACL,QAAQ,EACR,YAAY,EACZ,uBAAuB,GAKxB,EAAqD,EAAE;QACtD,IAAI,CAAC;YACH,OAAO,MAAM,iBAAiB,CAAC;gBAC7B,QAAQ;gBACR,YAAY;gBACZ,uBAAuB;aACxB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,IAAI,QAAQ,CAAC,EAAE,KAAK,eAAe,EAAE,EAAE,EAAE,CAAC;gBACxC,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAChC,CAAC;YACD,IAAI,QAAQ,CAAC,EAAE,KAAK,oBAAoB,EAAE,SAAS,EAAE,CAAC;gBACpD,uBAAuB,CAAC,SAAS,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC,EACD,CAAC,eAAe,EAAE,EAAE,EAAE,oBAAoB,EAAE,SAAS,CAAC,CACvD,CAAC;IAEF,MAAM,wBAAwB,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACrE,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;QACtC,YAAY,CAAC,MAAM,CAAC,CAAC;QAErB,IAAI,MAAM,EAAE,CAAC;YACX,gBAAgB,CAAC,OAAO,CAAC,cAAc,GAAG,uBAAuB,CAC/D,KAAK,EAAE,QAAyC,EAAE,EAAE;gBAClD,uBAAuB,CAAC,SAAS,CAAC,CAAC;gBACnC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;YAC/B,CAAC,CACF,CAAC;YAEF,gBAAgB,CAAC,OAAO,CAAC,aAAa,GAAG,qBAAqB,CAC5D,CAAC,KAAoB,EAAE,EAAE;gBACvB,kBAAkB,CAAC,SAAS,CAAC,CAAC;gBAC9B,uBAAuB,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;gBAC1B,gBAAgB,CAAC,OAAO,CAAC,mBAAmB,GAAG,qBAAqB,CAClE,CAAC,KAAuB,EAAE,EAAE;oBAC1B,sBAAsB,CAAC,CAAC,YAAY,EAAE,EAAE,CACtC,KAAK,CAAC,WAAW;wBACf,CAAC,CAAC,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC;wBACtC,CAAC,CAAC,YAAY,CACjB,CAAC;gBACJ,CAAC,CACF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,wBAAwB,EAAE,CAAC;QAC3B,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAEtD,OAAO,GAAG,EAAE;YACV,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;YAC9C,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;YAC7C,oBAAoB,CAAC,mBAAmB,EAAE,MAAM,EAAE,CAAC;YACnD,aAAa,EAAE,CAAC;YAChB,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC;IAE/B,OAAO;QACL,SAAS;QACT,QAAQ;QACR,mBAAmB;QACnB,aAAa;QACb,iBAAiB;QACjB,iBAAiB;QACjB,kBAAkB;QAClB,eAAe;QACf,oBAAoB;QACpB,WAAW,EAAE,eAAe;QAC5B,gBAAgB,EAAE,oBAAoB;QACtC,qBAAqB,EAAE,yBAAyB;QAChD,oBAAoB,EAAE,wBAAwB;KAC/C,CAAC;AACJ,CAAC","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n getSubscriptions,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {TransactionEvent} from './modules/ios';\nimport {Subscription} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\ntype IAP_STATUS = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n finishTransaction: ({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n developerPayloadAndroid?: string;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: () => Promise<void>;\n getPurchaseHistories: () => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n};\n\nexport function useIAP(): IAP_STATUS {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n // 구독을 훅 인스턴스별로 관리하기 위한 ref\n const subscriptionsRef = useRef<{\n purchaseUpdate?: Subscription;\n purchaseError?: Subscription;\n promotedProductsIos?: Subscription;\n }>({});\n\n const requestProducts = useCallback(async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n }, []);\n\n const requestSubscriptions = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const requestAvailablePurchases = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const requestPurchaseHistories = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n developerPayloadAndroid?: string;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransaction({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n setCurrentPurchase(undefined);\n }\n if (purchase.id === currentPurchaseError?.productId) {\n setCurrentPurchaseError(undefined);\n }\n }\n },\n [currentPurchase?.id, currentPurchaseError?.productId],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n if (Platform.OS === 'ios') {\n subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(\n (event: TransactionEvent) => {\n setPromotedProductsIOS((prevProducts) =>\n event.transaction\n ? [...prevProducts, event.transaction]\n : prevProducts,\n );\n },\n );\n }\n }\n }, []);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n getProducts: requestProducts,\n getSubscriptions: requestSubscriptions,\n getAvailablePurchases: requestAvailablePurchases,\n getPurchaseHistories: requestPurchaseHistories,\n };\n}\n"]}
1
+ {"version":3,"file":"useIap.js","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n endConnection,\n initConnection,\n purchaseErrorListener,\n purchaseUpdatedListener,\n transactionUpdatedIos,\n getProducts,\n getAvailablePurchases,\n getPurchaseHistory,\n finishTransaction as finishTransactionInternal,\n getSubscriptions,\n} from './';\nimport {useCallback, useEffect, useState, useRef} from 'react';\nimport {\n Product,\n ProductPurchase,\n Purchase,\n PurchaseError,\n PurchaseResult,\n SubscriptionProduct,\n SubscriptionPurchase,\n} from './ExpoIap.types';\nimport {TransactionEvent} from './modules/ios';\nimport {Subscription} from 'expo-modules-core';\nimport {Platform} from 'react-native';\n\ntype IAP_STATUS = {\n connected: boolean;\n products: Product[];\n promotedProductsIOS: ProductPurchase[];\n subscriptions: SubscriptionProduct[];\n purchaseHistories: ProductPurchase[];\n availablePurchases: ProductPurchase[];\n currentPurchase?: ProductPurchase;\n currentPurchaseError?: PurchaseError;\n finishTransaction: ({\n purchase,\n isConsumable,\n developerPayloadAndroid,\n }: {\n purchase: Purchase;\n isConsumable?: boolean;\n developerPayloadAndroid?: string;\n }) => Promise<string | boolean | PurchaseResult | void>;\n getAvailablePurchases: () => Promise<void>;\n getPurchaseHistories: () => Promise<void>;\n getProducts: (skus: string[]) => Promise<void>;\n getSubscriptions: (skus: string[]) => Promise<void>;\n};\n\nexport function useIAP(): IAP_STATUS {\n const [connected, setConnected] = useState<boolean>(false);\n const [products, setProducts] = useState<Product[]>([]);\n const [promotedProductsIOS, setPromotedProductsIOS] = useState<\n ProductPurchase[]\n >([]);\n const [subscriptions, setSubscriptions] = useState<SubscriptionProduct[]>([]);\n const [purchaseHistories, setPurchaseHistories] = useState<ProductPurchase[]>(\n [],\n );\n const [availablePurchases, setAvailablePurchases] = useState<\n ProductPurchase[]\n >([]);\n const [currentPurchase, setCurrentPurchase] = useState<ProductPurchase>();\n const [currentPurchaseError, setCurrentPurchaseError] =\n useState<PurchaseError>();\n\n // 구독을 훅 인스턴스별로 관리하기 위한 ref\n const subscriptionsRef = useRef<{\n purchaseUpdate?: Subscription;\n purchaseError?: Subscription;\n promotedProductsIos?: Subscription;\n }>({});\n\n const requestProducts = useCallback(async (skus: string[]): Promise<void> => {\n setProducts(await getProducts(skus));\n }, []);\n\n const requestSubscriptions = useCallback(\n async (skus: string[]): Promise<void> => {\n setSubscriptions(await getSubscriptions(skus));\n },\n [],\n );\n\n const requestAvailablePurchases = useCallback(async (): Promise<void> => {\n setAvailablePurchases(await getAvailablePurchases());\n }, []);\n\n const requestPurchaseHistories = useCallback(async (): Promise<void> => {\n setPurchaseHistories(await getPurchaseHistory());\n }, []);\n\n const finishTransaction = useCallback(\n async ({\n purchase,\n isConsumable,\n }: {\n purchase: ProductPurchase;\n isConsumable?: boolean;\n }): Promise<string | boolean | PurchaseResult | void> => {\n try {\n return await finishTransactionInternal({\n purchase,\n isConsumable,\n });\n } catch (err) {\n throw err;\n } finally {\n if (purchase.id === currentPurchase?.id) {\n setCurrentPurchase(undefined);\n }\n if (purchase.id === currentPurchaseError?.productId) {\n setCurrentPurchaseError(undefined);\n }\n }\n },\n [currentPurchase?.id, currentPurchaseError?.productId],\n );\n\n const initIapWithSubscriptions = useCallback(async (): Promise<void> => {\n const result = await initConnection();\n setConnected(result);\n\n if (result) {\n subscriptionsRef.current.purchaseUpdate = purchaseUpdatedListener(\n async (purchase: Purchase | SubscriptionPurchase) => {\n setCurrentPurchaseError(undefined);\n setCurrentPurchase(purchase);\n },\n );\n\n subscriptionsRef.current.purchaseError = purchaseErrorListener(\n (error: PurchaseError) => {\n setCurrentPurchase(undefined);\n setCurrentPurchaseError(error);\n },\n );\n\n if (Platform.OS === 'ios') {\n subscriptionsRef.current.promotedProductsIos = transactionUpdatedIos(\n (event: TransactionEvent) => {\n setPromotedProductsIOS((prevProducts) =>\n event.transaction\n ? [...prevProducts, event.transaction]\n : prevProducts,\n );\n },\n );\n }\n }\n }, []);\n\n useEffect(() => {\n initIapWithSubscriptions();\n const currentSubscriptions = subscriptionsRef.current;\n\n return () => {\n currentSubscriptions.purchaseUpdate?.remove();\n currentSubscriptions.purchaseError?.remove();\n currentSubscriptions.promotedProductsIos?.remove();\n endConnection();\n setConnected(false);\n };\n }, [initIapWithSubscriptions]);\n\n return {\n connected,\n products,\n promotedProductsIOS,\n subscriptions,\n purchaseHistories,\n finishTransaction,\n availablePurchases,\n currentPurchase,\n currentPurchaseError,\n getProducts: requestProducts,\n getSubscriptions: requestSubscriptions,\n getAvailablePurchases: requestAvailablePurchases,\n getPurchaseHistories: requestPurchaseHistories,\n };\n}\n"]}
package/iap.md CHANGED
@@ -142,17 +142,39 @@ This section describes purchase properties in `expo-iap`.
142
142
  | `id` | `string` | Purchased product ID |
143
143
  | `transactionId` | `string?` | Transaction ID (optional) |
144
144
  | `transactionDate` | `number` | Unix timestamp |
145
- | `purchaseTokenAndroid` | `string?` | Purchase token (optional) |
145
+ | `transactionReceipt` | `string` | Receipt data |
146
146
 
147
147
  ### Android-Only Purchase Types
148
148
 
149
- - **`ProductPurchase`**: Adds `ids`, `dataAndroid`, `signatureAndroid`, `purchaseStateAndroid`.
150
- - **`SubscriptionPurchase`**: Includes `autoRenewingAndroid`.
149
+ - **`ProductPurchase`**:
150
+ - Adds the following properties specific to in-app product purchases:
151
+ - **`ids`**: `string[]` - A list of product IDs associated with the purchase (for multi-item purchases).
152
+ - **`dataAndroid`**: `string` - The raw purchase data from Google Play (e.g., JSON payload).
153
+ - **`signatureAndroid`**: `string` - The cryptographic signature from Google Play to verify the purchase's authenticity.
154
+ - **`purchaseStateAndroid`**: `number` - The state of the purchase (e.g., 0 = purchased, 1 = canceled, 2 = pending).
155
+
156
+ - **`SubscriptionPurchase`**:
157
+ - Extends the base properties and includes:
158
+ - **`autoRenewingAndroid`**: `boolean` - Indicates whether the subscription automatically renews (true) or not (false).
159
+
160
+ - **`purchaseTokenAndroid`**:
161
+ - **Description**: A unique identifier provided by Google Play for each purchase, used to track, verify, and manage the transaction. For example, it is required to "consume" a consumable product (e.g., in-game coins) or query purchase details via the Google Play Developer API.
151
162
 
152
163
  ### iOS-Only Purchase Types
153
164
 
154
- - **`ProductPurchase`**: Adds fields like `quantityIos`, `expirationDateIos`, `subscriptionGroupIdIos`.
155
- - **`SubscriptionPurchase`**: Extends with subscription-specific handling.
165
+ - **`ProductPurchase`**:
166
+ - Extends the base purchase properties with iOS-specific fields:
167
+ - **`quantityIos`**: `number` - The quantity of the product purchased (e.g., how many units of an item were bought).
168
+ - **`expirationDateIos`**: `number?` - The expiration date of the purchase as a Unix timestamp (in milliseconds), if applicable (optional, may be null for non-expiring products).
169
+ - **`subscriptionGroupIdIos`**: `string?` - The identifier of the subscription group this product belongs to, used for managing related subscriptions in the App Store (optional, may be null for non-subscription products).
170
+
171
+ - **`SubscriptionPurchase`**:
172
+ - Extends the base purchase properties with iOS-specific subscription handling:
173
+ - Includes all fields from `ProductPurchase` where applicable, plus additional subscription-specific logic.
174
+ - May include fields like:
175
+ - **`expirationDateIos`**: `number?` - The date and time when the subscription expires, represented as a Unix timestamp (in milliseconds), unless auto-renewed.
176
+ - **`autoRenewingIos`**: `boolean` - Indicates whether the subscription is set to automatically renew (true) or not (false).
177
+ - Handles subscription-specific features such as renewals, grace periods, and App Store receipt validation.
156
178
 
157
179
  ## Implementation Notes
158
180
 
@@ -17,105 +17,84 @@ struct IapEvent {
17
17
 
18
18
  @available(iOS 15.0, *)
19
19
  func serializeTransaction(_ transaction: Transaction) -> [String: Any?] {
20
- // Determine if this is a subscription by productType or expirationDate
21
20
  let isSubscription =
22
21
  transaction.productType.rawValue.lowercased().contains("renewable")
23
22
  || transaction.expirationDate != nil
24
23
 
25
- // Parse transaction reason from jsonRepresentation if available
26
24
  var transactionReasonIos: String? = nil
27
25
  var webOrderLineItemId: Int? = nil
28
26
  var jsonData: [String: Any]? = nil
27
+ var jwsReceipt: String = ""
28
+
29
+ let jsonRep = transaction.jsonRepresentation
30
+ jwsReceipt = String(data: jsonRep, encoding: .utf8) ?? ""
29
31
 
30
- // JSON representation handling (JWS 데이터)
31
- var jwsReceipt: String
32
32
  do {
33
- let jsonRep = transaction.jsonRepresentation
34
- jwsReceipt = String(data: jsonRep, encoding: .utf8) ?? "" // JWS 형식으로 변환
35
- let jsonObj = try JSONSerialization.jsonObject(with: jsonRep) as! [String: Any]
36
- jsonData = jsonObj
37
- if let reason = jsonObj["transactionReason"] as? String {
38
- transactionReasonIos = reason
39
- }
40
- if let webOrderId = jsonObj["webOrderLineItemID"] as? NSNumber {
41
- webOrderLineItemId = webOrderId.intValue
33
+ if let jsonObj = try JSONSerialization.jsonObject(with: jsonRep) as? [String: Any] {
34
+ jsonData = jsonObj
35
+ transactionReasonIos = jsonObj["transactionReason"] as? String
36
+ if let webOrderId = jsonObj["webOrderLineItemID"] as? NSNumber {
37
+ webOrderLineItemId = webOrderId.intValue
38
+ }
42
39
  }
43
40
  } catch {
44
41
  print("Error parsing JSON representation: \(error)")
45
- jwsReceipt = ""
46
42
  }
47
43
 
48
- // Create base purchase object that matches Purchase type in TypeScript
49
44
  var purchaseMap: [String: Any?] = [
50
- // Core purchase fields
51
45
  "id": transaction.productID,
52
46
  "ids": [transaction.productID],
53
47
  "transactionId": String(transaction.id),
54
48
  "transactionDate": transaction.purchaseDate.timeIntervalSince1970 * 1000,
55
- "transactionReceipt": jwsReceipt, // JWS 데이터를 transactionReceipt에 담음
49
+ "transactionReceipt": jwsReceipt,
56
50
 
57
- // iOS specific fields - basic info
58
51
  "quantityIos": transaction.purchasedQuantity,
59
52
  "originalTransactionDateIos": transaction.originalPurchaseDate.timeIntervalSince1970 * 1000,
60
53
  "originalTransactionIdentifierIos": transaction.originalID,
61
54
  "appAccountToken": transaction.appAccountToken?.uuidString,
62
55
 
63
- // App and Product Identifiers
64
56
  "appBundleIdIos": transaction.appBundleID,
65
57
  "productTypeIos": transaction.productType.rawValue,
66
58
  "subscriptionGroupIdIos": transaction.subscriptionGroupID,
67
59
 
68
- // Transaction Identifiers
69
60
  "webOrderLineItemIdIos": webOrderLineItemId,
70
61
 
71
- // Purchase and Expiration Dates
72
62
  "expirationDateIos": transaction.expirationDate.map { $0.timeIntervalSince1970 * 1000 },
73
63
 
74
- // Purchase Details
75
64
  "isUpgradedIos": transaction.isUpgraded,
76
65
  "ownershipTypeIos": transaction.ownershipType.rawValue,
77
66
 
78
- // Revocation Status
79
67
  "revocationDateIos": transaction.revocationDate.map { $0.timeIntervalSince1970 * 1000 },
80
68
  "revocationReasonIos": transaction.revocationReason?.rawValue,
69
+ "transactionReasonIos": transactionReasonIos,
81
70
  ]
82
71
 
83
- // Environment (iOS 16.0+)
84
72
  if #available(iOS 16.0, *) {
85
73
  purchaseMap["environmentIos"] = transaction.environment.rawValue
86
74
  }
87
75
 
88
- // Storefront (iOS 17.0+)
89
76
  if #available(iOS 17.0, *) {
90
77
  purchaseMap["storefrontCountryCodeIos"] = transaction.storefront.countryCode
91
- }
92
-
93
- // Transaction Reason (iOS 17.0+)
94
- if #available(iOS 17.0, *) {
95
78
  purchaseMap["reasonIos"] = transaction.reason.rawValue
96
79
  }
97
80
 
98
- // reasonStringRepresentation은 Transaction에 없으므로 제거
99
- purchaseMap["transactionReasonIos"] = transactionReasonIos
100
-
101
- // Add offer information if available with proper availability check
102
81
  if #available(iOS 17.2, *) {
103
82
  if let offer = transaction.offer {
104
83
  purchaseMap["offerIos"] = [
105
- "id": offer.id as Any,
84
+ "id": offer.id,
106
85
  "type": offer.type.rawValue,
107
86
  "paymentMode": offer.paymentMode?.rawValue ?? "",
108
87
  ]
109
88
  }
110
89
  }
111
90
 
112
- // Add additional pricing info if available
113
- if #available(iOS 15.4, *), let priceInfo = jsonData?["price"] as? NSNumber {
114
- purchaseMap["priceIos"] = priceInfo.doubleValue
115
- }
116
-
117
- if #available(iOS 15.4, *), let currencyInfo = jsonData?["currency"] as? String {
118
- purchaseMap["currencyIos"] = currencyInfo
91
+ if #available(iOS 15.4, *), let jsonData = jsonData {
92
+ if let price = jsonData["price"] as? NSNumber {
93
+ purchaseMap["priceIos"] = price.doubleValue
94
+ }
95
+ if let currency = jsonData["currency"] as? String {
96
+ purchaseMap["currencyIos"] = currency
97
+ }
119
98
  }
120
99
 
121
100
  return purchaseMap
package/ios/Types.swift CHANGED
@@ -14,7 +14,6 @@ public enum StoreError: Error {
14
14
 
15
15
  enum IapErrors: String, CaseIterable {
16
16
  case E_UNKNOWN = "E_UNKNOWN"
17
- case E_NOT_INITIALIZED = "E_NOT_INITIALIZED"
18
17
  case E_SERVICE_ERROR = "E_SERVICE_ERROR"
19
18
  case E_USER_CANCELLED = "E_USER_CANCELLED"
20
19
  case E_USER_ERROR = "E_USER_ERROR"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "2.2.4-rc.2",
3
+ "version": "2.2.5-rc.1",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
package/src/useIap.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  getProducts,
8
8
  getAvailablePurchases,
9
9
  getPurchaseHistory,
10
+ finishTransaction as finishTransactionInternal,
10
11
  getSubscriptions,
11
12
  } from './';
12
13
  import {useCallback, useEffect, useState, useRef} from 'react';
@@ -94,17 +95,14 @@ export function useIAP(): IAP_STATUS {
94
95
  async ({
95
96
  purchase,
96
97
  isConsumable,
97
- developerPayloadAndroid,
98
98
  }: {
99
99
  purchase: ProductPurchase;
100
100
  isConsumable?: boolean;
101
- developerPayloadAndroid?: string;
102
101
  }): Promise<string | boolean | PurchaseResult | void> => {
103
102
  try {
104
- return await finishTransaction({
103
+ return await finishTransactionInternal({
105
104
  purchase,
106
105
  isConsumable,
107
- developerPayloadAndroid,
108
106
  });
109
107
  } catch (err) {
110
108
  throw err;