Skip to main content

Getting started with Gin (Go)

Start accepting x402 payments in your Gin server in 2 minutes.
You can find the full code for this example here.

Step 1: Create a Go module and install dependencies

go mod init myserver
go get github.com/coinbase/x402/go github.com/gin-gonic/gin github.com/joho/godotenv

Step 2: Set your environment variables

Your .env file should look like this:
EVM_PAYEE_ADDRESS=0x...   # EVM wallet address to receive payments
SVM_PAYEE_ADDRESS=...     # Solana wallet address to receive payments
FACILITATOR_URL=https://facilitator.payai.network

Step 3: Preview the server code

This is the main.go the example uses. It loads your env, applies the x402 payment middleware, and defines protected and health routes.
package main

import (
	"fmt"
	"net/http"
	"os"
	"time"

	x402 "github.com/coinbase/x402/go"
	x402http "github.com/coinbase/x402/go/http"
	ginmw "github.com/coinbase/x402/go/http/gin"
	evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
	svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
	ginfw "github.com/gin-gonic/gin"
	"github.com/joho/godotenv"
)

const (
	DefaultPort = "4021"
)

func main() {
	godotenv.Load()

	evmAddress := os.Getenv("EVM_PAYEE_ADDRESS")
	if evmAddress == "" {
		fmt.Println("❌ EVM_PAYEE_ADDRESS environment variable is required")
		os.Exit(1)
	}

	svmAddress := os.Getenv("SVM_PAYEE_ADDRESS")
	if svmAddress == "" {
		fmt.Println("❌ SVM_PAYEE_ADDRESS environment variable is required")
		os.Exit(1)
	}

	facilitatorURL := os.Getenv("FACILITATOR_URL")
	if facilitatorURL == "" {
		fmt.Println("❌ FACILITATOR_URL environment variable is required")
		fmt.Println("   Example: https://x402.org/facilitator")
		os.Exit(1)
	}

	// Network configuration - Base Sepolia testnet
	evmNetwork := x402.Network("eip155:84532")
	svmNetwork := x402.Network("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1")

	fmt.Printf("🚀 Starting Gin x402 server...\n")
	fmt.Printf("   EVM Payee address: %s\n", evmAddress)
	fmt.Printf("   SVM Payee address: %s\n", svmAddress)
	fmt.Printf("   EVM Network: %s\n", evmNetwork)
	fmt.Printf("   SVM Network: %s\n", svmNetwork)
	fmt.Printf("   Facilitator: %s\n", facilitatorURL)

	// Create Gin router
	r := ginfw.Default()

	// Create HTTP facilitator client
	facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
		URL: facilitatorURL,
	})

	/**
	 * Configure x402 payment middleware
	 *
	 * This middleware protects specific routes with payment requirements.
	 * When a client accesses a protected route without payment, they receive
	 * a 402 Payment Required response with payment details.
	 */
	routes := x402http.RoutesConfig{
		"GET /weather": {
			Accepts: x402http.PaymentOptions{
				{
					Scheme:  "exact",
					Price:   "$0.001",
					Network: "eip155:84532",
					PayTo:   evmAddress,
				},
				{
					Scheme:  "exact",
					Price:   "$0.001",
					Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
					PayTo:   svmAddress,
				},
			},
			Description: "Get weather data for a city",
			MimeType:    "application/json",
		},
	}

	// Apply x402 payment middleware
	r.Use(ginmw.X402Payment(ginmw.Config{
		Routes:      routes,
		Facilitator: facilitatorClient,
		Schemes: []ginmw.SchemeConfig{
			ginmw.SchemeConfig{Network: "eip155:84532", Server: evm.NewExactEvmScheme()},
			ginmw.SchemeConfig{Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", Server: svm.NewExactSvmScheme()},
		},
		Timeout:    30 * time.Second,
	}))

	/**
	 * Protected endpoint - requires $0.001 USDC payment
	 *
	 * Clients must provide a valid x402 payment to access this endpoint.
	 * The payment is verified and settled before the endpoint handler runs.
	 */
	r.GET("/weather", func(c *ginfw.Context) {
		city := c.DefaultQuery("city", "San Francisco")

		weatherData := map[string]map[string]interface{}{
			"San Francisco": {"weather": "foggy", "temperature": 60},
			"New York":      {"weather": "cloudy", "temperature": 55},
			"London":        {"weather": "rainy", "temperature": 50},
			"Tokyo":         {"weather": "clear", "temperature": 65},
		}

		data, exists := weatherData[city]
		if !exists {
			data = map[string]interface{}{"weather": "sunny", "temperature": 70}
		}

		c.JSON(http.StatusOK, ginfw.H{
			"city":        city,
			"weather":     data["weather"],
			"temperature": data["temperature"],
			"timestamp":   time.Now().Format(time.RFC3339),
		})
	})

	/**
	 * Health check endpoint - no payment required
	 *
	 * This endpoint is not protected by x402 middleware.
	 */
	r.GET("/health", func(c *ginfw.Context) {
		c.JSON(http.StatusOK, ginfw.H{
			"status":  "ok",
			"version": "2.0.0",
		})
	})

	fmt.Printf("   Server listening on http://localhost:%s\n\n", DefaultPort)

	if err := r.Run(":" + DefaultPort); err != nil {
		fmt.Printf("Error starting server: %v\n", err)
		os.Exit(1)
	}
}

Step 4: Run the server

go run .
Your server is now accepting 402 payments!

Step 5: Test the server

You can test payments against your server locally by following the fetch example or the axios example, or by using the Go client examples in the x402 repository.

x402 reference

For a deeper dive into message shapes, headers, verification and settlement responses, see the x402 Reference.

Need help?

Join our Community

Have questions or want to connect with other developers? Join our Discord server.