|
|
@@ -98,6 +98,48 @@
|
|
|
.admin-login-area .login-title { color: #e65100 !important; }
|
|
|
.admin-tabs .module-tab.active { color: #e65100; border-bottom-color: #e65100; }
|
|
|
.admin-tabs .module-tab:hover { background: #fff3e0; color: #e65100; }
|
|
|
+
|
|
|
+ /* 支付宝扫码支付弹窗 - DeepSeek 风格 */
|
|
|
+ .alipay-modal-overlay {
|
|
|
+ position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
|
|
+ background: rgba(0,0,0,0.45); z-index: 1000;
|
|
|
+ display: flex; align-items: center; justify-content: center;
|
|
|
+ backdrop-filter: blur(2px);
|
|
|
+ }
|
|
|
+ .alipay-modal {
|
|
|
+ background: #fff; border-radius: 16px; width: 360px; max-width: 92vw;
|
|
|
+ padding: 28px 24px 22px; box-shadow: 0 20px 60px rgba(0,0,0,0.18);
|
|
|
+ text-align: center; position: relative; animation: modalIn 0.2s ease-out;
|
|
|
+ }
|
|
|
+ @keyframes modalIn {
|
|
|
+ from { opacity: 0; transform: scale(0.95) translateY(10px); }
|
|
|
+ to { opacity: 1; transform: scale(1) translateY(0); }
|
|
|
+ }
|
|
|
+ .alipay-modal-close {
|
|
|
+ position: absolute; top: 14px; right: 16px; width: 28px; height: 28px;
|
|
|
+ border: none; background: transparent; color: #999; font-size: 22px; line-height: 1;
|
|
|
+ cursor: pointer; border-radius: 50%; transition: all 0.2s;
|
|
|
+ }
|
|
|
+ .alipay-modal-close:hover { background: #f5f5f5; color: #333; }
|
|
|
+ .alipay-modal-logo {
|
|
|
+ display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
|
+ margin-bottom: 16px; font-size: 18px; font-weight: 700; color: #1677ff;
|
|
|
+ }
|
|
|
+ .alipay-modal-logo svg { width: 26px; height: 26px; }
|
|
|
+ .alipay-modal-title { font-size: 15px; color: #666; margin-bottom: 6px; }
|
|
|
+ .alipay-modal-amount { font-size: 32px; font-weight: 700; color: #111; margin-bottom: 18px; }
|
|
|
+ .alipay-modal-amount span { font-size: 18px; margin-right: 2px; }
|
|
|
+ .alipay-modal-qr {
|
|
|
+ width: 200px; height: 200px; margin: 0 auto 14px;
|
|
|
+ border: 1px solid #f0f0f0; border-radius: 8px; padding: 8px; background: #fff;
|
|
|
+ }
|
|
|
+ .alipay-modal-qr img { width: 100%; height: 100%; object-fit: contain; }
|
|
|
+ .alipay-modal-hint {
|
|
|
+ font-size: 12px; color: #999; line-height: 1.6; margin-top: 8px;
|
|
|
+ }
|
|
|
+ .alipay-modal-status {
|
|
|
+ margin-top: 10px; font-size: 13px; color: #1677ff; min-height: 20px;
|
|
|
+ }
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
@@ -296,36 +338,36 @@ async function runTest(sectionId, method, path, body, requireAuth, validatorName
|
|
|
if (durEl) durEl.textContent = result.duration + 'ms';
|
|
|
if (rawEl) rawEl.textContent = JSON.stringify(result.data, null, 2);
|
|
|
|
|
|
- // 检测响应中的二维码并渲染图片预览
|
|
|
+ // 检测响应中的支付结果并渲染
|
|
|
const qrBase64 = result.data?.data?.qrCodeBase64;
|
|
|
const payUrl = result.data?.data?.payUrl;
|
|
|
const channel = result.data?.data?.channel;
|
|
|
- let qrBox = document.getElementById(sectionId + '-qrcode');
|
|
|
- if (qrBase64) {
|
|
|
+ const orderNo = result.data?.data?.orderNo;
|
|
|
+ const amount = result.data?.data?.amount;
|
|
|
+
|
|
|
+ // 自动回填订单号到相关输入框
|
|
|
+ if (orderNo) {
|
|
|
+ ['payment-query-order-orderNo', 'payment-mock-pay-orderNo',
|
|
|
+ 'payment-callback-wechat-out_trade_no', 'payment-callback-alipay-out_trade_no']
|
|
|
+ .forEach(fid => { const el = document.getElementById(fid); if (el) el.value = orderNo; });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (channel === 'ALIPAY' && qrBase64) {
|
|
|
+ // 支付宝:DeepSeek 风格自定义弹窗,展示二维码
|
|
|
+ showAlipayModal(qrBase64, amount, orderNo);
|
|
|
+ } else if (qrBase64) {
|
|
|
+ // 微信:显示扫码二维码
|
|
|
+ let qrBox = document.getElementById(sectionId + '-qrcode');
|
|
|
if (!qrBox) {
|
|
|
qrBox = document.createElement('div');
|
|
|
qrBox.id = sectionId + '-qrcode';
|
|
|
qrBox.style.cssText = 'text-align:center;padding:12px;background:#fff;border-radius:4px;margin-top:8px;';
|
|
|
rawEl.parentNode.insertBefore(qrBox, rawEl.nextSibling);
|
|
|
}
|
|
|
- // 支付宝电脑网站支付:payUrl是跳转链接/表单,显示跳转按钮+二维码
|
|
|
- if (channel === 'ALIPAY' && payUrl) {
|
|
|
- qrBox.innerHTML = '<img src="' + qrBase64 + '" style="max-width:180px;border:1px solid #eee;border-radius:4px;opacity:0.5;">'
|
|
|
- + '<div style="margin-top:8px;"><a href="' + payUrl + '" target="_blank" style="display:inline-block;padding:8px 20px;background:#1677ff;color:#fff;border-radius:4px;text-decoration:none;font-size:14px;">点击跳转支付宝收银台</a></div>'
|
|
|
- + '<div style="margin-top:6px;font-size:12px;color:#999;">支付宝电脑网站支付(页面跳转模式,也可手动确认支付测试回调)</div>';
|
|
|
- } else {
|
|
|
- qrBox.innerHTML = '<img src="' + qrBase64 + '" style="max-width:220px;border:1px solid #eee;border-radius:4px;"><div style="margin-top:6px;font-size:12px;color:#666;">支付二维码(扫码付款后回调自动激活,也可手动确认支付测试回调)</div>';
|
|
|
- }
|
|
|
-
|
|
|
- // 自动回填订单号到相关输入框
|
|
|
- const orderNo = result.data?.data?.orderNo;
|
|
|
- if (orderNo) {
|
|
|
- ['payment-query-order-orderNo', 'payment-mock-pay-orderNo',
|
|
|
- 'payment-callback-wechat-out_trade_no', 'payment-callback-alipay-out_trade_no']
|
|
|
- .forEach(fid => { const el = document.getElementById(fid); if (el) el.value = orderNo; });
|
|
|
- }
|
|
|
- } else if (qrBox) {
|
|
|
- qrBox.remove();
|
|
|
+ qrBox.innerHTML = '<img src="' + qrBase64 + '" style="max-width:220px;border:1px solid #eee;border-radius:4px;"><div style="margin-top:6px;font-size:12px;color:#666;">微信扫码付款后回调自动激活,也可手动确认支付测试回调</div>';
|
|
|
+ } else {
|
|
|
+ const oldBox = document.getElementById(sectionId + '-qrcode');
|
|
|
+ if (oldBox) oldBox.remove();
|
|
|
}
|
|
|
|
|
|
let passed = false;
|
|
|
@@ -1186,7 +1228,7 @@ function renderPaymentModule() {
|
|
|
${testSection('payment-plans', '支付方案列表', 'GET', 'get', '/api/payment/plans', '公开', 'auth-public', null, true, null, '获取所有可用的支付方案列表(高级会员月卡/年卡)')}
|
|
|
${testSection('payment-create-order', '创建支付订单', 'POST', 'post', '/api/payment/order/create', '需认证', 'auth-required',
|
|
|
[{label:'方案ID(planId)', name:'planId', placeholder:'1', default:'1'},
|
|
|
- {label:'支付渠道(channel)', name:'channel', placeholder:'WECHAT/ALIPAY', default:'WECHAT'}], true, null, '创建支付订单并返回二维码(qrCodeBase64)和支付链接(payUrl),15分钟内未付款订单自动过期。mock模式返回模拟二维码,真实支付模式返回真实支付链接,也可点击「手动确认支付」按钮测试回调流程')}
|
|
|
+ {label:'支付渠道(channel)', name:'channel', placeholder:'WECHAT/ALIPAY', default:'WECHAT'}], true, null, '创建支付订单,微信返回二维码(qrCodeBase64)供扫码支付,支付宝通过alipay.trade.precreate返回qr_code,前端自定义弹窗展示二维码并自动轮询支付状态。15分钟内未付款订单自动过期')}
|
|
|
${testSection('payment-query-order', '查询订单', 'GET', 'get', '/api/payment/order/{orderNo}', '需认证', 'auth-required',
|
|
|
[{label:'订单号(orderNo)', name:'orderNo', placeholder:'OP...', default:''}], true, null, '根据订单号查询支付订单详情,响应中expireHint含过期提示')}
|
|
|
${testSection('payment-orders', '我的订单列表', 'GET', 'get', '/api/payment/orders', '需认证', 'auth-required', null, true, null, '查询当前用户的所有支付订单,待支付订单含expireHint过期提示')}
|
|
|
@@ -1288,6 +1330,7 @@ function renderSearchRecordModule() {
|
|
|
</div>
|
|
|
${testSection('sr-save', '保存搜索记录', 'POST', 'post', '/api/search-record/history', '需认证', 'auth-required',
|
|
|
[{label:'药品名称(drugName)', name:'drugName', placeholder:'阿莫西林', default:'阿莫西林'},
|
|
|
+ {label:'搜索时间(searchedAt)', name:'searchedAt', placeholder:'2026-06-26T08:30:00.000Z', default:'2026-06-26T08:30:00.000Z'},
|
|
|
{label:'状态文案(statusText)', name:'statusText', placeholder:'已返回 2 个平台的可比价结果。', default:'已返回 2 个平台的可比价结果。'},
|
|
|
{label:'平台结果(platformResults)', name:'platformResults', placeholder:'[{"platformId":"yiyaocheng","platformName":"壹药城"}]', default:'[{"platformId":"yiyaocheng","platformName":"壹药城","response":{"status":"ok","products":[]}}]', type:'json'},
|
|
|
{label:'搜索日志(searchLog)', name:'searchLog', placeholder:'null', default:'null', type:'json'}], true, null, '保存一次完整的比价搜索快照,包含平台响应数据。platformResults和searchLog支持任意JSON结构')}
|
|
|
@@ -1546,11 +1589,78 @@ function resetStats() {
|
|
|
}
|
|
|
|
|
|
function clearAllResults() {
|
|
|
+ stopAlipayPolling();
|
|
|
+ closeAlipayModal();
|
|
|
document.querySelectorAll('.response-box').forEach(b => b.style.display = 'none');
|
|
|
document.querySelectorAll('.status-badge').forEach(b => { b.textContent = ''; b.className = 'status-badge'; });
|
|
|
resetStats();
|
|
|
}
|
|
|
|
|
|
+// ===================== 支付宝支付弹窗 =====================
|
|
|
+let currentAlipayModal = null;
|
|
|
+let currentAlipayOrderNo = null;
|
|
|
+function showAlipayModal(qrBase64, amount, orderNo) {
|
|
|
+ closeAlipayModal();
|
|
|
+ currentAlipayOrderNo = orderNo;
|
|
|
+ const overlay = document.createElement('div');
|
|
|
+ overlay.className = 'alipay-modal-overlay';
|
|
|
+ overlay.id = 'alipay-modal-overlay';
|
|
|
+ overlay.onclick = (e) => { if (e.target === overlay) closeAlipayModal(); };
|
|
|
+ const amountText = amount ? '<span>¥</span>' + amount : '';
|
|
|
+ overlay.innerHTML = '<div class="alipay-modal">'
|
|
|
+ + '<button class="alipay-modal-close" onclick="closeAlipayModal()" title="关闭">×</button>'
|
|
|
+ + '<div class="alipay-modal-logo">'
|
|
|
+ + '<svg viewBox="0 0 1024 1024"><path fill="#1677ff" d="M876.8 128H147.2C114.9 128 89 154.4 89 186.7v650.6c0 32.3 25.9 58.7 58.2 58.7h729.6c32.3 0 58.2-26.4 58.2-58.7V186.7c.6-32.3-25.3-58.7-57.2-58.7zM512 714.7c-112 0-202.7-91.2-202.7-203.5 0-44.8 14.4-86.4 39.5-120.3C320 416 288 416 256 416V352c53.3 0 101.3-10.7 144-32 35.2-17.6 66.7-41.6 92.8-70.4 27.2-30.4 48-65.6 61.9-104h140.8v64h-64c8.5 30.9 13.3 63.5 13.3 97.1 0 48-9.6 92.8-27.7 133.3C690.7 464 729.6 480 768 480v64c-53.3 0-101.3-10.7-144-32-19.2 34.1-47.5 62.9-81.6 83.2 74.7 21.3 153.6 26.7 234.7 16v64c-93.9 10.7-185.6 3.2-270.9-26.7C464 693.3 432 704 400 704v-64c32 0 64-2.1 94.9-6.4C458.7 604.8 437.3 558.9 437.3 509.3c0-93.9 76.8-170.1 171.5-170.1 10.7 0 21.3 1.1 31.5 3.2-4.8-25.1-7.5-51.2-7.5-78.4 0-9.6.5-19.2 1.6-28.3-43.2 21.3-92.8 34.1-145.6 36.8V192c85.3 6.4 162.1 41.6 220.3 96 29.9-5.9 61.3-9.1 93.3-9.1V341.3c-26.7 0-52.8 2.7-77.9 7.5C755.2 384 768 416 768 448v32h-64v-32c0-53.3-43.7-96.5-97.1-96.5-42.7 0-78.9 27.7-92.3 65.6 36.3 15.5 61.9 51.7 61.9 94.1 0 56.5-46.1 102.4-102.9 102.4S370.7 565.3 370.7 509.3c0-71.5 61.3-129.6 136.5-129.6 4.3 0 8.5.5 12.8 1.1-8-29.9-12.3-61.3-12.3-93.9 0-4.3.5-8.5.5-12.8-52.3 4.8-100.3 22.4-140.3 49.6-34.1 23.5-62.4 53.3-83.2 88-18.7 31.5-29.3 68.3-29.3 107.2 0 112 90.7 203.5 202.7 203.5 62.9 0 118.9-28.8 156.3-73.6l48 42.7C660.5 677.3 590.9 714.7 512 714.7z"/></svg>'
|
|
|
+ + '支付宝'
|
|
|
+ + '</div>'
|
|
|
+ + '<div class="alipay-modal-title">扫一扫付款</div>'
|
|
|
+ + '<div class="alipay-modal-amount">' + amountText + '</div>'
|
|
|
+ + '<div class="alipay-modal-qr"><img src="' + qrBase64 + '" alt="支付宝付款码"></div>'
|
|
|
+ + '<div class="alipay-modal-status" id="alipay-modal-status">轮询支付状态中...</div>'
|
|
|
+ + '<div class="alipay-modal-hint">二维码有效期 15 分钟<br>请尽快完成付款</div>'
|
|
|
+ + '</div>';
|
|
|
+ document.body.appendChild(overlay);
|
|
|
+ currentAlipayModal = overlay;
|
|
|
+ if (orderNo) startAlipayPolling(orderNo);
|
|
|
+}
|
|
|
+function closeAlipayModal() {
|
|
|
+ if (currentAlipayModal) {
|
|
|
+ currentAlipayModal.remove();
|
|
|
+ currentAlipayModal = null;
|
|
|
+ currentAlipayOrderNo = null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ===================== 支付宝支付轮询 =====================
|
|
|
+let alipayPollTimer = null;
|
|
|
+function startAlipayPolling(orderNo) {
|
|
|
+ stopAlipayPolling();
|
|
|
+ const statusEl = document.getElementById('alipay-modal-status');
|
|
|
+ if (statusEl) statusEl.textContent = '轮询支付状态中...';
|
|
|
+ alipayPollTimer = setInterval(async () => {
|
|
|
+ try {
|
|
|
+ const res = await api('GET', '/api/payment/order/' + orderNo, null, true);
|
|
|
+ const status = res.data?.data?.status;
|
|
|
+ const el = document.getElementById('alipay-modal-status');
|
|
|
+ if (!el || !currentAlipayModal) { stopAlipayPolling(); return; }
|
|
|
+ if (status === 'PAID') {
|
|
|
+ stopAlipayPolling();
|
|
|
+ el.innerHTML = '<span style="color:#2e7d32;font-weight:600;">✓ 支付成功!订单已激活</span>';
|
|
|
+ } else if (status === 'EXPIRED') {
|
|
|
+ stopAlipayPolling();
|
|
|
+ el.innerHTML = '<span style="color:#c62828;">订单已过期</span>';
|
|
|
+ } else {
|
|
|
+ el.textContent = '轮询支付状态中... (当前: ' + (status || '未知') + ')';
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('轮询支付状态异常:', e);
|
|
|
+ }
|
|
|
+ }, 3000);
|
|
|
+}
|
|
|
+function stopAlipayPolling() {
|
|
|
+ if (alipayPollTimer) { clearInterval(alipayPollTimer); alipayPollTimer = null; }
|
|
|
+}
|
|
|
+
|
|
|
// 用户侧快捷批量运行
|
|
|
function runAuthTests() { runBatchTests('auth'); }
|
|
|
function runCrawlerTests() { runBatchTests('crawler'); }
|