Files
xhs-auto-comment/buy.js
T
personal82555 6a8d395eb9 fix: 到期时间改取验证接口真实数据,不再把1天卡显示成永久;激活结果回读本机状态确认 v1.8.6
用户真实购买1天试用卡后投诉:①页面显示「到期时间:永久」(orders/query 不返回 expire_time,插件兜底写死永久)②对自动激活存疑。修复:authQueryOrder 从 verify 的 licenseState 透传真实 expire_time/is_permanent/remaining_days,showCard 优先级=永久卡→永久有效、限时卡→真实日期+剩余天数、兜底→套餐名,删除「默认永久」;activated 后回读 authGetStatus 确认 state=authorized 才算成功,否则走失败态;成功/失败两态均显示「如侧边栏未显示已授权→复制卡密→侧边栏粘贴激活」兜底指引。
2026-10-21 11:01:32 +08:00

187 lines
7.8 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 && res.confirmed !== false);
// 到期信息:永久 → 永久;有 expire_time → 具体日期(含剩余天数);都没有 → 显示套餐名。绝不默认「永久」
let expireLine;
if (res.isPermanent) {
expireLine = '到期时间:永久有效';
} else if (res.expireTime) {
expireLine = '到期时间:' + res.expireTime + (res.remainingDays ? `(剩 ${res.remainingDays} 天)` : '');
} else if (res.cardTypeName) {
expireLine = '套餐:' + res.cardTypeName;
} else {
expireLine = '到期时间以侧边栏授权状态为准';
}
const manualTip = '如侧边栏顶部未显示「已授权」,请复制上方卡密 → 回到插件侧边栏「输入卡密」框粘贴 → 点「激活」即可。';
status.className = 'order-status ' + (ok ? 'success' : 'error');
status.innerHTML = ok
? `<div class="ok-title">✓ 支付成功,已自动激活本机</div>
<div class="ok-line">${esc(expireLine)}</div>
<div class="ok-line muted">回到插件侧边栏即可使用全部功能。</div>
<div class="ok-line muted">${esc(manualTip)}</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">${esc(manualTip)}</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();