IAPs actually validating the receipt (Swift)

你离开我真会死。 提交于 2019-12-03 03:59:42

Thats is where you return your receipt as JSON and can access it. ie

if parseJSON["status"] as? Int == 0 {
                       println("Sucessfully returned purchased receipt data")
                    }

Will tell you if you have successfully got the receipt because ["status"] 0 means its been returned ok

You can further query and use the receipt data to find and use items from the JSON response. Here you can print the latest receipt info

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)
}

Now you have your json data to use

You can now query that data, in this example we are finding out when the subscription expires of an auto renew subscription form the JSOn response

 // 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)")

Post the above code under

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

Here is a solution using Swift version 2.1 following the Apple guide.

Apple also recommends the following:

"When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code “Sandbox receipt used in production”, validate against the test environment instead."

validateReceipt(NSBundle.mainBundle().appStoreReceiptURL) { (success: Bool) -> Void in
            print(success)
        }

private func receiptData(appStoreReceiptURL : NSURL?) -> NSData? {

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

    do {
        let receiptData = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
        let requestContents = ["receipt-data" : receiptData]
        let requestData = try NSJSONSerialization.dataWithJSONObject(requestContents, options: [])
        return requestData
    }
    catch let error as NSError {
        print(error)
    }

    return nil
}

private func validateReceiptInternal(appStoreReceiptURL : NSURL?, isProd: Bool , onCompletion: (Int?) -> Void) {

    let serverURL = isProd ? "https://buy.itunes.apple.com/verifyReceipt" : "https://sandbox.itunes.apple.com/verifyReceipt"

    guard let receiptData = receiptData(appStoreReceiptURL),
        url = NSURL(string: serverURL)  else {
        onCompletion(nil)
        return
    }

    let request = NSMutableURLRequest(URL: url)
    request.HTTPMethod = "POST"
    request.HTTPBody = receiptData

    let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        guard let data = data where error == nil else {
            onCompletion(nil)
            return
        }

        do {
            let json = try NSJSONSerialization.JSONObjectWithData(data, options:[])
            print(json)
            guard let statusCode = json["status"] as? Int else {
                onCompletion(nil)
                return
            }
            onCompletion(statusCode)
        }
        catch let error as NSError {
            print(error)
            onCompletion(nil)
        }
    })
    task.resume()
}

public func validateReceipt(appStoreReceiptURL : NSURL?, onCompletion: (Bool) -> Void) {

    validateReceiptInternal(appStoreReceiptURL, isProd: true) { (statusCode: Int?) -> Void in
        guard let status = statusCode else {
            onCompletion(false)
            return
        }

        // This receipt is from the test environment, but it was sent to the production environment for verification.
        if status == 21007 {
            self.validateReceiptInternal(appStoreReceiptURL, isProd: false) { (statusCode: Int?) -> Void in
                guard let statusValue = statusCode else {
                    onCompletion(false)
                    return
                }

                // 0 if the receipt is valid
                if statusValue == 0 {
                    onCompletion(true)
                } else {
                    onCompletion(false)
                }

            }

        // 0 if the receipt is valid
        } else if status == 0 {
            onCompletion(true)
        } else {
            onCompletion(false)
        }
    }
}

Today, I have trouble with this problem. And I referenced on this answer. But I discover new way to check subscription is expired or not.

This is my code in 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);

Hope this help.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!