HMAC MD5 Validation with Node.js, Express and Trialpay

I’m trying to authenticate a message sent from TrialPay using Node.js and Express. TrialPay signs requests with an HMAC-MD5 hash, and provides these instructions on validating.

This is my code:

app.post('/trialpay', function(req, res) {

    var key = "[MY MERCHANT KEY]";
    var hash = req.header("TrialPay-HMAC-MD5");
    var data = req.body.toString();

    var crypted = require("crypto").createHmac("md5", key)
        .update(data)
        .digest("hex");

    if (hash == crypted) {
        res.writeHead(200, {"Content-Type": "plain/text"});
        res.end("Success!");
    } else {
        throw new Error("Invalid TrialPay Hash");
    }  
});

This is, obviously, not working (hash doesn’t match).

Disclaimer: I’m extremely new to Node.js, and have little Javascript experience, to begin with.

UPDATE

I did not realize that the link was protected.

TrialPay uses your Notification-Key (set in your account information)
as the secret key to sign the HMAC. For GET requests the query string
that follows the question mark (in the URL) is signed. For POST
requests the entire POST body is signed.

Here is an example of how TrialPay instructs you to validate in Google App Engine (Python):

class MyHandler(webapp.RequestHandler):
  def post(self):
  key = '[YOUR MERCHANT KEY]'
  tphash = self.request.headers['TrialPay-HMAC-MD5'] 
  if hmacmd5(key,self.request.body) != tphash:
    logging.info('invalid trialpay hash')
    return 

UPDATE 2

The req.body prints out as:

{ 
  oid: 'sample-order-id',
  sid: 'customer-sid',
  order_date: '04/24/2012',
  timestamp: '04/24/2012 16:28:46',
  first_name: 'customer-firstname',
  last_name: 'customer-lastname',
  email: '[email protected]',
  revenue: '10.00',
  zip_code: '94041',
  country: 'US' 
}

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

This should do the trick:

var crypto = require('crypto');

function calculateSignature(key) {
    return function(req, res, next) {
        var hash = req.header("TrialPay-HMAC-MD5"),
            hmac = crypto.createHmac("md5", key);

        req.on("data", function(data) {
            hmac.update(data);
        });

        req.on("end", function() {
            var crypted = hmac.digest("hex");

            if(crypted === hash) {
                // Valid request
                return res.send("Success!", { "Content-Type": "text/plain" });
            } else {
                // Invalid request
                return res.send("Invalid TrialPay hash", { "Content-Type": "text/plain" }, 403);
            }
        });

        req.on("error", function(err) {
            return next(err);
        });
    }
}

app.post("/trialpay", calculateSignature("[MY MERCHANT KEY]"));

Solution 2

For Parse Cloud Code:(I have tested)
The point is express.bodyParser will parse the url encoded string which is used to hash.

var parseExpressRawBody = require('parse-express-raw-body');
var queryString = require('querystring');
app.post('/trialpay',parseExpressRawBody(),function(req, res) {
var hmac, calculatedSignature,payloadStr=req.body.toString();
hmac = crypto.createHmac('md5', TrialPayMerchentKey);
hmac.update(payloadStr);
calculatedSignature = hmac.digest('hex');

if (req.headers['trialpay-hmac-md5'] === calculatedSignature) {
   ~~~~~~

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply