@@ -11,12 +11,15 @@ use crate::payment::store::{
1111use crate :: types:: ChannelManager ;
1212
1313use lightning:: ln:: channelmanager:: { PaymentId , Retry } ;
14+ use lightning:: offers:: invoice:: Bolt12Invoice ;
1415use lightning:: offers:: offer:: { Amount , Offer } ;
1516use lightning:: offers:: parse:: Bolt12SemanticError ;
17+ use lightning:: offers:: refund:: Refund ;
1618
1719use rand:: RngCore ;
1820
1921use std:: sync:: { Arc , RwLock } ;
22+ use std:: time:: { Duration , SystemTime , UNIX_EPOCH } ;
2023
2124/// A payment handler allowing to create and pay [BOLT 12] offers and refunds.
2225///
@@ -264,4 +267,82 @@ impl Bolt12Payment {
264267
265268 Ok ( offer)
266269 }
270+
271+ /// Requests a refund payment for the given [`Refund`].
272+ ///
273+ /// The returned [`Bolt12Invoice`] is for informational purposes only (i.e., isn't needed to
274+ /// retrieve the refund).
275+ pub fn request_refund ( & self , refund : & Refund ) -> Result < Bolt12Invoice , Error > {
276+ let invoice = self . channel_manager . request_refund_payment ( refund) . map_err ( |e| {
277+ log_error ! ( self . logger, "Failed to request refund payment: {:?}" , e) ;
278+ Error :: InvoiceRequestCreationFailed
279+ } ) ?;
280+
281+ let payment_hash = invoice. payment_hash ( ) ;
282+ let payment_id = PaymentId ( payment_hash. 0 ) ;
283+
284+ let payment = PaymentDetails {
285+ id : payment_id,
286+ kind : PaymentKind :: Bolt12Refund {
287+ hash : Some ( payment_hash) ,
288+ preimage : None ,
289+ secret : None ,
290+ } ,
291+ amount_msat : Some ( refund. amount_msats ( ) ) ,
292+ direction : PaymentDirection :: Inbound ,
293+ status : PaymentStatus :: Pending ,
294+ } ;
295+
296+ self . payment_store . insert ( payment) ?;
297+
298+ Ok ( invoice)
299+ }
300+
301+ /// Returns a [`Refund`] object that can be used to offer a refund payment of the amount given.
302+ pub fn create_refund ( & self , amount_msat : u64 , expiry_secs : u32 ) -> Result < Refund , Error > {
303+ let mut random_bytes = [ 0u8 ; 32 ] ;
304+ rand:: thread_rng ( ) . fill_bytes ( & mut random_bytes) ;
305+ let payment_id = PaymentId ( random_bytes) ;
306+
307+ let expiration = ( SystemTime :: now ( ) + Duration :: from_secs ( expiry_secs as u64 ) )
308+ . duration_since ( UNIX_EPOCH )
309+ . unwrap ( ) ;
310+ let retry_strategy = Retry :: Timeout ( LDK_PAYMENT_RETRY_TIMEOUT ) ;
311+ let max_total_routing_fee_msat = None ;
312+
313+ let refund = self
314+ . channel_manager
315+ . create_refund_builder (
316+ amount_msat,
317+ expiration,
318+ payment_id,
319+ retry_strategy,
320+ max_total_routing_fee_msat,
321+ )
322+ . map_err ( |e| {
323+ log_error ! ( self . logger, "Failed to create refund builder: {:?}" , e) ;
324+ Error :: RefundCreationFailed
325+ } ) ?
326+ . build ( )
327+ . map_err ( |e| {
328+ log_error ! ( self . logger, "Failed to create refund: {:?}" , e) ;
329+ Error :: RefundCreationFailed
330+ } ) ?;
331+
332+ log_info ! ( self . logger, "Offering refund of {}msat" , amount_msat) ;
333+
334+ let kind = PaymentKind :: Bolt12Refund { hash : None , preimage : None , secret : None } ;
335+
336+ let payment = PaymentDetails {
337+ id : payment_id,
338+ kind,
339+ amount_msat : Some ( amount_msat) ,
340+ direction : PaymentDirection :: Outbound ,
341+ status : PaymentStatus :: Pending ,
342+ } ;
343+
344+ self . payment_store . insert ( payment) ?;
345+
346+ Ok ( refund)
347+ }
267348}
0 commit comments