Here’s a Node.js microservice that validates iTunes receipts

If you’re an app developer and you need to validate customers’ iTunes receipts, you’ll have to hit Apple’s API. This is especially important if your app contains subscriptions, which you’ll need to periodically check to see if they are still valid.

Unfortunately, the response JSON that Apple provides is kind of weird and bloated. For example, if a customer has a monthly renewable subscription, you’ll likely get back a bunch of objects, and you’ll need to find the latest one to properly validate their subscription.

I created a Node microservice, below, that will validate receipts and can be deployed to any cloud provider. I used Now, which is pretty awesome, by the way.

**Update: I switched to Google’s Firebase functions. New code below.

var functions = require('firebase-functions');
const request = require('request')

exports.validate = functions.https.onRequest((req, res) => {
	//console.log(req.headers['content-type']);

	const reqData = req.body;
	console.log("Validation request received.");
	console.log(reqData);
	var respObj = { status: 0, expired: true, expiration: 0 }
	var now = new Date();

    //hit Apple api for receipt data
    let promise = new Promise((resolve, reject) => {
        //get secret from Firebase function process/config
        var secret = functions.config().appsec;
        var postData = {
            'receipt-data':reqData.receipt,
            'password':secret
        };
        request({
            url: 'https://buy.itunes.apple.com/verifyReceipt',
            method: 'POST',
            json: true,
            body: postData
        }, function (error, response, body){
            if (response.statusCode == 200) {
                resolve(validateResp(body, respObj));
            } else {
                var err = 'Error: ' + response.statusCode;
                reject(err);
            }
        });
    });

    var validateResp = function(body, respObj) {
        if (body.status > 0) {
            respObj.status = body.status;
            return respObj;
        }
        var nowMs = new Date().getTime();
        var inapp = body.receipt.in_app;
        var latest = body.latest_receipt_info;
        var receipts;

        if(body.latest_receipt_info && body.latest_receipt_info.length > 0){
            receipts = inapp.concat(latest);
        } else {
            receipts = inapp;
        }
        //locate expiration dates (in milliseconds), and find greatest value
        var expirations = receipts.map(function(x) { return x.expires_date_ms });
        var expMs = expirations.reduce(function(x,y){ return (x>y) ? x:y });
        respObj.expiration = expMs;

        //check if expiration date is greater than current time
        if (expMs > nowMs) {
            respObj.expired = false;
        }

        return respObj;
    };

    promise.then(function(json){
        res.status(200).send(json);
    }).catch(function(err){
        res.status(500).send(err);
    });

});
Need an extra hand on a project? Hire me

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s