Integrate PayUMoney Payment Gateway In Node.js and Meteor.js

In Skyward we keep exploring new things and keep sharing things with developers. This post will help you to integrate payumoney payment gateway integration in Node.js and Meteor.js.

When we started integrating we have not found any node.js code from payumoney so we decided to study their PHP and ASP.net code and implemented the same in Meteor technology. Which you can easily use in node.js also.

Prerequisite

– Key and Salt (which you can get from your payumoney dashboard)

– Let’s prepare settings.json

{
"public": {
"payUMoney": {
"Key": "rjQUPktU"
}
},
"private": {
"Plans": [{
"id": "1day",
"name": "1 Day",
"amount": 1000,
"duration": 1
}, {
"id": "1week",
"name": "1 Week",
"amount": 2000,
"duration": 7
}, {
"id": "1month",
"name": "1 Month",
"amount": 3000,
"duration": 1
}],
"transactionLogFilePath": "C:\\Hitesh\\Projects\\payment_log.txt",
"payUMoney": {
"Key": "rjQUPktU",
"Salt": "e5iIg1jwi8"
}
}
}

It’s good idea to log response received from payment gateway on successful transaction in text file. transactionLogFilePath is used for that purpose only.

Introduction

Before you start please go though this page. This page will guide you through overall process for payment gateway integration.

As you may have seen in the page above that first thing you will require to do is to send parameter values to your server and server will generate SHA512 Hash and send it back to client. You will require to write code on server to generate hash, I will explain this going forward.

Client side code

In meteor in my case, I had template called subscription.html with route /subscription. This is the page from where payment process will initiate.

<template name="subscription">
<form id="payment_form" name="payment_form" action="https://test.payu.in/_payment" method="post">
<input type="hidden" name="key" value="">
<input type="hidden" name="hash" value="">
<input type="hidden" name="txnid" value="">
<input type="hidden" name="amount" value="">
<input type="hidden" name="productinfo" value="">
<input type="hidden" name="firstname" value="">
<input type="hidden" name="email" value="">
<input type="hidden" name="phone" value="">
<input type="hidden" name="surl" value="">
<input type="hidden" name="furl" value="">
<input type="hidden" name="service_provider" value="">
<input type="hidden" name="curl" value="">
<input type="hidden" name="lastname" value="">
<input type="hidden" name="address1" value="">
<input type="hidden" name="address2" value="">
<input type="hidden" name="city" value="">
<input type="hidden" name="state" value="">
<input type="hidden" name="country" value="">
<input type="hidden" name="zipcode" value="">
<input type="hidden" name="udf1" value="">
<input type="hidden" name="udf2" value="">
<input type="hidden" name="udf3" value="">
<input type="hidden" name="udf4" value="">
<input type="hidden" name="udf5" value="">
<input type="hidden" name="pg" value="">
<div class="main-section subscription inherit-props l-1-8">
<div class="main-card">
<div class="col-sm-6 col-md-6">
<br/>
<div class="subscription-card">
<div class="panel panel-default">
<div class="panel-heading">
<h1>Subscription</h1></div>
<div></div>
<div class="panel-body">
<div class="content">
You can subscribe to our services to have access to our unlimited trade advices.
<br/> Your account expires by the end of the duration of the subscription.
</div>
<br/>
<div class="btn-grp-create-btns">
<br/>
<table>
<tr>
<td>
<button class="create subscription-plan" name="b1" id="1day" value="1000">1 Day&nbsp;</button>
</td>
<td>
<label class="content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rs.1,000/-</label>
<br/>
<br/>
</td>
</tr>
<tr>
<td>
<button class="create subscription-plan" name="b2" id="1week" value="4000">1 Week&nbsp;</button>
</td>
<td>
<label class="content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rs.4,000/-</label>
<br/>
<br/>
</td>
</tr>
<tr>
<td>
<button class="create subscription-plan" name="b3" id="1month" value="20000">1 Month&nbsp;</button>
</td>
<td>
<label class="content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rs.20,000/-</label>
<br/>
<br/>
</td>
</tr>
<tr>
<td>
<button class="create subscription-plan" name="b4" id="3month" value="55000">3 Months</button>
</td>
<td>
<label class="content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rs.55,000/-</label>
<br/>
<br/>
</td>
</tr>
<tr>
<td>
<button class="create subscription-plan" name="b5" id="6month" value="100000">6 Months</button>
</td>
<td>
<label class="content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rs.1,00,000/-</label>
<br/>
<br/>
</td>
</tr>
<tr>
<td>
<button class="create subscription-plan" name="b6" id="12month" value="180000">&nbsp;&nbsp;1 Year&nbsp;&nbsp;&nbsp;</button>
</td>
<td>
<label class="content">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rs.1,80,000/-</label>
<br/>
</td>
</tr>
<tr></tr>
</table>
<br/>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-md-6">
<br/>
<div class="wallet-card">
<div class="panel panel-default">
<div class="panel-heading">
<center>
<h1>Wallet</h1></center>
</div>
<div class="panel-body">
<div class="content">
When you successfully create a portfolio, <i>Rs.1,500 plus Rs. 100 per every stock between 6-20 stocks is deducted from the wallet.</i>
<br/>
<br/>
<div class="Note">
For example, if you select<strong> the minimum 5 stock portfolio, Rs.1500 is deducted from the wallet.
If you successfully create a 20 stock portfolio, Rs.1,500 + Rs. 100*(20-5) = Rs.3,000 is deducted from the wallet.
For every row in the Daily Forecast table Rs.100 is deducted from the wallet.</strong>
<br/>
<center>
<table>
<tr>
<td><strong>Portfolio</strong></td>
<td><b><i> Rs.1,500/-(5 stocks)+ Rs.100/- per stock (6-20)</i></b></td>
</tr>
<tr>
<td><strong>Daily Forecast</strong> &nbsp;&nbsp;&nbsp;&nbsp;</td>
<td><b><i>Rs.100/- for every advice.</i></b></td>
</tr>
</table>
</center>
</div>
</div>
<br/>
<div class="trd-field">
<div class="trd-lbl">Enter the amount for your wallet</div>
<div class="trd-input">
<input type="text" placeholder="Enter at least Rs.1500 for portfolio creation" value="{{Amount}}" name="wallet" class="numberOnly wallet-amount">
</div>
</div>
<center>
<div class="create-btns">
<input type="submit" class="create wallet-button" value="Add Money">
</div>
</center>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</template>

