在实际设备上测试中断的购买,下面的第10项不会在同一会话中出现。只有当
TransactionObserver
又被调用了?)这也谈到了类似的事情:Apple In App Purchase, Interrupted purchase in sandbox
发布于 2021-12-26 06:38:28
多年来,我一直试图找到解决这个问题的办法。在苹果论坛上,很多人都是如此,也是如此。
参考线程:
根据Apple Docs,中断的事务应该发送失败,然后在用户同意T&C/继续中断购买后购买(但实际上似乎没有发生-参考上面的链接线程)
这就是我现在的解决方案。这并不完全理想,但鉴于当时的情况,这是最好的(我发现)。请参阅链接3,其中用户观察到调用restoreCompletedTransactions()
实际上能够让事务观察者处理(已完成)中断的事务。这样做,用户不需要关闭(背景),然后重新打开应用程序。(请注意,我尝试了各种方法再次调用事务观察者,但都没有帮助)
这是我的解决方案。
private func purchaseFailed(_ transaction: SKPaymentTransaction) {
var failureReason: String = ""
var message: String = ""
var code = Int()
print("\(FormatDisplay.datems(Date())) [IAP] Transcation FAILED/CANCELLED")
// https://stackoverflow.com/q/55641652/14414215
// https://adapty.io/blog/ios-in-app-purchases-part-5-list-of-skerror-codes-and-how-to-handle-them
if let skError = transaction.error as? SKError {
switch skError.code { // https://developer.apple.com/reference/storekit/skerror.code
case .unknown:
// https://developer.apple.com/forums/thread/674081
if let underlyingError = skError.userInfo["NSUnderlyingError"] as? NSError {
if underlyingError.code == 3038 {
print(">> General conditions have changed, don't display an error for the interrupted transaction")
failureReason = "ERROR: Unknown Error. Transaction Interrupted"
message = "Transaction Interrupted. Your Purchase is being processed. Please Check Back in 5mins."
code = underlyingError.code
} else {
failureReason = "Unknown or unexpected error occurred"
message = "Oops, something unknown occurred or the transaction was interrupted. If the interrupted purchase was successful, please check back in 5mins."
code = skError.code.rawValue
}
}
break
case .clientInvalid:
failureReason = "ERROR: Invalid Client"
message = "The purchase cannot be completed. Please, change your account or device."
code = skError.code.rawValue
break
case .paymentCancelled:
failureReason = "ERROR: User Cancelled Payment"
message = ""
code = skError.code.rawValue
break
case .paymentInvalid:
failureReason = "ERROR: Invalid Payment"
message = "Your purchase was declined. Please, check the payment details and make sure there are enough funds in your account."
code = skError.code.rawValue
break
case .paymentNotAllowed:
failureReason = "ERROR: Payment not allowed"
message = "The purchase is not available for the selected payment method. Please, make sure your payment method allows you to make online purchases."
code = skError.code.rawValue
break
case .storeProductNotAvailable:
failureReason = "ERROR: Store product not available"
message = "This product is not available in your region. Please, change the store and try again"
code = skError.code.rawValue
break
case .cloudServicePermissionDenied:
failureReason = "ERROR: Cloud service permission denied"
message = "Your purchase was declined"
code = skError.code.rawValue
break
case .cloudServiceNetworkConnectionFailed:
failureReason = "ERROR: Cloud service network connection failed"
message = "he purchase cannot be completed because your device is not connected to the Internet. Please, try again later with a stable internet connection"
code = skError.code.rawValue
break
case .cloudServiceRevoked:
failureReason = "ERROR: Cloud service revoked"
message = "Sorry, an error has occurred."
code = skError.code.rawValue
break
case .privacyAcknowledgementRequired:
failureReason = "ERROR: Privacy Acknowledgement Required"
message = "The purchase cannot be completed because you have not accepted the terms of use of the AppStore. Please, confirm your consent in the settings and then return to the purchase."
code = skError.code.rawValue
break
case .unauthorizedRequestData:
failureReason = "ERROR: Unauthorized Request Data"
message = "An error has occurred. Please, try again later."
code = skError.code.rawValue
break
case .invalidOfferIdentifier:
failureReason = "ERROR: Invalid offer identifier"
message = "The promotional offer is invalid or expired."
code = skError.code.rawValue
break
case .invalidSignature:
failureReason = "ERROR: Invalid Signature"
message = "Sorry, an error has occurred when applying the promo code. Please, try again later."
code = skError.code.rawValue
break
case .missingOfferParams:
failureReason = "ERROR: Missing offer params"
message = "Sorry, an error has occurred when applying the promo code. Please, try again later."
code = skError.code.rawValue
break
case .invalidOfferPrice:
failureReason = "ERROR: Invalid offer price"
message = "Sorry, your purchase cannot be completed. Please, try again later."
code = skError.code.rawValue
break
case .overlayCancelled:
failureReason = "ERROR: overlay Cancelled"
message = ""
code = skError.code.rawValue
break
case .overlayInvalidConfiguration:
failureReason = "ERROR: Overlay Invalid Configuration"
message = ""
code = skError.code.rawValue
break
case .overlayTimeout:
failureReason = "ERROR: Overlay Timeout"
message = ""
code = skError.code.rawValue
break
case .ineligibleForOffer:
failureReason = "ERROR: Ineligible Offer"
message = "Sorry, your purchase cannot be completed. Please, try again later."
code = skError.code.rawValue
break
case .unsupportedPlatform:
failureReason = "ERROR: Unsupported Platform"
message = "Sorry, unsupported Platform"
code = skError.code.rawValue
break
case .overlayPresentedInBackgroundScene:
failureReason = "ERROR: Overlay Presented In Background Scene"
message = ""
code = skError.code.rawValue
break
@unknown default:
failureReason = "ERROR: Unknown Default"
message = "Oops. Something Has Happened. Please try again later."
code = skError.code.rawValue
break
}
let title = "Error"
let errorMsg = failureReason
if message != "" {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
DisplayAlert.presentIapFailed(title: title, message: message, errMsg: errorMsg)
}
}
failureReason += " ErrorCode: \(code)"
}
print("\(FormatDisplay.datems(Date())) [IAP] \(failureReason)")
print("\(FormatDisplay.datems(Date())) [IAP] -- \(transaction.error?.localizedDescription ?? "")")
print("\(FormatDisplay.datems(Date())) [IAP] -- \(transaction.error.debugDescription)")
print("\(FormatDisplay.datems(Date())) [IAP] -- Calling SKPaymentQ.FinishTransaction")
SKPaymentQueue.default().finishTransaction(transaction)
purchaseCompletionHandler?(true)
}
这是purchaseIapFailed()
的内容,一旦用户按下OK按钮,就调用restoreCompletedPurchases()
。
static func presentIapFailed(title: String, message: String, errMsg: String) {
let root = UIApplication.shared.keyWindow?.rootViewController
let alertController = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: {( action: UIAlertAction ) in
print(" displayAlert - presentIapFailed: \(errMsg) - OK Pressed")
// Call RestorePurchases to force replaying the transaction list.
Medals.store.restorePurchases()
}))
root?.present(alertController, animated: true, completion: nil)
}
一旦事务被“恢复”,它将继续触发后续代码来跟踪购买,发送一个通知(购买完成/成功),然后弹出一条警告消息,通知用户购买成功并已贷记消耗品。
https://stackoverflow.com/questions/70436152
复制相似问题