Skip to main content

Installation

cargo add l402kit
Or in Cargo.toml:
[dependencies]
l402kit = "1.0"

Quick start

use axum::{Router, routing::get, Json};
use l402kit::l402_middleware;
use serde_json::{json, Value};

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/api/data", get(handler))
        .layer(l402_middleware(l402kit::Options {
            price_sats: 10,
            lightning_address: "you@blink.sv".into(),
        }));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn handler() -> Json<Value> {
    Json(json!({ "data": "premium content" }))
}

l402kit::Options

FieldTypeRequiredDescription
price_satsu64Amount in satoshis per call
lightning_addressStringYour Lightning address (you@blink.sv)
invoice_expiryu32Invoice TTL in seconds (default: 3600)

l402_middleware(opts: Options)

Returns an axum::middleware::Layer compatible with axum 0.8+. First call (no token):
HTTP 402 Payment Required
WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."
Subsequent call (with payment proof):
Authorization: L402 <macaroon>:<preimage>
→ HTTP 200 OK

Verification

The middleware verifies SHA256(preimage) == paymentHash using the sha2 crate — no external call on the hot path.

Features

The crate has a single feature flag:
[features]
default = ["axum-middleware"]
axum-middleware = ["dep:axum", "dep:reqwest", "dep:http"]
Disable axum-middleware if you only need the core verify_token function.

Error codes

StatusMeaning
402No payment token — pay the invoice
401Invalid preimage (wrong payment proof)
401Replayed token (already used)

Contact

shinydapps@gmail.com · crates.io/crates/l402kit