As you can see in the code above that I have 3 subscription plans and hidden inputs for payumoney. Everything is wrapped inside form element and form element has action value https://test.payu.in/_payment.

Now let’s just understand what we need to do on click of each subscription plan button. I’d another file called subscription.js where all actions are written. Let’s just look at it.

Template.subscription.events({‘
click.subscription - plan’: function(e, t) {
e.preventDefault();
var plan = e.currentTarget.id;
var amount = +(e.currentTarget.value);
process_payment(‘subscriptionPlan’, plan, amount);
},
});

As you can see in the code above that on click of any subscription plan we will get plan and amount value and passing the same in process_payment function. Now let’s define process_payment function.

function process_payment(type, plan, amount) {
var txnid = 'HL' + Random.hexString(5) + Random.hexString(5);
var __user = Meteor.user();
var form_data = {
key: Meteor.settings.public.payUMoney.Key,
txnid: txnid,
amount: amount,
productinfo: plan,
firstname: __user.profile.name,
email: __user.emails[0].address,
phone: '9898989898',
surl: 'http://localhost:3000/payment_success',
furl: 'http://localhost:3000/payment_failed',
service_provider: 'payu_paisa',
udf1: JSON.stringify({ userId: __user._id, type: type })
}
Meteor.call('generateHash', type, form_data, (err, hash) => {
if (err) {
//handle error
} else {
form_data.hash = hash;

$('#payment_form [name=key]').val(form_data.key);
$('#payment_form [name=txnid]').val(form_data.txnid);
$('#payment_form [name=amount]').val(form_data.amount);
$('#payment_form [name=productinfo]').val(form_data.productinfo);
$('#payment_form [name=firstname]').val(form_data.firstname);
$('#payment_form [name=email]').val(form_data.email);
$('#payment_form [name=phone]').val(form_data.phone);
$('#payment_form [name=surl]').val(form_data.surl);
$('#payment_form [name=furl]').val(form_data.furl);
$('#payment_form [name=service_provider]').val(form_data.service_provider);
$('#payment_form [name=hash]').val(form_data.hash);
$('#payment_form [name=udf1]').val(form_data.udf1);

$('#payment_form').submit();
}
});
}

Code above will actually process payment and will redirect current page to payumoney payment gateway page. In the code above we have read key from settings.json, generated new transaction id, use meteor’s user method to get user details and set other required param values. Note that I have also passed userid and type of subscription in udf1 field. When I provided solution to my client I’d to handle 2 types of payment options 1. subscription 2. wallet. Please note that, wallet is just a term and payumoney does not provide any wallet option at all. You need to manage wallet by your own. If required, You can pass any custom values in udf1 to udf5. I have only used udf1 in my case. Once you will have form_data object ready you are able to generate SHA-512 hash.

