@@ -677,28 +677,67 @@ where
677677 } )
678678 }
679679
680+ fn create_refund_builder_intern < ES : Deref , PF , I > (
681+ & self , entropy_source : ES , make_paths : PF , amount_msats : u64 , absolute_expiry : Duration ,
682+ payment_id : PaymentId ,
683+ ) -> Result < RefundBuilder < secp256k1:: All > , Bolt12SemanticError >
684+ where
685+ ES :: Target : EntropySource ,
686+ PF : FnOnce (
687+ PublicKey ,
688+ MessageContext ,
689+ & secp256k1:: Secp256k1 < secp256k1:: All > ,
690+ ) -> Result < I , Bolt12SemanticError > ,
691+ I : IntoIterator < Item = BlindedMessagePath > ,
692+ {
693+ let node_id = self . get_our_node_id ( ) ;
694+ let expanded_key = & self . inbound_payment_key ;
695+ let entropy = & * entropy_source;
696+ let secp_ctx = & self . secp_ctx ;
697+
698+ let nonce = Nonce :: from_entropy_source ( entropy) ;
699+ let context = MessageContext :: Offers ( OffersContext :: OutboundPayment { payment_id, nonce } ) ;
700+
701+ // Create the base builder with common properties
702+ let mut builder = RefundBuilder :: deriving_signing_pubkey (
703+ node_id,
704+ expanded_key,
705+ nonce,
706+ secp_ctx,
707+ amount_msats,
708+ payment_id,
709+ ) ?
710+ . chain_hash ( self . chain_hash )
711+ . absolute_expiry ( absolute_expiry) ;
712+
713+ for path in make_paths ( node_id, context, secp_ctx) ? {
714+ builder = builder. path ( path) ;
715+ }
716+
717+ Ok ( builder. into ( ) )
718+ }
719+
680720 /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
681721 /// [`OffersMessageFlow`], and any corresponding [`Bolt12Invoice`] received for the refund
682722 /// can be verified using [`Self::verify_bolt12_invoice`].
683723 ///
724+ /// # Privacy
725+ ///
726+ /// Uses the [`OffersMessageFlow`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`]
727+ /// for the offer. See those docs for privacy implications.
728+ ///
684729 /// The builder will have the provided expiration set. Any changes to the expiration on the
685730 /// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
686731 /// block time minus two hours is used for the current time when determining if the refund has
687732 /// expired.
688733 ///
689- /// To refund can be revoked by the user prior to receiving the invoice.
734+ /// The refund can be revoked by the user prior to receiving the invoice.
690735 /// If abandoned, or if an invoice is not received before expiration, the payment will fail
691736 /// with an [`Event::PaymentFailed`].
692737 ///
693738 /// If `max_total_routing_fee_msat` is not specified, the default from
694739 /// [`RouteParameters::from_payment_params_and_value`] is applied.
695740 ///
696- /// # Privacy
697- ///
698- /// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
699- /// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
700- /// privacy implications.
701- ///
702741 /// Also uses a derived payer id in the refund for payer privacy.
703742 ///
704743 /// # Errors
@@ -717,32 +756,63 @@ where
717756 where
718757 ES :: Target : EntropySource ,
719758 {
720- let node_id = self . get_our_node_id ( ) ;
721- let expanded_key = & self . inbound_payment_key ;
722- let entropy = & * entropy_source;
723- let secp_ctx = & self . secp_ctx ;
724-
725- let nonce = Nonce :: from_entropy_source ( entropy) ;
726- let context = OffersContext :: OutboundPayment { payment_id, nonce } ;
727-
728- let path = self
729- . create_blinded_paths_using_absolute_expiry ( context, Some ( absolute_expiry) , peers)
730- . and_then ( |paths| paths. into_iter ( ) . next ( ) . ok_or ( ( ) ) )
731- . map_err ( |_| Bolt12SemanticError :: MissingPaths ) ?;
732-
733- let builder = RefundBuilder :: deriving_signing_pubkey (
734- node_id,
735- expanded_key,
736- nonce,
737- secp_ctx,
759+ self . create_refund_builder_intern (
760+ & * entropy_source,
761+ |_, context, _| {
762+ self . create_blinded_paths ( peers, context)
763+ . map ( |paths| paths. into_iter ( ) . take ( 1 ) )
764+ . map_err ( |_| Bolt12SemanticError :: MissingPaths )
765+ } ,
738766 amount_msats,
767+ absolute_expiry,
739768 payment_id,
740- ) ?
741- . chain_hash ( self . chain_hash )
742- . absolute_expiry ( absolute_expiry)
743- . path ( path) ;
769+ )
770+ }
744771
745- Ok ( builder)
772+ /// Same as [`Self::create_refund_builder`] but allows specifying a custom [`MessageRouter`]
773+ /// instead of using the one provided via the [`OffersMessageFlow`] parameterization.
774+ ///
775+ /// This gives users full control over how the [`BlindedMessagePath`] is constructed,
776+ /// including the option to omit it entirely.
777+ ///
778+ /// See [`Self::create_refund_builder`] for:
779+ /// - how the resulting [`Refund`] is recognized by [`OffersMessageFlow`] and verified via [`Self::verify_bolt12_invoice`],
780+ /// - refund expiration handling,
781+ /// - rules around revocation and [`Event::PaymentFailed`] behavior,
782+ /// - and defaulting logic for `max_total_routing_fee_msat`.
783+ ///
784+ /// # Errors
785+ ///
786+ /// In addition to the errors documented in [`Self::create_refund_builder`], this method will
787+ /// return an error if the provided [`MessageRouter`] fails to construct a valid
788+ /// [`BlindedMessagePath`] for the refund.
789+ ///
790+ /// [`Refund`]: crate::offers::refund::Refund
791+ /// [`BlindedMessagePath`]: crate::blinded_path::message::BlindedMessagePath
792+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
793+ /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
794+ /// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value
795+ pub fn create_refund_builder_using_router < ES : Deref , ME : Deref > (
796+ & self , router : ME , entropy_source : ES , amount_msats : u64 , absolute_expiry : Duration ,
797+ payment_id : PaymentId , peers : Vec < MessageForwardNode > ,
798+ ) -> Result < RefundBuilder < secp256k1:: All > , Bolt12SemanticError >
799+ where
800+ ME :: Target : MessageRouter ,
801+ ES :: Target : EntropySource ,
802+ {
803+ let receive_key = self . get_receive_auth_key ( ) ;
804+ self . create_refund_builder_intern (
805+ & * entropy_source,
806+ |node_id, context, secp_ctx| {
807+ router
808+ . create_blinded_paths ( node_id, receive_key, context, peers, secp_ctx)
809+ . map ( |paths| paths. into_iter ( ) . take ( 1 ) )
810+ . map_err ( |_| Bolt12SemanticError :: MissingPaths )
811+ } ,
812+ amount_msats,
813+ absolute_expiry,
814+ payment_id,
815+ )
746816 }
747817
748818 /// Creates an [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized
0 commit comments