node-paytmpg 7.4.3 → 7.5.3
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.
- package/app/views/init.hbs +23 -18
- package/app/views/layouts/index.hbs +6 -6
- package/dist/app/controllers/htmlhelper.js +3 -2
- package/dist/app/controllers/payment.controller.d.ts +6 -4
- package/dist/app/controllers/payment.controller.js +92 -40
- package/dist/app/models/index.d.ts +27 -3
- package/dist/app/utils/buildConfig.d.ts +3 -1
- package/dist/app/utils/buildConfig.js +14 -0
- package/dist/app/views/init.hbs +23 -18
- package/dist/app/views/layouts/index.hbs +6 -6
- package/dist/index.js +7 -6
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/app/views/init.hbs
CHANGED
|
@@ -41,10 +41,15 @@
|
|
|
41
41
|
<input type="email" name="EMAIL" required value="{{EMAIL}}" {{readonly}} />
|
|
42
42
|
</label>
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
|
|
45
|
+
{{#if MOBILE_NO}}
|
|
46
|
+
<label class="field">
|
|
47
|
+
<span class="field__label">Phone</span>
|
|
48
|
+
<input type="text" name="MOBILE_NO" required value="{{MOBILE_NO}}" {{readonly}} />
|
|
49
|
+
</label>
|
|
50
|
+
{{/if}}
|
|
51
|
+
|
|
52
|
+
|
|
48
53
|
|
|
49
54
|
{{#if check}}
|
|
50
55
|
<label class="field">
|
|
@@ -82,17 +87,17 @@
|
|
|
82
87
|
</section>
|
|
83
88
|
</div>
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
</script>
|
|
90
|
+
<script>
|
|
91
|
+
var enableConfirmationPage = {{#if enableConfirmationPage}}true{{else}}false{{/if}};
|
|
92
|
+
var readonly = {{#if readonly}}true{{else}}false{{/if}};
|
|
93
|
+
// if in readonly mode( all data recieved) and enableConfirmationPage is false, submit the form automatically
|
|
94
|
+
// show a spinner or something while redirecting to payment gateway to avoid user confusion
|
|
95
|
+
// instead of the form
|
|
96
|
+
if (readonly && !enableConfirmationPage) {
|
|
97
|
+
const form = document.getElementById('payment-form');
|
|
98
|
+
const payButton = document.getElementById('pay-button');
|
|
99
|
+
payButton.disabled = true;
|
|
100
|
+
payButton.innerText = 'Redirecting...';
|
|
101
|
+
form.submit();
|
|
102
|
+
}
|
|
103
|
+
</script>
|
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
}
|
|
25
25
|
</style>
|
|
26
26
|
</head>
|
|
27
|
-
<body class="theme-{{#if
|
|
27
|
+
<body class="theme-{{#if theme.name}}{{theme.name}}{{else}}dark{{/if}}">
|
|
28
28
|
<div class="shell">
|
|
29
29
|
<header class="shell__header">
|
|
30
30
|
<div class="brand">
|
|
31
|
-
{{#if logo}}
|
|
32
|
-
<span class="brand__mark brand__mark--img"><img src="{{logo}}" alt="{{brand}} logo"></span>
|
|
31
|
+
{{#if theme.logo}}
|
|
32
|
+
<span class="brand__mark brand__mark--img"><img src="{{theme.logo}}" alt="{{theme.brand}} logo"></span>
|
|
33
33
|
{{else}}
|
|
34
|
-
<span class="brand__mark">{{brand}}</span>
|
|
34
|
+
<span class="brand__mark">{{theme.brand}}</span>
|
|
35
35
|
{{/if}}
|
|
36
36
|
<div class="brand__text">
|
|
37
|
-
<div class="brand__title">{{brand}}</div>
|
|
37
|
+
<div class="brand__title">{{theme.brand}}</div>
|
|
38
38
|
<div class="brand__meta">Secure checkout</div>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
</main>
|
|
47
47
|
|
|
48
48
|
<footer class="shell__footer">
|
|
49
|
-
<span>Protected by {{brand}}</span>
|
|
49
|
+
<span>Protected by {{theme.brand}}</span>
|
|
50
50
|
</footer>
|
|
51
51
|
</div>
|
|
52
52
|
</body>
|
|
@@ -58,13 +58,14 @@ function renderPaytmJsCheckout(req, res, paytmJsToken, config) {
|
|
|
58
58
|
return res.send(html);
|
|
59
59
|
}
|
|
60
60
|
function renderRazorpayCheckout(req, res, params, config, loadingSVG) {
|
|
61
|
+
var _a, _b;
|
|
61
62
|
const options = {
|
|
62
63
|
key: String(config.KEY),
|
|
63
64
|
amount: Number(params['TXN_AMOUNT']) * 100,
|
|
64
65
|
currency: 'INR',
|
|
65
66
|
name: params['PRODUCT_NAME'],
|
|
66
67
|
description: `Order # ${params['ORDER_ID']}`,
|
|
67
|
-
image: config.logo,
|
|
68
|
+
image: ((_a = config.theme) === null || _a === void 0 ? void 0 : _a.logo) || '',
|
|
68
69
|
order_id: params['ORDER_ID'],
|
|
69
70
|
callback_url: params['CALLBACK_URL'],
|
|
70
71
|
prefill: {
|
|
@@ -73,7 +74,7 @@ function renderRazorpayCheckout(req, res, params, config, loadingSVG) {
|
|
|
73
74
|
contact: params['MOBILE_NO']
|
|
74
75
|
},
|
|
75
76
|
theme: {
|
|
76
|
-
color: config.
|
|
77
|
+
color: ((_b = config.theme) === null || _b === void 0 ? void 0 : _b.accent) || '#086cfe'
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
80
|
if (wantsJson(req)) {
|
|
@@ -2,7 +2,7 @@ import { MultiDbORM } from 'multi-db-orm';
|
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
3
|
import { NPConfig, NPTableNames } from '../models';
|
|
4
4
|
export declare class PaymentController {
|
|
5
|
-
private
|
|
5
|
+
private baseConfig;
|
|
6
6
|
private callbacks;
|
|
7
7
|
private db;
|
|
8
8
|
private tableNames;
|
|
@@ -11,16 +11,18 @@ export declare class PaymentController {
|
|
|
11
11
|
private payuInstance;
|
|
12
12
|
private openMoneyInstance;
|
|
13
13
|
private razorPayInstance;
|
|
14
|
-
constructor(
|
|
15
|
-
encodeTxnDataForUrl(txnDataJson: any): string;
|
|
16
|
-
decodeTxnDataFromUrl(encodedStr: string): any;
|
|
14
|
+
constructor(baseConfig: NPConfig, db: MultiDbORM, callbacks?: any, tableNames?: NPTableNames);
|
|
15
|
+
encodeTxnDataForUrl(txnDataJson: any, req: Request): string;
|
|
16
|
+
decodeTxnDataFromUrl(encodedStr: string, req?: Request): any;
|
|
17
17
|
private configure;
|
|
18
18
|
private insertTransactionInDb;
|
|
19
19
|
private generateChecksum;
|
|
20
20
|
home(req: Request, res: Response): void | Response<any, Record<string, any>>;
|
|
21
21
|
init(req: Request, res: Response): Promise<void | Response<any, Record<string, any>>>;
|
|
22
22
|
updateTransaction(req: Request, res: Response): Promise<void>;
|
|
23
|
+
private getOrder;
|
|
23
24
|
callback(req: Request, res: Response): Promise<void>;
|
|
25
|
+
getServiceUsed(req: Request, baseConfig: NPConfig): string;
|
|
24
26
|
webhook(req: Request, res: Response): Promise<void>;
|
|
25
27
|
createTxn(req: Request, res: Response): Promise<void>;
|
|
26
28
|
createTxnToken(req: Request, res: Response): Promise<void>;
|
|
@@ -50,6 +50,7 @@ const user_controller_1 = require("./user.controller");
|
|
|
50
50
|
const utils_1 = require("../utils/utils");
|
|
51
51
|
const loadingsvg_1 = require("./static/loadingsvg");
|
|
52
52
|
const htmlhelper_1 = require("./htmlhelper");
|
|
53
|
+
const buildConfig_1 = require("../utils/buildConfig");
|
|
53
54
|
const IDLEN = 14;
|
|
54
55
|
function makeid(length) {
|
|
55
56
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
@@ -60,23 +61,24 @@ function makeid(length) {
|
|
|
60
61
|
return result;
|
|
61
62
|
}
|
|
62
63
|
class PaymentController {
|
|
63
|
-
constructor(
|
|
64
|
+
constructor(baseConfig, db, callbacks, tableNames) {
|
|
64
65
|
this.tableNames = { USER: 'npusers', TRANSACTION: 'nptransactions' };
|
|
65
66
|
this.viewPath = '';
|
|
66
|
-
this.
|
|
67
|
+
this.baseConfig = baseConfig;
|
|
67
68
|
this.callbacks = callbacks;
|
|
68
69
|
this.db = db;
|
|
69
70
|
if (tableNames) {
|
|
70
71
|
this.tableNames = tableNames;
|
|
71
72
|
}
|
|
72
73
|
this.useController = new user_controller_1.NPUserController(this.db, this.tableNames.USER);
|
|
73
|
-
this.configure(
|
|
74
|
+
this.configure(baseConfig);
|
|
74
75
|
}
|
|
75
|
-
encodeTxnDataForUrl(txnDataJson) {
|
|
76
|
+
encodeTxnDataForUrl(txnDataJson, req) {
|
|
77
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
76
78
|
// Accept either an object or a JSON string.
|
|
77
79
|
const payloadStr = typeof txnDataJson === 'string' ? txnDataJson : JSON.stringify(txnDataJson);
|
|
78
80
|
// Derive a 32-byte key from config.SECRET (fallback to config.KEY).
|
|
79
|
-
const secret = String(
|
|
81
|
+
const secret = String(config.SECRET || config.KEY || '');
|
|
80
82
|
if (!secret) {
|
|
81
83
|
// No secret available — fallback to url-safe base64 (not secure).
|
|
82
84
|
return Buffer.from(payloadStr, 'utf8').toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
@@ -90,7 +92,7 @@ class PaymentController {
|
|
|
90
92
|
const out = Buffer.concat([iv, tag, encrypted]).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
|
|
91
93
|
return out;
|
|
92
94
|
}
|
|
93
|
-
decodeTxnDataFromUrl(encodedStr) {
|
|
95
|
+
decodeTxnDataFromUrl(encodedStr, req) {
|
|
94
96
|
if (!encodedStr)
|
|
95
97
|
return '';
|
|
96
98
|
// Convert back to standard base64 and pad
|
|
@@ -112,7 +114,8 @@ class PaymentController {
|
|
|
112
114
|
const iv = raw.slice(0, 12);
|
|
113
115
|
const tag = raw.slice(12, 28);
|
|
114
116
|
const ciphertext = raw.slice(28);
|
|
115
|
-
const
|
|
117
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
118
|
+
const secret = String(config.SECRET || config.KEY || '');
|
|
116
119
|
if (!secret)
|
|
117
120
|
return raw.toString('utf8');
|
|
118
121
|
const key = crypto.createHash('sha256').update(secret).digest();
|
|
@@ -165,9 +168,10 @@ class PaymentController {
|
|
|
165
168
|
await this.db.insert(this.tableNames.TRANSACTION, txnData);
|
|
166
169
|
return txnData;
|
|
167
170
|
}
|
|
168
|
-
async generateChecksum(params) {
|
|
171
|
+
async generateChecksum(params, req) {
|
|
172
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
169
173
|
return await new Promise((resolve, reject) => {
|
|
170
|
-
checksum_1.default.genchecksum(params,
|
|
174
|
+
checksum_1.default.genchecksum(params, config.KEY, (err, cs) => {
|
|
171
175
|
if (err || !cs) {
|
|
172
176
|
reject(err || new Error('Error generating checksum'));
|
|
173
177
|
return;
|
|
@@ -182,12 +186,11 @@ class PaymentController {
|
|
|
182
186
|
}
|
|
183
187
|
async init(req, res) {
|
|
184
188
|
var _a;
|
|
185
|
-
const config = this.config;
|
|
186
189
|
const callbacks = this.callbacks;
|
|
187
190
|
const vp = this.viewPath;
|
|
188
191
|
const razorPayInstance = this.razorPayInstance;
|
|
189
192
|
if (!req.body.ORDER_ID && !req.body.EMAIL && ((_a = req.query) === null || _a === void 0 ? void 0 : _a.to)) {
|
|
190
|
-
let toData = JSON.parse(this.decodeTxnDataFromUrl(req.query.to));
|
|
193
|
+
let toData = JSON.parse(this.decodeTxnDataFromUrl(req.query.to, req));
|
|
191
194
|
req.body.NAME = toData.NAME;
|
|
192
195
|
req.body.EMAIL = toData.EMAIL;
|
|
193
196
|
req.body.TXN_AMOUNT = toData.TXN_AMOUNT;
|
|
@@ -195,7 +198,9 @@ class PaymentController {
|
|
|
195
198
|
req.body.ORDER_ID = toData.ORDER_ID || toData.ORDERID;
|
|
196
199
|
req.body.PRODUCT_NAME = toData.PRODUCT_NAME;
|
|
197
200
|
req.body.RETURN_URL = toData.RETURN_URL;
|
|
201
|
+
req.body.CLIENT_ID = toData.CLIENT_ID;
|
|
198
202
|
}
|
|
203
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
199
204
|
utils_1.Utils.sanitizeRequest(req.body);
|
|
200
205
|
let gotAllParams = true;
|
|
201
206
|
let checkedFields = ['TXN_AMOUNT', 'PRODUCT_NAME',
|
|
@@ -233,7 +238,7 @@ class PaymentController {
|
|
|
233
238
|
params['MOBILE_NO'] = req.body.MOBILE_NO;
|
|
234
239
|
params['PRODUCT_NAME'] = req.body.PRODUCT_NAME;
|
|
235
240
|
params['NAME'] = req.body.NAME;
|
|
236
|
-
if (
|
|
241
|
+
if (config.paytm_url) {
|
|
237
242
|
let initTxnbody = {
|
|
238
243
|
"requestType": "Payment",
|
|
239
244
|
"mid": params['MID'],
|
|
@@ -251,11 +256,11 @@ class PaymentController {
|
|
|
251
256
|
"email": params['EMAIL']
|
|
252
257
|
}
|
|
253
258
|
};
|
|
254
|
-
if (
|
|
255
|
-
initTxnbody["enablePaymentMode"] = JSON.parse(
|
|
259
|
+
if (config.mode) {
|
|
260
|
+
initTxnbody["enablePaymentMode"] = JSON.parse(config.mode);
|
|
256
261
|
}
|
|
257
|
-
let checksum = await PaytmChecksum_1.default.generateSignature(JSON.stringify(initTxnbody),
|
|
258
|
-
let initTxnUrl =
|
|
262
|
+
let checksum = await PaytmChecksum_1.default.generateSignature(JSON.stringify(initTxnbody), config.KEY);
|
|
263
|
+
let initTxnUrl = config.paytm_url + `/theia/api/v1/initiateTransaction?mid=${params['MID']}&orderId=${params['ORDER_ID']}`;
|
|
259
264
|
try {
|
|
260
265
|
const resp = await axios_1.default.post(initTxnUrl, {
|
|
261
266
|
body: initTxnbody,
|
|
@@ -275,7 +280,7 @@ class PaymentController {
|
|
|
275
280
|
paytmJsToken.TXN_AMOUNT = params['TXN_AMOUNT'];
|
|
276
281
|
paytmJsToken.MID = params['MID'];
|
|
277
282
|
paytmJsToken.CALLBACK_URL = params['CALLBACK_URL'];
|
|
278
|
-
return (0, htmlhelper_1.renderPaytmJsCheckout)(req, res, paytmJsToken,
|
|
283
|
+
return (0, htmlhelper_1.renderPaytmJsCheckout)(req, res, paytmJsToken, config);
|
|
279
284
|
}
|
|
280
285
|
else {
|
|
281
286
|
console.log('ERROR:::', resp.status, '\n', body);
|
|
@@ -303,14 +308,14 @@ class PaymentController {
|
|
|
303
308
|
return (0, htmlhelper_1.sendAutoPostForm)(req, res, params['CALLBACK_URL'], errorResp);
|
|
304
309
|
}
|
|
305
310
|
}
|
|
306
|
-
else if (
|
|
307
|
-
return (0, htmlhelper_1.renderRazorpayCheckout)(req, res, params,
|
|
311
|
+
else if (config.razor_url) {
|
|
312
|
+
return (0, htmlhelper_1.renderRazorpayCheckout)(req, res, params, config, loadingsvg_1.LoadingSVG);
|
|
308
313
|
}
|
|
309
|
-
else if (
|
|
314
|
+
else if (config.payu_url) {
|
|
310
315
|
const payuRequest = this.payuInstance.generatePaymentRequest(params);
|
|
311
316
|
this.payuInstance.renderProcessingPage(params, payuRequest, res, loadingsvg_1.LoadingSVG);
|
|
312
317
|
}
|
|
313
|
-
else if (
|
|
318
|
+
else if (config.open_money_url) {
|
|
314
319
|
try {
|
|
315
320
|
let pmttoken = await this.openMoneyInstance.generatePaymentToken(params);
|
|
316
321
|
this.openMoneyInstance.renderProcessingPage(params, pmttoken, res, loadingsvg_1.LoadingSVG);
|
|
@@ -351,6 +356,9 @@ class PaymentController {
|
|
|
351
356
|
params['PRODUCT_NAME'] = txnData.pname;
|
|
352
357
|
const showConfirmation = (checksum = '') => {
|
|
353
358
|
return (0, htmlhelper_1.renderView)(req, res, vp + "init.hbs", {
|
|
359
|
+
name: 'ASDASD',
|
|
360
|
+
enableConfirmationPage: config.enableConfirmationPage,
|
|
361
|
+
theme: config.theme,
|
|
354
362
|
path_prefix: config.path_prefix,
|
|
355
363
|
action: "/" + config.path_prefix + "/init",
|
|
356
364
|
readonly: 'readonly',
|
|
@@ -371,7 +379,7 @@ class PaymentController {
|
|
|
371
379
|
});
|
|
372
380
|
};
|
|
373
381
|
if (config.paytm_url) {
|
|
374
|
-
const checksum = await this.generateChecksum(params);
|
|
382
|
+
const checksum = await this.generateChecksum(params, req);
|
|
375
383
|
showConfirmation(checksum);
|
|
376
384
|
}
|
|
377
385
|
else if (config.razor_url || config.payu_url || config.open_money_url) {
|
|
@@ -458,8 +466,11 @@ class PaymentController {
|
|
|
458
466
|
}
|
|
459
467
|
else {
|
|
460
468
|
return (0, htmlhelper_1.renderView)(req, res, this.viewPath + "init.hbs", {
|
|
461
|
-
|
|
462
|
-
|
|
469
|
+
name: 'ASDASD',
|
|
470
|
+
enableConfirmationPage: config.enableConfirmationPage,
|
|
471
|
+
theme: config.theme,
|
|
472
|
+
path_prefix: config.path_prefix,
|
|
473
|
+
action: "/" + config.path_prefix + "/init",
|
|
463
474
|
readonly: '',
|
|
464
475
|
check: true,
|
|
465
476
|
BUTTON: 'Submit',
|
|
@@ -480,7 +491,6 @@ class PaymentController {
|
|
|
480
491
|
}
|
|
481
492
|
}
|
|
482
493
|
async updateTransaction(req, res) {
|
|
483
|
-
const config = this.config;
|
|
484
494
|
const vp = this.viewPath;
|
|
485
495
|
const callbacks = this.callbacks;
|
|
486
496
|
const orderToFind = req.body.ORDERID || req.body.ORDER_ID || req.body.ORDERId || (req.query && req.query.order_id) || req.body.ORDER_ID;
|
|
@@ -502,6 +512,7 @@ class PaymentController {
|
|
|
502
512
|
webhookUrl = null;
|
|
503
513
|
if (returnUrl === 'undefined')
|
|
504
514
|
returnUrl = null;
|
|
515
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, objForUpdate);
|
|
505
516
|
if (!objForUpdate) {
|
|
506
517
|
if (webhookUrl) {
|
|
507
518
|
try {
|
|
@@ -579,12 +590,24 @@ class PaymentController {
|
|
|
579
590
|
return res.redirect(`${returnUrl}${separator}status=${objForUpdate.status}&ORDERID=${objForUpdate.orderId}&TXNID=${objForUpdate.txnId}`);
|
|
580
591
|
}
|
|
581
592
|
(0, htmlhelper_1.renderView)(req, res, vp + "result.hbs", {
|
|
593
|
+
theme: config.theme,
|
|
582
594
|
path_prefix: config.path_prefix,
|
|
583
595
|
...objForUpdate
|
|
584
596
|
});
|
|
585
597
|
}
|
|
598
|
+
async getOrder(req = { body: {}, query: {} }, orderId) {
|
|
599
|
+
const orderToFind = orderId || req.body.ORDERID || req.body.ORDER_ID || req.body.ORDER_ID || req.body.ORDERId || req.query.ORDERID || req.query.ORDERId || req.query.ORDER_ID || req.query.order_id;
|
|
600
|
+
const myquery = { orderId: orderToFind };
|
|
601
|
+
let objForUpdate = null;
|
|
602
|
+
try {
|
|
603
|
+
objForUpdate = await this.db.getOne(this.tableNames.TRANSACTION, myquery).catch(() => null);
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
objForUpdate = objForUpdate || null;
|
|
607
|
+
}
|
|
608
|
+
return objForUpdate;
|
|
609
|
+
}
|
|
586
610
|
async callback(req, res) {
|
|
587
|
-
const config = this.config;
|
|
588
611
|
const payuInstance = this.payuInstance;
|
|
589
612
|
const openMoneyInstance = this.openMoneyInstance;
|
|
590
613
|
console.log("request_data ", req.originalUrl, JSON.stringify(req.body));
|
|
@@ -605,13 +628,15 @@ class PaymentController {
|
|
|
605
628
|
}
|
|
606
629
|
let result = false;
|
|
607
630
|
let isCancelled = false;
|
|
631
|
+
const objForUpdate = await this.getOrder(req);
|
|
632
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, objForUpdate);
|
|
608
633
|
if (config.paytm_url) {
|
|
609
634
|
const checksumhash = req.body.CHECKSUMHASH;
|
|
610
635
|
if (checksumhash) {
|
|
611
636
|
result = await checksum_1.default.verifychecksum(req.body, config.KEY, checksumhash);
|
|
612
637
|
}
|
|
613
638
|
else {
|
|
614
|
-
const liveStatus = await this.getStatusFromPaytm({ MID: config.MID, ORDERID: req.body.ORDERID }, req.body.ORDERID);
|
|
639
|
+
const liveStatus = await this.getStatusFromPaytm({ MID: config.MID, ORDERID: req.body.ORDERID }, req.body.ORDERID, req);
|
|
615
640
|
// Merge important fields when live status is available
|
|
616
641
|
if (liveStatus && typeof liveStatus === 'object') {
|
|
617
642
|
req.body.STATUS = liveStatus.STATUS || req.body.STATUS;
|
|
@@ -673,18 +698,41 @@ class PaymentController {
|
|
|
673
698
|
res.send({ message: "Something went wrong ! Please try again later .", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
674
699
|
}
|
|
675
700
|
}
|
|
701
|
+
getServiceUsed(req, baseConfig) {
|
|
702
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(baseConfig, req);
|
|
703
|
+
let serviceUsed = '';
|
|
704
|
+
if (config.paytm_url)
|
|
705
|
+
serviceUsed = 'Paytm';
|
|
706
|
+
if (config.razor_url)
|
|
707
|
+
serviceUsed = 'Razorpay';
|
|
708
|
+
if (config.payu_url)
|
|
709
|
+
serviceUsed = 'PayU';
|
|
710
|
+
if (config.open_money_url)
|
|
711
|
+
serviceUsed = 'OpenMoney';
|
|
712
|
+
// https://razorpay.com/docs/webhooks/?preferred-country=IN
|
|
713
|
+
if (req.headers["x-razorpay-signature"])
|
|
714
|
+
serviceUsed = 'Razorpay';
|
|
715
|
+
// https://business.paytm.com/docs/payment-status/
|
|
716
|
+
else if (req.body.PAYMENTMODE)
|
|
717
|
+
serviceUsed = 'Paytm';
|
|
718
|
+
// https://docs.payu.in/docs/webhooks
|
|
719
|
+
else if (req.body.payment_source)
|
|
720
|
+
serviceUsed = 'PayU';
|
|
721
|
+
return serviceUsed;
|
|
722
|
+
}
|
|
676
723
|
async webhook(req, res) {
|
|
677
|
-
|
|
724
|
+
let config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
678
725
|
const payuInstance = this.payuInstance;
|
|
679
726
|
const openMoneyInstance = this.openMoneyInstance;
|
|
680
727
|
console.log("request_data ", req.originalUrl, JSON.stringify(req.body));
|
|
681
728
|
console.log("request_data rawBody", req.originalUrl, req.rawBody);
|
|
682
729
|
console.log("request_headers ", req.originalUrl, JSON.stringify(req.headers));
|
|
683
|
-
|
|
730
|
+
let serviceUsed = this.getServiceUsed(req, config);
|
|
731
|
+
if (serviceUsed === 'Paytm') {
|
|
684
732
|
await this.callback(req, res);
|
|
685
733
|
return;
|
|
686
734
|
}
|
|
687
|
-
if (
|
|
735
|
+
if (serviceUsed === 'Razorpay') {
|
|
688
736
|
const events = ["payment.captured", "payment.pending", "payment.failed"];
|
|
689
737
|
if (req.body.event && events.indexOf(req.body.event) > -1) {
|
|
690
738
|
if (req.body.payload &&
|
|
@@ -692,6 +740,8 @@ class PaymentController {
|
|
|
692
740
|
req.body.payload.payment.entity) {
|
|
693
741
|
const entity = req.body.payload.payment.entity;
|
|
694
742
|
const razorpay_order_id = entity.order_id;
|
|
743
|
+
const order = await this.getOrder(undefined, razorpay_order_id);
|
|
744
|
+
config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, order || undefined);
|
|
695
745
|
const razorpay_payment_id = entity.id;
|
|
696
746
|
const status = entity.status;
|
|
697
747
|
const event = req.body.event;
|
|
@@ -739,16 +789,16 @@ class PaymentController {
|
|
|
739
789
|
}
|
|
740
790
|
return;
|
|
741
791
|
}
|
|
742
|
-
if (
|
|
792
|
+
if (serviceUsed === 'PayU') {
|
|
743
793
|
payuInstance.processWebhook(req, res, this.updateTransaction);
|
|
744
794
|
return;
|
|
745
795
|
}
|
|
746
|
-
if (
|
|
796
|
+
if (serviceUsed === 'OpenMoney') {
|
|
747
797
|
openMoneyInstance.processWebhook(req, res, this.updateTransaction);
|
|
748
798
|
}
|
|
749
799
|
}
|
|
750
800
|
async createTxn(req, res) {
|
|
751
|
-
const config = this.
|
|
801
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
752
802
|
const razorPayInstance = this.razorPayInstance;
|
|
753
803
|
// mandayory field
|
|
754
804
|
const requiredFields = ['NAME', 'EMAIL',
|
|
@@ -813,8 +863,9 @@ class PaymentController {
|
|
|
813
863
|
WEBHOOK_URL: txn.webhookUrl,
|
|
814
864
|
TXN_AMOUNT: txn.amount,
|
|
815
865
|
PRODUCT_NAME: txn.pname,
|
|
816
|
-
clientId: txn.clientId
|
|
817
|
-
|
|
866
|
+
clientId: txn.clientId,
|
|
867
|
+
CLIENT_ID: txn.clientId,
|
|
868
|
+
}), req);
|
|
818
869
|
txn.payurl = config.host_url + '/' + config.path_prefix + '/init?to=' + urlData64;
|
|
819
870
|
res.send(txn);
|
|
820
871
|
}
|
|
@@ -882,7 +933,7 @@ class PaymentController {
|
|
|
882
933
|
}
|
|
883
934
|
}
|
|
884
935
|
async status(req, res) {
|
|
885
|
-
const config = this.
|
|
936
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
886
937
|
const callbacks = this.callbacks;
|
|
887
938
|
const payuInstance = this.payuInstance;
|
|
888
939
|
const openMoneyInstance = this.openMoneyInstance;
|
|
@@ -936,7 +987,7 @@ class PaymentController {
|
|
|
936
987
|
}
|
|
937
988
|
};
|
|
938
989
|
if (config.paytm_url) {
|
|
939
|
-
const paytmResponse = await this.getStatusFromPaytm(params, req.body.ORDER_ID);
|
|
990
|
+
const paytmResponse = await this.getStatusFromPaytm(params, req.body.ORDER_ID, req);
|
|
940
991
|
await onStatusUpdate(paytmResponse);
|
|
941
992
|
}
|
|
942
993
|
else if (config.razor_url) {
|
|
@@ -1013,10 +1064,11 @@ class PaymentController {
|
|
|
1013
1064
|
res.send(orderData);
|
|
1014
1065
|
}
|
|
1015
1066
|
}
|
|
1016
|
-
async getStatusFromPaytm(params, orderId) {
|
|
1017
|
-
const checksum = await this.generateChecksum(params);
|
|
1067
|
+
async getStatusFromPaytm(params, orderId, req) {
|
|
1068
|
+
const checksum = await this.generateChecksum(params, req);
|
|
1069
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
1018
1070
|
try {
|
|
1019
|
-
const resp = await axios_1.default.post(`${
|
|
1071
|
+
const resp = await axios_1.default.post(`${config.paytm_url}/order/status`, { MID: config.MID, ORDERID: orderId, CHECKSUMHASH: checksum });
|
|
1020
1072
|
if (resp.status === 200) {
|
|
1021
1073
|
return resp.data;
|
|
1022
1074
|
}
|
|
@@ -34,6 +34,17 @@ export interface NPCallbacks {
|
|
|
34
34
|
onStart: (orderId: string, paymentData?: NPTransaction) => void;
|
|
35
35
|
onFinish: (orderId: string, paymentData?: NPTransaction) => void;
|
|
36
36
|
}
|
|
37
|
+
export type NPConfigTheme = {
|
|
38
|
+
logo: string;
|
|
39
|
+
primary: string;
|
|
40
|
+
accent: string;
|
|
41
|
+
surface: string;
|
|
42
|
+
text: string;
|
|
43
|
+
success: string;
|
|
44
|
+
danger: string;
|
|
45
|
+
name: string;
|
|
46
|
+
brand: string;
|
|
47
|
+
};
|
|
37
48
|
export type NPConfig = {
|
|
38
49
|
KEY: string;
|
|
39
50
|
SECRET: string;
|
|
@@ -42,6 +53,7 @@ export type NPConfig = {
|
|
|
42
53
|
CHANNEL_ID?: string;
|
|
43
54
|
INDUSTRY_TYPE_ID?: string;
|
|
44
55
|
CALLBACK_URL?: string;
|
|
56
|
+
enableConfirmationPage?: boolean;
|
|
45
57
|
paytm_url?: string;
|
|
46
58
|
mode?: string;
|
|
47
59
|
razor_url?: string;
|
|
@@ -49,12 +61,24 @@ export type NPConfig = {
|
|
|
49
61
|
payu_url?: string;
|
|
50
62
|
templateDir?: string;
|
|
51
63
|
view_path: string;
|
|
52
|
-
theme_color?: string;
|
|
53
|
-
brand?: string;
|
|
54
|
-
logo?: string;
|
|
55
64
|
host_url?: string;
|
|
56
65
|
path_prefix: string;
|
|
57
66
|
id_length?: number;
|
|
67
|
+
getClientConfig?: (clientId: string) => Partial<NPConfigOverrides>;
|
|
68
|
+
theme?: NPConfigTheme;
|
|
69
|
+
};
|
|
70
|
+
export type NPConfigOverrides = {
|
|
71
|
+
theme?: NPConfigTheme;
|
|
72
|
+
paytm_url?: string;
|
|
73
|
+
razor_url?: string;
|
|
74
|
+
open_money_url?: string;
|
|
75
|
+
payu_url?: string;
|
|
76
|
+
KEY: string;
|
|
77
|
+
SECRET: string;
|
|
78
|
+
MID?: string;
|
|
79
|
+
CALLBACK_URL?: string;
|
|
80
|
+
host_url?: string;
|
|
81
|
+
path_prefix: string;
|
|
58
82
|
};
|
|
59
83
|
export type NPTableNames = {
|
|
60
84
|
USER: string;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Request } from "express";
|
|
2
|
+
import { NPConfig, NPTransaction } from "../models";
|
|
3
|
+
export declare function withClientConfigOverrides(config: NPConfig, req: Request, orderData?: NPTransaction): NPConfig;
|
|
2
4
|
export declare function buildConfig(userConfig?: Record<string, any>): NPConfig;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withClientConfigOverrides = withClientConfigOverrides;
|
|
3
4
|
exports.buildConfig = buildConfig;
|
|
4
5
|
const defaults = {
|
|
5
6
|
// Server configuration
|
|
@@ -111,6 +112,19 @@ function validateOpenMoneyConfig(config) {
|
|
|
111
112
|
throw new Error(`OpenMoney configuration incomplete. Missing fields: ${missing.join(', ')}`);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
115
|
+
function withClientConfigOverrides(config, req, orderData) {
|
|
116
|
+
let _client = config;
|
|
117
|
+
if (config.getClientConfig && (req || (orderData === null || orderData === void 0 ? void 0 : orderData.clientId))) {
|
|
118
|
+
const clientId = (orderData === null || orderData === void 0 ? void 0 : orderData.clientId) || req.headers['x-client-id'] || req.query.client_id || req.body.client_id || req.body.CLIENT_ID || req.query.CLIENT_ID;
|
|
119
|
+
if (clientId) {
|
|
120
|
+
const clientConfig = config.getClientConfig(clientId);
|
|
121
|
+
if (clientConfig) {
|
|
122
|
+
_client = { ...config, ...clientConfig };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return _client;
|
|
127
|
+
}
|
|
114
128
|
function buildConfig(userConfig = {}) {
|
|
115
129
|
var _a, _b, _c, _d, _e, _f;
|
|
116
130
|
const envOverrides = pickEnv([
|
package/dist/app/views/init.hbs
CHANGED
|
@@ -41,10 +41,15 @@
|
|
|
41
41
|
<input type="email" name="EMAIL" required value="{{EMAIL}}" {{readonly}} />
|
|
42
42
|
</label>
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
|
|
45
|
+
{{#if MOBILE_NO}}
|
|
46
|
+
<label class="field">
|
|
47
|
+
<span class="field__label">Phone</span>
|
|
48
|
+
<input type="text" name="MOBILE_NO" required value="{{MOBILE_NO}}" {{readonly}} />
|
|
49
|
+
</label>
|
|
50
|
+
{{/if}}
|
|
51
|
+
|
|
52
|
+
|
|
48
53
|
|
|
49
54
|
{{#if check}}
|
|
50
55
|
<label class="field">
|
|
@@ -82,17 +87,17 @@
|
|
|
82
87
|
</section>
|
|
83
88
|
</div>
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
</script>
|
|
90
|
+
<script>
|
|
91
|
+
var enableConfirmationPage = {{#if enableConfirmationPage}}true{{else}}false{{/if}};
|
|
92
|
+
var readonly = {{#if readonly}}true{{else}}false{{/if}};
|
|
93
|
+
// if in readonly mode( all data recieved) and enableConfirmationPage is false, submit the form automatically
|
|
94
|
+
// show a spinner or something while redirecting to payment gateway to avoid user confusion
|
|
95
|
+
// instead of the form
|
|
96
|
+
if (readonly && !enableConfirmationPage) {
|
|
97
|
+
const form = document.getElementById('payment-form');
|
|
98
|
+
const payButton = document.getElementById('pay-button');
|
|
99
|
+
payButton.disabled = true;
|
|
100
|
+
payButton.innerText = 'Redirecting...';
|
|
101
|
+
form.submit();
|
|
102
|
+
}
|
|
103
|
+
</script>
|
|
@@ -24,17 +24,17 @@
|
|
|
24
24
|
}
|
|
25
25
|
</style>
|
|
26
26
|
</head>
|
|
27
|
-
<body class="theme-{{#if
|
|
27
|
+
<body class="theme-{{#if theme.name}}{{theme.name}}{{else}}dark{{/if}}">
|
|
28
28
|
<div class="shell">
|
|
29
29
|
<header class="shell__header">
|
|
30
30
|
<div class="brand">
|
|
31
|
-
{{#if logo}}
|
|
32
|
-
<span class="brand__mark brand__mark--img"><img src="{{logo}}" alt="{{brand}} logo"></span>
|
|
31
|
+
{{#if theme.logo}}
|
|
32
|
+
<span class="brand__mark brand__mark--img"><img src="{{theme.logo}}" alt="{{theme.brand}} logo"></span>
|
|
33
33
|
{{else}}
|
|
34
|
-
<span class="brand__mark">{{brand}}</span>
|
|
34
|
+
<span class="brand__mark">{{theme.brand}}</span>
|
|
35
35
|
{{/if}}
|
|
36
36
|
<div class="brand__text">
|
|
37
|
-
<div class="brand__title">{{brand}}</div>
|
|
37
|
+
<div class="brand__title">{{theme.brand}}</div>
|
|
38
38
|
<div class="brand__meta">Secure checkout</div>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
</main>
|
|
47
47
|
|
|
48
48
|
<footer class="shell__footer">
|
|
49
|
-
<span>Protected by {{brand}}</span>
|
|
49
|
+
<span>Protected by {{theme.brand}}</span>
|
|
50
50
|
</footer>
|
|
51
51
|
</div>
|
|
52
52
|
</body>
|
package/dist/index.js
CHANGED
|
@@ -68,19 +68,20 @@ function createPaymentMiddleware(app, userConfig, db, callbacks, authenticationM
|
|
|
68
68
|
callbacks = callbacks || config.callbacks;
|
|
69
69
|
const pc = new payment_controller_1.PaymentController(config, db, callbacks, tableNames);
|
|
70
70
|
subApp.use((req, res, next) => {
|
|
71
|
-
|
|
71
|
+
let _client = (0, buildConfig_1.withClientConfigOverrides)(config, req);
|
|
72
|
+
const theme = _client.theme || {};
|
|
72
73
|
res.locals.theme = {
|
|
73
|
-
primary: theme.primary || '#
|
|
74
|
+
primary: theme.primary || '#086cfe',
|
|
74
75
|
accent: theme.accent || '#5ce1e6',
|
|
75
76
|
surface: theme.surface || '#0f1021',
|
|
76
77
|
text: theme.text || '#e9ecf2',
|
|
77
78
|
success: theme.success || '#24cf5f',
|
|
78
79
|
danger: theme.danger || '#ff6b6b',
|
|
79
80
|
};
|
|
80
|
-
res.locals.themeName =
|
|
81
|
-
res.locals.brand =
|
|
82
|
-
res.locals.logo =
|
|
83
|
-
res.locals.path_prefix =
|
|
81
|
+
res.locals.themeName = theme.name || 'dark';
|
|
82
|
+
res.locals.brand = theme.brand || 'Secure Pay';
|
|
83
|
+
res.locals.logo = theme.logo || '';
|
|
84
|
+
res.locals.path_prefix = _client.path_prefix;
|
|
84
85
|
next();
|
|
85
86
|
});
|
|
86
87
|
subApp.use((req, res, next) => {
|
package/dist/package.json
CHANGED