0

我在我的应用程序中使用以下帮助程序进行购买。当我调用时IAPHelper.default.initialize {_ in },就会调用 productsRequest 方法。

但是当我想进行购买并调用方法时

IAPHelper.default.purchaseProduct (productId: "month_subscription") {[weak self] result in
             print ("results =", result) }

SKPaymentQueue.default().add(self)进行了购买,但在方法 purchaseProduct() 中添加的 updatedTransactions 方法没有被调用(在 SKPaymentTransactionObserver 中)

我究竟做错了什么?

enum IAPHelperError: Error {
    case purchaseInProgress
    case productNotFound
    case unknown
}

typealias RequestProductsResult = Result<[SKProduct], Error>
typealias PurchaseProductResult = Result<Bool, Error>

typealias RequestProductsCompletion = (RequestProductsResult) -> Void
typealias PurchaseProductCompletion = (PurchaseProductResult) -> Void


class IAPHelperTest: NSObject {
    static let `default` = IAPHelperTest()

    //MARK: - product identificators
    private let productIdentifiers =  Set(["month_subscription"])

    var products: [String: SKProduct]?
    private var productRequest: SKProductsRequest?

    private var productPurchaseCallback: ((PurchaseProductResult) -> Void)?

    func initialize(completion: @escaping RequestProductsCompletion) {
        requestProducts(completion: completion)
    }

    private var productsRequestCallbacks = [RequestProductsCompletion]()

    private func requestProducts(completion: @escaping RequestProductsCompletion) {
        guard productsRequestCallbacks.isEmpty else {
            productsRequestCallbacks.append(completion)
            return
        }

        productsRequestCallbacks.append(completion)

        let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
        productRequest.delegate = self
        productRequest.start()

        self.productRequest = productRequest
    }

    //запрос продуктов по идентификаторам пришедшим с сервера
    private func requestProducts(productIdentifiers: Set<String>, completion: @escaping RequestProductsCompletion) {
        guard productsRequestCallbacks.isEmpty else {
            productsRequestCallbacks.append(completion)
            return
        }

        productsRequestCallbacks.append(completion)

        let productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
        productRequest.delegate = self
        productRequest.start()

        self.productRequest = productRequest
    }

    //Purchase Product
    public func purchaseProduct(productId: String, completion: @escaping (PurchaseProductResult) -> Void) {

        guard productPurchaseCallback == nil else {
            completion(.failure(IAPHelperError.purchaseInProgress))
            return
        }

        guard let product = products?[productId] else {
            completion(.failure(IAPHelperError.productNotFound))
            return
        }

        productPurchaseCallback = completion

        let payment = SKPayment(product: product)

        SKPaymentQueue.default().add(payment)
    }


    public func restorePurchases(completion: @escaping (PurchaseProductResult) -> Void) {
        guard productPurchaseCallback == nil else {
            completion(.failure(IAPHelperError.purchaseInProgress))
            return
        }

        productPurchaseCallback = completion

        SKPaymentQueue.default().restoreCompletedTransactions()
    }

}

//MARK: - SKProductsRequestDelegate
extension IAPHelperTest: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

        for invalidIdentifier in response.invalidProductIdentifiers {
            print("invalidIdentifier =", invalidIdentifier)
        }

        guard !response.products.isEmpty else {
            productsRequestCallbacks.forEach { $0(.success(response.products)) }
            productsRequestCallbacks.removeAll()
            return
        }

        var products = [String: SKProduct]()
        for skProduct in response.products {
            products[skProduct.productIdentifier] = skProduct
        }

        self.products = products

        productsRequestCallbacks.forEach { $0(.success(response.products)) }
        productsRequestCallbacks.removeAll()
    }

    func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Failed to load products with error:\n \(error)")

        productsRequestCallbacks.forEach { $0(.failure(error)) }
        productsRequestCallbacks.removeAll()
    }
}

//MARK: - SKPaymentTransactionObserver
extension IAPHelperTest: SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {

        for transaction in transactions {
            switch transaction.transactionState {

            case .purchased, .restored:
                if finishTransaction(transaction) {
                    SKPaymentQueue.default().finishTransaction(transaction)
                    productPurchaseCallback?(.success(true))
                } else {
                    productPurchaseCallback?(.failure(IAPHelperError.unknown))
                }
                break
            case .failed:
                productPurchaseCallback?(.failure(transaction.error ?? IAPHelperError.unknown))
                SKPaymentQueue.default().finishTransaction(transaction)
                break
            default:
                break
            }
        }

        productPurchaseCallback = nil
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload]) {

    }
}

extension IAPHelperTest {

    private func finishTransaction(_ transaction: SKPaymentTransaction) -> Bool {
        let productId = transaction.payment.productIdentifier
        print("Product \(productId) successfully purchased")

        
        if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
            FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {

            do {
                let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                print("receiptData =", receiptData)

                let receiptString = receiptData.base64EncodedString(options: [])
                print("receiptString = ",receiptString)

                // Read receiptData
            }
            catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
        }

        return true
    }
}
4

0 回答 0