node-paytmpg 3.0.4 → 4.1.0

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,71 @@
1
+ # For most projects, this workflow file will not need changing; you simply need
2
+ # to commit it to your repository.
3
+ #
4
+ # You may wish to alter this file to override the set of languages analyzed,
5
+ # or to provide custom queries or build logic.
6
+ #
7
+ # ******** NOTE ********
8
+ # We have attempted to detect the languages in your repository. Please check
9
+ # the `language` matrix defined below to confirm you have the correct set of
10
+ # supported CodeQL languages.
11
+ #
12
+ name: "CodeQL"
13
+
14
+ on:
15
+ push:
16
+ branches: [ master ]
17
+ pull_request:
18
+ # The branches below must be a subset of the branches above
19
+ branches: [ master ]
20
+ schedule:
21
+ - cron: '16 23 * * 4'
22
+
23
+ jobs:
24
+ analyze:
25
+ name: Analyze
26
+ runs-on: ubuntu-latest
27
+ permissions:
28
+ actions: read
29
+ contents: read
30
+ security-events: write
31
+
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ language: [ 'javascript' ]
36
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
37
+ # Learn more:
38
+ # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
39
+
40
+ steps:
41
+ - name: Checkout repository
42
+ uses: actions/checkout@v2
43
+
44
+ # Initializes the CodeQL tools for scanning.
45
+ - name: Initialize CodeQL
46
+ uses: github/codeql-action/init@v1
47
+ with:
48
+ languages: ${{ matrix.language }}
49
+ # If you wish to specify custom queries, you can do so here or in a config file.
50
+ # By default, queries listed here will override any specified in a config file.
51
+ # Prefix the list here with "+" to use these queries and those in the config file.
52
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
53
+
54
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55
+ # If this step fails, then you should remove it and run the build manually (see below)
56
+ - name: Autobuild
57
+ uses: github/codeql-action/autobuild@v1
58
+
59
+ # ℹ️ Command-line programs to run using the OS shell.
60
+ # 📚 https://git.io/JvXDl
61
+
62
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63
+ # and modify them (or add more) to build your code if your project
64
+ # uses a compiled language
65
+
66
+ #- run: |
67
+ # make bootstrap
68
+ # make release
69
+
70
+ - name: Perform CodeQL Analysis
71
+ uses: github/codeql-action/analyze@v1
@@ -0,0 +1,23 @@
1
+ # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2
+ # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3
+
4
+ name: Node.js Package
5
+
6
+ on:
7
+ release:
8
+ types: [created]
9
+ push:
10
+ branches: [ master ]
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v1
16
+ - uses: actions/setup-node@v1
17
+ with:
18
+ node-version: 10
19
+ - run: npm install
20
+ - run: npm test
21
+ - uses: JS-DevTools/npm-publish@v1
22
+ with:
23
+ token: ${{ secrets.NPM_TOKEN }}
package/README.MD CHANGED
@@ -1,9 +1,13 @@
1
1
  ## Node JS Payments Easy Integration
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)
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
+
2
6
  Support for :
3
7
  - Paytm
4
8
  - RazorPay
5
9
 
6
- Does all the hardwork for you while integrating paytm with nodejs server.
10
+ 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 .
7
11
 
8
12
  ## Example
9
13
 
@@ -198,6 +202,25 @@ var User=PayTMPG.User;
198
202
 
