BOLT 12: Reusable Payments on the Bitcoin Lightning Network

Backend Software Engineer with over 5 years of working experience with engineering teams building and maintaining solutions across various sections
I have solid experience in building products using technologies such as JavaScript, nodeJs, ExpressJs, Typescript, ReactJs, NoSQL/SQL database, PHP/Laravel, Firebase, AWS S3, elastic beanstalk, and AWS Lambda function
I write articles on my blog around backend engineering, Bitcoin Lighening, building distributed Microservice systems and give talks in my local tech communities.
BOLT 12 introduces a new payment flow for the Bitcoin Lightning Network that enables reusable payment codes, improved privacy, and more flexible payment interactions than traditional BOLT 11 invoices.
At the core of BOLT 12 is the Offer: a static, reusable payment descriptor that allows a sender to pay without knowing the receiver’s node identity.
Why BOLT 12?
BOLT 11 invoices work well, but they have structural limitations:
Single-use (unsafe if reused)
Amount-specific (poor UX for donations, subscriptions, or dynamic pricing)
Receiver must often be online to generate an invoice
Not ideal for static identifiers or merchant checkout flows
BOLT 12 addresses these issues by separating payment intent from invoice creation.
Key Concepts in BOLT 12
1. Offer (Static & Reusable)
An Offer is a reusable Lightning payment descriptor published by the receiver.
An offer:
Can be paid multiple times
Can specify a fixed amount or no amount at all
Can be encoded as a QR code, URL, or text
Is safe to reuse
Does not require revealing the receiver’s node ID
Internally, an offer encodes everything needed to request an invoice:
How to reach the receiver (node ID or blinded path)
Description
Expiry time
Currency
Minimum / maximum quantity
Optional fixed amount
An offer does not move funds — it only enables invoice discovery.
2. Creating Offers in Rust (LDK)
Fixed-amount Offer
let offer = context.node.bolt12_payment().receive(
50_000, // amount in msat
"Coffee payment",
Some(3600), // expiry in seconds
Some(1), // quantity
)?;
Use this when:
The price is fixed
You want deterministic payments
Variable-amount Offer
let offer = context.node.bolt12_payment().receive_variable_amount(
"Donation",
Some(3600),
)?;
Use this when:
The payer chooses the amount
The receiver computes pricing dynamically (e.g. fiat → sats)
3. Invoice Request (Payer → Receiver)
Once a payer obtains an offer, they create an InvoiceRequest.
The payer:
Selects an offer
Optionally chooses an amount
Specifies quantity and metadata (payer note)
This request is:
Onion-routed
End-to-end encrypted
Sent over Lightning without requiring a channel
Sending an Invoice Request in Rust
Receiver decide the amount
let payment_id = context.node.bolt12_payment().send(
&offer,
Some(1),
Some("Thanks!"), // payer note
route_parameters,
)?;
Here:
The payer sends intent
The receiver computes the final invoice amount
Payer decides the amount
let payment_id = context.node.bolt12_payment().send_using_amount(
&offer,
100_000, // amount in msat
Some(1),
Some("Tip"),
route_parameters,
)?;
This is common for:
Donations
Tips
Pay-what-you-want flows
Invoice (Receiver → Payer)
Upon receiving the InvoiceRequest, the offer creator:
Validates the request
Computes the amount (if not specified)
Generates a real Lightning invoice
Cryptographically signs it
Sends it back to the payer
At this point, the payer receives a standard payable invoice.
Payment Execution
The final step is identical to BOLT 11:
Routes are found
HTLCs are constructed
Funds are transferred
The difference is how the invoice was obtained, not how it is paid.
Privacy Improvements in BOLT 12
BOLT 12 significantly improves privacy:
Receiver node ID is not revealed up front
Invoice requests are onion-routed
Supports blinded paths, hiding network topology
No static invoices to track or correlate
This makes BOLT 12 ideal for:
Merchant payments
Static Lightning identifiers
Recurring and subscription payments
Conclusion
BOLT 12 introduces a cleaner and more powerful Lightning payment model:
Static, reusable offers
Flexible pricing models
Better privacy
Improved merchant UX
By separating offers, invoice requests, and invoices, BOLT 12 enables payment flows that were awkward or impossible with BOLT 11.



