Context
NWC-321 (pay method) supports payer_proof in its response (field payer_proof, format lnp1...). BOLT-12 defines a payer proof: a message signed by the payer that commits to the payer id, payment hash, the recipient's offer signing pubkey, and optionally selected invoice fields. Unlike a preimage (which only proves an HTLC was settled), a payer proof lets the payer prove to a third party who paid whom for what (e.g. for refund disputes or receipts).
Related: BOLT-12 offer payments were added in #2543 / feat/bolt12-nwc.
What needs to be done
- In
lnclient/ldk/ldk.go PayOfferSync, capture the Bolt12Invoice returned in the PaymentSuccessful event (upstream ldk-node exposes it as bolt12_invoice on the event).
- Call
Bolt12Payment().CreatePayerProof(paymentId, preimage, invoice, options) (upstream API) to obtain the lnp1... payer proof.
- Return it from
PayOfferSync (extend lnclient.PayOfferResponse with e.g. PayerProof string).
- In
transactions.PayOfferSync, store the payer proof in the transaction metadata (e.g. metadata.payer_proof).
- In
nip47/controllers/pay_controller.go (payBolt12), include payer_proof in the NWC-321 pay result per the spec (field already defined in NWC-321).
- Consider surfacing it in the transaction list UI (
frontend/src/components/TransactionItem.tsx).
Blocking
- Alby's ldk-node fork (https://github.com/getAlby/ldk-node) and the Go bindings (https://github.com/getAlby/ldk-node-go, currently pinned at
v0.0.0-20260805080406-af22e238c194, bindings from 2026-04) predate upstream payer-proof support:
EventPaymentSuccessful in the bindings has no Bolt12Invoice field (only PaymentId, PaymentHash, PaymentPreimage, FeePaidMsat).
- No
CreatePayerProof binding exists.
- Once the fork is synced to an upstream ldk-node release containing
create_payer_proof and bindings are regenerated, the steps above can be implemented without further external changes.
Notes
- Payments settled via a static invoice (async payments) cannot be proven this way (upstream limitation) — only regular BOLT-12 offer payments.
- Until this lands, the best available proof of payment for a BOLT-12 offer payment is preimage + payment hash + offer ID in transaction metadata.
Context
NWC-321 (
paymethod) supportspayer_proofin its response (fieldpayer_proof, formatlnp1...). BOLT-12 defines a payer proof: a message signed by the payer that commits to the payer id, payment hash, the recipient's offer signing pubkey, and optionally selected invoice fields. Unlike a preimage (which only proves an HTLC was settled), a payer proof lets the payer prove to a third party who paid whom for what (e.g. for refund disputes or receipts).Related: BOLT-12 offer payments were added in #2543 / feat/bolt12-nwc.
What needs to be done
lnclient/ldk/ldk.goPayOfferSync, capture theBolt12Invoicereturned in thePaymentSuccessfulevent (upstream ldk-node exposes it asbolt12_invoiceon the event).Bolt12Payment().CreatePayerProof(paymentId, preimage, invoice, options)(upstream API) to obtain thelnp1...payer proof.PayOfferSync(extendlnclient.PayOfferResponsewith e.g.PayerProof string).transactions.PayOfferSync, store the payer proof in the transaction metadata (e.g.metadata.payer_proof).nip47/controllers/pay_controller.go(payBolt12), includepayer_proofin the NWC-321 pay result per the spec (field already defined in NWC-321).frontend/src/components/TransactionItem.tsx).Blocking
v0.0.0-20260805080406-af22e238c194, bindings from 2026-04) predate upstream payer-proof support:EventPaymentSuccessfulin the bindings has noBolt12Invoicefield (onlyPaymentId,PaymentHash,PaymentPreimage,FeePaidMsat).CreatePayerProofbinding exists.create_payer_proofand bindings are regenerated, the steps above can be implemented without further external changes.Notes