node-paytmpg 6.4.7 → 7.0.1

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.
Files changed (58) hide show
  1. package/README.MD +132 -182
  2. package/app/views/layouts/index.hbs +7 -7
  3. package/app/views/result.hbs +1 -1
  4. package/dist/app/controllers/adapters/open_money.js +400 -0
  5. package/dist/app/controllers/adapters/paytm.js +34 -0
  6. package/{app → dist/app}/controllers/adapters/payu.js +208 -239
  7. package/dist/app/controllers/checksum/PaytmChecksum.js +118 -0
  8. package/dist/app/controllers/checksum/checksum.js +158 -0
  9. package/dist/app/controllers/checksum/crypt.js +117 -0
  10. package/dist/app/controllers/checksum/server.js +130 -0
  11. package/dist/app/controllers/payment.controller.js +985 -0
  12. package/dist/app/controllers/static/loadingsvg.js +54 -0
  13. package/dist/app/controllers/user.controller.js +53 -0
  14. package/dist/app/models/index.js +2 -0
  15. package/dist/app/routes/payment_route.js +46 -0
  16. package/dist/app/utils/buildConfig.js +210 -0
  17. package/dist/app/utils/utils.js +20 -0
  18. package/dist/app/views/home.hbs +22 -0
  19. package/dist/app/views/init.hbs +98 -0
  20. package/dist/app/views/layouts/index.hbs +53 -0
  21. package/dist/app/views/result.hbs +33 -0
  22. package/dist/index.js +119 -0
  23. package/dist/package.json +67 -0
  24. package/dist/public/css/style.css +455 -0
  25. package/dist/public/js/index.js +283 -0
  26. package/dist/public/layer_checkout.js +38 -0
  27. package/dist/public/pay.png +0 -0
  28. package/dist/public/start.png +0 -0
  29. package/dist/public/start2.png +0 -0
  30. package/dist/public/stat.png +0 -0
  31. package/dist/public/test.html +24 -0
  32. package/dist/public/test.html~ +24 -0
  33. package/package.json +29 -6
  34. package/public/test.html~ +24 -0
  35. package/.github/workflows/codeql-analysis.yml +0 -71
  36. package/.github/workflows/nodejs.yml +0 -24
  37. package/.github/workflows/npm-publish.yml +0 -23
  38. package/Dockerfile +0 -9
  39. package/app/controllers/adapters/open_money.js +0 -515
  40. package/app/controllers/checksum/PaytmChecksum.js +0 -94
  41. package/app/controllers/checksum/checksum.js +0 -154
  42. package/app/controllers/checksum/crypt.js +0 -98
  43. package/app/controllers/checksum/server.js +0 -132
  44. package/app/controllers/np_user.controller.js +0 -89
  45. package/app/controllers/payment_controller.js +0 -1295
  46. package/app/models/np_multidbplugin.js +0 -111
  47. package/app/models/np_transaction.model.js +0 -16
  48. package/app/models/np_user.model.js +0 -12
  49. package/app/routes/payment_route.js +0 -73
  50. package/app.yaml +0 -18
  51. package/example.js +0 -34
  52. package/index.js +0 -90
  53. package/lib/config/buildConfig.js +0 -113
  54. package/lib/config/defaults.js +0 -37
  55. package/lib/config/validator.js +0 -103
  56. package/lib/services/database.service.js +0 -153
  57. package/lib/utils/id-generator.js +0 -30
  58. package/lib/utils/sanitizer.js +0 -25
