From d9fc4d7b57dcf35bfdaa2186bc3e96419bc3852b Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Fri, 4 Jul 2025 01:40:12 +0200 Subject: [PATCH] 3&4 fixes for Payjoin (#2358) * fix: update Payjoin transaction details list item type to `TransactionDetailsListItem` * fix: add error handling for unrecoverable Payjoin sender sessions * fix: centralize Payjoin connectivity error handling with `_isPayjoinConnectivityError` function --- cw_bitcoin/lib/bitcoin_wallet_addresses.dart | 7 ++++-- cw_bitcoin/lib/payjoin/manager.dart | 25 +++++++++++-------- .../payjoin_details_view_model.dart | 2 +- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index d84d958be..fcd0b7d8c 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -57,6 +57,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S return generateP2WPKHAddress(hd: hd, index: index, network: network); } + bool _isPayjoinConnectivityError(String error) => + ["error sending request for url", "Instance of 'FfiIoError'"].any((e) => error.contains(e)); + @action Future initPayjoin() async { try { @@ -68,7 +71,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S } catch (e) { printV(e); // Ignore Connectivity errors - if (!e.toString().contains("error sending request for url")) rethrow; + if (!_isPayjoinConnectivityError(e.toString())) rethrow; } } @@ -82,7 +85,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S } catch (e) { printV(e); // Ignore Connectivity errors - if (!e.toString().contains("error sending request for url")) rethrow; + if (!_isPayjoinConnectivityError(e.toString())) rethrow; } } } diff --git a/cw_bitcoin/lib/payjoin/manager.dart b/cw_bitcoin/lib/payjoin/manager.dart index 95a523d89..b1886cb4b 100644 --- a/cw_bitcoin/lib/payjoin/manager.dart +++ b/cw_bitcoin/lib/payjoin/manager.dart @@ -18,6 +18,7 @@ import 'package:payjoin_flutter/common.dart'; import 'package:payjoin_flutter/receive.dart'; import 'package:payjoin_flutter/send.dart'; import 'package:payjoin_flutter/src/config.dart' as pj_config; +import 'package:payjoin_flutter/src/generated/api.dart' as pj_api; import 'package:payjoin_flutter/uri.dart' as PayjoinUri; class PayjoinManager { @@ -44,17 +45,21 @@ class PayjoinManager { final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id); final spawnedSessions = allSessions.map((session) { - if (session.isSenderSession) { - printV("Resuming Payjoin Sender Session ${session.pjUri!}"); - return _spawnSender( - sender: Sender.fromJson(json: session.sender!), - pjUri: session.pjUri!, - ); + try { + if (session.isSenderSession) { + printV("Resuming Payjoin Sender Session ${session.pjUri!}"); + return _spawnSender( + sender: Sender.fromJson(json: session.sender!), + pjUri: session.pjUri!, + ); + } + final receiver = Receiver.fromJson(json: session.receiver!); + printV("Resuming Payjoin Receiver Session ${receiver.id()}"); + return spawnReceiver(receiver: receiver); + } on pj_api.FfiSerdeJsonError catch (_) { + _payjoinStorage.markSenderSessionUnrecoverable(session.pjUri!, "Outdated Session"); } - final receiver = Receiver.fromJson(json: session.receiver!); - printV("Resuming Payjoin Receiver Session ${receiver.id()}"); - return spawnReceiver(receiver: receiver); - }); + }).nonNulls; printV("Resumed ${spawnedSessions.length} Payjoin Sessions"); await Future.wait(spawnedSessions); diff --git a/lib/view_model/payjoin_details_view_model.dart b/lib/view_model/payjoin_details_view_model.dart index 7d63aaae3..d6bebfe7f 100644 --- a/lib/view_model/payjoin_details_view_model.dart +++ b/lib/view_model/payjoin_details_view_model.dart @@ -27,7 +27,7 @@ abstract class PayjoinDetailsViewModelBase with Store { this.transactionInfo, { required this.payjoinSessionSource, required this.themeStore, - }) : items = ObservableList(), + }) : items = ObservableList(), payjoinSession = payjoinSessionSource.get(payjoinSessionId)! { listener = payjoinSessionSource.watch().listen((e) { if (e.key == payjoinSessionId) _updateItems();