Files
SoonDesign/frontend-web/pages/admin/assets/js/views/payment.js
T
24kycj 88c6ce8ccc 重构 monorepo 并完善网页端订阅与首页体验
- 迁移为 frontend-web、frontend-electron、backend-web 与 docker 部署结构
- 网页端:订阅门禁二次弹窗、套餐/支付组件化、顶栏分组对齐
- 首页:最近文件与模板库布局优化,缩略图对齐,下载与删除操作
- 新增管理后台、支付与云端文件 API,更新 README 与项目规范

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 18:17:39 +08:00

291 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function () {
'use strict';
var Api = function () { return window.AdminApi; };
var Ui = function () { return window.AdminUi; };
var I = function () { return window.AdminI18n; };
var Form = function () { return window.AdminForm; };
var Tabs = function () { return window.AdminTabs; };
var CHANNEL_CONFIG = {
alipay: {
label: '支付宝',
kinds: [
{ kind: 'private_key', file: 'alipay_private_key.pem', label: '应用私钥 private_key' },
{ kind: 'public_key', file: 'alipay_public_key.pem', label: '支付宝公钥 public_key' },
],
},
wechat: {
label: '微信支付',
kinds: [
{ kind: 'mch_private_key', file: 'wechat_mch_private_key.pem', label: '商户私钥 mch_private_key' },
{ kind: 'api_v3_key', file: 'wechat_api_v3_key.pem', label: 'APIv3 密钥 api_v3_key' },
{ kind: 'platform_cert', file: 'wechat_platform_cert.pem', label: '平台证书 platform_cert' },
],
},
};
function fileSet(items) {
var set = {};
(items || []).forEach(function (p) { set[p.name] = p; });
return set;
}
function channelFormHtml(channel, files) {
var cfg = CHANNEL_CONFIG[channel];
var rows = cfg.kinds.map(function (k) {
var ok = !!files[k.file];
var statusCls = ok ? 'soon-admin-key-form-row__status--ok' : 'soon-admin-key-form-row__status--miss';
var statusText = ok ? '已就绪' : '缺失';
var id = channel + '_' + k.kind;
return '<div class="soon-admin-key-form-row">' +
'<div><div class="soon-admin-key-form-row__label">' + I().esc(k.label) + '</div>' +
'<div class="soon-admin-key-form-row__status ' + statusCls + '">' + statusText + '</div></div>' +
'<textarea id="' + id + '" class="soon-input soon-input--sm soon-input--textarea" rows="4" ' +
'placeholder="粘贴 PEM 内容或拖入 .pem 文件"></textarea>' +
'<button type="button" class="soon-btn soon-btn--sm pay-upload-btn" data-channel="' + channel +
'" data-kind="' + k.kind + '" data-target="' + id + '">上传</button></div>';
}).join('');
return '<div class="soon-admin-form">' + Form().panel(cfg.label + ' 密钥', rows) +
'<details class="soon-admin-hint"><summary>回调说明</summary>' +
'<p>notify<code>/api/v1/pay/' + channel + '/notify</code>(须配置 site.base_url 为 HTTPS 外网地址)</p></details>' +
'<div class="soon-admin-upload" id="payDrop_' + channel + '">拖拽 .pem 到此处自动填入对应字段</div></div>';
}
function filesTabHtml(items) {
var rows = (items || []).map(function (p) {
return '<tr><td>' + I().esc(p.name) + '</td><td>' + p.bytes + ' B</td><td>' +
I().formatDateTime(p.mtime) + '</td></tr>';
}).join('');
return Ui().dataTable(['文件名', '大小', '修改时间'], rows);
}
function uploadPem(channel, kind, content, onDone) {
return Api().fetch('payment/upload', {
method: 'POST',
body: JSON.stringify({ channel: channel, kind: kind, content: content }),
}).then(function (j) {
if (j) {
soonToast('密钥已上传', 'success');
if (onDone) onDone();
}
return j;
});
}
function bindChannel(channel, panel) {
panel.querySelectorAll('.pay-upload-btn').forEach(function (btn) {
btn.onclick = function () {
var target = Ui().el(btn.dataset.target);
var content = target ? target.value.trim() : '';
if (!content) {
soonToast('请先粘贴或拖入 PEM 内容', 'warn');
return;
}
Ui().runFormSubmit(btn, function () {
return uploadPem(btn.dataset.channel, btn.dataset.kind, content, render);
});
};
});
var drop = Ui().el('payDrop_' + channel);
if (!drop) return;
drop.ondragover = function (e) { e.preventDefault(); drop.classList.add('is-dragover'); };
drop.ondragleave = function () { drop.classList.remove('is-dragover'); };
drop.ondrop = function (e) {
e.preventDefault();
drop.classList.remove('is-dragover');
var file = e.dataTransfer.files[0];
if (!file) return;
file.text().then(function (text) {
var name = file.name.toLowerCase();
var cfg = CHANNEL_CONFIG[channel];
var matched = cfg.kinds.find(function (k) { return name.indexOf(k.kind.replace(/_/g, '')) >= 0 || name === k.file; });
var targetId = matched ? channel + '_' + matched.kind : channel + '_' + cfg.kinds[0].kind;
var ta = Ui().el(targetId);
if (ta) ta.value = text;
soonToast('已填入 ' + (matched ? matched.label : '首个字段') + ',请确认后点击上传', 'success');
});
};
}
function render() {
var panel = Ui().el('panel-payment');
if (!panel) return;
panel.innerHTML = '<div class="soon-admin-page">' + Ui().pageHeader('支付配置', '按渠道管理密钥文件,上传后立即热加载') +
Ui().skeletonTable() + '</div>';
Api().get('payment/status').then(function (j) {
if (!j) return;
var items = j.data.items || [];
var files = fileSet(items);
var tabsHtml = Tabs().html('paymentTabs', [
{ id: 'alipay', label: '支付宝' },
{ id: 'wechat', label: '微信支付' },
{ id: 'files', label: '文件清单' },
], {
alipay: channelFormHtml('alipay', files),
wechat: channelFormHtml('wechat', files),
files: filesTabHtml(items),
}, 'alipay');
panel.innerHTML = '<div class="soon-admin-page">' +
Ui().pageHeader('支付配置', '按渠道管理密钥文件,上传后立即热加载') +
tabsHtml + '</div>';
var tabsRoot = Ui().el('paymentTabs');
Tabs().bind(tabsRoot);
bindChannel('alipay', panel);
bindChannel('wechat', panel);
});
}
window.AdminViews = window.AdminViews || {};
window.AdminViews.payment = { render: render };
})();