Files
xhs-auto-comment/buy.js
T
personal82555 5be9027fe7 feat: 支付成功页直接显示卡密(激活失败也显示+手动指引)v1.8.4
购买页轮询到服务端发卡即渲染卡密框:等宽大字 + 一键复制(clipboard API + execCommand 降级)。自动激活成功显示「已自动激活本机」;失败显示「自动激活未生效」并指引粘贴卡密到侧边栏手动激活——付了钱拿不到卡密、只能找客服的情况不再出现。
2026-10-20 22:17:52 +08:00

171 lines
7.0 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
/* 购买授权页:选套餐 → 选支付方式 → 下单 → 支付 → 自动激活 */
const $ = (id) => document.getElementById(id);
let packages = [];
let selected = null;
let orderNo = null;
let pollTimer = null;
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, (c) => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
}[c]));
}
function durationText(days) {
const n = Number(days);
if (!n) return '限时体验';
if (n >= 999999) return '永久有效';
return n + ' 天';
}
async function loadPackages() {
$('package-list').innerHTML = '<div class="loading">正在加载套餐…</div>';
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 = '<div class="empty">暂无在售套餐,请稍后再试</div>';
return;
}
renderPackages();
} catch (e) {
$('package-list').innerHTML = '<div class="empty">套餐加载失败:' + esc(e.message) + '</div>';
}
}
function renderPackages() {
$('package-list').innerHTML = packages.map((p) => `
<div class="package" data-id="${esc(p.id)}">
<div class="pkg-name">${esc(p.name)}</div>
<div class="pkg-price">¥${esc(p.price)}</div>
<div class="pkg-dur">${esc(durationText(p.duration_days))}</div>
<button class="pkg-btn" data-id="${esc(p.id)}">选择此套餐</button>
</div>`).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 = `已选套餐:<b>${esc(selected.name)}</b> 价格 <b>¥${esc(selected.price)}</b> ${esc(durationText(selected.duration_days))}`;
$('step-pay').hidden = false;
$('step-pay').scrollIntoView({ behavior: 'smooth', block: 'center' });
}
async function createOrder(payType) {
if (!selected) return;
$('order-panel').hidden = false;
$('order-panel').scrollIntoView({ behavior: 'smooth', block: 'center' });
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;
const priceText = res.order.couponCode
? `¥${res.order.amount}(原价 ¥${res.order.originalPrice},已用优惠码 ${res.order.couponCode})`
: `¥${res.order.amount}`;
status.textContent = `订单 ${orderNo}(${priceText})已创建,正在打开支付页面…`;
if (res.payUrl) window.open(res.payUrl, '_blank');
startPolling();
} catch (e) {
status.className = 'order-status error';
status.textContent = '✗ ' + e.message;
}
}
function startPolling() {
if (pollTimer) clearInterval(pollTimer);
let attempts = 0;
pollTimer = setInterval(async () => {
attempts++;
if (attempts > 240) {
clearInterval(pollTimer);
pollTimer = null;
const status = $('order-status');
status.className = 'order-status error';
status.textContent = `✗ 等待支付超时。订单号 ${orderNo},如已付款请把订单号发给客服QQ 1556688283 手动补发。`;
return;
}
try {
const res = await chrome.runtime.sendMessage({ action: 'authQueryOrder', orderNo });
if (!res || !res.ok) return;
if (res.cardKey) {
// 只要服务端已发卡就把卡密显示出来——哪怕自动激活失败,用户也能自己复制激活
clearInterval(pollTimer);
pollTimer = null;
showCard(res);
} else {
const status = $('order-status');
status.className = 'order-status pending';
status.textContent = `⏳ 等待支付中…(订单 ${orderNo},已等待 ${attempts * 5} 秒)`;
}
} catch (e) {
/* 单次查询失败不中断轮询 */
}
}, 5000);
}
// 支付成功:显示卡密(激活成功/失败都显示,激活失败时给出手动激活指引)
function showCard(res) {
const status = $('order-status');
const ok = !!res.activated;
status.className = 'order-status ' + (ok ? 'success' : 'error');
status.innerHTML = ok
? `<div class="ok-title">✓ 支付成功,已自动激活本机</div>
<div class="ok-line">到期时间:${esc(res.expireTime || '永久')}</div>
<div class="ok-line muted">回到插件侧边栏即可使用全部功能。</div>`
: `<div class="ok-title">✓ 支付成功</div>
<div class="ok-line warn">自动激活未生效${(res.licenseState && res.licenseState.message) ? ':' + esc(res.licenseState.message) : ''}</div>
<div class="ok-line muted">请复制下面的卡密,回到插件侧边栏「输入卡密」框粘贴 → 点「激活」即可。</div>`;
$('card-key').textContent = res.cardKey || '';
$('card-hint').textContent = ok
? '卡密已随本机自动激活,无需手动输入;保存好可换机或重装后使用。'
: '本页不会关闭也不会失效,卡密就在这里,随时可以复制。';
$('card-box').hidden = false;
$('card-box').scrollIntoView({ behavior: 'smooth', block: 'center' });
}
// 复制卡密(剪贴板 API 失败时降级 execCommand)
$('copy-card').addEventListener('click', async () => {
const key = ($('card-key').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.cssText = 'position:fixed;left:-9999px';
document.body.appendChild(ta);
ta.select();
ok = document.execCommand('copy');
ta.remove();
} catch (e) { ok = false; }
}
$('copy-card').textContent = ok ? '✓ 已复制' : '复制失败,请手动选中卡密复制';
setTimeout(() => { $('copy-card').textContent = '复制卡密'; }, 2500);
});
document.querySelectorAll('.pay-btn').forEach((btn) => {
btn.addEventListener('click', () => createOrder(btn.dataset.pay));
});
loadPackages();