如果我在完成removeTransaction委托内的所有待处理交易后调用收据验证,Apple会拒绝应用程序吗?
我正在开发自动更新的应用内购买。现在,我正在 updatedTransactions
委托内部调用 Receipt Validation
函数,如下所示:
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach { (transaction) in
switch transaction.transactionState {
case .purchased:
self.IAPResponseCheck(iapReceiptValidationFrom: .purchaseButton)
KeychainWrapper.standard.set(false, forKey: receiptValidationAllow)
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
totalRestoredPurchases += 1
self.IAPResponseCheck(iapReceiptValidationFrom: .restoreButton)
KeychainWrapper.standard.set(false, forKey: receiptValidationAllow)
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
if let error = transaction.error as? SKError {
if error.code != .paymentCancelled {
onBuyProductHandler?(.failure(error))
} else {
onBuyProductHandler?(.failure(IAPManagerError.paymentWasCancelled))
}
PrintUtility.printLog(tag: String(describing: type(of: self)), text: "IAPError: \(error.localizedDescription)")
}
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred, .purchasing: break
@unknown default: break
}
}
}
首先,我调用 Receipt Validation
函数,我只是得到列出所有以前的交易,并计算到期日期和购买日期,以从最新信息收据
响应数组中解锁我的高级功能。在此函数中,我根据我的逻辑检查购买状态
并返回true
或false
。如果是真的,我会将用户带入我的应用程序内,如果是假的,我会将他带到购买屏幕。
然后我像这样立即完成交易:
SKPaymentQueue.default().finishTransaction(transaction)
但我注意到,如果用户有很长的交易列表(100+),则需要很长时间才能完成所有交易。我打印已完成的交易并将交易保留在 removedTransactions
委托中,如下所示:
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
PrintUtility.printLog(tag: String(describing: type(of: self)), text: "Removed transactions: \(transactions.count)")
PrintUtility.printLog(tag: String(describing: type(of: self)), text: "Unfinished transaction: \(queue.transactions.count)")
}
问题是,如果我在完成所有待处理交易之前尝试恢复或购买新产品,则会触发 updatedTransactions
奇怪的是。如果我等到它一一完成所有交易,它就可以正常工作。所以我的问题是,如果我在 removedTransactions
委托内部调用收据验证,并在 updateTransactions
委托内完成每笔交易,这是否会被视为 Apple 拒绝应用程序的可能原因?
最后,它看起来像这样:
updatedTransactions
delegate:
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach { (transaction) in
switch transaction.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
totalRestoredPurchases += 1
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
totalPurchaseOrRestoreFailed += 1
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred, .purchasing: break
@unknown default: break
}
}
}
removedTransactions
delegate:
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Removed transactions: \(transactions.count)")
print("Unfinished transaction: \(queue.transactions.count)")
//This will be called after finishing all transactions
if queue.transactions.count == 0 {
if totalPurchaseOrRestoreFailed != 0 {
transactions.forEach { (transaction) in
switch transaction.transactionState {
case .purchased:break
case .restored: break
case .failed:
if let error = transaction.error as? SKError {
if error.code != .paymentCancelled {
onBuyProductHandler?(.failure(error))
} else {
onBuyProductHandler?(.failure(IAPManagerError.paymentWasCancelled))
}
print("IAP Error:", error.localizedDescription)
totalPurchaseOrRestoreFailed = 0
}
case .deferred, .purchasing: break
@unknown default: break
}
}
} else {
self.IAPResponseCheck(iapReceiptValidationFrom: .purchaseAndRestoreButton)
UserDefaults.standard.set(false, forKey: "receiptValidationAllow")
}
}
}
I am developing Auto-Renewable In-App Purchase. Right now I am calling the Receipt Validation
function inside of updatedTransactions
delegate like this :
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach { (transaction) in
switch transaction.transactionState {
case .purchased:
self.IAPResponseCheck(iapReceiptValidationFrom: .purchaseButton)
KeychainWrapper.standard.set(false, forKey: receiptValidationAllow)
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
totalRestoredPurchases += 1
self.IAPResponseCheck(iapReceiptValidationFrom: .restoreButton)
KeychainWrapper.standard.set(false, forKey: receiptValidationAllow)
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
if let error = transaction.error as? SKError {
if error.code != .paymentCancelled {
onBuyProductHandler?(.failure(error))
} else {
onBuyProductHandler?(.failure(IAPManagerError.paymentWasCancelled))
}
PrintUtility.printLog(tag: String(describing: type(of: self)), text: "IAPError: \(error.localizedDescription)")
}
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred, .purchasing: break
@unknown default: break
}
}
}
First I am calling the Receipt Validation
function where I am simply getting all the previous transactions list and calculating expiration dates and purchase dates to unlock my premium features from the lastest Info Receipt
response array. In this function, I am checking the Purchase Status
according to my logic and returning true
or false
. If it's true I take the user inside of my app and if it's false I take him to the purchase screen.
Then I am finishing the transaction immediately like this:
SKPaymentQueue.default().finishTransaction(transaction)
But what I have noticed is that If the user has a long transaction list (100+), It takes a long time to finish all the transactions. I print the finished transactions and remain transactions in the removedTransactions
delegate like this:
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
PrintUtility.printLog(tag: String(describing: type(of: self)), text: "Removed transactions: \(transactions.count)")
PrintUtility.printLog(tag: String(describing: type(of: self)), text: "Unfinished transaction: \(queue.transactions.count)")
}
The problem is If I try to restore or purchase a new product before finishing all pending transactions it triggers updatedTransactions
weirdly. It works fine If I wait till it finishes all transactions one by one. So my question is, If I call receipt validation inside of removedTransactions
delegate, and finish each transaction inside updateTransactions
delegate will it be considered as a possible reason for app rejection on Apple?
Finally, It will look like this:
updatedTransactions
delegate:
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
transactions.forEach { (transaction) in
switch transaction.transactionState {
case .purchased:
SKPaymentQueue.default().finishTransaction(transaction)
case .restored:
totalRestoredPurchases += 1
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
totalPurchaseOrRestoreFailed += 1
SKPaymentQueue.default().finishTransaction(transaction)
case .deferred, .purchasing: break
@unknown default: break
}
}
}
removedTransactions
delegate:
func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
print("Removed transactions: \(transactions.count)")
print("Unfinished transaction: \(queue.transactions.count)")
//This will be called after finishing all transactions
if queue.transactions.count == 0 {
if totalPurchaseOrRestoreFailed != 0 {
transactions.forEach { (transaction) in
switch transaction.transactionState {
case .purchased:break
case .restored: break
case .failed:
if let error = transaction.error as? SKError {
if error.code != .paymentCancelled {
onBuyProductHandler?(.failure(error))
} else {
onBuyProductHandler?(.failure(IAPManagerError.paymentWasCancelled))
}
print("IAP Error:", error.localizedDescription)
totalPurchaseOrRestoreFailed = 0
}
case .deferred, .purchasing: break
@unknown default: break
}
}
} else {
self.IAPResponseCheck(iapReceiptValidationFrom: .purchaseAndRestoreButton)
UserDefaults.standard.set(false, forKey: "receiptValidationAllow")
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我相信这个问题与这个问题完全一样:
finishTransaction 后 SKPaymentTransaction 卡在队列中
在调用 问题不在于 Apple 拒绝您的应用因为您在其中完成了每笔交易updateTransactions,这是因为如果你这样做,苹果的框架会引入错误,并且支付根本不起作用。重要的是要认识到真正的用户永远不会遇到这个问题,因为他们不会有 100 多个订阅。因为只有在调试/模拟器中,一年的订阅才会被视为 1 小时的订阅。所以解决办法很简单,等队列为空后开始支付即可。这只是苹果框架中的一个错误,真正的用户不会有问题。这是我正在使用的代码,自从我使用此代码以来,Apple 没有拒绝我的应用程序:
I believe the issue is exactly like in this SO question:
SKPaymentTransaction's stuck in queue after finishTransaction called
The problem isn't that Apple rejects your App because you finish each transaction inside updateTransactions, it's because Apple's framework introduces bugs if you do, and the payment simply doesn't work. It's important to realize that real users will never have this issue, because they won't have 100+ subscriptions. Because only in debugging/simulators a year-long-subscription will be considered as a 1 hour-subscription. So the solution is easy, just start the payment after the queue is empty. It's just a bug in Apple's framework, and real users won't have an issue. This is the code that I'm using, and Apple has not rejected my App since I used this: