diff --git a/BTCPayServer.Plugins.Payjoin.IntegrationTests/TestUtils/PayjoinTestPayer.cs b/BTCPayServer.Plugins.Payjoin.IntegrationTests/TestUtils/PayjoinTestPayer.cs index 4f75042..5d77373 100644 --- a/BTCPayServer.Plugins.Payjoin.IntegrationTests/TestUtils/PayjoinTestPayer.cs +++ b/BTCPayServer.Plugins.Payjoin.IntegrationTests/TestUtils/PayjoinTestPayer.cs @@ -138,7 +138,7 @@ private async Task CreateSenderPsbtAsync(SystemUri payme paymentAmount = Money.Satoshis(checked((long)amountSats.Value)).ToDecimal(MoneyUnit.BTC); using var _ = parsedUri.CheckPjSupported(); } - catch (PjParseException ex) + catch (UriParseException ex) { throw new InvalidOperationException($"Invalid BIP21 URI '{paymentUrl}': {ex.Message}", ex); } @@ -233,9 +233,9 @@ private async Task RequestProposalAsync(SystemUri paymentUrl, IReadOnlyLis } proposalPsbtBase64 = await PollForProposalAsync(current, senderPersister, ohttpRelayUrls, cancellationToken).ConfigureAwait(false); } - catch (SenderPersistedException.ResponseException ex) + catch (SenderPersistedException ex) when (SenderResponseOf(ex) is { } senderResponse) { - throw CreateSenderResponseFailure(ex); + throw CreateSenderResponseFailure(ex, senderResponse); } if (string.IsNullOrWhiteSpace(proposalPsbtBase64)) @@ -355,13 +355,23 @@ private async Task RequestProposalAsync(SystemUri paymentUrl, IReadOnlyLis throw new InvalidOperationException($"All configured OHTTP relays failed for payer store '{_payer.StoreId}'. LastError='{lastError?.Message}'", lastError); } - private InvalidOperationException CreateSenderResponseFailure(SenderPersistedException.ResponseException ex) + private InvalidOperationException CreateSenderResponseFailure(SenderPersistedException ex, global::Payjoin.ResponseException senderResponse) { return new InvalidOperationException( - $"Payjoin receiver rejected the proposal for payer store '{_payer.StoreId}': {FormatSenderResponseException(ex.v1)}", + $"Payjoin receiver rejected the proposal for payer store '{_payer.StoreId}': {FormatSenderResponseException(senderResponse)}", ex); } + private static global::Payjoin.ResponseException? SenderResponseOf(SenderPersistedException exception) + { + return exception switch + { + SenderPersistedException.Fatal { v1: SenderException.Response response } => response.v1, + SenderPersistedException.Transient { v1: SenderException.Response response } => response.v1, + _ => null + }; + } + private static string FormatSenderResponseException(global::Payjoin.ResponseException responseException) { return responseException switch diff --git a/BTCPayServer.Plugins.Payjoin.Tests/PayjoinSettlementFlowTests.cs b/BTCPayServer.Plugins.Payjoin.Tests/PayjoinSettlementFlowTests.cs index 807f288..8da97f9 100644 --- a/BTCPayServer.Plugins.Payjoin.Tests/PayjoinSettlementFlowTests.cs +++ b/BTCPayServer.Plugins.Payjoin.Tests/PayjoinSettlementFlowTests.cs @@ -407,7 +407,7 @@ private sealed class FixedPsbtProposal(string psbt) : IPayjoinProposal public RequestResponse CreatePostRequest(string ohttpRelay) => throw new NotSupportedException(); public PayjoinProposalTransition ProcessResponse(byte[] body, ClientResponse ohttpContext) => throw new NotSupportedException(); public string Psbt() => psbt; - public PayjoinOutPoint[] UtxosToBeLocked() => throw new NotSupportedException(); + public bool ProposalTxidIsStable() => throw new NotSupportedException(); } private sealed class NoOpSessionProcessor : IPayjoinReceiverSessionProcessor diff --git a/BTCPayServer.Plugins.Payjoin.Tests/Services/PayjoinReceiverProposalFinalizerTests.cs b/BTCPayServer.Plugins.Payjoin.Tests/Services/PayjoinReceiverProposalFinalizerTests.cs index b44b857..d2fbd49 100644 --- a/BTCPayServer.Plugins.Payjoin.Tests/Services/PayjoinReceiverProposalFinalizerTests.cs +++ b/BTCPayServer.Plugins.Payjoin.Tests/Services/PayjoinReceiverProposalFinalizerTests.cs @@ -170,7 +170,7 @@ private sealed class FixedPsbtProposal(string psbt) : IPayjoinProposal public RequestResponse CreatePostRequest(string ohttpRelay) => throw new NotSupportedException(); public PayjoinProposalTransition ProcessResponse(byte[] body, ClientResponse ohttpContext) => throw new NotSupportedException(); public string Psbt() => psbt; - public PayjoinOutPoint[] UtxosToBeLocked() => throw new NotSupportedException(); + public bool ProposalTxidIsStable() => throw new NotSupportedException(); } private sealed class ThrowingProposal : IPayjoinProposal @@ -179,7 +179,7 @@ private sealed class ThrowingProposal : IPayjoinProposal public RequestResponse CreatePostRequest(string ohttpRelay) => throw new NotSupportedException(); public PayjoinProposalTransition ProcessResponse(byte[] body, ClientResponse ohttpContext) => throw new NotSupportedException(); public string Psbt() => throw new NotSupportedException(); - public PayjoinOutPoint[] UtxosToBeLocked() => throw new NotSupportedException(); + public bool ProposalTxidIsStable() => throw new NotSupportedException(); } private sealed class UnusedRelayRequestSender : IPayjoinReceiverRelayRequestSender diff --git a/BTCPayServer.Plugins.Payjoin/Controllers/UIPayJoinController.cs b/BTCPayServer.Plugins.Payjoin/Controllers/UIPayJoinController.cs index 3a5fced..c80f560 100644 --- a/BTCPayServer.Plugins.Payjoin/Controllers/UIPayJoinController.cs +++ b/BTCPayServer.Plugins.Payjoin/Controllers/UIPayJoinController.cs @@ -145,7 +145,7 @@ public async Task> RunTestPayment([FromBody paymentAmount = Money.Satoshis(checked((long)amountSats.Value)).ToDecimal(MoneyUnit.BTC); using var _ = parsedUri.CheckPjSupported(); } - catch (PjParseException ex) + catch (UriParseException ex) { return RunTestPaymentFailure($"Invalid BIP21 URI: {ex.Message}"); } diff --git a/BTCPayServer.Plugins.Payjoin/Services/PayjoinInvoicePaymentUrlService.cs b/BTCPayServer.Plugins.Payjoin/Services/PayjoinInvoicePaymentUrlService.cs index 07cec2b..70fe649 100644 --- a/BTCPayServer.Plugins.Payjoin/Services/PayjoinInvoicePaymentUrlService.cs +++ b/BTCPayServer.Plugins.Payjoin/Services/PayjoinInvoicePaymentUrlService.cs @@ -151,7 +151,7 @@ private static bool IsPayjoinEnabled(string paymentUrl) using var _ = parsedUri.CheckPjSupported(); return true; } - catch (PjParseException) + catch (UriParseException) { return false; } diff --git a/BTCPayServer.Plugins.Payjoin/Services/RunTestPaymentService.cs b/BTCPayServer.Plugins.Payjoin/Services/RunTestPaymentService.cs index 2af20a7..a0636d9 100644 --- a/BTCPayServer.Plugins.Payjoin/Services/RunTestPaymentService.cs +++ b/BTCPayServer.Plugins.Payjoin/Services/RunTestPaymentService.cs @@ -272,13 +272,13 @@ private async Task RequestProposalAsync( current.Dispose(); } } - catch (BuildSenderException ex) + catch (SenderInputException ex) { throw new RunTestPaymentExecutionException($"Sender build failed: {ex.Message}", ex); } - catch (SenderPersistedException.ResponseException ex) + catch (SenderPersistedException ex) when (SenderResponseOf(ex) is { } senderResponse) { - throw new RunTestPaymentExecutionException($"Sender rejected by receiver: {FormatSenderResponseException(ex.v1)}", ex); + throw new RunTestPaymentExecutionException($"Sender rejected by receiver: {FormatSenderResponseException(senderResponse)}", ex); } catch (HttpRequestException ex) { @@ -442,6 +442,16 @@ private static IReadOnlyList GetConfiguredRelayUrls(RunTestPaymentCon throw new RunTestPaymentExecutionException("No OHTTP relays configured for test payment."); } + private static global::Payjoin.ResponseException? SenderResponseOf(SenderPersistedException exception) + { + return exception switch + { + SenderPersistedException.Fatal { v1: SenderException.Response response } => response.v1, + SenderPersistedException.Transient { v1: SenderException.Response response } => response.v1, + _ => null + }; + } + private static string FormatSenderResponseException(global::Payjoin.ResponseException responseException) { return responseException switch diff --git a/rust-payjoin b/rust-payjoin index f4a6008..dee54d6 160000 --- a/rust-payjoin +++ b/rust-payjoin @@ -1 +1 @@ -Subproject commit f4a6008a3a531530434bdd6911db864bcb42fc66 +Subproject commit dee54d6512f6860ffefefa93681694a623ab2328