| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /// 首页:商品展示 + 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<HomePage> createState() => _HomePageState();
- }
- class _HomePageState extends State<HomePage> {
- final PayService _pay = PayService.instance;
- final APIServer _api = APIServer();
- PaymentProduct? _product;
- bool _loading = true;
- @override
- void initState() {
- super.initState();
- _loadProduct();
- }
- /// 拉取商品(失败时使用本地兜底商品,保证页面可展示)
- Future<void> _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<void> _pickCheckoutChannel() async {
- final product = _product;
- if (product == null) return;
- if (!mounted) return;
- final channel = await showDialog<String>(
- 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)),
- ],
- ),
- ),
- );
- }
- }
|