@@ -71,6 +71,8 @@ use crate::offers::offer::{Offer, OfferBuilder};
7171use crate::offers::parse::Bolt12SemanticError;
7272use crate::offers::refund::{Refund, RefundBuilder};
7373use crate::offers::signer;
74+ #[cfg(async_payments)]
75+ use crate::offers::static_invoice::StaticInvoice;
7476use crate::onion_message::async_payments::{AsyncPaymentsMessage, HeldHtlcAvailable, ReleaseHeldHtlc, AsyncPaymentsMessageHandler};
7577use crate::onion_message::messenger::{Destination, MessageRouter, Responder, ResponseInstruction, MessageSendInstructions};
7678use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
@@ -4284,6 +4286,61 @@ where
42844286 )
42854287 }
42864288
4289+ #[cfg(async_payments)]
4290+ fn initiate_async_payment(
4291+ &self, invoice: &StaticInvoice, payment_id: PaymentId
4292+ ) -> Result<(), Bolt12PaymentError> {
4293+ let mut res = Ok(());
4294+ PersistenceNotifierGuard::optionally_notify(self, || {
4295+ let outbound_pmts_res = self.pending_outbound_payments.static_invoice_received(
4296+ invoice, payment_id, &*self.entropy_source, &self.pending_events
4297+ );
4298+ let payment_release_secret = match outbound_pmts_res {
4299+ Ok(secret) => secret,
4300+ Err(Bolt12PaymentError::UnexpectedInvoice) | Err(Bolt12PaymentError::DuplicateInvoice) => {
4301+ res = outbound_pmts_res.map(|_| ());
4302+ return NotifyOption::SkipPersistNoEvents
4303+ },
4304+ Err(e) => {
4305+ res = Err(e);
4306+ return NotifyOption::DoPersist
4307+ }
4308+ };
4309+
4310+ let reply_paths = match self.create_blinded_paths(
4311+ MessageContext::AsyncPayments(AsyncPaymentsContext::OutboundPayment { payment_id })
4312+ ) {
4313+ Ok(paths) => paths,
4314+ Err(()) => {
4315+ self.abandon_payment_with_reason(payment_id, PaymentFailureReason::RouteNotFound);
4316+ res = Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::RouteNotFound));
4317+ return NotifyOption::DoPersist
4318+ }
4319+ };
4320+
4321+ let mut pending_async_payments_messages = self.pending_async_payments_messages.lock().unwrap();
4322+ const HTLC_AVAILABLE_LIMIT: usize = 10;
4323+ reply_paths
4324+ .iter()
4325+ .flat_map(|reply_path| invoice.message_paths().iter().map(move |invoice_path| (invoice_path, reply_path)))
4326+ .take(HTLC_AVAILABLE_LIMIT)
4327+ .for_each(|(invoice_path, reply_path)| {
4328+ let instructions = MessageSendInstructions::WithSpecifiedReplyPath {
4329+ destination: Destination::BlindedPath(invoice_path.clone()),
4330+ reply_path: reply_path.clone(),
4331+ };
4332+ let message = AsyncPaymentsMessage::HeldHtlcAvailable(
4333+ HeldHtlcAvailable { payment_release_secret }
4334+ );
4335+ pending_async_payments_messages.push((message, instructions));
4336+ });
4337+
4338+ NotifyOption::DoPersist
4339+ });
4340+
4341+ res
4342+ }
4343+
42874344 /// Signals that no further attempts for the given payment should occur. Useful if you have a
42884345 /// pending outbound payment with retries remaining, but wish to stop retrying the payment before
42894346 /// retries are exhausted.
@@ -10948,14 +11005,34 @@ where
1094811005 }
1094911006 },
1095011007 #[cfg(async_payments)]
10951- OffersMessage::StaticInvoice(_invoice) => {
11008+ OffersMessage::StaticInvoice(invoice) => {
11009+ let payment_id = match context {
11010+ Some(OffersContext::OutboundPayment { payment_id, nonce: _, hmac: _ }) => payment_id,
11011+ _ => return None
11012+ };
11013+ // TODO: DRY this with the above regular invoice error handling
11014+ let error = match self.initiate_async_payment(&invoice, payment_id) {
11015+ Err(Bolt12PaymentError::UnknownRequiredFeatures) => {
11016+ log_trace!(
11017+ self.logger, "Invoice requires unknown features: {:?}",
11018+ invoice.invoice_features()
11019+ );
11020+ InvoiceError::from(Bolt12SemanticError::UnknownRequiredFeatures)
11021+ },
11022+ Err(Bolt12PaymentError::SendingFailed(e)) => {
11023+ log_trace!(self.logger, "Failed paying invoice: {:?}", e);
11024+ InvoiceError::from_string(format!("{:?}", e))
11025+ },
11026+ Err(Bolt12PaymentError::UnexpectedInvoice)
11027+ | Err(Bolt12PaymentError::DuplicateInvoice)
11028+ | Ok(()) => return None,
11029+ };
1095211030 match responder {
10953- Some(responder) => {
10954- return Some((OffersMessage::InvoiceError(
10955- InvoiceError::from_string("Static invoices not yet supported".to_string())
10956- ), responder.respond()));
11031+ Some(responder) => Some((OffersMessage::InvoiceError(error), responder.respond())),
11032+ None => {
11033+ log_trace!(self.logger, "No reply path to send error: {:?}", error);
11034+ None
1095711035 },
10958- None => return None,
1095911036 }
1096011037 },
1096111038 OffersMessage::InvoiceError(invoice_error) => {
0 commit comments