6

After struggling a few days trying to get something to work and getting no where, I was wondering if someone has gotten iOS Receipt Validation working on Node.js. I have tried the node module iap_verifier found here but I could not get it to work properly for me. the only response I received back form Apples servers is 21002, data was malformed.

One thing that has worked for me was a client side validation request to apples servers that I got directly from the tutorials provided by Apple here, with the code shown below.

// The transaction looks ok, so start the verify process.

// Encode the receiptData for the itms receipt verification POST request.
NSString *jsonObjectString = [self encodeBase64:(uint8_t *)transaction.transactionReceipt.bytes
                                         length:transaction.transactionReceipt.length];

// Create the POST request payload.
NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\", \"password\" : \"%@\"}",
                     jsonObjectString, ITC_CONTENT_PROVIDER_SHARED_SECRET];

NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];


// Use ITMS_SANDBOX_VERIFY_RECEIPT_URL while testing against the sandbox.
NSString *serverURL = ITMS_SANDBOX_VERIFY_RECEIPT_URL;

// Create the POST request to the server.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverURL]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:payloadData];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn start];

I have a bunch of different code I have been using to send a wide array of things to my node server. and all of my different attempts have failed. I have even tried just funneling the "payloadData" I constructed in the client side validation example above to my server and sending that to Apples servers with the following code:

function verifyReceipt(receiptData, responder)
{

var options = {
    host: 'sandbox.itunes.apple.com',
    port: 443,
    path: '/verifyReceipt',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(receiptData)
    }
};

var req = https.request(options, function(res) {
    res.setEncoding('utf8');
    res.on('data', function (chunk) {
        console.log("body: " + chunk);
    });
});

req.write(receiptData);
req.end();
}

Where the function is passed the payloadData. The response received from Apple is always 21002. I'm still basically a node novice,so I can't figure out what exactly is going wrong. I think there might be some data corruption happening when I am sending the data from ObjC to my Node server, so perhaps I am not transmitting right.

If anyone can point me in the right direction, or provide some example of how they got receipt validation to work in node for them, it would be a great help. It would be great if anyone has had any experience with the iap_verifier module, and exactly what data it requires. I'll provide any code example I need to, as I have been fighting this process for a few days now.

Thanks!

user2908412
  • 87
  • 1
  • 8

3 Answers3

6

For anyone using the npm library "request", here's how to avoid that bothersome 21002 error.

formFields = {
  'receipt-data': receiptData_64
  'password': yourAppleSecret
}

verifyURL = 'https://buy.itunes.apple.com/verifyReceipt' // or 'https://sandbox.itunes.apple.com/verifyReceipt'

req = request.post({url: verifyURL, json: formFields}, function(err, res, body) {
    console.log('Response:', body);
})
5

This is my working solution for auto-renewable subscriptions, using the npm request-promise library. Without JSON stringify-ing the body form, I was receiving 21002 error (The data in the receipt-data property was malformed or missing)

const rp = require('request-promise');

var verifyURL = 'https://sandbox.itunes.apple.com/verifyReceipt';
// use 'https://buy.itunes.apple.com/verifyReceipt' for production

var options = {
    uri: verifyURL,
    method: 'POST',
    headers: {
        'User-Agent': 'Request-Promise',
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    json: true
};

options.form = JSON.stringify({
    'receipt-data': receiptData,
    'password': password
});

rp(options).then(function (resData) {
    devLog.log(resData); // 0
}).catch(function (err) {
    devLog.log(err);
});
4

Do you have composed correctly receiptData? Accordlying with Apple specification it should have the format

{"receipt-data": "your base64 receipt"}

Modifying your code wrapping the base64 receipt string with receipt-data object the validation should works

function (receiptData_base64, production, cb)
{
    var url = production ? 'buy.itunes.apple.com' : 'sandbox.itunes.apple.com'
    var receiptEnvelope = {
        "receipt-data": receiptData_base64
    };
    var receiptEnvelopeStr = JSON.stringify(receiptEnvelope);
    var options = {
        host: url,
        port: 443,
        path: '/verifyReceipt',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(receiptEnvelopeStr)
        }
    };

    var req = https.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            console.log("body: " + chunk);
            cb(true, chunk);
        });
        res.on('error', function (error) {
            console.log("error: " + error);
            cb(false, error);
        });
    });
    req.write(receiptEnvelopeStr);
    req.end();
}
Giulio Roggero
  • 1,692
  • 1
  • 16
  • 11