You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
11 KiB
281 lines
11 KiB
import 'package:flutter/material.dart';
|
|
import 'package:get/get.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';
|
|
|
|
/// goods 模块基础控制器,封装通用的商品加载与支付流程
|
|
abstract class GoodsBaseController extends GetxController {
|
|
final RxList<DBGoods> items = <DBGoods>[].obs;
|
|
final RxBool loading = false.obs;
|
|
final RxString error = ''.obs;
|
|
|
|
late final PayService _payService = Get.find<PayService>();
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_init();
|
|
}
|
|
|
|
Future<void> _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<List<DBGoods>> fetchProducts();
|
|
|
|
/// 统一入口:不指定支付方式时,跳转到支付页面交由用户选择
|
|
Future<void> 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<bool> 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;
|
|
}
|
|
return _payService.wechatPay(paymentData: paymentData, onResult: onResult);
|
|
}
|
|
|
|
/// 指定支付宝支付(直接下单并拉起支付宝)
|
|
Future<bool> 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;
|
|
}
|
|
return _payService.alipay(orderInfo: orderInfo, onResult: onResult);
|
|
}
|
|
|
|
/// 指定 PayPal 支付(创建订单并打开授权链接)
|
|
Future<bool> 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 }
|
|
|