setPurchaseListener 方法中的异步方法在订阅时调用多次
Async methods inside setPurchaseListener method calls multiple time when do a subscription
我正在使用 expo-in-app-purchases 进行购买。
当我第一次进行测试订阅时 processNewPurchase(purchase)
只调用一次(正确行为)。但是 5 分钟后(因为它是一个测试订阅)订阅被取消并再次尝试进行订阅 processNewPurchase(purchase)
调用两次等等,(下次是三次)。
InAppPurchases.setPurchaseListener(
({ responseCode, results, errorCode }) => {
// Purchase was successful
if (responseCode === InAppPurchases.IAPResponseCode.OK) {
results.forEach(async (purchase) => {
if (!purchase.acknowledged) {
await processNewPurchase(purchase)
// finish the transaction on platform's end
InAppPurchases.finishTransactionAsync(purchase, true)
}
})
// handle particular error codes
} else if (
responseCode === InAppPurchases.IAPResponseCode.USER_CANCELED
) {
console.log('User canceled the transaction')
} else if (responseCode === InAppPurchases.IAPResponseCode.DEFERRED) {
console.log(
'User does not have permissions to buy but requested parental approval (iOS only)'
)
} else {
console.warn(
`Something went wrong with the purchase. Received errorCode ${errorCode}`
)
}
setProcessing(false)
}
)
因为 setPurchaseListener
应该处于全局状态,我在 App.js
中将其用作 <IAPManagerWrapped>
export default function App() {
return (
<ErrorBoundary onError={errorHandler}>
<Auth>
<IAPManagerWrapped>
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<StatusBar barStyle='dark-content' />
<Main navigationRef={navigationRef} />
</NavigationContainer>
</Provider>
</IAPManagerWrapped>
</Auth>
</ErrorBoundary>
)
}
在我的购买屏幕 (PurchaseScreen.js) 中,我正在使用 useIAP() 挂钩来获取产品详细信息。
const { getProducts } = useIap()
const [subscription, setSubscription] = useState([])
useEffect(() => {
getProducts().then((results) => {
console.log(results)
if (results && results.length > 0) {
const sub = results.find((o) => o.productId === subscribeId)
if (sub) {
setSubscription(sub)
}
}
})
return () => {}
}, [])
在setPurchaseListener
中多次调用processNewPurchase(purchase)
的原因是什么?
谢谢。
我在消耗品方面遇到了同样的问题。花了很多时间调试后,我注意到购买处理程序只被调用了一次;但是在 android 它包含超过 1 个产品,包括列表中以前购买的产品。
如官方文档中所述
On Android, it will return both finished and unfinished purchases,
hence the array return type. This is because the Google Play Billing
API detects purchase updates but doesn't differentiate which item was
just purchased, therefore there's no good way to tell but in general
it will be whichever purchase has acknowledged set to false, so those
are the ones that you have to handle in the response. Consumed items
will not be returned however, so if you consume an item that record
will be gone and no longer appear in the results array when a new
purchase is made.
尽管上面写着
Consumed items will not be returned however, so if you consume an item
that record will be gone and no longer appear in the results array
when a new purchase is made.
但是我仍然在列表中得到之前购买的项目,即使它在服务器端被确认并在客户端被消费。但是仍然在同一个会话中(应用程序没有重新启动并且进行了新的购买),它在数组列表中显示为 acknowledged: false
。因此,我们在代码 !purchase.acknowledged
中的检查并未过滤掉之前购买的商品。
results.forEach(async (purchase) => {
if (!purchase.acknowledged) {
await processNewPurchase(purchase)
// finish the transaction on platform's end
InAppPurchases.finishTransactionAsync(purchase, true)
}
})
正如我们从代码中看到的那样,我们的 processNewPurchase
函数在循环内,因此它会根据数组长度被调用多次。
问题
所以问题是图书馆没有正确标记同一会话中的 acknowledged/consumed 产品,并且 returns 它们在下次购买时返回为非 acknowledged/consumed 项目(不要'不知道它的错误是在 Google 端还是在库内),而我们的循环本应只 运行 一次, 运行 不止一次导致多次调用我们的成功代码.
解决方案
幸运的是,在数组中,当它 returns 以前的购买时,它 returns 与每次购买相关联的唯一 purchaseToken,我们可以使用它们来了解哪些购买已经被处理并且尚未处理。这是我解决这个问题的 sudo 代码。
1. Send all the purchases to server
2. Filter the unprocessed purchase from the incoming purchases. [recorded in step 3]
3. Process the filtered unprocessed purchase and keep the record of the processed purchase to help filtering the unprocessed purchase next time.
重要提示
Purchase Listener 每次调用都会被调用 getPurchaseHistoryAsync
所以它不会只在有新的购买时被调用。为确保我们授予正确的权利,需要验证从 Google 服务器购买。
我正在使用 expo-in-app-purchases 进行购买。
当我第一次进行测试订阅时 processNewPurchase(purchase)
只调用一次(正确行为)。但是 5 分钟后(因为它是一个测试订阅)订阅被取消并再次尝试进行订阅 processNewPurchase(purchase)
调用两次等等,(下次是三次)。
InAppPurchases.setPurchaseListener(
({ responseCode, results, errorCode }) => {
// Purchase was successful
if (responseCode === InAppPurchases.IAPResponseCode.OK) {
results.forEach(async (purchase) => {
if (!purchase.acknowledged) {
await processNewPurchase(purchase)
// finish the transaction on platform's end
InAppPurchases.finishTransactionAsync(purchase, true)
}
})
// handle particular error codes
} else if (
responseCode === InAppPurchases.IAPResponseCode.USER_CANCELED
) {
console.log('User canceled the transaction')
} else if (responseCode === InAppPurchases.IAPResponseCode.DEFERRED) {
console.log(
'User does not have permissions to buy but requested parental approval (iOS only)'
)
} else {
console.warn(
`Something went wrong with the purchase. Received errorCode ${errorCode}`
)
}
setProcessing(false)
}
)
因为 setPurchaseListener
应该处于全局状态,我在 App.js
中将其用作 <IAPManagerWrapped>
export default function App() {
return (
<ErrorBoundary onError={errorHandler}>
<Auth>
<IAPManagerWrapped>
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<StatusBar barStyle='dark-content' />
<Main navigationRef={navigationRef} />
</NavigationContainer>
</Provider>
</IAPManagerWrapped>
</Auth>
</ErrorBoundary>
)
}
在我的购买屏幕 (PurchaseScreen.js) 中,我正在使用 useIAP() 挂钩来获取产品详细信息。
const { getProducts } = useIap()
const [subscription, setSubscription] = useState([])
useEffect(() => {
getProducts().then((results) => {
console.log(results)
if (results && results.length > 0) {
const sub = results.find((o) => o.productId === subscribeId)
if (sub) {
setSubscription(sub)
}
}
})
return () => {}
}, [])
在setPurchaseListener
中多次调用processNewPurchase(purchase)
的原因是什么?
谢谢。
我在消耗品方面遇到了同样的问题。花了很多时间调试后,我注意到购买处理程序只被调用了一次;但是在 android 它包含超过 1 个产品,包括列表中以前购买的产品。
如官方文档中所述
On Android, it will return both finished and unfinished purchases, hence the array return type. This is because the Google Play Billing API detects purchase updates but doesn't differentiate which item was just purchased, therefore there's no good way to tell but in general it will be whichever purchase has acknowledged set to false, so those are the ones that you have to handle in the response. Consumed items will not be returned however, so if you consume an item that record will be gone and no longer appear in the results array when a new purchase is made.
尽管上面写着
Consumed items will not be returned however, so if you consume an item that record will be gone and no longer appear in the results array when a new purchase is made.
但是我仍然在列表中得到之前购买的项目,即使它在服务器端被确认并在客户端被消费。但是仍然在同一个会话中(应用程序没有重新启动并且进行了新的购买),它在数组列表中显示为 acknowledged: false
。因此,我们在代码 !purchase.acknowledged
中的检查并未过滤掉之前购买的商品。
results.forEach(async (purchase) => {
if (!purchase.acknowledged) {
await processNewPurchase(purchase)
// finish the transaction on platform's end
InAppPurchases.finishTransactionAsync(purchase, true)
}
})
正如我们从代码中看到的那样,我们的 processNewPurchase
函数在循环内,因此它会根据数组长度被调用多次。
问题
所以问题是图书馆没有正确标记同一会话中的 acknowledged/consumed 产品,并且 returns 它们在下次购买时返回为非 acknowledged/consumed 项目(不要'不知道它的错误是在 Google 端还是在库内),而我们的循环本应只 运行 一次, 运行 不止一次导致多次调用我们的成功代码.
解决方案
幸运的是,在数组中,当它 returns 以前的购买时,它 returns 与每次购买相关联的唯一 purchaseToken,我们可以使用它们来了解哪些购买已经被处理并且尚未处理。这是我解决这个问题的 sudo 代码。
1. Send all the purchases to server
2. Filter the unprocessed purchase from the incoming purchases. [recorded in step 3]
3. Process the filtered unprocessed purchase and keep the record of the processed purchase to help filtering the unprocessed purchase next time.
重要提示
Purchase Listener 每次调用都会被调用 getPurchaseHistoryAsync
所以它不会只在有新的购买时被调用。为确保我们授予正确的权利,需要验证从 Google 服务器购买。