Skip to main content

Getting Started

This guide will help you set up and integrate x402-go into your Go applications.

Prerequisites​

  • Go 1.24.5 or later
  • An Ethereum wallet with private key (for facilitator or client)
  • Familiarity with the Gin HTTP framework (for middleware usage)
  • Access to blockchain RPC endpoints (for facilitator)

Installation​

Add the module to your existing Go project:

go get github.com/vorpalengineering/x402-go

Import only the packages your application uses. Available packages include:

import (
// Facilitator service and client
"github.com/vorpalengineering/x402-go/facilitator"
"github.com/vorpalengineering/x402-go/facilitator/client"

// Resource server middleware
"github.com/vorpalengineering/x402-go/resource/middleware"

// Resource client (for buyers)
resourceclient "github.com/vorpalengineering/x402-go/resource/client"

// Shared types
"github.com/vorpalengineering/x402-go/types"
)

Running the Facilitator Service​

The facilitator service processes payment verification and settlement requests. Download the source archive, extract it, and run the following commands from the repository root. The facilitator wallet needs native currency on each configured chain to pay transaction fees.

1. Set Environment Variables​

The facilitator requires a private key for signing and executing on-chain transactions:

export X402_FACILITATOR_PRIVATE_KEY="<your-facilitator-private-key>"

Security Note: Never commit your private key to version control. Use environment variables or a secure secret manager.

2. Create Configuration​

Copy the example configuration:

cp facilitator/config.example.yaml facilitator/config.yaml

Edit facilitator/config.yaml:

server:
host: "0.0.0.0"
port: 4020

# Networks use CAIP-2 identifiers (namespace:reference)
networks:
eip155:8453:
rpc_url: "https://mainnet.base.org"
eip155:84532:
rpc_url: "https://sepolia.base.org"

# Supported scheme-network combinations
supported:
- scheme: "exact"
network: "eip155:8453"
- scheme: "exact"
network: "eip155:84532"

transaction:
timeout_seconds: 120
max_gas_price: "100000000000" # 100 gwei in wei

log:
level: "info" # debug, info, warn, error

3. Start the Service​

# Uses facilitator/config.yaml by default
go run ./cmd/facilitator

# Or specify a custom config path
go run ./cmd/facilitator --config=path/to/config.yaml

The facilitator starts on the configured port (default: 4020) and exposes:

EndpointMethodDescription
/supportedGETList supported payment schemes and networks
/verifyPOSTVerify a payment payload
/settlePOSTSettle a payment on-chain

Protecting APIs with Middleware​

Add payment requirements to your Gin routes using the resource middleware. Replace the sample PayTo address with your payment recipient and confirm the token address and EIP-712 domain for your deployment:

package main

import (
"github.com/gin-gonic/gin"
"github.com/vorpalengineering/x402-go/resource/middleware"
"github.com/vorpalengineering/x402-go/types"
)

func main() {
router := gin.Default()

// Configure x402 middleware
x402 := middleware.NewX402Middleware(&middleware.MiddlewareConfig{
FacilitatorURL: "http://localhost:4020",
DefaultRequirements: types.PaymentRequirements{
Scheme: "exact",
Network: "eip155:8453",
Amount: "1000000", // 1 USDC (6 decimals)
PayTo: "0x1111111111111111111111111111111111111111",
Asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
MaxTimeoutSeconds: 120,
Extra: map[string]any{"name": "USD Coin", "version": "2"},
},
ProtectedPaths: []string{"/api/*"},
RouteResources: map[string]*types.ResourceInfo{
"/api/data": {
Description: "Protected data endpoint",
MimeType: "application/json",
},
},
MaxBufferSize: 5 * 1024 * 1024, // 5 MB max response
DiscoveryEnabled: true,
BaseURL: "https://api.example.com",
DiscoverableEndpoints: []string{"/api/data"},
})

// Apply middleware globally
router.Use(x402.Handler())

// Your routes
router.GET("/api/data", func(c *gin.Context) {
c.JSON(200, gin.H{"data": "protected content"})
})

router.Run(":3000")
}

The middleware implements the full x402 payment flow:

  1. Returns 402 with PAYMENT-REQUIRED header when no payment is provided
  2. Verifies payment with the facilitator
  3. Executes the handler if payment is valid (response is buffered)
  4. Settles payment on-chain after the handler produces a 2xx response
  5. Returns PAYMENT-RESPONSE header with settlement details

A non-2xx handler response is returned without settlement. Handler side effects cannot be rolled back by the middleware if settlement fails.

Using the Resource Client​

