Lightning Network Payments
ngx_l402 implements the L402 protocol, enabling API monetization via Lightning Network payments. When a client hits a protected endpoint without a valid token, the module responds with 402 Payment Required and a Lightning invoice. The client pays the invoice, receives a preimage, and presents it alongside the macaroon to gain access.
Supported Backends
Configure the backend via the LN_CLIENT_TYPE environment variable:
LN_CLIENT_TYPE | Description |
|---|---|
LND | Lightning Network Daemon — direct gRPC connection |
LND + LNC_PAIRING_PHRASE | Lightning Node Connect — remote LND via mailbox (no open port needed) |
CLN | Core Lightning |
ECLAIR | Eclair node |
LNURL | Lightning Network URL — delegate invoice generation to an LNURL server |
NWC | Nostr Wallet Connect |
BOLT12 | Reusable Lightning Offers (BOLT12) |
See Environment Variables for the full list of per-backend settings.
Each worker connects to the backend on the first request that needs it, so an unreachable node does not stop nginx from starting. While it is down, requests that need a new invoice return 500 (counted in l402_invoices_generation_errors_total), and so do auto-detect retries whose preimage isn’t cached in Redis. A full macaroon:preimage credential still verifies without the node.
Payment Flow
- Client requests a protected endpoint (no auth header).
- Module generates a macaroon and requests an invoice from the configured Lightning backend.
- Module responds
402 Payment Requiredwith:WWW-Authenticate: L402 macaroon="<macaroon>", invoice="<bolt11>" - Client pays the invoice and obtains the preimage.
- Client retries with:
Authorization: L402 <macaroon>:<preimage> - Module verifies the macaroon + preimage and returns
200 OK.
Authorization Header Format
Two formats are accepted:
| Format | Header value | When to use |
|---|---|---|
| Classic | L402 <macaroon>:<preimage_hex> | Client has the preimage (standard wallet flow) |
| Auto-detect | L402 <macaroon> | Server queries the node; no preimage needed from client |
The preimage in the classic format must be the 32-byte (256-bit) hex-encoded payment preimage corresponding to the invoice’s
payment_hash.
Auto-Detect Payment (Server-Side Settlement Lookup)
With auto-detect enabled the client only needs to send the macaroon — no preimage required. The module queries your Lightning node directly to check whether the invoice is settled and retrieves the preimage from the node.
Enabling auto-detect
Add l402_auto_detect_payment on to any location {} block:
location /protected {
l402 on;
l402_amount_msat_default 10000;
l402_macaroon_timeout 0;
l402_auto_detect_payment on; # ← enables server-side lookup
}
All boolean directives (l402, l402_auto_detect_payment) accept: on / off / true / false / 1 / 0 / yes / no (case-insensitive).
Client flow with auto-detect enabled
- Client requests a protected endpoint → receives
402 Payment Requiredwith an invoice. - Client pays the invoice (no preimage handling needed).
- Client retries with just the macaroon:
Authorization: L402 <macaroon> - Module extracts the
payment_hashfrom the macaroon identifier, queries the node, and — if the invoice is settled — uses the returned preimage to verify the macaroon signature. - On success the module returns
200 OK. If the invoice is not yet settled, it returns402 Payment Required. If the lookup fails or takes longer than 5 seconds, it returns500.
Preimage caching (Redis)
When Redis is configured (REDIS_URL), settled preimages are cached under the key l402:settled:<payment_hash_hex>. Subsequent requests for the same payment hash are served from the cache, avoiding repeated node round-trips.
Backend support matrix
LN_CLIENT_TYPE | Auto-detect supported | Notes |
|---|---|---|
LND | ✅ | Uses LookupInvoice gRPC |
CLN | ✅ | Uses listinvoices JSON-RPC over unix socket |
BOLT12 | ✅ | Uses listinvoices like CLN, so BOLT12_OFFER must be an offer from your own node |
ECLAIR | ✅ | Uses POST /getreceivedinfo REST API |
NWC | ⚠️ | Uses NIP-47 lookup_invoice, which wallets may not implement |
LND over LNC | ❌ | LNC mailbox does not expose LookupInvoice |
LNURL | ❌ | Remote wallet — no server-side query API |
Note
Even when
l402_auto_detect_payment onis set, the classicL402 <macaroon>:<preimage>format is still accepted — auto-detect only activates when the client omits the preimage.
Wallet Compatibility
Note
NWC works for accepting payments, not just paying. A common misconception is that Nostr Wallet Connect (NIP-47) is only a spending protocol.
LN_CLIENT_TYPE=NWCuses the connection to receive: it creates each L402 invoice withmake_invoiceand verifies payment withlookup_invoice. The catch is that both are optional NIP-47 methods — not every wallet implements them, and a given connection secret may be permission-scoped (e.g. pay-only). Use a wallet and connection that grantmake_invoiceandlookup_invoice(a wallet advertises its supported commands in its NIP-47 info event); a pay-only connection can spend but cannot accept.
Warning
Some wallets (e.g. Wallet of Satoshi) return 48-byte non-standard preimages, which are not compatible with this module. Use a wallet that returns a standard 32-byte preimage.
Also Supported: Cashu eCash
In addition to Lightning, the module accepts Cashu eCash tokens via the X-Cashu header as an alternative payment method. See Cashu eCash Support for details.