Context
Nostr zaps are defined by NIP-57 (built on LUD-06, which this repo already implements). LUD-16 is only the lightning-address resolution layer, not the zap spec. This issue covers the dependency-free server-side surface: advertise zap support, accept a zap request, and turn it into the invoice description. Signature verification + zap-receipt publishing (which need crypto + relay/WebSocket + a payment-settled hook) are the follow-up issue.
Relevant code:
src/Invoice/Application/CallbackUrl.php — pay-params response (add the two fields here).
src/Invoice/Infrastructure/Controller/InvoiceController.php — reads only amount today; also read nostr.
src/Invoice/InvoiceFacade.php → src/Invoice/Application/InvoiceGenerator.php — thread the zap request through.
src/Invoice/Domain/BackendInvoice/LnbitsBackendInvoice.php — already builds unhashed_description/description_hash via sha256; feed it the zap JSON.
src/Shared/Value/LnurlPayMetadata.php — the normal-metadata builder (a zap request replaces it).
Goal
Make the server produce a spec-correct zap invoice: advertise allowsNostr/nostrPubkey, accept the nostr zap request on the callback, structurally validate it, and set the invoice description_hash = sha256(zap-request JSON).
Scope / Tasks
Implementation notes
- Do NOT re-serialize the zap request — hash and later-embed the same bytes received, or
SHA256(description) won't match the receipt (NIP-57 Appendix E).
amount is already handled as millisats through the controller/generator.
Acceptance criteria
Out of scope (→ follow-up issue)
- Zap-request signature verification (Appendix D rule 1) — needs secp256k1/BIP-340.
- Zap receipt (kind 9735) signing + relay publishing + payment-settled detection.
References
Context
Nostr zaps are defined by NIP-57 (built on LUD-06, which this repo already implements). LUD-16 is only the lightning-address resolution layer, not the zap spec. This issue covers the dependency-free server-side surface: advertise zap support, accept a zap request, and turn it into the invoice description. Signature verification + zap-receipt publishing (which need crypto + relay/WebSocket + a payment-settled hook) are the follow-up issue.
Relevant code:
src/Invoice/Application/CallbackUrl.php— pay-params response (add the two fields here).src/Invoice/Infrastructure/Controller/InvoiceController.php— reads onlyamounttoday; also readnostr.src/Invoice/InvoiceFacade.php→src/Invoice/Application/InvoiceGenerator.php— thread the zap request through.src/Invoice/Domain/BackendInvoice/LnbitsBackendInvoice.php— already buildsunhashed_description/description_hashviasha256; feed it the zap JSON.src/Shared/Value/LnurlPayMetadata.php— the normal-metadata builder (a zap request replaces it).Goal
Make the server produce a spec-correct zap invoice: advertise
allowsNostr/nostrPubkey, accept thenostrzap request on the callback, structurally validate it, and set the invoicedescription_hash = sha256(zap-request JSON).Scope / Tasks
nostrPubkeysetting (server's x-only BIP-340 pubkey, 64 hex chars) viaLightningConfig/InvoiceConfig/ConfigKey. Advertising it needs no crypto — it's a static string.CallbackUrl): whennostrPubkeyis configured, addallowsNostr => trueandnostrPubkey => <hex>to the response.InvoiceControllerreads thenostrquery param, URI-decode → JSON-decode into the kind-9734 event; thread it through the facade intoInvoiceGenerator.tags; exactly oneptag; 0 or 1etag; 0 or 1Ptag;relaystag SHOULD be present (store it for the receipt step);amounttag exists it MUST equal theamountquery param (both millisats);atag exists it MUST be a valid event coordinate;9734.Reject invalid requests with the LNURL
{status:ERROR, reason}shape.description_hashMUST be built from the exact zap-request JSON string (as received, post-URI-decode) — not the normalLnurlPayMetadata. Store the raw JSON verbatim (needed later for the receipt soSHA256(description)matches). When absent, behavior is unchanged.src/Shared/Value/(mirroringLnurlPayMetadata) is a good home for parsing/validation/description.Implementation notes
SHA256(description)won't match the receipt (NIP-57 Appendix E).amountis already handled as millisats through the controller/generator.Acceptance criteria
nostrPubkeyset, pay params includeallowsNostr:true+nostrPubkey; without it, unchanged.nostrrequest yields an invoice whosedescription_hash == sha256(zap-request JSON); invalid requests return an LNURL error.composer test-allgreen.Out of scope (→ follow-up issue)
References