199
203
  ```
200
204
 
205
+ ### Webhooks
206
+
207
+ Webhooks can issued at at `/_pay/api/webhook` and are useful for payments captured late.
208
+
209
+ If you are using `bodyparser` , make sure to remove these from your express app since the body parsers will be already added with the library .
210
+ ```
211
+ Comment these if present in your app
212
+ app.use(bodyParser.json());
213
+ app.use(bodyParser.urlencoded({ extended: true }));
214
+ ```
215
+
216
+ #### For razorpay webhook
217
+ Make sure to use the same secret in your webhook as the merchant secret.
218
+ https://razorpay.com/docs/webhooks/
219
+
220
+ #### For paytm
221
+ Nothing extra needed
222
+ https://developer.paytm.com/docs/callback-and-webhook/?ref=callbackWebhook
223
+
201
224
 
202
225
 
203
226
  License : GPL
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+
3
+ var crypto = require('crypto');
4
+
5
+ class PaytmChecksum {
6
+
7
+ static encrypt(input, key) {
8
+ var cipher = crypto.createCipheriv('AES-128-CBC', key, PaytmChecksum.iv);
9
+ var encrypted = cipher.update(input, 'binary', 'base64');
10
+ encrypted += cipher.final('base64');
11
+ return encrypted;
12
+ }
13
+ static decrypt(encrypted, key) {
14
+ var decipher = crypto.createDecipheriv('AES-128-CBC', key, PaytmChecksum.iv);
15
+ var decrypted = decipher.update(encrypted, 'base64', 'binary');
16
+ try {
17
+ decrypted += decipher.final('binary');
18
+ }
19
+ catch (e) {
20
+ console.log(e);
21
+ }
22
+ return decrypted;
23
+ }
24
+ static generateSignature(params, key) {
25
+ if (typeof params !== "object" && typeof params !== "string") {
26
+ var error = "string or object expected, " + (typeof params) + " given.";
27
+ return Promise.reject(error);
28
+ }
29
+ if (typeof params !== "string"){
30
+ params = PaytmChecksum.getStringByParams(params);
31
+ }
32
+ return PaytmChecksum.generateSignatureByString(params, key);
33
+ }
34
+
35
+
36
+ static verifySignature(params, key, checksum) {
37
+ if (typeof params !== "object" && typeof params !== "string") {
38
+ var error = "string or object expected, " + (typeof params) + " given.";
39
+ return Promise.reject(error);
40
+ }
41
+ if(params.hasOwnProperty("CHECKSUMHASH")){
42
+ delete params.CHECKSUMHASH
43
+ }
44
+ if (typeof params !== "string"){
45
+ params = PaytmChecksum.getStringByParams(params);
46
+ }
47
+ return PaytmChecksum.verifySignatureByString(params, key, checksum);
48
+ }
49
+
50
+ static async generateSignatureByString(params, key) {
51
+ var salt = await PaytmChecksum.generateRandomString(4);
52
+ return PaytmChecksum.calculateChecksum(params, key, salt);
53
+ }
54
+
55
+ static verifySignatureByString(params, key, checksum) {
56
+ var paytm_hash = PaytmChecksum.decrypt(checksum, key);
57
+ var salt = paytm_hash.substr(paytm_hash.length - 4);
58
+ return (paytm_hash === PaytmChecksum.calculateHash(params, salt));
59
+ }
60
+
61
+ static generateRandomString(length) {
62
+ return new Promise(function (resolve, reject) {
63
+ crypto.randomBytes((length * 3.0) / 4.0, function (err, buf) {
64
+ if (!err) {
65
+ var salt = buf.toString("base64");
66
+ resolve(salt);
67
+ }
68
+ else {
69
+ console.log("error occurred in generateRandomString: " + err);
70
+ reject(err);
71
+ }
72
+ });
73
+ });
74
+ }
75
+
76
+ static getStringByParams(params) {
77
+ var data = {};
78
+ Object.keys(params).sort().forEach(function(key,value) {
79
+ data[key] = (params[key] !== null && params[key].toLowerCase() !== "null") ? params[key] : "";
80
+ });
81
+ return Object.values(data).join('|');
82
+ }
83
+
84
+ static calculateHash(params, salt) {
85
+ var finalString = params + "|" + salt;
86
+ return crypto.createHash('sha256').update(finalString).digest('hex') + salt;
87
+ }
88
+ static calculateChecksum(params, key, salt) {
89
+ var hashString = PaytmChecksum.calculateHash(params, salt);
90
+ return PaytmChecksum.encrypt(hashString,key);
91
+ }
92
+ }
93
+ PaytmChecksum.iv = '@@@@&&&&####$$$$';
94
+ module.exports = PaytmChecksum;
@@ -15,14 +15,17 @@ module.exports = function (app, callbacks) {
15
15
  var module = {};
16
16
  var config = (app.get('np_config'))
17
17
 
18
+ let usingMultiDbOrm = false;
18
19
  if (config.db_url) {
19
20
  User = require('../models/np_user.model.js');
21
+ usingMultiDbOrm = false;
20
22
  } else if (app.multidborm) {
21
23
  User = require('../models/np_multidbplugin.js')('npusers',app.multidborm);
22
24
  User.db=app.multidborm;
23
25
  User.modelname='npusers'
24
26
  User.idFieldName='id'
25
27
  app.NPUser = User;
28
+ usingMultiDbOrm = true;
26
29
  }
27
30
  module.create = (userData, cb) => {
28
31
 
@@ -55,7 +58,7 @@ module.exports = function (app, callbacks) {
55
58
 
56
59
  // console.log("User New : ",userData.name);
57
60
 
58
- userData.id = makeid(IDLEN);
61
+ userData.id = "user_"+makeid(IDLEN);
59
62
  var userTask = new User(userData);
60
63
  userTask.save()
61
64
  .then(user => {
@@ -69,7 +72,7 @@ module.exports = function (app, callbacks) {
69
72
 
70
73
  }
71
74
 
72
- },User);
75
+ },usingMultiDbOrm ? User : undefined);
73
76
 
74
77
  };
75
78
  return module;
@@ -5,6 +5,9 @@ var Transaction;
5
5
  var IDLEN = 10;
6
6
  var nodeBase64 = require('nodejs-base64-converter');
7
7
  var RazorPay = require('razorpay');
8
+ const PaytmChecksum = require('./checksum/PaytmChecksum.js');
9
+ const { stat } = require('fs');
10
+
8
11
 
9
12
  function sanitizeRequest(body) {
10
13
 
@@ -21,14 +24,19 @@ module.exports = function (app, callbacks) {
21
24
  if (config.razor_url)
22
25
  var razorPayInstance = new RazorPay({ key_id: config.KEY, key_secret: config.SECRET })
23
26
 
27
+ let usingMultiDbOrm = false;
24
28
  if (config.db_url) {
25
29
  Transaction = require('../models/np_transaction.model.js');
30
+ usingMultiDbOrm = false;
31
+
26
32
  } else if (app.multidborm) {
27
33
  Transaction = require('../models/np_multidbplugin.js')('nptransactions', app.multidborm);
28
34
  Transaction.db = app.multidborm;
29
35
  Transaction.modelname = 'nptransactions'
30
36
  Transaction.idFieldName = 'orderId'
31
37
  app.NPTransaction = Transaction;
38
+ usingMultiDbOrm = true;
39
+
32
40
  }
33
41
 
34
42
  var module = {};
@@ -101,7 +109,7 @@ module.exports = function (app, callbacks) {
101
109
  params['ORDER_ID'] = req.body.ORDER_ID;
102
110
  params['CUST_ID'] = req.body.CUST_ID;
103
111
  params['TXN_AMOUNT'] = req.body.TXN_AMOUNT;
104
- params['CALLBACK_URL'] = req.body.CALLBACK_URL;
112
+ params['CALLBACK_URL'] = req.body.CALLBACK_URL + "?order_id=" + req.body.ORDER_ID;
105
113
  params['EMAIL'] = req.body.EMAIL;
106
114
  params['MOBILE_NO'] = req.body.MOBILE_NO;
107
115
  params['PRODUCT_NAME'] = req.body.PRODUCT_NAME;
@@ -109,24 +117,244 @@ module.exports = function (app, callbacks) {
109
117
 
110
118
  if (config.paytm_url) {
111
119
 
120
+ let initTxnbody = {
121
+ "requestType": "Payment",
122
+ "mid": params['MID'],
123
+ "websiteName": params['WEBSITE'],
124
+ "orderId": params['ORDER_ID'],
125
+ "callbackUrl": params['CALLBACK_URL'],
126
+ "txnAmount": {
127
+ "value": params['TXN_AMOUNT'],
128
+ "currency": params['CURRENCY'] || "INR",
129
+ },
130
+ "userInfo": {
131
+ "custId": params['CUST_ID'],
132
+ "mobile": params['MOBILE_NO'],
133
+ "firstName": params['NAME'],
134
+ "email": params['EMAIL']
135
+ }
136
+ };
137
+ let checksum = await PaytmChecksum.generateSignature(JSON.stringify(initTxnbody), config.KEY)
138
+ let initTxnUrl = config.paytm_url + `/theia/api/v1/initiateTransaction?mid=${params['MID']}&orderId=${params['ORDER_ID']}`;
139
+
140
+ request.post(
141
+ initTxnUrl,
142
+ {
143
+ json: {
144
+ "body": initTxnbody,
145
+ "head": {
146
+ "signature": checksum,
147
+ "channelId": params['CHANNEL_ID']
148
+ }
149
+ }
150
+ },
151
+ function (error, response, body) {
112
152
 
113
- checksum_lib.genchecksum(params, config.KEY, function (err, checksum) {
153
+ if (!error && response.statusCode != undefined
154
+ && response.statusCode != NaN &&
155
+ response.statusCode == 200 &&
156
+ body.body &&
157
+ body.body.resultInfo &&
158
+ body.body.resultInfo.resultStatus == "S") {
114
159
 
115
160
 
116
- var txn_url = config.paytm_url + "/theia/processTransaction"; // for staging
161
+ let paytmJsCheckouHtml = `<html>
162
+ <head>
163
+ <title>Merchant Checkout</title>
164
+ <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0"/>
165
+
166
+ </head>
167
+ <body>
168
+ <center>
169
+ <h1>Please donot close this page or press the back button. Processing...</h1>
170
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin:auto;background:#fff;display:block;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
171
+ <g transform="rotate(0 50 50)">
172
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
173
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate>
174
+ </rect>
175
+ </g><g transform="rotate(30 50 50)">
176
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
177
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate>
178
+ </rect>
179
+ </g><g transform="rotate(60 50 50)">
180
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
181
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate>
182
+ </rect>
183
+ </g><g transform="rotate(90 50 50)">
184
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
185
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate>
186
+ </rect>
187
+ </g><g transform="rotate(120 50 50)">
188
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
189
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate>
190
+ </rect>
191
+ </g><g transform="rotate(150 50 50)">
192
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
193
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate>
194
+ </rect>
195
+ </g><g transform="rotate(180 50 50)">
196
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
197
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate>
198
+ </rect>
199
+ </g><g transform="rotate(210 50 50)">
200
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
201
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate>
202
+ </rect>
203
+ </g><g transform="rotate(240 50 50)">
204
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
205
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate>
206
+ </rect>
207
+ </g><g transform="rotate(270 50 50)">
208
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
209
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate>
210
+ </rect>
211
+ </g><g transform="rotate(300 50 50)">
212
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
213
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate>
214
+ </rect>
215
+ </g><g transform="rotate(330 50 50)">
216
+ <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#fe718d">
217
+ <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate>
218
+ </rect>
219
+ </g>
220
+ </svg>
221
+ </center>
222
+ <form id="cancelform" action="${params['CALLBACK_URL']}" method="post">
223
+ <input type="hidden" name="TXNID" value="na"/>
224
+ <input type="hidden" name="STATUS" value="TXN_FAILURE"/>
225
+ <input type="hidden" name="CANCELLED" value="cancelled"/>
226
+ <input id="RESPMSG" type="hidden" name="RESPMSG" value=""/>
227
+ <input type="hidden" name="ORDERID" value="${params["ORDER_ID"]}"/>
228
+ </form>
229
+
117
230
 
