From 7af9c6c1494dcf15fd7471a7954ac4901b47d205 Mon Sep 17 00:00:00 2001 From: jbesraa Date: Thu, 1 Feb 2024 10:44:12 +0200 Subject: [PATCH 1/2] Impl `send::ResponseError::is_version_supported` Add `is_version_supported` for `ResponseError` public API so users could check if the error is suggesting to send a payjoin request using different version. --- payjoin/src/send/error.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/payjoin/src/send/error.rs b/payjoin/src/send/error.rs index 9fc58b0a7..4224b1238 100644 --- a/payjoin/src/send/error.rs +++ b/payjoin/src/send/error.rs @@ -318,6 +318,20 @@ impl ResponseError { Err(_) => InternalValidationError::Parse.into(), } } + + /// Check whether or not the payjoin receiver supports the supplied version. + /// + /// This is useful when you support multiple versions of payjoin. For example, if you support + /// v1 and v2 (BIP78 and BIP77 respectively) and sent a v2 request that resulted in this error, + /// you could pass `1` to test whether or not the receiver might respond well to another + /// request using the older version. + pub fn is_version_supported(&self, v: &u64) -> bool { + if let Self::WellKnown(WellKnownError::VersionUnsupported(_, supported)) = self { + supported.contains(v) + } else { + false + } + } } impl std::error::Error for ResponseError {} From 61a721b355b5e0eb6c32ed5b98cb97a21a1effa0 Mon Sep 17 00:00:00 2001 From: jbesraa Date: Sun, 28 Jan 2024 19:24:24 +0200 Subject: [PATCH 2/2] Try v1 request if v2 fail in payjoin-cli When we make a payjoin request based on version 2 and the payjoin receiver returns an error indicating they only support version 1, we attempt to make a version 1 request. --- payjoin-cli/src/app.rs | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index 42123fbcc..8642dedde 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -194,6 +194,22 @@ impl App { Ok(()) } + #[cfg(feature = "v2")] + async fn try_v1(&self, req_ctx: &mut payjoin::send::RequestContext) -> Result { + let (req, ctx) = req_ctx.clone().extract_v1().unwrap(); + let http = http_agent()?; + println!("Sending fallback v1 request to {} after v2 request attempt fail", &req.url); + let response = spawn_blocking(move || { + http.post(req.url.as_ref()) + .set("Content-Type", "text/plain") + .send_bytes(&req.body) + .map_err(map_ureq_err) + }) + .await??; + println!("Sent fallback v1 request"); + Ok(ctx.process_response(&mut response.into_reader())?) + } + #[cfg(feature = "v2")] async fn long_poll_post(&self, req_ctx: &mut payjoin::send::RequestContext) -> Result { loop { @@ -211,12 +227,18 @@ impl App { println!("Sent fallback transaction"); match ctx.process_response(&mut response.into_reader()) { Ok(Some(psbt)) => return Ok(psbt), - Ok(None) => std::thread::sleep(std::time::Duration::from_secs(5)), - Err(re) => { - println!("{}", re); - log::debug!("{:?}", re); + Ok(None) => { + std::thread::sleep(std::time::Duration::from_secs(5)); + continue; } - } + Err(error) => { + if error.is_version_supported(&1) { + return self.try_v1(req_ctx).await; + }; + println!("{}", error); + log::debug!("{:?}", error); + } + }; } }