1use std::num::NonZeroU64;
13use std::sync::{Arc, RwLock};
14use std::time::{Duration, SystemTime, UNIX_EPOCH};
15
16use lightning::blinded_path::message::BlindedMessagePath;
17use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId, Retry};
18use lightning::offers::offer::{Amount, Offer as LdkOffer, Quantity};
19use lightning::offers::parse::Bolt12SemanticError;
20use lightning::routing::router::RouteParametersConfig;
21#[cfg(feature = "uniffi")]
22use lightning::util::ser::{Readable, Writeable};
23use lightning_types::string::UntrustedString;
24use rand::RngCore;
25
26use crate::config::{AsyncPaymentsRole, Config, LDK_PAYMENT_RETRY_TIMEOUT};
27use crate::error::Error;
28use crate::ffi::{maybe_deref, maybe_wrap};
29use crate::logger::{log_error, log_info, LdkLogger, Logger};
30use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus};
31use crate::types::{ChannelManager, PaymentStore};
32
33#[cfg(not(feature = "uniffi"))]
34type Bolt12Invoice = lightning::offers::invoice::Bolt12Invoice;
35#[cfg(feature = "uniffi")]
36type Bolt12Invoice = Arc<crate::ffi::Bolt12Invoice>;
37
38#[cfg(not(feature = "uniffi"))]
39type Offer = LdkOffer;
40#[cfg(feature = "uniffi")]
41type Offer = Arc<crate::ffi::Offer>;
42
43#[cfg(not(feature = "uniffi"))]
44type Refund = lightning::offers::refund::Refund;
45#[cfg(feature = "uniffi")]
46type Refund = Arc<crate::ffi::Refund>;
47
48pub struct Bolt12Payment {
55 channel_manager: Arc<ChannelManager>,
56 payment_store: Arc<PaymentStore>,
57 config: Arc<Config>,
58 is_running: Arc<RwLock<bool>>,
59 logger: Arc<Logger>,
60 async_payments_role: Option<AsyncPaymentsRole>,
61}
62
63impl Bolt12Payment {
64 pub(crate) fn new(
65 channel_manager: Arc<ChannelManager>, payment_store: Arc<PaymentStore>,
66 config: Arc<Config>, is_running: Arc<RwLock<bool>>, logger: Arc<Logger>,
67 async_payments_role: Option<AsyncPaymentsRole>,
68 ) -> Self {
69 Self { channel_manager, payment_store, config, is_running, logger, async_payments_role }
70 }
71
72 pub fn send(
82 &self, offer: &Offer, quantity: Option<u64>, payer_note: Option<String>,
83 route_parameters: Option<RouteParametersConfig>,
84 ) -> Result<PaymentId, Error> {
85 if !*self.is_running.read().unwrap() {
86 return Err(Error::NotRunning);
87 }
88
89 let offer = maybe_deref(offer);
90
91 let mut random_bytes = [0u8; 32];
92 rand::rng().fill_bytes(&mut random_bytes);
93 let payment_id = PaymentId(random_bytes);
94 let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
95 let route_parameters =
96 route_parameters.or(self.config.route_parameters).unwrap_or_default();
97
98 let offer_amount_msat = match offer.amount() {
99 Some(Amount::Bitcoin { amount_msats }) => amount_msats,
100 Some(_) => {
101 log_error!(self.logger, "Failed to send payment as the provided offer was denominated in an unsupported currency.");
102 return Err(Error::UnsupportedCurrency);
103 },
104 None => {
105 log_error!(self.logger, "Failed to send payment due to the given offer being \"zero-amount\". Please use send_using_amount instead.");
106 return Err(Error::InvalidOffer);
107 },
108 };
109
110 let params = OptionalOfferPaymentParams {
111 payer_note: payer_note.clone(),
112 retry_strategy,
113 route_params_config: route_parameters,
114 };
115 let res = if let Some(quantity) = quantity {
116 self.channel_manager
117 .pay_for_offer_with_quantity(&offer, None, payment_id, params, quantity)
118 } else {
119 self.channel_manager.pay_for_offer(&offer, None, payment_id, params)
120 };
121
122 match res {
123 Ok(()) => {
124 let payee_pubkey = offer.issuer_signing_pubkey();
125 log_info!(
126 self.logger,
127 "Initiated sending {}msat to {:?}",
128 offer_amount_msat,
129 payee_pubkey
130 );
131
132 let kind = PaymentKind::Bolt12Offer {
133 hash: None,
134 preimage: None,
135 secret: None,
136 offer_id: offer.id(),
137 payer_note: payer_note.map(UntrustedString),
138 quantity,
139 };
140 let payment = PaymentDetails::new(
141 payment_id,
142 kind,
143 Some(offer_amount_msat),
144 None,
145 PaymentDirection::Outbound,
146 PaymentStatus::Pending,
147 );
148 self.payment_store.insert(payment)?;
149
150 Ok(payment_id)
151 },
152 Err(e) => {
153 log_error!(self.logger, "Failed to send invoice request: {:?}", e);
154 match e {
155 Bolt12SemanticError::DuplicatePaymentId => Err(Error::DuplicatePayment),
156 _ => {
157 let kind = PaymentKind::Bolt12Offer {
158 hash: None,
159 preimage: None,
160 secret: None,
161 offer_id: offer.id(),
162 payer_note: payer_note.map(UntrustedString),
163 quantity,
164 };
165 let payment = PaymentDetails::new(
166 payment_id,
167 kind,
168 Some(offer_amount_msat),
169 None,
170 PaymentDirection::Outbound,
171 PaymentStatus::Failed,
172 );
173 self.payment_store.insert(payment)?;
174 Err(Error::InvoiceRequestCreationFailed)
175 },
176 }
177 },
178 }
179 }
180
181 pub fn send_using_amount(
194 &self, offer: &Offer, amount_msat: u64, quantity: Option<u64>, payer_note: Option<String>,
195 route_parameters: Option<RouteParametersConfig>,
196 ) -> Result<PaymentId, Error> {
197 if !*self.is_running.read().unwrap() {
198 return Err(Error::NotRunning);
199 }
200
201 let offer = maybe_deref(offer);
202
203 let mut random_bytes = [0u8; 32];
204 rand::rng().fill_bytes(&mut random_bytes);
205 let payment_id = PaymentId(random_bytes);
206 let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
207 let route_parameters =
208 route_parameters.or(self.config.route_parameters).unwrap_or_default();
209
210 let offer_amount_msat = match offer.amount() {
211 Some(Amount::Bitcoin { amount_msats }) => amount_msats,
212 Some(_) => {
213 log_error!(self.logger, "Failed to send payment as the provided offer was denominated in an unsupported currency.");
214 return Err(Error::UnsupportedCurrency);
215 },
216 None => amount_msat,
217 };
218
219 if amount_msat < offer_amount_msat {
220 log_error!(
221 self.logger,
222 "Failed to pay as the given amount needs to be at least the offer amount: required {}msat, gave {}msat.", offer_amount_msat, amount_msat);
223 return Err(Error::InvalidAmount);
224 }
225
226 let params = OptionalOfferPaymentParams {
227 payer_note: payer_note.clone(),
228 retry_strategy,
229 route_params_config: route_parameters,
230 };
231 let res = if let Some(quantity) = quantity {
232 self.channel_manager.pay_for_offer_with_quantity(
233 &offer,
234 Some(amount_msat),
235 payment_id,
236 params,
237 quantity,
238 )
239 } else {
240 self.channel_manager.pay_for_offer(&offer, Some(amount_msat), payment_id, params)
241 };
242
243 match res {
244 Ok(()) => {
245 let payee_pubkey = offer.issuer_signing_pubkey();
246 log_info!(
247 self.logger,
248 "Initiated sending {}msat to {:?}",
249 amount_msat,
250 payee_pubkey
251 );
252
253 let kind = PaymentKind::Bolt12Offer {
254 hash: None,
255 preimage: None,
256 secret: None,
257 offer_id: offer.id(),
258 payer_note: payer_note.map(UntrustedString),
259 quantity,
260 };
261 let payment = PaymentDetails::new(
262 payment_id,
263 kind,
264 Some(amount_msat),
265 None,
266 PaymentDirection::Outbound,
267 PaymentStatus::Pending,
268 );
269 self.payment_store.insert(payment)?;
270
271 Ok(payment_id)
272 },
273 Err(e) => {
274 log_error!(self.logger, "Failed to send payment: {:?}", e);
275 match e {
276 Bolt12SemanticError::DuplicatePaymentId => Err(Error::DuplicatePayment),
277 _ => {
278 let kind = PaymentKind::Bolt12Offer {
279 hash: None,
280 preimage: None,
281 secret: None,
282 offer_id: offer.id(),
283 payer_note: payer_note.map(UntrustedString),
284 quantity,
285 };
286 let payment = PaymentDetails::new(
287 payment_id,
288 kind,
289 Some(amount_msat),
290 None,
291 PaymentDirection::Outbound,
292 PaymentStatus::Failed,
293 );
294 self.payment_store.insert(payment)?;
295 Err(Error::PaymentSendingFailed)
296 },
297 }
298 },
299 }
300 }
301
302 pub(crate) fn receive_inner(
303 &self, amount_msat: u64, description: &str, expiry_secs: Option<u32>, quantity: Option<u64>,
304 ) -> Result<LdkOffer, Error> {
305 let mut offer_builder = self.channel_manager.create_offer_builder().map_err(|e| {
306 log_error!(self.logger, "Failed to create offer builder: {:?}", e);
307 Error::OfferCreationFailed
308 })?;
309
310 if let Some(expiry_secs) = expiry_secs {
311 let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64))
312 .duration_since(UNIX_EPOCH)
313 .unwrap();
314 offer_builder = offer_builder.absolute_expiry(absolute_expiry);
315 }
316
317 let mut offer =
318 offer_builder.amount_msats(amount_msat).description(description.to_string());
319
320 if let Some(qty) = quantity {
321 if qty == 0 {
322 log_error!(self.logger, "Failed to create offer: quantity can't be zero.");
323 return Err(Error::InvalidQuantity);
324 } else {
325 offer = offer.supported_quantity(Quantity::Bounded(NonZeroU64::new(qty).unwrap()))
326 };
327 };
328
329 let finalized_offer = offer.build().map_err(|e| {
330 log_error!(self.logger, "Failed to create offer: {:?}", e);
331 Error::OfferCreationFailed
332 })?;
333
334 Ok(finalized_offer)
335 }
336
337 pub fn receive(
340 &self, amount_msat: u64, description: &str, expiry_secs: Option<u32>, quantity: Option<u64>,
341 ) -> Result<Offer, Error> {
342 let offer = self.receive_inner(amount_msat, description, expiry_secs, quantity)?;
343 Ok(maybe_wrap(offer))
344 }
345
346 pub fn receive_variable_amount(
349 &self, description: &str, expiry_secs: Option<u32>,
350 ) -> Result<Offer, Error> {
351 let mut offer_builder = self.channel_manager.create_offer_builder().map_err(|e| {
352 log_error!(self.logger, "Failed to create offer builder: {:?}", e);
353 Error::OfferCreationFailed
354 })?;
355
356 if let Some(expiry_secs) = expiry_secs {
357 let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64))
358 .duration_since(UNIX_EPOCH)
359 .unwrap();
360 offer_builder = offer_builder.absolute_expiry(absolute_expiry);
361 }
362
363 let offer = offer_builder.description(description.to_string()).build().map_err(|e| {
364 log_error!(self.logger, "Failed to create offer: {:?}", e);
365 Error::OfferCreationFailed
366 })?;
367
368 Ok(maybe_wrap(offer))
369 }
370
371 pub fn request_refund_payment(&self, refund: &Refund) -> Result<Bolt12Invoice, Error> {
379 if !*self.is_running.read().unwrap() {
380 return Err(Error::NotRunning);
381 }
382
383 let refund = maybe_deref(refund);
384 let invoice = self.channel_manager.request_refund_payment(&refund).map_err(|e| {
385 log_error!(self.logger, "Failed to request refund payment: {:?}", e);
386 Error::InvoiceRequestCreationFailed
387 })?;
388
389 let payment_hash = invoice.payment_hash();
390 let payment_id = PaymentId(payment_hash.0);
391
392 let kind = PaymentKind::Bolt12Refund {
393 hash: Some(payment_hash),
394 preimage: None,
395 secret: None,
396 payer_note: refund.payer_note().map(|note| UntrustedString(note.0.to_string())),
397 quantity: refund.quantity(),
398 };
399
400 let payment = PaymentDetails::new(
401 payment_id,
402 kind,
403 Some(refund.amount_msats()),
404 None,
405 PaymentDirection::Inbound,
406 PaymentStatus::Pending,
407 );
408
409 self.payment_store.insert(payment)?;
410
411 Ok(maybe_wrap(invoice))
412 }
413
414 pub fn initiate_refund(
421 &self, amount_msat: u64, expiry_secs: u32, quantity: Option<u64>,
422 payer_note: Option<String>, route_parameters: Option<RouteParametersConfig>,
423 ) -> Result<Refund, Error> {
424 let mut random_bytes = [0u8; 32];
425 rand::rng().fill_bytes(&mut random_bytes);
426 let payment_id = PaymentId(random_bytes);
427
428 let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64))
429 .duration_since(UNIX_EPOCH)
430 .unwrap();
431 let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT);
432 let route_parameters =
433 route_parameters.or(self.config.route_parameters).unwrap_or_default();
434
435 let mut refund_builder = self
436 .channel_manager
437 .create_refund_builder(
438 amount_msat,
439 absolute_expiry,
440 payment_id,
441 retry_strategy,
442 route_parameters,
443 )
444 .map_err(|e| {
445 log_error!(self.logger, "Failed to create refund builder: {:?}", e);
446 Error::RefundCreationFailed
447 })?;
448
449 if let Some(qty) = quantity {
450 refund_builder = refund_builder.quantity(qty);
451 }
452
453 if let Some(note) = payer_note.clone() {
454 refund_builder = refund_builder.payer_note(note);
455 }
456
457 let refund = refund_builder.build().map_err(|e| {
458 log_error!(self.logger, "Failed to create refund: {:?}", e);
459 Error::RefundCreationFailed
460 })?;
461
462 log_info!(self.logger, "Offering refund of {}msat", amount_msat);
463
464 let kind = PaymentKind::Bolt12Refund {
465 hash: None,
466 preimage: None,
467 secret: None,
468 payer_note: payer_note.map(|note| UntrustedString(note)),
469 quantity,
470 };
471 let payment = PaymentDetails::new(
472 payment_id,
473 kind,
474 Some(amount_msat),
475 None,
476 PaymentDirection::Outbound,
477 PaymentStatus::Pending,
478 );
479
480 self.payment_store.insert(payment)?;
481
482 Ok(maybe_wrap(refund))
483 }
484
485 pub fn receive_async(&self) -> Result<Offer, Error> {
497 self.channel_manager
498 .get_async_receive_offer()
499 .map(maybe_wrap)
500 .or(Err(Error::OfferCreationFailed))
501 }
502
503 #[cfg(not(feature = "uniffi"))]
511 pub fn set_paths_to_static_invoice_server(
512 &self, paths: Vec<BlindedMessagePath>,
513 ) -> Result<(), Error> {
514 self.channel_manager
515 .set_paths_to_static_invoice_server(paths)
516 .or(Err(Error::InvalidBlindedPaths))
517 }
518
519 #[cfg(feature = "uniffi")]
527 pub fn set_paths_to_static_invoice_server(&self, paths: Vec<u8>) -> Result<(), Error> {
528 let decoded_paths = <Vec<BlindedMessagePath> as Readable>::read(&mut &paths[..])
529 .or(Err(Error::InvalidBlindedPaths))?;
530
531 self.channel_manager
532 .set_paths_to_static_invoice_server(decoded_paths)
533 .or(Err(Error::InvalidBlindedPaths))
534 }
535
536 #[cfg(not(feature = "uniffi"))]
544 pub fn blinded_paths_for_async_recipient(
545 &self, recipient_id: Vec<u8>,
546 ) -> Result<Vec<BlindedMessagePath>, Error> {
547 self.blinded_paths_for_async_recipient_internal(recipient_id)
548 }
549
550 #[cfg(feature = "uniffi")]
558 pub fn blinded_paths_for_async_recipient(
559 &self, recipient_id: Vec<u8>,
560 ) -> Result<Vec<u8>, Error> {
561 let paths = self.blinded_paths_for_async_recipient_internal(recipient_id)?;
562
563 let mut bytes = Vec::new();
564 paths.write(&mut bytes).or(Err(Error::InvalidBlindedPaths))?;
565 Ok(bytes)
566 }
567
568 fn blinded_paths_for_async_recipient_internal(
569 &self, recipient_id: Vec<u8>,
570 ) -> Result<Vec<BlindedMessagePath>, Error> {
571 match self.async_payments_role {
572 Some(AsyncPaymentsRole::Server) => {},
573 _ => {
574 return Err(Error::AsyncPaymentServicesDisabled);
575 },
576 }
577
578 self.channel_manager
579 .blinded_paths_for_async_recipient(recipient_id, None)
580 .or(Err(Error::InvalidBlindedPaths))
581 }
582}