So, as you can see in the code above I have made call to generateHash server method with form_data as parameter value and in return it will give me hash. Once I received hash, I have just set all the hidden input field’s value and just submitted form manually. Nothing fancy!!

Server side code

On server we need to do following tasks

1. Generate Hash

2. Handle Payment Success Callback

3. Handle Payment Failure Callback

Let’s start with Generate Hash

On server we first need to install few modules before we start. In my case I have used flow router meteor package so it’s good idea to use picker module for server side routing. Apart from that we will need to install other npm modules

npm install js-sha512 
npm install body-parser

Once everything is installed write server side method generateHash.

Meteor.methods({

generateHash: function(type, form_data) {
check(type, String);
check(form_data, Object);

if (type == "subscriptionPlan") { //get plan price from db/settings
//get amount based on plan
var planDetail = getPlanDetail(form_data.productinfo);
form_data.amount = planDetail.amount;
}

var hashSequence = "key|txnid|amount|productinfo|firstname|email|udf1|udf2|udf3|udf4|udf5|udf6|udf7|udf8|udf9|udf10";

arrHashKeys = hashSequence.split('|');
var hashString = '';
for (var i = 0; i & lt; arrHashKeys.length; i++) {
if (form_data[arrHashKeys[i]]) {
hashString += form_data[arrHashKeys[i]]
}
hashString += '|';
}

hashString += Meteor.settings.private.payUMoney.Salt;
// console.log(sha512(hashString).toLowerCase());

var hash = sha512(hashString).toLowerCase();

console.log(hashString);
console.log(hash);
return hash;
}

});

function getPlanDetail(planId) {
var planDetail = null;
for (var i = 0; i & lt; Meteor.settings.private.Plans.length; i++) {
if (Meteor.settings.private.Plans[i].id == planId) {
planDetail = Meteor.settings.private.Plans[i];
break;
}
}
return planDetail;
}

I have done little trick inside generateHash function, have you noticed that? I have overwrite amount field again inside generateHash function because we can not rely on form_data for sensitive field like amount. In case if anyone’d tampered with amount field, hash will mismatch and it will give an error at payment gateway page saying “Checksum failed”.

Now we need to write server side routes to handle payment success and failed callback. Remember the surl and furl key which have passed in form data.

var bodyParser = Npm.require('body-parser');
Picker.middleware(bodyParser.json());
Picker.middleware(bodyParser.urlencoded({ extended: false }));

var postRoutes = Picker.filter(function(req, res) {
// you can write any logic you want.
// but this callback does not run inside a fiber
// at the end, you must return either true or false
return req.method == "POST";
});

postRoutes.route('/payment_success', function(params, req, res, next) {
var self = res;
fs = Npm.require('fs')

console.log('Received payment..................');

var urlToRedirect = '';
if (req.body) {
fs.appendFile(Meteor.settings.private.transactionLogFilePath, "Success Start=======================================================================")
fs.appendFile(Meteor.settings.private.transactionLogFilePath, JSON.stringify(req.body))
fs.appendFile(Meteor.settings.private.transactionLogFilePath, "Success End*************************************************************************")

var udf1 = JSON.parse(req.body.udf1.replace(/&amp;quot;/g, '\"'));

req.body.userId = udf1.userId;

//store transaction in your collection if needed
//update user subscription in users collection

urlToRedirect = '/thank-you';
} else {
urlToRedirect = '/error';
}
self.writeHead(302, {
'Location': urlToRedirect
});
self.end();
});

Picker.route('/payment_failed', function(params, req, res, next) {
console.log('Payment failed....');

if (req.body) {
fs.appendFile(Meteor.settings.private.transactionLogFilePath, "Failed Start=======================================================================")
fs.appendFile(Meteor.settings.private.transactionLogFilePath, JSON.stringify(req.body))
fs.appendFile(Meteor.settings.private.transactionLogFilePath, "Failed Error*************************************************************************")
}

var urlToRedirect = '/error';
self.writeHead(302, {
'Location': urlToRedirect
});
self.end();
});

As you can see in the code above we have handle 2 roues /payment_success and /payment_failed which will get triggered depending upon payment gateway response. Also, if you have noticed I have used transactionLogFilePath here to log payment gateway response in text file.

In this post, I have tried to explain payumoney integration with Meteor.js and I hope it will help you during your actual development.

For any development opportunity contact us. Or call us. Or email us. We are also available on skype.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.