易耗品-已购买此应用内购买商品。将免费恢复

时间:2019-03-10 17:15:17

标签: ios swift in-app-purchase

在我的实时应用程序上,用户不断遇到消耗品错误。这是非常随机的错误,很少发生。

此应用内购买已被购买。它将免费恢复。

在我的应用中,除非应用购买过程完成,否则我无法阻止用户点击“立即购买”按钮。

我已经阅读了以下问题的解决方案

Sandbox trying to restore consumable IAP

My IAP isn't working. Bugs at func Paymentqueue

我在代码中的两个位置都有SKPaymentQueue.default()。add(),如下所示。我还为每个transactionState调用SKPaymentQueue.default()。finishTransaction(transaction)。

任何人都可以让我知道解决此问题还需要检查什么吗?

open class IAPHelper: NSObject  {

    // Callback
    var purchaseStatusBlock: ((IAPHandlerAlertType, String, NSData) -> Void)?
    var purchaseFailed: ((SKPaymentTransaction) -> Void)?

    private let productIdentifiers: Set<ProductIdentifier>

    private var productsRequest: SKProductsRequest?

    private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?

    public init(productIds: Set<ProductIdentifier>) {

        productIdentifiers = productIds

        super.init()

        SKPaymentQueue.default().add(self)  // #1
    }
}

第二个是

extension IAPHelper {

    public func requestProducts(_ completionHandler: @escaping ProductsRequestCompletionHandler) {
        productsRequest?.cancel()
        productsRequestCompletionHandler = completionHandler

        productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
        productsRequest!.delegate = self
        productsRequest!.start()
    }

    public func buyProduct(_ product: SKProduct, vc: UIViewController) {
        let viewController = vc as! PurchaseViewController

        let payment = SKPayment(product: product)

        SKPaymentQueue.default().add(payment) // #2
    }
}

交易

extension IAPHelper: 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) {
        deliverPurchaseNotificationFor(identifier: transaction.payment.productIdentifier)

        let receiptURL = Bundle.main.appStoreReceiptURL
        let receipt = NSData(contentsOf: receiptURL!)
        if (receipt == nil) {
            // No local receipt -- handle the error
            let alert = UIAlertController(title: "Purchase Error", message: "No local receipt", preferredStyle: UIAlertController.Style.alert)
            let okAction = UIAlertAction(title: "Ok", style: UIAlertAction.Style.default) { (action) in

            }
            alert.addAction(okAction)

            return
        }

        // Callback
        purchaseStatusBlock?(.purchased, transaction.payment.productIdentifier, receipt!)

        SKPaymentQueue.default().finishTransaction(transaction)
    }

    private func fail(transaction: SKPaymentTransaction) {
        if let transactionError = transaction.error as NSError?,
            let localizedDescription = transaction.error?.localizedDescription,
            transactionError.code != SKError.paymentCancelled.rawValue {
        }

        // Callback
        purchaseFailed?(transaction)

        SKPaymentQueue.default().finishTransaction(transaction)
    }

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

        deliverPurchaseNotificationFor(identifier: productIdentifier)
        SKPaymentQueue.default().finishTransaction(transaction)
    }

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

        //    purchasedProductIdentifiers.insert(identifier)
        //    UserDefaults.standard.set(true, forKey: identifier)
        NotificationCenter.default.post(name: .IAPHelperPurchaseNotification, object: identifier)
    }
}

1 个答案:

答案 0 :(得分:0)

我们有一个类似的问题困扰了我们很长时间...

当用户发起购买交易,然后在完全处理交易之前失去互联网连接或终止了该应用程序时,将向他们收费,但即使恢复后也不会收到IAP内容

解决方案

跟随Apple's best practices,并在应用启动时添加交易观察者

以您为例

您应该删除:

SKPaymentQueue.default().add(self)  // #1

通过您的IAPHelper.init方法。

并在AppDelegate中添加观察者:

class AppDelegate: UIResponder, UIApplicationDelegate {

    let iapHelper = IAPHelper()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions 
                launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        SKPaymentQueue.default().add(iapHelper)
    }

然后在需要的ViewController中,可以使用以下命令访问iapHelper

let iapHelper = (UIApplication.shared.delegate as! AppDelegate).iapHelper