swift - 应用内购买 - 购买失败的比例很高 - SKError 代码 2 或 0

标签 swift in-app-purchase storekit

我们在我们的应用程序中面临应用程序内购买的严重问题。

我们提供 3 个 IAP:自动续订订阅 3M,自动续订订阅 1Y,非消耗性一次性购买(终身访问)

在我们的例子中,70-80% 的交易失败,我们大多得到 SKError code=0 或 code=2 - 无法连接到 iTunes Store。根据 SKError 文档,这是未知错误(代码 0)或取消的交易(错误 2)。

有时同一用户多次购买失败,因此很难相信用户连续 3 或 4 次故意取消同一产品的交易。

无论 iOS 版本、设备型号、我们的应用程序版本如何,它都会发生。
下面是我们用于获取产品和进行交易的代码。

我们已经检查了具有相同问题的多个线程,但找不到任何解决方案。

我们不提供任何促销事件,产品标识符是有效的...
一些用户可以毫无问题地进行购买。

有任何想法吗?

import Foundation
import SwiftyStoreKit
import StoreKit

final class IAPService: NSObject {

    static let shared = IAPService()
    public var isSubscriptionAvailable = false

    private var identifiers = ["product_x_id",
                               "product_y_id",
                               "product_z_id"]
    var products: [SKProduct] = []
    var purchaseProducts: [PurchaseProduct] = []

    private var successBlock: ((String?,String?)->())? //product Id, receipt
    private var errorBlock: ((String)->())?

    private var productsRequest: SKProductsRequest?
    private var productsRequestCompletionHandler: (()->())?

    override init() {
        super.init()
        SKPaymentQueue.default().add(self)
        self.loadProducts()
    }


    func loadProducts(completion: (()->())? = nil) {
        productsRequest?.cancel()
        productsRequestCompletionHandler = completion

        productsRequest = SKProductsRequest(productIdentifiers: Set(identifiers))
        productsRequest?.delegate = self
        productsRequest?.start()
    }

    func purchaseProduct(identifier: String, onSuccess: ((String?,String?) -> ())?, onError: ((String?)  -> ())?) {
        guard products.count > 0 else {
            loadProducts {
                self.purchaseProduct(identifier: identifier, onSuccess: onSuccess, onError: onError)
            }
            return
        }
        guard let product = self.products.first(where: { (skProduct) -> Bool in
            return skProduct.productIdentifier == identifier
        }) else {
            onError?("IAP error: cannot find product id")
            return
        }
        clearHandlers()
        self.successBlock = onSuccess
        self.errorBlock = onError

        print("Buying \(product.productIdentifier)...")
        let payment = SKPayment(product: product)
        SKPaymentQueue.default().add(payment)
    }

    public func restorePurchases(onSuccess: ((String?,String?) -> ())?, onError: ((String?)  -> ())?) {
        clearHandlers()
        self.successBlock = onSuccess
        self.errorBlock = onError
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    public func fetchReceipt(productId: String) {
        SwiftyStoreKit.fetchReceipt(forceRefresh: false) { (result) in
            switch result {
            case .success(let receiptData):
                self.successBlock?(productId, receiptData.base64EncodedString(options: []))
                self.clearHandlers()
                break
            case .error(let error):
                print("Receipt verification failed: \(error.localizedDescription)")

                self.errorBlock?(error.localizedDescription)
                self.clearHandlers()
                break
            }
        }
    }

    private func clearHandlers() {
        successBlock = nil
        errorBlock = nil
        productsRequestCompletionHandler = nil
        productsRequest = nil
    }
}

extension IAPService: SKProductsRequestDelegate {

    public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        print("Loaded list of products...")
        let skProducts = response.products

        skProducts.forEach { (skProduct) in
            products.append(skProduct)
        }
        productsRequestCompletionHandler?()
        clearHandlers()
    }

    public func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Failed to load list of products: \(error.localizedDescription)")
        productsRequestCompletionHandler?()
        clearHandlers()
    }
}


extension IAPService: SKPaymentTransactionObserver {

