@@ -92,6 +92,7 @@ use crate::ln::outbound_payment::{
9292};
9393use crate::ln::types::ChannelId;
9494use crate::offers::async_receive_offer_cache::AsyncReceiveOfferCache;
95+ use crate::offers::contacts::ContactSecrets;
9596use crate::offers::flow::{HeldHtlcReplyPath, InvreqResponseInstructions, OffersMessageFlow};
9697use crate::offers::invoice::{Bolt12Invoice, UnsignedBolt12Invoice};
9798use crate::offers::invoice_error::InvoiceError;
@@ -728,6 +729,34 @@ pub struct OptionalOfferPaymentParams {
728729 /// will ultimately fail once all pending paths have failed (generating an
729730 /// [`Event::PaymentFailed`]).
730731 pub retry_strategy: Retry,
732+ /// Contact secrets to include in the invoice request for BLIP-42 contact management.
733+ /// If provided, these secrets will be used to establish a contact relationship with the recipient.
734+ pub contact_secrects: Option<ContactSecrets>,
735+ /// A custom payer offer to include in the invoice request for BLIP-42 contact management.
736+ ///
737+ /// If provided, this offer will be included in the invoice request, allowing the recipient to
738+ /// contact you back. If `None`, **no payer offer will be included** in the invoice request.
739+ ///
740+ /// You can create custom offers using [`OffersMessageFlow::create_compact_offer_builder`]:
741+ /// - Pass `None` for no blinded path (smallest size, ~70 bytes)
742+ /// - Pass `Some(intro_node_id)` for a single blinded path (~200 bytes)
743+ ///
744+ /// # Example
745+ /// ```rust,ignore
746+ /// // Include a compact offer with a single blinded path
747+ /// let payer_offer = flow.create_compact_offer_builder(
748+ /// &entropy_source,
749+ /// Some(trusted_peer_pubkey)
750+ /// )?.build()?;
751+ ///
752+ /// let params = OptionalOfferPaymentParams {
753+ /// payer_offer: Some(payer_offer),
754+ /// ..Default::default()
755+ /// };
756+ /// ```
757+ ///
758+ /// [`OffersMessageFlow::create_compact_offer_builder`]: crate::offers::flow::OffersMessageFlow::create_compact_offer_builder
759+ pub payer_offer: Option<Offer>,
731760}
732761
733762impl Default for OptionalOfferPaymentParams {
@@ -739,6 +768,8 @@ impl Default for OptionalOfferPaymentParams {
739768 retry_strategy: Retry::Timeout(core::time::Duration::from_secs(2)),
740769 #[cfg(not(feature = "std"))]
741770 retry_strategy: Retry::Attempts(3),
771+ contact_secrects: None,
772+ payer_offer: None,
742773 }
743774 }
744775}
@@ -12944,6 +12975,8 @@ where
1294412975 payment_id,
1294512976 None,
1294612977 create_pending_payment_fn,
12978+ optional_params.contact_secrects,
12979+ optional_params.payer_offer,
1294712980 )
1294812981 }
1294912982
@@ -12973,6 +13006,8 @@ where
1297313006 payment_id,
1297413007 Some(offer.hrn),
1297513008 create_pending_payment_fn,
13009+ optional_params.contact_secrects,
13010+ optional_params.payer_offer,
1297613011 )
1297713012 }
1297813013
@@ -13015,6 +13050,8 @@ where
1301513050 payment_id,
1301613051 None,
1301713052 create_pending_payment_fn,
13053+ optional_params.contact_secrects,
13054+ optional_params.payer_offer,
1301813055 )
1301913056 }
1302013057
@@ -13023,6 +13060,7 @@ where
1302313060 &self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
1302413061 payer_note: Option<String>, payment_id: PaymentId,
1302513062 human_readable_name: Option<HumanReadableName>, create_pending_payment: CPP,
13063+ contacts: Option<ContactSecrets>, payer_offer: Option<Offer>,
1302613064 ) -> Result<(), Bolt12SemanticError> {
1302713065 let entropy = &*self.entropy_source;
1302813066 let nonce = Nonce::from_entropy_source(entropy);
@@ -13048,6 +13086,20 @@ where
1304813086 Some(hrn) => builder.sourced_from_human_readable_name(hrn),
1304913087 };
1305013088
13089+ let builder = if let Some(secrets) = contacts.as_ref() {
13090+ builder.contact_secrets(secrets.clone())
13091+ } else {
13092+ builder
13093+ };
13094+
13095+ // Add payer offer only if provided by the user.
13096+ // If the user explicitly wants to include an offer, they should provide it via payer_offer parameter.
13097+ let builder = if let Some(offer) = payer_offer {
13098+ builder.payer_offer(&offer)
13099+ } else {
13100+ builder
13101+ };
13102+
1305113103 let invoice_request = builder.build_and_sign()?;
1305213104 let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
1305313105
@@ -15649,7 +15701,7 @@ where
1564915701 self.pending_outbound_payments
1565015702 .received_offer(payment_id, Some(retryable_invoice_request))
1565115703 .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)
15652- });
15704+ }, None, None );
1565315705 if offer_pay_res.is_err() {
1565415706 // The offer we tried to pay is the canonical current offer for the name we
1565515707 // wanted to pay. If we can't pay it, there's no way to recover so fail the
0 commit comments