@@ -622,28 +622,71 @@ where
622622 } )
623623 }
624624
625+ fn create_refund_builder_intern < ES : Deref , PF , I > (
626+ & self , entropy_source : ES , make_paths : PF , amount_msats : u64 , absolute_expiry : Duration ,
627+ payment_id : PaymentId ,
628+ ) -> Result < RefundBuilder < secp256k1:: All > , Bolt12SemanticError >
629+ where
630+ ES :: Target : EntropySource ,
631+ PF : FnOnce (
632+ PublicKey ,
633+ MessageContext ,
634+ & secp256k1:: Secp256k1 < secp256k1:: All > ,
635+ ) -> Result < I , Bolt12SemanticError > ,
636+ I : IntoIterator < Item = BlindedMessagePath > ,
637+ {
638+ let node_id = self . get_our_node_id ( ) ;
639+ let expanded_key = & self . inbound_payment_key ;
640+ let entropy = & * entropy_source;
641+ let secp_ctx = & self . secp_ctx ;
642+
643+ let nonce = Nonce :: from_entropy_source ( entropy) ;
644+ let context = MessageContext :: Offers ( OffersContext :: OutboundPayment {
645+ payment_id,
646+ nonce,
647+ hmac : None ,
648+ } ) ;
649+
650+ // Create the base builder with common properties
651+ let mut builder = RefundBuilder :: deriving_signing_pubkey (
652+ node_id,
653+ expanded_key,
654+ nonce,
655+ secp_ctx,
656+ amount_msats,
657+ payment_id,
658+ ) ?
659+ . chain_hash ( self . chain_hash )
660+ . absolute_expiry ( absolute_expiry) ;
661+
662+ for path in make_paths ( node_id, context, secp_ctx) ? {
663+ builder = builder. path ( path) ;
664+ }
665+
666+ Ok ( builder. into ( ) )
667+ }
668+
625669 /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
626670 /// [`OffersMessageFlow`], and any corresponding [`Bolt12Invoice`] received for the refund
627671 /// can be verified using [`Self::verify_bolt12_invoice`].
628672 ///
673+ /// # Privacy
674+ ///
675+ /// Uses the [`OffersMessageFlow`]'s [`MessageRouter`] to construct a [`BlindedMessagePath`]
676+ /// for the offer. See those docs for privacy implications.
677+ ///
629678 /// The builder will have the provided expiration set. Any changes to the expiration on the
630679 /// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
631680 /// block time minus two hours is used for the current time when determining if the refund has
632681 /// expired.
633682 ///
634- /// To refund can be revoked by the user prior to receiving the invoice.
683+ /// The refund can be revoked by the user prior to receiving the invoice.
635684 /// If abandoned, or if an invoice is not received before expiration, the payment will fail
636685 /// with an [`Event::PaymentFailed`].
637686 ///
638687 /// If `max_total_routing_fee_msat` is not specified, the default from
639688 /// [`RouteParameters::from_payment_params_and_value`] is applied.
640689 ///
641- /// # Privacy
642- ///
643- /// Uses [`MessageRouter`] to construct a [`BlindedMessagePath`] for the refund based on the given
644- /// `absolute_expiry` according to [`MAX_SHORT_LIVED_RELATIVE_EXPIRY`]. See those docs for
645- /// privacy implications.
646- ///
647690 /// Also uses a derived payer id in the refund for payer privacy.
648691 ///
649692 /// # Errors
@@ -662,32 +705,76 @@ where
662705 where
663706 ES :: Target : EntropySource ,
664707 {
665- let node_id = self . get_our_node_id ( ) ;
666- let expanded_key = & self . inbound_payment_key ;
667- let entropy = & * entropy_source;
668- let secp_ctx = & self . secp_ctx ;
669-
670- let nonce = Nonce :: from_entropy_source ( entropy) ;
671- let context = OffersContext :: OutboundPayment { payment_id, nonce, hmac : None } ;
672-
673- let path = self
674- . create_blinded_paths_using_absolute_expiry ( context, Some ( absolute_expiry) , peers)
675- . and_then ( |paths| paths. into_iter ( ) . next ( ) . ok_or ( ( ) ) )
676- . map_err ( |_| Bolt12SemanticError :: MissingPaths ) ?;
677-
678- let builder = RefundBuilder :: deriving_signing_pubkey (
679- node_id,
680- expanded_key,
681- nonce,
682- secp_ctx,
708+ self . create_refund_builder_intern (
709+ & * entropy_source,
710+ |_, context, _| {
711+ self . create_blinded_paths ( peers, context)
712+ . map ( |paths| paths. into_iter ( ) . take ( 1 ) )
713+ . map_err ( |_| Bolt12SemanticError :: MissingPaths )
714+ } ,
683715 amount_msats,
716+ absolute_expiry,
684717 payment_id,
685- ) ?
686- . chain_hash ( self . chain_hash )
687- . absolute_expiry ( absolute_expiry)
688- . path ( path) ;
718+ )
719+ }
689720
690- Ok ( builder)
721+ /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
722+ /// [`OffersMessageFlow`] when handling [`Bolt12Invoice`] messages for the refund.
723+ ///
724+ /// # Privacy
725+ ///
726+ /// Constructs a [`BlindedMessagePath`] for the refund using a custom [`MessageRouter`].
727+ /// Users can implement a custom [`MessageRouter`] to define properties of the
728+ /// [`BlindedMessagePath`] as required or opt not to create any `BlindedMessagePath`.
729+ ///
730+ /// # Payment
731+ ///
732+ /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
733+ /// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
734+ ///
735+ /// The builder will have the provided expiration set. Any changes to the expiration on the
736+ /// returned builder will not be honored by [`OffersMessageFlow`]. For non-`std`, the highest seen
737+ /// block time minus two hours is used for the current time when determining if the refund has
738+ /// expired.
739+ ///
740+ /// The refund can be revoked by the user prior to receiving the invoice.
741+ /// If abandoned, or if an invoice is not received before expiration, the payment will fail
742+ /// with an [`Event::PaymentFailed`].
743+ ///
744+ /// If `max_total_routing_fee_msat` is not specified, The default from
745+ /// [`RouteParameters::from_payment_params_and_value`] is applied.
746+ ///
747+ /// Also, uses a derived payer id in the refund for payer privacy.
748+ ///
749+ /// # Errors
750+ ///
751+ /// Errors if:
752+ /// - a duplicate `payment_id` is provided given the caveats in the aforementioned link,
753+ /// - `amount_msats` is invalid, or
754+ /// - the provided [`MessageRouter`] is unable to create a blinded path for the refund.
755+ ///
756+ /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
757+ /// [`RouteParameters::from_payment_params_and_value`]: crate::routing::router::RouteParameters::from_payment_params_and_value
758+ pub fn create_refund_builder_using_router < ES : Deref , ME : Deref > (
759+ & self , router : ME , entropy_source : ES , amount_msats : u64 , absolute_expiry : Duration ,
760+ payment_id : PaymentId , peers : Vec < MessageForwardNode > ,
761+ ) -> Result < RefundBuilder < secp256k1:: All > , Bolt12SemanticError >
762+ where
763+ ME :: Target : MessageRouter ,
764+ ES :: Target : EntropySource ,
765+ {
766+ self . create_refund_builder_intern (
767+ & * entropy_source,
768+ |node_id, context, secp_ctx| {
769+ router
770+ . create_blinded_paths ( node_id, context, peers, secp_ctx)
771+ . map ( |paths| paths. into_iter ( ) . take ( 1 ) )
772+ . map_err ( |_| Bolt12SemanticError :: MissingPaths )
773+ } ,
774+ amount_msats,
775+ absolute_expiry,
776+ payment_id,
777+ )
691778 }
692779
693780 /// Creates an [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized
0 commit comments