【发布时间】:2017-02-04 07:11:33
【问题描述】:
我正在使用 Swift 3 开发一个 iOS 应用程序,并尝试按照本教程实现收据验证:http://savvyapps.com/blog/how-setup-test-auto-renewable-subscription-ios-app。但是,该教程似乎是使用早期版本的 Swift 编写的,所以我不得不进行一些更改。这是我的receiptValidation() 函数:
func receiptValidation() {
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
let postString = "receipt-data=" + receiptString! + "&password=" + SUBSCRIPTION_SECRET
let storeURL = NSURL(string:"https://sandbox.itunes.apple.com/verifyReceipt")!
let storeRequest = NSMutableURLRequest(url: storeURL as URL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = postString.data(using: .utf8)
let session = URLSession(configuration:URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest as URLRequest) { data, response, error in
do{
let jsonResponse:NSDictionary = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let expirationDate:NSDate = self.expirationDateFromResponse(jsonResponse: jsonResponse)!
self.updateIAPExpirationDate(date: expirationDate)
}
catch{
print("ERROR: " + error.localizedDescription)
}
}
task.resume()
}
}
当我尝试调用 expireDateFromResponse() 方法时,问题就出现了。事实证明,传递给此方法的 jsonResponse 仅包含:status = 21002;。我查了一下,它的意思是“receipt-data 属性中的数据格式错误或丢失。”但是,我正在测试的设备具有该产品的有效沙盒订阅,并且订阅似乎除了这个问题之外可以正常工作。我还需要做些什么来确保正确读取和编码receiptData 值,或者其他可能导致此问题的问题吗?
编辑:
我尝试了另一种设置 storeRequest.httpBody 的方法:
func receiptValidation() {
let receiptPath = Bundle.main.appStoreReceiptURL?.path
if FileManager.default.fileExists(atPath: receiptPath!){
var receiptData:NSData?
do{
receiptData = try NSData(contentsOf: Bundle.main.appStoreReceiptURL!, options: NSData.ReadingOptions.alwaysMapped)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let receiptString = receiptData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)) //.URLEncoded
let dict = ["receipt-data":receiptString, "password":SUBSCRIPTION_SECRET] as [String : Any]
var jsonData:Data?
do{
jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
}
catch{
print("ERROR: " + error.localizedDescription)
}
let storeURL = NSURL(string:"https://sandbox.itunes.apple.com/verifyReceipt")!
let storeRequest = NSMutableURLRequest(url: storeURL as URL)
storeRequest.httpMethod = "POST"
storeRequest.httpBody = jsonData!
let session = URLSession(configuration:URLSessionConfiguration.default)
let task = session.dataTask(with: storeRequest as URLRequest) { data, response, error in
do{
let jsonResponse:NSDictionary = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let expirationDate:NSDate = self.expirationDateFromResponse(jsonResponse: jsonResponse)!
self.updateIAPExpirationDate(date: expirationDate)
}
catch{
print("ERROR: " + error.localizedDescription)
}
}
task.resume()
}
}
但是,当我使用此代码运行应用程序时,它会在到达jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) 行时挂起。它甚至没有到达 catch 块,它只是停止做任何事情。从我在网上看到的情况来看,其他人似乎无法在 Swift 3 中使用 JSONSerialization.data 设置请求 httpBody。
【问题讨论】:
-
确保您在 base64 编码收据中对任何 + 字符进行了 % 编码。即用 %2b 替换 + 的实例
-
我在声明后立即更改了代码以对receiptString 进行此更改,但我仍然看到相同的错误。另外,当我打印出receiptString 时,我注意到它包含很多“/”字符来分隔长的base 64 字符串。这是正确编码后的样子吗?
-
我还应该提到我尝试删除“/”字符,但我仍然看到 21002 状态。
-
我更新了我的 Gist 以显示我用来检索收据和编码 base64 数据的代码。在我的情况下,这将发送到我的 php 代码,然后将其发送到 Apple 的服务器 gist.github.com/paulw11/fa76e10f785e055338ce06673787c6d2
-
查看您的代码,您发送的数据不正确。您将收据和密码作为 POST 数据发送,但您需要发送一个包含收据和密码的 JSON 对象。如果你这样做了,那么你不需要担心 % 编码,这是我需要它来使用我的 PHP 的东西。
标签: ios swift in-app-purchase swift3 receipt-validation