feat: 购买流程重做(新页选套餐→选支付方式)+ 支付后后台自动激活 v1.8.0
- 修复「点击购买授权没反应」:支付方式选择行被 .pay-select-row 的 display:flex 压过 hidden 属性常驻显示;新增全局 [hidden]{display:none!important}
- 点「购买授权」改为打开新页面 buy.html:①展示本项目全部在售套餐 ②选套餐后才显示支付方式 ③下单支付
- 自动激活改为后台 chrome.alarms 每分钟轮询(关掉侧边栏/支付页也能激活),侧边栏轮询保留为快速通道
- authCreateOrder 支持传入所选套餐 cardTypeId;新增 getCardTypes 消息
This commit is contained in:
1 parent
540f506c78
commit
9348d52a98
10 files changed
+522
-92
No files matched your search
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> 一键搜索 · 自动点击 · 智能评论 —— 小红书运营引流的效率神器
|
> 一键搜索 · 自动点击 · 智能评论 —— 小红书运营引流的效率神器
|
||||||
|
|
||||||
[](https://dl.88531.cn/xhs.html)
|
[](https://dl.88531.cn/xhs.html)
|
||||||
[](#安装方法)
|
[](#安装方法)
|
||||||
[](#开发说明)
|
[](#开发说明)
|
||||||
|
|
||||||
@@ -104,10 +104,16 @@
|
|||||||
|
|
||||||
## 📜 版本历史
|
## 📜 版本历史
|
||||||
|
|
||||||
当前版本:**1.7.9**
|
当前版本:**1.8.0**
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>展开查看完整变更记录(v1.2.0 ~ v1.7.9)</summary>
|
<summary>展开查看完整变更记录(v1.2.0 ~ v1.8.0)</summary>
|
||||||
|
|
||||||
|
### 1.8.0
|
||||||
|
- **购买流程重做**:点「购买授权」不再原地展开支付方式,而是**打开新页面** → ① 展示本项目全部在售套餐供选择 → ② 选套餐后才出现支付方式(支付宝/微信)→ ③ 下单支付,支付成功**自动发卡并激活本机**
|
||||||
|
- **支付后必定自动激活**:订单轮询从侧边栏搬到后台定时任务,**关掉侧边栏、关掉购买页、甚至重启浏览器**都能在支付完成后自动完成激活,不再出现「付了钱还显示未授权」
|
||||||
|
- **修复「点击购买授权没反应」**:支付方式选择行此前被 CSS 常驻显示(`display:flex` 压过了 `hidden` 属性),点击后视觉上毫无变化;现已修复,选择行默认隐藏、按需展开
|
||||||
|
- 套餐与价格实时读取服务端在售列表(不再写死金额),购买页会跳过 0 元测试套餐
|
||||||
|
|
||||||
### 1.7.9
|
### 1.7.9
|
||||||
- **试用策略升级「72 小时 + 100 条」**:时间与条数先到先停——拉长试用必须配用量上限,避免长时间白嫖评论量
|
- **试用策略升级「72 小时 + 100 条」**:时间与条数先到先停——拉长试用必须配用量上限,避免长时间白嫖评论量
|
||||||
|
|||||||
+56
-9
@@ -16,7 +16,7 @@ const DEFAULT_AI = {
|
|||||||
const AUTH_BASE = 'https://auth.88531.cn';
|
const AUTH_BASE = 'https://auth.88531.cn';
|
||||||
const AUTH_PROJECT_ID = 3;
|
const AUTH_PROJECT_ID = 3;
|
||||||
const AUTH_API_KEY = 'ak_bbf83b76df8219388d7f60b9e67c7db2';
|
const AUTH_API_KEY = 'ak_bbf83b76df8219388d7f60b9e67c7db2';
|
||||||
const CLIENT_VERSION = '1.7.9';
|
const CLIENT_VERSION = '1.8.0';
|
||||||
const TRIAL_MS = 72 * 60 * 60 * 1000; // 试用 72 小时(3 天)
|
const TRIAL_MS = 72 * 60 * 60 * 1000; // 试用 72 小时(3 天)
|
||||||
const TRIAL_QUOTA = 100; // 试用期最多 100 条评论(时间/条数先到先停,防止白嫖薅量)
|
const TRIAL_QUOTA = 100; // 试用期最多 100 条评论(时间/条数先到先停,防止白嫖薅量)
|
||||||
const GRACE_DAILY = 3; // 试用到期不硬锁:每天保留 3 条体验额度
|
const GRACE_DAILY = 3; // 试用到期不硬锁:每天保留 3 条体验额度
|
||||||
@@ -210,6 +210,13 @@ async function authOnInstalled() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('启动时授权复验失败:', error);
|
console.error('启动时授权复验失败:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 有未完成订单则恢复后台轮询(支付后自动激活不依赖侧边栏是否开着)
|
||||||
|
try {
|
||||||
|
await scheduleOrderPolling();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('恢复订单轮询失败:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册定时任务:心跳/复验/公告/更新检查
|
// 注册定时任务:心跳/复验/公告/更新检查
|
||||||
@@ -234,6 +241,8 @@ chrome.alarms?.onAlarm?.addListener(async (alarm) => {
|
|||||||
await authGetAnnouncements();
|
await authGetAnnouncements();
|
||||||
} else if (alarm.name === 'authUpdateCheck') {
|
} else if (alarm.name === 'authUpdateCheck') {
|
||||||
await authCheckUpdate();
|
await authCheckUpdate();
|
||||||
|
} else if (alarm.name === 'orderPoll') {
|
||||||
|
await pollPendingOrder();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('授权定时任务失败:', alarm.name, error);
|
console.error('授权定时任务失败:', alarm.name, error);
|
||||||
@@ -459,16 +468,19 @@ async function usageTick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建购买订单(contact_info 带机器码 → 支付成功后系统自动发卡绑定)
|
// 创建购买订单(contact_info 带机器码 → 支付成功后系统自动发卡绑定)
|
||||||
async function authCreateOrder(payType) {
|
async function authCreateOrder(payType, cardTypeId) {
|
||||||
const pay = payType === 'wxpay' ? 'wxpay' : 'alipay';
|
const pay = payType === 'wxpay' ? 'wxpay' : 'alipay';
|
||||||
|
|
||||||
// 取项目第一个套餐
|
// 读取在售套餐:购买页选定 cardTypeId 时用选中的,否则取第一个(兼容旧调用)
|
||||||
const typesRes = await authFetch(`/api/public/projects/${AUTH_PROJECT_ID}/card-types`);
|
const typesRes = await authFetch(`/api/public/projects/${AUTH_PROJECT_ID}/card-types`);
|
||||||
if (!typesRes.ok || !Array.isArray(typesRes.data) || typesRes.data.length === 0) {
|
if (!typesRes.ok || !Array.isArray(typesRes.data) || typesRes.data.length === 0) {
|
||||||
return { ok: false, message: '暂无在售套餐,请稍后再试' };
|
return { ok: false, message: '暂无在售套餐,请稍后再试' };
|
||||||
}
|
}
|
||||||
const cardType = typesRes.data[0];
|
const cardType = (cardTypeId ? typesRes.data.find((t) => String(t.id) === String(cardTypeId)) : null)
|
||||||
|
|| typesRes.data.find((t) => Number(t.price) > 0)
|
||||||
|
|| typesRes.data[0];
|
||||||
|
if (!cardType) return { ok: false, message: '套餐不存在,请重新选择' };
|
||||||
|
|
||||||
const machineId = await getDeviceId();
|
const machineId = await getDeviceId();
|
||||||
const price = Number(cardType.price);
|
const price = Number(cardType.price);
|
||||||
// 试用到期 24 小时内下单自动带限时优惠码(服务端按折扣重算实付金额)
|
// 试用到期 24 小时内下单自动带限时优惠码(服务端按折扣重算实付金额)
|
||||||
@@ -510,8 +522,34 @@ async function authCreateOrder(payType) {
|
|||||||
createdAt: Date.now()
|
createdAt: Date.now()
|
||||||
};
|
};
|
||||||
await chrome.storage.local.set({ pendingOrder });
|
await chrome.storage.local.set({ pendingOrder });
|
||||||
|
await scheduleOrderPolling();
|
||||||
return { ok: true, order: pendingOrder, payUrl: res.data.pay_url };
|
|
||||||
|
return { ok: true, order: pendingOrder, payUrl: res.data.pay_url, cardTypeName: cardType.name };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后台订单轮询:侧边栏/购买页关闭也能完成「支付 → 自动发卡 → 自动激活」
|
||||||
|
async function scheduleOrderPolling() {
|
||||||
|
if (!chrome.alarms) return;
|
||||||
|
const { pendingOrder } = await chrome.storage.local.get(['pendingOrder']);
|
||||||
|
if (!pendingOrder || !pendingOrder.orderNo) return;
|
||||||
|
await chrome.alarms.create('orderPoll', { periodInMinutes: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pollPendingOrder() {
|
||||||
|
const { pendingOrder } = await chrome.storage.local.get(['pendingOrder']);
|
||||||
|
if (!pendingOrder || !pendingOrder.orderNo) {
|
||||||
|
try { await chrome.alarms.clear('orderPoll'); } catch (e) { /* 无该 alarm */ }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const result = await authQueryOrder(pendingOrder.orderNo);
|
||||||
|
if (result && result.activated) {
|
||||||
|
await chrome.storage.local.remove(['pendingOrder']);
|
||||||
|
try { await chrome.alarms.clear('orderPoll'); } catch (e) { /* 无该 alarm */ }
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('后台订单轮询失败:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询订单(支付+自动发卡后返回 card_key;拿到卡密自动激活)
|
// 查询订单(支付+自动发卡后返回 card_key;拿到卡密自动激活)
|
||||||
@@ -586,7 +624,16 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.action === 'authCreateOrder') {
|
if (request.action === 'authCreateOrder') {
|
||||||
authCreateOrder(request.payType).then((res) => sendResponse(res));
|
authCreateOrder(request.payType, request.cardTypeId).then((res) => sendResponse(res));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.action === 'getCardTypes') {
|
||||||
|
authFetch(`/api/public/projects/${AUTH_PROJECT_ID}/card-types`).then((res) => {
|
||||||
|
sendResponse({ ok: !!res.ok, data: res.data || [], message: res.message });
|
||||||
|
}).catch((error) => {
|
||||||
|
sendResponse({ ok: false, message: error.message, data: [] });
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,256 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
background: #f6f7f9;
|
||||||
|
color: #222;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
max-width: 720px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 28px 18px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #fff;
|
||||||
|
background: #ff2442;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 4px 14px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head h1 {
|
||||||
|
font-size: 26px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 17px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.num {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ff2442;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package {
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 16px 14px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package:hover {
|
||||||
|
border-color: #ffb3bf;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.package.active {
|
||||||
|
border-color: #ff2442;
|
||||||
|
background: #fff6f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-name {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-price {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ff2442;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-dur {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #ff2442;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pkg-btn:hover {
|
||||||
|
background: #e01030;
|
||||||
|
}
|
||||||
|
|
||||||
|
.package.active .pkg-btn {
|
||||||
|
background: #e01030;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading,
|
||||||
|
.empty {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 24px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chosen {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chosen b {
|
||||||
|
color: #ff2442;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-methods {
|
||||||
|
display: flex;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 14px 0;
|
||||||
|
border: 2px solid #eee;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn.alipay {
|
||||||
|
color: #1677ff;
|
||||||
|
border-color: #bcd6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn.wxpay {
|
||||||
|
color: #09bb07;
|
||||||
|
border-color: #b6e6b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn.alipay:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pay-btn.wxpay:hover {
|
||||||
|
border-color: #09bb07;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f6f7f9;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status.pending {
|
||||||
|
color: #b26a00;
|
||||||
|
background: #fff8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status.error {
|
||||||
|
color: #c62828;
|
||||||
|
background: #fdecea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-status.success {
|
||||||
|
color: #1b5e20;
|
||||||
|
background: #eef7ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok-line {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ok-line b {
|
||||||
|
color: #1b5e20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.foot {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.foot a {
|
||||||
|
color: #ff2442;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>购买授权 · 小红书自动评论助手</title>
|
||||||
|
<link rel="stylesheet" href="buy.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
<header class="head">
|
||||||
|
<div class="logo">小红书自动评论助手</div>
|
||||||
|
<h1>购买授权</h1>
|
||||||
|
<p class="flow">① 选择套餐 → ② 选择支付方式 → ③ 完成支付(支付成功自动激活本机)</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section id="step-package">
|
||||||
|
<h2><span class="num">1</span> 选择套餐</h2>
|
||||||
|
<div id="package-list" class="package-list"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="step-pay" hidden>
|
||||||
|
<h2><span class="num">2</span> 选择支付方式</h2>
|
||||||
|
<p class="chosen" id="chosen-package"></p>
|
||||||
|
<div class="pay-methods">
|
||||||
|
<button class="pay-btn alipay" data-pay="alipay">支付宝</button>
|
||||||
|
<button class="pay-btn wxpay" data-pay="wxpay">微信支付</button>
|
||||||
|
</div>
|
||||||
|
<p class="tip">点击后会打开支付页面,付款完成回到本页会自动显示激活结果。</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="order-panel" hidden>
|
||||||
|
<h2><span class="num">3</span> 订单状态</h2>
|
||||||
|
<div id="order-status" class="order-status">等待创建订单…</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="foot">
|
||||||
|
<p>官网 <a href="https://dl.88531.cn/xhs.html" target="_blank" rel="noopener">dl.88531.cn/xhs.html</a> · 客服QQ 1556688283 · <a href="https://t.me/+7YVPMu9EOChkYmM1" target="_blank" rel="noopener">Telegram 交流群</a></p>
|
||||||
|
<p class="muted">支付成功后无需手动输入卡密,系统自动发卡并激活本机。</p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<script src="buy.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/* 购买授权页:选套餐 → 选支付方式 → 下单 → 支付 → 自动激活 */
|
||||||
|
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) => ({
|
||||||
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
||||||
|
}[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.activated) {
|
||||||
|
clearInterval(pollTimer);
|
||||||
|
pollTimer = null;
|
||||||
|
showActivated(res);
|
||||||
|
} else if (res.cardKey && !res.activated) {
|
||||||
|
clearInterval(pollTimer);
|
||||||
|
pollTimer = null;
|
||||||
|
const status = $('order-status');
|
||||||
|
status.className = 'order-status error';
|
||||||
|
status.textContent = '✗ 已支付但激活失败:' + ((res.licenseState && res.licenseState.message) || '请把订单号发给客服手动处理');
|
||||||
|
} else {
|
||||||
|
const status = $('order-status');
|
||||||
|
status.className = 'order-status pending';
|
||||||
|
status.textContent = `⏳ 等待支付中…(订单 ${orderNo},已等待 ${attempts * 5} 秒)`;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
/* 单次查询失败不中断轮询 */
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showActivated(res) {
|
||||||
|
const status = $('order-status');
|
||||||
|
status.className = 'order-status success';
|
||||||
|
status.innerHTML = `
|
||||||
|
<div class="ok-title">✓ 支付成功,已自动激活</div>
|
||||||
|
<div class="ok-line">卡密:<b>${esc(res.cardKey || '')}</b></div>
|
||||||
|
<div class="ok-line">到期时间:${esc(res.expireTime || '永久')}</div>
|
||||||
|
<div class="ok-line muted">回到插件侧边栏即可使用全部功能(本机已授权)。</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('.pay-btn').forEach((btn) => {
|
||||||
|
btn.addEventListener('click', () => createOrder(btn.dataset.pay));
|
||||||
|
});
|
||||||
|
|
||||||
|
loadPackages();
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "小红书自动评论助手",
|
"name": "小红书自动评论助手",
|
||||||
"version": "1.7.9",
|
"version": "1.8.0",
|
||||||
"description": "右侧侧边栏常驻的自动评论助手,支持试用/卡密授权、固定池/自定义文字/图片/AI评论",
|
"description": "右侧侧边栏常驻的自动评论助手,支持试用/卡密授权、固定池/自定义文字/图片/AI评论",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>小红书自动评论助手 · 隐私政策</h1>
|
<h1>小红书自动评论助手 · 隐私政策</h1>
|
||||||
<div class="meta">生效日期:2026年10月10日 · 适用版本:v1.7.9 及以上</div>
|
<div class="meta">生效日期:2026年10月10日 · 适用版本:v1.8.0 及以上</div>
|
||||||
|
|
||||||
<p>感谢使用「小红书自动评论助手」浏览器扩展(以下简称"本插件")。我们重视你的隐私。本页面说明本插件收集哪些信息、如何使用以及如何保护。</p>
|
<p>感谢使用「小红书自动评论助手」浏览器扩展(以下简称"本插件")。我们重视你的隐私。本页面说明本插件收集哪些信息、如何使用以及如何保护。</p>
|
||||||
|
|
||||||
|
|||||||
+8
-27
@@ -894,35 +894,11 @@ body {
|
|||||||
background: linear-gradient(135deg, #f57c00, #ff9800);
|
background: linear-gradient(135deg, #f57c00, #ff9800);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pay-select-row {
|
.buy-hint {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
|
||||||
|
|
||||||
.pay-select-label {
|
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #666;
|
line-height: 1.6;
|
||||||
}
|
color: #888;
|
||||||
|
|
||||||
.pay-btn {
|
|
||||||
padding: 6px 12px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: #fff;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pay-btn:hover {
|
|
||||||
border-color: #4caf50;
|
|
||||||
color: #2e7d32;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pay-cancel {
|
|
||||||
color: #999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-status {
|
.order-status {
|
||||||
@@ -1055,4 +1031,9 @@ body {
|
|||||||
@keyframes latest-version-pulse {
|
@keyframes latest-version-pulse {
|
||||||
0%, 100% { box-shadow: 0 4px 14px rgba(255, 36, 66, 0.45); }
|
0%, 100% { box-shadow: 0 4px 14px rgba(255, 36, 66, 0.45); }
|
||||||
50% { box-shadow: 0 4px 22px rgba(255, 36, 66, 0.75); }
|
50% { box-shadow: 0 4px 22px rgba(255, 36, 66, 0.75); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hidden 属性必须压过 display:flex 等规则,否则元素常驻显示(购买授权点了没反应即此原因) */
|
||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
}
|
}
|
||||||
+2
-7
@@ -26,12 +26,7 @@
|
|||||||
<span class="coupon-text">⏰ 试用到期限时优惠:码 <b id="coupon-code">XHS24H</b> · <span id="coupon-percent">8 折</span> · 剩余 <b id="coupon-left">--:--:--</b></span>
|
<span class="coupon-text">⏰ 试用到期限时优惠:码 <b id="coupon-code">XHS24H</b> · <span id="coupon-percent">8 折</span> · 剩余 <b id="coupon-left">--:--:--</b></span>
|
||||||
<button type="button" id="coupon-copy-btn" class="coupon-copy-btn">复制</button>
|
<button type="button" id="coupon-copy-btn" class="coupon-copy-btn">复制</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pay-select-row" id="pay-select-row" hidden>
|
<div class="buy-hint">点击「购买授权」将在<b>新页面</b>选择套餐与支付方式,支付成功后本机自动激活。</div>
|
||||||
<span class="pay-select-label">选择支付方式:</span>
|
|
||||||
<button class="pay-btn" data-pay="alipay">支付宝</button>
|
|
||||||
<button class="pay-btn" data-pay="wxpay">微信</button>
|
|
||||||
<button class="pay-btn pay-cancel" id="pay-cancel-btn">取消</button>
|
|
||||||
</div>
|
|
||||||
<div class="order-status" id="order-status"></div>
|
<div class="order-status" id="order-status"></div>
|
||||||
<div class="announcement-box" id="announcement-box" hidden>
|
<div class="announcement-box" id="announcement-box" hidden>
|
||||||
<div class="announcement-title">📢 公告</div>
|
<div class="announcement-title">📢 公告</div>
|
||||||
@@ -204,7 +199,7 @@
|
|||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="version-info">
|
<div class="version-info">
|
||||||
<span>版本: 1.7.9</span>
|
<span>版本: 1.8.0</span>
|
||||||
<span>侧边栏常驻 · 专注于自动评论</span>
|
<span>侧边栏常驻 · 专注于自动评论</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+3
-44
@@ -133,26 +133,13 @@ class AutoCommentManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 授权:购买(展开支付方式选择)
|
// 授权:购买(新页面选套餐 → 选支付方式 → 支付 → 自动激活)
|
||||||
const buyBtn = document.getElementById('buy-btn');
|
const buyBtn = document.getElementById('buy-btn');
|
||||||
if (buyBtn) {
|
if (buyBtn) {
|
||||||
buyBtn.addEventListener('click', () => {
|
buyBtn.addEventListener('click', () => {
|
||||||
const row = document.getElementById('pay-select-row');
|
chrome.tabs.create({ url: chrome.runtime.getURL('buy.html') });
|
||||||
if (row) row.hidden = !row.hidden;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const payCancelBtn = document.getElementById('pay-cancel-btn');
|
|
||||||
if (payCancelBtn) {
|
|
||||||
payCancelBtn.addEventListener('click', () => {
|
|
||||||
const row = document.getElementById('pay-select-row');
|
|
||||||
if (row) row.hidden = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
document.querySelectorAll('.pay-btn[data-pay]').forEach((btn) => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
this.startBuyFlow(btn.dataset.pay);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// API密钥配置
|
// API密钥配置
|
||||||
document.getElementById('api-key').addEventListener('input', (e) => {
|
document.getElementById('api-key').addEventListener('input', (e) => {
|
||||||
@@ -835,35 +822,7 @@ class AutoCommentManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 购买流程:下单(带机器码)→ 打开支付页 → 轮询订单 → 自动发卡激活
|
// 轮询订单:支付+自动发卡后自动激活(侧边栏打开时的快速通道;后台 alarm 兜底)
|
||||||
async startBuyFlow(payType) {
|
|
||||||
const payRow = document.getElementById('pay-select-row');
|
|
||||||
if (payRow) payRow.hidden = true;
|
|
||||||
|
|
||||||
this.showOrderStatus(true, '⏳ 正在创建订单…');
|
|
||||||
try {
|
|
||||||
const res = await chrome.runtime.sendMessage({ action: 'authCreateOrder', payType });
|
|
||||||
if (!res || !res.ok) {
|
|
||||||
this.showOrderStatus(false, '✗ 创建订单失败: ' + ((res && res.message) || '未知错误'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const order = res.order;
|
|
||||||
const priceText = order.couponCode
|
|
||||||
? `¥${order.amount},原价 ¥${order.originalPrice},已用优惠码 ${order.couponCode}`
|
|
||||||
: `¥${order.amount}`;
|
|
||||||
this.showOrderStatus(true, `订单 ${order.orderNo}(${priceText})已创建,请在新页面完成支付,支付成功后自动激活…`);
|
|
||||||
if (res.payUrl) {
|
|
||||||
chrome.tabs.create({ url: res.payUrl });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startOrderPolling(order.orderNo);
|
|
||||||
} catch (error) {
|
|
||||||
this.showOrderStatus(false, '✗ 购买失败: ' + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 轮询订单:支付+自动发卡后自动激活
|
|
||||||
startOrderPolling(orderNo) {
|
startOrderPolling(orderNo) {
|
||||||
if (this.orderPollTimer) {
|
if (this.orderPollTimer) {
|
||||||
clearInterval(this.orderPollTimer);
|
clearInterval(this.orderPollTimer);
|
||||||
|
|||||||
Reference in new issue
Block a user