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.
@@ -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,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 (this.config.paytm_url) {
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 (this.config.mode) {
255
- initTxnbody["enablePaymentMode"] = JSON.parse(this.config.mode);
259
+ if (config.mode) {
260
+ initTxnbody["enablePaymentMode"] = JSON.parse(config.mode);
256
261
  }
257
- let checksum = await PaytmChecksum_1.default.generateSignature(JSON.stringify(initTxnbody), this.config.KEY);
258
- 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']}`;
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, this.config);
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 (this.config.razor_url) {
307
- 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);
308
313
  }
309
- else if (this.config.payu_url) {
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 (this.config.open_money_url) {
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
- path_prefix: this.config.path_prefix,
462
- 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",
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
- const config = this.config;
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
- if (config.paytm_url) {
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 (config.razor_url) {
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 (config.payu_url) {
792
+ if (serviceUsed === 'PayU') {
743
793
  payuInstance.processWebhook(req, res, this.updateTransaction);
744
794
  return;
745
795
  }
746
- if (config.open_money_url) {
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.config;
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.config;
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(`${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 });
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 { 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.3",
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.3",
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",