package/dist/index.js ADDED
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.attachBodyParser = attachBodyParser;
21
+ exports.createPaymentMiddleware = createPaymentMiddleware;
22
+ const express_1 = __importDefault(require("express"));
23
+ const path_1 = __importDefault(require("path"));
24
+ const body_parser_1 = __importDefault(require("body-parser"));
25
+ const express_handlebars_1 = __importDefault(require("express-handlebars"));
26
+ const payment_controller_1 = require("./app/controllers/payment.controller");
27
+ const buildConfig_1 = require("./app/utils/buildConfig");
28
+ __exportStar(require("./app/models"), exports);
29
+ function attachBodyParser(app, userConfig = {}) {
30
+ const config = (0, buildConfig_1.buildConfig)(userConfig);
31
+ const saveRawBody = (req, res, buf) => {
32
+ req.rawBody = buf && buf.toString();
33
+ };
34
+ app.use(body_parser_1.default.urlencoded({ extended: true }));
35
+ app.use(body_parser_1.default.json({ verify: saveRawBody }));
36
+ const viewRoot = config.templateDir
37
+ ? path_1.default.resolve(config.templateDir)
38
+ : path_1.default.join(__dirname, 'app', 'views');
39
+ const layoutPath = path_1.default.join(viewRoot, 'layouts', 'index.hbs');
40
+ app.engine('hbs', (0, express_handlebars_1.default)({
41
+ extname: 'hbs',
42
+ defaultLayout: layoutPath,
43
+ helpers: {
44
+ theme_color: () => config.theme_color,
45
+ logo: () => config.logo,
46
+ brand: () => config.brand || 'Secure Pay',
47
+ },
48
+ }));
49
+ app.set('view engine', 'handlebars');
50
+ app.set('attachBodyParser', true);
51
+ }
52
+ function createPaymentMiddleware(app, userConfig, db, callbacks, authenticationMiddleware, tableNames) {
53
+ //check attachBodyParser
54
+ if (!app.get('attachBodyParser')) {
55
+ console.error('!!! Warning from node-paytmpg !!!: Body parser not attached by calling `attachBodyParser(app, config)`. Attaching default body parser. Please attach your own body parser if you have custom requirements. but make sure `rawBody` is attached to the request object for webhook verification.');
56
+ attachBodyParser(app, userConfig);
57
+ }
58
+ const config = (0, buildConfig_1.buildConfig)(userConfig);
59
+ const subApp = express_1.default.Router();
60
+ if (!authenticationMiddleware) {
61
+ authenticationMiddleware = (req, res, next) => next();
62
+ }
63
+ const saveRawBody = (req, res, buf) => {
64
+ req.rawBody = buf && buf.toString();
65
+ };
66
+ subApp.use(body_parser_1.default.urlencoded({ extended: true }));
67
+ subApp.use(body_parser_1.default.json({ verify: saveRawBody }));
68
+ callbacks = callbacks || config.callbacks;
69
+ const pc = new payment_controller_1.PaymentController(config, db, callbacks, tableNames);
70
+ subApp.use((req, res, next) => {
71
+ const theme = config.theme || {};
72
+ res.locals.theme = {
73
+ primary: theme.primary || '#2f8bff',
74
+ accent: theme.accent || '#5ce1e6',
75
+ surface: theme.surface || '#0f1021',
76
+ text: theme.text || '#e9ecf2',
77
+ success: theme.success || '#24cf5f',
78
+ danger: theme.danger || '#ff6b6b',
79
+ };
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;
84
+ next();
85
+ });
86
+ subApp.use((req, res, next) => {
87
+ console.log('Received request at', req.originalUrl);
88
+ next();
89
+ });
90
+ subApp.all('/init', authenticationMiddleware, (req, res) => {
91
+ pc.init(req, res);
92
+ });
93
+ subApp.all('/callback', authenticationMiddleware, (req, res) => {
94
+ pc.callback(req, res);
95
+ });
96
+ subApp.all('/api/webhook', authenticationMiddleware, (req, res) => {
97
+ pc.webhook(req, res);
98
+ });
99
+ subApp.all('/api/status', authenticationMiddleware, (req, res) => {
100
+ pc.status(req, res);
101
+ });
102
+ subApp.all('/api/transactions', authenticationMiddleware, (req, res) => {
103
+ pc.getTransactions(req, res);
104
+ });
105
+ subApp.all('/api/createTxn/token', authenticationMiddleware, (req, res) => {
106
+ pc.createTxnToken(req, res);
107
+ });
108
+ subApp.all('/api/createTxn', authenticationMiddleware, (req, res) => {
109
+ pc.createTxn(req, res);
110
+ });
111
+ subApp.all('/', authenticationMiddleware, (req, res) => {
112
+ pc.init(req, res);
113
+ });
114
+ subApp.use(express_1.default.static(path_1.default.join(__dirname, 'public')), authenticationMiddleware, (req, res) => {
115
+ pc.init(req, res);
116
+ });
117
+ return subApp;
118
+ }
119
+ exports.default = { createPaymentMiddleware };
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "node-paytmpg",
3
+ "version": "7.0.1",
4
+ "description": "Payment Gateway Integration using NodeJS",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "start": "node example.js",
8
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
9
+ "build:ts": "tsc -p tsconfig.json",
10
+ "copy:views": "copyfiles -u 1 \"app/views/**/*\" dist/app",
11
+ "copy:public": "copyfiles -u 1 \"public/**/*\" dist/public",
12
+ "build": "npm-run-all clean build:ts copy:views copy:public",
13
+ "prepare": "npm run build",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/shiveshnavin/node_paytm.git"
19
+ },
20
+ "keywords": [
21
+ "nodejs",
22
+ "paytm",
23
+ "payment",
24
+ "gateway",
25
+ "paytm",
26
+ "api",
27
+ "paytm",
28
+ "nodejs"
29
+ ],
30
+ "author": "Shivesh Navin",
31
+ "license": "MIT",
32
+ "bugs": {
33
+ "url": "https://github.com/shiveshnavin/node_paytm/issues"
34
+ },
35
+ "homepage": "https://github.com/shiveshnavin/node_paytm#readme",
36
+ "dependencies": {
37
+ "axios": "^1.13.5",
38
+ "body-parser": "^1.18.3",
39
+ "crypto": "^1.0.1",
40
+ "dotenv": "^17.2.3",
41
+ "express": "^4.16.4",
42
+ "express-handlebars": "^3.1.0",
43
+ "mongoose": "^5.4.16",
44
+ "multi-db-orm": "^3.1.0",
45
+ "nodejs-base64-converter": "^1.0.5",
46
+ "razorpay": "^2.0.6",
47
+ "request": "^2.88.0",
48
+ "sqlite3": "^5.1.7",
49
+ "util": "^0.11.1"
50
+ },
51
+ "devDependencies": {
52
+ "@types/express": "^5.0.6",
53
+ "@types/express-handlebars": "^5.3.1",
54
+ "@types/node": "^24.6.1",
55
+ "copyfiles": "^2.4.1",
56
+ "firebase-admin": "^13.6.1",
57
+ "npm-run-all": "^4.1.5",
58
+ "typescript": "^5.9.3"
59
+ },
60
+ "files": [
61
+ "dist",
62
+ "public",
63
+ "app/views",
64
+ "README.MD",
65
+ "LICENSE"
66
+ ]
67
+ }
@@ -0,0 +1,455 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ body {
6
+ min-height: 100vh;
7
+ margin: 0;
8
+ font-family: 'Space Grotesk', 'Segoe UI', sans-serif;
9
+ color: var(--color-text);
10
+ background: var(--bg-color);
11
+ }
12
+
13
+ body.theme-dark {
14
+ --color-primary: #2f8bff;
15
+ --color-accent: #5ce1e6;
16
+ --color-surface: #0f1021;
17
+ --color-text: #e9ecf2;
18
+ --color-success: #24cf5f;
19
+ --color-danger: #ff6b6b;
20
+ --color-outline: rgba(255,255,255,0.08);
21
+ --bg-color: #0c0c12;
22
+ }
23
+
24
+ body.theme-light {
25
+ --color-primary: #2467ff;
26
+ --color-accent: #16c1ff;
27
+ --color-surface: #ffffff;
28
+ --color-text: #1f2430;
29
+ --color-success: #1dbf73;
30
+ --color-danger: #e85050;
31
+ --color-outline: rgba(0,0,0,0.08);
32
+ --bg-color: #f7f9fc;
33
+ }
34
+
35
+ .shell {
36
+ max-width: 780px;
37
+ margin: 32px auto;
38
+ padding: 32px;
39
+ background: var(--color-surface);
40
+ border: 1px solid var(--color-outline);
41
+ border-radius: 18px;
42
+ box-shadow: 0 24px 60px rgba(0,0,0,0.12);
43
+ }
44
+
45
+ /* Two-column checkout layout */
46
+ .checkout-grid {
47
+ display: grid;
48
+ grid-template-columns: 320px 1fr;
49
+ gap: 20px;
50
+ align-items: start;
51
+ }
52
+
53
+ .product-panel {
54
+ padding: 22px;
55
+ border-radius: 16px;
56
+ background: var(--color-surface);
57
+ color: var(--color-text);
58
+ }
59
+
60
+ .product-panel .product-title {
61
+ font-weight: 700;
62
+ font-size: 16px;
63
+ margin-bottom: 8px;
64
+ }
65
+
66
+ .product-panel .product-sub {
67
+ font-size: 13px;
68
+ opacity: 0.8;
69
+ margin-bottom: 18px;
70
+ }
71
+
72
+ .product-preview {
73
+ border-radius: 12px;
74
+ overflow: hidden;
75
+ height: 140px;
76
+ background: var(--color-surface);
77
+ border: 1px solid var(--color-outline);
78
+ margin-bottom: 14px;
79
+ }
80
+
81
+ .product-price {
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: space-between;
85
+ gap: 10px;
86
+ column-gap: 12px;
87
+ row-gap: 6px;
88
+ flex-wrap: nowrap;
89
+ flex-direction: column;
90
+ }
91
+
92
+ .product-price .pill {
93
+ flex-shrink: 0;
94
+ }
95
+
96
+ .product-price .price {
97
+ font-weight: 800;
98
+ font-size: 20px;
99
+ flex: 1;
100
+ min-width: 0;
101
+ overflow: hidden;
102
+ display: -webkit-box;
103
+ -webkit-line-clamp: 2;
104
+ -webkit-box-orient: vertical;
105
+ word-break: break-word;
106
+ line-height: 1.2;
107
+ }
108
+
109
+ .form-panel {
110
+ padding: 24px;
111
+ border-radius: 16px;
112
+ background: var(--color-surface);
113
+ }
114
+
115
+ .shell__header,
116
+ .shell__footer {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: 12px;
120
+ }
121
+
122
+ .shell__header {
123
+ justify-content: space-between;
124
+ }
125
+
126
+ .shell__footer {
127
+ justify-content: center;
128
+ margin-top: 20px;
129
+ font-size: 13px;
130
+ color: rgba(0,0,0,0.55);
131
+ }
132
+
133
+ body.theme-dark .shell__footer {
134
+ color: rgba(255,255,255,0.7);
135
+ }
136
+
137
+ .shell__content {
138
+ margin-top: 20px;
139
+ }
140
+
141
+ .brand {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: 12px;
145
+ }
146
+
147
+ .brand__mark {
148
+ display: inline-flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ width: 42px;
152
+ height: 42px;
153
+ border-radius: 12px;
154
+ background: var(--color-primary);
155
+ color: #fff;
156
+ font-weight: 700;
157
+ letter-spacing: 0.3px;
158
+ }
159
+
160
+ .brand__mark--img img {
161
+ width: 100%;
162
+ height: 100%;
163
+ object-fit: contain;
164
+ }
165
+
166
+ .brand__title {
167
+ font-weight: 700;
168
+ font-size: 16px;
169
+ }
170
+
171
+ .brand__meta {
172
+ font-size: 12px;
173
+ color: rgba(255,255,255,0.7);
174
+ }
175
+
176
+ .pill {
177
+ padding: 6px 12px;
178
+ border-radius: 999px;
179
+ border: 1px solid var(--color-outline);
180
+ color: rgba(255,255,255,0.8);
181
+ font-size: 12px;
182
+ letter-spacing: 0.2px;
183
+ }
184
+
185
+ body.theme-light .pill {
186
+ color: rgba(0,0,0,0.7);
187
+ }
188
+
189
+ .card {
190
+ background: rgba(255,255,255,0.03);
191
+ border: 1px solid var(--color-outline);
192
+ border-radius: 16px;
193
+ padding: 20px;
194
+ backdrop-filter: blur(6px);
195
+ box-shadow: 0 24px 40px rgba(0,0,0,0.1);
196
+ }
197
+
198
+ body.theme-light .card {
199
+ background: #ffffff;
200
+ box-shadow: 0 10px 30px rgba(23, 38, 61, 0.08);
201
+ }
202
+
203
+ .card__header {
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: space-between;
207
+ gap: 16px;
208
+ margin-bottom: 10px;
209
+ }
210
+
211
+ .eyebrow {
212
+ margin: 0 0 4px;
213
+ font-size: 12px;
214
+ letter-spacing: 0.6px;
215
+ text-transform: uppercase;
216
+ color: rgba(255,255,255,0.65);
217
+ }
218
+
219
+ h2 {
220
+ margin: 0;
221
+ font-size: 24px;
222
+ letter-spacing: -0.2px;
223
+ }
224
+
225
+ .helper {
226
+ margin: 4px 0 16px;
227
+ color: rgba(0,0,0,0.65);
228
+ font-size: 14px;
229
+ }
230
+
231
+ body.theme-dark .helper {
232
+ color: rgba(255,255,255,0.7);
233
+ }
234
+
235
+ .amount-chip {
236
+ padding: 10px 14px;
237
+ border-radius: 12px;
238
+ background: var(--color-primary);
239
+ color: #ffffff;
240
+ font-weight: 700;
241
+ min-width: 120px;
242
+ text-align: right;
243
+ }
244
+
245
+ body.theme-light .amount-chip {
246
+ color: #0f172a;
247
+ }
248
+
249
+ .amount-chip span {
250
+ opacity: 0.8;
251
+ font-weight: 600;
252
+ margin-left: 6px;
253
+ }
254
+
255
+ .form-grid {
256
+ display: grid;
257
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
258
+ gap: 14px;
259
+ }
260
+
261
+ .form-grid--compact {
262
+ grid-template-columns: 1fr auto;
263
+ align-items: center;
264
+ }
265
+
266
+ .field {
267
+ display: flex;
268
+ flex-direction: column;
269
+ gap: 6px;
270
+ }
271
+
272
+ .field__label {
273
+ font-size: 13px;
274
+ color: rgba(255,255,255,0.7);
275
+ }
276
+
277
+ input[type="text"],
278
+ input[type="email"],
279
+ input[type="number"],
280
+ input[type="tel"],
281
+ input[type="password"],
282
+ textarea {
283
+ width: 100%;
284
+ padding: 12px 14px;
285
+ border-radius: 12px;
286
+ border: 1px solid var(--color-outline);
287
+ background: rgba(255,255,255,0.05);
288
+ color: #fff;
289
+ font-size: 15px;
290
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
291
+ }
292
+
293
+ body.theme-light input[type="text"],
294
+ body.theme-light input[type="email"],
295
+ body.theme-light input[type="number"],
296
+ body.theme-light input[type="tel"],
297
+ body.theme-light input[type="password"],
298
+ body.theme-light textarea {
299
+ background: #f8f9fb;
300
+ color: #1f2430;
301
+ }
302
+
303
+ input:focus,
304
+ textarea:focus {
305
+ outline: none;
306
+ border-color: var(--color-primary);
307
+ box-shadow: 0 0 0 3px rgba(255,255,255,0.05);
308
+ }
309
+
310
+ .checkbox {
311
+ display: flex;
312
+ align-items: center;
313
+ gap: 10px;
314
+ font-size: 14px;
315
+ color: rgba(0,0,0,0.8);
316
+ }
317
+
318
+ body.theme-dark .checkbox {
319
+ color: rgba(255,255,255,0.8);
320
+ }
321
+
322
+ .checkbox input {
323
+ width: auto;
324
+ height: 18px;
325
+ accent-color: var(--color-primary);
326
+ }
327
+
328
+ .button {
329
+ grid-column: 1 / -1;
330
+ padding: 14px;
331
+ border: 0;
332
+ border-radius: 12px;
333
+ background: var(--color-primary);
334
+ color: #ffffff;
335
+ font-weight: 700;
336
+ font-size: 15px;
337
+ cursor: pointer;
338
+ transition: transform 0.12s ease, box-shadow 0.12s ease, opacity 0.12s ease;
339
+ box-shadow: 0 18px 28px rgba(0,0,0,0.12);
340
+ }
341
+
342
+ .button:hover {
343
+ transform: translateY(-1px);
344
+ box-shadow: 0 20px 34px rgba(0,0,0,0.28);
345
+ }
346
+
347
+ .button:active {
348
+ transform: translateY(0);
349
+ box-shadow: 0 12px 20px rgba(0,0,0,0.2);
350
+ }
351
+
352
+ .button[disabled] {
353
+ opacity: 0.6;
354
+ cursor: not-allowed;
355
+ }
356
+
357
+ .result-grid {
358
+ display: grid;
359
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
360
+ gap: 12px;
361
+ margin: 14px 0 18px;
362
+ }
363
+
364
+ .result-item {
365
+ padding: 12px;
366
+ border: 1px solid var(--color-outline);
367
+ border-radius: 12px;
368
+ background: rgba(255,255,255,0.02);
369
+ }
370
+
371
+ body.theme-light .result-item {
372
+ background: #f8f9fb;
373
+ }
374
+
375
+ .result-item .label {
376
+ margin: 0;
377
+ font-size: 12px;
378
+ color: rgba(0,0,0,0.6);
379
+ text-transform: uppercase;
380
+ letter-spacing: 0.4px;
381
+ }
382
+
383
+ body.theme-dark .result-item .label {
384
+ color: rgba(255,255,255,0.6);
385
+ }
386
+
387
+ .result-item .value {
388
+ margin: 6px 0 0;
389
+ font-weight: 600;
390
+ color: #0f172a;
391
+ word-break: break-all;
392
+ }
393
+
394
+ body.theme-dark .result-item .value {
395
+ color: #fff;
396
+ }
397
+
398
+ .status-chip {
399
+ padding: 10px 12px;
400
+ border-radius: 12px;
401
+ font-weight: 700;
402
+ text-transform: uppercase;
403
+ letter-spacing: 0.4px;
404
+ }
405
+
406
+ .status-chip.TXN_SUCCESS,
407
+ .status-chip.success {
408
+ background: rgba(36, 207, 95, 0.15);
409
+ color: #4cff9f;
410
+ border: 1px solid rgba(76, 255, 159, 0.4);
411
+ }
412
+
413
+ .status-chip.TXN_FAILURE,
414
+ .status-chip.failed,
415
+ .status-chip.FAILURE {
416
+ background: rgba(255, 107, 107, 0.14);
417
+ color: #ff9b9b;
418
+ border: 1px solid rgba(255, 155, 155, 0.35);
419
+ }
420
+
421
+ .status-chip.TXN_PENDING,
422
+ .status-chip.pending {
423
+ background: rgba(255, 193, 79, 0.14);
424
+ color: #ffd77a;
425
+ border: 1px solid rgba(255, 215, 122, 0.35);
426
+ }
427
+
428
+ /* result icon toggles */
429
+ .status-TXN_SUCCESS .icon-success { display: block; }
430
+ .status-TXN_SUCCESS .icon-fail { display: none; }
431
+ .status-TXN_FAILURE .icon-success { display: none; }
432
+ .status-TXN_FAILURE .icon-fail { display: block; }
433
+
434
+ /* control which button group shows for success/failure */
435
+ .status-TXN_SUCCESS .fail-buttons { display: none !important; }
436
+ .status-TXN_FAILURE .success-buttons { display: none !important; }
437
+
438
+ .dot {
439
+ opacity: 0.6;
440
+ }
441
+
442
+ @media (max-width: 768px) {
443
+ .shell {
444
+ margin: 16px;
445
+ padding: 20px;
446
+ }
447
+
448
+ .checkout-grid {
449
+ grid-template-columns: 1fr;
450
+ }
451
+
452
+ .form-grid {
453
+ grid-template-columns: 1fr;
454
+ }
455
+ }