IAPs实际验证收据(Swift)

IAPs实际验证收据(Swift),swift,validation,in-app-purchase,receipt,Swift,Validation,In App Purchase,Receipt,我一直在尝试在我的spritekit游戏中实现收据验证。我一直在遵循各种教程,基本上以这段代码结束 enum RequestURL: String { case production = "https://buy.itunes.apple.com/verifyReceipt" case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt" case myServer = "my server address" }

我一直在尝试在我的spritekit游戏中实现收据验证。我一直在遵循各种教程,基本上以这段代码结束

enum RequestURL: String {
   case production = "https://buy.itunes.apple.com/verifyReceipt"
   case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt"
   case myServer = "my server address"
}

enum ReceiptStatusCode: Int {

// Not decodable status
case unknown = -2

// No status returned
case none = -1

// valid status
case valid = 0

// The App Store could not read the JSON object you provided.
case JSONNotReadable = 21000

// The data in the receipt-data property was malformed or missing.
case malformedOrMissingData = 21002

// The receipt could not be authenticated.
case receiptCouldNotBeAuthenticated = 21003

// The shared secret you provided does not match the shared secret on file for your account.
// Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
case sharedSecretNotMatching = 21004

// The receipt server is currently not available.
case receiptServerUnavailable = 21005

// This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.
// Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
case subscriptionExpired = 21006

//  This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
case testReceipt = 21007

// This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.
case productionEnvironment = 21008
 }

  func validateReceipt(forTransaction transaction: SKPaymentTransaction) {

    guard let receiptURL = NSBundle.mainBundle().appStoreReceiptURL else { return }

    guard let receipt = NSData(contentsOfURL: receiptURL) else { return }

    let receiptData = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
    let payload = ["receipt-data": receiptData]

    var receiptPayloadData: NSData?

    do {
        receiptPayloadData = try NSJSONSerialization.dataWithJSONObject(payload, options: NSJSONWritingOptions(rawValue: 0))
    }
    catch let error as NSError {
        print(error.localizedDescription)
        return
    }

    guard let payloadData = receiptPayloadData else { return }
    guard let requestURL = NSURL(string: RequestURL.sandbox.rawValue) else { return }

    let request = NSMutableURLRequest(URL: requestURL)
    request.HTTPMethod = "POST"
    request.HTTPBody = payloadData

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
         if let error = error {
            print(error.localizedDescription)
            return
        }  
         guard let data = data else { return }          

         do {
            let jsonData = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary

            guard let json = jsonData else { return }

            // Correct ?
            guard let status = json["status"] as? Int where status == ReceiptStatusCode.valid.rawValue else { return }

            // Unlock product here?
            // Other checks needed?
        }

        catch let error as NSError {
            print(error.localizedDescription)
            return
        }
     }

    task.resume()
}
这是相当不错的锅炉板代码和预期的工作。我现在的问题是,我不知道如何在最后一步(标记行)实际验证收据。 我想我现在必须携带5张左右的支票来验证收据。我只是不知道在swift中如何完成大部分操作。大多数教程要么是旧的,不包括此步骤,要么不是用swift编写的

如果任何成功使用收据验证的人能够帮助我朝着正确的方向前进,我将不胜感激。多谢各位

更新:

在JSA986I和cbartel给出了很好的答案之后,我将其转化为github上的助手。非常感谢你的帮助


在这里,您可以将收据作为JSON返回并访问它。即

if parseJSON["status"] as? Int == 0 {
    println("Sucessfully returned purchased receipt data")
}
将告诉您是否已成功获得收据,因为
[“status”]0
表示其已返回ok

您可以进一步查询和使用收据数据来查找和使用JSON响应中的项目。您可以在这里打印最新的收据信息

if let receiptInfo: NSArray = parseJSON["latest_receipt_info"] as? NSArray {
    let lastReceipt = receiptInfo.lastObject as! NSDictionary
    // Get last receipt
    println("LAST RECEIPT INFORMATION \n",lastReceipt)
}
现在您可以使用json数据了

您现在可以查询该数据,在本例中,我们将从JSOn响应中找出自动续订订阅的订阅何时到期

// Format date
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")

// Get Expiry date as NSDate
let subscriptionExpirationDate: NSDate = formatter.dateFromString(lastReceipt["expires_date"] as! String) as NSDate!
println("\n   - DATE SUBSCRIPTION EXPIRES = \(subscriptionExpirationDate)")
把上面的代码贴在下面

if let parseJSON = json {
    println("Receipt \(parseJSON)")
}

下面是一个使用Swift 2.1版的解决方案

苹果还:

在验证服务器上的收据时,服务器需要能够处理从苹果测试环境获取收据的生产签名应用程序。推荐的方法是,生产服务器始终首先根据生产应用商店验证收据。如果验证失败,错误代码为“生产中使用的沙盒收据”,请改为针对测试环境进行验证


今天,我遇到了这个问题。 我引用了这个答案。 但我发现了一种检查订阅是否过期的新方法

这是我在Objective-C中的代码

NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:resData options:0 error:&error]; 

// this is response from AppStore
NSDictionary *dictLatestReceiptsInfo = jsonResponse[@"latest_receipt_info"];
long long int expirationDateMs = [[dictLatestReceiptsInfo valueForKeyPath:@"@max.expires_date_ms"] longLongValue];
long long requestDateMs = [jsonResponse[@"receipt"][@"request_date_ms"] longLongValue];
isValidReceipt = [[jsonResponse objectForKey:@"status"] integerValue] == 0 && (expirationDateMs > requestDateMs);

希望这能帮上忙。

Omg非常感谢您。它一直在做我的脑袋,关于它的信息对我来说不是很清楚。我知道“…int==0”位,但第二个代码“if let receive…”正是我要找的。因此,我假设如果我想检查bundle,我需要将“…parseJSON[“bundle_id”]写为?NSArray{…”或者我需要使用常量lastReceive来检查bundle id吗?奇怪的是,“int==0”的东西起作用,但是“if let receiptInfo”对我来说不起作用,它不会在之后调用println。很抱歉打扰你,但我感觉我离你太近了。我建议阅读本教程,第二个教程是关于recipt Validation的。谢谢,我会查看它。我最近没有时间查看你的建议。我会尽快将其标记为已回答。谢谢非常多没问题希望它有用你能让它工作吗?如果是的话,你介意分享你完成的代码吗。我现在正在解决同样的问题。非常感谢!嘿,不幸的是我现在已经放弃了收据验证。上面的代码是正确的代码减去我对swift 2所做的一些更改(使用守卫声明)。我只是不理解最后一点验证,关于这方面的教程很少。谢谢你的回答。我已经取得了一些进展,如果我能够取得更多进展,我将在这里共享代码。@Crashaverride777-感谢你创建了一个很棒的帮助文件。帮助程序工作得非常出色。但是它失败了,错误代码为21002沙盒测试。在调试和花费大量时间后。了解到由于“付费应用”协议状态不是“活动”而发生的情况。如果您可以在使用该实用程序之前记录协议的检查状态,肯定会有所帮助!嘿,非常感谢,我将再次尝试。
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:resData options:0 error:&error]; 

// this is response from AppStore
NSDictionary *dictLatestReceiptsInfo = jsonResponse[@"latest_receipt_info"];
long long int expirationDateMs = [[dictLatestReceiptsInfo valueForKeyPath:@"@max.expires_date_ms"] longLongValue];
long long requestDateMs = [jsonResponse[@"receipt"][@"request_date_ms"] longLongValue];
isValidReceipt = [[jsonResponse objectForKey:@"status"] integerValue] == 0 && (expirationDateMs > requestDateMs);