Skip to content

bolt12 recurring invoices #6464

Open
Open
@ajtowns

Description

@ajtowns

Some thoughts on bolt12-based recurring invoices as implemented in cln. Hopefully this is an okay forum for that.

  • recurrence_limit setting the maximum value of a 0-based counter is confusing. Should be the total number of invoices you can pay, not one less than it.
  • fetchinvoice offer=X recurrence_label=Y recurrence_counter=0 is non-idempotent, and just requests a new invoice from the remote -- even if it's already been paid under that label, which potentially can result in double payments. When you use a higher counter value than 0, it either reports the existing payment, the current invoice, or gives an error that you haven't paid the previous invoice.
  • If you've got a recurring offer with a low interval, listpays becomes cluttered with many payments. It's also awkward to match up the payments with the actual invoice, and there doesn't seem any easy way to track which subscription index you're due to pay next?

It would perhaps be nicer to have a flow more like:

  • fetchinvoice offer=OFFER recurrence_label=XXXX # recurrence_counter implicitly 0
  • pay bolt11=INV label=XXXX
  • pay label=XXXX # picks up the "recurring" status from the label, and begins the next payment

That way the initial fetchinvoice is the only thing doing a bolt12 remote query, and the "pay" instructions are just calculating the timing and payment hash programmatically based on previous payments.

(You could perhaps still attempt fetchinvoice recurrence_counter=50 if you passed the calculated paywindow_end and wanted to resume the subscription, or alternatively could just declare any recurring invoice complete as soon as a paywindow is missed, and use recurrence_start for the scenario where resuming is okay?)

Could perhaps report all this via a single entry in listpays something like:

{
   "label": "XXXX",
   "created_at": 1690000000,
   "destination": "02...",

   "bolt12": "lni1...",
   "status": "recurring",
   "recurrence_counter": 5,
   "recurrence_limit": 100,
   "payment_hash": "SHA256^5{preimage}",
   "preimage": "preimage",

   "payments": [
      {"created_at": 1690000000, "completed_at": 1690010009, "amount_sent_msat": 1000 },
      {"created_at": 1690010000, "completed_at": 1690010002, "amount_sent_msat": 1000 },
      {"created_at": 1690020000, "completed_at": 1690010004, "amount_sent_msat": 1000 },
      {"created_at": 1690030000, "completed_at": 1690010003, "amount_sent_msat": 1000 },
      {"created_at": 1690040000, "completed_at": 1690010001, "amount_sent_msat": 1000 }
   ],

   "amount_msat": 5000,
   "amount_sent_msat": 5000
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions