@@ -71,6 +71,8 @@ use crate::offers::offer::{Offer, OfferBuilder};
71
71
use crate::offers::parse::Bolt12SemanticError;
72
72
use crate::offers::refund::{Refund, RefundBuilder};
73
73
use crate::offers::signer;
74
+ #[cfg(async_payments)]
75
+ use crate::offers::static_invoice::StaticInvoice;
74
76
use crate::onion_message::async_payments::{AsyncPaymentsMessage, HeldHtlcAvailable, ReleaseHeldHtlc, AsyncPaymentsMessageHandler};
75
77
use crate::onion_message::messenger::{Destination, MessageRouter, Responder, ResponseInstruction, MessageSendInstructions};
76
78
use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
@@ -4318,6 +4320,61 @@ where
4318
4320
)
4319
4321
}
4320
4322
4323
+ #[cfg(async_payments)]
4324
+ fn initiate_async_payment(
4325
+ &self, invoice: &StaticInvoice, payment_id: PaymentId
4326
+ ) -> Result<(), Bolt12PaymentError> {
4327
+ let mut res = Ok(());
4328
+ PersistenceNotifierGuard::optionally_notify(self, || {
4329
+ let outbound_pmts_res = self.pending_outbound_payments.static_invoice_received(
4330
+ invoice, payment_id, &*self.entropy_source, &self.pending_events
4331
+ );
4332
+ let payment_release_secret = match outbound_pmts_res {
4333
+ Ok(secret) => secret,
4334
+ Err(Bolt12PaymentError::UnexpectedInvoice) | Err(Bolt12PaymentError::DuplicateInvoice) => {
4335
+ res = outbound_pmts_res.map(|_| ());
4336
+ return NotifyOption::SkipPersistNoEvents
4337
+ },
4338
+ Err(e) => {
4339
+ res = Err(e);
4340
+ return NotifyOption::DoPersist
4341
+ }
4342
+ };
4343
+
4344
+ let reply_paths = match self.create_blinded_paths(
4345
+ MessageContext::AsyncPayments(AsyncPaymentsContext::OutboundPayment { payment_id })
4346
+ ) {
4347
+ Ok(paths) => paths,
4348
+ Err(()) => {
4349
+ self.abandon_payment_with_reason(payment_id, PaymentFailureReason::RouteNotFound);
4350
+ res = Err(Bolt12PaymentError::SendingFailed(RetryableSendFailure::RouteNotFound));
4351
+ return NotifyOption::DoPersist
4352
+ }
4353
+ };
4354
+
4355
+ let mut pending_async_payments_messages = self.pending_async_payments_messages.lock().unwrap();
4356
+ const HTLC_AVAILABLE_LIMIT: usize = 10;
4357
+ reply_paths
4358
+ .iter()
4359
+ .flat_map(|reply_path| invoice.message_paths().iter().map(move |invoice_path| (invoice_path, reply_path)))
4360
+ .take(HTLC_AVAILABLE_LIMIT)
4361
+ .for_each(|(invoice_path, reply_path)| {
4362
+ let instructions = MessageSendInstructions::WithSpecifiedReplyPath {
4363
+ destination: Destination::BlindedPath(invoice_path.clone()),
4364
+ reply_path: reply_path.clone(),
4365
+ };
4366
+ let message = AsyncPaymentsMessage::HeldHtlcAvailable(
4367
+ HeldHtlcAvailable { payment_release_secret }
4368
+ );
4369
+ pending_async_payments_messages.push((message, instructions));
4370
+ });
4371
+
4372
+ NotifyOption::DoPersist
4373
+ });
4374
+
4375
+ res
4376
+ }
4377
+
4321
4378
/// Signals that no further attempts for the given payment should occur. Useful if you have a
4322
4379
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
4323
4380
/// retries are exhausted.
@@ -11040,14 +11097,39 @@ where
11040
11097
}
11041
11098
},
11042
11099
#[cfg(async_payments)]
11043
- OffersMessage::StaticInvoice(_invoice) => {
11100
+ OffersMessage::StaticInvoice(invoice) => {
11101
+ let payment_id = match context {
11102
+ Some(OffersContext::OutboundPayment { payment_id, nonce, hmac: Some(hmac) }) => {
11103
+ if payment_id.verify(hmac, nonce, expanded_key).is_err() {
11104
+ return None
11105
+ }
11106
+ payment_id
11107
+ },
11108
+ _ => return None
11109
+ };
11110
+ // TODO: DRY this with the above regular invoice error handling
11111
+ let error = match self.initiate_async_payment(&invoice, payment_id) {
11112
+ Err(Bolt12PaymentError::UnknownRequiredFeatures) => {
11113
+ log_trace!(
11114
+ self.logger, "Invoice requires unknown features: {:?}",
11115
+ invoice.invoice_features()
11116
+ );
11117
+ InvoiceError::from(Bolt12SemanticError::UnknownRequiredFeatures)
11118
+ },
11119
+ Err(Bolt12PaymentError::SendingFailed(e)) => {
11120
+ log_trace!(self.logger, "Failed paying invoice: {:?}", e);
11121
+ InvoiceError::from_string(format!("{:?}", e))
11122
+ },
11123
+ Err(Bolt12PaymentError::UnexpectedInvoice)
11124
+ | Err(Bolt12PaymentError::DuplicateInvoice)
11125
+ | Ok(()) => return None,
11126
+ };
11044
11127
match responder {
11045
- Some(responder) => {
11046
- return Some((OffersMessage::InvoiceError(
11047
- InvoiceError::from_string("Static invoices not yet supported".to_string())
11048
- ), responder.respond()));
11128
+ Some(responder) => Some((OffersMessage::InvoiceError(error), responder.respond())),
11129
+ None => {
11130
+ log_trace!(self.logger, "No reply path to send error: {:?}", error);
11131
+ None
11049
11132
},
11050
- None => return None,
11051
11133
}
11052
11134
},
11053
11135
OffersMessage::InvoiceError(invoice_error) => {
0 commit comments