Browse Source

Merge branch 'liwei' of https://github.com/deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
9152988398
  1. 4
      lib/core/bindings/initial_binding.dart
  2. 9
      lib/data/services/network/api.dart
  3. 197
      lib/data/services/pay_service.dart
  4. 9
      lib/modules/login/controllers/weixinLogin.dart
  5. 9
      lib/modules/payment/bindings/payment_binding.dart
  6. 85
      lib/modules/payment/controllers/payment_controller.dart
  7. 365
      lib/modules/payment/views/payment_view.dart
  8. 23
      lib/modules/settings/views/settings_view.dart
  9. 9
      lib/routes/app_pages.dart
  10. 3
      lib/routes/app_routes.dart

4
lib/core/bindings/initial_binding.dart

@ -1,3 +1,4 @@
import 'package:voitrans/data/services/pay_service.dart';
import 'package:voitrans/data/services/qq_music.dart';
import '../../../data/services/user_portrait.dart';
@ -92,6 +93,9 @@ class InitialBinding extends Bindings {
Get.lazyPut<FloatingWindowManager>(() => FloatingWindowManager(),
fenix: true);
// 支付服务
Get.lazyPut<PayService>(() => PayService(), fenix: true);
// 杰理蓝牙服务
// // 语言管理器(需要最先初始化)

9
lib/data/services/network/api.dart

@ -171,4 +171,13 @@ class Api {
params: params,
);
}
//创建支付订单
static paycreateorder([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/pay_createorder',
params: params,
);
}
}

197
lib/data/services/pay_service.dart

@ -0,0 +1,197 @@
import 'package:get/get.dart';
import 'package:voitrans/data/services/network/api.dart';
import '../../modules/login/controllers/weixinLogin.dart';
typedef PaymentResultCallback = void Function(bool success, String message);
/// 支付服务
class PayService extends GetxService {
late FlWX _flwx;
@override
void onInit() {
super.onInit();
//微信
FlWX().register(
appSecret: '9ceb39df312b3ba95a229b2aa0d15496',
appId: 'wx4b5ec8957ad5e425',
partnerId: '1719983673',
universalLink: 'https://voitrans.net/app/',
params: FlWXCallbackParams(onHttp: (url) async {
return '';
}));
_flwx = FlWX();
}
/// 微信支付
///
/// [paymentData] 支付参数,包含以下字段:
/// - appId: 应用ID
/// - partnerId: 商户号
/// - prepayId: 预支付交易会话ID
/// - packageValue: 扩展字段,固定值"Sign=WXPay"
/// - nonceStr: 随机字符串
/// - timestamp: 时间戳
/// - sign: 签名
///
/// [onResult] 支付结果回调
Future<bool> wechatPay({
required Map<String, dynamic> paymentData,
PaymentResultCallback? onResult,
}) async {
try {
// 检查微信是否安装
if (!await _flwx.isInstalled) {
onResult?.call(false, '微信未安装');
return false;
}
// 将支付数据转换为Payment对象
final payment = paymentData.toPayment();
if (payment == null) {
onResult?.call(false, '支付参数格式错误');
return false;
}
// 调用微信支付
final result = await _flwx.pay(
payment,
onSuccess: () {
onResult?.call(true, '支付成功');
},
onResponse: (response) {
if (response.errCode == 0) {
onResult?.call(true, '支付成功');
} else if (response.errCode == -2) {
onResult?.call(false, '用户取消支付');
} else {
onResult?.call(false, '支付失败: ${response.errStr}');
}
},
);
return result;
} catch (e) {
onResult?.call(false, '支付异常: $e');
return false;
}
}
/// 创建微信支付订单
///
/// [productId] 商品ID
/// [productName] 商品名称
/// [productDesc] 商品描述
/// [amount] 支付金额(分)
/// [paymentMethod] 支付方式(1=微信支付)
/// [clientIp] 客户端IP(可选)
/// [notifyUrl] 通知URL(可选)
/// [extraData] 扩展数据(可选)
///
/// 返回支付参数,用于调用 [wechatPay] 方法
Future<Map<String, dynamic>?> createWechatPayOrder({
required String productId,
required String productName,
String? productDesc,
required int amount,
int paymentMethod = 4, // 1表示微信支付
String? clientIp,
String? notifyUrl,
String? extraData,
}) async {
try {
// 调用后端API创建支付订单
final response = await Api.paycreateorder({
'product_id': productId,
'product_name': productName,
'product_desc': productDesc ?? '',
'amount': amount,
'payment_method': paymentMethod,
'client_ip': clientIp ?? '',
'notify_url': notifyUrl ?? '',
'extra_data': extraData ?? '',
});
print('创建支付订单: $response');
// 检查响应是否成功
if (response != null) {
final pay_params = response["pay_params"] as Map<String, dynamic>;
// 返回微信支付所需的参数
return {
'appId': pay_params['appid'],
'partnerId': pay_params['partnerid'],
'prepayId': pay_params['prepayid'],
'packageValue': pay_params['package'] ?? 'Sign=WXPay',
'nonceStr': pay_params['noncestr'],
'timestamp': pay_params['timestamp'],
'sign': pay_params['sign'],
};
} else {
print('创建支付订单失败: ${response?['message'] ?? '未知错误'}');
return null;
}
} catch (e) {
print('创建支付订单异常: $e');
return null;
}
}
/// 查询支付结果
///
/// [orderId] 订单ID
/// 返回支付状态:success, failed, pending
Future<String?> queryPaymentStatus(String orderId) async {
try {
// TODO: 调用后端API查询支付状态
// final response = await Api.queryPaymentStatus({'orderId': orderId});
// return response['status'] as String?;
// 临时返回,实际使用时需要删除
return 'pending';
} catch (e) {
print('查询支付状态失败: $e');
return null;
}
}
/// 完整的支付流程
///
/// [orderId] 订单ID
/// [amount] 支付金额(分)
/// [description] 商品描述
/// [userId] 用户ID
/// [onResult] 支付结果回调
Future<bool> processWechatPayment({
required String orderId,
required int amount,
required String description,
String? userId,
PaymentResultCallback? onResult,
}) async {
try {
// 1. 创建支付订单
final paymentData = await createWechatPayOrder(
productId: 'prod_001',
productName: '会员充值',
productDesc: '1个月会员服务',
amount: 1, // 29.99元,单位:分
paymentMethod: 4, // 微信支付
notifyUrl: 'https://api.ideapsound.com/api/home/pay_wechatnotify',
);
if (paymentData == null) {
onResult?.call(false, '创建支付订单失败');
return false;
}
// 2. 调用微信支付
return await wechatPay(
paymentData: paymentData,
onResult: onResult,
);
} catch (e) {
onResult?.call(false, '支付流程异常: $e');
return false;
}
}
}

