import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:voitrans/data/models/user_Info.dart'; import '../../../routes/app_routes.dart'; import '../../../data/services/pay_service.dart'; import '../../../data/models/goods.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../../../data/services/network/dio_manager.dart'; import '../../../data/services/network/api.dart'; /// goods 模块基础控制器,封装通用的商品加载与支付流程 abstract class GoodsBaseController extends GetxController { final RxList items = [].obs; final RxBool loading = false.obs; final RxString error = ''.obs; late final PayService _payService = Get.find(); @override void onInit() { super.onInit(); _init(); } Future _init() async { loading.value = true; error.value = ''; try { final list = await fetchProducts(); items.assignAll(list); } catch (e) { error.value = e.toString(); } finally { loading.value = false; } } /// 子类实现:返回商品列表(统一使用 DBGoods) Future> fetchProducts(); /// 统一入口:不指定支付方式时,跳转到支付页面交由用户选择 Future purchase(DBGoods item) async { final method = await _showPayMethodSheet(); if (method == null) return; bool ok = false; String failMsg = '支付失败,请重试'; if (method == _PayMethod.wechat) { ok = await payWechat(item, onResult: (success, msg) { if (!success) failMsg = msg ?? failMsg; }); } else if (method == _PayMethod.alipay) { ok = await payAlipay(item, onResult: (success, msg) { if (!success) failMsg = msg ?? failMsg; }); } else if (method == _PayMethod.paypal) { ok = await payPaypal(item, onResult: (success, msg) { if (!success) failMsg = msg ?? failMsg; }); } if (!ok) { Get.snackbar('提示', failMsg, snackPosition: SnackPosition.BOTTOM, margin: const EdgeInsets.all(12)); } } /// 底部弹窗:选择支付方式 Future<_PayMethod?> _showPayMethodSheet() async { final context = Get.context; final isDark = context != null && Theme.of(context).brightness == Brightness.dark; return await Get.bottomSheet<_PayMethod>( SafeArea( child: Container( decoration: BoxDecoration( color: isDark ? Colors.grey[900] : Colors.white, borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Container( width: 40, height: 4, decoration: BoxDecoration( color: isDark ? Colors.white24 : Colors.black12, borderRadius: BorderRadius.circular(2), ), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( '选择支付方式', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: isDark ? Colors.white : Colors.black, ), ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ GestureDetector( onTap: () => Get.back(result: _PayMethod.wechat), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 56, height: 56, decoration: BoxDecoration( shape: BoxShape.circle, color: isDark ? Colors.white10 : Colors.black12, border: Border.all( color: isDark ? Colors.white24 : Colors.black12, ), ), child: Icon(Icons.wechat, color: Colors.green[600]), ), const SizedBox(height: 8), Text( '微信支付', style: TextStyle( fontSize: 13, color: isDark ? Colors.white : Colors.black, ), ), ], ), ), GestureDetector( onTap: () => Get.back(result: _PayMethod.alipay), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 56, height: 56, decoration: BoxDecoration( shape: BoxShape.circle, color: isDark ? Colors.white10 : Colors.black12, border: Border.all( color: isDark ? Colors.white24 : Colors.black12, ), ), child: Icon(Icons.payment, color: Colors.blue[600]), ), const SizedBox(height: 8), Text( '支付宝', style: TextStyle( fontSize: 13, color: isDark ? Colors.white : Colors.black, ), ), ], ), ), GestureDetector( onTap: () => Get.back(result: _PayMethod.paypal), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 56, height: 56, decoration: BoxDecoration( shape: BoxShape.circle, color: isDark ? Colors.white10 : Colors.black12, border: Border.all( color: isDark ? Colors.white24 : Colors.black12, ), ), child: Icon( Icons.account_balance_wallet, color: Colors.indigo[600], ), ), const SizedBox(height: 8), Text( 'PayPal', style: TextStyle( fontSize: 13, color: isDark ? Colors.white : Colors.black, ), ), ], ), ), ], ), const SizedBox(height: 12), ], ), ), ], ), ), ), isScrollControlled: false, ignoreSafeArea: false, backgroundColor: Colors.transparent, ); } /// 指定微信支付(直接下单并拉起微信) Future payWechat(DBGoods item, {PaymentResultCallback? onResult}) async { final cents = item.price; // DBGoods.price 单位:分 final serverUrl = dotenv.env['SERVER_URL'] ?? DioManager().baseUrl; final paymentData = await _payService.createWechatPayOrder( productId: item.id, productName: item.name, productDesc: item.desc, amount: cents, notifyUrl: '$serverUrl/api/home/pay_wechatnotify', ); if (paymentData == null) { onResult?.call(false, '创建微信订单失败'); return false; } final orderId = paymentData['orderId'] as String? ?? _payService.lastOrderId; return _payService.wechatPay( paymentData: paymentData, onResult: (success, msg) async { // 先透传给外部回调 onResult?.call(success, msg); if (success) { await _confirmOrderAndShow(orderId, channel: '微信', initialMessage: msg); } else { _showPayResultDialog(false, msg ?? '支付失败'); } }, ); } /// 指定支付宝支付(直接下单并拉起支付宝) Future payAlipay(DBGoods item, {PaymentResultCallback? onResult}) async { final cents = item.price; // DBGoods.price 单位:分 final orderInfo = await _payService.createAlipayOrder( productId: item.id, productName: item.name, productDesc: item.desc, amount: cents, notifyUrl: 'https://api.ideapsound.com/api/home/pay_alipaynotify', ); if (orderInfo == null) { onResult?.call(false, '创建支付宝订单失败'); return false; } final orderId = _payService.lastOrderId; // 创建订单时已缓存 return _payService.alipay( orderInfo: orderInfo, onResult: (success, msg) async { onResult?.call(success, msg); if (success) { await _confirmOrderAndShow(orderId, channel: '支付宝', initialMessage: msg); } else { _showPayResultDialog(false, msg ?? '支付失败'); } }, ); } Future _confirmOrderAndShow(String? orderId, {required String channel, String? initialMessage}) async { // 若无法获取订单号,直接弹成功提示但提醒稍后刷新 if (orderId == null || orderId.isEmpty) { _showPayResultDialog(true, '${channel}支付成功,订单处理中,请稍后查看。'); return; } String message = initialMessage ?? '${channel}支付成功'; bool success = false; try { // 简单轮询回查,避免回调延迟 for (int i = 0; i < 5; i++) { final resp = await Api.payaskorder({'order_id': orderId}); print("查询订单 resp: $resp"); final order = resp?['order'] as Map?; final user = resp?['user'] as Map?; if (order != null && user != null) { final orderStatus = order["status"]; if (orderStatus == 1) { User.instance.updateUserInfo(user); success = true; break; } } await Future.delayed(const Duration(milliseconds: 1500)); } // 若标记成功但未拿到用户信息,使用token登录接口回拉用户资料,确保额度刷新 if (success) { try { final response = await Api.tokenlogin(); if (response is Map && response['user'] is Map) { User.instance .updateUserInfo(response['user'] as Map); } } catch (_) {} } } catch (e) { message = '查询支付结果异常: $e'; } _showPayResultDialog(success, message); } void _showPayResultDialog(bool success, String message) { final context = Get.context; final isDark = context != null && Theme.of(context).brightness == Brightness.dark; Get.dialog( Dialog( insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), backgroundColor: isDark ? Colors.grey[900] : Colors.white, child: Padding( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: success ? (isDark ? Colors.green[800] : Colors.green[100]) : (isDark ? Colors.red[800] : Colors.red[100]), borderRadius: BorderRadius.circular(12), ), child: Icon( success ? Icons.check_circle : Icons.error, color: success ? Colors.green[600] : Colors.redAccent[200], size: 28, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( success ? '支付成功' : '支付失败', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: isDark ? Colors.white : Colors.black, ), ), const SizedBox(height: 6), Text( message, style: TextStyle( fontSize: 13, color: isDark ? Colors.white70 : Colors.grey[700], ), ), ], ), ), ], ), const SizedBox(height: 16), Divider(color: isDark ? Colors.white24 : Colors.black12), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Get.back(), child: const Text('知道了'), ), ], ), ], ), ), ), barrierDismissible: true, ); } /// 指定 PayPal 支付(创建订单并打开授权链接) Future payPaypal(DBGoods item, {PaymentResultCallback? onResult}) async { final cents = item.price; // DBGoods.price 单位:分 final approvalUrl = await _payService.createPaypalOrder( productId: item.id, productName: item.name, productDesc: item.desc, amount: cents, notifyUrl: 'https://api.ideapsound.com/api/home/pay_paypalnotify', ); if (approvalUrl == null) { onResult?.call(false, '创建 PayPal 订单失败'); return false; } return _payService.paypalPay(approvalUrl: approvalUrl, onResult: onResult); } } enum _PayMethod { wechat, alipay, paypal }