    public func paymentQueue(_ queue: SKPaymentQueue,
                             updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                complete(transaction: transaction)
                break
            case .failed:
                fail(transaction: transaction)
                break
            case .restored:
                restore(transaction: transaction)
                break
            case .deferred:
                break
            case .purchasing:
                break
            }
        }
    }

    private func complete(transaction: SKPaymentTransaction) {
        print("complete...")
        deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)
        SKPaymentQueue.default().finishTransaction(transaction)
    }

    private func restore(transaction: SKPaymentTransaction) {
        guard let productIdentifier = transaction.original?.payment.productIdentifier else { return }

        print("restore... \(productIdentifier)")
        deliverPurchaseNotificationFor(identifier: productIdentifier)
        SKPaymentQueue.default().finishTransaction(transaction)
    }

    private func fail(transaction: SKPaymentTransaction) {
        print("fail...")
        var failureReason: String = ""

        if let skError = transaction.error as? SKError {
            switch skError.code {  // https://developer.apple.com/reference/storekit/skerror.code
            case .unknown:
                failureReason = "Unknown or unexpected error occurred"
                break
            case .paymentCancelled:
                failureReason = "Payment cancelled by user"
                break
            case .clientInvalid:
                failureReason = "Invalid Client"
                break
            case .paymentInvalid:
                failureReason = "Invalid Payment"
                break
            case .paymentNotAllowed:
                failureReason = "Payment not allowed"
                break
            case .cloudServiceNetworkConnectionFailed:
                failureReason = "Cloud service network connection failed"
                break
            case .cloudServicePermissionDenied:
                failureReason = "Cloud service permission denied"
                break
            case .storeProductNotAvailable:
                failureReason = "Store product not available"
                break
            case .cloudServiceRevoked:
                failureReason = "Cloud service revoked"
                break
            case .privacyAcknowledgementRequired:
                failureReason = "Privacy Acknowledgement Required"
                break
            case .unauthorizedRequestData:
                failureReason = "Unauthorized Request Data"
                break
            case .invalidOfferIdentifier:
                failureReason = "Invalid offer identifier"
                break
            case .invalidSignature:
                failureReason = "Invalid signature"
                break
            case .missingOfferParams:
                failureReason = "Missing offer params"
                break
            case .invalidOfferPrice:
                failureReason = "Invalid offer price"
                break
            }
            failureReason += " code: \(skError.code.rawValue)"
        }
        else if let isCancelledError = transaction.error?.isCancelledError, isCancelledError == true {
            failureReason = "isCancelledError"
        }
        else {
            failureReason = "\(transaction.error.debugDescription)"
        }

        SKPaymentQueue.default().finishTransaction(transaction)

        errorBlock?(failureReason)
        self.clearHandlers()
    }

    private func deliverPurchaseNotificationFor(identifier: String?) {
        guard let identifier = identifier else { return }

        fetchReceipt(productId: identifier)
    }
}

最佳答案

我遇到了同样的问题,我收到错误代码 0 或错误代码 2(通常即使购买成功也会触发错误代码 2),并且在过去几个月里一直与 Apple 的开发人员技术支持来回发送电子邮件在这一点上。

这似乎是当前 Apple 的某些应用程序发生的已知问题,在您的情况下,最好的做法是直接从 Apple 提出技术支持事件 (TSI) 以获得代码级支持。

或者,您也可以使用反馈助手来提出错误报告。

不幸的是,这似乎是由 Store Kit/App Store 方面引起的,需要 Apple 逐案调查它确实发生的情况。

关于swift - 应用内购买 - 购买失败的比例很高 - SKError 代码 2 或 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55641652/

相关文章:

ios - 通过试用购买自动续订购买后应用程序崩溃

swift - 推送远程通知

javascript - 将变量从 Swift 传递到 javascript 函数

swift - 从本地通知将 iPhone 变成 iBeacon

ios - 已取消的 SKPaymentTransaction 中 transactionIdentifier 的唯一性

ios - 为非续订订阅恢复未处理的 SKPaymentTransaction

xcode - podspec swift - 左侧紫色图标

iphone - iOS 应用内购买收据未返回重新下载的 original_purchase_date

ios - 在 App Purchase ios 中 - 产品详细信息未显示

android - 通过应用内购买去除广告