Skip to main content

Installazione

go get github.com/shinydapps/l402-kit/go@v1.10.0
Requisiti: Go 1.21+

Avvio rapido

package main

import (
    "encoding/json"
    "net/http"
    "os"

    l402 "github.com/shinydapps/l402-kit/go"
)

func main() {
    lightning := l402.NewBlinkProvider(
        os.Getenv("BLINK_API_KEY"),
        os.Getenv("BLINK_WALLET_ID"),
    )

    mux := http.NewServeMux()
    mux.Handle("/api/data", l402.Middleware(l402.Options{
        PriceSats: 10,
        Lightning: lightning,
    }, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{"data": "premium content"})
    })))

    http.ListenAndServe(":8080", mux)
}
Ottieni le credenziali Blink gratuitamente su dashboard.blink.sv. I pagamenti vanno direttamente al tuo portafoglio.

Modalità self-hosted (porta il tuo provider)

import l402 "github.com/shinydapps/l402-kit/go"

type MyProvider struct{}

func (p *MyProvider) CreateInvoice(ctx context.Context, amountSats int) (l402.Invoice, error) {
    // create invoice with your Lightning node
    // return l402.Invoice{PaymentRequest: "lnbc...", PaymentHash: "...", Macaroon: "..."}
}

mux.Handle("/api/data", l402.Middleware(l402.Options{
    PriceSats: 10,
    Lightning: &MyProvider{},
}, handler))

l402.Options

CampoTipoPredefinitoDescrizione
PriceSatsintobbligatorioPrezzo per chiamata in satoshi
LightningLightningProviderobbligatorioIl tuo backend Lightning (usa NewBlinkProvider)
OnPaymentfunc(L402Token, int)Callback eseguito dopo ogni pagamento verificato

l402.Middleware(opts Options, next http.Handler) http.Handler

Restituisce un http.Handler compatibile con net/http, Chi, Gorilla Mux e qualsiasi framework HTTP Go standard.

Comportamento

RichiestaRisposta
Nessun header Authorization402 + WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."
L402 <macaroon>:<preimage> validonext.ServeHTTP(w, r)
Token non valido o scaduto401 Unauthorized
preimage già utilizzato401 Token already used

Corpo della risposta 402

{
  "error": "Payment Required",
  "invoice": "lnbc100n1...",
  "macaroon": "eyJoYXNoIjoiYWJjMTIzIiwiZXhwIjoxNzAwMDAwMDAwfQ==",
  "price_sats": 10
}

Interfaccia LightningProvider

type LightningProvider interface {
    CreateInvoice(ctx context.Context, amountSats int) (Invoice, error)
}

Callback OnPayment

lightning := l402.NewBlinkProvider(os.Getenv("BLINK_API_KEY"), os.Getenv("BLINK_WALLET_ID"))

mux.Handle("/api/data", l402.Middleware(l402.Options{
    PriceSats: 10,
    Lightning: lightning,
    OnPayment: func(token l402.L402Token, amountSats int) {
        log.Printf("payment: %d sats, preimage: %s", amountSats, token.Preimage)
    },
}, handler))

Verifica

SHA256(preimage) == paymentHash viene verificato localmente in memoria — sub-millisecondo, nessuna chiamata di rete sul percorso critico. La scadenza del token (campo exp nel macaroon) viene controllata nella stessa operazione. Il controllo anti-replay utilizza una mappa in memoria per impostazione predefinita (sicuro per un singolo processo). Per distribuzioni multi-istanza, utilizza uno store condiviso (Redis o simile) — che aggiunge un round-trip di 5–50 ms per ogni richiesta verificata.

Esempio con router Chi

l402.Middleware restituisce un http.Handler standard, quindi funziona direttamente con r.Handle di Chi:
import (
    "github.com/go-chi/chi/v5"
    l402 "github.com/shinydapps/l402-kit/go"
)

r := chi.NewRouter()
lightning := l402.NewBlinkProvider(os.Getenv("BLINK_API_KEY"), os.Getenv("BLINK_WALLET_ID"))

r.Handle("/api/data", l402.Middleware(l402.Options{
    PriceSats: 10,
    Lightning: lightning,
}, http.HandlerFunc(handler)))

Codici di errore

StatoSignificato
402Nessun token di pagamento — paga la fattura
401Token non valido o macaroon scaduto
401Token già utilizzato (preimage già usato)

Avvio

go run main.go

# Test — genera un 402
curl http://localhost:8080/api/data

# Paga la fattura, poi:
curl -H "Authorization: L402 <macaroon>:<preimage>" http://localhost:8080/api/data