Stuart Breckenridge

App Store Receipt Validation with Node and Express

I just released my first Node package: itunes-validation. The package provides functionality to validate app receipts with the App Store, in either sandbox or production scenarios. Here’s an example of how to use the package:

$ git clone https://github.com/stuartbreckenridge/itunes-validation.git
$ cd itunes-validation
$ npm install
$ npm start

Once the app is running, two endpoints are available with the following query parameters:

  • GET /0.1/sandbox (for sandbox receipt validation)
  • GET /0.1/production (for production receipt validation)
Parameter Required Description
receipt Yes Base 64 encoded receipt string.
secret No Only used for receipts that contain auto-renewable subscriptions. Your app’s shared secret (a hexadecimal string).
exclude No Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions. If value is true, response includes only the latest renewal transaction for any subscriptions.

If you are testing with the sandbox, then the below sample code should provide some pointers.1

// Struct which represents receipt data returned by Apple.
struct Receipt: Decodable {
    var receipt: [String:String]
    var status: Int
}

// Function to validate receipt
func obfuscatedValidationMethod() {
    let receiptData = NSData(contentsOf: Bundle.main.appStoreReceiptURL!) // get receipt data
    let base64Receipt = receiptData?.base64EncodedString(options: .endLineWithLineFeed) // Convert to base 64

    var valUrl = URLComponents(string: "http://localhost:8443/0.1/sandbox") // create URL
    let queryItems = [URLQueryItem(name: "receipt", value: base64Receipt)] // create query
    valUrl?.queryItems = queryItems // add query items to URL
    let request = URLRequest(url: valUrl!.url!) // create URL request with URL
    
    let session = URLSession.shared // create URLSession
    /* Create task */
    let task = session.dataTask(with: request) { (data, response, error) in
        guard let responseData = data else {
            return 
        }
        let decoder = JSONDecoder()
        do {
            let decodedReceipt = try decoder.decode(Receipt.self, from: responseData)
            if decodedReceipt.status == 1 {
                // Do something with invalid receipt.
            }
        } catch {
            // Handle error.
        }
    }

    task.resume()
}

Links:

  1. Swift 4 code. For brevity, I also assume there is receipt data(!). ↩︎


— Supported by —