12<html lang="en">3<head>4<meta charset="utf-8">5<meta name="viewport" content="width=device-width, initial-scale=1">6<title>NIP-57 - Lightning Zaps — Nostr Instruction Manual</title>7<meta name="description" content="NIP-57: Lightning zaps. Zap request (kind 9734), zap receipt (kind 9735), LNURL server setup, validation rules, and zap splits. Complete manual page.">8<link rel="preload" href="../assets/fonts/michroma-400-latin.woff2" as="font" type="font/woff2" crossorigin>9<link rel="preload" href="../assets/fonts/exo2-var-latin.woff2" as="font" type="font/woff2" crossorigin>10<link rel="preload" href="../assets/fonts/sairacondensed-600-latin.woff2" as="font" type="font/woff2" crossorigin>11<link rel="preload" href="../assets/fonts/sairacondensed-700-latin.woff2" as="font" type="font/woff2" crossorigin>12<link rel="preload" href="../assets/fonts/sairacondensed-500-latin.woff2" as="font" type="font/woff2" crossorigin>13<link rel="preload" href="../assets/fonts/sharetechmono-400-latin.woff2" as="font" type="font/woff2" crossorigin>14<link rel="preload" href="../assets/fonts/exo2-400i-latin.woff2" as="font" type="font/woff2" crossorigin>15<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml">16<link rel="stylesheet" href="../assets/manual.css">17</head>18<body>1920<svg width="0" height="0" style="position:absolute">21 <defs>22 <marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">23 <path d="M1,1 L8,4.5 L1,8 Z" fill="#f26a00"/>24 </marker>25 <marker id="arrb" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">26 <path d="M1,1 L8,4.5 L1,8 Z" fill="#2e5f9e"/>27 </marker>28 </defs>29</svg>3031<div class="page">32 <span class="staple s1"></span><span class="staple s2"></span>3334 <header class="band">35 <a class="logo" href="../index.html">NOSTR<b>▸</b>PROTOCOL MANUAL</a>36 <span class="booklet-note">Chapter 57 - Payments</span>37 </header>38 <nav class="tabs">39 <a href="../index.html">Contents</a>40 <a href="#overview" class="on">Overview</a>41 <a href="#spec">Spec</a>42 <a href="#examples">Examples</a>43 <a href="#history">History</a>44 </nav>4546 <div class="chapter">47 <div class="nipno" data-n="57">57</div>48 <div class="titles">49 <div class="kicker">Nostr Implementation Possibility - Hex № 57</div>50 <h1>Lightning Zaps</h1>51 <p class="sub">Record Lightning payments between users as signed events: a zap request asks a wallet for an invoice, and a zap receipt confirms the payment.</p>52 </div>53 </div>5455 <main class="spread">56 <div class="main">5758 <h2 class="bar" id="overview"><span class="num">1</span> Overview</h2>59 <p>A <strong>zap</strong> is a Lightning payment that leaves a public trace on Nostr. This NIP defines two event kinds for that trace. Kind <code>9734</code> is a <strong>zap request</strong>. It represents a payer's request to a recipient's Lightning wallet for an invoice. Kind <code>9735</code> is a <strong>zap receipt</strong>. It represents the confirmation, by the recipient's Lightning wallet, that the invoice issued for a zap request has been paid.</p>60 <p>The <strong>sender</strong> is the user who pays. The <strong>recipient</strong> is the user who gets paid. The recipient's wallet sits behind an <strong>LNURL server</strong>: an HTTP service that issues Lightning invoices per the <a href="https://github.com/lnurl/luds">lnurl specifications</a>.</p>61 <p>With zap receipts on Nostr, clients can display Lightning payments from entities on the network. These can be used for fun or for spam deterrence.</p>6263 <div class="box warn">64 <span class="tag">Warning!</span>65 <p>A zap receipt is <strong>not</strong> a proof of payment. It only proves that some Nostr user fetched an invoice. The receipt implies the invoice was paid, but a rogue implementation could lie. You trust the author of the zap receipt for the legitimacy of the payment.</p>66 </div>6768 <h2 class="bar" id="spec"><span class="num">2</span> Protocol Flow</h2>69 <p>A zap runs through nine steps. HTTP carries the request and the invoice. Lightning carries the money. Nostr relays carry the receipt.</p>70 <ol>71 <li>The client finds the recipient's lnurl pay request URL. If the target event has a <code>zap</code> tag, calculate it from that tag (see §9). Otherwise decode the <code>lud16</code> field on the recipient's profile per the lnurl specifications. The client MUST send a GET request to this URL and parse the response. If <code>allowsNostr</code> exists and is <code>true</code>, and if <code>nostrPubkey</code> exists and is a valid BIP-340 public key in hex, the client should associate this information with the user, along with the response's <code>callback</code>, <code>minSendable</code>, and <code>maxSendable</code> values.</li>72 <li>Clients may display a zap button on each post or on a user's profile. If the recipient's lnurl endpoint supports Nostr, the client SHOULD use this NIP to request a zap receipt instead of a normal lnurl invoice.</li>73 <li>When the sender starts a zap, the client creates a kind <code>9734</code> zap request event as described in §3 and signs it.</li>74 <li>The client does <em>not</em> publish the zap request. It sends the event to the <code>callback</code> URL from the lnurl pay endpoint, using a GET request. See §4.</li>75 <li>The recipient's LNURL server receives the zap request and validates it. See §5 for server configuration and §6 for the validation rules.</li>76 <li>If the zap request is valid, the server fetches a description hash invoice. The description is this zap request note, and this note only. No additional lnurl metadata goes into the description. The server returns the invoice per <a href="https://github.com/lnurl/luds/blob/luds/06.md">LUD06</a>.</li>77 <li>On receiving the invoice, the client MAY pay it or pass it to an app that can pay it.</li>78 <li>Once the invoice is paid, the recipient's LNURL server MUST generate a zap receipt as described in §7. It publishes the receipt to the <code>relays</code> listed in the zap request.</li>79 <li>Clients MAY fetch zap receipts on posts and profiles, but MUST authorize their validity as described in §8. If the zap request has a non-empty <code>content</code>, the client may display it as a zap comment. Generally clients should show users the zap request note, and use the zap receipt to show "zap authorized by ...". This is optional.</li>80 </ol>8182 <figure class="diagram">83 <div class="frame">84 <svg viewBox="0 0 560 330" role="img" aria-label="Sequence diagram: sender client fetches lnurl endpoint, sends zap request, receives invoice, pays over Lightning, and the LNURL server publishes a zap receipt to relays">85 <line class="d-wire" x1="80" y1="40" x2="80" y2="310" stroke-dasharray="3 4"/>86 <line class="d-wire" x1="300" y1="40" x2="300" y2="310" stroke-dasharray="3 4"/>87 <line class="d-wire" x1="490" y1="40" x2="490" y2="310" stroke-dasharray="3 4"/>88 <rect class="d-actor" x="25" y="8" width="110" height="30" rx="6"/>89 <text class="d-actor-label" x="80" y="28" text-anchor="middle">Sender client</text>90 <rect class="d-actor" x="245" y="8" width="110" height="30" rx="6"/>91 <text class="d-actor-label" x="300" y="28" text-anchor="middle">LNURL server</text>92 <rect class="d-actor" x="435" y="8" width="110" height="30" rx="6"/>93 <text class="d-actor-label" x="490" y="28" text-anchor="middle">Relays</text>94 <path class="d-msg" d="M80,62 L295,62"/>95 <text class="d-label" x="188" y="55" text-anchor="middle">GET /.well-known/lnurlp/<user></text>96 <path class="d-msg blue" d="M300,92 L85,92"/>97 <text class="d-label" x="188" y="85" text-anchor="middle">allowsNostr, nostrPubkey, callback</text>98 <path class="d-msg" d="M80,128 L295,128"/>99 <text class="d-label" x="188" y="121" text-anchor="middle">GET callback?amount&nostr=9734&lnurl</text>100 <path class="d-msg blue" d="M300,158 L85,158"/>101 <text class="d-label" x="188" y="151" text-anchor="middle">{"pr": bolt11 invoice}</text>102 <path class="d-msg d-flow" d="M80,200 L295,200"/>103 <text class="d-label" x="188" y="193" text-anchor="middle">pay invoice (Lightning network)</text>104 <path class="d-msg d-flow" d="M300,240 L485,240"/>105 <text class="d-label" x="393" y="233" text-anchor="middle">["EVENT", kind 9735 receipt]</text>106 <text class="d-note" x="280" y="285" text-anchor="middle">Recipient and sender clients fetch the zap receipt from the relays.</text>107 <circle r="4" fill="#f26a00">108 <animateMotion dur="2.6s" repeatCount="indefinite" path="M80,200 L295,200"/>109 </circle>110 <circle r="4" fill="#f26a00">111 <animateMotion dur="2.6s" begin="1.3s" repeatCount="indefinite" path="M300,240 L485,240"/>112 </circle>113 </svg>114 </div>115 <figcaption>Fig. 57-1 — <b>The zap flow.</b> The zap request travels over HTTP, the payment over Lightning, and the receipt over Nostr relays.</figcaption>116 </figure>117118 <h2 class="bar" id="request"><span class="num">3</span> The Zap Request (kind 9734)</h2>119 <p>A zap request is an event of kind <code>9734</code>. It is <em>not</em> published to relays. The client sends it to the recipient's lnurl pay <code>callback</code> URL instead. The <code>content</code> MAY be an optional message to send along with the payment.</p>120 <p>The event MUST include these tags:</p>121 <div class="tscroll"><table class="ref">122 <caption>Required zap request tags</caption>123 <tr><th>Tag</th><th>Value</th><th>Rule</th></tr>124 <tr><td class="btn">relays</td><td>one or more relay URLs</td><td>The relays the recipient's wallet should publish its zap receipt to. Put each URL as its own element of the tag. Do not nest the relays in an additional list.</td></tr>125 <tr><td class="btn">amount</td><td>string, millisats</td><td>The amount the sender intends to pay, in <em>millisats</em>, formatted as a string. Recommended, but optional.</td></tr>126 <tr><td class="btn">lnurl</td><td>bech32 string</td><td>The lnurl pay URL of the recipient, encoded with bech32 and the prefix <code>lnurl</code>. Recommended, but optional.</td></tr>127 <tr><td class="btn">p</td><td>hex pubkey</td><td>The hex-encoded pubkey of the recipient.</td></tr>128 </table></div>129 <p>The event MAY include these tags:</p>130 <div class="tscroll"><table class="ref">131 <caption>Optional zap request tags</caption>132 <tr><th>Tag</th><th>Value</th><th>Rule</th></tr>133 <tr><td class="btn">e</td><td>hex event id</td><td>Optional. Clients MUST include it when they zap an event rather than a person.</td></tr>134 <tr><td class="btn">a</td><td>event coordinate</td><td>Optional. Allows tipping addressable events, such as <a href="23.html">NIP-23</a> long-form notes.</td></tr>135 <tr><td class="btn">k</td><td>string</td><td>The stringified kind of the target event.</td></tr>136 </table></div>137138 <div class="crt">139 <div class="titlebar">Example - zap request (kind 9734)</div>140<pre>{141 <span class="k">"kind"</span>: 9734,142 <span class="k">"content"</span>: <span class="s">"Zap!"</span>,143 <span class="k">"tags"</span>: [144 [<span class="s">"relays"</span>, <span class="s">"wss://nostr-pub.wellorder.com"</span>, <span class="s">"wss://anotherrelay.example.com"</span>],145 [<span class="s">"amount"</span>, <span class="s">"21000"</span>],146 [<span class="s">"lnurl"</span>, <span class="s">"lnurl1dp68gurn8ghj7um5v93kketj9ehx2amn9uh8wetvdskkkmn0wahz7mrww4excup0dajx2mrv92x9xp"</span>],147 [<span class="s">"p"</span>, <span class="s">"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9"</span>],148 [<span class="s">"e"</span>, <span class="s">"9ae37aa68f48645127299e9453eb5d908a0cbb6058ff340d528ed4d37c8994fb"</span>],149 [<span class="s">"k"</span>, <span class="s">"1"</span>]150 ],151 <span class="k">"pubkey"</span>: <span class="s">"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322"</span>,152 <span class="k">"created_at"</span>: 1679673265,153 <span class="k">"id"</span>: <span class="s">"30efed56a035b2549fcaeec0bf2c1595f9a9b3bb4b1a38abaf8ee9041c4b7d93"</span>,154 <span class="k">"sig"</span>: <span class="s">"f2cb581a84ed10e4dc84937bd98e27acac71ab057255f6aa8dfa561808c981fe8870f4a03c1e3666784d82a9c802d3704e174371aa13d63e2aeaf24ff5374d9d"</span>155}</pre>156 </div>157158 <h2 class="bar" id="http"><span class="num">4</span> Sending the Request over HTTP</h2>159 <p>The client sends the signed zap request with an HTTP GET request to the recipient's <code>callback</code> URL. The lnurl pay endpoint provided that URL in step 1. The request should define these query parameters:</p>160 <div class="tscroll"><table class="ref">161 <caption>Callback query parameters</caption>162 <tr><th>Parameter</th><th>Content</th></tr>163 <tr><td class="btn">amount</td><td>The amount in <em>millisats</em> the sender intends to pay.</td></tr>164 <tr><td class="btn">nostr</td><td>The kind <code>9734</code> zap request event, JSON encoded then URI encoded.</td></tr>165 <tr><td class="btn">lnurl</td><td>The lnurl pay URL of the recipient, encoded with bech32 and the prefix <code>lnurl</code>.</td></tr>166 </table></div>167 <p>The response is a JSON object with a <code>pr</code> key. The <code>pr</code> value is the invoice the sender must pay to finalize the zap. This flow looks like this in JavaScript:</p>168 <div class="crt">169 <div class="titlebar">Example - client-side flow (JavaScript)</div>170<pre><span class="c">// senderPubkey: the sender's pubkey</span>171<span class="c">// recipientPubkey: the recipient's pubkey</span>172<span class="c">// callback: from the recipient's lnurl pay endpoint</span>173<span class="c">// lnurl: the recipient's lightning address, encoded as a lnurl</span>174const sats = 21175176const amount = sats * 1000177const relays = [<span class="s">'wss://nostr-pub.wellorder.net'</span>]178const event = encodeURI(JSON.stringify(await signEvent({179 <span class="k">kind</span>: 9734,180 <span class="k">content</span>: <span class="s">""</span>,181 <span class="k">pubkey</span>: senderPubkey,182 <span class="k">created_at</span>: Math.round(Date.now() / 1000),183 <span class="k">tags</span>: [184 [<span class="s">"relays"</span>, ...relays],185 [<span class="s">"amount"</span>, amount.toString()],186 [<span class="s">"lnurl"</span>, lnurl],187 [<span class="s">"p"</span>, recipientPubkey],188 ],189})))190191const {pr: invoice} =192 await fetchJson(`${callback}?amount=${amount}&nostr=${event}&lnurl=${lnurl}`)</pre>193 </div>194195 <h2 class="bar" id="server"><span class="num">5</span> LNURL Server Configuration</h2>196 <p>An LNURL server needs two extra fields so that clients know it supports zap invoices. Add them to the lnurl-pay static endpoint <code>/.well-known/lnurlp/<user></code>:</p>197 <ol>198 <li>Add a <code>nostrPubkey</code> field. This is the Nostr pubkey your server uses to sign zap receipt events. Clients use it to validate zap receipts.</li>199 <li>Add an <code>allowsNostr</code> field and set it to <code>true</code>.</li>200 </ol>201 <div class="box hint">202 <span class="tag">Hint!</span>203 <p>The <code>nostrPubkey</code> belongs to the server, not to the user. One zap-enabled server signs every receipt it publishes with this one key. Clients compare a receipt's <code>pubkey</code> against it in §8.</p>204 </div>205206 <h2 class="bar" id="validation"><span class="num">6</span> Server-Side Validation</h2>207 <p>When a client calls the lnurl-pay callback URL, the <code>nostr</code> query parameter holds the zap request event, URI- and JSON-encoded. If the parameter is present, the server must validate the event with these rules:</p>208 <ol>209 <li>It MUST have a valid Nostr signature.</li>210 <li>It MUST have tags.</li>211 <li>It MUST have only one <code>p</code> tag.</li>212 <li>It MUST have 0 or 1 <code>e</code> tags.</li>213 <li>There should be a <code>relays</code> tag with the relays to send the zap receipt to.</li>214 <li>If there is an <code>amount</code> tag, it MUST equal the <code>amount</code> query parameter.</li>215 <li>If there is an <code>a</code> tag, it MUST be a valid event coordinate.</li>216 <li>There MUST be 0 or 1 <code>P</code> tags. If there is one, it MUST equal the zap receipt's <code>pubkey</code>.</li>217 </ol>218 <p>The server MUST then store the event for later use, when the invoice is paid.</p>219 <div class="box tech">220 <span class="tag">Tech Note</span>221 <p>Rule 8 is inconsistent with §7. Here, a <code>P</code> tag in the zap <em>request</em> must equal the zap receipt's <code>pubkey</code> — the LNURL server's <code>nostrPubkey</code>. In §7, the <code>P</code> tag of the zap <em>receipt</em> holds the zap sender's pubkey instead. The source spec defines no sender-side meaning for a <code>P</code> tag in a zap request. Implementations should treat rule 8 with care.</p>222 </div>223224 <h2 class="bar" id="receipt"><span class="num">7</span> The Zap Receipt (kind 9735)</h2>225 <p>A zap receipt is an event of kind <code>9735</code>. A Lightning node creates it when an invoice generated by a zap request is paid. Zap receipts are only created when the invoice description — committed to the description hash — contains a zap request note.</p>226 <p>When the payment arrives, the server executes these steps:</p>227 <ol>228 <li>Get the description for the invoice. Save it somewhere during the generation of the description hash invoice. CLN, the reference implementation here, saves it automatically.</li>229 <li>Parse the bolt11 description as a JSON Nostr event. This SHOULD be validated against the rules in §6, either when it is received, or before the invoice is paid.</li>230 <li>Create a Nostr event of kind <code>9735</code> as described below. Publish it to the <code>relays</code> declared in the zap request.</li>231 </ol>232 <p>The following should be true of the zap receipt event:</p>233 <ul>234 <li>The <code>content</code> SHOULD be empty.</li>235 <li>The <code>created_at</code> date SHOULD be set to the invoice <code>paid_at</code> date, for idempotency.</li>236 <li><code>tags</code> MUST include the <code>p</code> tag (zap recipient) AND the optional <code>e</code> tag from the zap request AND the optional <code>a</code> tag from the zap request AND the optional <code>P</code> tag from the pubkey of the zap request (zap sender).</li>237 <li>The receipt MUST have a <code>bolt11</code> tag that contains the description hash bolt11 invoice.</li>238 <li>The receipt MUST contain a <code>description</code> tag: the JSON-encoded zap request.</li>239 <li><code>SHA256(description)</code> SHOULD match the description hash in the bolt11 invoice.</li>240 <li>The receipt MAY contain a <code>preimage</code> tag to match against the payment hash of the bolt11 invoice. This is not really a payment proof. There is no real way to prove that the invoice is real or has been paid. You trust the author of the zap receipt for the legitimacy of the payment.</li>241 </ul>242 <p>A reference implementation for a zap-enabled LNURL server is <a href="https://github.com/jb55/cln-nostr-zapper">cln-nostr-zapper</a>.</p>243244 <figure class="diagram">245 <div class="frame">246 <svg viewBox="0 0 560 130" role="img" aria-label="The zap request is hashed with sha256 into the bolt11 description hash, and copied verbatim into the receipt's description tag">247 <rect class="d-actor" x="10" y="38" width="130" height="50" rx="8"/>248 <text class="d-actor-label" x="75" y="60" text-anchor="middle">Zap request</text>249 <text class="d-note" x="75" y="77" text-anchor="middle">kind 9734 JSON</text>250 <rect class="d-actor" x="212" y="38" width="100" height="50" rx="8"/>251 <text class="d-actor-label" x="262" y="60" text-anchor="middle">sha256</text>252 <text class="d-note" x="262" y="77" text-anchor="middle">description hash</text>253 <rect class="d-actor" x="392" y="10" width="158" height="44" rx="8"/>254 <text class="d-actor-label" x="471" y="32" text-anchor="middle">bolt11 invoice</text>255 <text class="d-note" x="471" y="46" text-anchor="middle">→ bolt11 tag</text>256 <rect class="d-actor" x="392" y="72" width="158" height="44" rx="8"/>257 <text class="d-actor-label" x="471" y="94" text-anchor="middle">Receipt kind 9735</text>258 <text class="d-note" x="471" y="108" text-anchor="middle">→ description tag</text>259 <path class="d-msg d-flow" d="M142,63 L209,63"/>260 <path class="d-msg d-flow" d="M314,55 C350,45 355,38 389,32"/>261 <path class="d-msg blue d-flow" d="M142,80 C250,120 300,98 389,94"/>262 <text class="d-label" x="345" y="30" text-anchor="middle">commit</text>263 <text class="d-label" x="265" y="112" text-anchor="middle">copy verbatim</text>264 </svg>265 </div>266 <figcaption>Fig. 57-2 — <b>The description hash link.</b> The invoice commits to the sha256 of the zap request. The receipt carries both the invoice and the request.</figcaption>267 </figure>268269 <div class="crt">270 <div class="titlebar">Example - zap receipt (kind 9735)</div>271<pre>{272 <span class="k">"id"</span>: <span class="s">"67b48a14fb66c60c8f9070bdeb37afdfcc3d08ad01989460448e4081eddda446"</span>,273 <span class="k">"pubkey"</span>: <span class="s">"9630f464cca6a5147aa8a35f0bcdd3ce485324e732fd39e09233b1d848238f31"</span>,274 <span class="k">"created_at"</span>: 1674164545,275 <span class="k">"kind"</span>: 9735,276 <span class="k">"tags"</span>: [277 [<span class="s">"p"</span>, <span class="s">"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"</span>],278 [<span class="s">"P"</span>, <span class="s">"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322"</span>],279 [<span class="s">"e"</span>, <span class="s">"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8"</span>],280 [<span class="s">"k"</span>, <span class="s">"1"</span>],281 [<span class="s">"bolt11"</span>, <span class="s">"lnbc10u1p3unwfusp5t9r3yymhpfqculx78u027lxspgxcr2n2987mx2j55nnfs95nxnzqpp5jmrh92pfld78spqs78v9euf2385t83uvpwk9ldrlvf6ch7tpascqhp5zvkrmemgth3tufcvflmzjzfvjt023nazlhljz2n9hattj4f8jq8qxqyjw5qcqpjrzjqtc4fc44feggv7065fqe5m4ytjarg3repr5j9el35xhmtfexc42yczarjuqqfzqqqqqqqqlgqqqqqqgq9q9qxpqysgq079nkq507a5tw7xgttmj4u990j7wfggtrasah5gd4ywfr2pjcn29383tphp4t48gquelz9z78p4cq7ml3nrrphw5w6eckhjwmhezhnqpy6gyf0"</span>],282 [<span class="s">"description"</span>, <span class="s">"{\"pubkey\":\"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322\",\"content\":\"\",\"id\":\"d9cc14d50fcb8c27539aacf776882942c1a11ea4472f8cdec1dea82fab66279d\",\"created_at\":1674164539,\"sig\":\"77127f636577e9029276be060332ea565deaf89ff215a494ccff16ae3f757065e2bc59b2e8c113dd407917a010b3abd36c8d7ad84c0e3ab7dab3a0b0caa9835d\",\"kind\":9734,\"tags\":[[\"e\",\"3624762a1274dd9636e0c552b53086d70bc88c165bc4dc0f9e836a1eaf86c3b8\"],[\"p\",\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\"],[\"relays\",\"wss://relay.damus.io\",\"wss://nostr-relay.wlvs.space\",\"wss://nostr.fmt.wiz.biz\",\"wss://relay.nostr.bg\",\"wss://nostr.oxtr.dev\",\"wss://nostr.v0l.io\",\"wss://brb.io\",\"wss://nostr.bitcoiner.social\",\"ws://monad.jb55.com:8080\",\"wss://relay.snort.social\"]]}"</span>],283 [<span class="s">"preimage"</span>, <span class="s">"5d006d2cf1e73c7148e7519a4c68adc81642ce0e25a432b2434c99f97344c15f"</span>]284 ],285 <span class="k">"content"</span>: <span class="s">""</span>286}</pre>287 </div>288 <div class="box tech">289 <span class="tag">Tech Note</span>290 <p>The source spec's receipt example is not valid JSON. It has a trailing comma after <code>content</code> and omits the <code>sig</code> field. A real kind <code>9735</code> event must carry a valid <code>sig</code> per <a href="01.html">NIP-01</a>. The example above fixes the comma but keeps the source's field set. The source also mentions the <code>lud16</code> profile field only in the flow; older <code>lud06</code> lookup was removed from the spec in 2025.</p>291 </div>292293 <h2 class="bar" id="verify"><span class="num">8</span> Client-Side Receipt Validation</h2>294 <p>A client can retrieve zap receipts on events and pubkeys with a <a href="01.html">NIP-01</a> filter, for example <code>{"kinds": [9735], "#e": [...]}</code>. Zaps MUST be validated with these steps:</p>295 <ul>296 <li>The zap receipt's <code>pubkey</code> MUST be the same as the recipient's lnurl provider's <code>nostrPubkey</code>, retrieved in step 1 of the protocol flow.</li>297 <li>The <code>invoiceAmount</code> in the receipt's <code>bolt11</code> tag MUST equal the <code>amount</code> tag of the zap request, if present.</li>298 <li>The <code>lnurl</code> tag of the zap request, if present, SHOULD equal the recipient's <code>lnurl</code>.</li>299 </ul>300301 <h2 class="bar" id="splits"><span class="num">9</span> Zap Splits: the <span class="kbd">zap</span> Tag</h2>302 <p>An event can carry one or more <code>zap</code> tags. Clients that want to zap such an event SHOULD calculate the lnurl pay request from these tags, not from the author's profile field. Each <code>zap</code> tag has this shape: the second element is the hex pubkey of a receiver. The third element is the relay to download the receiver's kind-0 metadata from. The optional fourth element is a <strong>weight</strong>: a generalization of a percentage.</p>303 <p>Clients should parse all weights, calculate their sum, and then a percentage for each receiver. If no weights are present, clients should divide the zap amount equally across all receivers. If weights are only partially present, receivers without a weight get <code>weight = 0</code> and are not zapped.</p>304 <div class="crt">305 <div class="titlebar">Example - zap split tags on an event</div>306<pre>{307 <span class="k">"tags"</span>: [308 [<span class="s">"zap"</span>, <span class="s">"82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2"</span>, <span class="s">"wss://nostr.oxtr.dev"</span>, <span class="s">"1"</span>], <span class="c">// 25%</span>309 [<span class="s">"zap"</span>, <span class="s">"fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52"</span>, <span class="s">"wss://nostr.wine/"</span>, <span class="s">"1"</span>], <span class="c">// 25%</span>310 [<span class="s">"zap"</span>, <span class="s">"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"</span>, <span class="s">"wss://nos.lol/"</span>, <span class="s">"2"</span>] <span class="c">// 50%</span>311 ]312}</pre>313 </div>314 <p>Clients MAY display the zap split configuration in the note.</p>315316 <figure class="diagram">317 <div class="frame">318 <svg viewBox="0 0 560 150" role="img" aria-label="A 21000 millisat zap splits by weights 1, 1, and 2 into 25 percent, 25 percent, and 50 percent shares">319 <rect class="d-actor" x="10" y="50" width="120" height="50" rx="8"/>320 <text class="d-actor-label" x="70" y="72" text-anchor="middle">Zap amount</text>321 <text class="d-note" x="70" y="89" text-anchor="middle">21000 msat</text>322 <rect x="250" y="14" width="90" height="26" rx="4" fill="#f26a00"/>323 <text class="d-label" x="295" y="31" text-anchor="middle" fill="#fff">weight 1</text>324 <text class="d-note" x="355" y="31">→ 25% - 5250 msat</text>325 <rect x="250" y="62" width="90" height="26" rx="4" fill="#f26a00"/>326 <text class="d-label" x="295" y="79" text-anchor="middle" fill="#fff">weight 1</text>327 <text class="d-note" x="355" y="79">→ 25% - 5250 msat</text>328 <rect x="250" y="110" width="180" height="26" rx="4" fill="#2e5f9e"/>329 <text class="d-label" x="340" y="127" text-anchor="middle" fill="#fff">weight 2</text>330 <text class="d-note" x="445" y="127">→ 50% - 10500 msat</text>331 <path class="d-msg d-flow" d="M132,65 C190,50 200,35 247,27"/>332 <path class="d-msg d-flow" d="M132,75 L247,75"/>333 <path class="d-msg d-flow" d="M132,85 C190,100 200,115 247,123"/>334 </svg>335 </div>336 <figcaption>Fig. 57-3 — <b>Zap split by weight.</b> Weights 1, 1, and 2 sum to 4. Each receiver gets its weight divided by the sum.</figcaption>337 </figure>338339 <h2 class="bar" id="examples"><span class="num">10</span> Worked Example</h2>340 <p>Alice zaps 21 sats to Bob's note. Her client already fetched Bob's lnurl endpoint and saw <code>allowsNostr: true</code>. The transcript shows the HTTP leg first, then the relay leg. Orange lines go out from a client. Blue lines come back.</p>341 <div class="crt">342 <div class="titlebar">Session transcript - HTTP callback + wss://relay.damus.io</div>343<pre><span class="c">-- 1. Alice's client asks Bob's LNURL server for an invoice --</span>344<span class="send">→ GET https://pay.bob.example/callback?amount=21000345 &nostr=%7B%22kind%22%3A9734%2C%22content%22%3A%22Zap!%22%2C...%7D346 &lnurl=lnurl1dp68gurn8ghj7...</span>347<span class="recv">← {"pr": "lnbc210n1p3unwfu...qy9qxpq"}</span>348349<span class="c">-- 2. Alice pays the bolt11 invoice with her Lightning wallet --</span>350351<span class="c">-- 3. Bob's LNURL server publishes the receipt to the request's relays --</span>352<span class="send">→ ["EVENT", {"kind":9735, "pubkey":"9630f464cca6…",353 "created_at":1674164545, <span class="c">// = invoice paid_at</span>354 "tags":[["p","32e18276…"], ["P","97c70a44…"], ["e","3624762a…"],355 ["bolt11","lnbc210n1p3unwfu…"], ["description","{\"kind\":9734,…}"],356 ["preimage","5d006d2c…"]],357 "content":"", "sig":"c2e9f3a1…"}]</span>358<span class="recv">← ["OK", "67b48a14fb…", true, ""]</span>359360<span class="c">-- 4. Carol's client fetches and validates zaps on Bob's note --</span>361<span class="send">→ ["REQ", "zaps", {"kinds":[9735], "#e":["3624762a1274dd96…"]}]</span>362<span class="recv">← ["EVENT", "zaps", {"kind":9735, "pubkey":"9630f464cca6…", …}]</span>363<span class="recv">← ["EOSE", "zaps"]</span>364<span class="c">-- Carol checks: pubkey == Bob's nostrPubkey; bolt11 amount == 21000 msat --</span></pre>365 </div>366 <div class="box hint">367 <span class="tag">Hint!</span>368 <p>Zaps can become more private in the future. Encrypt the zap request note to the target user, and only the recipient can read who paid. The spec leaves this out of the initial draft for simplicity.</p>369 </div>370371 <h2 class="bar" id="history"><span class="num">11</span> Version History</h2>372 <p>William Casarin proposed zaps in February 2023. Damus and Snort shipped them within weeks. Notable spec changes, newest first:</p>373 <ul class="patchnotes">374 <li><span class="ver">2025</span><span class="when">2025-09</span>Removed the <code>lud06</code> profile field from the lookup flow. Clients now decode <code>lud16</code> only.</li>375 <li><span class="ver">2025</span><span class="when">2025-02</span>Relaxed the description hash check: <code>SHA256(description)</code> SHOULD (no longer MUST) match the bolt11 invoice.</li>376 <li><span class="ver">2024</span><span class="when">2024-01</span>Added the <code>P</code> tag so zap receipts index the zap sender.</li>377 <li><span class="ver">2023</span><span class="when">2023-09</span>Zap receipts must copy the optional <code>a</code> tag from the zap request.</li>378 <li><span class="ver">2023</span><span class="when">2023-05</span>Added zap splits with weighted <code>zap</code> tags. Renamed "zap note" to "zap receipt".</li>379 <li><span class="ver">2023</span><span class="when">2023-03</span>Full rewrite: consolidated the protocol flow and the appendix structure, clarified terminology.</li>380 <li class="minor"><span class="ver">2023</span><span class="when">2023-02</span>Added the <code>amount</code> tag and the <code>a</code> tag for addressable event coordinates.</li>381 <li><span class="ver">Origin</span><span class="when">2023-02-13</span>William Casarin published NIP-57 (#224).</li>382 </ul>383384 <div class="credits">385 <h3 class="cardhead">Original Proposal - Credits</h3>386 <table>387 <tr><td>Author</td><td>William Casarin (jb55)</td></tr>388 <tr><td>First commit</td><td>2023-02-13 — “NIP-57: Lightning Zaps (#224)”</td></tr>389 <tr><td>Pull request</td><td><a href="https://github.com/nostr-protocol/nips/pull/224">nostr-protocol/nips#224</a></td></tr>390 <tr><td>Status</td><td>draft - optional — clients and LNURL servers may implement it</td></tr>391 <tr><td>Source</td><td><a href="https://github.com/nostr-protocol/nips/blob/master/57.md">57.md on GitHub</a> - <a href="https://github.com/nostr-protocol/nips/commits/master/57.md">full change history</a></td></tr>392 </table>393 </div>394395 <h2 class="bar"><span class="num">12</span> See Also</h2>396 <ul>397 <li><a href="01.html">NIP-01</a> — event format, filters, and the relay messages used to fetch receipts.</li>398 <li><a href="23.html">NIP-23</a> — long-form notes; zap them through the <code>a</code> coordinate tag.</li>399 <li><a href="47.html">NIP-47</a> — Nostr Wallet Connect; lets a client pay the zap invoice through a remote wallet.</li>400 <li><a href="19.html">NIP-19</a> — bech32 encodings; the <code>lnurl</code> tag uses the same bech32 alphabet with an <code>lnurl</code> prefix.</li>401 </ul>402 </div>403404 <aside>405 <div class="cartridge">406 <div class="inner">407 <h3 class="cardhead">Spec Card</h3>408 <div class="lights"><span class="led on"></span><span class="led on"></span><span class="led on"></span></div>409 <dl>410 <dt>Status</dt><dd><span class="chip draft">draft</span><span class="chip">optional</span></dd>411 <dt>Defines</dt><dd>zap request - zap receipt - bolt11/description/preimage/P tags - zap split tag</dd>412 <dt>Kinds</dt><dd>9734 (request, unpublished) - 9735 (receipt)</dd>413 <dt>Requires</dt><dd><a href="01.html">NIP-01</a> - LNURL LUD06/LUD16</dd>414 <dt>Transport</dt><dd>HTTPS GET (request/invoice) - WebSocket (receipt)</dd>415 <dt>Crypto</dt><dd>BIP-340 Schnorr - sha256 description hash - bolt11</dd>416 <dt>First commit</dt><dd>2023-02-13</dd>417 <dt>Last updated</dt><dd>2026-06-13</dd>418 </dl>419 </div>420 </div>421 </aside>422 </main>423424 <nav class="navbtns">425 <a href="56.html">◀ NIP-56: Reporting</a>426 <a href="58.html">NIP-58: Badges ▶</a>427 </nav>428429 <footer class="pagefoot">430 <a href="../index.html">Nostr Protocol Manual</a>431 <span class="pageno">NIP 57 / F4</span>432 </footer>433</div>434435<script src="../assets/webmcp.js" defer></script>436</body>437</html>