elpaypg 0.0.0-stage → 2.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.
- package/.env.example +8 -0
- package/README.md +176 -2
- package/package.json +21 -4
- package/public/elpay-checkout.js +769 -0
- package/public/elpay-sms-gateway.apk +0 -0
- package/public/elpay-sms-gateway.apk.sha256 +1 -0
- package/public/elpay.js +785 -0
- package/public/fonts/dm-sans.woff2 +0 -0
- package/public/fonts/sora.woff2 +0 -0
- package/public/views/checkout.html +2839 -0
- package/public/views/partials/auth.html +110 -0
- package/public/views/partials/footer.html +54 -0
- package/public/views/partials/header.html +101 -0
- package/public/views/partials/modals.html +179 -0
- package/public/views/partials/payment-methods.html +526 -0
- package/public/views/partials/sidebar.html +75 -0
- package/public/views/partials/splash.html +35 -0
- package/src/__tests__/smoke.test.js +8 -0
- package/src/auth-guard.js +95 -0
- package/src/index.js +137 -0
- package/src/router.js +46 -0
- package/src/routes/admin.routes.js +2424 -0
- package/src/routes/checkout.routes.js +2000 -0
- package/src/routes/customer.routes.js +650 -0
- package/src/routes/device.routes.js +303 -0
- package/src/routes/payments.routes.js +446 -0
- package/src/routes/pollers.routes.js +653 -0
- package/src/services/observer-poller.service.js +369 -0
- package/src/services/tunnel-manager.js +130 -0
- package/src/services/whatsapp-berry.service.js +743 -0
- package/src/vendor/common/__tests__/common.test.js +297 -0
- package/src/vendor/common/__tests__/common.test.ts +160 -0
- package/src/vendor/common/contracts.js +64 -0
- package/src/vendor/common/contracts.ts +188 -0
- package/src/vendor/common/crypto.js +132 -0
- package/src/vendor/common/crypto.ts +112 -0
- package/src/vendor/common/enums.js +91 -0
- package/src/vendor/common/enums.ts +91 -0
- package/src/vendor/common/index.js +12 -0
- package/src/vendor/common/index.ts +8 -0
- package/src/vendor/common/money.js +62 -0
- package/src/vendor/common/money.ts +56 -0
- package/src/vendor/common/observation.js +27 -0
- package/src/vendor/common/observation.ts +52 -0
- package/src/vendor/common/runtime-config.js +246 -0
- package/src/vendor/common/secrets.js +374 -0
- package/src/vendor/common/state-machine.d.ts +1 -0
- package/src/vendor/common/state-machine.js +1 -0
- package/src/vendor/common/state-transitions.js +85 -0
- package/src/vendor/common/tunnel-command.js +59 -0
- package/src/vendor/common/upi-providers.js +389 -0
- package/src/vendor/common/upi-providers.ts +400 -0
- package/src/vendor/common/upi.js +69 -0
- package/src/vendor/common/upi.ts +61 -0
- package/src/vendor/core/__tests__/core.test.js +683 -0
- package/src/vendor/core/__tests__/queue-timing.test.js +237 -0
- package/src/vendor/core/__tests__/webhook-engine.test.js +243 -0
- package/src/vendor/core/attribution-engine.js +855 -0
- package/src/vendor/core/bank-email-parser.js +105 -0
- package/src/vendor/core/bank-sms-parser.js +183 -0
- package/src/vendor/core/device-security.js +506 -0
- package/src/vendor/core/index.js +13 -0
- package/src/vendor/core/normalizer.js +165 -0
- package/src/vendor/core/qr-lib.js +8 -0
- package/src/vendor/core/qr.js +62 -0
- package/src/vendor/core/queue-engine.js +153 -0
- package/src/vendor/core/recovery-engine.js +159 -0
- package/src/vendor/core/state-machine.js +42 -0
- package/src/vendor/core/timing-engine.js +272 -0
- package/src/vendor/core/verification-pipeline.js +1164 -0
- package/src/vendor/core/webhook-engine.js +280 -0
- package/src/vendor/db/__tests__/auth-matrix.test.js +564 -0
- package/src/vendor/db/__tests__/customer-reset.test.js +95 -0
- package/src/vendor/db/__tests__/db.test.js +265 -0
- package/src/vendor/db/__tests__/schema-parity.test.js +137 -0
- package/src/vendor/db/__tests__/sqlite_verify.test.js +15 -0
- package/src/vendor/db/database.js +181 -0
- package/src/vendor/db/index.js +12 -0
- package/src/vendor/db/migrate.js +210 -0
- package/src/vendor/db/repositories/audit-log.repository.js +68 -0
- package/src/vendor/db/repositories/customer-session.repository.js +181 -0
- package/src/vendor/db/repositories/customer.repository.js +636 -0
- package/src/vendor/db/repositories/device.repository.js +285 -0
- package/src/vendor/db/repositories/merchant.repository.js +158 -0
- package/src/vendor/db/repositories/observation.repository.js +124 -0
- package/src/vendor/db/repositories/payment-session.repository.js +618 -0
- package/src/vendor/db/repositories/receiving-account.repository.js +244 -0
- package/src/vendor/db/repositories/support-ticket.repository.js +140 -0
- package/src/vendor/db/schema.sql +266 -0
- package/src/vendor/db/sqlite-schema.sql +270 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!-- TOAST NOTIFICATION -->
|
|
2
|
+
<div id="ypgToast" class="ypg-toast"><img src="/icons/upi.svg" alt="UPI" style="height:12px; vertical-align:middle; filter:brightness(0) invert(1); margin-right:4px;" /> Direct UPI QR is active for instant banking verification</div>
|
|
3
|
+
|
|
4
|
+
<!-- ELPAY PG CHECKOUT CARD / DRAWER WRAPPER -->
|
|
5
|
+
<div class="ypg-wrapper">
|
|
6
|
+
|
|
7
|
+
<!-- 1. INITIAL ELPAY PG LOADING SPLASH -->
|
|
8
|
+
<div id="ypgSplashScreen" class="ypg-splash-screen">
|
|
9
|
+
<div class="ypg-splash-center">
|
|
10
|
+
<div class="ypg-shield-orbit-box">
|
|
11
|
+
<img id="splashBrandLogo" alt="Logo"
|
|
12
|
+
style="display:none; max-width:82px; max-height:82px; border-radius:16px; object-fit:contain; box-shadow:0 8px 24px rgba(0,0,0,0.12);" />
|
|
13
|
+
<!-- Authentic ELPAY PG Shield with 'Y' Monogram -->
|
|
14
|
+
<svg class="ypg-shield-icon-svg" id="splashDefaultShield" viewBox="0 0 100 120" fill="none">
|
|
15
|
+
<defs>
|
|
16
|
+
<linearGradient id="ypgShieldGrad" x1="0" y1="0" x2="1" y2="1">
|
|
17
|
+
<stop offset="0%" stop-color="#0b63e5" />
|
|
18
|
+
<stop offset="100%" stop-color="#0747a6" />
|
|
19
|
+
</linearGradient>
|
|
20
|
+
</defs>
|
|
21
|
+
<path d="M50 4 L92 22 C92 72, 50 116, 50 116 C50 116, 8 72, 8 22 Z" fill="url(#ypgShieldGrad)" />
|
|
22
|
+
<!-- Sharp white & cyan 'Y' monogram -->
|
|
23
|
+
<path d="M33 32 L46 54 L46 84 L54 84 L54 54 L67 32 L56 32 L50 44 L44 32 Z" fill="#ffffff" />
|
|
24
|
+
<polygon points="50,44 56,32 61,32 54,49" fill="#38bdf8" />
|
|
25
|
+
</svg>
|
|
26
|
+
<div class="ypg-orbit-dot"></div>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="ypg-splash-footer" id="splashFooterWrap">
|
|
30
|
+
<span>Secured By</span>
|
|
31
|
+
<strong id="splashSecuredByText" style="color:var(--ypg-blue); font-size:15px; letter-spacing:-0.2px;">ELPAY
|
|
32
|
+
PG</strong>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { getAdminToken, getObserverToken, safeEqual, hasBearer } from './vendor/common/index.js';
|
|
2
|
+
function json(res, code, body) { res.status(code).json(body); }
|
|
3
|
+
|
|
4
|
+
/** Admin-prefixed routes that are intentionally public (static APK download for pairing). */
|
|
5
|
+
const PUBLIC_ADMIN_PATHS = new Set(['/api/v1/admin/sms-gateway/download-apk']);
|
|
6
|
+
|
|
7
|
+
/** Admin-prefixed routes the local observer processes call with the observer token. */
|
|
8
|
+
const OBSERVER_ADMIN_PATHS = new Set(['/api/v1/admin/gmail/credentials']);
|
|
9
|
+
|
|
10
|
+
/** SSE cannot send headers from EventSource, so these accept `?token=` on GET. */
|
|
11
|
+
const QUERY_TOKEN_PATHS = new Set(['/api/v1/admin/stream']);
|
|
12
|
+
|
|
13
|
+
function bearerToken(req) {
|
|
14
|
+
const header = req.headers['authorization'] || '';
|
|
15
|
+
const match = /^Bearer\s+(.+)$/i.exec(header);
|
|
16
|
+
return match ? match[1].trim() : '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isDevTool(pathname) {
|
|
20
|
+
return pathname.startsWith('/api/v1/admin/dev/') || /^\/api\/v1\/checkout\/[^/]+\/simulate-observation$/.test(pathname);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Decides which credential a request needs.
|
|
25
|
+
* @returns {null | 'admin' | 'observer'} null = public, 'observer' = admin OR observer token
|
|
26
|
+
*/
|
|
27
|
+
function requiredLevel(method, pathname) {
|
|
28
|
+
if (pathname.startsWith('/api/v1/admin/')) {
|
|
29
|
+
if (PUBLIC_ADMIN_PATHS.has(pathname)) return null;
|
|
30
|
+
if (OBSERVER_ADMIN_PATHS.has(pathname)) return 'observer';
|
|
31
|
+
return 'admin';
|
|
32
|
+
}
|
|
33
|
+
if (method === 'POST' && pathname === '/api/v1/pollers/events') return 'observer';
|
|
34
|
+
// Injects a synthetic bank message into the real verification pipeline.
|
|
35
|
+
if (method === 'POST' && /^\/api\/v1\/checkout\/[^/]+\/simulate-observation$/.test(pathname)) return 'admin';
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Guard for HttpServer.use(). Admin credential = ADMIN_API_TOKEN (or data/.secrets/admin_api_token).
|
|
41
|
+
* Observer credential = OBSERVER_API_TOKEN (or data/.secrets/observer_api_token); admin token also satisfies it.
|
|
42
|
+
*/
|
|
43
|
+
export function authGuard(req, res, pathname, url) {
|
|
44
|
+
const method = req.method.toUpperCase();
|
|
45
|
+
|
|
46
|
+
// Developer simulators push synthetic bank messages into the real pipeline and can settle live sessions.
|
|
47
|
+
if (method !== 'GET' && isDevTool(pathname) && process.env.NODE_ENV === 'production' && process.env.ELPAY_ALLOW_DEV_TOOLS !== 'true') {
|
|
48
|
+
json(res, 403, { error: 'Developer tools are disabled in production.', code: 'DEV_TOOLS_DISABLED' });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const level = requiredLevel(method, pathname);
|
|
53
|
+
if (!level) return false;
|
|
54
|
+
|
|
55
|
+
let presented = bearerToken(req);
|
|
56
|
+
if (!presented && method === 'GET' && QUERY_TOKEN_PATHS.has(pathname)) {
|
|
57
|
+
presented = url.searchParams.get('token') || '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const ok = presented && (
|
|
61
|
+
safeEqual(presented, getAdminToken()) ||
|
|
62
|
+
(level === 'observer' && safeEqual(presented, getObserverToken()))
|
|
63
|
+
);
|
|
64
|
+
if (ok) return false;
|
|
65
|
+
|
|
66
|
+
json(res, 401, { error: 'Unauthorized', code: 'AUTH_REQUIRED' });
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** True only when the request carries the admin token (observer token is not enough). */
|
|
71
|
+
export function isAdminRequest(req) {
|
|
72
|
+
const presented = bearerToken(req);
|
|
73
|
+
return Boolean(presented) && safeEqual(presented, getAdminToken());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* True when the caller may read the merchant-private fields of a payment session.
|
|
78
|
+
*
|
|
79
|
+
* Requires proof of ownership: either the gateway's own admin/observer credential, or the
|
|
80
|
+
* owning merchant's webhook secret presented via `X-Merchant-Key` (which every merchant
|
|
81
|
+
* integration already holds). Without it, only the public view is returned, so a leaked or
|
|
82
|
+
* guessed payment id cannot be used to read internal status, verification internals,
|
|
83
|
+
* merchant metadata or the return URL.
|
|
84
|
+
*/
|
|
85
|
+
export function isPaymentOwner(req, session, ctx) {
|
|
86
|
+
if (isAdminRequest(req)) return true;
|
|
87
|
+
if (hasBearer(req, getObserverToken())) return true;
|
|
88
|
+
|
|
89
|
+
const presented = req.headers['x-merchant-key'];
|
|
90
|
+
if (!presented || typeof presented !== 'string' || !presented.trim()) return false;
|
|
91
|
+
|
|
92
|
+
const merchant = ctx.merchantRepo?.findById ? ctx.merchantRepo.findById(session.merchantId) : null;
|
|
93
|
+
if (!merchant?.webhookSecret) return false;
|
|
94
|
+
return safeEqual(presented.trim(), merchant.webhookSecret);
|
|
95
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { PaymentStatus } from './vendor/common/index.js';
|
|
5
|
+
import {
|
|
6
|
+
getDatabase,
|
|
7
|
+
ensureDatabaseInitialized,
|
|
8
|
+
runMigrations,
|
|
9
|
+
MerchantRepository,
|
|
10
|
+
ReceivingAccountRepository,
|
|
11
|
+
CustomerRepository,
|
|
12
|
+
CustomerSessionRepository,
|
|
13
|
+
PaymentSessionRepository,
|
|
14
|
+
ObservationRepository,
|
|
15
|
+
SupportTicketRepository,
|
|
16
|
+
AuditLogRepository,
|
|
17
|
+
DeviceRepository
|
|
18
|
+
} from './vendor/db/index.js';
|
|
19
|
+
import {
|
|
20
|
+
QueueEngine,
|
|
21
|
+
TimingEngine,
|
|
22
|
+
AttributionEngine,
|
|
23
|
+
RecoveryEngine,
|
|
24
|
+
WebhookEngine,
|
|
25
|
+
PaymentVerificationPipeline
|
|
26
|
+
} from './vendor/core/index.js';
|
|
27
|
+
import { createRouter } from './router.js';
|
|
28
|
+
|
|
29
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
30
|
+
const __dirname = path.dirname(__filename);
|
|
31
|
+
|
|
32
|
+
export async function elpay(options = {}) {
|
|
33
|
+
const db = options.db || getDatabase(options.databaseUrl);
|
|
34
|
+
ensureDatabaseInitialized(db);
|
|
35
|
+
const { defaultMerchant } = runMigrations(db);
|
|
36
|
+
|
|
37
|
+
const merchantRepo = new MerchantRepository(db);
|
|
38
|
+
const accountRepo = new ReceivingAccountRepository(db);
|
|
39
|
+
const customerRepo = new CustomerRepository(db);
|
|
40
|
+
const customerSessionRepo = new CustomerSessionRepository(db, customerRepo);
|
|
41
|
+
const sessionRepo = new PaymentSessionRepository(db);
|
|
42
|
+
const obsRepo = new ObservationRepository(db);
|
|
43
|
+
const ticketRepo = new SupportTicketRepository(db);
|
|
44
|
+
const auditRepo = new AuditLogRepository(db);
|
|
45
|
+
const deviceRepo = new DeviceRepository(db);
|
|
46
|
+
|
|
47
|
+
const eventEmitter = new EventEmitter();
|
|
48
|
+
|
|
49
|
+
const parseWindowMs = (raw, fallback) => {
|
|
50
|
+
if (raw === undefined || raw === null || String(raw).trim() === '') return fallback;
|
|
51
|
+
const value = Number(raw);
|
|
52
|
+
if (!Number.isFinite(value) || value <= 0 || value > 24 * 60 * 60 * 1000) return fallback;
|
|
53
|
+
return Math.floor(value);
|
|
54
|
+
};
|
|
55
|
+
const activeDurationMs = parseWindowMs(process.env.PAYMENT_ACTIVE_WINDOW_MS, 300000);
|
|
56
|
+
const processingDurationMs = parseWindowMs(process.env.PAYMENT_PROCESSING_WINDOW_MS, 30000);
|
|
57
|
+
|
|
58
|
+
const queueEngine = new QueueEngine({ sessionRepo, accountRepo, eventEmitter, auditRepo, activeDurationMs, processingDurationMs });
|
|
59
|
+
const timingEngine = new TimingEngine({ sessionRepo, queueEngine, eventEmitter, auditRepo, activeDurationMs, processingDurationMs });
|
|
60
|
+
const attributionEngine = new AttributionEngine({ sessionRepo, obsRepo, ticketRepo, auditRepo, eventEmitter });
|
|
61
|
+
const recoveryEngine = new RecoveryEngine({ db, sessionRepo, accountRepo, queueEngine, timingEngine, auditRepo, eventEmitter });
|
|
62
|
+
const webhookEngine = new WebhookEngine({ db, merchantRepo, maxRetries: Number(process.env.WEBHOOK_MAX_RETRIES || 5) });
|
|
63
|
+
|
|
64
|
+
const checkoutBaseUrl = options.baseUrl || process.env.BASE_CHECKOUT_URL || 'http://localhost:3000';
|
|
65
|
+
|
|
66
|
+
const handleSlotFreedAndPromote = (receivingAccountId) => {
|
|
67
|
+
if (!receivingAccountId) return null;
|
|
68
|
+
const promoted = queueEngine.promoteNext(receivingAccountId);
|
|
69
|
+
if (promoted) {
|
|
70
|
+
if (promoted.expiresAt) timingEngine.startActiveTimer(promoted.id, promoted.expiresAt);
|
|
71
|
+
}
|
|
72
|
+
return promoted;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
eventEmitter.on('PAYMENT.SUCCESS', (data) => {
|
|
76
|
+
const session = sessionRepo.findById(data.paymentId);
|
|
77
|
+
if (session) {
|
|
78
|
+
webhookEngine.dispatch('SUCCESS', session).catch(() => {});
|
|
79
|
+
handleSlotFreedAndPromote(session.receivingAccountId);
|
|
80
|
+
}
|
|
81
|
+
if (options.onSuccess) options.onSuccess(data);
|
|
82
|
+
});
|
|
83
|
+
eventEmitter.on('PAYMENT.FAILED', (data) => {
|
|
84
|
+
const session = sessionRepo.findById(data.paymentId);
|
|
85
|
+
if (session) {
|
|
86
|
+
webhookEngine.dispatch('FAILED', session).catch(() => {});
|
|
87
|
+
handleSlotFreedAndPromote(session.receivingAccountId);
|
|
88
|
+
}
|
|
89
|
+
if (options.onFailed) options.onFailed(data);
|
|
90
|
+
});
|
|
91
|
+
eventEmitter.on('PAYMENT.EXPIRED', (data) => {
|
|
92
|
+
const session = sessionRepo.findById(data.paymentId);
|
|
93
|
+
if (session) {
|
|
94
|
+
webhookEngine.dispatch('EXPIRED', session).catch(() => {});
|
|
95
|
+
handleSlotFreedAndPromote(session.receivingAccountId);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
eventEmitter.on('PAYMENT.CANCELLED', (data) => {
|
|
99
|
+
const session = sessionRepo.findById(data.paymentId);
|
|
100
|
+
if (session) {
|
|
101
|
+
webhookEngine.dispatch('CANCELLED', session).catch(() => {});
|
|
102
|
+
handleSlotFreedAndPromote(session.receivingAccountId);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
recoveryEngine.recover();
|
|
107
|
+
|
|
108
|
+
const pipeline = new PaymentVerificationPipeline({
|
|
109
|
+
db, sessionRepo, obsRepo, customerRepo, attributionEngine, auditRepo, merchantRepo,
|
|
110
|
+
server: null, eventEmitter
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const ctx = {
|
|
114
|
+
db, merchantRepo, accountRepo, customerRepo, customerSessionRepo, sessionRepo, deviceRepo,
|
|
115
|
+
obsRepo, ticketRepo, auditRepo, queueEngine, timingEngine, attributionEngine, pipeline,
|
|
116
|
+
recoveryEngine, eventEmitter, defaultMerchant, checkoutBaseUrl, webhookEngine,
|
|
117
|
+
otpCooldownMs: options.otpCooldownMs,
|
|
118
|
+
theme: options.theme || {}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const router = createRouter(ctx);
|
|
122
|
+
|
|
123
|
+
const destroy = () => {
|
|
124
|
+
timingEngine.destroy?.();
|
|
125
|
+
attributionEngine.destroy?.();
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
router,
|
|
130
|
+
on: (event, cb) => eventEmitter.on(event, cb),
|
|
131
|
+
init: async () => {},
|
|
132
|
+
destroy,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export { createRouter };
|
|
137
|
+
export default { elpay };
|
package/src/router.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Router } from 'express';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import { registerPaymentRoutes } from './routes/payments.routes.js';
|
|
7
|
+
import { registerCheckoutRoutes } from './routes/checkout.routes.js';
|
|
8
|
+
import { registerPollerRoutes } from './routes/pollers.routes.js';
|
|
9
|
+
import { registerAdminRoutes } from './routes/admin.routes.js';
|
|
10
|
+
import { registerCustomerRoutes } from './routes/customer.routes.js';
|
|
11
|
+
import { registerDeviceRoutes } from './routes/device.routes.js';
|
|
12
|
+
|
|
13
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
|
|
15
|
+
export function createRouter(ctx = {}) {
|
|
16
|
+
const router = Router();
|
|
17
|
+
router.use(express.static(path.join(__dirname, '..', 'public')));
|
|
18
|
+
|
|
19
|
+
router.get('/checkout/:id', (req, res) => {
|
|
20
|
+
const htmlPath = path.join(__dirname, '..', 'public', 'views', 'checkout.html');
|
|
21
|
+
const html = fs.readFileSync(htmlPath, 'utf8')
|
|
22
|
+
.replace(/\{\{API_BASE_URL\}\}/g, ctx.baseUrl || 'http://localhost:5000')
|
|
23
|
+
.replace(/\{\{PAYMENT_ID\}\}/g, req.params.id);
|
|
24
|
+
res.type('html').send(html);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
router.get('/theme.css', (req, res) => {
|
|
28
|
+
const t = ctx.theme || {};
|
|
29
|
+
const vars = [
|
|
30
|
+
t.primaryColor ? `--elpay-primary: ${t.primaryColor};` : '',
|
|
31
|
+
t.borderRadius ? `--elpay-radius: ${t.borderRadius};` : '',
|
|
32
|
+
t.fontFamily ? `--elpay-font: ${t.fontFamily};` : '',
|
|
33
|
+
].filter(Boolean).join(' ');
|
|
34
|
+
res.type('text/css').send(`:root { ${vars} }`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
registerPaymentRoutes(router, ctx);
|
|
38
|
+
registerCheckoutRoutes(router, ctx);
|
|
39
|
+
registerPollerRoutes(router, ctx);
|
|
40
|
+
registerAdminRoutes(router, ctx);
|
|
41
|
+
registerCustomerRoutes(router, ctx);
|
|
42
|
+
registerDeviceRoutes(router, ctx);
|
|
43
|
+
return router;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default createRouter;
|