A live reference app for Native L402 integration on Node + Express. Curl it from your terminal and watch a 402 Payment Required come back with a real Lightning invoice. Then pay it and curl again — your second request gets through.
The whole integration is 3 lines of code.
Live URL: (deploy your own — see below)
# Health check — free, ungated
curl -i $URL/api/free/health
# Premium endpoint — 100 sats per request
curl -i $URL/api/premium/weather?city=miami
# Variable pricing — premium model costs 500 sats
curl -i $URL/api/premium/llm?prompt=hello&model=premiumgit clone https://github.com/refined-element/l402-example-node
cd l402-example-node
npm install
cp .env.example .env
# edit .env, fill in LIGHTNING_ENABLE_API_KEY
# (generate one at https://api.lightningenable.com/dashboard/settings)
npm run devThen in another terminal:
curl -i http://localhost:3000/api/premium/weatherYou should see something like:
HTTP/1.1 402 Payment Required
Content-Type: application/json
WWW-Authenticate: L402 macaroon="AgEL...", invoice="lnbc1u..."
{
"error": "Payment Required",
"l402": {
"macaroon": "AgEL...",
"invoice": "lnbc1u...",
"amount_sats": 100,
"payment_hash": "abc123...",
"expires_at": "2026-05-12T01:00:00Z",
"resource": "/api/premium/weather"
}
}Pay that Lightning invoice with any Lightning wallet (Phoenix, Muun, Zeus, Alby) to get a preimage, then retry:
curl -i http://localhost:3000/api/premium/weather \
-H 'Authorization: L402 AgEL...:<your-preimage-hex>'You'll get the real weather response.
The whole L402 integration is one import and one app.use():
// src/server.ts
import express from "express";
import { l402 } from "l402-express"; // ← 1. import
const app = express();
app.use( // ← 2. mount the middleware
"/api/premium",
l402({
apiKey: process.env.LIGHTNING_ENABLE_API_KEY!,
priceSats: (req) => req.query.model === "premium" ? 500 : 100,
}),
);
app.get("/api/premium/weather", (req, res) => { // ← 3. your normal route
res.json({ temp: 72, city: req.query.city ?? "Miami" });
});The 30 lines around it are vanilla Express. The l402-express middleware handles:
- Reading the
Authorization: L402 ...header - Minting a Lightning invoice and macaroon via Lightning Enable's hosted API
- Sending
402 Payment Requiredwith the invoice - Verifying the preimage when the client retries
- Stashing the verified credential on
res.locals.l402for your handler to read
See src/server.ts for the whole file (under 100 lines).
- Free + paid routes in the same app —
/api/free/*passes through;/api/premium/*is gated - Function-form variable pricing —
priceSats: (req) => req.query.model === "premium" ? 500 : 100 - Two paid endpoints —
/weatherand/llm(mock data; swap in your real provider) res.locals.l402access — handlers read the verified credential and echo it in the response
Two zero-config deploy options included:
- Fork this repo
- Visit https://render.com/deploy and connect your fork
- Render reads
render.yamland provisions a free web service - Set
LIGHTNING_ENABLE_API_KEYin the Render dashboard env vars (don't commit it) - Done — your URL is
https://l402-example-node.onrender.com
fly launch --copy-config --no-deploy
fly secrets set LIGHTNING_ENABLE_API_KEY=<your-key>
fly deployBoth configs (render.yaml, fly.toml, Dockerfile) are in this repo.
npm run dev # tsx watch — auto-reload on saveThings to try:
- Add a new paid endpoint at
/api/premium/your-thing - Tweak the price function — by user (header), by time of day, by model
- Add a free
/api/free/manifest.jsonexposing your endpoint catalog - Wire up a real upstream API behind the L402 gate
When you graduate from this example to your real API:
- Get
LIGHTNING_ENABLE_API_KEYfrom your real merchant account, not a test key - Verify your payment provider (Strike or OpenNode) is configured in the Lightning Enable dashboard
- Make sure your reverse proxy / load balancer forwards the
Authorizationheader - Add structured logging —
res.locals.l402haspaymentHash,amountSats,resourcefor usage analytics - Decide if you want a custom
onInvalidTokenhandler (e.g., to send fresh 402 instead of 401)
- This app: https://github.com/refined-element/l402-example-node
- The middleware: https://github.com/refined-element/le-server-l402-express-node (
l402-expresson npm) - The SDK: https://github.com/refined-element/le-server-l402-node (
l402-serveron npm) - Lightning Enable docs: https://docs.lightningenable.com/products/l402-microtransactions/native-integration
MIT