add: gui sep requests

This commit is contained in:
2026-02-01 09:43:51 +03:30
parent 1406e4bd24
commit 18d3f17ce3

133
index.js
View File

@@ -855,7 +855,7 @@ app.post("/sep-pay-request", async (req, res) => {
action: "token",
TerminalId: SEP_TERMINAL_ID,
Amount: parsedAmount,
ResNum,
ResNum: resNum,
RedirectUrl: redirectUrl,
CellNumber: phone,
SettlementIBANInfo: wages,
@@ -961,16 +961,8 @@ app.post("/sepverify", async (req, res) => {
}
});
app.get("/sep-pay-requests/gui", async (req, res) => {
const basePath =
(req.baseUrl || "").replace(/\/sep-pay-requests\/gui$/, "") || "";
const listPath = basePath
? basePath + "/sep-pay-requests"
: "/sep-pay-requests";
const submitPathPrefix = basePath
? basePath + "/sep-pay-request/"
: "/sep-pay-request/";
// all-payments: list of saved SEP pay requests (from MongoDB)
app.get("/all-payments", async (req, res) => {
let list = [];
try {
const coll = await getSepPayCollection();
@@ -984,7 +976,7 @@ app.get("/sep-pay-requests/gui", async (req, res) => {
_id: doc._id ? doc._id.toString() : doc._id,
}));
} catch (err) {
console.error("sep-pay-requests gui list error", err);
console.error("all-payments list error", err);
}
const listJson = JSON.stringify(list)
@@ -1000,75 +992,33 @@ app.get("/sep-pay-requests/gui", async (req, res) => {
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SEP Pay Requests</title>
<title>همه پرداخت‌ها</title>
<style>
* { box-sizing: border-box; }
body { font-family: Tahoma, Arial, sans-serif; margin: 0; padding: 16px; background: #f5f5f5; }
h1 { color: #333; margin-bottom: 16px; }
.card { background: #fff; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); padding: 16px; margin-bottom: 12px; }
.row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; align-items: center; }
.label { font-weight: bold; color: #555; min-width: 90px; }
.value { color: #333; }
button { background: #1976d2; color: #fff; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; margin-top: 8px; }
button:hover { background: #1565c0; }
.msg { margin-top: 8px; padding: 8px; border-radius: 6px; font-size: 14px; }
.msg.success { background: #e8f5e9; color: #2e7d32; }
.msg.error { background: #ffebee; color: #c62828; }
input { padding: 6px 8px; border: 1px solid #ccc; border-radius: 4px; width: 180px; }
#list { margin-top: 16px; }
table { width: 100%; border-collapse: collapse; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 8px; overflow: hidden; }
th, td { padding: 10px 12px; text-align: right; border-bottom: 1px solid #eee; }
th { background: #fafafa; font-weight: bold; color: #555; }
tr:hover { background: #f9f9f9; }
</style>
</head>
<body>
<h1>درخواست‌های پرداخت SEP</h1>
<p><a href="${listPath}">API لیست (JSON)</a></p>
<h1>همه پرداختها</h1>
<div id="list"></div>
<script>
(function() {
const list = JSON.parse("${listJson}");
const submitPathPrefix = ${JSON.stringify(submitPathPrefix)};
const submitPath = function(id) { return submitPathPrefix + id + '/submit'; };
const listEl = document.getElementById('list');
var list = JSON.parse("${listJson}");
var listEl = document.getElementById('list');
if (!list || list.length === 0) {
listEl.innerHTML = '<p>موردی یافت نشد.</p>';
return;
}
listEl.innerHTML = list.map(function(item) {
const id = item._id;
const token = (item.token || '').toString();
const traceNo = (item.traceNo || '').toString();
const securePan = (item.securePan || '').toString();
const createdAt = item.createdAt ? new Date(item.createdAt).toLocaleString('fa-IR') : '-';
return '<div class="card" data-id="' + id + '">' +
'<div class="row"><span class="label">مبلغ:</span><span class="value">' + (item.amountRaw || item.amount) + '</span></div>' +
'<div class="row"><span class="label">استان:</span><span class="value">' + (item.provincecode || '-') + '</span></div>' +
'<div class="row"><span class="label">لینک:</span><span class="value">' + (item.isLink ? 'بله' : 'خیر') + '</span></div>' +
'<div class="row"><span class="label">Token:</span><span class="value">' + (token ? token.substring(0, 20) + '...' : '-') + '</span></div>' +
'<div class="row"><span class="label">RefId (TraceNo):</span><input type="text" id="ref-' + id + '" value="' + traceNo.replace(/"/g, '&quot;') + '" placeholder="اختیاری" /></div>' +
'<div class="row"><span class="label">CardPan:</span><input type="text" id="pan-' + id + '" value="' + securePan.replace(/"/g, '&quot;') + '" placeholder="اختیاری" /></div>' +
'<div class="row"><span class="label">تاریخ:</span><span class="value">' + createdAt + '</span></div>' +
'<button type="button" onclick="window.submitToTaavon(\\'' + id + '\\', this)">ارسال به تعاون (تراکنش موفق)</button>' +
'<div class="msg" id="msg-' + id + '"></div>' +
'</div>';
var rows = list.map(function(item) {
var createdAt = item.createdAt ? new Date(item.createdAt).toLocaleString('fa-IR') : '-';
return '<tr><td>' + (item.amountRaw || item.amount) + '</td><td>' + (item.provincecode || '-') + '</td><td>' + (item.isLink ? 'بله' : 'خیر') + '</td><td>' + (item.phone || '-') + '</td><td>' + createdAt + '</td></tr>';
}).join('');
window.submitToTaavon = async function(id, btn) {
const msgEl = document.getElementById('msg-' + id);
if (msgEl) { msgEl.textContent = ''; msgEl.className = 'msg'; }
btn.disabled = true;
var refId = (document.getElementById('ref-' + id) || {}).value || '';
var cardPan = (document.getElementById('pan-' + id) || {}).value || '';
var body = JSON.stringify({ refId: refId || undefined, cardHolderPan: cardPan || undefined });
try {
var r = await fetch(submitPath(id), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: body });
var j = await r.json();
if (msgEl) { msgEl.textContent = j.error || (j.message || 'ارسال شد'); msgEl.className = 'msg ' + (j.error ? 'error' : 'success'); }
} catch (e) {
if (msgEl) { msgEl.textContent = e.message; msgEl.className = 'msg error'; }
}
btn.disabled = false;
};
listEl.innerHTML = '<table><thead><tr><th>مبلغ</th><th>استان</th><th>لینک</th><th>موبایل</th><th>تاریخ</th></tr></thead><tbody>' + rows + '</tbody></table>';
})();
</script>
</body>
@@ -1077,57 +1027,6 @@ app.get("/sep-pay-requests/gui", async (req, res) => {
res.send(html);
});
// List all SEP pay requests (API)
app.get("/sep-pay-requests", async (req, res) => {
try {
const coll = await getSepPayCollection();
const list = await coll
.find({})
.sort({ createdAt: -1 })
.limit(500)
.toArray();
// Ensure _id is string for GUI
const listWithIds = list.map((doc) => ({
...doc,
_id: doc._id ? doc._id.toString() : doc._id,
}));
return res.json(listWithIds);
} catch (err) {
console.error("sep-pay-requests list error", err);
return res.status(500).json({ error: err.message });
}
});
// Manual submit to Taavon (like sepverify does) - use stored or body: authority, refId, cardHolderPan
app.post("/sep-pay-request/:id/submit", async (req, res) => {
const id = req.params.id;
const { authority, refId, cardHolderPan } = req.body;
try {
const coll = await getSepPayCollection();
const doc = await coll.findOne({ _id: new ObjectId(id) });
if (!doc) {
return res.status(404).json({ error: "Record not found" });
}
const province = (doc.provincecode || "").toString().substring(0, 2);
const isLink =
doc.isLink === true || doc.isLink === "true" || doc.isLink === "1";
const data = {
authority: authority ?? doc.token,
refId: refId ?? doc.traceNo,
cardHolderPan: cardHolderPan ?? doc.securePan,
};
if (isLink) {
await taavonSendDataZarinPalLink(province, data);
} else {
await taavonSendDataZarinPal(province, data);
}
return res.json({ ok: true, message: "Submitted to Taavon" });
} catch (err) {
console.error("sep-pay-request submit error", err);
return res.status(500).json({ error: err.message });
}
});
//end sep ---------------------------------------------------------------------------------------------
// samasat crack