/* 当前商品选择页:选套餐 → 选支付方式 → 下单 → 支付 → 自动激活本机 */ const $ = (id) => document.getElementById(id); let packages = []; let selected = null; let orderNo = null; let pollTimer = null; let lastPayType = null; // 记住当前支付方式,「重新下单」时直接复用 let payHelpShown = false; let helpTipStage = 0; // 1=30秒提示 2=120秒提示;同阶段不重复覆盖刚点出来的反馈 function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); } function durationText(days) { const n = Number(days); if (!n) return '限时体验'; if (n >= 999999) return '永久有效'; return n + ' 天'; } async function loadPackages() { $('package-list').innerHTML = '
正在加载当前商品…
'; try { const res = await chrome.runtime.sendMessage({ action: 'getCardTypes' }); if (!res || !res.ok) throw new Error((res && res.message) || '加载失败'); packages = (res.data || []).filter((p) => Number(p.price) > 0); if (!packages.length) { $('package-list').innerHTML = '
当前暂无在售商品,请稍后再试
'; return; } renderPackages(); } catch (e) { $('package-list').innerHTML = '
商品加载失败:' + esc(e.message) + '
'; } } function renderPackages() { $('package-list').innerHTML = packages.map((p) => `
${esc(p.name)}
¥${esc(p.price)}
${esc(durationText(p.duration_days))}
`).join(''); document.querySelectorAll('.pkg-btn').forEach((btn) => { btn.addEventListener('click', () => selectPackage(btn.dataset.id)); }); } function selectPackage(id) { selected = packages.find((p) => String(p.id) === String(id)); if (!selected) return; document.querySelectorAll('.package').forEach((el) => { el.classList.toggle('active', el.dataset.id === String(id)); }); $('chosen-package').innerHTML = `已选商品:${esc(selected.name)} 价格 ¥${esc(selected.price)} ${esc(durationText(selected.duration_days))}`; $('step-pay').hidden = false; $('step-pay').scrollIntoView({ behavior: 'smooth', block: 'center' }); } async function createOrder(payType) { if (!selected) return; lastPayType = payType; $('order-panel').hidden = false; $('order-panel').scrollIntoView({ behavior: 'smooth', block: 'center' }); hidePayHelp(); const status = $('order-status'); status.className = 'order-status pending'; status.textContent = '⏳ 正在创建订单…'; try { const res = await chrome.runtime.sendMessage({ action: 'authCreateOrder', payType, cardTypeId: selected.id }); if (!res || !res.ok) throw new Error((res && res.message) || '创建订单失败'); orderNo = res.order.orderNo; status.textContent = `订单 ${orderNo}(¥${res.order.amount})已创建,正在打开支付页面…`; if (res.payUrl) window.open(res.payUrl, '_blank'); startPolling(); } catch (e) { status.className = 'order-status error'; status.textContent = '✗ ' + e.message; } } let attempts = 0; /** 单次订单查询:5 秒轮询与「立即刷新核对」按钮共用同一段逻辑 */ async function queryOrderOnce() { attempts++; if (attempts > 240) { clearInterval(pollTimer); pollTimer = null; const status = $('order-status'); status.className = 'order-status error'; status.textContent = `✗ 等待支付超时。订单号 ${orderNo},如已付款请把订单号发给官网客服处理。`; return; } try { const res = await chrome.runtime.sendMessage({ action: 'authQueryOrder', orderNo }); if (!res || !res.ok) return; // 只要拿到卡密就先展示出来,不等激活结果——自动激活失败时用户手里必须有码 if (res.cardKey) showCardBox(res.cardKey, !!res.activated); if (res.activated) { clearInterval(pollTimer); pollTimer = null; showActivated(res); } else if (res.cardKey) { clearInterval(pollTimer); pollTimer = null; hidePayHelp(); const status = $('order-status'); status.className = 'order-status error'; status.innerHTML = '✗ 已支付并已发卡,但自动激活未成功(' + esc(res.message || '本机绑定可能被占用') + ')。请用上方卡密手动激活:回到插件侧边栏 → 卡密输入框粘贴 → 点「粘贴卡密并激活」。
' + '订单号:' + esc(orderNo || '') + ''; } else if (res.paid) { // 已付款但服务端还没把卡发出来(最常见原因是卡池库存不足) const waited = attempts * 5; const status = $('order-status'); status.className = 'order-status pending'; status.textContent = '⏳ 已支付,正在为你发卡…(订单 ' + orderNo + ',已等待 ' + waited + ' 秒)'; if (waited >= 90) { status.className = 'order-status error'; status.innerHTML = '⏳ 已支付,但发卡耗时较长(已 ' + waited + ' 秒)。订单 ' + esc(orderNo) + ' 付款状态为已支付,权益不会丢失:继续等会自动发放,或凭此订单号联系客服补发。'; } } else { const waited = attempts * 5; const status = $('order-status'); status.className = 'order-status pending'; status.textContent = '⏳ 等待支付中…(订单 ' + orderNo + ',已等待 ' + waited + ' 秒)'; // 30 秒还没动静就主动给出口(付款通知丢失 / 扫了过期码 / 实际没扣款) if (waited >= 30) showPayHelp(waited); } } catch (e) { /* 单次查询失败不中断轮询 */ } } function startPolling() { if (pollTimer) clearInterval(pollTimer); attempts = 0; hidePayHelp(); queryOrderOnce(); // 先立刻查一次,不干等 5 秒 pollTimer = setInterval(queryOrderOnce, 5000); } /** 付款迟迟没动静时给出路,别让用户对着「等待支付中」干等 */ function showPayHelp(waited) { const box = $('pay-help'); if (!box) return; box.hidden = false; payHelpShown = true; const tip = $('ph-tip'); if (!tip) return; const stage = waited >= 120 ? 2 : 1; if (tip.textContent && stage <= helpTipStage) return; // 不覆盖用户刚点「刷新」得到的反馈 helpTipStage = stage; if (stage === 2) { tip.textContent = '已等待 ' + waited + ' 秒仍未确认支付。若你确已完成付款:请勿重复支付,留好订单号 ' + (orderNo || '') + ' 与微信/支付宝交易号;若二维码是旧的/已过期,点「重新下单」换一个新码再付。'; } else { tip.textContent = '正在等待付款通知…已完成付款的话,点「立即刷新核对」可以马上重查一次。'; } } function hidePayHelp() { const box = $('pay-help'); if (box) box.hidden = true; payHelpShown = false; helpTipStage = 0; const tip = $('ph-tip'); if (tip) tip.textContent = ''; } function wirePayHelp() { const rf = $('ph-refresh'), rn = $('ph-renew'); if (rf) rf.addEventListener('click', async () => { const tip = $('ph-tip'); if (tip) tip.textContent = '正在向授权系统核对…'; await queryOrderOnce(); const box = $('pay-help'); if (!box || box.hidden || !tip) return; // 已出卡激活就不必再提示 tip.textContent = '已于 ' + new Date().toLocaleTimeString() + ' 重新核对:仍未确认支付。若你确已完成付款请勿重复支付(通知可能延迟);二维码过期就点「重新下单」。'; }); if (rn) rn.addEventListener('click', () => { if (!selected) { const tip = $('ph-tip'); if (tip) tip.textContent = '请先在上方重新选择套餐,再选支付方式生成新订单。'; const sp = $('step-package'); if (sp) sp.scrollIntoView({ behavior: 'smooth', block: 'start' }); return; } if (pollTimer) { clearInterval(pollTimer); pollTimer = null; } createOrder(lastPayType || 'wxpay'); }); } /** 支付成功后立刻把卡密展示给用户(含自动激活失败的场景) */ function showCardBox(key, activated) { const box = $('card-box'); if (!box || !key) return; if (!box.hidden && $('cb-key-text').textContent === key) return; $('cb-key-text').textContent = key; box.hidden = false; $('cb-copy-tip').textContent = activated ? '已自动激活本机,这是你的卡密,建议留存' : ''; box.scrollIntoView({ behavior: 'smooth', block: 'center' }); } function showActivated(res) { hidePayHelp(); const status = $('order-status'); const permanent = !!res.isPermanent; const exp = String(res.expireTime || '').trim(); const plan = String(res.planName || '').trim(); // 绝不默认「永久」:到期时间只在服务端明确给出时才显示 let expireLine; if (permanent) expireLine = '到期时间:永久'; else if (exp) expireLine = '到期时间:' + esc(exp) + ''; else if (plan) expireLine = '套餐:' + esc(plan) + '(实际到期时间以插件侧边栏「已授权 · 到期 …」为准)'; else expireLine = '到期时间:以插件侧边栏显示为准'; status.className = 'order-status success'; status.innerHTML = `
✓ 支付成功,已自动激活
卡密:${esc(res.cardKey || '')}
${expireLine}
回到插件侧边栏即可使用全部功能(本机已授权)。
`; } document.querySelectorAll('.pay-btn').forEach((btn) => { btn.addEventListener('click', () => createOrder(btn.dataset.pay)); }); // 复制卡密:优先 clipboard API,无权限时降级 execCommand $('cb-copy') && $('cb-copy').addEventListener('click', async () => { const key = ($('cb-key-text').textContent || '').trim(); if (!key) return; let ok = false; try { await navigator.clipboard.writeText(key); ok = true; } catch (e) { /* 降级 */ } if (!ok) { try { const ta = document.createElement('textarea'); ta.value = key; ta.style.position = 'fixed'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.select(); ok = document.execCommand('copy'); document.body.removeChild(ta); } catch (e2) { ok = false; } } $('cb-copy-tip').textContent = ok ? '已复制,可直接粘贴到侧边栏激活' : '复制失败,请手动长按选择卡密'; }); wirePayHelp(); loadPackages();