Files
jiu/web/checkout.njk
T
wangjia 9b37568fdb
Design Source Checks / design-source (push) Successful in 16s
ci: 设计真相源检查挂 CI(PR + main push 自动跑四道闸)
- 新增 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
2026-07-04 07:14:49 +08:00

177 lines
8.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
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.
---
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>