9b37568fdb
Design Source Checks / design-source (push) Successful in 16s
- 新增 tools/check-l1-sync.mjs:L1 同源四道——tokens 快照逐字节、icons 两端同集同内容、官网 token 值对齐原型主题 A、web 硬编码 hex 扫描 (白名单 #fff/#1677ff,ds-allow 可豁免);注入测试验证红绿行为 - 新增 .gitea/workflows/checks.yml:pull_request + push(main) 触发, 串跑 check-ds 12 道 / check-l1-sync / check_ds_code / codegen 新鲜度 (regen→dart format→零 diff,消除重跑假 diff 问题) - 修复 web/checkout.njk 对公转账图标底色硬编码 #8A94A6 → var(--faint) - CLAUDE.md「设计真相源分层」登记 CI 闸与本地等价命令 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
177 lines
8.3 KiB
Plaintext
177 lines
8.3 KiB
Plaintext
---
|
||
layout: base.njk
|
||
title: 确认订单
|
||
description: 选择套餐与计费周期,在线支付开通或续费门店授权。
|
||
permalink: /checkout/
|
||
---
|
||
{# 官网内购买/续费(需登录):套餐+周期联动汇总 → POST /api/v1/license/purchase
|
||
拿 pay_url 跳收银台(契约 pay-contract v1.0.0)。版式仿原型 screens/checkout.html。 #}
|
||
<div class="container" style="padding:40px 24px 72px">
|
||
<div class="crumb"><a href="/#pricing">购买授权</a><span>/</span><span>确认订单</span></div>
|
||
<h1 class="co-title">确认订单</h1>
|
||
|
||
<div class="checkout">
|
||
<div>
|
||
<div class="co-card">
|
||
<h2>选择套餐</h2>
|
||
<div class="seg" id="planSeg">
|
||
<button class="seg-btn on" data-v="standard">标准版</button>
|
||
<button class="seg-btn promo" data-v="promo" id="promoSegBtn">🔥 首月特惠 ¥1</button>
|
||
<button class="seg-btn" data-v="pro">高级版</button>
|
||
</div>
|
||
<div class="promo-banner" id="promoBanner" style="display:none">
|
||
<b>新店专享 · 仅 ¥1 体验 30 天</b>
|
||
解锁标准版全部功能,原价 ¥299,每个门店限购一次——试过才知道多省心。
|
||
</div>
|
||
<ul class="co-feats" id="planFeats"></ul>
|
||
<div class="seg-label" id="cycleLabel">计费周期</div>
|
||
<div class="seg" id="cycleSeg">
|
||
<button class="seg-btn on" data-v="annual">年付 <span class="seg-tip">省两个月</span></button>
|
||
<button class="seg-btn" data-v="monthly">月付</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="co-card">
|
||
<h2>订单内容</h2>
|
||
<div class="co-item">
|
||
<div class="co-ic"><svg class="ic"><use href="#i-ic07"/></svg></div>
|
||
<div class="co-meta"><b id="itName">标准版授权</b><span id="itDur">365 天授权 · 到期时间自动叠加</span></div>
|
||
<div class="co-amt" id="itAmt">¥2,999</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="co-card">
|
||
<h2>付款方式</h2>
|
||
<div class="co-pay">
|
||
<div class="pm on">
|
||
<div class="pic" style="background:#1677FF"><svg class="ic"><use href="#i-ic08"/></svg></div>
|
||
<div class="nm"><b>支付宝</b><span>电脑扫码 / 手机拉起支付宝,即时到账</span></div>
|
||
<div class="radio"></div>
|
||
</div>
|
||
<div class="pm off">
|
||
<div class="pic" style="background:var(--faint)"><svg class="ic"><use href="#i-ic11"/></svg></div>
|
||
<div class="nm"><b>对公转账</b><span>需要合同与对公收款请<a href="mailto:{{ site.support.email }}">联系我们</a></span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="co-card co-summary">
|
||
<h2>费用汇总</h2>
|
||
<div class="sum-row"><span id="sumName">标准版 · 年付</span><span id="sumBase">¥2,999</span></div>
|
||
<div class="sum-row muted"><span>优惠</span><span id="sumOff">− ¥0</span></div>
|
||
<div class="sum-div"></div>
|
||
<div class="sum-total"><span class="lab">应付总额</span><span class="val" id="sumTotal">¥2,999</span></div>
|
||
<div class="auth-alert" id="coErr" style="margin-top:12px"></div>
|
||
<button class="btn primary lg" id="coBtn" style="width:100%;margin-top:16px">提交订单</button>
|
||
<div class="fine">提交后跳转支付宝收银台,支付成功即时续期到当前门店。时长在现有到期时间上叠加,不覆盖。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
var API = '{{ site.appBaseUrl }}';
|
||
function $(id) { return document.getElementById(id); }
|
||
function gotoLogin() { location.href = '/login/?next=' + encodeURIComponent('/checkout/' + location.search); }
|
||
// access token 只有 60 分钟,只要 refresh token(7 天)还在就算登录,请求时自动续签
|
||
if (!jiuAuth.loggedIn()) { gotoLogin(); return; }
|
||
|
||
/* 套餐目录:价格仅前端展示,实付金额以 pay 套餐表为准(下单响应回传) */
|
||
var PLANS = {
|
||
standard: {
|
||
name: '标准版',
|
||
feats: ['单门店 · 单仓库', '2 台客户端同时使用', '1,000 张商品图片分享'],
|
||
monthly: { biz: 'monthly_standard', price: 299 },
|
||
annual: { biz: 'annual_standard', price: 2999 },
|
||
},
|
||
promo: {
|
||
name: '首月特惠',
|
||
feats: ['标准版全部功能,一分不少', '单门店 · 单仓库 · 2 台客户端', '1,000 张商品图片分享', '每个门店限购一次'],
|
||
single: { biz: 'promo_first_month', price: 1, orig: 299 },
|
||
},
|
||
pro: {
|
||
name: '高级版',
|
||
feats: ['单门店 · 多仓库', '5 台客户端同时使用', '10,000 张商品图片分享', '免费 AI 周度 / 月度商业数据分析'],
|
||
monthly: { biz: 'monthly_pro', price: 599 },
|
||
annual: { biz: 'annual_pro', price: 5999 },
|
||
},
|
||
};
|
||
var q = new URLSearchParams(location.search);
|
||
var plan = PLANS[q.get('plan')] ? q.get('plan') : 'standard';
|
||
var cycle = 'annual';
|
||
|
||
function fmt(n) { return '¥' + n.toLocaleString('zh-CN'); }
|
||
function selected() { return plan === 'promo' ? PLANS.promo.single : PLANS[plan][cycle]; }
|
||
function render() {
|
||
var p = PLANS[plan], c = selected(), isPromo = plan === 'promo';
|
||
var days = isPromo || cycle === 'monthly' ? 30 : 365;
|
||
var cyc = isPromo ? '仅 ¥1 · 30 天' : (cycle === 'annual' ? '年付' : '月付');
|
||
$('promoBanner').style.display = isPromo ? 'block' : 'none';
|
||
$('cycleLabel').style.display = isPromo ? 'none' : '';
|
||
$('cycleSeg').style.display = isPromo ? 'none' : '';
|
||
$('planFeats').innerHTML = p.feats.map(function (f) {
|
||
return '<li><svg class="ic"><use href="#i-check"/></svg>' + f + '</li>';
|
||
}).join('');
|
||
$('itName').textContent = p.name + '授权 · ' + cyc;
|
||
$('itDur').textContent = days + ' 天授权 · 到期时间自动叠加';
|
||
$('itAmt').textContent = fmt(c.price);
|
||
$('sumName').textContent = p.name + (isPromo ? '(原价 ¥299/月)' : ' · ' + cyc);
|
||
$('sumBase').textContent = fmt(isPromo ? c.orig : c.price);
|
||
$('sumOff').textContent = '− ' + fmt(isPromo ? c.orig - c.price : 0);
|
||
$('sumTotal').textContent = fmt(c.price);
|
||
document.querySelectorAll('#planSeg .seg-btn').forEach(function (b) { b.classList.toggle('on', b.dataset.v === plan); });
|
||
document.querySelectorAll('#cycleSeg .seg-btn').forEach(function (b) { b.classList.toggle('on', b.dataset.v === cycle); });
|
||
}
|
||
document.querySelectorAll('#planSeg .seg-btn').forEach(function (b) {
|
||
b.addEventListener('click', function () {
|
||
if (b.disabled) return;
|
||
plan = b.dataset.v; render();
|
||
});
|
||
});
|
||
document.querySelectorAll('#cycleSeg .seg-btn').forEach(function (b) {
|
||
b.addEventListener('click', function () { cycle = b.dataset.v; render(); });
|
||
});
|
||
render();
|
||
|
||
// 首月特惠每店限一次:已享用则该档置灰不可选
|
||
jiuAuth.authFetch(API, '/api/v1/license/promo-status')
|
||
.then(function (r) { return r.ok ? r.json() : null; })
|
||
.then(function (j) {
|
||
if (!j || !j.data || !j.data.used) return;
|
||
var b = $('promoSegBtn');
|
||
b.disabled = true;
|
||
b.classList.add('used');
|
||
b.innerHTML = '首月特惠(已享受)';
|
||
if (plan === 'promo') { plan = 'standard'; render(); }
|
||
})
|
||
.catch(function () {});
|
||
|
||
function showErr(msg) { var el = $('coErr'); el.textContent = msg; el.classList.add('show'); }
|
||
$('coBtn').addEventListener('click', function () {
|
||
$('coErr').classList.remove('show');
|
||
var btn = $('coBtn'); btn.disabled = true; btn.textContent = '正在创建订单…';
|
||
jiuAuth.authFetch(API, '/api/v1/license/purchase', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ biz_code: selected().biz }),
|
||
})
|
||
.then(function (r) {
|
||
// authFetch 已自动用 refresh_token 续签重试过,仍 401 才是真过期
|
||
if (r.status === 401) { gotoLogin(); throw new Error('登录已过期'); }
|
||
return r.json().then(function (j) { return { ok: r.ok, j: j }; });
|
||
})
|
||
.then(function (res) {
|
||
if (!res.ok) throw new Error((res.j && res.j.error) || '下单失败,请稍后重试');
|
||
var d = res.j.data || {};
|
||
if (!d.pay_url) throw new Error('下单响应异常');
|
||
location.href = d.pay_url;
|
||
})
|
||
.catch(function (e) {
|
||
showErr(e && e.message ? e.message : '无法连接服务器,请稍后重试');
|
||
btn.disabled = false; btn.textContent = '提交订单';
|
||
});
|
||
});
|
||
})();
|
||
</script>
|