118
- // console.log(checksum)
119
- var form_fields = "";
120
- for (var x in params) {
121
- form_fields += "<input type='hidden' name='" + x + "' value='" + params[x] + "' >";
122
- }
123
- form_fields += "<input type='hidden' name='CHECKSUMHASH' value='" + checksum + "' >";
231
+ <script>
124
232
 
125
- res.writeHead(200, { 'Content-Type': 'text/html' });
126
- res.write('<html><head><title>Merchant Checkout Page</title></head><body><center><h1>Processing ! Please do not refresh this page...</h1></center><form method="post" action="' + txn_url + '" name="f1">' + form_fields + '</form><script type="text/javascript">document.f1.submit();</script></body></html>');
127
- res.end();
233
+ function getBodyColor(color){
234
+ const hex = color.replace('#', '');
235
+ const c_r = parseInt(hex.substr(0, 2), 16);
236
+ const c_g = parseInt(hex.substr(2, 2), 16);
237
+ const c_b = parseInt(hex.substr(4, 2), 16);
238
+ const brightness = ((c_r * 299) + (c_g * 587) + (c_b * 114)) / 1000;
239
+ // console.log(brightness , brightness > 155 ? "#fff" : "#1a1a1c")
240
+ return brightness > 155 ? "#1a1a1c" : "#ffffff";
241
+ }
242
+
243
+ function shadeColor(color, percent) {
244
+
245
+ var R = parseInt(color.substring(1,3),16);
246
+ var G = parseInt(color.substring(3,5),16);
247
+ var B = parseInt(color.substring(5,7),16);
248
+
249
+ R = parseInt(R * (100 + percent) / 100);
250
+ G = parseInt(G * (100 + percent) / 100);
251
+ B = parseInt(B * (100 + percent) / 100);
252
+
253
+ R = (R<255)?R:255;
254
+ G = (G<255)?G:255;
255
+ B = (B<255)?B:255;
256
+
257
+ var RR = ((R.toString(16).length==1)?"0"+R.toString(16):R.toString(16));
258
+ var GG = ((G.toString(16).length==1)?"0"+G.toString(16):G.toString(16));
259
+ var BB = ((B.toString(16).length==1)?"0"+B.toString(16):B.toString(16));
260
+
261
+ return "#"+RR+GG+BB;
262
+ }
263
+
264
+ function failTxn(reason) {
265
+ var form = document.getElementById("cancelform");
266
+ var element2 = document.getElementById("RESPMSG");
267
+ element2.value=reason;
268
+ form.submit();
269
+ }
270
+ function onScriptLoad(){
271
+ var config = {
272
+ "root": "",
273
+ "flow": "DEFAULT",
274
+ "style": {
275
+ // "bodyColor": shadeColor("${config.theme_color}",+40),
276
+ "themeBackgroundColor": "${config.theme_color}",
277
+ "themeColor": getBodyColor("${config.theme_color}"),
278
+ "headerBackgroundColor": "${config.theme_color}",
279
+ "headerColor": getBodyColor("${config.theme_color}")
280
+ },
281
+ "data": {
282
+ "orderId": "${params['ORDER_ID']}", /* update order id */
283
+ "token": "${body.body.txnToken}", /* update token value */
284
+ "tokenType": "TXN_TOKEN",
285
+ "amount": "${params['TXN_AMOUNT']}" /* update amount */
286
+ },
287
+ "handler": {
288
+ "notifyMerchant": function(eventName,data){
289
+ // console.log("notifyMerchant handler function called");
290
+ // console.log("eventName => ",eventName);
291
+ // console.log("data => ",data);
292
+ if(eventName == "APP_CLOSED"){
293
+ failTxn(eventName)
294
+ }
295
+ }
296
+ }
297
+ };
298
+
299
+ if(window.Paytm && window.Paytm.CheckoutJS){
300
+ window.Paytm.CheckoutJS.onLoad(function excecuteAfterCompleteLoad() {
301
+ // initialze configuration using init method
302
+ window.Paytm.CheckoutJS.init(config).then(function onSuccess() {
303
+ // after successfully updating configuration, invoke JS Checkout
304
+ window.Paytm.CheckoutJS.invoke();
305
+ }).catch(function onError(error){
306
+ // console.log("error => ",error);
307
+ failTxn(error.message)
308
+ });
309
+ });
310
+ }
311
+ }
312
+ </script>
313
+ <script type="application/javascript" crossorigin="anonymous" src="${config.paytm_url}/merchantpgpui/checkoutjs/merchants/${params['MID']}.js" onload="onScriptLoad();" crossorigin="anonymous"></script>
314
+
315
+
316
+ </body>
317
+ </html>`
318
+ return res.send(paytmJsCheckouHtml)
319
+
320
+ }
321
+ else {
322
+ console.log('ERROR:::', error, '\n', body);
323
+ res.status(500)
324
+ var form_fields = "";
325
+ let errorResp = {
326
+ TXNID: "na",
327
+ STATUS: "TXN_FAILURE",
328
+ CANCELLED: "cancelled",
329
+ ORDERID: params["ORDER_ID"]
330
+ }
331
+ for (var x in errorResp) {
332
+ form_fields += "<input type='hidden' name='" + x + "' value='" + errorResp[x] + "' >";
333
+ }
334
+ form_fields += "<input type='hidden' name='CHECKSUMHASH' value='" + checksum + "' >";
335
+
336
+ res.writeHead(200, { 'Content-Type': 'text/html' });
337
+ res.write(`<html>
338
+
339
+ <head>
340
+ <title>Merchant Checkout Error</title>
341
+ </head>
342
+
343
+ <body>
344
+ <center>
345
+ <h1>Something went wrong. Please wait you will be redirected automatically...</h1>
346
+ </center>
347
+ <form method="post" action="${params['CALLBACK_URL']}" name="f1">${form_fields}</form>
348
+ <script type="text/javascript">document.f1.submit();</script>
349
+ </body>
350
+
351
+ </html>`);
352
+ res.end();
353
+
354
+ }
355
+ }
356
+ );
128
357
 
129
- });
130
358
  }
131
359
  else if (config.razor_url) {
132
360
 
@@ -246,7 +474,7 @@ module.exports = function (app, callbacks) {
246
474
 
247
475
  onTxn(objForUpdate);
248
476
 
249
- }, Transaction);
477
+ }, usingMultiDbOrm ? Transaction : undefined);
250
478
 
251
479
 
252
480
 
@@ -261,6 +489,7 @@ module.exports = function (app, callbacks) {
261
489
  orderId: orderId,
262
490
  cusId: user.id,
263
491
  time: Date.now(),
492
+ timeStamp: Date.now(),
264
493
  status: 'INITIATED',
265
494
  name: user.name,
266
495
  email: user.email,
@@ -282,7 +511,7 @@ module.exports = function (app, callbacks) {
282
511
 
283
512
  let orderId;
284
513
  if (config.paytm_url) {
285
- orderId = makeid(config.id_length || IDLEN)
514
+ orderId = "pay_" + makeid(config.id_length || IDLEN)
286
515
  onOrder(orderId)
287
516
  }
288
517
  else if (config.razor_url) {
@@ -296,7 +525,7 @@ module.exports = function (app, callbacks) {
296
525
 
297
526
  razorPayInstance.orders.create(options, function (err, order) {
298
527
  if (err) {
299
- res.send({ message: "An error occurred ! " + err.message })
528
+ res.send({ message: "An error occurred ! " + err.description })
300
529
  return;
301
530
  }
302
531
  orderId = order.id
@@ -346,6 +575,44 @@ module.exports = function (app, callbacks) {
346
575
 
347
576
  }
348
577
 
578
+ function updateTransaction(req, res) {
579
+ var myquery = { orderId: req.body.ORDERID };
580
+
581
+ Transaction.findOne(myquery, function (err, objForUpdate) {
582
+
583
+ if (err) {
584
+ res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
585
+ return;
586
+ }
587
+ if (objForUpdate.status != ("INITIATED") && objForUpdate.status != ("TXN_PENDING")) {
588
+ res.send({ message: "Transaction already processed", status: objForUpdate.status, ORDERID: objForUpdate.orderId, TXNID: objForUpdate.TXNID, TXNID: req.body.TXNID })
589
+ return;
590
+ }
591
+ if (req.body.status == "paid" && !req.body.STATUS) {
592
+ req.body.STATUS = "TXN_SUCCESS"
593
+ }
594
+ objForUpdate.status = req.body.STATUS;
595
+ objForUpdate.TXNID = req.body.TXNID;
596
+ objForUpdate.extra = JSON.stringify(req.body);
597
+
598
+ var newvalues = { $set: objForUpdate };
599
+ Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
600
+
601
+ if (err) {
602
+ res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
603
+ }
604
+ else {
605
+
606
+ if (callbacks !== undefined)
607
+ callbacks.onFinish(req.body.ORDERID, req.body);
608
+ objForUpdate.readonly = "readonly"
609
+ objForUpdate.action = config.homepage
610
+ res.render(vp + "result.hbs", objForUpdate);
611
+ }
612
+ });
613
+
614
+ }, usingMultiDbOrm ? Transaction : undefined)
615
+ }
349
616
 
350
617
  module.callback = (req, res) => {
351
618
 
@@ -354,6 +621,10 @@ module.exports = function (app, callbacks) {
354
621
  if (config.paytm_url) {
355
622
  var checksumhash = req.body.CHECKSUMHASH;
356
623
  result = checksum_lib.verifychecksum(req.body, config.KEY, checksumhash);
624
+ if (req.body.STATUS == 'TXN_FAILURE' && req.body.CANCELLED == "cancelled" && req.body.TXNID) {
625
+ isCancelled = true;
626
+ }
627
+
357
628
  }
358
629
  else if (config.razor_url) {
359
630
 
@@ -374,7 +645,7 @@ module.exports = function (app, callbacks) {
374
645
  req.body.razorpay_order_id = orderId
375
646
  }
376
647
  req.body.STATUS = 'TXN_FAILURE'
377
- req.body.ORDERID = req.body.razorpay_order_id
648
+ req.body.ORDERID = req.body.razorpay_order_id || req.query.order_id
378
649
  isCancelled = true;
379
650
  }
380
651
  }
@@ -385,34 +656,7 @@ module.exports = function (app, callbacks) {
385
656
 
386
657
  if (result || isCancelled) {
387
658
 
388
- var myquery = { orderId: req.body.ORDERID };
389
- Transaction.findOne(myquery, function (err, objForUpdate) {
390
-
391
- if (err) {
392
- res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
393
- return;
394
- }
395
- objForUpdate.status = req.body.STATUS;
396
- objForUpdate.TXNID = req.body.TXNID;
397
- objForUpdate.extra = JSON.stringify(req.body);
398
-
399
- var newvalues = { $set: objForUpdate };
400
- Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
401
-
402
- if (err) {
403
- res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
404
- }
405
- else {
406
-
407
- if (callbacks !== undefined)
408
- callbacks.onFinish(req.body.ORDERID, req.body);
409
- objForUpdate.readonly = "readonly"
410
- objForUpdate.action = config.homepage
411
- res.render(vp + "result.hbs", objForUpdate);
412
- }
413
- });
414
-
415
- }, Transaction)
659
+ updateTransaction(req, res);
416
660
 
417
661
  }
418
662
  else {
@@ -423,6 +667,61 @@ module.exports = function (app, callbacks) {
423
667
 
424
668
  }
425
669
 
670
+ module.webhook = (req, res) => {
671
+ if (config.paytm_url) {
672
+ module.callback(req, res)
673
+ }
674
+ else if (config.razor_url) {
675
+ let events = ["payment.captured", "payment.pending", "payment.failed"]
676
+ if (req.body.event && events.indexOf(req.body.event) > -1) {
677
+ if (req.body.payload &&
678
+ req.body.payload.payment &&
679
+ req.body.payload.payment.entity) {
680
+
681
+ let entity = req.body.payload.payment.entity;
682
+ let razorpay_order_id = entity.order_id;
683
+ let razorpay_payment_id = entity.id;
684
+ let status = entity.status;
685
+ let event = req.body.event;
686
+ console.log(`Razorpay webhook payment order=${razorpay_order_id} payid=${razorpay_payment_id} status=${status}`)
687
+
688
+ let reqBody = req.rawBody, signature = req.headers["x-razorpay-signature"];
689
+
690
+ result = RazorPay.validateWebhookSignature(reqBody, req.headers['x-razorpay-signature'], config.SECRET)
691
+ req.signatureVerified = result;
692
+ if (true) {
693
+ if (event == events[0]) {
694
+ req.body.STATUS = "TXN_SUCCESS";
695
+ }
696
+ else if (event == events[1]) { //pending
697
+ req.body.STATUS = "TXN_PENDING";
698
+ }
699
+ else { // failed
700
+ req.body.STATUS = "TXN_FAILURE";
701
+ }
702
+ req.body.ORDERID = razorpay_order_id;
703
+ req.body.TXNID = razorpay_payment_id;
704
+ setTimeout(() => {
705
+ updateTransaction(req, res)
706
+ }, 3000)
707
+ }
708
+ else {
709
+ res.status(401)
710
+ res.send({ message: "Invalid Rzpay signature" })
711
+ }
712
+ }
713
+ else {
714
+ res.status(400)
715
+ res.send({ message: "Invalid Payload" })
716
+ }
717
+ }
718
+ else {
719
+ res.status(400)
720
+ res.send({ message: "Unsupported event : " + req.body.event })
721
+ }
722
+ }
723
+ }
724
+
426
725
  module.createTxn = (req, res) => {
427
726
 
428
727
 
@@ -432,7 +731,7 @@ module.exports = function (app, callbacks) {
432
731
 
433
732
  let id;
434
733
  if (config.paytm_url) {
435
- id = makeid(config.id_length || IDLEN)
734
+ id = "pay_" + makeid(config.id_length || IDLEN)
436
735
  }
437
736
  else if (config.razor_url) {
438
737
 
@@ -575,7 +874,7 @@ module.exports = function (app, callbacks) {
575
874
  }
576
875
 
577
876
 
578
- }, Transaction);
877
+ }, usingMultiDbOrm ? Transaction : undefined);
579
878
 
580
879
 
581
880
  }
@@ -49,19 +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
- router.all('/', function (req, res) {
58
- res.send({ message: packageInfo.description, developer: packageInfo.author, version: packageInfo.version })
59
-
60
- });
61
+ router.all('/', pc.init);
62
+ router.all('/init', pc.init);
61
63
 
62
- router.all('/home', pc.home)
63
- router.all('/init', pc.init)
64
+ // router.all('/home', pc.home)
64
65
  router.all('/callback', pc.callback)
66
+ router.all('/api/webhook', pc.webhook)
65
67
  router.all('/api/status', pc.status)
66
68
  router.all('/api/createTxn', pc.createTxn)
67
69
 
@@ -7,9 +7,6 @@
7
7
  <tr>
8
8
  <td>Name</td><td>{{name}}</td>
9
9
  </tr>
10
- <tr>
11
- <td>Author</td><td><a href='https://www.linkedin.com/in/shiveshnavin/'>Shivesh Navin</a></td>
12
- </tr>
13
10
  <tr>
14
11
  <td>Version</td><td>{{version}}</td>
15
12
  </tr>
package/example.js CHANGED
@@ -24,6 +24,7 @@ app.set('np_config', {
24
24
  "INDUSTRY_TYPE_ID":"Retail",
25
25
  "homepage":"/",
26
26
  "path_prefix":"_pay",
27
+ "theme_color":"#231530",
27
28
  "db_url":MONGOURL // Remove this property in case you want to use multidborm
28
29
  });
29
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-paytmpg",
3
- "version": "3.0.4",
3
+ "version": "4.1.0",
4
4
  "description": "Payment Gateway Integration using NodeJS",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -38,4 +38,4 @@
38
38
  "request": "^2.88.0",
39
39
  "util": "^0.11.1"
40
40
  }
41
- }
41
+ }