breez_sdk_spark/models/
payment_observer.rs1use std::sync::Arc;
2
3use spark_wallet::{TransferId, TransferObserverError};
4use thiserror::Error;
5
6#[derive(Debug, Clone)]
7#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
8pub struct ProvisionalPayment {
9 pub payment_id: String,
11 pub amount: u128,
13 pub details: ProvisionalPaymentDetails,
15}
16
17#[derive(Debug, Clone)]
18#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
19pub enum ProvisionalPaymentDetails {
20 Bitcoin {
21 withdrawal_address: String,
23 },
24 Lightning {
25 invoice: String,
27 },
28 Spark {
29 pay_request: String,
31 },
32 Token {
33 token_id: String,
35 pay_request: String,
37 },
38}
39
40#[derive(Debug, Clone)]
41#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
42pub struct PaymentIdUpdate {
43 pub provisional_payment_id: String,
45 pub final_payment_id: String,
47}
48
49#[derive(Debug, Error, Clone)]
50#[cfg_attr(feature = "uniffi", derive(uniffi::Error))]
51pub enum PaymentObserverError {
52 #[error("Service connectivity: {0}")]
53 ServiceConnectivity(String),
54 #[error("Generic: {0}")]
55 Generic(String),
56}
57
58impl From<PaymentObserverError> for TransferObserverError {
59 fn from(error: PaymentObserverError) -> Self {
60 match error {
61 PaymentObserverError::ServiceConnectivity(msg) => {
62 TransferObserverError::ServiceConnectivity(msg)
63 }
64 PaymentObserverError::Generic(msg) => TransferObserverError::Generic(msg),
65 }
66 }
67}
68
69#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
75#[macros::async_trait]
76pub trait PaymentObserver: Send + Sync {
77 async fn before_send(
79 &self,
80 payments: Vec<ProvisionalPayment>,
81 ) -> Result<(), PaymentObserverError>;
82 async fn after_send(&self, updates: Vec<PaymentIdUpdate>) -> Result<(), PaymentObserverError>;
85}
86
87pub(crate) struct SparkTransferObserver {
88 inner: Arc<dyn PaymentObserver>,
89}
90
91impl SparkTransferObserver {
92 pub fn new(inner: Arc<dyn PaymentObserver>) -> Self {
93 Self { inner }
94 }
95}
96
97#[macros::async_trait]
98impl spark_wallet::TransferObserver for SparkTransferObserver {
99 async fn before_coop_exit(
100 &self,
101 transfer_id: &TransferId,
102 withdrawal_address: &bitcoin::Address,
103 amount_sats: u64,
104 ) -> Result<(), TransferObserverError> {
105 Ok(self
106 .inner
107 .before_send(vec![ProvisionalPayment {
108 payment_id: transfer_id.to_string(),
109 amount: u128::from(amount_sats),
110 details: ProvisionalPaymentDetails::Bitcoin {
111 withdrawal_address: withdrawal_address.to_string(),
112 },
113 }])
114 .await?)
115 }
116 async fn before_send_lightning_payment(
117 &self,
118 transfer_id: &TransferId,
119 invoice: &str,
120 amount_sats: u64,
121 ) -> Result<(), TransferObserverError> {
122 Ok(self
123 .inner
124 .before_send(vec![ProvisionalPayment {
125 payment_id: transfer_id.to_string(),
126 amount: u128::from(amount_sats),
127 details: ProvisionalPaymentDetails::Lightning {
128 invoice: invoice.to_string(),
129 },
130 }])
131 .await?)
132 }
133
134 async fn before_send_token(
135 &self,
136 partial_tx_id: &str,
137 token_id: &str,
138 receiver_outputs: Vec<spark_wallet::ReceiverTokenOutput>,
139 ) -> Result<(), TransferObserverError> {
140 Ok(self
141 .inner
142 .before_send(
143 receiver_outputs
144 .into_iter()
145 .enumerate()
146 .map(|(index, output)| ProvisionalPayment {
147 payment_id: format!("{partial_tx_id}:{index}"),
148 amount: output.amount,
149 details: ProvisionalPaymentDetails::Token {
150 token_id: token_id.to_string(),
151 pay_request: output.pay_request,
152 },
153 })
154 .collect(),
155 )
156 .await?)
157 }
158
159 async fn before_send_transfer(
160 &self,
161 transfer_id: &TransferId,
162 receiver_address: &str,
163 amount_sats: u64,
164 ) -> Result<(), TransferObserverError> {
165 Ok(self
166 .inner
167 .before_send(vec![ProvisionalPayment {
168 payment_id: transfer_id.to_string(),
169 amount: u128::from(amount_sats),
170 details: ProvisionalPaymentDetails::Spark {
171 pay_request: receiver_address.to_string(),
172 },
173 }])
174 .await?)
175 }
176
177 async fn after_send_token(
178 &self,
179 partial_tx_id: &str,
180 final_tx_id: &str,
181 receiver_output_count: usize,
182 ) -> Result<(), TransferObserverError> {
183 let updates = (0..receiver_output_count)
187 .map(|i| PaymentIdUpdate {
188 provisional_payment_id: format!("{partial_tx_id}:{i}"),
189 final_payment_id: format!("{final_tx_id}:{i}"),
190 })
191 .collect();
192 Ok(self.inner.after_send(updates).await?)
193 }
194}