@@ -243,9 +243,11 @@ macro_rules! invoice_explicit_signing_pubkey_builder_methods {
243243 #[ cfg_attr( c_bindings, allow( dead_code) ) ]
244244 pub ( super ) fn for_offer(
245245 invoice_request: & ' a InvoiceRequest , payment_paths: Vec <BlindedPaymentPath >,
246- created_at: Duration , payment_hash: PaymentHash , signing_pubkey: PublicKey ,
246+ created_at: Duration , recurrence_basetime: Option <u64 >, payment_hash: PaymentHash ,
247+ signing_pubkey: PublicKey ,
247248 ) -> Result <Self , Bolt12SemanticError > {
248249 let amount_msats = Self :: amount_msats( invoice_request) ?;
250+ let invoice_recurrence = Self :: recurrence_fields( invoice_request, recurrence_basetime) ?;
249251 let contents = InvoiceContents :: ForOffer {
250252 invoice_request: invoice_request. contents. clone( ) ,
251253 fields: Self :: fields(
@@ -254,6 +256,7 @@ macro_rules! invoice_explicit_signing_pubkey_builder_methods {
254256 payment_hash,
255257 amount_msats,
256258 signing_pubkey,
259+ invoice_recurrence,
257260 ) ,
258261 } ;
259262
@@ -274,6 +277,7 @@ macro_rules! invoice_explicit_signing_pubkey_builder_methods {
274277 payment_hash,
275278 amount_msats,
276279 signing_pubkey,
280+ None ,
277281 ) ,
278282 } ;
279283
@@ -315,9 +319,11 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods {
315319 #[ cfg_attr( c_bindings, allow( dead_code) ) ]
316320 pub ( super ) fn for_offer_using_keys(
317321 invoice_request: & ' a InvoiceRequest , payment_paths: Vec <BlindedPaymentPath >,
318- created_at: Duration , payment_hash: PaymentHash , keys: Keypair ,
322+ created_at: Duration , recurrence_basetime: Option <u64 >, payment_hash: PaymentHash ,
323+ keys: Keypair ,
319324 ) -> Result <Self , Bolt12SemanticError > {
320325 let amount_msats = Self :: amount_msats( invoice_request) ?;
326+ let invoice_recurrence = Self :: recurrence_fields( invoice_request, recurrence_basetime) ?;
321327 let signing_pubkey = keys. public_key( ) ;
322328 let contents = InvoiceContents :: ForOffer {
323329 invoice_request: invoice_request. contents. clone( ) ,
@@ -327,6 +333,7 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods {
327333 payment_hash,
328334 amount_msats,
329335 signing_pubkey,
336+ invoice_recurrence,
330337 ) ,
331338 } ;
332339
@@ -348,6 +355,7 @@ macro_rules! invoice_derived_signing_pubkey_builder_methods {
348355 payment_hash,
349356 amount_msats,
350357 signing_pubkey,
358+ None ,
351359 ) ,
352360 } ;
353361
@@ -408,10 +416,17 @@ macro_rules! invoice_builder_methods {
408416 }
409417 }
410418
419+ pub ( crate ) fn recurrence_fields(
420+ _invoice_request: & InvoiceRequest , _recurrence_basetime: Option <u64 >,
421+ ) -> Result <Option <InvoiceRecurrence >, Bolt12SemanticError > {
422+ todo!( "Future commits will introduce the Recurrence Token creation logic" )
423+ }
424+
411425 #[ cfg_attr( c_bindings, allow( dead_code) ) ]
412426 fn fields(
413427 payment_paths: Vec <BlindedPaymentPath >, created_at: Duration ,
414428 payment_hash: PaymentHash , amount_msats: u64 , signing_pubkey: PublicKey ,
429+ invoice_recurrence: Option <InvoiceRecurrence >,
415430 ) -> InvoiceFields {
416431 InvoiceFields {
417432 payment_paths,
@@ -424,6 +439,7 @@ macro_rules! invoice_builder_methods {
424439 signing_pubkey,
425440 #[ cfg( test) ]
426441 experimental_baz: None ,
442+ invoice_recurrence,
427443 }
428444 }
429445
@@ -775,6 +791,18 @@ struct InvoiceFields {
775791 signing_pubkey : PublicKey ,
776792 #[ cfg( test) ]
777793 experimental_baz : Option < u64 > ,
794+ invoice_recurrence : Option < InvoiceRecurrence > ,
795+ }
796+
797+ #[ derive( Clone , Debug , PartialEq ) ]
798+ /// Recurrence fields included in an invoice for a recurring offer.
799+ ///
800+ /// `recurrence_basetime` anchors period 0 for recurring invoices when the offer did not include
801+ /// an explicit recurrence base. `recurrence_token` is the opaque token issued by the payee for
802+ /// the payer to echo in the next recurring [`InvoiceRequest`].
803+ pub struct InvoiceRecurrence {
804+ recurrence_basetime : u64 ,
805+ recurrence_token : Vec < u8 > ,
778806}
779807
780808macro_rules! invoice_accessors { ( $self: ident, $contents: expr) => {
@@ -1415,6 +1443,14 @@ impl InvoiceFields {
14151443 }
14161444 } ;
14171445
1446+ let ( invoice_recurrence_basetime, invoice_recurrence_token) = match & self . invoice_recurrence
1447+ {
1448+ None => ( None , None ) ,
1449+ Some ( recurrence) => {
1450+ ( Some ( recurrence. recurrence_basetime ) , Some ( recurrence. recurrence_token . as_ref ( ) ) )
1451+ } ,
1452+ } ;
1453+
14181454 (
14191455 InvoiceTlvStreamRef {
14201456 paths : Some ( Iterable (
@@ -1429,6 +1465,8 @@ impl InvoiceFields {
14291465 features,
14301466 node_id : Some ( & self . signing_pubkey ) ,
14311467 held_htlc_available_paths : None ,
1468+ invoice_recurrence_basetime,
1469+ invoice_recurrence_token,
14321470 } ,
14331471 ExperimentalInvoiceTlvStreamRef {
14341472 #[ cfg( test) ]
@@ -1510,6 +1548,8 @@ tlv_stream!(InvoiceTlvStream, InvoiceTlvStreamRef<'a>, INVOICE_TYPES, {
15101548 ( 172 , fallbacks: ( Vec <FallbackAddress >, WithoutLength ) ) ,
15111549 ( 174 , features: ( Bolt12InvoiceFeatures , WithoutLength ) ) ,
15121550 ( 176 , node_id: PublicKey ) ,
1551+ ( 177 , invoice_recurrence_basetime: ( u64 , HighZeroBytesDroppedBigSize ) ) ,
1552+ ( 179 , invoice_recurrence_token: ( Vec <u8 >, WithoutLength ) ) ,
15131553 // Only present in `StaticInvoice`s.
15141554 ( 236 , held_htlc_available_paths: ( Vec <BlindedMessagePath >, WithoutLength ) ) ,
15151555} ) ;
@@ -1701,6 +1741,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
17011741 features,
17021742 node_id,
17031743 held_htlc_available_paths,
1744+ invoice_recurrence_basetime,
1745+ invoice_recurrence_token,
17041746 } ,
17051747 experimental_offer_tlv_stream,
17061748 experimental_invoice_request_tlv_stream,
@@ -1731,6 +1773,14 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
17311773
17321774 let signing_pubkey = node_id. ok_or ( Bolt12SemanticError :: MissingSigningPubkey ) ?;
17331775
1776+ let invoice_recurrence = match ( invoice_recurrence_basetime, invoice_recurrence_token) {
1777+ ( None , None ) => None ,
1778+ ( Some ( basetime) , Some ( token) ) => {
1779+ Some ( InvoiceRecurrence { recurrence_basetime : basetime, recurrence_token : token } )
1780+ } ,
1781+ _ => return Err ( Bolt12SemanticError :: InvalidRecurrence ) ,
1782+ } ;
1783+
17341784 let fields = InvoiceFields {
17351785 payment_paths,
17361786 created_at,
@@ -1742,6 +1792,7 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
17421792 signing_pubkey,
17431793 #[ cfg( test) ]
17441794 experimental_baz,
1795+ invoice_recurrence,
17451796 } ;
17461797
17471798 check_invoice_signing_pubkey ( & fields. signing_pubkey , & offer_tlv_stream) ?;
@@ -1759,6 +1810,10 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
17591810 return Err ( Bolt12SemanticError :: InvalidAmount ) ;
17601811 }
17611812
1813+ if fields. invoice_recurrence . is_some ( ) {
1814+ return Err ( Bolt12SemanticError :: UnexpectedRecurrence ) ;
1815+ }
1816+
17621817 Ok ( InvoiceContents :: ForRefund { refund, fields } )
17631818 } else {
17641819 let invoice_request = InvoiceRequestContents :: try_from ( (
@@ -2047,6 +2102,8 @@ mod tests {
20472102 features: None ,
20482103 node_id: Some ( & recipient_pubkey( ) ) ,
20492104 held_htlc_available_paths: None ,
2105+ invoice_recurrence_basetime: None ,
2106+ invoice_recurrence_token: None ,
20502107 } ,
20512108 SignatureTlvStreamRef { signature: Some ( & invoice. signature( ) ) } ,
20522109 ExperimentalOfferTlvStreamRef { experimental_foo: None } ,
@@ -2159,6 +2216,8 @@ mod tests {
21592216 features: None ,
21602217 node_id: Some ( & recipient_pubkey( ) ) ,
21612218 held_htlc_available_paths: None ,
2219+ invoice_recurrence_basetime: None ,
2220+ invoice_recurrence_token: None ,
21622221 } ,
21632222 SignatureTlvStreamRef { signature: Some ( & invoice. signature( ) ) } ,
21642223 ExperimentalOfferTlvStreamRef { experimental_foo: None } ,
0 commit comments