9
lib/modules/login/controllers/weixinLogin.dart

@ -73,6 +73,11 @@ class FlWX {
/// 获取当前设置的 appSecret
String? get appSecret => _appSecret;
String? _partnerId;
/// 获取当前设置的 partnerId
String? get partnerId => _partnerId;
String _appName = '';
String token = '';
String code = '';
@ -109,6 +114,9 @@ class FlWX {
/// 用于获取用户信息
String? appSecret,
/// 商户号
String? partnerId,
/// app名称(用于分享时使用)
String appName = '',
@ -123,6 +131,7 @@ class FlWX {
_isRegister = true;
_appId = appId;
_appSecret = appSecret;
_partnerId = partnerId;
_appName = appName;
_shareThumbnail = shareThumbnail;
_params = params;

9
lib/modules/payment/bindings/payment_binding.dart

@ -0,0 +1,9 @@
import 'package:get/get.dart';
import '../controllers/payment_controller.dart';
class PaymentBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<PaymentController>(() => PaymentController());
}
}

85
lib/modules/payment/controllers/payment_controller.dart

@ -0,0 +1,85 @@
import 'package:get/get.dart';
import '../../../data/services/pay_service.dart';
class PaymentController extends GetxController {
final PayService _payService = Get.find<PayService>();
// 支付状态
final RxBool isLoading = false.obs;
final RxString paymentStatus = ''.obs;
final RxString errorMessage = ''.obs;
// 测试订单信息
final RxString testOrderId = ''.obs;
final RxDouble testAmount = 0.01.obs; // 测试金额 1分钱
@override
void onInit() {
super.onInit();
// 生成测试订单ID
testOrderId.value = 'test_${DateTime.now().millisecondsSinceEpoch}';
}
/// 测试微信支付
Future<void> testWechatPayment() async {
try {
isLoading.value = true;
paymentStatus.value = '正在发起支付...';
errorMessage.value = '';
// 调用支付服务,使用回调处理结果
final launched = await _payService.processWechatPayment(
orderId: testOrderId.value,
amount: (testAmount.value * 100).toInt(), // 转换为分
description: '微信支付测试',
onResult: (success, message) {
// 支付结果回调
isLoading.value = false;
if (success) {
paymentStatus.value = '支付成功!';
errorMessage.value = '';
Get.snackbar(
'支付成功',
message ?? '微信支付测试完成',
snackPosition: SnackPosition.TOP,
);
} else {
paymentStatus.value = '支付失败';
errorMessage.value = message ?? '未知错误';
Get.snackbar(
'支付失败',
message ?? '未知错误',
snackPosition: SnackPosition.TOP,
);
}
},
);
if (!launched) {
// 如果支付没有成功启动
isLoading.value = false;
paymentStatus.value = '支付启动失败';
errorMessage.value = '无法启动微信支付';
Get.snackbar('支付启动失败', '无法启动微信支付');
}
} catch (e) {
isLoading.value = false;
paymentStatus.value = '支付异常';
errorMessage.value = e.toString();
Get.snackbar('支付异常', e.toString());
}
}
/// 重置状态
void resetStatus() {
paymentStatus.value = '';
errorMessage.value = '';
testOrderId.value = 'test_${DateTime.now().millisecondsSinceEpoch}';
}
/// 更新测试金额
void updateTestAmount(double amount) {
testAmount.value = amount;
}
}

365
lib/modules/payment/views/payment_view.dart

@ -0,0 +1,365 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../controllers/payment_controller.dart';
class PaymentView extends GetView<PaymentController> {
const PaymentView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
elevation: 0.5,
title: Text(
'微信支付测试',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
centerTitle: true,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
color: isDarkMode ? Colors.white : Colors.black,
),
onPressed: () => Get.back(),
),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTestInfoCard(isDarkMode),
SizedBox(height: 20.h),
_buildAmountSelector(isDarkMode),
SizedBox(height: 20.h),
_buildPaymentButton(isDarkMode),
SizedBox(height: 20.h),
_buildStatusCard(isDarkMode),
SizedBox(height: 20.h),
_buildInstructions(isDarkMode),
],
),
),
);
}
/// 测试信息卡片
Widget _buildTestInfoCard(bool isDarkMode) {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'测试订单信息',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
SizedBox(height: 12.h),
Obx(() => _buildInfoRow('订单ID', controller.testOrderId.value, isDarkMode)),
SizedBox(height: 8.h),
_buildInfoRow('商品描述', '微信支付测试', isDarkMode),
SizedBox(height: 8.h),
Obx(() => _buildInfoRow('支付金额', '¥${controller.testAmount.value.toStringAsFixed(2)}', isDarkMode)),
],
),
);
}
/// 信息行
Widget _buildInfoRow(String label, String value, bool isDarkMode) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 80.w,
child: Text(
'$label:',
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
),
),
Expanded(
child: Text(
value,
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white : Colors.black,
),
),
),
],
);
}
/// 金额选择器
Widget _buildAmountSelector(bool isDarkMode) {
final amounts = [0.01, 0.10, 1.00, 10.00];
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.white,
borderRadius: BorderRadius.circular(12.r),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'选择测试金额',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
SizedBox(height: 12.h),
Wrap(
spacing: 12.w,
runSpacing: 8.h,
children: amounts.map((amount) {
return Obx(() => GestureDetector(
onTap: () => controller.updateTestAmount(amount),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
decoration: BoxDecoration(
color: controller.testAmount.value == amount
? Colors.blue[600]
: (isDarkMode ? Colors.grey[800] : Colors.grey[200]),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
'¥${amount.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 14.sp,
color: controller.testAmount.value == amount
? Colors.white
: (isDarkMode ? Colors.white : Colors.black),
),
),
),
));
}).toList(),
),
],
),
);
}
/// 支付按钮
Widget _buildPaymentButton(bool isDarkMode) {
return Obx(() => SizedBox(
width: double.infinity,
height: 50.h,
child: ElevatedButton(
onPressed: controller.isLoading.value ? null : controller.testWechatPayment,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green[600],
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
elevation: 2,
),
child: controller.isLoading.value
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20.w,
height: 20.w,
child: const CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
SizedBox(width: 12.w),
const Text('处理中...'),
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.payment, size: 20.w),
SizedBox(width: 8.w),
Text(
'发起微信支付',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
],
),
),
));
}
/// 状态卡片
Widget _buildStatusCard(bool isDarkMode) {
return Obx(() {
if (controller.paymentStatus.value.isEmpty) {
return const SizedBox.shrink();
}
final isError = controller.errorMessage.value.isNotEmpty;
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isError
? (isDarkMode ? Colors.red[900] : Colors.red[50])
: (isDarkMode ? Colors.green[900] : Colors.green[50]),
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isError ? Colors.red[300]! : Colors.green[300]!,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
isError ? Icons.error : Icons.check_circle,
color: isError ? Colors.red[600] : Colors.green[600],
size: 20.w,
),
SizedBox(width: 8.w),
Text(
'支付状态',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: isError ? Colors.red[600] : Colors.green[600],
),
),
],
),
SizedBox(height: 8.h),
Text(
controller.paymentStatus.value,
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white : Colors.black,
),
),
if (controller.errorMessage.value.isNotEmpty) ...[
SizedBox(height: 8.h),
Text(
'错误信息: ${controller.errorMessage.value}',
style: TextStyle(
fontSize: 12.sp,
color: Colors.red[600],
),
),
],
SizedBox(height: 12.h),
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: controller.resetStatus,
style: OutlinedButton.styleFrom(
side: BorderSide(
color: isError ? Colors.red[600]! : Colors.green[600]!,
),
),
child: Text(
'重新测试',
style: TextStyle(
color: isError ? Colors.red[600] : Colors.green[600],
),
),
),
),
],
),
);
});
}
/// 使用说明
Widget _buildInstructions(bool isDarkMode) {
return Container(
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.blue[50],
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: Colors.blue[200]!,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.info_outline,
color: Colors.blue[600],
size: 20.w,
),
SizedBox(width: 8.w),
Text(
'使用说明',
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
color: Colors.blue[600],
),
),
],
),
SizedBox(height: 12.h),
Text(
'1. 选择测试金额(建议使用0.01元进行测试)\n'
'2. 点击"发起微信支付"按钮\n'
'3. 系统将调用微信支付SDK\n'
'4. 在微信中完成支付流程\n'
'5. 返回应用查看支付结果\n\n'
'注意:这是测试环境,实际不会产生真实扣费',
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
height: 1.5,
),
),
],
),
);
}
}

