node-paytmpg 5.4.2 → 6.4.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/controllers/payment_controller.js +88 -63
- package/app/views/init.hbs +85 -80
- package/app/views/layouts/index.hbs +46 -51
- package/app/views/result.hbs +37 -47
- package/example.js +26 -34
- package/index.js +75 -15
- package/lib/config/buildConfig.js +113 -0
- package/lib/config/defaults.js +37 -0
- package/lib/config/validator.js +103 -0
- package/lib/services/database.service.js +153 -0
- package/lib/utils/id-generator.js +30 -0
- package/lib/utils/sanitizer.js +25 -0
- package/package.json +1 -1
- package/public/css/style.css +373 -203
|
@@ -10,6 +10,7 @@ var PayU = require('./adapters/payu')
|
|
|
10
10
|
const PaytmChecksum = require('./checksum/PaytmChecksum.js');
|
|
11
11
|
const { stat } = require('fs');
|
|
12
12
|
const { config } = require('process');
|
|
13
|
+
const path = require('path');
|
|
13
14
|
|
|
14
15
|
let loadingSVG = ` <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin:auto;background:#fff;display:block;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
|
15
16
|
<g transform="rotate(0 50 50)">
|
|
@@ -132,7 +133,10 @@ module.exports = function (app, callbacks) {
|
|
|
132
133
|
return text;
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
const viewRoot = config.templateDir
|
|
137
|
+
? config.templateDir
|
|
138
|
+
: path.join(__dirname, '..', 'views');
|
|
139
|
+
var vp = config.resolved_view_path || (viewRoot.endsWith(path.sep) ? viewRoot : viewRoot + path.sep)
|
|
136
140
|
|
|
137
141
|
module.home = (req, res) => {
|
|
138
142
|
|
|
@@ -144,7 +148,7 @@ module.exports = function (app, callbacks) {
|
|
|
144
148
|
|
|
145
149
|
module.init = async function (req, res) {
|
|
146
150
|
|
|
147
|
-
if (!req.body.ORDER_ID && !req.body.EMAIL && req.query
|
|
151
|
+
if (!req.body.ORDER_ID && !req.body.EMAIL && req.query?.to) {
|
|
148
152
|
let toData = JSON.parse(nodeBase64.decode(req.query.to));
|
|
149
153
|
req.body.NAME = toData.NAME
|
|
150
154
|
req.body.EMAIL = toData.EMAIL
|
|
@@ -478,8 +482,9 @@ module.exports = function (app, callbacks) {
|
|
|
478
482
|
openMoneyInstance.renderError(params, e, res)
|
|
479
483
|
}
|
|
480
484
|
}
|
|
481
|
-
if (callbacks
|
|
485
|
+
if (callbacks && typeof callbacks.onStart === 'function') {
|
|
482
486
|
callbacks.onStart(params['ORDER_ID'], params);
|
|
487
|
+
}
|
|
483
488
|
}
|
|
484
489
|
else if ((req.body.ORDER_ID !== undefined && req.body.ORDER_ID.length > 2) || gotAllParams) {
|
|
485
490
|
|
|
@@ -512,7 +517,7 @@ module.exports = function (app, callbacks) {
|
|
|
512
517
|
let showConfirmation =
|
|
513
518
|
function (err, checksum) {
|
|
514
519
|
res.render(vp + "init.hbs", {
|
|
515
|
-
action:
|
|
520
|
+
action:"/"+ config.path_prefix+"/init",
|
|
516
521
|
readonly: 'readonly',
|
|
517
522
|
BUTTON: 'Pay',
|
|
518
523
|
NAME: params['NAME'],
|
|
@@ -644,7 +649,7 @@ module.exports = function (app, callbacks) {
|
|
|
644
649
|
|
|
645
650
|
res.render(vp + "init.hbs", {
|
|
646
651
|
|
|
647
|
-
action:
|
|
652
|
+
action:"/"+ config.path_prefix+"/init",
|
|
648
653
|
readonly: '',
|
|
649
654
|
check: true,
|
|
650
655
|
BUTTON: 'Submit',
|
|
@@ -668,77 +673,96 @@ module.exports = function (app, callbacks) {
|
|
|
668
673
|
|
|
669
674
|
}
|
|
670
675
|
|
|
671
|
-
function updateTransaction(req, res) {
|
|
672
|
-
var
|
|
676
|
+
async function updateTransaction(req, res) {
|
|
677
|
+
var orderToFind = req.body.ORDERID || req.body.ORDER_ID || req.body.ORDERId || (req.query && req.query.order_id) || req.body.ORDER_ID;
|
|
678
|
+
var myquery = { orderId: orderToFind };
|
|
679
|
+
|
|
680
|
+
let objForUpdate = null;
|
|
681
|
+
try {
|
|
682
|
+
// try default
|
|
683
|
+
objForUpdate = await Transaction.findOne(myquery).catch(() => null);
|
|
684
|
+
// try id
|
|
685
|
+
if (!objForUpdate) objForUpdate = await Transaction.findOne({ id: orderToFind }).catch(() => null);
|
|
686
|
+
// try uppercase key
|
|
687
|
+
if (!objForUpdate) objForUpdate = await Transaction.findOne({ ORDERID: orderToFind }).catch(() => null);
|
|
688
|
+
} catch (e) {
|
|
689
|
+
// ignore lookup errors
|
|
690
|
+
objForUpdate = objForUpdate || null;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
let returnUrl = objForUpdate ? objForUpdate.returnUrl : null;
|
|
694
|
+
if (returnUrl == 'undefined') returnUrl = undefined;
|
|
673
695
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
returnUrl =
|
|
696
|
+
if (!objForUpdate) {
|
|
697
|
+
if (returnUrl) {
|
|
698
|
+
let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
699
|
+
returnUrl = returnUrl + separator + 'status=FAILED&message=txn_not_found&ORDERID=' + req.body.ORDERID;
|
|
700
|
+
return res.redirect(returnUrl);
|
|
678
701
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
702
|
+
return res.send({ message: "Transaction Not Found !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (objForUpdate.status != ("INITIATED") && objForUpdate.status != ("TXN_PENDING") && objForUpdate.status != ("PENDING")) {
|
|
706
|
+
objForUpdate.readonly = "readonly";
|
|
707
|
+
objForUpdate.action = config.homepage;
|
|
708
|
+
if (returnUrl) {
|
|
709
|
+
let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
710
|
+
returnUrl = returnUrl + separator + 'status=' + objForUpdate.status + '&ORDERID=' + objForUpdate.orderId + '&TXNID=' + objForUpdate.TXNID;
|
|
711
|
+
return res.redirect(returnUrl);
|
|
687
712
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
713
|
+
else {
|
|
714
|
+
return res.render(vp + "result.hbs", objForUpdate);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (req.body.status == "paid" && !req.body.STATUS) req.body.STATUS = "TXN_SUCCESS";
|
|
719
|
+
objForUpdate.status = req.body.STATUS;
|
|
720
|
+
objForUpdate.TXNID = req.body.TXNID;
|
|
721
|
+
objForUpdate.extra = JSON.stringify(req.body);
|
|
722
|
+
|
|
723
|
+
var newvalues = { $set: objForUpdate };
|
|
724
|
+
Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
|
|
725
|
+
if (err) {
|
|
691
726
|
if (returnUrl) {
|
|
692
727
|
let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
693
|
-
returnUrl = returnUrl + separator + 'status=
|
|
728
|
+
returnUrl = returnUrl + separator + 'status=FAILED&message=update_error&ORDERID=' + req.body.ORDERID;
|
|
694
729
|
return res.redirect(returnUrl);
|
|
695
730
|
}
|
|
696
|
-
|
|
697
|
-
res.render(vp + "result.hbs", objForUpdate);
|
|
698
|
-
}
|
|
699
|
-
console.log("Transaction already processed ", req.body.ORDERID)
|
|
700
|
-
// res.send({ message: "Transaction already processed", status: objForUpdate.status, ORDERID: objForUpdate.orderId, TXNID: objForUpdate.TXNID, TXNID: req.body.TXNID })
|
|
701
|
-
return;
|
|
731
|
+
return res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID });
|
|
702
732
|
}
|
|
703
|
-
if (req.body.status == "paid" && !req.body.STATUS) {
|
|
704
|
-
req.body.STATUS = "TXN_SUCCESS"
|
|
705
|
-
}
|
|
706
|
-
objForUpdate.status = req.body.STATUS;
|
|
707
|
-
objForUpdate.TXNID = req.body.TXNID;
|
|
708
|
-
objForUpdate.extra = JSON.stringify(req.body);
|
|
709
|
-
|
|
710
|
-
var newvalues = { $set: objForUpdate };
|
|
711
|
-
Transaction.updateOne(myquery, newvalues, function (err, saveRes) {
|
|
712
|
-
|
|
713
|
-
if (err) {
|
|
714
|
-
if (returnUrl) {
|
|
715
|
-
let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
716
|
-
returnUrl = returnUrl + separator + 'status=FAILED&message=update_error&ORDERID=' + req.body.ORDERID;
|
|
717
|
-
return res.redirect(returnUrl);
|
|
718
|
-
}
|
|
719
|
-
res.send({ message: "Error Occured !", ORDERID: req.body.ORDERID, TXNID: req.body.TXNID })
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
733
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}, usingMultiDbOrm ? Transaction : undefined)
|
|
734
|
+
if (callbacks && typeof callbacks.onFinish === 'function') {
|
|
735
|
+
callbacks.onFinish(req.body.ORDERID, objForUpdate);
|
|
736
|
+
}
|
|
737
|
+
objForUpdate.readonly = "readonly";
|
|
738
|
+
objForUpdate.action = config.homepage;
|
|
739
|
+
if (returnUrl) {
|
|
740
|
+
let separator = returnUrl.indexOf('?') > -1 ? '&' : '?';
|
|
741
|
+
returnUrl = returnUrl + separator + 'status=' + objForUpdate.status + '&ORDERID=' + objForUpdate.orderId + '&TXNID=' + objForUpdate.TXNID;
|
|
742
|
+
return res.redirect(returnUrl);
|
|
743
|
+
}
|
|
744
|
+
res.render(vp + "result.hbs", objForUpdate);
|
|
745
|
+
});
|
|
737
746
|
}
|
|
738
747
|
|
|
739
748
|
module.callback = async (req, res) => {
|
|
740
749
|
console.log("request_data ", req.originalUrl, JSON.stringify(req.body))
|
|
741
750
|
|
|
751
|
+
// Normalize common order id and txn id field names (support ORDER_ID, ORDERID, etc.)
|
|
752
|
+
try {
|
|
753
|
+
if ((!req.body.ORDERID || req.body.ORDERID === '') && req.body.ORDER_ID) {
|
|
754
|
+
req.body.ORDERID = req.body.ORDER_ID;
|
|
755
|
+
}
|
|
756
|
+
if ((!req.body.TXNID || req.body.TXNID === '') && req.body.TXN_ID) {
|
|
757
|
+
req.body.TXNID = req.body.TXN_ID;
|
|
758
|
+
}
|
|
759
|
+
if ((!req.body.ORDERID || req.body.ORDERID === '') && req.query && req.query.order_id) {
|
|
760
|
+
req.body.ORDERID = req.query.order_id;
|
|
761
|
+
}
|
|
762
|
+
} catch (e) {
|
|
763
|
+
// ignore
|
|
764
|
+
}
|
|
765
|
+
|
|
742
766
|
var result = false;
|
|
743
767
|
let isCancelled = false;
|
|
744
768
|
if (config.paytm_url) {
|
|
@@ -1050,8 +1074,9 @@ module.exports = function (app, callbacks) {
|
|
|
1050
1074
|
res.send({ message: "Error Occured !", ORDERID: paytmResponse.ORDERID, TXNID: paytmResponse.TXNID })
|
|
1051
1075
|
}
|
|
1052
1076
|
else {
|
|
1053
|
-
if (callbacks
|
|
1077
|
+
if (callbacks && typeof callbacks.onFinish === 'function') {
|
|
1054
1078
|
callbacks.onFinish(req.body.ORDER_ID, orderData);
|
|
1079
|
+
}
|
|
1055
1080
|
res.send(paytmResponse)
|
|
1056
1081
|
}
|
|
1057
1082
|
});
|
package/app/views/init.hbs
CHANGED
|
@@ -1,93 +1,98 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<div class="checkout-grid">
|
|
2
|
+
<aside class="product-panel card">
|
|
3
|
+
{{#if productImage}}
|
|
4
|
+
<div class="product-preview">
|
|
5
|
+
<img src="{{productImage}}" alt="{{PRODUCT_NAME}}" style="width:100%;height:100%;object-fit:cover" />
|
|
6
|
+
</div>
|
|
7
|
+
{{/if}}
|
|
8
|
+
|
|
9
|
+
<div class="product-price">
|
|
10
|
+
<div class="pill">Product</div>
|
|
11
|
+
<div class="price">{{PRODUCT_NAME}}</div>
|
|
12
|
+
</div>
|
|
13
|
+
{{#if PRODUCT_DESC}}
|
|
14
|
+
<p class="helper" style="margin-top:14px;">{{PRODUCT_DESC}}</p>
|
|
15
|
+
{{/if}}
|
|
16
|
+
</aside>
|
|
17
|
+
|
|
18
|
+
<section class="form-panel card">
|
|
19
|
+
<header class="card__header">
|
|
20
|
+
<div>
|
|
21
|
+
<p class="eyebrow">Payment</p>
|
|
22
|
+
<h2>Review & pay</h2>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="amount-chip">₹ {{TXN_AMOUNT}}</div>
|
|
25
|
+
</header>
|
|
13
26
|
|
|
14
|
-
|
|
15
|
-
<li class="form-list__row">
|
|
16
|
-
<label>E-Mail</label>
|
|
17
|
-
<input type="text" name="EMAIL" required="" value="{{EMAIL}}" {{readonly}} />
|
|
18
|
-
</li>
|
|
27
|
+
<p class="helper">Your payment is processed over an encrypted channel. Double-check the details before you continue.</p>
|
|
19
28
|
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
29
|
+
<form action="{{action}}" method="POST" class="form-grid" id="payment-form">
|
|
30
|
+
{{#if check}}
|
|
31
|
+
<label class="field">
|
|
32
|
+
<span class="field__label">Full Name</span>
|
|
33
|
+
<input type="text" name="NAME" required value="{{NAME}}" {{readonly}} />
|
|
34
|
+
</label>
|
|
35
|
+
{{else}}
|
|
36
|
+
<input type="hidden" name="NAME" required value="{{NAME}}" {{readonly}} />
|
|
37
|
+
{{/if}}
|
|
24
38
|
|
|
39
|
+
<label class="field">
|
|
40
|
+
<span class="field__label">Email address</span>
|
|
41
|
+
<input type="email" name="EMAIL" required value="{{EMAIL}}" {{readonly}} />
|
|
42
|
+
</label>
|
|
25
43
|
|
|
26
|
-
|
|
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>
|
|
27
48
|
|
|
28
49
|
{{#if check}}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
</label>
|
|
34
|
-
<input type="text" name="PRODUCT_NAME" required="" value="{{PRODUCT_NAME}}" {{readonly}} />
|
|
35
|
-
</div>
|
|
50
|
+
<label class="field">
|
|
51
|
+
<span class="field__label">Product</span>
|
|
52
|
+
<input type="text" name="PRODUCT_NAME" required value="{{PRODUCT_NAME}}" {{readonly}} />
|
|
53
|
+
</label>
|
|
36
54
|
{{else}}
|
|
37
|
-
<input type="hidden" name="PRODUCT_NAME" required
|
|
55
|
+
<input type="hidden" name="PRODUCT_NAME" required value="{{PRODUCT_NAME}}" {{readonly}} />
|
|
38
56
|
{{/if}}
|
|
39
57
|
|
|
40
|
-
|
|
41
|
-
|
|
58
|
+
<label class="field">
|
|
59
|
+
<span class="field__label">Amount</span>
|
|
60
|
+
<input type="text" name="TXN_AMOUNT" required value="{{TXN_AMOUNT}}" {{readonly}} />
|
|
61
|
+
</label>
|
|
42
62
|
|
|
43
|
-
<div>
|
|
44
|
-
<label>
|
|
45
|
-
|
|
63
|
+
<div style="grid-column:1 / -1; display:flex; gap:10px; align-items:center; justify-content:space-between;">
|
|
64
|
+
<label class="checkbox" style="margin:0">
|
|
65
|
+
<input type="checkbox" name="save_cc" checked="checked" />
|
|
66
|
+
<span>I agree to the merchant terms</span>
|
|
46
67
|
</label>
|
|
47
|
-
<
|
|
68
|
+
<div style="font-size:12px; color:rgba(255,255,255,0.6)">Secure by PCI-DSS</div>
|
|
48
69
|
</div>
|
|
49
70
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<input type="hidden" name="MID" value="{{MID}}" {{readonly}}>
|
|
80
|
-
<input type="hidden" name="WEBSITE" value="{{WEBSITE}}" {{readonly}}>
|
|
81
|
-
<input type="hidden" name="ORDER_ID" value="{{ORDER_ID}}" {{readonly}}>
|
|
82
|
-
<input type="hidden" name="CUST_ID" value="{{CUST_ID}}" {{readonly}}>
|
|
83
|
-
<input type="hidden" name="INDUSTRY_TYPE_ID" value="{{INDUSTRY_TYPE_ID}}" {{readonly}}>
|
|
84
|
-
<input type="hidden" name="CHANNEL_ID" value="{{CHANNEL_ID}}" {{readonly}}>
|
|
85
|
-
<input type="hidden" name="CALLBACK_URL" value="{{CALLBACK_URL}}" {{readonly}}>
|
|
86
|
-
<input type="hidden" name="CHECKSUMHASH" value="{{CHECKSUMHASH}}" {{readonly}}>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
</form>
|
|
71
|
+
<button id="pay-button" type="submit" class="button">{{BUTTON}}</button>
|
|
72
|
+
|
|
73
|
+
<input type="hidden" name="MID" value="{{MID}}" {{readonly}} />
|
|
74
|
+
<input type="hidden" name="WEBSITE" value="{{WEBSITE}}" {{readonly}} />
|
|
75
|
+
<input type="hidden" name="ORDER_ID" value="{{ORDER_ID}}" {{readonly}} />
|
|
76
|
+
<input type="hidden" name="CUST_ID" value="{{CUST_ID}}" {{readonly}} />
|
|
77
|
+
<input type="hidden" name="INDUSTRY_TYPE_ID" value="{{INDUSTRY_TYPE_ID}}" {{readonly}} />
|
|
78
|
+
<input type="hidden" name="CHANNEL_ID" value="{{CHANNEL_ID}}" {{readonly}} />
|
|
79
|
+
<input type="hidden" name="CALLBACK_URL" value="{{CALLBACK_URL}}" {{readonly}} />
|
|
80
|
+
<input type="hidden" name="CHECKSUMHASH" value="{{CHECKSUMHASH}}" {{readonly}} />
|
|
81
|
+
</form>
|
|
82
|
+
</section>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
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> --}}
|
|
@@ -1,58 +1,53 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html>
|
|
3
|
-
|
|
2
|
+
<html lang="en">
|
|
4
3
|
<head>
|
|
5
|
-
<meta charset="UTF-8"
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
<link
|
|
11
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/
|
|
12
|
-
|
|
13
|
-
<link rel="
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</head>
|
|
17
|
-
|
|
18
|
-
<body>
|
|
19
|
-
|
|
20
|
-
<div class="modal">
|
|
21
|
-
<div class="modal__container">
|
|
22
|
-
|
|
23
|
-
<div class="modal__content">
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{{{body}}}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
</div> <!-- END: .modal__content -->
|
|
33
|
-
</div> <!-- END: .modal__container -->
|
|
34
|
-
</div> <!-- END: .modal -->
|
|
35
|
-
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js'></script>
|
|
36
|
-
|
|
37
|
-
<script src="js/index.js"></script>
|
|
38
|
-
|
|
39
|
-
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{brand}} · Secure Checkout</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
|
10
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" />
|
|
11
|
+
<link rel="stylesheet" href="css/style.css" />
|
|
12
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%3E%3Crect%20width='16'%20height='16'%20rx='3'%20fill='%23ff5722'/%3E%3C/svg%3E" />
|
|
13
|
+
<link rel="shortcut icon" href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%3E%3Crect%20width='16'%20height='16'%20rx='3'%20fill='%23ff5722'/%3E%3C/svg%3E" />
|
|
40
14
|
<style>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
color:
|
|
49
|
-
|
|
50
|
-
input {
|
|
51
|
-
color: #cc0f0f;
|
|
15
|
+
:root {
|
|
16
|
+
--color-primary: {{theme.primary}};
|
|
17
|
+
--color-accent: {{theme.accent}};
|
|
18
|
+
--color-surface: {{theme.surface}};
|
|
19
|
+
--color-text: {{theme.text}};
|
|
20
|
+
--color-success: {{theme.success}};
|
|
21
|
+
--color-danger: {{theme.danger}};
|
|
22
|
+
--color-outline: rgba(255,255,255,0.08);
|
|
23
|
+
--radius: 14px;
|
|
52
24
|
}
|
|
53
|
-
|
|
54
|
-
*/
|
|
55
25
|
</style>
|
|
26
|
+
</head>
|
|
27
|
+
<body class="theme-{{themeName}}">
|
|
28
|
+
<div class="shell">
|
|
29
|
+
<header class="shell__header">
|
|
30
|
+
<div class="brand">
|
|
31
|
+
{{#if logo}}
|
|
32
|
+
<span class="brand__mark brand__mark--img"><img src="{{logo}}" alt="{{brand}} logo"></span>
|
|
33
|
+
{{else}}
|
|
34
|
+
<span class="brand__mark">{{brand}}</span>
|
|
35
|
+
{{/if}}
|
|
36
|
+
<div class="brand__text">
|
|
37
|
+
<div class="brand__title">{{brand}}</div>
|
|
38
|
+
<div class="brand__meta">Secure checkout</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="pill">Encrypted</div>
|
|
42
|
+
</header>
|
|
43
|
+
|
|
44
|
+
<main class="shell__content">
|
|
45
|
+
{{{body}}}
|
|
46
|
+
</main>
|
|
47
|
+
|
|
48
|
+
<footer class="shell__footer">
|
|
49
|
+
<span>Protected by {{brand}}</span>
|
|
50
|
+
</footer>
|
|
51
|
+
</div>
|
|
56
52
|
</body>
|
|
57
|
-
|
|
58
53
|
</html>
|
package/app/views/result.hbs
CHANGED
|
@@ -1,50 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
<section class="card" style="text-align:center; padding:36px 28px;">
|
|
2
|
+
<div style="display:flex; flex-direction:column; align-items:center; gap:18px;" class="status-{{status}}">
|
|
3
|
+
<div style="width:84px; height:84px; border-radius:999px; display:grid; place-items:center; background:rgba(255,255,255,0.03);">
|
|
4
|
+
<svg class="icon-success" width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M20 6L9 17l-5-5" stroke="#4cff9f" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
5
|
+
<svg class="icon-fail" width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="display:none"><circle cx="12" cy="12" r="9" stroke="#ff9b9b" stroke-width="2.2"/><path d="M12 8v5" stroke="#ff9b9b" stroke-width="2.2" stroke-linecap="round"/><path d="M12 16h.01" stroke="#ff9b9b" stroke-width="2.2" stroke-linecap="round"/></svg>
|
|
6
|
+
</div>
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
8
|
+
<div>
|
|
9
|
+
<h2 style="margin:0">Payment</h2>
|
|
10
|
+
<p class="helper" style="margin-top:6px">{{message}}</p>
|
|
11
|
+
</div>
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
<div class="result-grid" style="max-width:560px; width:100%;">
|
|
14
|
+
<div class="result-item">
|
|
15
|
+
<p class="label">Order ID</p>
|
|
16
|
+
<p class="value">{{orderId}}</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="result-item">
|
|
19
|
+
<p class="label">Transaction ID</p>
|
|
20
|
+
<p class="value">{{TXNID}}</p>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="result-item">
|
|
23
|
+
<p class="label">Amount</p>
|
|
24
|
+
<p class="value">{{amount}}</p>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="result-item">
|
|
27
|
+
<p class="label">Date & Time</p>
|
|
28
|
+
<p class="value">{{date}}</p>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
11
31
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<li class="form-list__row">
|
|
23
|
-
<label>Phone</label>
|
|
24
|
-
<input type="text" name="MOBILE_NO" required="" value="{{phone}}" {{readonly}}/>
|
|
25
|
-
</li>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<li class="form-list__row form-list__row--inline">
|
|
29
|
-
|
|
30
|
-
<div>
|
|
31
|
-
<label>
|
|
32
|
-
Amount
|
|
33
|
-
</label>
|
|
34
|
-
<input type="text" name="TXN_AMOUNT" required="" value="{{amount}}" {{readonly}} />
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
</li>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<li>
|
|
42
|
-
<button type="submit" class="button">DONE</button>
|
|
43
|
-
</li>
|
|
44
|
-
</ul>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</form>
|
|
32
|
+
<div style="display:flex; gap:12px; margin-top:8px; width:100%; justify-content:center;">
|
|
33
|
+
<div class="success-buttons" style="display:flex; gap:12px;">
|
|
34
|
+
<a href="{{receiptUrl}}" class="button" style="text-decoration:none;">Download Receipt</a>
|
|
35
|
+
<form action="{{homeAction}}" method="GET" style="display:inline"><button type="submit" class="button">Done</button></form>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</section>
|