
支付是电商应用的最后一步,用户在这里输入支付信息、确认订单、完成交易。一个好的支付实现需要提供安全的支付方式、清晰的支付流程和友好的支付反馈。本文将详细讲解如何在 Flutter for OpenHarmony 项目中实现一个功能完整的支付系统,包括支付方式选择、支付信息输入、支付处理和支付结果反馈等功能。
用户数据模型
用户信息是支付的基础。User 类包含了用户的个人信息和账户信息。
dart
class User {
const User({
required this.id, // 用户ID
required this.email, // 邮箱
required this.nickname, // 昵称
this.avatar, // 头像URL
this.phone, // 电话
this.memberLevel = 'Basic', // 会员等级
this.points = 0, // 积分
});
final String id;
final String email;
final String nickname;
final String? avatar;
final String? phone;
final String memberLevel;
final int points;
// 复制并修改用户信息
User copyWith({
String? id,
String? email,
String? nickname,
String? avatar,
String? phone,
String? memberLevel,
int? points,
}) {
return User(
id: id ?? this.id,
email: email ?? this.email,
nickname: nickname ?? this.nickname,
avatar: avatar ?? this.avatar,
phone: phone ?? this.phone,
memberLevel: memberLevel ?? this.memberLevel,
points: points ?? this.points,
);
}
}
这个用户模型展示了如何管理用户信息:
用户信息:
- 包含基本信息(ID、邮箱、昵称)
- 包含联系方式(头像、电话)
- 包含账户信息(会员等级、积分)
不可变性:
- 使用
const构造函数确保不可变性 copyWith方法用于创建修改后的副本- 支持函数式编程风格
应用状态管理
AppState 是应用的全局状态管理类,包含用户、订单、优惠券等信息。
dart
class AppState extends ChangeNotifier {
// 用户状态
User? _currentUser;
User? get currentUser => _currentUser;
bool get isLoggedIn => _currentUser != null;
// 订单列表
final List<Order> _orders = [];
List<Order> get orders => List.unmodifiable(_orders);
// 优惠券列表
final List<Coupon> _coupons = [];
List<Coupon> get coupons => List.unmodifiable(_coupons);
List<Coupon> get validCoupons => _coupons.where((c) => c.isValid).toList();
// 积分管理方法
void addPoints(int amount) {
if (_currentUser == null) return;
_currentUser = _currentUser!.copyWith(
points: _currentUser!.points + amount,
);
notifyListeners();
}
void usePoints(int amount) {
if (_currentUser == null) return;
if (_currentUser!.points >= amount) {
_currentUser = _currentUser!.copyWith(
points: _currentUser!.points - amount,
);
notifyListeners();
}
}
// 订单管理方法
void addOrder(Order order) {
_orders.insert(0, order);
notifyListeners();
}
// 登录方法
Future<bool> login(String email, String password) async {
await Future.delayed(const Duration(milliseconds: 800));
if (email.isNotEmpty && password.length >= 6) {
_currentUser = User(
id: 'user_001',
email: email,
nickname: email.split('@').first,
memberLevel: 'Gold',
points: 2580,
);
notifyListeners();
return true;
}
return false;
}
// 登出方法
void logout() {
_currentUser = null;
notifyListeners();
}
}
这个应用状态展示了如何管理全局状态:
状态管理:
- 使用
ChangeNotifier实现状态通知 - 维护用户、订单、优惠券等全局数据
- 提供修改状态的方法
积分管理:
addPoints增加用户积分usePoints使用用户积分- 检查用户是否有足够的积分
订单管理:
- 维护订单列表
- 新订单插入到列表开头
- 通知所有监听者状态改变
支付方式选择
支付前需要让用户选择支付方式。
dart
// 支付方式枚举
enum PaymentMethod {
creditCard, // 信用卡
debitCard, // 借记卡
wallet, // 钱包
bankTransfer, // 银行转账
}
// 支付方式选择UI
class PaymentMethodSelector extends StatefulWidget {
const PaymentMethodSelector({
required this.onMethodSelected,
});
final Function(PaymentMethod) onMethodSelected;
@override
State<PaymentMethodSelector> createState() => _PaymentMethodSelectorState();
}
class _PaymentMethodSelectorState extends State<PaymentMethodSelector> {
PaymentMethod? _selectedMethod;
@override
Widget build(BuildContext context) {
return Column(
children: [
// 信用卡选项
_buildMethodOption(
method: PaymentMethod.creditCard,
title: '信用卡',
icon: Icons.credit_card,
description: '使用信用卡支付',
),
// 借记卡选项
_buildMethodOption(
method: PaymentMethod.debitCard,
title: '借记卡',
icon: Icons.credit_card,
description: '使用借记卡支付',
),
// 钱包选项
_buildMethodOption(
method: PaymentMethod.wallet,
title: '钱包',
icon: Icons.account_balance_wallet,
description: '使用账户余额支付',
),
// 银行转账选项
_buildMethodOption(
method: PaymentMethod.bankTransfer,
title: '银行转账',
icon: Icons.account_balance,
description: '通过银行转账支付',
),
],
);
}
Widget _buildMethodOption({
required PaymentMethod method,
required String title,
required IconData icon,
required String description,
}) {
final isSelected = _selectedMethod == method;
return Card(
child: ListTile(
// 支付方式图标
leading: Icon(icon),
// 支付方式标题和描述
title: Text(title),
subtitle: Text(description),
// 选择状态
trailing: Radio<PaymentMethod>(
value: method,
groupValue: _selectedMethod,
onChanged: (value) {
setState(() {
_selectedMethod = value;
widget.onMethodSelected(value!);
});
},
),
// 点击卡片也可以选择
onTap: () {
setState(() {
_selectedMethod = method;
widget.onMethodSelected(method);
});
},
),
);
}
}
这个支付方式选择展示了如何让用户选择支付方式:
支付方式:
- 信用卡和借记卡支付
- 钱包余额支付
- 银行转账支付
UI设计:
- 使用卡片展示每个支付方式
- 显示支付方式的图标和描述
- 使用单选按钮表示选择状态
支付信息输入
用户需要输入支付信息来完成支付。
dart
// 支付信息表单
class PaymentForm extends StatefulWidget {
const PaymentForm({
required this.onSubmit,
});
final Function(PaymentInfo) onSubmit;
@override
State<PaymentForm> createState() => _PaymentFormState();
}
class _PaymentFormState extends State<PaymentForm> {
// 表单控制器
final _cardNumberController = TextEditingController();
final _cardHolderController = TextEditingController();
final _expiryController = TextEditingController();
final _cvvController = TextEditingController();
@override
Widget build(BuildContext context) {
return Column(
children: [
// 卡号输入
TextField(
controller: _cardNumberController,
decoration: InputDecoration(
labelText: '卡号',
hintText: '1234 5678 9012 3456',
prefixIcon: const Icon(Icons.credit_card),
),
// 限制输入为数字,并自动添加空格
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
CardNumberFormatter(),
],
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
// 持卡人名字
TextField(
controller: _cardHolderController,
decoration: InputDecoration(
labelText: '持卡人名字',
hintText: 'JOHN DOE',
prefixIcon: const Icon(Icons.person),
),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z\s]')),
],
),
const SizedBox(height: 16),
// 有效期和CVV
Row(
children: [
Expanded(
child: TextField(
controller: _expiryController,
decoration: InputDecoration(
labelText: '有效期',
hintText: 'MM/YY',
),
// 限制输入格式为MM/YY
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
ExpiryDateFormatter(),
],
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
controller: _cvvController,
decoration: InputDecoration(
labelText: 'CVV',
hintText: '123',
),
// 限制输入为3位数字
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(3),
],
keyboardType: TextInputType.number,
obscureText: true,
),
),
],
),
const SizedBox(height: 24),
// 提交按钮
ElevatedButton(
onPressed: _submitForm,
child: const Text('确认支付'),
),
],
);
}
void _submitForm() {
final paymentInfo = PaymentInfo(
cardNumber: _cardNumberController.text,
cardHolder: _cardHolderController.text,
expiry: _expiryController.text,
cvv: _cvvController.text,
);
widget.onSubmit(paymentInfo);
}
@override
void dispose() {
_cardNumberController.dispose();
_cardHolderController.dispose();
_expiryController.dispose();
_cvvController.dispose();
super.dispose();
}
}
// 卡号格式化器
class CardNumberFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final text = newValue.text;
if (text.isEmpty) return newValue;
// 每4位添加一个空格
final buffer = StringBuffer();
for (int i = 0; i < text.length; i++) {
buffer.write(text[i]);
if ((i + 1) % 4 == 0 && i + 1 != text.length) {
buffer.write(' ');
}
}
return TextEditingValue(
text: buffer.toString(),
selection: TextSelection.collapsed(offset: buffer.length),
);
}
}
// 有效期格式化器
class ExpiryDateFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final text = newValue.text;
if (text.isEmpty) return newValue;
// 格式化为MM/YY
if (text.length >= 2) {
final month = text.substring(0, 2);
final year = text.length > 2 ? text.substring(2) : '';
return TextEditingValue(
text: year.isEmpty ? month : '$month/$year',
selection: TextSelection.collapsed(
offset: year.isEmpty ? month.length : '$month/$year'.length,
),
);
}
return newValue;
}
}
// 支付信息类
class PaymentInfo {
const PaymentInfo({
required this.cardNumber,
required this.cardHolder,
required this.expiry,
required this.cvv,
});
final String cardNumber;
final String cardHolder;
final String expiry;
final String cvv;
}
这个支付信息输入展示了如何安全地收集支付信息:
输入字段:
- 卡号:自动格式化为
1234 5678 9012 3456格式 - 持卡人名字:只允许字母和空格
- 有效期:自动格式化为
MM/YY格式 - CVV:只允许3位数字,并隐藏显示
格式化器:
CardNumberFormatter每4位添加空格ExpiryDateFormatter自动添加斜杠FilteringTextInputFormatter限制输入类型
安全性:
- CVV 字段使用
obscureText: true隐藏 - 限制输入长度和类型
- 防止用户输入无效数据
支付处理
支付需要进行异步处理,并显示加载状态。
dart
// 支付处理方法
Future<bool> processPayment(
PaymentInfo paymentInfo,
double amount,
AppState appState,
) async {
try {
// 显示加载对话框
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('正在处理支付...'),
],
),
),
);
// 模拟支付处理(实际应该调用支付API)
await Future.delayed(const Duration(seconds: 2));
// 关闭加载对话框
if (mounted) Navigator.pop(context);
// 支付成功
if (mounted) {
// 创建订单
final order = Order(
id: 'order_${DateTime.now().millisecondsSinceEpoch}',
items: [],
status: OrderStatus.paid,
createdAt: DateTime.now(),
totalUsd: amount,
paidAt: DateTime.now(),
);
// 添加订单到应用状态
appState.addOrder(order);
// 显示成功提示
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('支付成功!')),
);
return true;
}
return false;
} catch (e) {
// 关闭加载对话框
if (mounted) Navigator.pop(context);
// 显示错误提示
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('支付失败:$e')),
);
}
return false;
}
}
这个支付处理展示了如何处理支付流程:
加载状态:
- 显示加载对话框
- 禁止用户关闭对话框
- 显示加载提示
支付处理:
- 模拟支付处理(实际应该调用支付API)
- 创建订单对象
- 添加订单到应用状态
错误处理:
- 使用 try-catch 捕获异常
- 显示错误提示
- 关闭加载对话框
支付结果反馈
支付完成后需要显示结果反馈。
dart
// 支付结果页面
class PaymentResultPage extends StatelessWidget {
const PaymentResultPage({
required this.success,
required this.orderId,
required this.amount,
});
final bool success;
final String orderId;
final double amount;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('支付结果'),
automaticallyImplyLeading: false,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 结果图标
Icon(
success ? Icons.check_circle : Icons.error,
size: 80,
color: success ? Colors.green : Colors.red,
),
const SizedBox(height: 24),
// 结果文本
Text(
success ? '支付成功' : '支付失败',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),
// 订单信息
if (success) ...[
Text('订单号:$orderId'),
const SizedBox(height: 8),
Text('金额:¥${amount.toStringAsFixed(2)}'),
],
const SizedBox(height: 32),
// 操作按钮
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('返回'),
),
const SizedBox(width: 16),
if (success)
ElevatedButton(
onPressed: () {
// 查看订单详情
},
child: const Text('查看订单'),
),
],
),
],
),
),
);
}
}
这个支付结果反馈展示了如何显示支付结果:
成功状态:
- 显示成功图标和文本
- 显示订单号和金额
- 提供查看订单的选项
失败状态:
- 显示失败图标和文本
- 提供返回的选项
用户体验:
- 清晰的视觉反馈
- 提供后续操作选项
- 防止用户返回支付页面
总结
支付的实现涉及多个重要的技术点。首先是用户和应用状态的管理,维护用户信息和订单数据。其次是支付方式的选择,让用户选择合适的支付方式。再次是支付信息的输入,安全地收集支付信息。最后是支付处理和结果反馈,提供清晰的支付流程和用户体验。
这种设计确保了支付流程的安全性和用户体验的流畅性。用户可以轻松选择支付方式、输入支付信息、完成支付,整个流程自然而直观。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net