Skip to content

Commit fece238

Browse files
committed
feat: add resume and history commands for payjoin sessions
- Implement resume_payjoins() to continue pending sessions - Add history() to display all session states - Add session status text helpers for UI display - Support filtering by session ID
1 parent 8a6db16 commit fece238

File tree

2 files changed

+80
-12
lines changed

2 files changed

+80
-12
lines changed

src/commands.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,20 @@ pub enum OnlineWalletSubCommand {
479479
)]
480480
fee_rate: u64,
481481
},
482+
/// Resume pending payjoin sessions.
483+
ResumePayjoin {
484+
/// Payjoin directory for the session
485+
#[arg(env = "PAYJOIN_DIRECTORY", long = "directory", required = true)]
486+
directory: String,
487+
/// URL of the Payjoin OHTTP relay. Can be repeated multiple times.
488+
#[arg(env = "PAYJOIN_OHTTP_RELAY", long = "ohttp_relay", required = true)]
489+
ohttp_relay: Vec<String>,
490+
/// Resume only a specific active session ID (sender and/or receiver).
491+
#[arg(env = "PAYJOIN_SESSION_ID", long = "session_id")]
492+
session_id: Option<i64>,
493+
},
494+
/// Show payjoin session history.
495+
PayjoinHistory,
482496
}
483497

484498
/// Subcommands for Key operations.

src/payjoin/mod.rs

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,60 @@ pub(crate) struct PayjoinManager<'a> {
4242
relay_manager: Arc<Mutex<RelayManager>>,
4343
db: Arc<crate::payjoin::db::Database>,
4444
}
45+
46+
trait StatusText {
47+
fn status_text(&self) -> &'static str;
48+
}
49+
50+
impl StatusText for SendSession {
51+
fn status_text(&self) -> &'static str {
52+
match self {
53+
SendSession::WithReplyKey(_) | SendSession::PollingForProposal(_) => {
54+
"Waiting for proposal"
55+
}
56+
SendSession::Closed(session_outcome) => match session_outcome {
57+
SenderSessionOutcome::Failure => "Session failure",
58+
SenderSessionOutcome::Success(_) => "Session success",
59+
SenderSessionOutcome::Cancel => "Session cancelled",
60+
},
61+
}
62+
}
63+
}
64+
65+
impl StatusText for ReceiveSession {
66+
fn status_text(&self) -> &'static str {
67+
match self {
68+
ReceiveSession::Initialized(_) => "Waiting for original proposal",
69+
ReceiveSession::UncheckedOriginalPayload(_)
70+
| ReceiveSession::MaybeInputsOwned(_)
71+
| ReceiveSession::MaybeInputsSeen(_)
72+
| ReceiveSession::OutputsUnknown(_)
73+
| ReceiveSession::WantsOutputs(_)
74+
| ReceiveSession::WantsInputs(_)
75+
| ReceiveSession::WantsFeeRange(_)
76+
| ReceiveSession::ProvisionalProposal(_) => "Processing original proposal",
77+
ReceiveSession::PayjoinProposal(_) => "Payjoin proposal sent",
78+
ReceiveSession::HasReplyableError(_) => {
79+
"Session failure, waiting to post error response"
80+
}
81+
ReceiveSession::Monitor(_) => "Monitoring payjoin proposal",
82+
ReceiveSession::Closed(session_outcome) => match session_outcome {
83+
ReceiverSessionOutcome::Failure => "Session failure",
84+
ReceiverSessionOutcome::Success(_) => {
85+
"Session success, Payjoin proposal was broadcasted"
86+
}
87+
ReceiverSessionOutcome::Cancel => "Session cancelled",
88+
ReceiverSessionOutcome::FallbackBroadcasted => "Fallback broadcasted",
89+
},
90+
}
91+
}
92+
}
93+
94+
struct SessionHistoryRow {
95+
id: String,
96+
role: &'static str,
97+
status: String,
98+
completed_at: Option<String>,
4599
}
46100

47101
impl<'a> PayjoinManager<'a> {
@@ -230,14 +284,14 @@ impl<'a> PayjoinManager<'a> {
230284
SenderPersister::new(self.db.clone(), receiver_pubkey)?
231285
};
232286

233-
let sender = payjoin::send::v2::SenderBuilder::new(original_psbt.clone(), uri)
234-
.build_recommended(fee_rate)?
235-
.save(&persister)
236-
.map_err(|e| {
237-
Error::Generic(format!(
238-
"Failed to save the Payjoin v2 sender in the persister: {e}"
239-
))
240-
})?;
287+
let sender = payjoin::send::v2::SenderBuilder::new(original_psbt.clone(), uri)
288+
.build_recommended(fee_rate)?
289+
.save(&persister)
290+
.map_err(|e| {
291+
Error::Generic(format!(
292+
"Failed to save the Payjoin v2 sender in the persister: {e}"
293+
))
294+
})?;
241295

242296
(SendSession::WithReplyKey(sender), persister)
243297
};
@@ -450,7 +504,7 @@ impl<'a> PayjoinManager<'a> {
450504
) -> Result<(), Error> {
451505
let next_receiver_typestate = receiver
452506
.identify_receiver_outputs(&mut |output_script| {
453-
Ok(self.wallet.is_mine(output_script.to_owned()))
507+
Ok(self.wallet.is_mine(output_script.to_owned()))
454508
})
455509
.save(persister)?;
456510

@@ -582,7 +636,7 @@ impl<'a> PayjoinManager<'a> {
582636
.create_post_request(
583637
self.unwrap_relay_or_else_fetch(vec![], None::<&str>)
584638
.await?
585-
.as_str(),
639+
.as_str(),
586640
)
587641
.map_err(|e| {
588642
Error::Generic(format!("Error occurred when creating a post request for sending final Payjoin proposal: {e}"))
@@ -750,7 +804,7 @@ impl<'a> PayjoinManager<'a> {
750804
blockchain_client,
751805
relay.to_string(),
752806
)
753-
.await
807+
.await
754808
}
755809
SendSession::PollingForProposal(context) => {
756810
let relay = self
@@ -762,7 +816,7 @@ impl<'a> PayjoinManager<'a> {
762816
blockchain_client,
763817
relay.to_string(),
764818
)
765-
.await
819+
.await
766820
}
767821
SendSession::Closed(SenderSessionOutcome::Success(psbt)) => {
768822
self.process_payjoin_proposal(psbt, blockchain_client).await

0 commit comments

Comments
 (0)