/// 首页:商品展示 + 7 种支付方式入口 /// /// 渠道与流程(详见 docs/): /// - 微信支付:移动端 App 支付 / Web Native 扫码 /// - 支付宝支付:移动端 App 支付 / Web WAP 跳转 /// - 云闪付:移动端 App 支付(拉起云闪付 App)/ Web 银联收银台跳转 /// - Stripe 支付:PaymentSheet 卡片支付 /// - Apple Pay / Google Pay:经 Stripe 确认 /// - 网页收银台:聚合支付(微信扫码 / 支付宝 / Stripe 跳转) library; import 'package:flutter/material.dart'; import '../models/payment.dart'; import '../service/api_service.dart'; import '../service/pay_service.dart'; import '../utils/toast.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { final PayService _pay = PayService.instance; final APIServer _api = APIServer(); PaymentProduct? _product; bool _loading = true; @override void initState() { super.initState(); _loadProduct(); } /// 拉取商品(失败时使用本地兜底商品,保证页面可展示) Future _loadProduct() async { try { final products = await _api.fetchProducts(); if (!mounted) return; setState(() { _product = products.consume.isNotEmpty ? products.consume.first : null; _loading = false; }); } on Exception catch (e) { if (!mounted) return; setState(() { _product = PaymentProduct( id: 'p001', name: '橘子', quota: 1, retailPrice: 1100, retailPriceUSD: 150, expirePolicy: 'once', expirePolicyText: '一次性', ); _loading = false; }); showErrorMessage('商品加载失败,已使用演示数据:${_api.resolveError(e)}'); } } /// 打开网页收银台渠道选择 Future _pickCheckoutChannel() async { final product = _product; if (product == null) return; if (!mounted) return; final channel = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('选择收银台支付渠道'), content: const Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: Icon(Icons.qr_code, color: Colors.green), title: Text('微信支付(扫码)'), subtitle: Text('Native 扫码'), ), ListTile( leading: Icon(Icons.payment, color: Colors.blue), title: Text('支付宝'), subtitle: Text('电脑/手机网站跳转'), ), ListTile( leading: Icon(Icons.credit_card, color: Colors.indigo), title: Text('Stripe'), subtitle: Text('托管收银台跳转'), ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop('wechat'), child: const Text('微信'), ), TextButton( onPressed: () => Navigator.of(ctx).pop('alipay'), child: const Text('支付宝'), ), TextButton( onPressed: () => Navigator.of(ctx).pop('stripe'), child: const Text('Stripe'), ), TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('取消'), ), ], ), ); if (channel == null) return; if (!mounted) return; await _pay.payWithCheckout(context, channel: channel, productId: product.id); } @override Widget build(BuildContext context) { final product = _product; return Scaffold( appBar: AppBar( title: const Text('支付演示'), centerTitle: true, ), body: _loading || product == null ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildProductCard(product), const SizedBox(height: 24), const Text( '选择支付方式', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), _buildPayButtons(product), ], ), ), ); } /// 商品卡片 Widget _buildProductCard(PaymentProduct product) { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(20), child: Row( children: [ Container( width: 72, height: 72, decoration: BoxDecoration( color: Colors.orange.shade100, borderRadius: BorderRadius.circular(12), ), child: const Icon(Icons.agriculture, size: 40, color: Colors.orange), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(product.name, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold)), const SizedBox(height: 4), Text('数量:${product.quota}${product.expirePolicyText}', style: const TextStyle(color: Colors.grey)), const SizedBox(height: 4), Row( children: [ Text(product.retailPriceText, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.red.shade600, )), if (product.retailPriceUSD > 0) ...[ const SizedBox(width: 8), Text(product.retailPriceUSDText, style: const TextStyle(color: Colors.grey)), ], ], ), ], ), ), ], ), ), ); } /// 7 个支付按钮 Widget _buildPayButtons(PaymentProduct product) { final entries = <_PayEntry>[ _PayEntry('微信支付', Icons.wechat, Colors.green, onTap: () => _pay.payWithWechat(context, productId: product.id)), _PayEntry('支付宝支付', Icons.account_balance_wallet, Colors.blue, onTap: () => _pay.payWithAlipay(context, productId: product.id)), _PayEntry('云闪付', Icons.account_balance, Colors.red, onTap: () => _pay.payWithUnionPay(context, productId: product.id)), _PayEntry('Stripe 支付', Icons.credit_card, Colors.indigo, onTap: () => _pay.payWithStripe(context, productId: product.id)), _PayEntry('Apple Pay', Icons.apple, Colors.black, onTap: () => _pay.payWithApple(context, product: product)), _PayEntry('Google Pay', Icons.g_mobiledata, Colors.deepOrange, onTap: () => _pay.payWithGoogle(context, product: product)), _PayEntry('网页收银台', Icons.storefront, Colors.teal, onTap: _pickCheckoutChannel), ]; return GridView.count( crossAxisCount: 2, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), mainAxisSpacing: 12, crossAxisSpacing: 12, childAspectRatio: 1.6, children: entries.map((e) => e.build()).toList(), ); } } /// 支付入口项 class _PayEntry { final String label; final IconData icon; final Color color; final VoidCallback onTap; _PayEntry(this.label, this.icon, this.color, {required this.onTap}); Widget build() { return Material( color: color.withValues(alpha: 0.06), borderRadius: BorderRadius.circular(12), child: InkWell( borderRadius: BorderRadius.circular(12), onTap: onTap, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color, size: 32), const SizedBox(height: 8), Text(label, style: const TextStyle(fontSize: 14)), ], ), ), ); } }