node-paytmpg 5.3.2 → 5.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,251 @@
1
+ const crypto = require('crypto');
2
+ const axios = require('axios');
3
+
4
+ class PayU {
5
+ config
6
+ constructor(npconfig) {
7
+ const baseUrl = (npconfig.payu_url || '').replace(/\/$/, '');
8
+ const isSandbox = baseUrl.indexOf('test.payu.in') > -1;
9
+ const verifyUrl = npconfig.payu_verify_url || (isSandbox
10
+ ? 'https://test.payu.in/merchant/postservice.php?form=2'
11
+ : 'https://info.payu.in/merchant/postservice.php?form=2');
12
+ this.config = {
13
+ key: npconfig.KEY,
14
+ salt: npconfig.SECRET,
15
+ baseUrl: baseUrl,
16
+ paymentUrl: npconfig.payu_payment_url || (baseUrl ? baseUrl + '/_payment' : ''),
17
+ verifyUrl: verifyUrl
18
+ };
19
+ }
20
+
21
+ normalizeAmount(amount) {
22
+ const value = parseFloat(amount || 0);
23
+ return value.toFixed(2);
24
+ }
25
+
26
+ buildRequestHash(payload) {
27
+ const parts = [
28
+ payload.key,
29
+ payload.txnid,
30
+ this.normalizeAmount(payload.amount),
31
+ payload.productinfo,
32
+ payload.firstname,
33
+ payload.email,
34
+ payload.udf1 || '',
35
+ payload.udf2 || '',
36
+ payload.udf3 || '',
37
+ payload.udf4 || '',
38
+ payload.udf5 || '',
39
+ payload.udf6 || '',
40
+ payload.udf7 || '',
41
+ payload.udf8 || '',
42
+ payload.udf9 || '',
43
+ payload.udf10 || '',
44
+ this.config.salt
45
+ ];
46
+ return crypto.createHash('sha512').update(parts.join('|')).digest('hex');
47
+ }
48
+
49
+ buildResponseHash(data) {
50
+ const amount = this.normalizeAmount(data.amount);
51
+ const sequence = [
52
+ data.additionalCharges || null,
53
+ this.config.salt,
54
+ data.status || '',
55
+ '', '', '', '', '', '', '', '', '', '',
56
+ data.udf5 || '',
57
+ data.udf4 || '',
58
+ data.udf3 || '',
59
+ data.udf2 || '',
60
+ data.udf1 || '',
61
+ data.email || '',
62
+ data.firstname || '',
63
+ data.productinfo || '',
64
+ amount,
65
+ data.txnid || '',
66
+ data.key || ''
67
+ ];
68
+ const filtered = sequence.filter((v) => v !== null);
69
+ return crypto.createHash('sha512').update(filtered.join('|')).digest('hex');
70
+ }
71
+
72
+ generatePaymentRequest(params) {
73
+ const payload = {
74
+ key: this.config.key,
75
+ txnid: params['ORDER_ID'],
76
+ amount: this.normalizeAmount(params['TXN_AMOUNT']),
77
+ productinfo: params['PRODUCT_NAME'],
78
+ firstname: params['NAME'],
79
+ email: params['EMAIL'],
80
+ phone: params['MOBILE_NO'],
81
+ surl: params['CALLBACK_URL'],
82
+ furl: params['CALLBACK_URL'],
83
+ udf1: params['CUST_ID'] || '',
84
+ udf2: params['ORDER_ID'] || '',
85
+ service_provider: 'payu_paisa'
86
+ };
87
+
88
+ payload.hash = this.buildRequestHash(payload);
89
+
90
+ const formFields = Object.keys(payload).map((key) => {
91
+ return "<input type='hidden' name='" + key + "' value='" + payload[key] + "' />";
92
+ }).join('');
93
+ const html = `<form action='${this.config.paymentUrl}' method='post' id='payu_payment_form' style='display:none'>${formFields}</form><script>document.getElementById('payu_payment_form').submit();</script>`;
94
+
95
+ return { html: html, payload: payload };
96
+ }
97
+ decodeTransactionResponse(txnDataBase64FromPayu) {
98
+ const txnDataJson = Buffer.from(txnDataBase64FromPayu, 'base64').toString('utf-8');
99
+ return JSON.parse(txnDataJson);
100
+ }
101
+ async verifyResult(req) {
102
+ const originalBody = req.body || {};
103
+ const lookupId = originalBody.txnid || req.query.order_id;
104
+ const statusResp = await this.checkBqrTxnStatus(lookupId);
105
+
106
+ let resData = null;
107
+ if (!resData && statusResp && statusResp.transaction_details) {
108
+ const td = statusResp.transaction_details;
109
+ // try direct lookup by lookupId
110
+ if (td[lookupId]) {
111
+ resData = td[lookupId];
112
+ }
113
+ else {
114
+ // find entry where txnid matches lookupId or key ends with lookupId
115
+ for (const k of Object.keys(td)) {
116
+ const t = td[k];
117
+ if (!t) continue;
118
+ if ((t.txnid && t.txnid.toString() === lookupId) || k.toString().endsWith(lookupId)) {
119
+ resData = t;
120
+ break;
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ // Determine source for status and mapping (prefer decoded resData)
127
+ const source = resData || (statusResp || {}) || originalBody;
128
+
129
+ const msg = (statusResp?.msg || '').toString();
130
+ const statusText = (source.status || source.unmappedstatus || msg || '').toString().toLowerCase();
131
+ let status = 'TXN_FAILURE';
132
+ if (statusText.includes('success') || statusText.includes('completed') || statusText.includes('captured')) {
133
+ status = 'TXN_SUCCESS';
134
+ }
135
+ else if (statusText.includes('pending')) {
136
+ status = 'TXN_PENDING';
137
+ }
138
+
139
+ const orderId = (source.udf2 || source.order_id || source.txnid || lookupId).toString();
140
+ const txnId = source.mihpayid || source.txnid || null;
141
+
142
+ return {
143
+ STATUS: status,
144
+ ORDERID: orderId,
145
+ TXNID: txnId,
146
+ data: resData || statusResp || originalBody,
147
+ cancelled: source.unmappedstatus?.toLowerCase()?.includes('cancelled') || false
148
+ };
149
+ }
150
+
151
+ async getPaymentStatus(txnId) {
152
+ const verifyPayload = new URLSearchParams();
153
+ verifyPayload.append('key', this.config.key || '');
154
+ verifyPayload.append('command', 'verify_payment');
155
+ verifyPayload.append('var1', txnId || '');
156
+ const hashString = [this.config.key, 'verify_payment', txnId, this.config.salt].join('|');
157
+ verifyPayload.append('hash', crypto.createHash('sha512').update(hashString).digest('hex'));
158
+
159
+ try {
160
+ const response = await axios.post(this.config.verifyUrl, verifyPayload.toString(), {
161
+ headers: {
162
+ 'Content-Type': 'application/x-www-form-urlencoded'
163
+ }
164
+ });
165
+ return response.data;
166
+ }
167
+ catch (e) {
168
+ return { error: e.message };
169
+ }
170
+ }
171
+
172
+ async postCommand(command, transactionId) {
173
+ const payload = new URLSearchParams();
174
+ payload.append('key', this.config.key || '');
175
+ payload.append('command', command || '');
176
+ payload.append('var1', transactionId || '');
177
+
178
+ // build hash: key|command|var1...|salt
179
+ const hashParts = [this.config.key, command, transactionId, this.config.salt];
180
+ const hashString = hashParts.join('|');
181
+ payload.append('hash', crypto.createHash('sha512').update(hashString).digest('hex'));
182
+
183
+ try {
184
+ const response = await axios.post(this.config.verifyUrl, payload.toString(), {
185
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
186
+ });
187
+ return response.data;
188
+ }
189
+ catch (e) {
190
+ return { error: e.message };
191
+ }
192
+ }
193
+
194
+ /**
195
+ * https://docs.payu.in/reference/transaction-status-check-api-2#sample-request
196
+ * @param {*} transactionId mandatory
197
+ * @param {*} paymentmode optional
198
+ * @param {*} productype optional
199
+ * @returns
200
+ */
201
+ async checkBqrTxnStatus(transactionId) {
202
+ return this.postCommand('verify_payment', transactionId);
203
+ }
204
+
205
+ renderProcessingPage(params, paymentReq, res, loadingSVG) {
206
+ res.writeHead(200, { 'Content-Type': 'text/html' });
207
+ res.write(`<html><head><title>Merchant Checkout Page</title></head><body><center><h1>Processing ! Please do not refresh this page...</h1><br>${paymentReq.html}<br><br>${loadingSVG}</center></body></html>`);
208
+ res.end();
209
+ }
210
+
211
+ renderError(params, error, res) {
212
+ console.log('ERROR:::', error, '\n');
213
+ res.status(500);
214
+ let formFields = '';
215
+ const errorResp = {
216
+ TXNID: 'na',
217
+ STATUS: 'TXN_FAILURE',
218
+ CANCELLED: 'cancelled',
219
+ ORDERID: params['ORDER_ID']
220
+ };
221
+ Object.keys(errorResp).forEach((key) => {
222
+ formFields += "<input type='hidden' name='" + key + "' value='" + errorResp[key] + "' >";
223
+ });
224
+ formFields += "<input type='hidden' name='CHECKSUMHASH' value='" + (params['CHECKSUM'] || '') + "' >";
225
+
226
+ res.writeHead(200, { 'Content-Type': 'text/html' });
227
+ res.write(`<html>
228
+
229
+ <head>
230
+ <title>Merchant Checkout Error</title>
231
+ </head>
232
+
233
+ <body>
234
+ <center>
235
+ <h1>Something went wrong. Please wait you will be redirected automatically...</h1>
236
+ </center>
237
+ <form method="post" action="${params['CALLBACK_URL']}" name="f1">${formFields}</form>
238
+ <script type="text/javascript">document.f1.submit();</script>
239
+ </body>
240
+
241
+ </html>`);
242
+ res.end();
243
+ }
244
+
245
+ processWebhook(req, res) {
246
+ res.status(201);
247
+ res.send({ message: 'Webhook not implemented for PayU' });
248
+ }
249
+ }
250
+
251
+ module.exports = PayU;
@@ -1,80 +1,89 @@
1
- var User ;
2
- var Transaction = require('../models/np_transaction.model.js');
3
- var IDLEN = 10 ;
4
- function makeid(length) {
5
- var text = "";
6
- var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
7
-
8
- for (var i = 0; i < length; i++)
9
- text += possible.charAt(Math.floor(Math.random() * possible.length));
10
-
11
- return text;
12
- }
13
-
14
- module.exports = function (app, callbacks) {
15
- var module = {};
16
- var config = (app.get('np_config'))
17
-
18
- let usingMultiDbOrm = false;
19
- if (config.db_url) {
20
- User = require('../models/np_user.model.js');
21
- usingMultiDbOrm = false;
22
- } else if (app.multidborm) {
23
- User = require('../models/np_multidbplugin.js')('npusers',app.multidborm);
24
- User.db=app.multidborm;
25
- User.modelname='npusers'
26
- User.idFieldName='id'
27
- app.NPUser = User;
28
- usingMultiDbOrm = true;
29
- }
30
- module.create = (userData, cb) => {
31
-
32
- User.findOne({ email: userData.email }, function (err, user) {
33
- if (user) {
34
-
35
- // console.log("User Update : ",userData.name );
36
- var myquery = { email: userData.email };
37
-
38
- var objForUpdate = user;
39
-
40
- if (userData.email && userData.email.indexOf("@") !== -1) objForUpdate.email = userData.email;
41
- if (userData.phone && userData.phone.length > 2) objForUpdate.phone = userData.phone;
42
- if (userData.name && userData.name.length > 2) objForUpdate.name = userData.name;
43
- delete objForUpdate._id ;
44
- var newvalues = { $set: objForUpdate };
45
- //console.log("User Old : ",userData.name);
46
- User.updateOne(myquery, newvalues, function (err, saveRes) {
47
- if (err) cb({
48
- message: err.message || "Some error occurred while updating users."
49
- });
50
-
51
- // console.log("Sendiing callback")
52
- cb(user);
53
- // console.log("sent callback")
54
- });
55
-
56
-
57
- } else {
58
-
59
- // console.log("User New : ",userData.name);
60
-
61
- userData.id = "user_"+makeid(IDLEN);
62
- var userTask = new User(userData);
63
- userTask.save()
64
- .then(user => {
65
- // console.log("Sendiing callback")
66
- cb(user);
67
- // console.log("sent callback")
68
-
69
- }).catch(err => {
70
- return cb(err);
71
- });
72
-
73
- }
74
-
75
- },usingMultiDbOrm ? User : undefined);
76
-
77
- };
78
- return module;
79
-
1
+ var User;
2
+ var Transaction = require('../models/np_transaction.model.js');
3
+ var IDLEN = 10;
4
+ function makeid(length) {
5
+ var text = "";
6
+ var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
7
+
8
+ for (var i = 0; i < length; i++)
9
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
10
+
11
+ return text;
12
+ }
13
+
14
+ module.exports = function (app, callbacks) {
15
+ var module = {};
16
+ var config = (app.get('np_config'))
17
+
18
+ let usingMultiDbOrm = false;
19
+ if (config.db_url) {
20
+ User = require('../models/np_user.model.js');
21
+ usingMultiDbOrm = false;
22
+ } else if (app.multidborm) {
23
+ const sample = {
24
+ "id": "user_aB3dE9xY1Z",
25
+ "name": "tset",
26
+ "email": "testgmailcom",
27
+ "phone": "12345678",
28
+ "createdAt": "stringlarge",
29
+ "updatedAt": "stringlarge",
30
+ "returnUrl": "stringlarge"
31
+ }
32
+ User = require('../models/np_multidbplugin.js')('npusers', app.multidborm, sample);
33
+ User.db = app.multidborm;
34
+ User.modelname = 'npusers'
35
+ User.idFieldName = 'id'
36
+ app.NPUser = User;
37
+ usingMultiDbOrm = true;
38
+ }
39
+ module.create = (userData, cb) => {
40
+
41
+ User.findOne({ email: userData.email }, function (err, user) {
42
+ if (user) {
43
+
44
+ // console.log("User Update : ",userData.name );
45
+ var myquery = { email: userData.email };
46
+
47
+ var objForUpdate = user;
48
+
49
+ if (userData.email && userData.email.indexOf("@") !== -1) objForUpdate.email = userData.email;
50
+ if (userData.phone && userData.phone.length > 2) objForUpdate.phone = userData.phone;
51
+ if (userData.name && userData.name.length > 2) objForUpdate.name = userData.name;
52
+ delete objForUpdate._id;
53
+ var newvalues = { $set: objForUpdate };
54
+ //console.log("User Old : ",userData.name);
55
+ User.updateOne(myquery, newvalues, function (err, saveRes) {
56
+ if (err) cb({
57
+ message: err.message || "Some error occurred while updating users."
58
+ });
59
+
60
+ // console.log("Sendiing callback")
61
+ cb(user);
62
+ // console.log("sent callback")
63
+ });
64
+
65
+
66
+ } else {
67
+
68
+ // console.log("User New : ",userData.name);
69
+
70
+ userData.id = "user_" + makeid(IDLEN);
71
+ var userTask = new User(userData);
72
+ userTask.save()
73
+ .then(user => {
74
+ // console.log("Sendiing callback")
75
+ cb(user);
76
+ // console.log("sent callback")
77
+
78
+ }).catch(err => {
79
+ return cb(err);
80
+ });
81
+
82
+ }
83
+
84
+ }, usingMultiDbOrm ? User : undefined);
85
+
86
+ };
87
+ return module;
88
+
80
89
  }
@@ -6,6 +6,7 @@ var IDLEN = 10;
6
6
  var nodeBase64 = require('nodejs-base64-converter');
7
7
  var RazorPay = require('razorpay');
8
8
  var OpenMoney = require('./adapters/open_money')
9
+ var PayU = require('./adapters/payu')
9
10
  const PaytmChecksum = require('./checksum/PaytmChecksum.js');
10
11
  const { stat } = require('fs');
11
12
  const { config } = require('process');
@@ -76,12 +77,17 @@ module.exports = function (app, callbacks) {
76
77
 
77
78
  var razorPayInstance;
78
79
  var openMoneyInstance = new OpenMoney(config);
80
+ var payuInstance = new PayU(config)
79
81
 
80
- if (config.razor_url)
82
+ if (config.razor_url) {
81
83
  razorPayInstance = new RazorPay({ key_id: config.KEY, key_secret: config.SECRET })
84
+ }
82
85
  if (config.open_money_url) {
83
86
  openMoneyInstance = new OpenMoney(config);
84
87
  }
88
+ if (config.payu_url) {
89
+ payuInstance = new PayU(config);
90
+ }
85
91
 
86
92
  let usingMultiDbOrm = false;
87
93
  if (config.db_url) {
@@ -89,7 +95,22 @@ module.exports = function (app, callbacks) {
89
95
  usingMultiDbOrm = false;
90
96
 
91
97
  } else if (app.multidborm) {
92
- Transaction = require('../models/np_multidbplugin.js')('nptransactions', app.multidborm);
98
+ const sample = {
99
+ orderId: "string",
100
+ cusId: "string",
101
+ time: 1770051201752,
102
+ timeStamp: 1770051201752,
103
+ status: "string",
104
+ name: "string",
105
+ email: "string",
106
+ phone: "string",
107
+ amount: 1,
108
+ pname: "string",
109
+ extra: "stringlarge",
110
+ TXNID: "27118670199",
111
+ returnUrl: "string"
112
+ }
113
+ Transaction = require('../models/np_multidbplugin.js')('nptransactions', app.multidborm, sample);
93
114
  Transaction.db = app.multidborm;
94
115
  Transaction.modelname = 'nptransactions'
95
116
  Transaction.idFieldName = 'orderId'
@@ -124,24 +145,24 @@ module.exports = function (app, callbacks) {
124
145
  module.init = async function (req, res) {
125
146
 
126
147
  if (!req.body.ORDER_ID && !req.body.EMAIL && req.query.to) {
127
-
128
148
  let toData = JSON.parse(nodeBase64.decode(req.query.to));
129
149
  req.body.NAME = toData.NAME
130
150
  req.body.EMAIL = toData.EMAIL
151
+ req.body.TXN_AMOUNT = toData.TXN_AMOUNT
131
152
  req.body.MOBILE_NO = toData.MOBILE_NO
132
- req.body.ORDER_ID = toData.ORDER_ID
153
+ req.body.ORDER_ID = toData.ORDER_ID || toData.ORDERID
154
+ req.body.PRODUCT_NAME = toData.PRODUCT_NAME
155
+ req.body.RETURN_URL = toData.RETURN_URL
133
156
  }
134
157
 
135
158
  sanitizeRequest(req.body);
136
159
  let gotAllParams = true;
137
-
160
+ let checkedFields = ['TXN_AMOUNT', 'PRODUCT_NAME', 'MOBILE_NO', 'NAME', 'EMAIL']
138
161
  if (req.body !== undefined) {
139
- let checks = [req.body.TXN_AMOUNT, req.body.PRODUCT_NAME,
140
- req.body.MOBILE_NO, req.body.NAME, req.body.EMAIL]
141
162
 
142
- for (var i = 0; i < checks.length; i++) {
163
+ for (var i = 0; i < checkedFields.length; i++) {
143
164
 
144
- if (checks[i] === undefined) {
165
+ if (req.body[checkedFields[i]] === undefined) {
145
166
  gotAllParams = false;
146
167
  break;
147
168
  }
@@ -165,7 +186,7 @@ module.exports = function (app, callbacks) {
165
186
  params['WEBSITE'] = req.body.WEBSITE;
166
187
  params['CHANNEL_ID'] = req.body.CHANNEL_ID;
167
188
  params['INDUSTRY_TYPE_ID'] = req.body.INDUSTRY_TYPE_ID;
168
- params['ORDER_ID'] = req.body.ORDER_ID;
189
+ params['ORDER_ID'] = req.body.ORDER_ID || req.body.ORDERID;
169
190
  params['CUST_ID'] = req.body.CUST_ID;
170
191
  params['TXN_AMOUNT'] = req.body.TXN_AMOUNT;
171
192
  params['CALLBACK_URL'] = req.body.CALLBACK_URL + "?order_id=" + req.body.ORDER_ID;
@@ -430,6 +451,10 @@ module.exports = function (app, callbacks) {
430
451
  res.end();
431
452
 
432
453
  }
454
+ else if (config.payu_url) {
455
+ const payuRequest = payuInstance.generatePaymentRequest(params);
456
+ payuInstance.renderProcessingPage(params, payuRequest, res, loadingSVG);
457
+ }
433
458
  else if (config.open_money_url) {
434
459
  try {
435
460
  let pmttoken = await openMoneyInstance.generatePaymentToken(params);
@@ -511,6 +536,8 @@ module.exports = function (app, callbacks) {
511
536
  checksum_lib.genchecksum(params, config.KEY, showConfirmation);
512
537
  else if (config.razor_url) {
513
538
  showConfirmation()
539
+ } else if (config.payu_url) {
540
+ showConfirmation()
514
541
  } else if (config.open_money_url) {
515
542
  showConfirmation()
516
543
  }
@@ -519,49 +546,55 @@ module.exports = function (app, callbacks) {
519
546
 
520
547
 
521
548
 
522
- if ((req.body.ORDER_ID !== undefined && req.body.ORDER_ID.length > 2)) {
523
-
524
549
 
525
- var myquery = { orderId: req.body.ORDER_ID };
526
- Transaction.findOne(myquery, function (err, orderData) {
550
+ function onOrder(orderId) {
527
551
 
528
- onTxn(orderData);
552
+ var txnTask = new Transaction({
553
+ orderId: orderId,
554
+ cusId: user.id,
555
+ time: Date.now(),
556
+ timeStamp: Date.now(),
557
+ status: 'INITIATED',
558
+ name: user.name,
559
+ email: user.email,
560
+ phone: user.phone,
561
+ amount: req.body.TXN_AMOUNT,
562
+ pname: req.body.PRODUCT_NAME,
563
+ extra: '',
564
+ returnUrl: req.body.RETURN_URL || ''
565
+ });
529
566
 
530
- }, usingMultiDbOrm ? Transaction : undefined);
567
+ return txnTask.save().then(onTxn)
568
+ .catch(err => {
531
569
 
570
+ console.log(err)
571
+ if (req.body.RETURN_URL) {
572
+ res.redirect(req.body.RETURN_URL + "?status=failed")
573
+ return;
574
+ }
575
+ res.redirect('')
576
+ });
577
+ }
532
578
 
579
+ if ((req.body.ORDER_ID !== undefined && req.body.ORDER_ID.length > 2)) {
580
+ var myquery = { orderId: req.body.ORDER_ID };
581
+ Transaction.findOne(myquery, function (err, orderData) {
582
+ if (err || (!orderData)) {
583
+ if (gotAllParams) {
584
+ console.log("Creating new order for ", req.body.ORDER_ID)
585
+ onOrder(req.body.ORDER_ID)
586
+ }
587
+ else {
588
+ res.send({ message: "Order Not Found or missing required data: " + checkedFields.join(", "), ORDERID: req.body.ORDER_ID })
589
+ }
590
+ }
591
+ else {
592
+ onTxn(orderData);
593
+ }
533
594
 
595
+ }, usingMultiDbOrm ? Transaction : undefined);
534
596
  }
535
597
  else {
536
-
537
-
538
- function onOrder(orderId) {
539
-
540
- var txnTask = new Transaction({
541
-
542
- orderId: orderId,
543
- cusId: user.id,
544
- time: Date.now(),
545
- timeStamp: Date.now(),
546
- status: 'INITIATED',
547
- name: user.name,
548
- email: user.email,
549
- phone: user.phone,
550
- amount: req.body.TXN_AMOUNT,
551
- pname: req.body.PRODUCT_NAME,
552
- extra: ''
553
-
554
- });
555
-
556
- txnTask.save().then(onTxn)
557
- .catch(err => {
558
-
559
- console.log(err)
560
-
561
- res.redirect('')
562
- });
563
- }
564
-
565
598
  let orderId;
566
599
  if (config.paytm_url) {
567
600
  orderId = "pay_" + makeid(config.id_length || IDLEN)
@@ -588,6 +621,9 @@ module.exports = function (app, callbacks) {
588
621
  else if (config.open_money_url) {
589
622
  orderId = "pay_" + makeid(config.id_length || IDLEN)
590
623
  onOrder(orderId)
624
+ } else if (config.payu_url) {
625
+ orderId = "payu_" + makeid(config.id_length || IDLEN)
626
+ onOrder(orderId)
591
627
  }
592
628
 
593
629
 
@@ -636,15 +672,30 @@ module.exports = function (app, callbacks) {
636
672
  var myquery = { orderId: req.body.ORDERID };
637
673
 
638
674
  Transaction.findOne(myquery, function (err, objForUpdate) {
639
-
640
- if (err) {
675
+ let returnUrl = objForUpdate ? objForUpdate.returnUrl : null;
676
+ if (returnUrl == 'undefined') {
677
+ returnUrl = undefined
678
+ }
679
+ if (err || !objForUpdate) {
680
+ if (returnUrl) {
681
+ let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
682
+ returnUrl = returnUrl + separator + 'status=FAILED&message=txn_not_found&ORDERID=' + req.body.ORDERID;
683
+ return res.redirect(returnUrl);
684
+ }
641
685
  res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
642
686
  return;
643
687
  }
644
688
  if (objForUpdate.status != ("INITIATED") && objForUpdate.status != ("TXN_PENDING") && objForUpdate.status != ("PENDING")) {
645
689
  objForUpdate.readonly = "readonly"
646
690
  objForUpdate.action = config.homepage
647
- res.render(vp + "result.hbs", objForUpdate);
691
+ if (returnUrl) {
692
+ let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
693
+ returnUrl = returnUrl + separator + 'status=' + objForUpdate.status + '&ORDERID=' + objForUpdate.orderId + '&TXNID=' + objForUpdate.TXNID;
694
+ return res.redirect(returnUrl);
695
+ }
696
+ else {
697
+ res.render(vp + "result.hbs", objForUpdate);
698
+ }
648
699
  console.log("Transaction already processed ", req.body.ORDERID)
649
700
  // res.send({ message: "Transaction already processed", status: objForUpdate.status, ORDERID: objForUpdate.orderId, TXNID: objForUpdate.TXNID, TXNID: req.body.TXNID })
650
701
  return;
@@ -660,6 +711,11 @@ module.exports = function (app, callbacks) {
660
711
  Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
661
712
 
662
713
  if (err) {
714
+ if (returnUrl) {
715
+ let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
716
+ returnUrl = returnUrl + separator + 'status=FAILED&message=update_error&ORDERID=' + req.body.ORDERID;
717
+ return res.redirect(returnUrl);
718
+ }
663
719
  res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
664
720
  }
665
721
  else {
@@ -668,6 +724,11 @@ module.exports = function (app, callbacks) {
668
724
  callbacks.onFinish(req.body.ORDERID, req.body);
669
725
  objForUpdate.readonly = "readonly"
670
726
  objForUpdate.action = config.homepage
727
+ if (returnUrl) {
728
+ let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
729
+ returnUrl = returnUrl + separator + 'status=' + objForUpdate.status + '&ORDERID=' + objForUpdate.orderId + '&TXNID=' + objForUpdate.TXNID;
730
+ return res.redirect(returnUrl);
731
+ }
671
732
  res.render(vp + "result.hbs", objForUpdate);
672
733
  }
673
734
  });
@@ -721,6 +782,16 @@ module.exports = function (app, callbacks) {
721
782
  isCancelled = true;
722
783
  }
723
784
  }
785
+ else if (config.payu_url) {
786
+ const payuRest = await payuInstance.verifyResult(req);
787
+ result = payuRest.valid;
788
+ req.body.STATUS = payuRest.STATUS;
789
+ req.body.TXNID = payuRest.TXNID;
790
+ req.body.ORDERID = payuRest.ORDERID || req.query.order_id;
791
+ req.body.extras = payuRest.data;
792
+ result = true;
793
+ isCancelled = payuRest.cancelled;
794
+ }
724
795
  else if (config.open_money_url) {
725
796
  let openRest = await openMoneyInstance.verifyResult(req);
726
797
  result = true;
@@ -806,6 +877,9 @@ module.exports = function (app, callbacks) {
806
877
  res.send({ message: "Unsupported event : " + req.body.event })
807
878
  }
808
879
  }
880
+ else if (config.payu_url) {
881
+ payuInstance.processWebhook(req, res, updateTransaction)
882
+ }
809
883
  else if (config.open_money_url) {
810
884
  openMoneyInstance.processWebhook(req, res, updateTransaction)
811
885
  }
@@ -832,6 +906,9 @@ module.exports = function (app, callbacks) {
832
906
  let order = await razorPayInstance.orders.create(options);
833
907
  id = order.id;
834
908
  }
909
+ else if (config.payu_url) {
910
+ id = "payu_" + makeid(config.id_length || IDLEN)
911
+ }
835
912
  else if (config.open_money_url) {
836
913
  id = "pay_" + makeid(config.id_length || IDLEN)
837
914
  }
@@ -847,6 +924,7 @@ module.exports = function (app, callbacks) {
847
924
  phone: user.phone,
848
925
  amount: req.body.TXN_AMOUNT,
849
926
  pname: req.body.PRODUCT_NAME,
927
+ returnUrl: req.body.RETURN_URL || '',
850
928
  extra: (req.body.EXTRA || '')
851
929
 
852
930
  });
@@ -857,7 +935,10 @@ module.exports = function (app, callbacks) {
857
935
  NAME: txn.name,
858
936
  EMAIL: txn.email,
859
937
  MOBILE_NO: txn.phone,
860
- ORDER_ID: txn.orderId
938
+ ORDER_ID: txn.orderId,
939
+ RETURN_URL: txn.returnUrl,
940
+ TXN_AMOUNT: txn.amount,
941
+ PRODUCT_NAME: txn.pname
861
942
  }))
862
943
 
863
944
  txn.payurl = config.host_url + '/' + config.path_prefix + '/init?to=' + urlData64;
@@ -889,6 +970,7 @@ module.exports = function (app, callbacks) {
889
970
  req.body.EMAIL = createTxnResult.email
890
971
  req.body.MOBILE_NO = createTxnResult.phone
891
972
  req.body.ORDER_ID = createTxnResult.orderId
973
+ req.body.RETURN_URL = createTxnResult.returnUrl
892
974
  module.init(req, {
893
975
  render: (renderPath, initResultRender) => {
894
976
  // console.log(initResultRender)
@@ -1002,6 +1084,28 @@ module.exports = function (app, callbacks) {
1002
1084
  res.send(orderData);
1003
1085
  }
1004
1086
  }
1087
+ else if (config.payu_url) {
1088
+ let result = await payuInstance.getPaymentStatus(req.body.ORDER_ID)
1089
+ if (result && result.transaction_details && result.transaction_details[req.body.ORDER_ID]) {
1090
+ let txn = result.transaction_details[req.body.ORDER_ID];
1091
+ let status = 'TXN_FAILURE'
1092
+ if (txn.status == 'success') {
1093
+ status = 'TXN_SUCCESS'
1094
+ }
1095
+ else if (txn.status == 'pending') {
1096
+ status = 'TXN_PENDING'
1097
+ }
1098
+ onStatusUpdate({
1099
+ STATUS: status,
1100
+ ORDERID: req.body.ORDER_ID,
1101
+ TXNID: txn.mihpayid || txn.txnid,
1102
+ payu: txn
1103
+ })
1104
+ }
1105
+ else {
1106
+ res.send(orderData);
1107
+ }
1108
+ }
1005
1109
  else if (config.open_money_url) {
1006
1110
  let extras = JSON.parse(orderData.extra)
1007
1111
  if (!extras || !extras.layer_pay_token_id) {
@@ -1,101 +1,111 @@
1
- module.exports = function (modelName, db) {
2
-
3
- class MultiDbMapper {
4
-
5
- idFieldName
6
- objectData
7
- constructor(objectData) {
8
- MultiDbMapper.sanitizeRequest(objectData)
9
- this.objectData = objectData;
10
- }
11
-
12
-
13
- static async sanitizeRequest(body) {
14
-
15
- if (!body)
16
- return;
17
- if (body.amount)
18
- body.amount = parseFloat(body.amount);
19
- if (body.TXN_AMOUNT)
20
- body.amount = parseFloat(body.TXN_AMOUNT);
21
- }
22
-
23
- async save() {
24
- var response = await MultiDbMapper.db.insert(MultiDbMapper.modelname, this.objectData,this.objectData[MultiDbMapper.idFieldName]);
25
- if ( typeof response == Object && response.ops[0])
26
- response = response.ops[0];
27
- else
28
- response = this.objectData
29
-
30
- MultiDbMapper.sanitizeRequest(response)
31
-
32
- return response;
33
- }
34
-
35
- //callback(err,resp)
36
- static async findOne(query, cb) {
37
-
38
- var response;
39
- try {
40
- response = await MultiDbMapper.db.getOne(MultiDbMapper.modelname, query);
41
- MultiDbMapper.sanitizeRequest(response)
42
- } catch (e) {
43
- if (cb)
44
- return cb(e, undefined)
45
- else
46
- throw e;
47
- }
48
- if (cb)
49
- cb(undefined, response);
50
- else
51
- return response;
52
- }
53
-
54
- static async updateOne(query, newValue, cb) {
55
-
56
- var response;
57
- try {
58
-
59
- response = await MultiDbMapper.db.update(MultiDbMapper.modelname, query, newValue['$set']);
60
-
61
- } catch (e) {
62
- if (cb)
63
- return cb(e, undefined)
64
- else
65
- throw e;
66
- }
67
-
68
- if (cb)
69
- cb(undefined, response);
70
- else
71
- return response;
72
- }
73
-
74
- static async deleteOne(query, cb) {
75
-
76
- var response;
77
- try {
78
-
79
- response = await MultiDbMapper.db.delete(MultiDbMapper.modelname, query);
80
- MultiDbMapper.sanitizeRequest(response)
81
-
82
- } catch (e) {
83
- if (cb)
84
- return cb(e, undefined)
85
- else
86
- throw e;
87
- }
88
- if (cb)
89
- cb(undefined, response)
90
- else
91
- return response;
92
-
93
- }
94
-
95
- }
96
-
97
- MultiDbMapper.modelname = modelName;
98
- MultiDbMapper.db = db;
99
- return MultiDbMapper;
100
- }
101
-
1
+ module.exports = function (modelName, db, sampleData) {
2
+
3
+ if (db && sampleData && db.create) {
4
+ db.create(modelName, sampleData)
5
+ .then(() => {
6
+ console.log('Model Created in db ', modelName);
7
+ })
8
+ .catch((e) => {
9
+ console.log('Error in creating model ', e);
10
+ });
11
+ }
12
+
13
+ class MultiDbMapper {
14
+
15
+ idFieldName
16
+ objectData
17
+ constructor(objectData) {
18
+ MultiDbMapper.sanitizeRequest(objectData)
19
+ this.objectData = objectData;
20
+ }
21
+
22
+
23
+ static async sanitizeRequest(body) {
24
+
25
+ if (!body)
26
+ return;
27
+ if (body.amount)
28
+ body.amount = parseFloat(body.amount);
29
+ if (body.TXN_AMOUNT)
30
+ body.amount = parseFloat(body.TXN_AMOUNT);
31
+ }
32
+
33
+ async save() {
34
+ var response = await MultiDbMapper.db.insert(MultiDbMapper.modelname, this.objectData, this.objectData[MultiDbMapper.idFieldName]);
35
+ if (typeof response == Object && response.ops[0])
36
+ response = response.ops[0];
37
+ else
38
+ response = this.objectData
39
+
40
+ MultiDbMapper.sanitizeRequest(response)
41
+
42
+ return response;
43
+ }
44
+
45
+ //callback(err,resp)
46
+ static async findOne(query, cb) {
47
+
48
+ var response;
49
+ try {
50
+ response = await MultiDbMapper.db.getOne(MultiDbMapper.modelname, query);
51
+ MultiDbMapper.sanitizeRequest(response)
52
+ } catch (e) {
53
+ if (cb)
54
+ return cb(e, undefined)
55
+ else
56
+ throw e;
57
+ }
58
+ if (cb)
59
+ cb(undefined, response);
60
+ else
61
+ return response;
62
+ }
63
+
64
+ static async updateOne(query, newValue, cb) {
65
+
66
+ var response;
67
+ try {
68
+
69
+ response = await MultiDbMapper.db.update(MultiDbMapper.modelname, query, newValue['$set']);
70
+
71
+ } catch (e) {
72
+ if (cb)
73
+ return cb(e, undefined)
74
+ else
75
+ throw e;
76
+ }
77
+
78
+ if (cb)
79
+ cb(undefined, response);
80
+ else
81
+ return response;
82
+ }
83
+
84
+ static async deleteOne(query, cb) {
85
+
86
+ var response;
87
+ try {
88
+
89
+ response = await MultiDbMapper.db.delete(MultiDbMapper.modelname, query);
90
+ MultiDbMapper.sanitizeRequest(response)
91
+
92
+ } catch (e) {
93
+ if (cb)
94
+ return cb(e, undefined)
95
+ else
96
+ throw e;
97
+ }
98
+ if (cb)
99
+ cb(undefined, response)
100
+ else
101
+ return response;
102
+
103
+ }
104
+
105
+ }
106
+
107
+ MultiDbMapper.modelname = modelName;
108
+ MultiDbMapper.db = db;
109
+ return MultiDbMapper;
110
+ }
111
+
package/example.js CHANGED
@@ -1,51 +1,42 @@
1
- var express=require('express')
2
- var app=express()
1
+ var express = require('express')
2
+ var app = express()
3
+ try { require('dotenv').config(); } catch (e) { }
3
4
 
4
- var MONGOURL="mongodb+srv://username:pasws@host.net/dbname";
5
-
6
- /***
7
- * Uncomment in case you want to use multidborm
8
- * https://www.npmjs.com/package/multi-db-orm
9
-
10
- const { MultiDbORM, FireStoreDB, MongoDB, SQLiteDB, Sync } = require("multi-db-orm");
11
- var mongodb = new MongoDB(MONGOURL);
12
- app.multidborm = mongodb;
13
-
14
- */
5
+ // use https://www.npmjs.com/package/multi-db-orm for persistance
6
+ const { SQLiteDB } = require("multi-db-orm");
7
+ var db = new SQLiteDB("test.db");
8
+ app.multidborm = db;
15
9
 
10
+ // Configuration: prefer values from environment, fall back to sample defaults
16
11
  app.set('np_config', {
17
- "host_url":"http://127.0.0.1:5542",
18
- "view_path":"/../views/",
19
- "paytm_url":"https://securegw-stage.paytm.in",
20
- "MID":"XXXXX",
21
- "WEBSITE":"WEBSTAGING",
22
- "KEY":"XXXXX",
23
- "CHANNEL_ID":"WAP",
24
- "INDUSTRY_TYPE_ID":"Retail",
25
- "homepage":"/",
26
- "path_prefix":"_pay",
27
- "theme_color":"#231530",
28
- "db_url":MONGOURL // Remove this property in case you want to use multidborm
12
+ host_url: process.env.NP_HOST_URL || 'https://pay.example.com',
13
+ view_path: '/../views/',
14
+ payu_url: 'https://secure.payu.in', // for test use https://test.payu.in
15
+ //"razor_url":"https://api.razorpay.com/v1/", // for test use https://api.razorpay.com/v1/
16
+ //"open_money_url":"https://sandbox.openmoney, // for test use https://sandbox.openmoney
17
+ MID: process.env.NP_MID || '12345',
18
+ WEBSITE: process.env.NP_WEBSITE || 'WEBSTAGING',
19
+ KEY: process.env.NP_KEY || 'abcdef',
20
+ SECRET: process.env.NP_SECRET || 'abcdef', // salt for payu
21
+ CHANNEL_ID: 'WAP',
22
+ INDUSTRY_TYPE_ID: 'Retail',
23
+ homepage: '/',
24
+ path_prefix: '_pay',
25
+ theme_color: '#231530',
26
+ // "db_url":MONGOURL // Remove this property in case you want to use multidborm
29
27
  });
30
28
 
31
- if(process.env.CONFIG){
32
-
29
+ if (process.env.CONFIG) {
33
30
  app.set('np_config', JSON.parse(process.env.CONFIG));
34
- console.log('using config from env',process.env.CONFIG);
35
-
31
+ console.log('using config from env', process.env.CONFIG);
36
32
  }
37
33
 
38
- require('./index')(app,express)
34
+ require('./index')(app, express)
39
35
 
40
- app.all('/',function(req,res)
41
- {
36
+ app.all('/', function (req, res) {
42
37
  res.redirect('/_pay/init')
43
38
  })
44
39
 
45
-
46
- app.listen(process.env.PORT || 5542,function()
47
- {
48
-
49
- console.log("Server Started At ",process.env.PORT || 5542)
50
-
40
+ app.listen(process.env.PORT || 5542, function () {
41
+ console.log("Server Started At ", process.env.PORT || 5542)
51
42
  })
package/package.json CHANGED
@@ -1,42 +1,44 @@
1
- {
2
- "name": "node-paytmpg",
3
- "version": "5.3.2",
4
- "description": "Payment Gateway Integration using NodeJS",
5
- "main": "index.js",
6
- "scripts": {
7
- "start": "node example.js"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/shiveshnavin/node_paytm.git"
12
- },
13
- "keywords": [
14
- "nodejs",
15
- "paytm",
16
- "payment",
17
- "gateway",
18
- "paytm",
19
- "api",
20
- "paytm",
21
- "nodejs"
22
- ],
23
- "author": "Shivesh Navin",
24
- "license": "MIT",
25
- "bugs": {
26
- "url": "https://github.com/shiveshnavin/node_paytm/issues"
27
- },
28
- "homepage": "https://github.com/shiveshnavin/node_paytm#readme",
29
- "dependencies": {
30
- "axios": "^1.3.4",
31
- "body-parser": "^1.18.3",
32
- "crypto": "^1.0.1",
33
- "express": "^4.16.4",
34
- "express-handlebars": "^3.0.2",
35
- "mongoose": "^5.4.16",
36
- "multi-db-orm": "^1.0.9",
37
- "nodejs-base64-converter": "^1.0.5",
38
- "razorpay": "^2.0.6",
39
- "request": "^2.88.0",
40
- "util": "^0.11.1"
41
- }
42
- }
1
+ {
2
+ "name": "node-paytmpg",
3
+ "version": "5.4.2",
4
+ "description": "Payment Gateway Integration using NodeJS",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "node example.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/shiveshnavin/node_paytm.git"
12
+ },
13
+ "keywords": [
14
+ "nodejs",
15
+ "paytm",
16
+ "payment",
17
+ "gateway",
18
+ "paytm",
19
+ "api",
20
+ "paytm",
21
+ "nodejs"
22
+ ],
23
+ "author": "Shivesh Navin",
24
+ "license": "MIT",
25
+ "bugs": {
26
+ "url": "https://github.com/shiveshnavin/node_paytm/issues"
27
+ },
28
+ "homepage": "https://github.com/shiveshnavin/node_paytm#readme",
29
+ "dependencies": {
30
+ "axios": "^1.3.4",
31
+ "body-parser": "^1.18.3",
32
+ "crypto": "^1.0.1",
33
+ "dotenv": "^17.2.3",
34
+ "express": "^4.16.4",
35
+ "express-handlebars": "^3.0.2",
36
+ "mongoose": "^5.4.16",
37
+ "multi-db-orm": "^3.1.0",
38
+ "nodejs-base64-converter": "^1.0.5",
39
+ "razorpay": "^2.0.6",
40
+ "request": "^2.88.0",
41
+ "sqlite3": "^5.1.7",
42
+ "util": "^0.11.1"
43
+ }
44
+ }