Неавлидный receipt in ap purchase

swift
ios

#1

Здравствуйте, получаю почему то невалидный чек, вот мой код

 if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
               FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
                do {
                    let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                    let receiptString = receiptData.base64EncodedString(options: .endLineWithCarriageReturn)
                    NetworkManager.shared().sendReceipt(receiptString) { [weak self] (result) in
                        guard let strongSelf = self else { return }
                        switch result {
                        case .success(let receipt):
                        break
                        case .failure(let statusMessage):
                            SKPaymentQueue.default().finishTransaction(transaction)
                            strongSelf.completionHandler?(.failure(statusMessage))
                        }
                    }
                }
                catch {
                    let message = "Couldn't read receipt data with error: " + error.localizedDescription
                    print(message)
                }
            }

приходит короткий чек типа MIAGCSqGSIb3…, а должен MIIT0wYJ… и намного длинее, используется firebase cloud functions, аккаунт sandbox, пробовал отправлять чек через постман напрямую https://sandbox.itunes.apple.com/verifyReceipt, получаю туже ошибку 21002 The data in the receipt-data property was malformed. Что с этим не так? Используется Configuration.storekit


#2

покажите этот метод

попробуйте поиграть с опциями в кодировке .base64EncodedString: например, убрать опции ()

ну а вообще у меня тоже почему-то были проблемы с чеком при использовании Configuration.storekit - не приходила дата подписки в чеке. При этом в тестах с получением чека от сервака эппл(а не от Configuration.storekit ) в тестовой среде всё ок.


#3

игрался уже как мог с опциями)

extension NetworkManager {
    func sendReceipt(_ receiptString: String, completionHandler: @escaping CompletionReceiptHandler) {
        let headers: HTTPHeaders = ["Content-Type": "Application/json"]
        let params = ["receipt" : receiptString]

        AF.request(kMethodSendReceipt, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
            switch response.result {
            case .success:
                do {
                    guard let data = response.data else { return }
                    let receipt = try JSONDecoder().decode(Receipt.self, from: data)
                    completionHandler(.success(receipt))
                } catch {
                    completionHandler(.failure(.unknown(message: error.localizedDescription)))
                }
            case .failure(let error):
                completionHandler(.failure(.unknown(message: error.localizedDescription)))
            }
        }
    }
}

#4

url для request правильно для песочницы настроен?

   #if DEBUG
   let url = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")!
   #else
   let url = URL(string: "https://buy.itunes.apple.com/verifyReceipt")!
   #endif

я так и не понял: а без сторконфигурации в песочнице то нормально всё?


#5

не уверен что на стороне firebase cloud functions настроено, но думаю настроено, так как в постмане, попытка отправить на url sandbox получаю туже ошибку


#6

Как я говорил, в сторконфигурации у меня тоже чек не давал. Вы пробовали тестить нормально без конфигурции? Я поэтому и спрашиваю про запрос и урл к нему. В вашем методе не видно какой урл используется. Должно быть примерно так:

    guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path) else {return}

    let receiptData = try! Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
    let receiptString = receiptData.base64EncodedString()
    let jsonObjectBody: [String : Any] = ["receipt-data" : receiptString,
                                          "password" : "ххххххххххххххх",
                                          "exclude-old-transactions" : true]

    #if DEBUG
    let url = URL(string: "https://sandbox.itunes.apple.com/verifyReceipt")!
    #else
    let url = URL(string: "https://buy.itunes.apple.com/verifyReceipt")!
    #endif

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = try! JSONSerialization.data(withJSONObject: jsonObjectBody, options: .prettyPrinted)
    let task = URLSession.shared.dataTask(with: request) {data, response, error in ...// обработка данных чека}
    task.resume()

те для запроса по чеку, чтобы его получить, вы должны использовать разный урл для песочницы и релиза.

И естественно чек появляется если вы СОВЕРШИЛИ покупку или её восстановили сначала.
Да ещё у вас не видно, где вы передаёте свой пасворд для доступа к чеку (берётся в коннекте)


#7

Спасибо за ответы, проблема была в том, что чек создается в формате asn1, а должен в pkcs7, как здесь пишут, такой создается в Xcode 12, iOS 14 - https://github.com/tikhop/TPInAppReceipt/issues/58, решением стало использовать библиотеку TPInAppReceipt, receipt.base64, который под капотом конвертит в формат pkcs7