// purchase_page.dart — 购买套餐(三档 pro:月/季/年,pay v2 通道)。 // 档位数据来自 GET /v1/pay/catalog(server 单源);金额仅展示,扣款以 pay 为准。 import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../l10n/app_text.dart'; import '../models/payment.dart'; import '../pangolin_theme.dart'; import '../state/payment_provider.dart'; import '../widgets/pangolin_icons.dart'; import '../widgets/plan_card.dart'; class PurchaseScreen extends ConsumerWidget { const PurchaseScreen({super.key, required this.t, this.onBack, this.onOrderCreated, this.embedded = false}); final AppText t; final VoidCallback? onBack; /// 下单成功(进入 awaitingPayment)后由壳导航到支付页。 final VoidCallback? onOrderCreated; final bool embedded; String _name(String sku) => switch (sku) { 'pro_month' => t.proMonthly, 'pro_quarter' => t.proQuarterly, 'pro_year' => t.proYearly, _ => sku, }; String _period(String sku) => switch (sku) { 'pro_month' => t.perMonth, 'pro_quarter' => t.perQuarter, 'pro_year' => t.perYear, _ => '', }; Future _choose(BuildContext context, WidgetRef ref, PayCatalogItem item) async { final c = context.pangolin; final method = await showModalBottomSheet( context: context, backgroundColor: c.surface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(PangolinRadius.xl)), ), builder: (sheetCtx) => SafeArea( child: Column(mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.fromLTRB(20, 18, 20, 8), child: Align( alignment: Alignment.centerLeft, child: Text(t.choosePayMethod, style: PangolinText.h3.copyWith(color: c.fg1, fontWeight: FontWeight.w700)), ), ), ListTile( leading: Icon(PangolinIcons.creditCard, color: c.fg2), title: Text(t.payMethodAlipay, style: PangolinText.body.copyWith(color: c.fg1)), trailing: Icon(PangolinIcons.chevronRight, size: 18, color: c.fg3), onTap: () => Navigator.of(sheetCtx).pop('alipay'), ), ListTile( leading: Icon(PangolinIcons.globe, color: c.fg2), title: Text(t.payMethodCrypto, style: PangolinText.body.copyWith(color: c.fg1)), trailing: Icon(PangolinIcons.chevronRight, size: 18, color: c.fg3), onTap: () => Navigator.of(sheetCtx).pop('crypto'), ), const SizedBox(height: 12), ]), ), ); if (method == null || !context.mounted) return; await ref.read(paymentFlowProvider.notifier).start(item, method); if (!context.mounted) return; if (ref.read(paymentFlowProvider).phase == PaymentPhase.awaitingPayment) { onOrderCreated?.call(); } } @override Widget build(BuildContext context, WidgetRef ref) { final c = context.pangolin; final catalog = ref.watch(payCatalogProvider); final body = catalog.when( loading: () => const Center( child: Padding(padding: EdgeInsets.all(40), child: CircularProgressIndicator())), error: (_, __) => Center( child: Padding( padding: const EdgeInsets.all(40), child: Text(t.lang == AppLang.zh ? '加载失败,请重试' : 'Failed to load, retry', style: PangolinText.body.copyWith(color: c.fg3)), ), ), data: (items) => ListView( padding: const EdgeInsets.fromLTRB(20, 14, 20, 24), children: [ for (final it in items) Padding( padding: EdgeInsets.only(bottom: 14, top: it.sku == 'pro_year' ? 12 : 0), child: PlanCard( name: _name(it.sku), price: it.priceLabel(), period: _period(it.sku), features: t.featsPro, ctaLabel: t.buyNow, featured: it.sku == 'pro_year', popularLabel: it.sku == 'pro_year' ? t.mostPopular : null, onPressed: () => _choose(context, ref, it), ), ), ], ), ); if (embedded) return body; return Scaffold( backgroundColor: c.bg, body: SafeArea( child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.fromLTRB(8, 6, 16, 10), child: Row(children: [ IconButton( onPressed: onBack ?? () => Navigator.of(context).maybePop(), icon: Icon(PangolinIcons.arrowLeft, size: 22, color: c.fg1), ), Text(t.purchaseTitle, style: PangolinText.h3.copyWith(color: c.fg1, fontWeight: FontWeight.w700)), ]), ), Expanded(child: body), ]), ), ); } }