For services that need to pay for resources, use the resource client. The current Go client signs with the fixed token domain name USDC and version 2; it does not use requirements.Extra to choose the domain. Confirm compatibility before using Pay. For tokens with a different domain, use the CLI with the token’s actual --name and --version, or implement matching signing logic.

package main

import (
"fmt"
"io"
"log"
"os"
"strings"

"github.com/ethereum/go-ethereum/crypto"
"github.com/vorpalengineering/x402-go/resource/client"
)

func main() {
// Load your private key
privateKey, err := crypto.HexToECDSA(strings.TrimPrefix(os.Getenv("X402_PAYER_PRIVATE_KEY"), "0x"))
if err != nil {
log.Fatal(err)
}

// Create resource client
rc := client.NewResourceClient(privateKey)

url := "https://api.example.com/api/data"

// Step 1: Check if resource requires payment
resp, paymentRequired, err := rc.Check("GET", url, "", nil)
if err != nil {
log.Fatal(err)
}

// Step 2: If payment required, inspect requirements and decide to pay
if paymentRequired != nil {
if len(paymentRequired.Accepts) == 0 {
log.Fatal("server returned no payment options")
}
selected := paymentRequired.Accepts[0]
// Set these variables to a payment option you have reviewed and approved.
if selected.Scheme != "exact" ||
selected.Network != os.Getenv("X402_EXPECTED_NETWORK") ||
selected.Amount != os.Getenv("X402_EXPECTED_AMOUNT") ||
!strings.EqualFold(selected.Asset, os.Getenv("X402_EXPECTED_ASSET")) ||
!strings.EqualFold(selected.PayTo, os.Getenv("X402_EXPECTED_PAY_TO")) {
log.Fatal("payment requirements do not match the approved option")
}

log.Printf("Payment required: %s on %s", selected.Amount, selected.Network)

// Step 3: Pay for resource (generates EIP-3009 authorization)
resp, err = rc.Pay("GET", url, "", nil, &selected)
if err != nil {
log.Fatal(err)
}
}

// Use the response
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Fatalf("request failed: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Response: %s\n", body)
}

Using the Facilitator Client​

For direct integration, pass an existing payload and its approved requirements to the facilitator client. This helper verifies the authorization, then submits a real settlement transaction if verification succeeds. Verification alone does not reserve funds or guarantee settlement.

package payments

import (
"fmt"

"github.com/vorpalengineering/x402-go/facilitator/client"
"github.com/vorpalengineering/x402-go/types"
)

func VerifyAndSettle(
facilitatorURL string,
payload types.PaymentPayload,
requirements types.PaymentRequirements,
) (*types.SettleResponse, error) {
c := client.NewFacilitatorClient(facilitatorURL)
verified, err := c.Verify(&types.VerifyRequest{
PaymentPayload: payload,
PaymentRequirements: requirements,
})
if err != nil {
return nil, err
}
if !verified.IsValid {
return nil, fmt.Errorf("payment rejected: %s", verified.InvalidReason)
}
settled, err := c.Settle(&types.SettleRequest{
PaymentPayload: payload,
PaymentRequirements: requirements,
})
if err != nil {
return nil, err
}
if !settled.Success {
return settled, fmt.Errorf("settlement failed: %s", settled.ErrorReason)
}
return settled, nil
}

Using the CLI Tool​

Build and use the CLI from the repository root. Review the amount, recipient, asset, network, and token domain in requirements.json before signing. If extra.name and extra.version are absent, supply the token’s actual --name and --version to payload:

# Build
go build ./cmd/x402cli

# Check facilitator supported schemes
./x402cli supported -u http://localhost:4020

# Check if a resource requires payment
./x402cli check -u https://api.example.com/api/data

# Browse discovery endpoint
./x402cli browse -u https://api.example.com

# Fetch one payment option and save it
./x402cli req -u https://api.example.com/api/data -o requirements.json

# Generate payment payload (requires private key)
./x402cli payload --req requirements.json --private-key 0x... -o payload.json

# Pay for a resource
./x402cli pay -u https://api.example.com/api/data \
-p payload.json --req requirements.json

Docker​

Both the facilitator and CLI can be run as containers:

# Start the facilitator service
export X402_FACILITATOR_PRIVATE_KEY=0x...
docker compose up facilitator

# Run a CLI command
docker compose run --rm x402cli supported -u http://facilitator:4020

# Build images without starting
docker compose build

The facilitator mounts facilitator/config.yaml into the container. Ensure the file exists before running:

cp facilitator/config.example.yaml facilitator/config.yaml

Next Steps​