Flutter支付集成插件payjoin_flutter的使用
Flutter支付集成插件payjoin_flutter的使用
payjoin_flutter
是一个用于在 Flutter 应用中集成支付功能的库。它基于 Payjoin Dev Kit
实现,支持比特币支付协议。
如何使用
要在项目中使用 payjoin_flutter
插件,请将其添加为依赖项到项目的 pubspec.yaml
文件中:
dependencies:
payjoin_flutter: 0.20.0
然后运行 flutter pub get
来安装依赖。
要求
- Flutter 版本:3.0 或更高版本。
- Android 最低 SDK 版本:API 23 或更高版本。
- iOS 部署目标:12.0 或更高版本。
构建和运行代码
首先,需要在本地设置 bitcoin core
和 esplora
到 regtest
网络中。如果没有这些工具,可以参考以下页面进行安装:
或者可以安装 Nigiri Bitcoin
工具来简化运行本地实例的过程。你可以参考以下链接进行安装:
一旦 Nigiri Bitcoin
启动运行,你需要挖一些区块。可以参考以下链接了解如何挖区块:
运行集成测试
在 Regtest
网络中正确设置了 Bitcoin core
后,可以通过以下步骤运行集成测试:
git clone https://github.com/LtbLightning/payjoin-flutter.git
cd payjoin-flutter
cd example
# 运行集成测试
flutter test integration_test
示例代码
以下是一个完整的示例代码,展示了如何使用 payjoin_flutter
插件实现支付功能。
import 'dart:convert';
import 'package:bdk_flutter/bdk_flutter.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:payjoin_flutter/common.dart' as common;
import 'package:payjoin_flutter/uri.dart' as pay_join_uri;
import 'package:payjoin_flutter_example/bdk_client.dart';
import 'package:payjoin_flutter_example/payjoin_library.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
[@override](/user/override)
void initState() {
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false, home: PayJoin());
}
}
class PayJoin extends StatefulWidget {
const PayJoin({super.key});
[@override](/user/override)
State<PayJoin> createState() => _PayJoinState();
}
class _PayJoinState extends State<PayJoin> {
static const primaryColor = 0xffC71585;
PayJoinLibrary payJoinLibrary = PayJoinLibrary();
final sender = BdkClient(
"wpkh(tprv8ZgxMBicQKsPdgsqhkRVYkBBULxG3HvyXtwhWKEgfH4bsU8bmaqhdbZvxq4Z7BLFtUrT58ynRDrBcfG3vNpNHsKTV5xCEgRoKaNNzcVW3HW/84'/1'/0'/0/*)#ln3hfgcf",
Network.signet);
final receiver = BdkClient(
"wpkh(tprv8ZgxMBicQKsPfKJjrApLfm2BhWhV1JpL3StS8UPagm91Y215JGZktQKTtvErD92RKxEDYD9Sfc9eGZVkuH94NgEHPhz7rpgzhiNm2UPs1G1/84'/1'/0'/0/*)#h8uywf09",
Network.signet);
String displayText = "";
String pjUri = "";
late PartiallySignedTransaction senderPsbt;
late PartiallySignedTransaction processedAndFinalizedPsbt;
[@override](/user/override)
void initState() {
sender.restoreWallet();
receiver.restoreWallet();
setState(() {
displayText = "sender & receiver restored";
});
super.initState();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(primaryColor),
elevation: 0,
centerTitle: false,
title: Text('PayJoin App',
style: GoogleFonts.ibmPlexMono(
fontWeight: FontWeight.w900,
fontSize: 18,
color: Colors.white)), // 设置标题高度
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
margin: const EdgeInsets.only(bottom: 50),
padding: const EdgeInsets.only(left: 15, right: 15, bottom: 20),
color: const Color(primaryColor),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Response: ",
textAlign: TextAlign.center,
style: GoogleFonts.manrope(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700)),
Expanded(
child: SelectableText(
displayText,
maxLines: 3,
textAlign: TextAlign.start,
style: GoogleFonts.ibmPlexMono(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w700),
),
),
],
),
),
TextButton(
onPressed: () async {
await sender.syncWallet();
await receiver.syncWallet();
setState(() {
displayText = "sync complete";
});
debugPrint(
"sender balance: ${(sender.getBalance()).toString()}");
},
child: Text(
"Sync wallets",
style: GoogleFonts.manrope(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
final address = (receiver.getNewAddress()).address;
final res = await payJoinLibrary.buildPjUri(
0.0083285, address.toQrUri());
setState(() {
pjUri = res;
displayText = res;
});
},
child: Text(
"Build Receiver pj Uri",
style: GoogleFonts.manrope(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
final balance = sender.getBalance();
debugPrint("Sender Balance: ${balance.toString()}");
final uri = await pay_join_uri.Uri.fromStr(pjUri);
final address = uri.address();
int amount = (((uri.amount()) ?? 0) * 100000000).toInt();
final psbt = (await sender.createPsbt(address, amount, 2000));
debugPrint(
"\nOriginal sender psbt: ${psbt.toString()}",
);
setState(() {
senderPsbt = psbt;
});
},
child: Text(
"Create Sender psbt using receiver pjUri",
style: GoogleFonts.manrope(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
final (provisionalProposal, contextV1) = await payJoinLibrary
.handlePjRequest(senderPsbt.toString(), pjUri, (e) async {
final script = ScriptBuf(bytes: e);
return (receiver.getAddressInfo(script));
});
final unspent = receiver.listUnspent();
// 选择接收方的 payjoin 输入。
Map<BigInt, common.OutPoint> candidateInputs = {
for (var input in unspent)
input.txout.value: common.OutPoint(
txid: input.outpoint.txid.toString(),
vout: input.outpoint.vout)
};
final selectedOutpoint = await provisionalProposal
.tryPreservingPrivacy(candidateInputs: candidateInputs);
var selectedUtxo = unspent.firstWhere(
(i) =>
i.outpoint.txid.toString() == selectedOutpoint.txid &&
i.outpoint.vout == selectedOutpoint.vout,
orElse: () => throw Exception('UTXO not found'));
var txoToContribute = common.TxOut(
value: selectedUtxo.txout.value,
scriptPubkey: selectedUtxo.txout.scriptPubkey.bytes,
);
var outpointToContribute = common.OutPoint(
txid: selectedUtxo.outpoint.txid.toString(),
vout: selectedUtxo.outpoint.vout,
);
await provisionalProposal.contributeWitnessInput(
txo: txoToContribute, outpoint: outpointToContribute);
final payJoinProposal = await provisionalProposal
.finalizeProposal(processPsbt: (e) async {
debugPrint("\n Original receiver unsigned psbt: $e");
return (await receiver.signPsbt(
await PartiallySignedTransaction.fromString(e)))
.toString();
});
final receiverPsbt = await payJoinProposal.psbt();
debugPrint("\n Original receiver psbt: $receiverPsbt");
final receiverProcessedPsbt = await contextV1.processResponse(
response: utf8.encode(receiverPsbt));
final senderProcessedPsbt = (await sender.signPsbt(
await PartiallySignedTransaction.fromString(
receiverProcessedPsbt)));
setState(() {
processedAndFinalizedPsbt = senderProcessedPsbt;
});
},
child: Text(
"Process and finalize receiver Pj request",
style: GoogleFonts.manrope(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w800),
)),
TextButton(
onPressed: () async {
final res =
await sender.broadcastPsbt(processedAndFinalizedPsbt);
debugPrint("Broadcast success: $res");
},
child: Text(
"Broadcast processed psbt",
style: GoogleFonts.manrope(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w800),
))
],
),
),
);
}
}
更多关于Flutter支付集成插件payjoin_flutter的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter支付集成插件payjoin_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter应用中集成payjoin_flutter
插件的示例代码。payjoin_flutter
是一个假想的支付插件,用于演示目的,实际使用中请确保你使用的是真实可用的支付插件。
首先,确保你已经在pubspec.yaml
文件中添加了payjoin_flutter
依赖:
dependencies:
flutter:
sdk: flutter
payjoin_flutter: ^x.y.z # 替换为实际的版本号
然后运行flutter pub get
来获取依赖。
接下来,在你的Flutter应用中配置支付功能。以下是一个简单的示例,展示如何初始化支付插件并处理支付结果。
1. 初始化支付插件
在你的主文件(通常是main.dart
)中,导入payjoin_flutter
插件并进行初始化:
import 'package:flutter/material.dart';
import 'package:payjoin_flutter/payjoin_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Payment Integration',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PaymentScreen(),
);
}
}
class PaymentScreen extends StatefulWidget {
@override
_PaymentScreenState createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
final PayJoinFlutter _payJoin = PayJoinFlutter();
@override
void initState() {
super.initState();
// 初始化支付插件(如果需要的话)
_initializePaymentPlugin();
}
Future<void> _initializePaymentPlugin() async {
try {
// 假设有一个初始化方法
await _payJoin.initialize();
} catch (e) {
print('支付插件初始化失败: $e');
}
}
void _startPayment() async {
try {
// 假设支付请求需要一些参数,例如订单ID和金额
Map<String, dynamic> paymentDetails = {
'orderId': '123456',
'amount': 100.0,
};
// 发起支付请求
PaymentResult result = await _payJoin.startPayment(paymentDetails);
if (result.status == PaymentStatus.success) {
print('支付成功: ${result.transactionId}');
} else if (result.status == PaymentStatus.failed) {
print('支付失败: ${result.errorMessage}');
} else if (result.status == PaymentStatus.canceled) {
print('支付已取消');
}
} catch (e) {
print('支付过程中发生错误: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('支付集成示例'),
),
body: Center(
child: ElevatedButton(
onPressed: _startPayment,
child: Text('发起支付'),
),
),
);
}
}
// 假设支付结果的数据结构
class PaymentResult {
final PaymentStatus status;
final String? transactionId;
final String? errorMessage;
PaymentResult({required this.status, this.transactionId, this.errorMessage});
}
enum PaymentStatus { success, failed, canceled }
2. 处理支付回调(如果需要)
某些支付插件可能需要在应用的其他部分处理支付回调,例如处理从支付网关返回的结果。这通常涉及到在应用的MainActivity
或AppDelegate
中添加一些代码(对于原生部分的处理),但在Flutter插件中,回调通常会通过Dart代码处理。
由于payjoin_flutter
是一个假想的插件,具体实现细节可能会有所不同。在实际使用中,请查阅该插件的官方文档以了解如何处理支付回调。
注意事项
- 确保你使用的支付插件是安全且经过验证的。
- 在发布应用之前,进行充分的测试以确保支付流程的稳定性和安全性。
- 遵守相关的支付和隐私法规。
这个示例代码提供了一个基本的框架,用于在Flutter应用中集成支付功能。根据你所使用的具体支付插件,代码可能需要相应的调整。