ksp-pmt-server/src/routes/refund.js

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;