Flutter财务管理插件flutter_billpocket的使用
Flutter财务管理插件flutter_billpocket的使用
Billpocket插件
介绍Billpocket:简化Flutter应用中的移动卡支付和终端集成。
展示一些爱心并给仓库加星以支持项目
开始使用
Android
在Android设备上,你需要添加以下权限来请求蓝牙访问:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Dart
设置Billpocket的令牌和开发环境:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Billpocket.config(
isProduction: true,
token: '{YOUR_TOKEN}');
runApp(const MyAppPage());
}
文档
SDK状态
你可以通过调用以下代码行来检查SDK初始化的状态:
await Billpocket.getStatusSDK();
这将返回一个bool
值。
终端列表
你可以通过以下方法获取所有已通过蓝牙配对的终端列表:
await Billpocket.getReaders();
这将返回一个List<Reader>
值。
连接终端
一旦你选择了一个终端,必须调用以下方法,并发送从Reader
中获得的参数:
await Billpocket.connectReader(
readerType: readerType,
readerMacAddress: readerMacAddress,
name: name);
这将返回一个bool
值。
开始交易
开始交易的方法如下:
await Billpocket.doTransaction(
amount: "10",
tip: "0",
latitude: 19.42691938620286,
longitude: -99.16780320031096,
description: "description");
运行此方法将触发一个事件流。处理这些事件的方法如下:
Billpocket.transactionStream().listen((event) {
final eventName = event['event'];
final message = event['message'];
switch (eventName) {
case 'onTransactionAborted':
// 处理onTransactionAborted事件
print('Transaction aborted: $message');
break;
case 'onBeforeTransaction':
// 处理onBeforeTransaction事件
print('Transaction before: $message');
break;
case 'onCardRead':
// 处理onCardRead事件
print('Transaction card read: $message');
break;
case 'getSignature':
// 处理getSignature事件
print('Transaction get signature: $message');
break;
case 'onReaderWaitingForCard':
// 处理onReaderWaitingForCard事件
print('Transaction reader waiting for card: $message');
break;
case 'onMsiDefined':
// 处理onMsiDefined事件
final list = event['list'];
print('Transaction msi defined: $message');
print('MSI list: $list');
showMSI(list);
break;
case 'onGetPin':
// 处理onGetPin事件
print('Transaction get pin: $message');
break;
case 'onMagneticCardFound':
// 处理onMagneticCardFound事件
print('Transaction magnetic card found: $message');
break;
case 'onTransactionFinished':
// 处理onTransactionFinished事件
print('Transaction finished: $message');
break;
case 'onTransactionSuccessful':
// 处理onTransactionSuccessful事件
print('Transaction successful: $message');
break;
case 'resultStartTransaction':
// 处理resultStartTransaction事件
print('Transaction result start: $message');
break;
case 'resultStartTransactionSuccess':
// 处理resultStartTransactionSuccess事件
print('Transaction result start success: $message');
break;
case 'resultStartTransactionError':
// 处理resultStartTransactionError事件
print('Transaction result start error: $message');
break;
// 其他事件类似处理
default:
print('Unknown event received: $eventName');
break;
}
事件解释
-
onGetPin
和getSignature
: 这些方法会打开原生屏幕,因此无需处理响应。 -
onMsiDefined
: 此方法将返回一个字符串,必须解析为List<Installment>
。请查看示例。 -
其他方法: 这些方法是事务性的,取决于具体用例。
无息月数
如果你在doTransaction
中输入的金额超过无息月数的限制,上述onMsiDefined
事件将被触发。由于会得到一个列表,并且必须选择一个无息月数选项才能继续交易,因此需要调用以下方法并发送所选的参数:
Billpocket.continueWithMsi(
commission: installment[pos].commission!,
installments: installment[pos].value!,
minAmount: installment[pos].minAmount!,
);
调用该方法将继续在上述流中进行。
问题
请在GitHub仓库提交任何问题、错误或功能请求。
贡献
如果您希望对此仓库进行更改,请发送拉取请求。
致谢
此包最初由Abel Tarazona和Jared González创建。
完整示例Demo
以下是完整的示例代码:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_billpocket/billpocket.dart';
import 'package:flutter_billpocket/installment.dart';
import 'package:flutter_billpocket/reader.dart';
import 'package:permission_handler/permission_handler.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Billpocket.config(
isProduction: true,
token: '{YOUR_TOKEN}');
runApp(const MyAppPage());
}
class MyAppPage extends StatelessWidget {
const MyAppPage({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return const MaterialApp(home: MyApp());
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _statusSDK = false;
bool _statusBluetooth = false;
bool _statusConnection = false;
List<Reader> _readers = [];
StreamSubscription<Map<dynamic, dynamic>>? _subscription;
TextEditingController amountController = TextEditingController();
List<String> log = [];
[@override](/user/override)
void initState() {
super.initState();
getStatusSDK();
getStatusBluetoothPermission();
listenTransactionStream();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Billpocket'),
actions: [
IconButton(
onPressed: () async {
final log = await Billpocket.getLogs();
if (context.mounted) {
_showLogs(context, log);
}
},
icon: const Text('Logs')
)
],
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Estados',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Status(
title: 'SDK',
isActive: _statusSDK,
),
Status(
title: 'Bluetooth',
isActive: _statusBluetooth,
),
Status(
title: 'Terminal \nConnection',
isActive: _statusConnection,
)
],
),
const SizedBox(
height: 10,
),
const Divider(),
const SizedBox(
height: 10,
),
const Text(
'Opciones',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () async {
await [
Permission.bluetoothScan,
Permission.bluetoothAdvertise,
Permission.bluetoothConnect
].request();
if (await Permission.bluetoothConnect
.request()
.isGranted) {
setState(() {
_statusBluetooth = true;
});
}
},
child: const Text('Activar Bluetooth')),
),
const SizedBox(
width: 20,
),
Expanded(
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.brown)),
onPressed: () async {
getListReaders();
},
child: const Text('Obtener terminales')),
),
],
),
const SizedBox(
height: 10,
),
const Divider(),
const SizedBox(
height: 10,
),
const Text(
'Realizar cobro',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20,
),
TextField(
controller: amountController,
keyboardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r"[0-9.]"))
],
decoration: const InputDecoration.collapsed(
hintText: 'Monto',
),
),
const SizedBox(
height: 20,
),
Row(
children: [
Expanded(
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.green)),
onPressed: () async {
FocusManager.instance.primaryFocus?.unfocus();
setState(() {
log.clear();
});
await Billpocket.doTransaction(
amount: amountController.text,
tip: "0",
latitude: 19.42691938620286,
longitude: -99.16780320031096,
description: "description");
},
child: const Text('Cobrar')),
),
],
),
const SizedBox(
height: 20,
),
const Text('Log de Transacción',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(
height: 10,
),
ListView.separated(
itemBuilder: (context, pos) {
if (pos == 0) {
return Row(children: [
const Icon(Icons.arrow_forward_ios_rounded, size: 15, color: Colors.lightGreen,),
const SizedBox(width: 10,),
Expanded(child: Text(log[pos], style: const TextStyle(color: Colors.lightGreen, fontWeight: FontWeight.bold),))
],);
}
return Text(log[pos], style: const TextStyle(color: Colors.grey),);
},
itemCount: log.length,
shrinkWrap: true, separatorBuilder: (BuildContext context, int index) {
return Divider();
},
)
],
),
),
),
);
}
Future<void> _showLogs(BuildContext context, String log) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
content: Column(
children: [
const Text('LOGS'),
Expanded(
child: SingleChildScrollView(
child: Text(log.isEmpty ? 'Sin logs' : log)
)
),
],
),
)
);
}
Future<void> getStatusSDK() async {
bool statusSDK;
// 平台消息可能失败,所以我们使用try/catch处理PlatformException。
// 我们还处理消息可能返回null的情况。
try {
statusSDK = await Billpocket.getStatusSDK();
} on PlatformException {
statusSDK = false;
}
// 如果小部件在异步平台消息还在飞行时从树中移除,我们想丢弃回复而不是调用setState更新我们的不存在的外观。
if (!mounted) return;
setState(() {
_statusSDK = statusSDK;
});
}
getListReaders() async {
List<Reader> readers;
try {
readers = await Billpocket.getReaders();
} on PlatformException {
readers = [];
}
if (!mounted) return;
setState(() {
_readers = readers;
showTerminals();
});
}
connectReader(
{required int readerType,
required String readerMacAddress,
required String name}) async {
bool statusConnection;
try {
statusConnection = await Billpocket.connectReader(
readerType: readerType,
readerMacAddress: readerMacAddress,
name: name);
} on PlatformException {
statusConnection = false;
}
if (!mounted) return;
setState(() {
_statusConnection = statusConnection;
});
}
getStatusBluetoothPermission() async {
var status = await Permission.bluetoothScan.status;
if (status.isGranted) {
setState(() {
_statusBluetooth = true;
});
} else {
setState(() {
_statusBluetooth = false;
});
}
}
void listenTransactionStream() {
_subscription = Billpocket.transactionStream().listen((event) {
final eventName = event['event'];
final message = event['message'];
switch (eventName) {
case 'onTransactionAborted':
// 处理onTransactionAborted事件
print('Transaction aborted: $message');
break;
case 'onBeforeTransaction':
// 处理onBeforeTransaction事件
print('Transaction before: $message');
break;
case 'onCardRead':
// 处理onCardRead事件
print('Transaction card read: $message');
break;
case 'getSignature':
// 处理getSignature事件
print('Transaction get signature: $message');
break;
case 'onReaderWaitingForCard':
// 处理onReaderWaitingForCard事件
print('Transaction reader waiting for card: $message');
break;
case 'onMsiDefined':
// 处理onMsiDefined事件
final list = event['list'];
print('Transaction msi defined: $message');
print('MSI list: $list');
showMSI(list);
break;
case 'onGetPin':
// 处理onGetPin事件
print('Transaction get pin: $message');
break;
case 'onMagneticCardFound':
// 处理onMagneticCardFound事件
print('Transaction magnetic card found: $message');
break;
case 'onTransactionFinished':
// 处理onTransactionFinished事件
print('Transaction finished: $message');
break;
case 'onTransactionSuccessful':
// 处理onTransactionSuccessful事件
print('Transaction successful: $message');
break;
case 'resultStartTransaction':
// 处理resultStartTransaction事件
print('Transaction result start: $message');
break;
case 'resultStartTransactionSuccess':
// 处理resultStartTransactionSuccess事件
print('Transaction result start success: $message');
break;
case 'resultStartTransactionError':
// 处理resultStartTransactionError事件
print('Transaction result start error: $message');
break;
// 其他事件类似处理
default:
print('Unknown event received: $eventName');
break;
}
setState(() {
log.insert(0, message);
});
}, onError: (error) {
print("Error received: $error");
});
}
[@override](/user/override)
void dispose() {
_subscription?.cancel();
super.dispose();
}
void showTerminals() {
showModalBottomSheet<void>(
context: context,
builder: (context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
child: ListView.separated(
itemBuilder: (context, pos) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_readers[pos].name,
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 17),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.orange)),
onPressed: () {
Navigator.pop(context);
connectReader(
readerType: _readers[pos].type,
readerMacAddress: _readers[pos].macAddress,
name: _readers[pos].name);
},
child: const Text('Conectar'))
],
),
itemCount: _readers.length,
shrinkWrap: true,
separatorBuilder: (BuildContext context, int index) {
return const Divider();
},
),
),
);
});
}
void showMSI(list) {
List<Installment> installment = List<Installment>.from(
json.decode(list).map((x) => Installment.fromJson(x)));
showModalBottomSheet<void>(
context: context,
builder: (context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
child: ListView.separated(
itemBuilder: (context, pos) => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${installment[pos].value.toString()} meses sin intereses',
style: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 17),
),
ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.orange)),
onPressed: () {
Billpocket.continueWithMsi(
commission: installment[pos].commission!,
installments: installment[pos].value!,
minAmount: installment[pos].minAmount!,
);
Navigator.pop(context);
},
child: const Text('Elegir'))
],
),
itemCount: installment.length,
shrinkWrap: true,
separatorBuilder: (BuildContext context, int index) {
return const Divider();
},
),
),
);
});
}
}
class Status extends StatelessWidget {
const Status({
super.key,
required this.isActive,
required this.title,
});
final bool isActive;
final String title;
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isActive ? Colors.green : Colors.red),
width: 10,
height: 10,
),
const SizedBox(
height: 10,
),
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontWeight: FontWeight.bold),
)
],
);
}
}
更多关于Flutter财务管理插件flutter_billpocket的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter财务管理插件flutter_billpocket的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
flutter_billpocket
是一个用于财务管理的 Flutter 插件,它提供了与 Billpocket API 的集成,使开发者能够在 Flutter 应用中轻松处理支付、发票、交易等财务相关的操作。以下是使用 flutter_billpocket
插件的基本步骤和示例代码。
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 flutter_billpocket
插件的依赖:
dependencies:
flutter:
sdk: flutter
flutter_billpocket: ^1.0.0 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 配置 API 密钥
在使用 flutter_billpocket
之前,你需要在 Billpocket 平台上注册并获取 API 密钥。将 API 密钥配置到你的应用中:
import 'package:flutter_billpocket/flutter_billpocket.dart';
void main() {
// 初始化插件并设置 API 密钥
FlutterBillpocket.initialize(apiKey: 'YOUR_API_KEY');
runApp(MyApp());
}
3. 处理支付
你可以使用 flutter_billpocket
来处理支付。以下是一个简单的支付示例:
import 'package:flutter/material.dart';
import 'package:flutter_billpocket/flutter_billpocket.dart';
class PaymentPage extends StatefulWidget {
@override
_PaymentPageState createState() => _PaymentPageState();
}
class _PaymentPageState extends State<PaymentPage> {
final _amountController = TextEditingController();
final _descriptionController = TextEditingController();
Future<void> _processPayment() async {
try {
final amount = double.parse(_amountController.text);
final description = _descriptionController.text;
final response = await FlutterBillpocket.processPayment(
amount: amount,
description: description,
);
if (response.success) {
// 支付成功
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('支付成功: ${response.transactionId}')),
);
} else {
// 支付失败
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('支付失败: ${response.errorMessage}')),
);
}
} catch (e) {
// 处理异常
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('支付过程中发生错误: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('支付')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _amountController,
decoration: InputDecoration(labelText: '金额'),
keyboardType: TextInputType.number,
),
TextField(
controller: _descriptionController,
decoration: InputDecoration(labelText: '描述'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _processPayment,
child: Text('支付'),
),
],
),
),
);
}
}
4. 查询交易
你还可以使用 flutter_billpocket
来查询交易记录:
Future<void> _fetchTransactions() async {
try {
final transactions = await FlutterBillpocket.fetchTransactions();
// 处理交易记录
for (var transaction in transactions) {
print('Transaction ID: ${transaction.id}, Amount: ${transaction.amount}');
}
} catch (e) {
// 处理异常
print('查询交易记录时发生错误: $e');
}
}
5. 处理回调
flutter_billpocket
还支持支付回调的处理。你可以在应用中监听支付结果:
FlutterBillpocket.setPaymentCallback((response) {
if (response.success) {
// 支付成功
print('支付成功: ${response.transactionId}');
} else {
// 支付失败
print('支付失败: ${response.errorMessage}');
}
});
6. 处理错误
在使用过程中,可能会遇到各种错误。你可以通过捕获异常来处理这些错误:
try {
// 调用 API
} catch (e) {
// 处理错误
print('发生错误: $e');
}