23
lib/modules/settings/views/settings_view.dart

@ -516,6 +516,29 @@ class SettingsView extends GetView<SettingsController> {
// : Colors.grey[200]),
// //BLE测试
// 支付测试
_buildSimpleNavigationSetting(
title: '支付测试',
subtitle: '测试微信支付功能',
icon: Icons.payment,
iconBgColor: isDarkMode
? Colors.green[900]!.withOpacity(0.3)
: Colors.green[100]!,
iconColor:
isDarkMode ? Colors.green[300]! : Colors.green[600]!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () {
Get.toNamed(Routes.payment);
},
isDarkMode: isDarkMode,
),
Divider(
height: 1,
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.grey[200]),
Obx(() => controller.showDebugInfo.value
? _buildSimpleNavigationSetting(
title: 'opus解码测试',

9
lib/routes/app_pages.dart

@ -63,6 +63,8 @@ import '../modules/music/views/music_playlist_view.dart';
import '../modules/music/bindings/music_playlist_binding.dart';
import '../modules/FTFTranslation/bindings/FTFTranslation_binding.dart';
import '../modules/FTFTranslation/views/FTFTranslation_view.dart';
import '../modules/payment/bindings/payment_binding.dart';
import '../modules/payment/views/payment_view.dart';
abstract class AppPages {
static final pages = [
@ -288,5 +290,12 @@ abstract class AppPages {
page: () => const QqmusicPlaylistView(),
binding: QQMusicPlaylistBinding(),
),
GetPage(
name: Routes.payment,
page: () => const PaymentView(),
binding: PaymentBinding(),
transition: Transition.rightToLeft,
transitionDuration: Duration(milliseconds: 250),
),
];
}

3
lib/routes/app_routes.dart

@ -63,4 +63,7 @@ abstract class Routes {
static const ble = '/ble';
// ble设备管理
static const qqmusic = '/qqmusic';
// 支付相关
static const payment = '/payment'; // 支付测试页面
}

Loading…
Cancel
Save