How to verify ios In-App Purchase on your server

Fetch the app's receipt (from the file at [[NSBundle mainBundle] appStoreReceiptURL]) and send it to your server. Then either…

  • Parse the receipt and check its signature against Apple's certificate as described here (on your server, not in the app). Or,

  • Send the receipt (again, from your server) to the app store for validation as described here.


I just released a simple Python/Django web app (In-App Purchase Receipt Verifier) that automatically handles the annoying part of this process (i.e., verifying the receipt on your server while maintaining cryptographic integrity). The code itself is relatively simple, but I figured others would appreciate not having to do the grunt-work here.

To use it, just make sure you have your app-specific shared secret ready to go, then go here and click "Deploy to Heroku". Your app will be up and running within a minute.

I've also included some sample code for how to use this web app to validate your receipts within your app. Using Swift 4:

guard let receiptURL = Bundle.main.appStoreReceiptURL,
    let data = try? Data(contentsOf: receiptURL) else {
      return
}

let encodedData = data.base64EncodedData(options: [])
let url = URL(string: "https://your-app.herokuapp.com/verify")!

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

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
    guard let data = data,
        let object = try? JSONSerialization.jsonObject(with: data, options: []),
        let json = object as? [String: Any] else {
            return
    }

    // Your application logic here.
}
task.resume()

Note: There are other solutions floating around on StackOverflow that suggest to do the validation completely within the app itself, but I'd highly recommend against that. From Apple's documentation in Receipt Validation Programming Guide:

Use a trusted server to communicate with the App Store. Using your own server lets you design your app to recognize and trust only your server, and lets you ensure that your server connects with the App Store server. It is not possible to build a trusted connection between a user’s device and the App Store directly because you don’t control either end of that connection.

Further improvements could probably be made to add some sort of cryptographic signature within the response header so that your app can check whether the response is safe, but I'll probably add that in a future release. Done :)