add: gui sep requests

This commit is contained in:
2026-02-01 08:41:16 +03:30
parent c5aad8deca
commit 1406e4bd24

144
index.js
View File

@@ -927,26 +927,6 @@ app.post("/sepverify", async (req, res) => {
if (response.data.ResultCode === 0) {
let redirectUrl = `https://rasadyar.net/payment?finalAmount=${Amount}&cardHolderPan=${SecurePan}&date=${new Date()}&saleReferenceId=${TraceNo}`;
// Update DB with verify result for manual submit in GUI
try {
const coll = await getSepPayCollection();
await coll.updateOne(
{ token: Token },
{
$set: {
verified: true,
refNum: RefNum,
traceNo: TraceNo,
securePan: SecurePan,
verifyAmount: Amount,
updatedAt: new Date(),
},
},
);
} catch (dbErr) {
console.error("sepverify: failed to update DB", dbErr);
}
if (isLink) {
await taavonSendDataZarinPalLink(province, {
authority: Token,
@@ -990,6 +970,31 @@ app.get("/sep-pay-requests/gui", async (req, res) => {
const submitPathPrefix = basePath
? basePath + "/sep-pay-request/"
: "/sep-pay-request/";
let list = [];
try {
const coll = await getSepPayCollection();
const raw = await coll
.find({})
.sort({ createdAt: -1 })
.limit(500)
.toArray();
list = raw.map((doc) => ({
...doc,
_id: doc._id ? doc._id.toString() : doc._id,
}));
} catch (err) {
console.error("sep-pay-requests gui list error", err);
}
const listJson = JSON.stringify(list)
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029")
.replace(/</g, "\\u003c")
.replace(/>/g, "\\u003e")
.replace(/\\/g, "\\\\")
.replace(/"/g, '\\"');
const html = `<!DOCTYPE html>
<html dir="rtl" lang="fa">
<head>
@@ -1001,71 +1006,70 @@ app.get("/sep-pay-requests/gui", async (req, res) => {
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; }
.label { font-weight: bold; color: #555; min-width: 100px; }
.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; }
.verified { color: #2e7d32; }
.unverified { color: #c62828; }
button { background: #1976d2; color: #fff; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; }
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; }
button:disabled { background: #9e9e9e; cursor: not-allowed; }
.msg { margin-top: 12px; padding: 8px; border-radius: 6px; }
.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; }
</style>
</head>
<body>
<h1>درخواست‌های پرداخت SEP</h1>
<p><a href="${listPath}">API لیست (JSON)</a></p>
<div id="list">در حال بارگذاری...</div>
<div id="list"></div>
<script>
const listEl = document.getElementById('list');
const listPath = ${JSON.stringify(listPath)};
const submitPathPrefix = ${JSON.stringify(submitPathPrefix)};
const submitPath = (id) => submitPathPrefix + id + '/submit';
(function() {
const list = JSON.parse("${listJson}");
const submitPathPrefix = ${JSON.stringify(submitPathPrefix)};
const submitPath = function(id) { return submitPathPrefix + id + '/submit'; };
async function load() {
try {
const r = await fetch(listPath);
const data = await r.json();
if (!Array.isArray(data)) { listEl.innerHTML = '<p class="msg error">پاسخ نامعتبر</p>'; return; }
if (data.length === 0) { listEl.innerHTML = '<p>موردی یافت نشد.</p>'; return; }
listEl.innerHTML = data.map(item => {
const id = item._id;
const verified = item.verified === true;
const hasData = verified || (item.token && (item.traceNo || item.securePan));
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">وضعیت:</span><span class="' + (verified ? 'verified' : 'unverified') + '">' + (verified ? 'تایید شده' : 'تایید نشده') + '</span></div>' +
(item.traceNo ? '<div class="row"><span class="label">TraceNo:</span><span class="value">' + item.traceNo + '</span></div>' : '') +
'<div class="row"><span class="label">تاریخ:</span><span class="value">' + (item.createdAt ? new Date(item.createdAt).toLocaleString('fa-IR') : '-') + '</span></div>' +
'<button onclick="submit(\'' + id + '\', this)" ' + (!hasData ? 'disabled title="نیاز به authority/refId/cardHolderPan دارد"' : '') + '>ارسال به تعاون</button>' +
'<span class="msg" id="msg-' + id + '"></span>' +
'</div>';
}).join('');
} catch (e) {
listEl.innerHTML = '<p class="msg error">خطا: ' + e.message + '</p>';
const listEl = document.getElementById('list');
if (!list || list.length === 0) {
listEl.innerHTML = '<p>موردی یافت نشد.</p>';
return;
}
}
async function submit(id, btn) {
const msgEl = document.getElementById('msg-' + id);
if (msgEl) { msgEl.textContent = ''; msgEl.className = 'msg'; }
btn.disabled = true;
try {
const r = await fetch(submitPath(id), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' });
const 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 = 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>';
}).join('');
load();
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;
};
})();
</script>
</body>
</html>`;