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.
@@ -41,10 +41,15 @@
41
41
  <input type="email" name="EMAIL" required value="{{EMAIL}}" {{readonly}} />
42
42
  </label>
43
43
 
44
- <label class="field">
45
- <span class="field__label">Phone</span>
46
- <input type="text" name="MOBILE_NO" required value="{{MOBILE_NO}}" {{readonly}} />
47
- </label>
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
- {{!-- <script>
86
- (function(){
87
- if (window.__npPayBtnBound) return;
88
- window.__npPayBtnBound = true;
89
- const payBtn = document.getElementById('pay-button');
90
- if (payBtn) {
91
- payBtn.addEventListener('click', () => {
92
- payBtn.setAttribute('data-loading', 'true');
93
- payBtn.setAttribute('disabled', 'disabled');
94
- setTimeout(() => payBtn.innerText = 'Processing…', 10);
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 themeName}}{{themeName}}{{else}}dark{{/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.theme_color
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 config;
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(config: NPConfig, db: MultiDbORM, callbacks?: any, tableNames?: NPTableNames);
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(config, db, callbacks, tableNames) {
64
+ constructor(baseConfig, db, callbacks, tableNames) {
64
65
  this.tableNames = { USER: 'npusers', TRANSACTION: 'nptransactions' };
65
66
  this.viewPath = '';
66
- this.config = config;
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(config);
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(this.config.SECRET || this.config.KEY || '');
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 secret = String(this.config.SECRET || this.config.KEY || '');
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, this.config.KEY, (err, cs) => {
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', 'MOBILE_NO', 'NAME', 'EMAIL'];
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 (this.config.paytm_url) {
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 (this.config.mode) {
253
- initTxnbody["enablePaymentMode"] = JSON.parse(this.config.mode);
259
+ if (config.mode) {
260
+ initTxnbody["enablePaymentMode"] = JSON.parse(config.mode);
254
261
  }
255
- let checksum = await PaytmChecksum_1.default.generateSignature(JSON.stringify(initTxnbody), this.config.KEY);
256
- let initTxnUrl = this.config.paytm_url + `/theia/api/v1/initiateTransaction?mid=${params['MID']}&orderId=${params['ORDER_ID']}`;
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, this.config);
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 (this.config.razor_url) {
305
- return (0, htmlhelper_1.renderRazorpayCheckout)(req, res, params, this.config, loadingsvg_1.LoadingSVG);
311
+ else if (config.razor_url) {
312
+ return (0, htmlhelper_1.renderRazorpayCheckout)(req, res, params, config, loadingsvg_1.LoadingSVG);
306
313
  }
307
- else if (this.config.payu_url) {
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 (this.config.open_money_url) {
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
- path_prefix: this.config.path_prefix,
460
- action: "/" + this.config.path_prefix + "/init",
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
- const config = this.config;
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
- if (config.paytm_url) {
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 (config.razor_url) {
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 (config.payu_url) {
792
+ if (serviceUsed === 'PayU') {
741
793
  payuInstance.processWebhook(req, res, this.updateTransaction);
742
794
  return;
743
795
  }
744
- if (config.open_money_url) {
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.config;
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', 'MOBILE_NO', 'TXN_AMOUNT', 'PRODUCT_NAME'];
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.config;
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(`${this.config.paytm_url}/order/status`, { MID: this.config.MID, ORDERID: orderId, CHECKSUMHASH: checksum });
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 { NPConfig } from "../models";
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([
@@ -41,10 +41,15 @@
41
41
  <input type="email" name="EMAIL" required value="{{EMAIL}}" {{readonly}} />
42
42
  </label>
43
43
 
44
- <label class="field">
45
- <span class="field__label">Phone</span>
46
- <input type="text" name="MOBILE_NO" required value="{{MOBILE_NO}}" {{readonly}} />
47
- </label>
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
- {{!-- <script>
86
- (function(){
87
- if (window.__npPayBtnBound) return;
88
- window.__npPayBtnBound = true;
89
- const payBtn = document.getElementById('pay-button');
90
- if (payBtn) {
91
- payBtn.addEventListener('click', () => {
92
- payBtn.setAttribute('data-loading', 'true');
93
- payBtn.setAttribute('disabled', 'disabled');
94
- setTimeout(() => payBtn.innerText = 'Processing…', 10);
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 themeName}}{{themeName}}{{else}}dark{{/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
- const theme = config.theme || {};
71
+ let _client = (0, buildConfig_1.withClientConfigOverrides)(config, req);
72
+ const theme = _client.theme || {};
72
73
  res.locals.theme = {
73
- primary: theme.primary || '#2f8bff',
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 = config.themeName || theme.name || 'dark';
81
- res.locals.brand = config.brand || 'Secure Pay';
82
- res.locals.logo = config.logo || '';
83
- res.locals.path_prefix = config.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-paytmpg",
3
- "version": "7.4.2",
3
+ "version": "7.5.3",
4
4
  "description": "Payment Gateway Integration using NodeJS",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-paytmpg",
3
- "version": "7.4.2",
3
+ "version": "7.5.3",
4
4
  "description": "Payment Gateway Integration using NodeJS",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",