Skip to main content

Instalación

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

Inicio rápido

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)
}
Obtén credenciales de Blink gratis en dashboard.blink.sv. Los pagos van directamente a tu billetera.

Modo autoalojado (trae tu propio proveedor)

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

CampoTipoPredeterminadoDescripción
PriceSatsintrequeridoPrecio por llamada en satoshis
LightningLightningProviderrequeridoTu backend de Lightning (usa NewBlinkProvider)
OnPaymentfunc(L402Token, int)Callback ejecutado después de cada pago verificado

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

Devuelve un http.Handler compatible con net/http, Chi, Gorilla Mux y cualquier framework HTTP estándar de Go.

Comportamiento

SolicitudRespuesta
Sin encabezado Authorization402 + WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."
L402 <macaroon>:<preimage> válidonext.ServeHTTP(w, r)
Token inválido o expirado401 Unauthorized
preimage reproducido401 Token already used

Cuerpo de respuesta 402

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

Interfaz 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))

Verificación

SHA256(preimage) == paymentHash se verifica localmente en memoria — sub-milisegundo, sin llamada de red en la ruta crítica. La expiración del token (campo exp en el macaroon) se comprueba en la misma operación. El guardián de reproducción usa un mapa en memoria por defecto (seguro para un único proceso). Para despliegues con múltiples instancias, utiliza un almacén compartido (Redis o similar) — lo que añade un tiempo de ida y vuelta de 5–50 ms por solicitud verificada.

Ejemplo con router Chi

l402.Middleware devuelve un http.Handler estándar, por lo que funciona directamente con r.Handle de 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)))

Códigos de error

EstadoSignificado
402Sin token de pago — paga la factura
401Token inválido o macaroon expirado
401Token reproducido (preimage ya utilizado)

Ejecución

go run main.go

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

# Pay invoice, then:
curl -H "Authorization: L402 <macaroon>:<preimage>" http://localhost:8080/api/data