node-paytmpg 3.0.5 → 5.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
@@ -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,7 +1,12 @@
1
1
  ## Node JS Payments Easy Integration
2
+
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
+ [![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
9
+ - Open Money
5
10
 
6
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 .
7
12
 
@@ -18,7 +23,7 @@ Example App Sourcecode : https://github.com/shiveshnavin/payment-gateway-example
18
23
  ### Requirments
19
24
 
20
25
  1. MongoDB / Firestore / SQlite
21
- 2. Your Paytm Credentials
26
+ 2. Your Merchant Credentials
22
27
  3. Express . This only works with NodeJS express server
23
28
 
24
29
  You can get your paytm credentials here
@@ -74,8 +79,25 @@ homepage : Homepage of your website where user can go after payment confirmation
74
79
  path_prefix : All node_paytm_pg apis/pages will be available relative to this path prefix
75
80
  db_url : Your MongoDB url in case you want to use legacy mongodb connection . You can use multidborm to support MongoDB/Firestore/Sqlite
76
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
+
77
98
  ```
78
99
 
100
+
79
101
  Place these 2 statements in your main nodejs file before calling app.listen(..)
80
102
 
81
103
  ```javascript
@@ -198,6 +220,21 @@ var User=PayTMPG.User;
198
220
 
199
221
  ```
200
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
201
238
 
202
239
 
203
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;