SDKsGoHTTP Frameworks

Go HTTP Frameworks

The Go SDK provides payment middleware for 4 HTTP frameworks: Gin, Echo, Chi, and Fiber. Each middleware shares the same Config struct pattern and RoutesConfig for payment requirements.

Framework Overview

FrameworkPackageMiddleware FunctionType
Ginhttp/ginT402Payment(Config)gin.HandlerFunc
Echohttp/echoT402Payment(Config)echo.MiddlewareFunc
Chihttp/chiT402Payment(Config)func(http.Handler) http.Handler
Fiberhttp/fiberT402Payment(Config)fiber.Handler

Gin

package main
 
import (
    "github.com/gin-gonic/gin"
    t402 "github.com/t402-io/t402/sdks/go"
    t402http "github.com/t402-io/t402/sdks/go/http"
    ginmw "github.com/t402-io/t402/sdks/go/http/gin"
    evm "github.com/t402-io/t402/sdks/go/mechanisms/evm/exact/server"
)
 
func main() {
    r := gin.Default()
 
    routes := t402http.RoutesConfig{
        "GET /api/premium": {
            Accepts: t402http.PaymentOptions{
                {Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:8453"},
            },
            Description: "Premium content",
            MimeType:    "application/json",
        },
    }
 
    facilitator := t402http.NewHTTPFacilitatorClient(&t402http.FacilitatorConfig{
        URL: "https://facilitator.t402.io",
    })
 
    r.Use(ginmw.T402Payment(ginmw.Config{
        Routes:      routes,
        Facilitator: facilitator,
        Schemes: []ginmw.SchemeConfig{
            {Network: "eip155:8453", Server: evm.NewExactEvmScheme()},
        },
    }))
 
    r.GET("/api/premium", func(c *gin.Context) {
        c.JSON(200, gin.H{"data": "Premium content!"})
    })
 
    r.Run(":8080")
}

Gin Config

type Config struct {
    Routes                 t402http.RoutesConfig
    Facilitator            t402.FacilitatorClient
    Facilitators           []t402.FacilitatorClient
    Schemes                []SchemeConfig
    PaywallConfig          *t402http.PaywallConfig
    SyncFacilitatorOnStart bool
    Timeout                time.Duration
    ErrorHandler           func(*gin.Context, error)
    SettlementHandler      func(*gin.Context, *t402.SettleResponse)
}

Echo

package main
 
import (
    "github.com/labstack/echo/v4"
    t402 "github.com/t402-io/t402/sdks/go"
    t402http "github.com/t402-io/t402/sdks/go/http"
    echomw "github.com/t402-io/t402/sdks/go/http/echo"
    evm "github.com/t402-io/t402/sdks/go/mechanisms/evm/exact/server"
)
 
func main() {
    e := echo.New()
 
    routes := t402http.RoutesConfig{
        "GET /api/premium": {
            Accepts: t402http.PaymentOptions{
                {Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:8453"},
            },
            Description: "Premium content",
            MimeType:    "application/json",
        },
    }
 
    facilitator := t402http.NewHTTPFacilitatorClient(&t402http.FacilitatorConfig{
        URL: "https://facilitator.t402.io",
    })
 
    e.Use(echomw.T402Payment(echomw.Config{
        Routes:      routes,
        Facilitator: facilitator,
        Schemes: []echomw.SchemeConfig{
            {Network: "eip155:8453", Server: evm.NewExactEvmScheme()},
        },
    }))
 
    e.GET("/api/premium", func(c echo.Context) error {
        return c.JSON(200, map[string]string{"data": "Premium content!"})
    })
 
    e.Start(":8080")
}

Echo Config

type Config struct {
    Routes                 t402http.RoutesConfig
    Facilitator            t402.FacilitatorClient
    Facilitators           []t402.FacilitatorClient
    Schemes                []SchemeConfig
    PaywallConfig          *t402http.PaywallConfig
    SyncFacilitatorOnStart bool
    Timeout                time.Duration
    ErrorHandler           func(echo.Context, error) error
    SettlementHandler      func(echo.Context, *t402.SettleResponse)
}

Chi

Chi uses standard net/http types, making it compatible with any http.Handler-based router.

package main
 
import (
    "encoding/json"
    "net/http"
 
    "github.com/go-chi/chi/v5"
    t402 "github.com/t402-io/t402/sdks/go"
    t402http "github.com/t402-io/t402/sdks/go/http"
    chimw "github.com/t402-io/t402/sdks/go/http/chi"
    evm "github.com/t402-io/t402/sdks/go/mechanisms/evm/exact/server"
)
 
func main() {
    r := chi.NewRouter()
 
    routes := t402http.RoutesConfig{
        "GET /api/premium": {
            Accepts: t402http.PaymentOptions{
                {Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:8453"},
            },
            Description: "Premium content",
            MimeType:    "application/json",
        },
    }
 
    facilitator := t402http.NewHTTPFacilitatorClient(&t402http.FacilitatorConfig{
        URL: "https://facilitator.t402.io",
    })
 
    r.Use(chimw.T402Payment(chimw.Config{
        Routes:      routes,
        Facilitator: facilitator,
        Schemes: []chimw.SchemeConfig{
            {Network: "eip155:8453", Server: evm.NewExactEvmScheme()},
        },
    }))
 
    r.Get("/api/premium", func(w http.ResponseWriter, r *http.Request) {
        json.NewEncoder(w).Encode(map[string]string{"data": "Premium content!"})
    })
 
    http.ListenAndServe(":8080", r)
}

Chi Config

type Config struct {
    Routes                 t402http.RoutesConfig
    Facilitator            t402.FacilitatorClient
    Facilitators           []t402.FacilitatorClient
    Schemes                []SchemeConfig
    PaywallConfig          *t402http.PaywallConfig
    SyncFacilitatorOnStart bool
    Timeout                time.Duration
    ErrorHandler           func(http.ResponseWriter, *http.Request, error)
    SettlementHandler      func(http.ResponseWriter, *http.Request, *t402.SettleResponse)
}

The Chi middleware also works with any net/http compatible router, including Go’s built-in http.ServeMux (Go 1.22+).

Fiber

package main
 
import (
    "github.com/gofiber/fiber/v2"
    t402 "github.com/t402-io/t402/sdks/go"
    t402http "github.com/t402-io/t402/sdks/go/http"
    fibermw "github.com/t402-io/t402/sdks/go/http/fiber"
    evm "github.com/t402-io/t402/sdks/go/mechanisms/evm/exact/server"
)
 
func main() {
    app := fiber.New()
 
    routes := t402http.RoutesConfig{
        "GET /api/premium": {
            Accepts: t402http.PaymentOptions{
                {Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:8453"},
            },
            Description: "Premium content",
            MimeType:    "application/json",
        },
    }
 
    facilitator := t402http.NewHTTPFacilitatorClient(&t402http.FacilitatorConfig{
        URL: "https://facilitator.t402.io",
    })
 
    app.Use(fibermw.T402Payment(fibermw.Config{
        Routes:      routes,
        Facilitator: facilitator,
        Schemes: []fibermw.SchemeConfig{
            {Network: "eip155:8453", Server: evm.NewExactEvmScheme()},
        },
    }))
 
    app.Get("/api/premium", func(c *fiber.Ctx) error {
        return c.JSON(fiber.Map{"data": "Premium content!"})
    })
 
    app.Listen(":8080")
}

Fiber Config

type Config struct {
    Routes                 t402http.RoutesConfig
    Facilitator            t402.FacilitatorClient
    Facilitators           []t402.FacilitatorClient
    Schemes                []SchemeConfig
    PaywallConfig          *t402http.PaywallConfig
    SyncFacilitatorOnStart bool
    Timeout                time.Duration
    ErrorHandler           func(*fiber.Ctx, error) error
    SettlementHandler      func(*fiber.Ctx, *t402.SettleResponse)
}

Shared Configuration

All four middleware implementations share the same RoutesConfig and PaymentOptions types from the t402http package.

Route Configuration

routes := t402http.RoutesConfig{
    "GET /resource":     {...},  // Exact path match
    "GET /users/*":      {...},  // Wildcard suffix
    "POST /api/compute": {...},  // Specific method
    "*":                 {...},  // Catch-all
}

Multi-Network Support

Accept payments on multiple blockchains for a single route:

routes := t402http.RoutesConfig{
    "GET /api/data": {
        Accepts: t402http.PaymentOptions{
            {Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:8453"},
            {Scheme: "exact", PayTo: "0x...", Price: "$0.01", Network: "eip155:42161"},
            {Scheme: "exact", PayTo: "So1...", Price: "$0.01", Network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"},
        },
        Description: "Multi-chain data endpoint",
        MimeType:    "application/json",
    },
}

Dynamic Pricing

{
    Scheme:  "exact",
    PayTo:   "0x...",
    Network: "eip155:8453",
    Price: t402http.DynamicPriceFunc(func(ctx context.Context, reqCtx t402http.HTTPRequestContext) (t402.Price, error) {
        tier := getUserTier(reqCtx)
        if tier == "premium" {
            return "$0.001", nil
        }
        return "$0.01", nil
    }),
}

Dynamic PayTo

{
    Scheme:  "exact",
    Price:   "$10.00",
    Network: "eip155:8453",
    PayTo: t402http.DynamicPayToFunc(func(ctx context.Context, reqCtx t402http.HTTPRequestContext) (string, error) {
        sellerID := extractSellerFromPath(reqCtx.Path)
        return getSellerAddress(sellerID)
    }),
}

Paywall (Browser UI)

All frameworks support serving a browser-based payment UI for non-API requests:

config := ginmw.Config{
    Routes:      routes,
    Facilitator: facilitator,
    Schemes:     schemes,
    PaywallConfig: &t402http.PaywallConfig{
        Enabled:      true,
        DeliveryMode: "inline", // or "cdn" (default)
    },
}

When a browser requests a payment-protected page, the middleware returns an HTML page with a payment UI instead of raw JSON. The CDN mode (~500B) loads the UI from a CDN, while inline mode (~2.7MB) embeds the UI directly.

Settlement and Error Handlers

Error Handler

Customize how payment errors are returned to clients:

// Gin
ErrorHandler: func(c *gin.Context, err error) {
    log.Printf("Payment error: %v", err)
    c.JSON(http.StatusPaymentRequired, gin.H{"error": err.Error()})
}
 
// Echo
ErrorHandler: func(c echo.Context, err error) error {
    return c.JSON(http.StatusPaymentRequired, map[string]string{"error": err.Error()})
}
 
// Chi
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
    http.Error(w, err.Error(), http.StatusPaymentRequired)
}
 
// Fiber
ErrorHandler: func(c *fiber.Ctx, err error) error {
    return c.Status(fiber.StatusPaymentRequired).JSON(fiber.Map{"error": err.Error()})
}

Settlement Handler

Run custom logic after successful on-chain settlement:

// Gin
SettlementHandler: func(c *gin.Context, resp *t402.SettleResponse) {
    log.Printf("Payment settled: tx=%s payer=%s", resp.Transaction, resp.Payer)
    db.RecordPayment(resp.Transaction, resp.Payer)
}