无法还原活动的自动续订

时间:2019-04-25 13:53:38

标签: ios swift in-app-purchase auto-renewable

大多数时间恢复都可以,但是 有时latest_receipt_info 中没有关于当前有效的自动更新的记录(尝试购买时活动订阅,我会收到一条消息,说明我已经订阅了。有人遇到过吗?

其次,伙计们,您可以查看我的代码吗?当然,我不应该直接通过App Store服务器验证收据,但是我相信这不是问题。我怀疑代码是否有错误(例如我上面写的收据中没有最新记录),但我可能错了。也许我错过了一些重要的事情。

P.S。我首先怀疑的一件事是我可能无法正确比较日期(从收据中提取最新的到期日期后,我将其与Date()进行了这种比较:

if expirationDate > Date() {
    // There is an active subscription
}

我想也许我必须将Date()和expirationDate转换为GMT+0然后进行比较,但是两个日期似乎都是GMT + 0。

代码如下:

交易观察者

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    var purchased = false
    var restored = false

    for transaction in transactions {
        switch transaction.transactionState {
        case .purchased:
            purchased = true
            queue.finishTransaction(transaction)
        case .restored:
            restored = true
            queue.finishTransaction(transaction)
        case .deferred:
            queue.finishTransaction(transaction)
        case .failed:
            purchaseProductCompletionHandler?(IAPError.purchaseFailed)
            queue.finishTransaction(transaction)
        }
    }

    if purchased {
        processReceipt(completion: { error, subscriptionExpirationDate, appIsPurchased in
            if let error = error {
                self.purchaseProductCompletionHandler?(error)
            } else {
                self.purchaseProductCompletionHandler?(nil)
            }
        })
    } else if restored {
        processReceipt(completion: { error, subscriptionExpirationDate, appIsPurchased in
            self.restorePurchasesCompletionHandler?(error, subscriptionExpirationDate, appIsPurchased)
        })
    }
}

验证收据并对其进行解析的代码

private func processReceipt(completion: @escaping (_ error: Error?, _ subscriptionExpirationDate: Date?, _ appIsPurchased: Bool?) -> Void) {
    guard let receiptUrl = Bundle.main.appStoreReceiptURL, let receiptData = try? Data(contentsOf: receiptUrl) else {
        completion(IAPError.receiptNotFound, nil, nil)
        return
    }

    makeReceiptValidationRequest(receiptData: receiptData, url: receiptValidationProductionUrl, completion: { error, data in
        if let error = error {
            completion(error, nil, nil)
            return
        }

        guard let data = data else {
            completion(IAPError.receiptValidationRequestCompletedWithoutErrorAndData, nil, nil)
            return
        }

        guard let receiptStatus = try? self.extractReceiptStatus(data) else {
            completion(IAPError.jsonProcessingFailed, nil, nil)
            return
        }

        if receiptStatus == 21007 {
            self.makeReceiptValidationRequest(receiptData: receiptData, url: self.receiptValidationSandboxUrl, completion: { error, data in
                if let error = error {
                    completion(error, nil, nil)
                    return
                }

                guard let data = data else {
                    completion(IAPError.receiptValidationRequestCompletedWithoutErrorAndData, nil, nil)
                    return
                }

                let purchasesInfo = self.processReceiptValidationRequestResponse(data: data)
                completion(purchasesInfo.error, purchasesInfo.subscriptionExpirationDate, purchasesInfo.appIsPurchased)
            })
        } else {
            let purchasesInfo = self.processReceiptValidationRequestResponse(data: data)
            completion(purchasesInfo.error, purchasesInfo.subscriptionExpirationDate, purchasesInfo.appIsPurchased)
        }
    })
}

makeReceiptValidationRequest()

private func makeReceiptValidationRequest(receiptData: Data, url: URL, completion: @escaping (_ error: Error?, _ data: Data?) -> Void) {
    let payload = ["receipt-data": receiptData.base64EncodedString().toJSON(),
                   "password": secretKey.toJSON()]

    guard let serializedPayload = try? JSON.dictionary(payload).serialize() else {
        completion(IAPError.receiptValidationRequestCreationFailed, nil)
        return
    }

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = serializedPayload

    URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
        if let error = error {
            completion(error, nil)
        } else if let data = data {
            completion(nil, data)
        } else {
          completion(IAPError.receiptValidationRequestCompletedWithoutErrorAndData, nil)
        }
    }).resume()
}

processReceiptValidationRequestResponse()

private func processReceiptValidationRequestResponse(data: Data) -> (error: Error?, subscriptionExpirationDate: Date?, appIsPurchased: Bool?) {       
    guard let purchasesInfo = try? extractPurchasesInfo(data) else {
        return (IAPError.jsonProcessingFailed, nil, nil)
    }

    if purchasesInfo.receiptStatus != 0 {
        return (IAPError.invalidReceipt, nil, nil)
    }

    if let subscriptionExpirationDate = purchasesInfo.subscriptionExpirationDate, subscriptionExpirationDate > Date() {
        UserDefaults.standard.set(subscriptionExpirationDate, forKey: PurchasesInfoKey.subscriptionExpirationDate)
    }

    UserDefaults.standard.set(purchasesInfo.freeTrialWasUsedEarlier, forKey: PurchasesInfoKey.freeTrialWasUsedEarlier)
    UserDefaults.standard.set(purchasesInfo.appIsPurchased, forKey: PurchasesInfoKey.appIsPurchased)

    return (nil, purchasesInfo.subscriptionExpirationDate, purchasesInfo.appIsPurchased)
}

extractPurchasesInfo()

private func extractPurchasesInfo(_ data: Data) throws -> (receiptStatus: Int, subscriptionExpirationDate: Date?, freeTrialWasUsedEarlier: Bool?, appIsPurchased: Bool?) {
    let jsonData = try JSON(data: data)

    let receiptStatus = try jsonData.getInt(at: "status")

    if receiptStatus != 0 {
        return (receiptStatus, nil, nil, nil)
    }

    var subscriptionExpirationDate: Date?
    var freeTrialWasUsedEarlier = false
    var appIsPurchased = false

    if let receipt = jsonData["receipt"], let inApps = try? receipt.getArray(at: "in_app") {
        for inApp in inApps {
            let productId = try? inApp.getString(at: "product_id")

            if productId == ProductId.oneTimePurchase.rawValue {
                appIsPurchased = true
                break
            }
        }
    }

    if let latestReceiptInfo = try? jsonData.getArray(at: "latest_receipt_info") {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"

        for receipt in latestReceiptInfo {
            guard let productId = try? receipt.getString(at: "product_id") else {
                continue
            }

            if let isTrialPeriod = try? receipt.getString(at: "is_trial_period"), isTrialPeriod == "true", [, ProductId.oneMonthWithFreeTrial.rawValue, ProductId.oneYearWithFreeTrial.rawValue].contains(productId) {
                freeTrialWasUsedEarlier = true
            } else if productId == ProductId.oneTimePurchase.rawValue {
                appIsPurchased = true
            }

            guard let expiresDateString = try? receipt.getString(at: "expires_date"), let expiresDate = dateFormatter.date(from: expiresDateString) else {
                continue
            }

            if subscriptionExpirationDate == nil || expiresDate > subscriptionExpirationDate! {
                subscriptionExpirationDate = expiresDate
            }
        }
    }

    return (receiptStatus, subscriptionExpirationDate, freeTrialWasUsedEarlier, appIsPurchased)
}

0 个答案:

没有答案