@@ -56,10 +56,10 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
56
56
use crate::ln::outbound_payment;
57
57
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
58
58
use crate::ln::wire::Encode;
59
- use crate::offers::offer::{DerivedMetadata, OfferBuilder};
59
+ use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
60
60
use crate::offers::parse::Bolt12SemanticError;
61
61
use crate::offers::refund::RefundBuilder;
62
- use crate::onion_message::{OffersMessage, PendingOnionMessage};
62
+ use crate::onion_message::{Destination, OffersMessage, PendingOnionMessage};
63
63
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
64
64
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
65
65
use crate::util::wakers::{Future, Notifier};
@@ -7359,6 +7359,92 @@ where
7359
7359
Ok(builder)
7360
7360
}
7361
7361
7362
+ /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and
7363
+ /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual
7364
+ /// [`Bolt12Invoice`] once it is received.
7365
+ ///
7366
+ /// Uses [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized by
7367
+ /// the [`ChannelManager`] when handling a [`Bolt12Invoice`] message in response to the request.
7368
+ /// The optional parameters are used in the builder, if `Some`:
7369
+ /// - `quantity` for [`InvoiceRequest::quantity`] which must be set if
7370
+ /// [`Offer::expects_quantity`] is `true`.
7371
+ /// - `amount_msats` if overpaying what is required for the given `quantity` is desired, and
7372
+ /// - `payer_note` for [`InvoiceRequest::payer_note`].
7373
+ ///
7374
+ /// The provided `payment_id` is used to ensure that only one invoice is paid for the request
7375
+ /// when received. See [Avoiding Duplicate Payments] for other requirements once the payment has
7376
+ /// been sent. To revoke the request, use [`ChannelManager::abandon_payment`] prior to receiving
7377
+ /// the invoice.
7378
+ ///
7379
+ /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link.
7380
+ ///
7381
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
7382
+ /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
7383
+ /// [`InvoiceRequest::payer_note`]: crate::offers::invoice_request::InvoiceRequest::payer_note
7384
+ /// [`InvoiceRequestBuilder`]: crate::offers::invoice_request::InvoiceRequestBuilder
7385
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
7386
+ /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
7387
+ pub fn pay_for_offer(
7388
+ &self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
7389
+ payer_note: Option<String>, payment_id: PaymentId, retry_strategy: Retry,
7390
+ max_total_routing_fee_msat: Option<u64>
7391
+ ) -> Result<(), Bolt12SemanticError> {
7392
+ let expanded_key = &self.inbound_payment_key;
7393
+ let entropy = &*self.entropy_source;
7394
+ let secp_ctx = &self.secp_ctx;
7395
+
7396
+ let builder = offer
7397
+ .request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)?
7398
+ .chain_hash(self.chain_hash)?;
7399
+ let builder = match quantity {
7400
+ None => builder,
7401
+ Some(quantity) => builder.quantity(quantity)?,
7402
+ };
7403
+ let builder = match amount_msats {
7404
+ None => builder,
7405
+ Some(amount_msats) => builder.amount_msats(amount_msats)?,
7406
+ };
7407
+ let builder = match payer_note {
7408
+ None => builder,
7409
+ Some(payer_note) => builder.payer_note(payer_note),
7410
+ };
7411
+
7412
+ let invoice_request = builder.build_and_sign()?;
7413
+ let reply_path = self.create_one_hop_blinded_path();
7414
+
7415
+ let expiration = StaleExpiration::TimerTicks(1);
7416
+ self.pending_outbound_payments
7417
+ .add_new_awaiting_invoice(
7418
+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat
7419
+ )
7420
+ .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
7421
+
7422
+ let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
7423
+ if offer.paths().is_empty() {
7424
+ let message = PendingOnionMessage {
7425
+ contents: OffersMessage::InvoiceRequest(invoice_request),
7426
+ destination: Destination::Node(offer.signing_pubkey()),
7427
+ reply_path: Some(reply_path),
7428
+ };
7429
+ pending_offers_messages.push(message);
7430
+ } else {
7431
+ // Send as many invoice requests as there are paths in the offer (with an upper bound).
7432
+ // Using only one path could result in a failure if the path no longer exists. But only
7433
+ // one invoice for a given payment id will be paid, even if more than one is received.
7434
+ const REQUEST_LIMIT: usize = 10;
7435
+ for path in offer.paths().into_iter().take(REQUEST_LIMIT) {
7436
+ let message = PendingOnionMessage {
7437
+ contents: OffersMessage::InvoiceRequest(invoice_request.clone()),
7438
+ destination: Destination::BlindedPath(path.clone()),
7439
+ reply_path: Some(reply_path.clone()),
7440
+ };
7441
+ pending_offers_messages.push(message);
7442
+ }
7443
+ }
7444
+
7445
+ Ok(())
7446
+ }
7447
+
7362
7448
/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
7363
7449
/// to pay us.
7364
7450
///
0 commit comments