Расходные материалы - эта покупка в приложении уже куплена.Будет восстановлено бесплатно - PullRequest
1 голос
/ 10 марта 2019

В моем живом приложении пользователи продолжают получать эту ошибку для расходных материалов. Это очень случайная ошибка, которая случается редко.

Эта покупка в приложении уже куплена. Будет восстановлено бесплатно.

В моем приложении я запретил пользователям нажимать кнопку «Купить сейчас», если процесс покупки приложения не завершен.

Я уже прочитал решение по следующим вопросам

Песочница пытается восстановить расходный IAP

Мой IAP не работает. Ошибки в funcquequeue

У меня SKPaymentQueue.default (). Add () в двух местах в моем коде, как показано ниже. Я также вызываю SKPaymentQueue.default (). FinishTransaction (транзакция) для каждой транзакции.

Может кто-нибудь сообщить мне, что еще мне нужно проверить, чтобы решить эту проблему?

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 голосов
/ 02 мая 2019

У нас была похожая проблема, долгое время нас беспокоившая ...

Когда пользователи инициировали покупку, а затем потеряли подключение к Интернету или отключили приложение до того, как транзакция была полностью обработана, с них будет взиматься плата, но они никогда не получат содержимое IAP даже после восстановления

Решение

Следуйте рекомендациям Apple и добавьте наблюдателя транзакций при запуске приложения ?

В вашем случае

Вы должны удалить:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...