Observed: 2026-08-30, reproducible on demand at the time of writing.
Reported by: the operator of a self-hosted NWC wallet service that verifies, before storing it, that a client-supplied zap request actually belongs to the invoice its node paid.
Summary
The callback mints a correct NIP-57 zap invoice when the zap request's content is empty, and an ordinary comment invoice with no description_hash at all when content is non-empty. The zap request is otherwise identical: same signer, same p, amount, relays and lnurl tags, same amount parameter.
Since NIP-57 binds a zap receipt to its invoice through description_hash = sha256(zap request JSON), an invoice without that field cannot carry a verifiable zap. The commitment is simply absent.
Reproduction
Two requests differing only in the content field of the zap request.
PK=... # the address owner
LN=lnurl...
CB=https://getalby.com/lnurlp/.../callback
# A — comment-free zap request (content: "")
ZR=$(nak event -k 9734 -c "" -t p=$PK -t amount=28000 -t relays=wss://nos.lol -t lnurl=$LN --sec <key>)
curl -s "$CB?amount=28000&nostr=$(urlencode "$ZR")"
# B — identical, except content: "Nice test"
ZR=$(nak event -k 9734 -c "Nice test" -t p=$PK -t amount=21000 -t relays=wss://nos.lol -t lnurl=$LN --sec <key>)
curl -s "$CB?amount=21000&nostr=$(urlencode "$ZR")"
Decode each returned pr with lncli decodepayreq or any bolt11 decoder:
A — content "" description: ''
description_hash: 01d653fcf76be1f0ed0ebb5fdac84e38dfd17a22f4c099eb13ae0f339d28282f
sha256(zap request bytes sent):
01d653fcf76be1f0ed0ebb5fdac84e38dfd17a22f4c099eb13ae0f339d28282f
-> CORRECT: commits to the exact zap request
B — content "Nice test" description: 'Nice test'
description_hash: (absent)
-> the comment was lifted out of the zap request and used as the
invoice memo, and no commitment was made
Note that no comment query parameter was sent in either case. The text came only from the zap request's own content, so the fallback is triggered by parsing the zap request, not by the LUD-12 comment parameter.
The endpoint advertises zap support in both cases:
allowsNostr: true
nostrPubkey: ...
commentAllowed: 255
Expected
Per NIP-57, when a valid nostr parameter is supplied, the invoice's description_hash must be the SHA-256 of that zap request JSON, whether or not the request carries content. A comment in a zap request is normal: it is how a zap's message is conveyed, and it ends up in the zap receipt's content.
Impact
Zaps carrying a message cannot produce a verifiable zap receipt for addresses on this service. Anything that checks the binding must reject them; anything that does not check is accepting an unbound claim.
It is silent. The payment succeeds, the invoice looks normal, and only a verifier notices.
Zaps without a message are unaffected, which makes the failure look intermittent and user-specific. In our case it presented as "the default zap works, but a zap with a comment shows no sender".
How it was found
Five real payments from a phone wallet to Alby-hosted addresses, all with comments, were refused by our node because the invoice had nothing to bind the zap request to. Isolating the variable took the two curl calls above: it is the comment, not the pairing, the client, or the amount.
Observed: 2026-08-30, reproducible on demand at the time of writing.
Reported by: the operator of a self-hosted NWC wallet service that verifies, before storing it, that a client-supplied zap request actually belongs to the invoice its node paid.
Summary
The callback mints a correct NIP-57 zap invoice when the zap request's
contentis empty, and an ordinary comment invoice with nodescription_hashat all whencontentis non-empty. The zap request is otherwise identical: same signer, samep,amount,relaysandlnurltags, same amount parameter.Since NIP-57 binds a zap receipt to its invoice through
description_hash = sha256(zap request JSON), an invoice without that field cannot carry a verifiable zap. The commitment is simply absent.Reproduction
Two requests differing only in the
contentfield of the zap request.Decode each returned
prwithlncli decodepayreqor any bolt11 decoder:Note that no
commentquery parameter was sent in either case. The text came only from the zap request's owncontent, so the fallback is triggered by parsing the zap request, not by the LUD-12 comment parameter.The endpoint advertises zap support in both cases:
Expected
Per NIP-57, when a valid
nostrparameter is supplied, the invoice'sdescription_hashmust be the SHA-256 of that zap request JSON, whether or not the request carriescontent. A comment in a zap request is normal: it is how a zap's message is conveyed, and it ends up in the zap receipt'scontent.Impact
Zaps carrying a message cannot produce a verifiable zap receipt for addresses on this service. Anything that checks the binding must reject them; anything that does not check is accepting an unbound claim.
It is silent. The payment succeeds, the invoice looks normal, and only a verifier notices.
Zaps without a message are unaffected, which makes the failure look intermittent and user-specific. In our case it presented as "the default zap works, but a zap with a comment shows no sender".
How it was found
Five real payments from a phone wallet to Alby-hosted addresses, all with comments, were refused by our node because the invoice had nothing to bind the zap request to. Isolating the variable took the two curl calls above: it is the comment, not the pairing, the client, or the amount.