feat(client): 购买页三档 + 支付页 render_type 多态(crypto/redirect/qr 预留)+ 导航接线

购买页三档 pro(月/季/年,金额来自 GET /v1/pay/catalog)选档 → 选支付方式 → 下单;
支付页按 session.render_type 多态渲染(crypto_address 地址+精确金额+复制、redirect
外链拉起、qr 预留复制兜底),轮询用 Task 6 的 paymentFlowProvider,409
CURRENCY_MISMATCH 走「换方式开新单」。同时收口 paymentFlowProvider 非 autoDispose
带来的轮询生命周期缺口——支付页 dispose 时停轮询(cancel() 延后到微任务执行,避免
在自身 unmount 期间同步改 state 炸 Riverpod 断言)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-10 23:50:35 +08:00
parent e66f79fdf6
commit a5fff28134
15 changed files with 783 additions and 14 deletions
+25 -1
View File
@@ -12,6 +12,8 @@ import '../state/navigation_provider.dart';
import '../widgets/account_screens.dart';
import '../widgets/app_top_bar.dart';
import '../widgets/pangolin_icons.dart';
import 'payment_page.dart';
import 'purchase_page.dart';
/// 格式化到期日为 YYYY-MM-DD(本地时区)。
String _fmtDate(DateTime d) {
@@ -49,7 +51,29 @@ class AccountPage extends ConsumerWidget {
final body = ListView(
padding: EdgeInsets.fromLTRB(pad, isWide ? 6 : 0, pad, 24),
children: [
_PlanBanner(t: t, isFree: isFree, email: email, expiresLabel: expiresLabel, onUpgrade: () => open(NavView.plans, PlansScreen(t: t))),
_PlanBanner(
t: t,
isFree: isFree,
email: email,
expiresLabel: expiresLabel,
// mobile/tablet 全屏 push(desktop 走 open() 的 navView 分支,mobilePage 参数被忽略,
// 由 desktop_shell 自己那份 PlansScreen(onChoose: ...) 接线,见 desktop_shell.dart)。
onUpgrade: () => open(
NavView.plans,
PlansScreen(
t: t,
onChoose: (code) => code == 'pro'
? Navigator.of(context).push(MaterialPageRoute(
builder: (_) => PurchaseScreen(
t: t,
onOrderCreated: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => PaymentScreen(t: t))),
),
))
: Navigator.of(context).push(MaterialPageRoute(builder: (_) => RedeemScreen(t: t))),
),
),
),
const SizedBox(height: 18),
// 账户信息
_SectionLabel(text: t.accInfoTitle),