BOLT-12: support payer proofs for offer payments
- Dominant language
- TypeScript
- Stars
- 280
- Forks
- 132
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 11
Description
## 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
1. 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).
2. Call `Bolt12Payment().CreatePayerProof(paymentId, preimage, invoice, options)` (upstream API) to obtain the `lnp1...` payer proof.
3. Return it from `PayOfferSync` (extend `lnclient.PayOfferResponse` with e.g. `PayerProof string`).
4. In `transactions.PayOfferSync`, store the payer proof in the transaction metadata (e.g. `metadata.payer_proof`).
5. 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).
6. 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.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by checking lnclient/ldk/ldk.go and the pinned ldk-node Go bindings for Bolt12Invoice and CreatePayerProof support. Then trace PayOfferSync, transactions.PayOfferSync, nip47/controllers/pay_controller.go, and frontend/src/components/TransactionItem.tsx. Done means payer_proof is created for regular BOLT-12 offer payments, stored in metadata, and returned by NWC-321; implementation is blocked until the fork and bindings are updated.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, typescript
- Domain
- api, backend, frontend, payments
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 42/100