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

Konstantin Ullrich committed Jul 4, 2025 at 01:40 UTC d9fc4d7b57dcf35bfdaa2186bc3e96419bc3852b
3 files changed +21 -13
cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+5 -2
@@ -57,6 +57,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
57 return generateP2WPKHAddress(hd: hd, index: index, network: network);
58 }
59
60 + bool _isPayjoinConnectivityError(String error) =>
61 + ["error sending request for url", "Instance of 'FfiIoError'"].any((e) => error.contains(e));
62 +
63 @action
64 Future<void> initPayjoin() async {
65 try {
@@ -68,7 +71,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
71 } catch (e) {
72 printV(e);
73 // Ignore Connectivity errors
71 - if (!e.toString().contains("error sending request for url")) rethrow;
74 + if (!_isPayjoinConnectivityError(e.toString())) rethrow;
75 }
76 }
77
@@ -82,7 +85,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
85 } catch (e) {
86 printV(e);
87 // Ignore Connectivity errors
85 - if (!e.toString().contains("error sending request for url")) rethrow;
88 + if (!_isPayjoinConnectivityError(e.toString())) rethrow;
89 }
90 }
91 }
cw_bitcoin/lib/payjoin/manager.dart
+15 -10
@@ -18,6 +18,7 @@ import 'package:payjoin_flutter/common.dart';
18 import 'package:payjoin_flutter/receive.dart';
19 import 'package:payjoin_flutter/send.dart';
20 import 'package:payjoin_flutter/src/config.dart' as pj_config;
21 +import 'package:payjoin_flutter/src/generated/api.dart' as pj_api;
22 import 'package:payjoin_flutter/uri.dart' as PayjoinUri;
23
24 class PayjoinManager {
@@ -44,17 +45,21 @@ class PayjoinManager {
45 final allSessions = _payjoinStorage.readAllOpenSessions(_wallet.id);
46
47 final spawnedSessions = allSessions.map((session) {
47 - if (session.isSenderSession) {
48 - printV("Resuming Payjoin Sender Session ${session.pjUri!}");
49 - return _spawnSender(
50 - sender: Sender.fromJson(json: session.sender!),
51 - pjUri: session.pjUri!,
52 - );
48 + try {
49 + if (session.isSenderSession) {
50 + printV("Resuming Payjoin Sender Session ${session.pjUri!}");
51 + return _spawnSender(
52 + sender: Sender.fromJson(json: session.sender!),
53 + pjUri: session.pjUri!,
54 + );
55 + }
56 + final receiver = Receiver.fromJson(json: session.receiver!);
57 + printV("Resuming Payjoin Receiver Session ${receiver.id()}");
58 + return spawnReceiver(receiver: receiver);
59 + } on pj_api.FfiSerdeJsonError catch (_) {
60 + _payjoinStorage.markSenderSessionUnrecoverable(session.pjUri!, "Outdated Session");
61 }
54 - final receiver = Receiver.fromJson(json: session.receiver!);
55 - printV("Resuming Payjoin Receiver Session ${receiver.id()}");
56 - return spawnReceiver(receiver: receiver);
57 - });
62 + }).nonNulls;
63
64 printV("Resumed ${spawnedSessions.length} Payjoin Sessions");
65 await Future.wait(spawnedSessions);
lib/view_model/payjoin_details_view_model.dart
+1 -1
@@ -27,7 +27,7 @@ abstract class PayjoinDetailsViewModelBase with Store {
27 this.transactionInfo, {
28 required this.payjoinSessionSource,
29 required this.themeStore,
30 - }) : items = ObservableList<StandartListItem>(),
30 + }) : items = ObservableList<TransactionDetailsListItem>(),
31 payjoinSession = payjoinSessionSource.get(payjoinSessionId)! {
32 listener = payjoinSessionSource.watch().listen((e) {
33 if (e.key == payjoinSessionId) _updateItems();