Skip to main content

Payment Layer

l402-kit is a soberano middleware that adds a Bitcoin Lightning paywall to any HTTP endpoint in 3 lines of code. You bring your own Lightning provider — funds go directly to your wallet, no intermediary required.

Protocol: L402

L402 is an open standard that extends HTTP/1.1 with a native payment handshake:
Client → GET /api/data
Server ← 402 Payment Required
         WWW-Authenticate: L402 <macaroon>, invoice="<BOLT11>"

Client pays invoice via Lightning wallet
Client → GET /api/data
         Authorization: L402 <macaroon>:<preimage>
Server ← 200 OK + data
The macaroon is a capability token bound to the invoice’s paymentHash. The preimage is the cryptographic secret released by the Lightning node when payment settles. The server verifies:
SHA256(preimage) == paymentHash ✓
No account, no session, no JWT — the preimage is the proof of payment.

Invoice Creation Flow

Your API receives a request without valid Authorization


l402-kit middleware calls lightning.createInvoice(priceSats)
     │  (your provider: Blink, Alby, OpenNode, BTCPay, LNbits…)

Provider returns BOLT11 invoice + paymentHash


Middleware builds macaroon: base64({ hash: paymentHash, exp: now+1h })


Your API returns 402 + invoice + macaroon to client

Payment Verification Flow

Client pays BOLT11 invoice via any Lightning wallet


Lightning node releases preimage (32-byte secret)


Client sends Authorization: L402 <macaroon>:<preimage>


Middleware verifies locally — no network call:
  1. Decode macaroon (base64 → JSON { hash, exp })
  2. Check exp > now()
  3. SHA256(preimage) === hash ✓


Request passes through to your API handler → 200 OK
Verification is O(1) — pure cryptography, no database lookup on the hot path. Replay protection (Supabase payment_hash logging) runs asynchronously and does not block the request.

Macaroon format

l402-kit uses a lightweight custom macaroon — not libmacaroon. The token is a base64url-encoded JSON object:
{ "hash": "<paymentHash hex>", "exp": <unix timestamp> }
This is simpler and auditable without any external library. The Authorization header format is:
Authorization: L402 <base64url-macaroon>:<preimage-hex>

Fee Model

ModeFeeSetup
Soberano (any provider)0% — you keep 100%Bring your own provider credentials
Managed (ManagedProvider)0.3% to l402kit.comNo Lightning node — works immediately
Soberano mode is the default. Managed mode is an explicit opt-in:
// Soberano — 0% fee, you keep 100%
import { AlbyProvider } from 'l402-kit';
const lightning = new AlbyProvider(process.env.ALBY_TOKEN!);

// Managed — 0.3% fee, no Lightning node needed
import { ManagedProvider } from 'l402-kit';
const lightning = ManagedProvider.fromAddress('you@yourdomain.com');

Data Storage (optional — Supabase)

Set SUPABASE_URL + SUPABASE_ANON_KEY to log payments automatically:
create table payments (
  id            uuid primary key default gen_random_uuid(),
  payment_hash  text unique not null,  -- SHA256(preimage) — safe to store
  endpoint      text,
  amount_sats   integer,
  paid_at       timestamptz default now()
);
Why payment_hash instead of preimage? The payment_hash is already embedded in every BOLT11 invoice — it’s public by design. Only the preimage is secret. Storing the hash gives replay protection with zero additional exposure.

Lightning Providers

l402-kit is provider-agnostic. Any backend that implements LightningProvider works:
ProviderNotes
Alby HubSelf-custodial, 0% fee
BlinkFree custodial, no KYC for small amounts
BTCPay ServerSelf-hosted, full soberanoty
OpenNodeCustodial, no setup
LNbitsSelf-hosted or cloud
See the TypeScript SDK or Python SDK for provider setup.

Security Guarantees

ThreatMitigation
Replay attackPreimage marked used after first verification — in-memory or Redis adapter
Fake preimageSHA256(preimage) === paymentHash is cryptographically unforgeable
Token expiryMacaroon embeds exp timestamp — verified on every request
Webhook spoofingHMAC-SHA256(secret, body) verified before processing