55 lines
1.8 KiB
JavaScript
55 lines
1.8 KiB
JavaScript
import { Router } from 'express';
|
|
import { KASPI_QRPAY_URL } from '../config.js';
|
|
import { loggedFetch, signedQrPayHeaders } from '../helpers.js';
|
|
import { decryptSecret } from '../crypto.js';
|
|
|
|
const router = Router();
|
|
|
|
// Extract session from request headers
|
|
const extractSession = (req) => ({
|
|
tokenSN: req.headers['x-token-sn'] || null,
|
|
profileId: req.headers['x-profile-id'] || null,
|
|
vtokenSecret: req.headers['x-vtoken-secret'] || null,
|
|
});
|
|
|
|
const requireAuth = (req, res, next) => {
|
|
const session = extractSession(req);
|
|
if (!session.tokenSN) return res.status(401).json({ error: 'Missing X-Token-SN header.' });
|
|
if (!session.vtokenSecret) return res.status(401).json({ error: 'Missing X-Vtoken-Secret header.' });
|
|
try {
|
|
session.decryptedSecret = decryptSecret(session.vtokenSecret);
|
|
} catch {
|
|
return res.status(401).json({ error: 'Invalid or expired vtokenSecret. Re-authenticate.' });
|
|
}
|
|
req.session = session;
|
|
next();
|
|
};
|
|
|
|
router.use(requireAuth);
|
|
|
|
// ─── Return (refund) ───
|
|
|
|
router.post('/create', async (req, res) => {
|
|
const { qrOperationId, returnAmount } = req.body;
|
|
if (!qrOperationId || !returnAmount)
|
|
return res.status(400).json({ error: 'qrOperationId and returnAmount required' });
|
|
try {
|
|
const url = `${KASPI_QRPAY_URL}/v01/kaspi-qr/history-pos-return`;
|
|
const headers = { ...signedQrPayHeaders(url, req.session), 'Content-Type': 'application/json' };
|
|
const resp = await loggedFetch(url, {
|
|
method: 'POST',
|
|
headers,
|
|
body: JSON.stringify({
|
|
ReturnAmount: Number(returnAmount),
|
|
QrOperationId: Number(qrOperationId),
|
|
DeviceInterface: 'Pos',
|
|
}),
|
|
});
|
|
res.json(await resp.json());
|
|
} catch (err) {
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
export default router;
|