node-paytmpg 4.0.1 → 5.1.1

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.
@@ -6,7 +6,8 @@ name: Node.js Package
6
6
  on:
7
7
  release:
8
8
  types: [created]
9
-
9
+ push:
10
+ branches: [ master ]
10
11
  jobs:
11
12
  publish:
12
13
  runs-on: ubuntu-latest
package/README.MD CHANGED
@@ -1,11 +1,12 @@
1
1
  ## Node JS Payments Easy Integration
2
2
 
3
- [![NPM Publish](https://github.com/shiveshnavin/node_paytm/actions/workflows/nodejs.yml/badge.svg)](https://github.com/shiveshnavin/node_paytm/actions/workflows/npm-publish.yml)
3
+ [![NPM Publish](https://github.com/shiveshnavin/node_paytm/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/shiveshnavin/node_paytm/actions/workflows/npm-publish.yml)
4
4
  [![Node.js CI](https://github.com/shiveshnavin/node_paytm/actions/workflows/nodejs.yml/badge.svg)](https://github.com/shiveshnavin/node_paytm/actions/workflows/nodejs.yml)
5
5
 
6
6
  Support for :
7
7
  - Paytm
8
8
  - RazorPay
9
+ - Open Money
9
10
 
10
11
  Does all the hardwork for you while integrating payments in any express app. Comes with inbuilt UI and REST APIs for lightning fast development and prototyping .
11
12
 
@@ -22,7 +23,7 @@ Example App Sourcecode : https://github.com/shiveshnavin/payment-gateway-example
22
23
  ### Requirments
23
24
 
24
25
  1. MongoDB / Firestore / SQlite
25
- 2. Your Paytm Credentials
26
+ 2. Your Merchant Credentials
26
27
  3. Express . This only works with NodeJS express server
27
28
 
28
29
  You can get your paytm credentials here
@@ -78,8 +79,25 @@ homepage : Homepage of your website where user can go after payment confirmation
78
79
  path_prefix : All node_paytm_pg apis/pages will be available relative to this path prefix
79
80
  db_url : Your MongoDB url in case you want to use legacy mongodb connection . You can use multidborm to support MongoDB/Firestore/Sqlite
80
81
  id_length: Length of Order ID and User ID
82
+
83
+ ```
84
+
85
+ ### For Open Money
86
+ In case you want to use Open Money https://app.open.money/settings/developer-api/api . Use the below configuration
87
+ ```
88
+ host_url : Host URL of your server . This will be used to redirect user after payment
89
+ view_path : Ignore and dont change unless you know what you are doing . This is the useful in case you want to modify payment init page UI from node_paytm_pg library
90
+ open_money_url : SANDBOX https://sandbox-icp-api.bankopen.co/api OR LIVE https://icp-api.bankopen.co/api
91
+ KEY : Your generated API Key
92
+ SECRET : Your API secret
93
+ homepage : Homepage of your website where user can go after payment confirmation page
94
+ path_prefix : All node_paytm_pg apis/pages will be available relative to this path prefix
95
+ db_url : Your MongoDB url in case you want to use legacy mongodb connection . You can use multidborm to support MongoDB/Firestore/Sqlite
96
+ id_length: Length of Order ID and User ID (Optional)
97
+
81
98
  ```
82
99
 
100
+
83
101
  Place these 2 statements in your main nodejs file before calling app.listen(..)
84
102
 
85
103
  ```javascript
@@ -202,6 +220,21 @@ var User=PayTMPG.User;
202
220
 
203
221
  ```
204
222
 
223
+ ### Webhooks
224
+
225
+ Webhooks can issued at at `/_pay/api/webhook` and are useful for payments captured late.
226
+
227
+ #### For razorpay webhook
228
+ Make sure to use the same secret in your webhook as the merchant secret.
229
+ https://razorpay.com/docs/webhooks/
230
+
231
+ #### For paytm
232
+ Nothing extra needed
233
+ https://developer.paytm.com/docs/callback-and-webhook/?ref=callbackWebhook
234
+
235
+ #### For Open Money
236
+ Nothing extra needed
237
+ https://docs.bankopen.com/reference/webhook-url
205
238
 
206
239
 
207
240
  License : GPL
@@ -0,0 +1,514 @@
1
+ var crypto = require('crypto');
2
+ const { resolve } = require('path');
3
+ var reqpost = require('request');
4
+
5
+ class OpenMoney {
6
+ config
7
+ constructor(npconfig) {
8
+ npconfig.accesskey = npconfig.KEY
9
+ npconfig.secretkey = npconfig.SECRET
10
+ npconfig.url = npconfig.open_money_url
11
+ npconfig.script_url = (npconfig.url.indexOf("sandbox") == -1) ? "https://payments.open.money/layer" : "https://sandbox-payments.open.money/layer"
12
+ this.config = npconfig;
13
+
14
+ }
15
+
16
+ generatePaymentToken(params) {
17
+
18
+ let config = this.config;
19
+ return new Promise((resolve, reject) => {
20
+ var payment_token_data;
21
+ let open_txn = {
22
+ "amount": params['TXN_AMOUNT'],
23
+ "currency": params['CURRENCY'] || "INR",
24
+ "name": params['NAME'],
25
+ "email_id": params['EMAIL'],
26
+ "contact_number": params['MOBILE_NO'],
27
+ "mtx": params['ORDER_ID']
28
+ }
29
+ create_payment_token(open_txn,
30
+ config.accesskey,
31
+ config.secretkey,
32
+ config.url, function (layer_payment_token_data) {
33
+ /*Object.keys(layer_payment_token_data).forEach(function(key) {
34
+ console.log(key + layer_payment_token_data[key]);
35
+ });*/
36
+
37
+ if (typeof layer_payment_token_data['error'] != 'undefined')
38
+ return reject(JSON.stringify('E55 Payment error. ' + layer_payment_token_data['error']));
39
+
40
+ if (typeof layer_payment_token_data["id"] == 'undefined' || !layer_payment_token_data["id"])
41
+ return reject(JSON.stringify('Payment error. ' + 'Layer token ID cannot be empty.'));
42
+
43
+ if (typeof layer_payment_token_data["id"] != 'undefined') {
44
+
45
+ get_payment_token(layer_payment_token_data["id"], config.accesskey, config.secretkey, config.url, function (payment_token_data) {
46
+
47
+ if (payment_token_data.error) {
48
+ return reject({
49
+ error: payment_token_data.error
50
+ })
51
+ }
52
+ payment_token_data = JSON.parse(payment_token_data);
53
+
54
+ if (typeof payment_token_data['error'] != 'undefined')
55
+ return reject({ error: (JSON.stringify('E56 Payment error. ' + payment_token_data['error'])) })
56
+ if (typeof payment_token_data['status'] != 'undefined' && payment_token_data['status'] == "paid")
57
+ return reject({ error: (JSON.stringify("Layer: this order has already been paid.")) })
58
+ if (parseFloat(payment_token_data['amount']) != parseFloat(params['TXN_AMOUNT']))
59
+ return reject({ error: (JSON.stringify("Layer: an amount mismatch occurred.")) })
60
+
61
+ var hash = create_hash({
62
+ 'layer_pay_token_id': payment_token_data['id'],
63
+ 'layer_order_amount': payment_token_data['amount'],
64
+ 'tranid': params['ORDER_ID'],
65
+ }, config.accesskey, config.secretkey);
66
+ params['CHECKSUM'] = hash;
67
+
68
+ var html = `<form action='${params['CALLBACK_URL']}' method='post' style='display: none' name='layer_payment_int_form'>`;
69
+ html += "<input type='hidden' name='layer_pay_token_id' value='" + payment_token_data['id'] + "'>";
70
+ html += "<input type='hidden' name='tranid' value='" + params['ORDER_ID'] + "'>";
71
+ html += "<input type='hidden' name='layer_order_amount' value='" + payment_token_data['amount'] + "'>";
72
+ html += "<input type='hidden' id='layer_payment_id' name='layer_payment_id' value=''>";
73
+ html += "<input type='hidden' id='fallback_url' name='fallback_url' value=''>";
74
+ html += "<input type='hidden' name='hash' value='" + hash + "'></form>";
75
+ html += "<script>";
76
+ html += "var layer_params = {payment_token_id:'" + payment_token_data['id'] + "',accesskey:'" + config.accesskey + "'};";
77
+ html += "</script>";
78
+ html += `<script src="layer_checkout.js"></script>`;
79
+
80
+ return resolve({
81
+ html: html,
82
+ params: params,
83
+ data: config,
84
+ tokenid: payment_token_data['id'],
85
+ amount: payment_token_data['amount'],
86
+ hash: hash
87
+ })
88
+ });
89
+ }
90
+ });
91
+ })
92
+ }
93
+
94
+ verifyResult(req) {
95
+ let config = this.config;
96
+ return new Promise((resolve, reje) => {
97
+
98
+ var txnid = "";
99
+ var amount = "";
100
+ var status = "";
101
+ var msg = "";
102
+ var tokenid = "";
103
+ var paymentid = "";
104
+ var payment_data = {};
105
+
106
+ if (!req.body.layer_payment_id) {
107
+ return resolve({
108
+ STATUS: 'TXN_FAILURE',
109
+ ORDERID: txnid,
110
+ TXNID: paymentid,
111
+ reason: 'invalid response'
112
+ })
113
+ }
114
+ else {
115
+ txnid = req.body.tranid;
116
+ amount = req.body.layer_order_amount;
117
+ tokenid = req.body.layer_pay_token_id;
118
+ paymentid = req.body.layer_payment_id;
119
+ }
120
+ var data = {
121
+ 'layer_pay_token_id': tokenid,
122
+ 'layer_order_amount': amount,
123
+ 'tranid': txnid,
124
+ };
125
+
126
+ if (verify_hash(data, req.body.hash, config.accesskey, config.secretkey, config.url)) {
127
+ get_payment_details(paymentid, config.accesskey, config.secretkey, config.url, function (response) {
128
+ if (response === "{}") {
129
+
130
+ return resolve({
131
+ STATUS: 'TXN_FAILURE',
132
+ ORDERID: txnid,
133
+ TXNID: paymentid,
134
+ message: 'Invalid Response',
135
+ data: payment_data
136
+ })
137
+
138
+ }
139
+ else {
140
+ payment_data = JSON.parse(response);
141
+ if (!payment_data['payment_token'] || payment_data['payment_token']['id'] != tokenid) {
142
+ return resolve({
143
+ STATUS: 'TXN_FAILURE',
144
+ ORDERID: txnid,
145
+ TXNID: paymentid,
146
+ message: 'received layer_pay_token_id and collected layer_pay_token_id doesnt match',
147
+ data: payment_data
148
+ })
149
+ }
150
+ else {
151
+ let status = ""
152
+ if (payment_data.status == "captured" ||
153
+ payment_data.status == "late_authorized") {
154
+ status = 'TXN_SUCCESS'
155
+ }
156
+ else if (payment_data.status == "pending") {
157
+ status = 'TXN_PENDING'
158
+ }
159
+ else {
160
+ status = 'TXN_FAILURE'
161
+ }
162
+
163
+ return resolve({
164
+ STATUS: status,
165
+ ORDERID: txnid,
166
+ TXNID: paymentid,
167
+ data: (payment_data)
168
+ })
169
+ }
170
+ }
171
+ });
172
+
173
+ }
174
+ else {
175
+ return resolve({
176
+ STATUS: 'TXN_FAILURE',
177
+ ORDERID: txnid,
178
+ TXNID: paymentid,
179
+ message: 'Invalid Response'
180
+ })
181
+ }
182
+ })
183
+ }
184
+
185
+ processWebhook(req, res, updateTransaction) {
186
+ let config = this.config;
187
+ let events = [
188
+ "payment_captured", "payment_pending",
189
+ "payment_failed",
190
+ "payment_cancelled"]
191
+ if (req.body.event && events.indexOf(req.body.event) > -1) {
192
+ if (req.body.payment_token) {
193
+
194
+ let payment_token = req.body.payment_token;
195
+ let orderId = payment_token.mtx
196
+ let paymentid = req.body.id
197
+ let tokenid = payment_token.id
198
+ let payment_data = {}
199
+ let amount = req.body.amount;
200
+
201
+ setTimeout(() => {
202
+
203
+ req.body.layer_pay_token_id = tokenid;
204
+ // var data = {
205
+ // 'layer_pay_token_id': tokenid,
206
+ // 'layer_order_amount': amount,
207
+ // 'tranid': orderId,
208
+ // };
209
+
210
+ // if (verify_hash(data, req.headers['x-webhook-signature'], config.accesskey, config.secretkey, config.url)) {
211
+ // console.log('TODO verify signature')
212
+ // }
213
+ get_payment_details(paymentid, config.accesskey, config.secretkey, config.url, function (response) {
214
+ if (response === "{}") {
215
+ req.body.STATUS = 'TXN_FAILURE';
216
+ req.body.ORDERID = orderId;
217
+ req.body.TXNID = paymentid;
218
+ }
219
+ else {
220
+ payment_data = JSON.parse(response);
221
+ if (!payment_data['payment_token'] || payment_data['payment_token']['id'] != tokenid) {
222
+ req.body.STATUS = 'TXN_FAILURE';
223
+ req.body.ORDERID = orderId;
224
+ req.body.TXNID = paymentid;
225
+ }
226
+ else {
227
+ let status = ""
228
+ if (payment_data.status == "captured" ||
229
+ payment_data.status == "late_authorized") {
230
+ status = 'TXN_SUCCESS'
231
+ }
232
+ else if (payment_data.status == "pending") {
233
+ status = 'TXN_PENDING'
234
+ }
235
+ else {
236
+ status = 'TXN_FAILURE'
237
+ }
238
+
239
+ if (status != 'TXN_SUCCESS') {
240
+ if (req.body.status == "paid" || req.body.status == 'captured') {
241
+ status = 'TXN_SUCCESS'
242
+ }
243
+ else if (req.body.status == 'failed') {
244
+ status = 'TXN_FAILURE'
245
+ }
246
+ else if (req.body.status == 'pending') {
247
+ status = 'TXN_PENDING'
248
+ }
249
+ }
250
+ console.log(`Open Money ${req.body.event} webhook for order=${payment_token.mtx} payid=${paymentid} status=${req.body.status} || ${status}`)
251
+
252
+ req.body.STATUS = status;
253
+ req.body.ORDERID = orderId;
254
+ req.body.TXNID = paymentid;
255
+ }
256
+
257
+ }
258
+ updateTransaction(req, res)
259
+
260
+ });
261
+ }, 3000)
262
+ }
263
+ else {
264
+ res.status(401)
265
+ res.send({ message: "Missing payment_token" })
266
+ }
267
+ }
268
+ else {
269
+ res.status(201)
270
+ res.send({ message: "Webhook not supported" })
271
+ }
272
+ }
273
+
274
+ getPaymentStatus(paymentTokenId, cb) {
275
+ return new Promise((resolve, reject) => {
276
+ get_payment_token_details(paymentTokenId, this.config.accesskey, this.config.secretkey, this.config.url, (data) => {
277
+ resolve(data)
278
+ if (cb) {
279
+ cb(data)
280
+ }
281
+ })
282
+ })
283
+ }
284
+
285
+ renderProcessingPage(params, pmttoken, res) {
286
+ res.writeHead(200, { 'Content-Type': 'text/html' });
287
+ res.write(`<html><head><title>Merchant Checkout Page</title>
288
+ <script src="${this.config.script_url}"></script>
289
+ </head><body><center><h1>Processing ! Please do not refresh this page...</h1><br>${pmttoken.html}<br></center><script>triggerLayer();</script></body></html>`);
290
+ res.end();
291
+ }
292
+
293
+ renderError(params, error, res) {
294
+
295
+ console.log('ERROR:::', error, '\n');
296
+ res.status(500)
297
+ var form_fields = "";
298
+ let errorResp = {
299
+ TXNID: "na",
300
+ STATUS: "TXN_FAILURE",
301
+ CANCELLED: "cancelled",
302
+ ORDERID: params["ORDER_ID"]
303
+ }
304
+ for (var x in errorResp) {
305
+ form_fields += "<input type='hidden' name='" + x + "' value='" + errorResp[x] + "' >";
306
+ }
307
+ form_fields += "<input type='hidden' name='CHECKSUMHASH' value='" + params["CHECKSUM"] + "' >";
308
+
309
+ res.writeHead(200, { 'Content-Type': 'text/html' });
310
+ res.write(`<html>
311
+
312
+ <head>
313
+ <title>Merchant Checkout Error</title>
314
+ </head>
315
+
316
+ <body>
317
+ <center>
318
+ <h1>Something went wrong. Please wait you will be redirected automatically...</h1>
319
+ </center>
320
+ <form method="post" action="${params['CALLBACK_URL']}" name="f1">${form_fields}</form>
321
+ <script type="text/javascript">document.f1.submit();</script>
322
+ </body>
323
+
324
+ </html>`);
325
+ res.end();
326
+
327
+ }
328
+
329
+
330
+ }
331
+
332
+
333
+
334
+ //Layer functions
335
+ function create_payment_token(data, accesskey, secretkey, baseurl, callback) {
336
+ try {
337
+ var pay_token_request_data = {
338
+ 'amount': (data['amount']) ? data['amount'] : null,
339
+ 'currency': (data['currency']) ? data['currency'] : null,
340
+ 'name': (data['name']) ? data['name'] : null,
341
+ 'email_id': (data['email_id']) ? data['email_id'] : null,
342
+ 'contact_number': (data['contact_number']) ? data['contact_number'] : null,
343
+ 'mtx': (data['mtx']) ? data['mtx'] : null,
344
+ 'udf': (data['udf']) ? data['udf'] : null,
345
+ };
346
+ http_post(pay_token_request_data, "payment_token", accesskey, secretkey, baseurl, function (response) {
347
+ return callback(response);
348
+ });
349
+
350
+ } catch (e) {
351
+ return callback({
352
+ 'error': e
353
+ });
354
+ }
355
+ }
356
+
357
+ function get_payment_token(payment_token_id, accesskey, secretkey, url, callback) {
358
+ if (!payment_token_id) {
359
+ throw new Error("payment_token_id cannot be empty");
360
+ }
361
+
362
+ try {
363
+ http_get("payment_token/" + payment_token_id, accesskey, secretkey, url, function (response) {
364
+ return callback(response);
365
+ });
366
+ } catch (e) {
367
+ return callback({
368
+ 'error': e
369
+ });
370
+ }
371
+ }
372
+
373
+ function get_payment_token_details(payment_tokenid, accesskey, secretkey, baseurl, callback) {
374
+
375
+ if (!payment_tokenid) {
376
+ throw new Error("payment_id cannot be empty");
377
+ }
378
+ try {
379
+ http_get("payment_token/" + payment_tokenid + '/payment', accesskey, secretkey, baseurl, function (response) {
380
+ return callback(response);
381
+ });
382
+ } catch (e) {
383
+ callback({
384
+ 'error': e
385
+ })
386
+ }
387
+ }
388
+
389
+ function get_payment_details(payment_id, accesskey, secretkey, baseurl, callback) {
390
+
391
+ if (!payment_id) {
392
+ throw new Error("payment_id cannot be empty");
393
+ }
394
+ try {
395
+ http_get("payment/" + payment_id, accesskey, secretkey, baseurl, function (response) {
396
+ return callback(response);
397
+ });
398
+ } catch (e) {
399
+ callback({
400
+ 'error': e
401
+ })
402
+ }
403
+ }
404
+
405
+ function http_post(data, route, accesskey, secretkey, baseurl, callback) {
406
+ Object.keys(data).forEach(function (key) {
407
+ if (data[key] === null)
408
+ delete data[key];
409
+ });
410
+
411
+ var url = baseurl + "/" + route;
412
+
413
+ var options = {
414
+ method: 'POST',
415
+ uri: url,
416
+ json: true,
417
+ form: {
418
+ amount: data['amount'],
419
+ currency: data['currency'],
420
+ name: data['name'],
421
+ email_id: data['email_id'],
422
+ contact_number: data['contact_number'],
423
+ mtx: data['mtx']
424
+ },
425
+ headers: {
426
+ 'Content-Type': 'application/json',
427
+ 'Authorization': 'Bearer ' + accesskey + ':' + secretkey
428
+ }
429
+ };
430
+
431
+ reqpost(options)
432
+ .on('response', function (resp) {
433
+ //console.log('STATUS:'+resp.statusCode);
434
+ resp.setEncoding('utf8');
435
+ resp.on('data', function (chunk) {
436
+ var data = JSON.parse(chunk);
437
+ var rdata = "";
438
+ if ("error" in data) {
439
+ Object.keys(data).forEach(function (key) {
440
+ if (key == "error_data") {
441
+ var obj = data[key];
442
+ Object.keys(obj).forEach(function (k) {
443
+ rdata += obj[k] + ' ';
444
+ });
445
+ }
446
+ });
447
+ return callback({ "error": rdata });
448
+ }
449
+ else
450
+ return callback(data);
451
+
452
+ });
453
+ })
454
+ .on('error', function (err) {
455
+ return callback(err);
456
+ });
457
+ }
458
+
459
+ function http_get(route, accesskey, secretkey, baseurl, callback) {
460
+
461
+ var url = baseurl + "/" + route;
462
+
463
+ var options = {
464
+ method: 'GET',
465
+ uri: url,
466
+ headers: {
467
+ 'Content-Type': 'application/json',
468
+ 'Authorization': 'Bearer ' + accesskey + ':' + secretkey
469
+ }
470
+ };
471
+
472
+ reqpost(options)
473
+ .on('response', function (resp) {
474
+ resp.setEncoding('utf8');
475
+ resp.on('data', function (chunk) {
476
+ return callback(chunk);
477
+ });
478
+ })
479
+ .on('error', function (err) {
480
+ return callback(err);
481
+ });
482
+ }
483
+
484
+ function create_hash(data, accesskey, secretkey) {
485
+ data = ksort(data);
486
+ hash_string = accesskey;
487
+ Object.keys(data).forEach(function (key) {
488
+ hash_string += '|' + data[key];
489
+ });
490
+ var cryp = crypto.createHash('sha256', secretkey);
491
+ cryp.update(hash_string);
492
+ return cryp.digest('hex');
493
+ }
494
+
495
+ function verify_hash(data, rec_hash, accesskey, secretkey) {
496
+ var gen_hash = create_hash(data, accesskey, secretkey);
497
+ if (gen_hash === rec_hash) {
498
+ return true;
499
+ }
500
+ return false;
501
+ }
502
+
503
+ function ksort(obj) {
504
+ var keys = Object.keys(obj).sort(), sortedObj = {};
505
+
506
+ for (var i in keys) {
507
+ sortedObj[keys[i]] = obj[keys[i]];
508
+ }
509
+
510
+ return sortedObj;
511
+ }
512
+
513
+
514
+ module.exports = OpenMoney;
@@ -5,7 +5,10 @@ var Transaction;
5
5
  var IDLEN = 10;
6
6
  var nodeBase64 = require('nodejs-base64-converter');
7
7
  var RazorPay = require('razorpay');
8
+ var OpenMoney = require('./adapters/open_money')
8
9
  const PaytmChecksum = require('./checksum/PaytmChecksum.js');
10
+ const { stat } = require('fs');
11
+ const { config } = require('process');
9
12
 
10
13
 
11
14
  function sanitizeRequest(body) {
@@ -20,8 +23,14 @@ module.exports = function (app, callbacks) {
20
23
  var config = (app.get('np_config'))
21
24
  var useController = require('./np_user.controller.js')(app, callbacks);
22
25
 
26
+ var razorPayInstance;
27
+ var openMoneyInstance = new OpenMoney(config);
28
+
23
29
  if (config.razor_url)
24
- var razorPayInstance = new RazorPay({ key_id: config.KEY, key_secret: config.SECRET })
30
+ razorPayInstance = new RazorPay({ key_id: config.KEY, key_secret: config.SECRET })
31
+ if (config.open_money_url) {
32
+ openMoneyInstance = new OpenMoney(config);
33
+ }
25
34
 
26
35
  let usingMultiDbOrm = false;
27
36
  if (config.db_url) {
@@ -108,7 +117,7 @@ module.exports = function (app, callbacks) {
108
117
  params['ORDER_ID'] = req.body.ORDER_ID;
109
118
  params['CUST_ID'] = req.body.CUST_ID;
110
119
  params['TXN_AMOUNT'] = req.body.TXN_AMOUNT;
111
- params['CALLBACK_URL'] = req.body.CALLBACK_URL;
120
+ params['CALLBACK_URL'] = req.body.CALLBACK_URL + "?order_id=" + req.body.ORDER_ID;
112
121
  params['EMAIL'] = req.body.EMAIL;
113
122
  params['MOBILE_NO'] = req.body.MOBILE_NO;
114
123
  params['PRODUCT_NAME'] = req.body.PRODUCT_NAME;
@@ -357,8 +366,6 @@ module.exports = function (app, callbacks) {
357
366
  }
358
367
  else if (config.razor_url) {
359
368
 
360
-
361
-
362
369
  let fail = `<div style="display:none">
363
370
 
364
371
  <form method="post" action="${params['CALLBACK_URL']}" id="fail">
@@ -401,6 +408,29 @@ module.exports = function (app, callbacks) {
401
408
  res.end();
402
409
 
403
410
  }
411
+ else if (config.open_money_url) {
412
+ try {
413
+ let pmttoken = await openMoneyInstance.generatePaymentToken(params);
414
+ openMoneyInstance.renderProcessingPage(params, pmttoken, res);
415
+
416
+ var myquery = { orderId: params['ORDER_ID'] };
417
+ Transaction.findOne(myquery, function (err, objForUpdate) {
418
+
419
+ objForUpdate.extra = JSON.stringify({
420
+ layer_pay_token_id: pmttoken.tokenid
421
+ });
422
+
423
+ var newvalues = { $set: objForUpdate };
424
+ Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
425
+ let status = 'Updated TXNID'
426
+ });
427
+
428
+ }, usingMultiDbOrm ? Transaction : undefined)
429
+
430
+ } catch (e) {
431
+ openMoneyInstance.renderError(params, e, res)
432
+ }
433
+ }
404
434
  if (callbacks !== undefined)
405
435
  callbacks.onStart(params['ORDER_ID'], params);
406
436
  }
@@ -459,6 +489,8 @@ module.exports = function (app, callbacks) {
459
489
  checksum_lib.genchecksum(params, config.KEY, showConfirmation);
460
490
  else if (config.razor_url) {
461
491
  showConfirmation()
492
+ } else if (config.open_money_url) {
493
+ showConfirmation()
462
494
  }
463
495
 
464
496
  };
@@ -469,9 +501,9 @@ module.exports = function (app, callbacks) {
469
501
 
470
502
 
471
503
  var myquery = { orderId: req.body.ORDER_ID };
472
- Transaction.findOne(myquery, function (err, objForUpdate) {
504
+ Transaction.findOne(myquery, function (err, orderData) {
473
505
 
474
- onTxn(objForUpdate);
506
+ onTxn(orderData);
475
507
 
476
508
  }, usingMultiDbOrm ? Transaction : undefined);
477
509
 
@@ -524,13 +556,17 @@ module.exports = function (app, callbacks) {
524
556
 
525
557
  razorPayInstance.orders.create(options, function (err, order) {
526
558
  if (err) {
527
- res.send({ message: "An error occurred ! " + err.message })
559
+ res.send({ message: "An error occurred ! " + err.description })
528
560
  return;
529
561
  }
530
562
  orderId = order.id
531
563
  onOrder(orderId)
532
564
  })
533
565
  }
566
+ else if (config.open_money_url) {
567
+ orderId = "pay_" + makeid(config.id_length || IDLEN)
568
+ onOrder(orderId)
569
+ }
534
570
 
535
571
 
536
572
 
@@ -574,8 +610,50 @@ module.exports = function (app, callbacks) {
574
610
 
575
611
  }
576
612
 
613
+ function updateTransaction(req, res) {
614
+ var myquery = { orderId: req.body.ORDERID };
577
615
 
578
- module.callback = (req, res) => {
616
+ Transaction.findOne(myquery, function (err, objForUpdate) {
617
+
618
+ if (err) {
619
+ res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
620
+ return;
621
+ }
622
+ if (objForUpdate.status != ("INITIATED") && objForUpdate.status != ("TXN_PENDING")) {
623
+ objForUpdate.readonly = "readonly"
624
+ objForUpdate.action = config.homepage
625
+ res.render(vp + "result.hbs", objForUpdate);
626
+ console.log("Transaction already processed ", req.body.ORDERID)
627
+ // res.send({ message: "Transaction already processed", status: objForUpdate.status, ORDERID: objForUpdate.orderId, TXNID: objForUpdate.TXNID, TXNID: req.body.TXNID })
628
+ return;
629
+ }
630
+ if (req.body.status == "paid" && !req.body.STATUS) {
631
+ req.body.STATUS = "TXN_SUCCESS"
632
+ }
633
+ objForUpdate.status = req.body.STATUS;
634
+ objForUpdate.TXNID = req.body.TXNID;
635
+ objForUpdate.extra = JSON.stringify(req.body);
636
+
637
+ var newvalues = { $set: objForUpdate };
638
+ Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
639
+
640
+ if (err) {
641
+ res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
642
+ }
643
+ else {
644
+
645
+ if (callbacks !== undefined)
646
+ callbacks.onFinish(req.body.ORDERID, req.body);
647
+ objForUpdate.readonly = "readonly"
648
+ objForUpdate.action = config.homepage
649
+ res.render(vp + "result.hbs", objForUpdate);
650
+ }
651
+ });
652
+
653
+ }, usingMultiDbOrm ? Transaction : undefined)
654
+ }
655
+
656
+ module.callback = async (req, res) => {
579
657
 
580
658
  var result = false;
581
659
  let isCancelled = false;
@@ -606,10 +684,19 @@ module.exports = function (app, callbacks) {
606
684
  req.body.razorpay_order_id = orderId
607
685
  }
608
686
  req.body.STATUS = 'TXN_FAILURE'
609
- req.body.ORDERID = req.body.razorpay_order_id
687
+ req.body.ORDERID = req.body.razorpay_order_id || req.query.order_id
610
688
  isCancelled = true;
611
689
  }
612
690
  }
691
+ else if (config.open_money_url) {
692
+ let openRest = await openMoneyInstance.verifyResult(req);
693
+ result = true;
694
+ req.body.STATUS = openRest.STATUS
695
+ req.body.TXNID = openRest.TXNID
696
+ req.body.ORDERID = openRest.ORDERID || req.query.order_id
697
+ req.body.extras = openRest.data
698
+ }
699
+
613
700
 
614
701
  //console.log("Checksum Result => ", result, "\n");
615
702
  console.log("NodePayTMPG::Transaction => ", req.body.ORDERID, req.body.STATUS);
@@ -617,37 +704,7 @@ module.exports = function (app, callbacks) {
617
704
 
618
705
  if (result || isCancelled) {
619
706
 
620
- var myquery = { orderId: req.body.ORDERID };
621
- Transaction.findOne(myquery, function (err, objForUpdate) {
622
-
623
- if (err) {
624
- res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
625
- return;
626
- }
627
- if (req.body.status == "paid" && !req.body.STATUS) {
628
- req.body.STATUS = "TXN_SUCCESS"
629
- }
630
- objForUpdate.status = req.body.STATUS;
631
- objForUpdate.TXNID = req.body.TXNID;
632
- objForUpdate.extra = JSON.stringify(req.body);
633
-
634
- var newvalues = { $set: objForUpdate };
635
- Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
636
-
637
- if (err) {
638
- res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
639
- }
640
- else {
641
-
642
- if (callbacks !== undefined)
643
- callbacks.onFinish(req.body.ORDERID, req.body);
644
- objForUpdate.readonly = "readonly"
645
- objForUpdate.action = config.homepage
646
- res.render(vp + "result.hbs", objForUpdate);
647
- }
648
- });
649
-
650
- }, usingMultiDbOrm ? Transaction : undefined)
707
+ updateTransaction(req, res);
651
708
 
652
709
  }
653
710
  else {
@@ -658,6 +715,64 @@ module.exports = function (app, callbacks) {
658
715
 
659
716
  }
660
717
 
718
+ module.webhook = (req, res) => {
719
+ if (config.paytm_url) {
720
+ module.callback(req, res)
721
+ }
722
+ else if (config.razor_url) {
723
+ let events = ["payment.captured", "payment.pending", "payment.failed"]
724
+ if (req.body.event && events.indexOf(req.body.event) > -1) {
725
+ if (req.body.payload &&
726
+ req.body.payload.payment &&
727
+ req.body.payload.payment.entity) {
728
+
729
+ let entity = req.body.payload.payment.entity;
730
+ let razorpay_order_id = entity.order_id;
731
+ let razorpay_payment_id = entity.id;
732
+ let status = entity.status;
733
+ let event = req.body.event;
734
+ console.log(`Razorpay webhook payment order=${razorpay_order_id} payid=${razorpay_payment_id} status=${status}`)
735
+
736
+ let reqBody = req.rawBody, signature = req.headers["x-razorpay-signature"];
737
+
738
+ result = RazorPay.validateWebhookSignature(reqBody, req.headers['x-razorpay-signature'], config.SECRET)
739
+ req.signatureVerified = result;
740
+ if (result) {
741
+ if (event == events[0]) {
742
+ req.body.STATUS = "TXN_SUCCESS";
743
+ }
744
+ else if (event == events[1]) { //pending
745
+ req.body.STATUS = "TXN_PENDING";
746
+ }
747
+ else { // failed
748
+ req.body.STATUS = "TXN_FAILURE";
749
+ }
750
+ req.body.ORDERID = razorpay_order_id;
751
+ req.body.TXNID = razorpay_payment_id;
752
+ setTimeout(() => {
753
+ updateTransaction(req, res)
754
+ }, 3000)
755
+ }
756
+ else {
757
+ res.status(401)
758
+ res.send({ message: "Invalid Rzpay signature" })
759
+ }
760
+ }
761
+ else {
762
+ res.status(400)
763
+ res.send({ message: "Invalid Payload" })
764
+ }
765
+ }
766
+ else {
767
+ res.status(400)
768
+ res.send({ message: "Unsupported event : " + req.body.event })
769
+ }
770
+ }
771
+ else if (config.open_money_url) {
772
+ openMoneyInstance.processWebhook(req, res, updateTransaction)
773
+ }
774
+ }
775
+
661
776
  module.createTxn = (req, res) => {
662
777
 
663
778
 
@@ -679,6 +794,9 @@ module.exports = function (app, callbacks) {
679
794
  let order = await razorPayInstance.orders.create(options);
680
795
  id = order.id;
681
796
  }
797
+ else if(config.open_money_url){
798
+ id = "pay_" + makeid(config.id_length || IDLEN)
799
+ }
682
800
 
683
801
  var txnTask = new Transaction({
684
802
  id: id,
@@ -724,15 +842,18 @@ module.exports = function (app, callbacks) {
724
842
 
725
843
  module.status = (req, res) => {
726
844
 
845
+ if (!req.body.ORDER_ID && req.query.ORDER_ID) {
846
+ req.body.ORDER_ID = req.query.ORDER_ID
847
+ }
727
848
  var myquery = { orderId: req.body.ORDER_ID };
728
- Transaction.findOne(myquery, async function (err, objForUpdate) {
849
+ Transaction.findOne(myquery, async function (err, orderData) {
729
850
 
730
851
 
731
852
  if (err) {
732
853
  res.send(err)
733
854
  return
734
855
  }
735
- if (objForUpdate.status === "INITIATED") {
856
+ if (orderData.status === "INITIATED") {
736
857
 
737
858
  var params = {}
738
859
  params["MID"] = config.MID;
@@ -740,10 +861,10 @@ module.exports = function (app, callbacks) {
740
861
 
741
862
  async function onStatusUpdate(paytmResponse) {
742
863
  if (paytmResponse.TXNID.length > 4) {
743
- objForUpdate.status = paytmResponse.STATUS;
744
- objForUpdate.extra = JSON.stringify(paytmResponse);
864
+ orderData.status = paytmResponse.STATUS;
865
+ orderData.extra = JSON.stringify(paytmResponse);
745
866
 
746
- var newvalues = { $set: objForUpdate };
867
+ var newvalues = { $set: orderData };
747
868
  Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
748
869
 
749
870
  if (err) {
@@ -751,13 +872,13 @@ module.exports = function (app, callbacks) {
751
872
  }
752
873
  else {
753
874
  if (callbacks !== undefined)
754
- callbacks.onFinish(req.body.ORDER_ID, objForUpdate);
875
+ callbacks.onFinish(req.body.ORDER_ID, orderData);
755
876
  res.send(paytmResponse)
756
877
  }
757
878
  });
758
879
  }
759
880
  else {
760
- res.send(objForUpdate)
881
+ res.send(orderData)
761
882
 
762
883
  }
763
884
  }
@@ -800,13 +921,41 @@ module.exports = function (app, callbacks) {
800
921
  onStatusUpdate(result)
801
922
  }
802
923
  else {
803
- res.send(objForUpdate);
924
+ res.send(orderData);
925
+ }
926
+ }
927
+ else if (config.open_money_url) {
928
+ let extras = JSON.parse(orderData.extra)
929
+ if (!extras || !extras.layer_pay_token_id) {
930
+ res.status(500)
931
+ return res.send({ message: 'An unexpected error occured. No payment token exists' })
932
+ }
933
+ let result = await openMoneyInstance.getPaymentStatus(extras.layer_pay_token_id)
934
+ result = JSON.parse(result)
935
+ result.ORDERID = req.body.ORDER_ID
936
+ if (result.status == 'paid' || result.status == 'captured') {
937
+ result.STATUS = 'TXN_SUCCESS'
938
+ result.TXNID = result.id
939
+ onStatusUpdate(result)
940
+ }
941
+ else if (result.status == 'pending' || result.status == 'attempted') {
942
+ result.STATUS = 'TXN_PENDING'
943
+ result.TXNID = result.id
944
+ onStatusUpdate(result)
945
+ }
946
+ else if (result.status == 'failed' || result.status == 'cancelled') {
947
+ result.STATUS = 'TXN_FAILED'
948
+ result.TXNID = result.id
949
+ onStatusUpdate(result)
950
+ }
951
+ else {
952
+ res.send(orderData);
804
953
  }
805
954
  }
806
955
 
807
956
  }
808
957
  else {
809
- res.send(objForUpdate);
958
+ res.send(orderData);
810
959
  }
811
960
 
812
961
 
@@ -49,16 +49,21 @@ module.exports = (app, express, callbacks) => {
49
49
 
50
50
  app.set('view engine', 'handlebars');
51
51
 
52
+ let saveRawBody = function (req, res, buf, encoding) {
53
+ req.rawBody = buf.toString();
54
+ }
52
55
  app.use(bodyParser.urlencoded({ extended: true }))
53
- app.use(bodyParser.json())
56
+ app.use(bodyParser.json({ verify: saveRawBody }))
57
+
54
58
  app.use("/" + config.path_prefix, express.static(path.join(__dirname, '../../public')));
55
59
  app.use('/' + config.path_prefix, router);
56
60
 
57
61
  router.all('/', pc.init);
58
62
  router.all('/init', pc.init);
59
63
 
60
- router.all('/home', pc.home)
64
+ // router.all('/home', pc.home)
61
65
  router.all('/callback', pc.callback)
66
+ router.all('/api/webhook', pc.webhook)
62
67
  router.all('/api/status', pc.status)
63
68
  router.all('/api/createTxn', pc.createTxn)
64
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-paytmpg",
3
- "version": "4.0.1",
3
+ "version": "5.1.1",
4
4
  "description": "Payment Gateway Integration using NodeJS",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,38 @@
1
+ function triggerLayer() {
2
+
3
+ Layer.checkout(
4
+ {
5
+ token: layer_params.payment_token_id,
6
+ accesskey: layer_params.accesskey,
7
+ },
8
+ function (response) {
9
+ console.log(response)
10
+ if(response !== null || response.length > 0 ){
11
+
12
+ if(response.payment_id !== undefined){
13
+
14
+ document.getElementById('layer_payment_id').value = response.payment_id;
15
+
16
+ }
17
+
18
+ }
19
+
20
+ document.layer_payment_int_form.submit();
21
+ },
22
+ function (err) {
23
+ alert(err.message);
24
+ }
25
+ );
26
+ }
27
+ /*
28
+ var checkExist = setInterval(function() {
29
+ if (typeof Layer !== 'undefined') {
30
+ console.log('Layer Loaded...');
31
+ clearInterval(checkExist);
32
+ triggerLayer();
33
+ }
34
+ else {
35
+ console.log('Layer undefined...');
36
+ }
37
+ }, 1000);
38
+ */