node-paytmpg 7.4.2 → 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 +98 -42
- 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,10 +198,14 @@ 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
|
-
let checkedFields = ['TXN_AMOUNT', 'PRODUCT_NAME',
|
|
206
|
+
let checkedFields = ['TXN_AMOUNT', 'PRODUCT_NAME',
|
|
207
|
+
// 'MOBILE_NO',
|
|
208
|
+
'NAME', 'EMAIL'];
|
|
202
209
|
if (req.body !== undefined) {
|
|
203
210
|
for (var i = 0; i < checkedFields.length; i++) {
|
|
204
211
|
if (req.body[checkedFields[i]] === undefined) {
|
|
@@ -231,7 +238,7 @@ class PaymentController {
|
|
|
231
238
|
params['MOBILE_NO'] = req.body.MOBILE_NO;
|
|
232
239
|
params['PRODUCT_NAME'] = req.body.PRODUCT_NAME;
|
|
233
240
|
params['NAME'] = req.body.NAME;
|
|
234
|
-
if (
|
|
241
|
+
if (config.paytm_url) {
|
|
235
242
|
let initTxnbody = {
|
|
236
243
|
"requestType": "Payment",
|
|
237
244
|
"mid": params['MID'],
|
|
@@ -249,11 +256,11 @@ class PaymentController {
|
|
|
249
256
|
"email": params['EMAIL']
|
|
250
257
|
}
|
|
251
258
|
};
|
|
252
|
-
if (
|
|
253
|
-
initTxnbody["enablePaymentMode"] = JSON.parse(
|
|
259
|
+
if (config.mode) {
|
|
260
|
+
initTxnbody["enablePaymentMode"] = JSON.parse(config.mode);
|
|
254
261
|
}
|
|
255
|
-
let checksum = await PaytmChecksum_1.default.generateSignature(JSON.stringify(initTxnbody),
|
|
256
|
-
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']}`;
|
|
257
264
|
try {
|
|
258
265
|
const resp = await axios_1.default.post(initTxnUrl, {
|
|
259
266
|
body: initTxnbody,
|
|
@@ -273,7 +280,7 @@ class PaymentController {
|
|
|
273
280
|
paytmJsToken.TXN_AMOUNT = params['TXN_AMOUNT'];
|
|
274
281
|
paytmJsToken.MID = params['MID'];
|
|
275
282
|
paytmJsToken.CALLBACK_URL = params['CALLBACK_URL'];
|
|
276
|
-
return (0, htmlhelper_1.renderPaytmJsCheckout)(req, res, paytmJsToken,
|
|
283
|
+
return (0, htmlhelper_1.renderPaytmJsCheckout)(req, res, paytmJsToken, config);
|
|
277
284
|
}
|
|
278
285
|
else {
|
|
279
286
|
console.log('ERROR:::', resp.status, '\n', body);
|
|
@@ -301,14 +308,14 @@ class PaymentController {
|
|
|
301
308
|
return (0, htmlhelper_1.sendAutoPostForm)(req, res, params['CALLBACK_URL'], errorResp);
|
|
302
309
|
}
|
|
303
310
|
}
|
|
304
|
-
else if (
|
|
305
|
-
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);
|
|
306
313
|
}
|
|
307
|
-
else if (
|
|
314
|
+
else if (config.payu_url) {
|
|
308
315
|
const payuRequest = this.payuInstance.generatePaymentRequest(params);
|
|
309
316
|
this.payuInstance.renderProcessingPage(params, payuRequest, res, loadingsvg_1.LoadingSVG);
|
|
310
317
|
}
|
|
311
|
-
else if (
|
|
318
|
+
else if (config.open_money_url) {
|
|
312
319
|
try {
|
|
313
320
|
let pmttoken = await this.openMoneyInstance.generatePaymentToken(params);
|
|
314
321
|
this.openMoneyInstance.renderProcessingPage(params, pmttoken, res, loadingsvg_1.LoadingSVG);
|
|
@@ -349,6 +356,9 @@ class PaymentController {
|
|
|
349
356
|
params['PRODUCT_NAME'] = txnData.pname;
|
|
350
357
|
const showConfirmation = (checksum = '') => {
|
|
351
358
|
return (0, htmlhelper_1.renderView)(req, res, vp + "init.hbs", {
|
|
359
|
+
name: 'ASDASD',
|
|
360
|
+
enableConfirmationPage: config.enableConfirmationPage,
|
|
361
|
+
theme: config.theme,
|
|
352
362
|
path_prefix: config.path_prefix,
|
|
353
363
|
action: "/" + config.path_prefix + "/init",
|
|
354
364
|
readonly: 'readonly',
|
|
@@ -369,7 +379,7 @@ class PaymentController {
|
|
|
369
379
|
});
|
|
370
380
|
};
|
|
371
381
|
if (config.paytm_url) {
|
|
372
|
-
const checksum = await this.generateChecksum(params);
|
|
382
|
+
const checksum = await this.generateChecksum(params, req);
|
|
373
383
|
showConfirmation(checksum);
|
|
374
384
|
}
|
|
375
385
|
else if (config.razor_url || config.payu_url || config.open_money_url) {
|
|
@@ -456,8 +466,11 @@ class PaymentController {
|
|
|
456
466
|
}
|
|
457
467
|
else {
|
|
458
468
|
return (0, htmlhelper_1.renderView)(req, res, this.viewPath + "init.hbs", {
|
|
459
|
-
|
|
460
|
-
|
|
469
|
+
name: 'ASDASD',
|
|
470
|
+
enableConfirmationPage: config.enableConfirmationPage,
|
|
471
|
+
theme: config.theme,
|
|
472
|
+
path_prefix: config.path_prefix,
|
|
473
|
+
action: "/" + config.path_prefix + "/init",
|
|
461
474
|
readonly: '',
|
|
462
475
|
check: true,
|
|
463
476
|
BUTTON: 'Submit',
|
|
@@ -478,7 +491,6 @@ class PaymentController {
|
|
|
478
491
|
}
|
|
479
492
|
}
|
|
480
493
|
async updateTransaction(req, res) {
|
|
481
|
-
const config = this.config;
|
|
482
494
|
const vp = this.viewPath;
|
|
483
495
|
const callbacks = this.callbacks;
|
|
484
496
|
const orderToFind = req.body.ORDERID || req.body.ORDER_ID || req.body.ORDERId || (req.query && req.query.order_id) || req.body.ORDER_ID;
|
|
@@ -500,6 +512,7 @@ class PaymentController {
|
|
|
500
512
|
webhookUrl = null;
|
|
501
513
|
if (returnUrl === 'undefined')
|
|
502
514
|
returnUrl = null;
|
|
515
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, objForUpdate);
|
|
503
516
|
if (!objForUpdate) {
|
|
504
517
|
if (webhookUrl) {
|
|
505
518
|
try {
|
|
@@ -577,12 +590,24 @@ class PaymentController {
|
|
|
577
590
|
return res.redirect(`${returnUrl}${separator}status=${objForUpdate.status}&ORDERID=${objForUpdate.orderId}&TXNID=${objForUpdate.txnId}`);
|
|
578
591
|
}
|
|
579
592
|
(0, htmlhelper_1.renderView)(req, res, vp + "result.hbs", {
|
|
593
|
+
theme: config.theme,
|
|
580
594
|
path_prefix: config.path_prefix,
|
|
581
595
|
...objForUpdate
|
|
582
596
|
});
|
|
583
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
|
+
}
|
|
584
610
|
async callback(req, res) {
|
|
585
|
-
const config = this.config;
|
|
586
611
|
const payuInstance = this.payuInstance;
|
|
587
612
|
const openMoneyInstance = this.openMoneyInstance;
|
|
588
613
|
console.log("request_data ", req.originalUrl, JSON.stringify(req.body));
|
|
@@ -603,13 +628,15 @@ class PaymentController {
|
|
|
603
628
|
}
|
|
604
629
|
let result = false;
|
|
605
630
|
let isCancelled = false;
|
|
631
|
+
const objForUpdate = await this.getOrder(req);
|
|
632
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req, objForUpdate);
|
|
606
633
|
if (config.paytm_url) {
|
|
607
634
|
const checksumhash = req.body.CHECKSUMHASH;
|
|
608
635
|
if (checksumhash) {
|
|
609
636
|
result = await checksum_1.default.verifychecksum(req.body, config.KEY, checksumhash);
|
|
610
637
|
}
|
|
611
638
|
else {
|
|
612
|
-
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);
|
|
613
640
|
// Merge important fields when live status is available
|
|
614
641
|
if (liveStatus && typeof liveStatus === 'object') {
|
|
615
642
|
req.body.STATUS = liveStatus.STATUS || req.body.STATUS;
|
|
@@ -671,18 +698,41 @@ class PaymentController {
|
|
|
671
698
|
res.send({ message: "Something went wrong ! Please try again later .", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
672
699
|
}
|
|
673
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
|
+
}
|
|
674
723
|
async webhook(req, res) {
|
|
675
|
-
|
|
724
|
+
let config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
676
725
|
const payuInstance = this.payuInstance;
|
|
677
726
|
const openMoneyInstance = this.openMoneyInstance;
|
|
678
727
|
console.log("request_data ", req.originalUrl, JSON.stringify(req.body));
|
|
679
728
|
console.log("request_data rawBody", req.originalUrl, req.rawBody);
|
|
680
729
|
console.log("request_headers ", req.originalUrl, JSON.stringify(req.headers));
|
|
681
|
-
|
|
730
|
+
let serviceUsed = this.getServiceUsed(req, config);
|
|
731
|
+
if (serviceUsed === 'Paytm') {
|
|
682
732
|
await this.callback(req, res);
|
|
683
733
|
return;
|
|
684
734
|
}
|
|
685
|
-
if (
|
|
735
|
+
if (serviceUsed === 'Razorpay') {
|
|
686
736
|
const events = ["payment.captured", "payment.pending", "payment.failed"];
|
|
687
737
|
if (req.body.event && events.indexOf(req.body.event) > -1) {
|
|
688
738
|
if (req.body.payload &&
|
|
@@ -690,6 +740,8 @@ class PaymentController {
|
|
|
690
740
|
req.body.payload.payment.entity) {
|
|
691
741
|
const entity = req.body.payload.payment.entity;
|
|
692
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);
|
|
693
745
|
const razorpay_payment_id = entity.id;
|
|
694
746
|
const status = entity.status;
|
|
695
747
|
const event = req.body.event;
|
|
@@ -737,19 +789,21 @@ class PaymentController {
|
|
|
737
789
|
}
|
|
738
790
|
return;
|
|
739
791
|
}
|
|
740
|
-
if (
|
|
792
|
+
if (serviceUsed === 'PayU') {
|
|
741
793
|
payuInstance.processWebhook(req, res, this.updateTransaction);
|
|
742
794
|
return;
|
|
743
795
|
}
|
|
744
|
-
if (
|
|
796
|
+
if (serviceUsed === 'OpenMoney') {
|
|
745
797
|
openMoneyInstance.processWebhook(req, res, this.updateTransaction);
|
|
746
798
|
}
|
|
747
799
|
}
|
|
748
800
|
async createTxn(req, res) {
|
|
749
|
-
const config = this.
|
|
801
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
750
802
|
const razorPayInstance = this.razorPayInstance;
|
|
751
803
|
// mandayory field
|
|
752
|
-
const requiredFields = ['NAME', 'EMAIL',
|
|
804
|
+
const requiredFields = ['NAME', 'EMAIL',
|
|
805
|
+
// 'MOBILE_NO',
|
|
806
|
+
'TXN_AMOUNT', 'PRODUCT_NAME'];
|
|
753
807
|
const checkedFields = [];
|
|
754
808
|
let gotAllParams = true;
|
|
755
809
|
requiredFields.forEach(field => {
|
|
@@ -809,8 +863,9 @@ class PaymentController {
|
|
|
809
863
|
WEBHOOK_URL: txn.webhookUrl,
|
|
810
864
|
TXN_AMOUNT: txn.amount,
|
|
811
865
|
PRODUCT_NAME: txn.pname,
|
|
812
|
-
clientId: txn.clientId
|
|
813
|
-
|
|
866
|
+
clientId: txn.clientId,
|
|
867
|
+
CLIENT_ID: txn.clientId,
|
|
868
|
+
}), req);
|
|
814
869
|
txn.payurl = config.host_url + '/' + config.path_prefix + '/init?to=' + urlData64;
|
|
815
870
|
res.send(txn);
|
|
816
871
|
}
|
|
@@ -878,7 +933,7 @@ class PaymentController {
|
|
|
878
933
|
}
|
|
879
934
|
}
|
|
880
935
|
async status(req, res) {
|
|
881
|
-
const config = this.
|
|
936
|
+
const config = (0, buildConfig_1.withClientConfigOverrides)(this.baseConfig, req);
|
|
882
937
|
const callbacks = this.callbacks;
|
|
883
938
|
const payuInstance = this.payuInstance;
|
|
884
939
|
const openMoneyInstance = this.openMoneyInstance;
|
|
@@ -932,7 +987,7 @@ class PaymentController {
|
|
|
932
987
|
}
|
|
933
988
|
};
|
|
934
989
|
if (config.paytm_url) {
|
|
935
|
-
const paytmResponse = await this.getStatusFromPaytm(params, req.body.ORDER_ID);
|
|
990
|
+
const paytmResponse = await this.getStatusFromPaytm(params, req.body.ORDER_ID, req);
|
|
936
991
|
await onStatusUpdate(paytmResponse);
|
|
937
992
|
}
|
|
938
993
|
else if (config.razor_url) {
|
|
@@ -1009,10 +1064,11 @@ class PaymentController {
|
|
|
1009
1064
|
res.send(orderData);
|
|
1010
1065
|
}
|
|
1011
1066
|
}
|
|
1012
|
-
async getStatusFromPaytm(params, orderId) {
|
|
1013
|
-
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);
|
|
1014
1070
|
try {
|
|
1015
|
-
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 });
|
|
1016
1072
|
if (resp.status === 200) {
|
|
1017
1073
|
return resp.data;
|
|
1018
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