expo-iap 2.2.4 → 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.
- package/android/src/main/java/expo/modules/iap/ExpoIapModule.kt +17 -14
- package/android/src/main/java/expo/modules/iap/PlayUtils.kt +70 -0
- package/build/useIap.d.ts.map +1 -1
- package/build/useIap.js +3 -4
- package/build/useIap.js.map +1 -1
- package/iap.md +0 -3
- package/package.json +1 -1
- package/src/useIap.ts +2 -4
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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 {
|
package/build/useIap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIap.d.ts","sourceRoot":"","sources":["../src/useIap.ts"],"names":[],"mappings":"
|
|
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,
|
|
27
|
+
const finishTransaction = useCallback(async ({ purchase, isConsumable, }) => {
|
|
28
28
|
try {
|
|
29
|
-
return await
|
|
29
|
+
return await finishTransactionInternal({
|
|
30
30
|
purchase,
|
|
31
31
|
isConsumable,
|
|
32
|
-
developerPayloadAndroid,
|
|
33
32
|
});
|
|
34
33
|
}
|
|
35
34
|
catch (err) {
|
package/build/useIap.js.map
CHANGED
|
@@ -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,
|
|
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
|
@@ -147,7 +147,6 @@ This section describes purchase properties in `expo-iap`.
|
|
|
147
147
|
### Android-Only Purchase Types
|
|
148
148
|
|
|
149
149
|
- **`ProductPurchase`**:
|
|
150
|
-
|
|
151
150
|
- Adds the following properties specific to in-app product purchases:
|
|
152
151
|
- **`ids`**: `string[]` - A list of product IDs associated with the purchase (for multi-item purchases).
|
|
153
152
|
- **`dataAndroid`**: `string` - The raw purchase data from Google Play (e.g., JSON payload).
|
|
@@ -155,7 +154,6 @@ This section describes purchase properties in `expo-iap`.
|
|
|
155
154
|
- **`purchaseStateAndroid`**: `number` - The state of the purchase (e.g., 0 = purchased, 1 = canceled, 2 = pending).
|
|
156
155
|
|
|
157
156
|
- **`SubscriptionPurchase`**:
|
|
158
|
-
|
|
159
157
|
- Extends the base properties and includes:
|
|
160
158
|
- **`autoRenewingAndroid`**: `boolean` - Indicates whether the subscription automatically renews (true) or not (false).
|
|
161
159
|
|
|
@@ -165,7 +163,6 @@ This section describes purchase properties in `expo-iap`.
|
|
|
165
163
|
### iOS-Only Purchase Types
|
|
166
164
|
|
|
167
165
|
- **`ProductPurchase`**:
|
|
168
|
-
|
|
169
166
|
- Extends the base purchase properties with iOS-specific fields:
|
|
170
167
|
- **`quantityIos`**: `number` - The quantity of the product purchased (e.g., how many units of an item were bought).
|
|
171
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).
|
package/package.json
CHANGED
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
|
|
103
|
+
return await finishTransactionInternal({
|
|
105
104
|
purchase,
|
|
106
105
|
isConsumable,
|
|
107
|
-
developerPayloadAndroid,
|
|
108
106
|
});
|
|
109
107
|
} catch (err) {
|
|
110
108
|
throw err;
|