Flutter支付终端插件stripe_terminal的使用
Flutter支付终端插件stripe_terminal的使用
stripe_terminal
是一个用于扫描 Stripe 读卡器并连接到它们以获取支付方法的 Flutter 插件。本文将介绍如何在 Flutter 应用中使用该插件。
安装
Android
无需配置,开箱即用。
iOS
需要在 Info.plist
文件中提供权限请求字符串,并授权后台模式。
示例内容:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Location access is required in order to accept payments.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Bluetooth access is required in order to connect to supported bluetooth card readers.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to connect to supported card readers.</string>
后台模式授权:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
使用
初始化 SDK
首先需要初始化 SDK 并获取连接令牌。
import 'package:stripe_terminal/stripe_terminal.dart';
StripeTerminal stripeTerminal = StripeTerminal(
fetchToken: () async {
// 调用后端 API 获取连接令牌
const token = "pst_test_XXXXXXXXXX....";
return token;
},
);
示例后端代码(Node.js)
import Stripe from "stripe";
import express from "express";
const stripe = new Stripe("sk_test_XXXXXXXXXXXXXXXXXX", {
apiVersion: "2020-08-27"
});
const app = express();
app.get("/connectionToken", async (req, res) => {
const token = await stripe.terminal.connectionTokens.create();
res.send({
success: true,
data: token.secret
});
});
app.post("/createPaymentIntent", async (req, res) => {
const pi = await stripe.paymentIntents.create({
amount: 1000,
currency: "USD",
capture_method: "manual",
payment_method_types: ["card_present"]
});
res.send({
success: true,
paymentIntent: pi
});
});
app.listen(8000, () => {
console.log("Server started");
});
发现附近的设备并展示给用户
stripeTerminal
.discoverReaders(simulated: true)
.listen((List<StripeReader> readers) {
setState(() {
this.readers = readers;
});
});
连接到蓝牙读卡器
bool connected = await stripeTerminal.connectBluetoothReader(readers[0].serialNumber);
if (connected) {
print("Connected to a device");
}
扫描卡片
stripeTerminal
.readReusableCardDetail()
.then((StripePaymentMethod paymentMethod) {
print("A card was read, the last four digit is ${paymentMethod.card?.last4}");
});
扫描支付方式
Future<String> createPaymentIntent() async {
Response invoice = await _dio.post("/createPaymentIntent");
return invoice.data["paymentIntent"]["client_secret"];
}
String payment_intent_client_secret = await createPaymentIntent();
stripeTerminal
.collectPaymentMethod(payment_intent_client_secret)
.then((StripePaymentIntent paymentIntent) {
print("A payment intent has captured a payment method, send this payment intent to your backend to capture the payment");
});
目前支持的功能
- 初始化终端 SDK
- 扫描读卡器
- 连接到设备(仅限 Android 上的蓝牙设备)
- 检查连接状态
- 检查已连接的设备
- 从设备读取支付方式
缺少的功能
- 创建支付意图
- 处理支付
- 捕获支付
示例 Demo
以下是一个完整的示例代码,展示了如何使用 stripe_terminal
插件:
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:stripe_terminal/stripe_terminal.dart';
void main() {
runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Dio _dio = Dio(BaseOptions(baseUrl: "YOUR_BACKEND_URL"));
Future<String> getConnectionString() async {
Response response = await _dio.get("/connectionToken");
if (!(response.data["success"])) {
throw Exception("Failed to get connection token because ${response.data["message"]}");
}
return (response.data)["data"];
}
Future<void> _pushLogs(StripeLog log) async {
debugPrint(log.code);
debugPrint(log.message);
}
Future<String> createPaymentIntent() async {
Response invoice = await _dio.post("/createPaymentIntent");
return invoice.data['data']["client_secret"];
}
late StripeTerminal stripeTerminal;
[@override](/user/override)
void initState() {
super.initState();
_initStripe();
}
_initStripe() async {
stripeTerminal = await StripeTerminal.getInstance(fetchToken: getConnectionString);
stripeTerminal.onNativeLogs.listen(_pushLogs);
}
bool simulated = true;
StreamSubscription? _sub;
List<StripeReader>? readers;
String? paymentIntentId;
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Plugin example app')),
body: Center(
child: Column(
children: [
ListTile(
onTap: () {
setState(() {
simulated = !simulated;
_initStripe();
});
},
title: const Text("Scanning mode"),
trailing: Text(simulated ? "Simulator" : "Real"),
),
ListTile(
onTap: () async {
await Permission.bluetooth.request();
await Permission.bluetoothScan.request();
await Permission.bluetoothConnect.request();
},
title: const Text("Ask Bluetooth Permission"),
),
TextButton(child: const Text("Init Stripe"), onPressed: _initStripe),
TextButton(
child: const Text("Get Connection Token"),
onPressed: () async {
String connectionToken = await getConnectionString();
_showSnackbar(connectionToken);
},
),
if (_sub == null)
TextButton(
child: const Text("Scan Devices"),
onPressed: () async {
setState(() {
readers = [];
});
_sub = stripeTerminal
.discoverReaders(DiscoverConfig(discoveryMethod: DiscoveryMethod.bluetooth, simulated: simulated))
.listen((readers) {
setState(() {
this.readers = readers;
});
});
},
),
if (_sub != null)
TextButton(
child: const Text("Stop Scanning"),
onPressed: () async {
setState(() {
_sub?.cancel();
_sub = null;
});
},
),
TextButton(
child: const Text("Connection Status"),
onPressed: () async {
stripeTerminal.connectionStatus().then((status) {
_showSnackbar("Connection status: ${status.toString()}");
});
},
),
TextButton(
child: const Text("Connected Device"),
onPressed: () async {
stripeTerminal.fetchConnectedReader().then((StripeReader? reader) {
_showSnackbar("Connected Device: ${reader?.toJson()}");
});
},
),
if (readers != null)
...readers!.map(
(e) => ListTile(
title: Text(e.serialNumber),
trailing: Text(describeEnum(e.batteryStatus)),
leading: Text(e.locationId ?? "No Location Id"),
onTap: () async {
await stripeTerminal.connectBluetoothReader(e.serialNumber, locationId: "your_location_id").then((value) {
_showSnackbar("Connected to a device");
}).catchError((e) {
if (e is PlatformException) {
_showSnackbar(e.message ?? e.code);
}
});
},
subtitle: Text(describeEnum(e.deviceType)),
),
),
TextButton(
child: const Text("Read Reusable Card Detail"),
onPressed: () async {
stripeTerminal.readReusableCardDetail().then((StripePaymentMethod paymentMethod) {
_showSnackbar("A card was read: ${paymentMethod.card?.toJson()}");
});
},
),
TextButton(
child: const Text("Collect Payment Method"),
onPressed: () async {
paymentIntentId = await createPaymentIntent();
stripeTerminal.collectPaymentMethod(paymentIntentId!).then((StripePaymentIntent paymentIntent) async {
_dio.post("/confirmPaymentIntent", data: {"paymentIntentId": paymentIntent.id});
_showSnackbar("A payment method was captured");
});
},
),
],
),
),
);
}
_showSnackbar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(message, style: const TextStyle(color: Colors.red)),
),
);
}
}
更多关于Flutter支付终端插件stripe_terminal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter支付终端插件stripe_terminal的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter应用中使用stripe_terminal
插件来实现支付终端功能的代码示例。请注意,实际使用中你需要确保已经正确设置了Stripe账户和相关的API密钥。
首先,确保你的Flutter项目已经添加了stripe_terminal
依赖。在pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
stripe_terminal: ^x.y.z # 替换为最新版本号
然后运行flutter pub get
来安装依赖。
接下来,我们将展示一个基本的Flutter应用,它使用stripe_terminal
插件来连接到一个支付终端并执行支付。
1. 初始化Stripe Terminal
在你的主Dart文件中(例如main.dart
),你需要初始化Stripe Terminal并设置必要的回调。
import 'package:flutter/material.dart';
import 'package:stripe_terminal/stripe_terminal.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
StripeTerminal? _stripeTerminal;
String _status = '';
@override
void initState() {
super.initState();
_initStripeTerminal();
}
Future<void> _initStripeTerminal() async {
// 替换为你的Stripe Secret Key
String publishableKey = 'your_publishable_key';
_stripeTerminal = StripeTerminal(
options: StripeTerminalOptions(
publishableKey: publishableKey,
),
);
_stripeTerminal!.addListener(_stripeTerminalListener);
try {
await _stripeTerminal!.discoverReaders();
setState(() {
_status = 'Readers discovered';
});
} catch (e) {
setState(() {
_status = 'Failed to discover readers: $e';
});
}
}
void _stripeTerminalListener() {
// 监听Stripe Terminal的状态变化
setState(() {
_status = _stripeTerminal!.status.toString();
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stripe Terminal Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Status: $_status'),
SizedBox(height: 20),
ElevatedButton(
onPressed: _connectReader,
child: Text('Connect Reader'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _collectPaymentMethod,
child: Text('Collect Payment Method'),
enabled: _stripeTerminal!.connectedReader != null,
),
],
),
),
),
);
}
Future<void> _connectReader() async {
if (_stripeTerminal!.readers.isNotEmpty) {
try {
await _stripeTerminal!.connectReader(_stripeTerminal!.readers.first.serialNumber);
setState(() {
_status = 'Reader connected';
});
} catch (e) {
setState(() {
_status = 'Failed to connect reader: $e';
});
}
} else {
setState(() {
_status = 'No readers available';
});
}
}
Future<void> _collectPaymentMethod() async {
if (_stripeTerminal!.connectedReader != null) {
try {
PaymentIntentResult result = await _stripeTerminal!.collectPaymentMethod(
PaymentMethodParameters(
amount: 1000, // 支付金额,单位:分
currency: 'usd',
),
);
setState(() {
_status = 'Payment successful: ${result.paymentIntent.id}';
});
} catch (e) {
setState(() {
_status = 'Failed to collect payment method: $e';
});
}
} else {
setState(() {
_status = 'No reader connected';
});
}
}
@override
void dispose() {
_stripeTerminal?.removeListener(_stripeTerminalListener);
_stripeTerminal?.cancelConnection();
super.dispose();
}
}
注意事项
-
API密钥:确保你使用的是你的Stripe发布密钥(publishable key),而不是你的秘密密钥(secret key)。秘密密钥不应该在客户端代码中暴露。
-
支付金额:在
_collectPaymentMethod
方法中,amount
字段表示支付金额,单位为分。例如,1000
表示10美元。 -
错误处理:示例代码中对一些可能的错误进行了简单的处理,但在实际生产环境中,你可能需要更复杂的错误处理和用户反馈机制。
-
依赖管理:确保你的Flutter环境是最新的,并且所有依赖项都已正确安装。
-
硬件要求:为了使用Stripe Terminal,你需要一个兼容的支付读卡器。
-
支付处理:在服务器端处理支付结果,确保支付的安全性。
这个示例提供了一个基本的框架,展示了如何在Flutter应用中使用Stripe Terminal插件。根据你的具体需求,你可能需要进一步扩展和定制代码。