Documentation
¶
Overview ¶
Package x402 is the Hanzo Cloud native x402 pay-per-use subsystem (HTTP 402): challenge → the client pays → proof submitted → verify → serve → settle.
FLOW. A priced resource answers 402 with PaymentRequirements (what to pay + the recipient wallet). The client signs an ERC-3009 transferWithAuthorization over those terms and retries with the X-Payment header. The subsystem VERIFIES the EIP-712 signature (secp256k1 recovery via luxfi/crypto — the SAME primitive the wallets custody signs with), rejects a REPLAYED authorization (nonce dedup), SETTLES exactly once (payer debit through the metering spine so paid usage appears in billing/usage like any metered spend, plus a credit to the recipient wallet's ledger), and serves.
SEAMS. The marketplace registry (another subsystem) owns the mapping resource→Terms (price + recipient wallet); x402 only enforces it (Registry + Publish). Recipient resolution rides the wallets subsystem (wallets.ResolvePaymentTarget). On-chain broadcast of the authorization is a Settler seam; the LIVE default is ledger settlement.
Index ¶
- Constants
- func Enforce() zip.Handler
- func Mount(app cloud.Router, deps cloud.Deps) error
- func Publish(r Registry)
- func Shutdown() error
- func Verify(req PaymentRequirements, p Proof, tokenName, tokenVersion string, now int64) error
- type Config
- type PaymentRequirements
- type Proof
- type Receipt
- type Registry
- type Settlement
- type Terms
Constants ¶
const ( // Version is the x402 protocol version this subsystem speaks. Version = "1" // Scheme is the payment scheme: an ERC-3009 transferWithAuthorization the // client signs off-chain (EIP-712), settled on the ledger or on-chain. Scheme = "erc3009" // HeaderRequirements carries the PaymentRequirements on a 402 response. HeaderRequirements = "X-Payment-Required" // HeaderProof carries the client's signed Proof on the retry. HeaderProof = "X-Payment" // HeaderReceipt carries the settlement Receipt on a served (2xx) response. HeaderReceipt = "X-Payment-Receipt" // DefaultValidFor is the default authorization validity window, in seconds. DefaultValidFor = 300 )
const ( DefaultTokenName = "USD Coin" DefaultTokenVersion = "2" )
TokenName / TokenVersion are the ERC-3009 EIP-712 domain fields. USDC uses ("USD Coin", "2"); operators pin them per token in Config.
const ( // DefaultNetwork / DefaultChainID pin the challenge's settlement network when // neither the resource's Terms nor the operator config names one. Default to // the Hanzo L1 (its EVM chainID == its network id), matching wallets. DefaultNetwork = "hanzo" DefaultChainID = 36963 // DefaultTokenDecimals is USDC's precision (the smallest-unit scale the client // signs over). Operators override per token via CLOUD_X402_TOKEN_DECIMALS. DefaultTokenDecimals = 6 )
Variables ¶
This section is empty.
Functions ¶
func Enforce ¶
Enforce is the pay-per-use middleware a priced route group applies. It is a passthrough until Mount runs AND a Registry is Published — so linking x402 never gates traffic on its own.
func Mount ¶
Mount wires /v1/x402 and the settlement store. Direct construction (not cloud.Mount) because it holds the package singleton the middleware reaches.
func Publish ¶
func Publish(r Registry)
Publish installs the marketplace registry x402 enforces against. Passing nil detaches it (Enforce reverts to passthrough). One registry, process-wide.
func Verify ¶
func Verify(req PaymentRequirements, p Proof, tokenName, tokenVersion string, now int64) error
Verify checks that p is a well-formed, in-window, unaltered authorization for the requirements req: the To/Value match, the time window holds at now, and the EIP-712 signature recovers to p.From. It does NOT check replay (the store does) nor settle (the settler does) — one concern each.
Types ¶
type Config ¶
type Config struct {
Token string // ERC-3009 token contract (EIP-712 verifyingContract)
TokenName string // EIP-712 domain name (default "USD Coin")
TokenVersion string // EIP-712 domain version (default "2")
TokenDecimals int // token smallest-unit scale (default 6)
Network string // settlement network label (default "hanzo")
ChainID int64 // settlement chain id (default 36963)
ValidFor int64 // authorization validity window, seconds (default 300)
}
Config is the operator-pinned x402 settlement config. Token + its EIP-712 domain (name/version/decimals) MUST match the token the client signs against, or every signature fails to recover. Values are read from env in Mount; tests inject Config directly.
type PaymentRequirements ¶
type PaymentRequirements struct {
Version string `json:"version"`
Scheme string `json:"scheme"`
Resource string `json:"resource"` // the priced resource id (canonical path)
Network string `json:"network"`
ChainID int64 `json:"chainId"`
Token string `json:"token"` // ERC-3009 token contract (e.g. USDC)
Payee string `json:"payee"` // recipient wallet address
Amount string `json:"amount"` // token smallest-unit amount (decimal string)
ValidFor int64 `json:"validFor"` // seconds the authorization may remain valid
}
PaymentRequirements is the server's 402 challenge: exactly what the client must pay, and to whom, to access the resource. It is emitted both as the response body and the X-Payment-Required header.
type Proof ¶
type Proof struct {
From string `json:"from"` // payer address (recovers from Signature)
To string `json:"to"` // must equal the requirements Payee
Value string `json:"value"` // must equal the requirements Amount
ValidAfter int64 `json:"validAfter"` // unix seconds
ValidBefore int64 `json:"validBefore"` // unix seconds
Nonce string `json:"nonce"` // 32-byte hex (0x optional)
Signature string `json:"signature"` // 65-byte EIP-712 signature (hex)
}
Proof is the client's signed ERC-3009 authorization — the "payment". Nonce is a client-chosen 32-byte value and is the REPLAY ANCHOR: an authorization settles at most once per (From, Nonce).
func ParseProof ¶
ParseProof decodes a Proof from the X-Payment header value.
type Receipt ¶
type Receipt struct {
ID string `json:"id"`
Resource string `json:"resource"`
Payer string `json:"payer"` // payer ORG (the debited ledger)
From string `json:"from"` // payer address
Payee string `json:"payee"` // recipient address
PayeeOrg string `json:"payeeOrg"`
Amount string `json:"amount"` // exact 18-dp USD (money.Amount string)
Nonce string `json:"nonce"`
SettledVia string `json:"settledVia"` // "ledger" (live) | "chain" (seam)
TxHash string `json:"txHash,omitempty"`
SettledAt int64 `json:"settledAt"`
}
Receipt is the settlement record returned on the X-Payment-Receipt header and looked up via GET /v1/x402/settlements/:id.
type Registry ¶
type Registry interface {
Price(ctx context.Context, resource string) (terms Terms, ok bool, err error)
}
Registry resolves a resource's payment Terms. ok=false means the resource is FREE — no enforcement. err is a real lookup failure (fail closed). The marketplace subsystem implements this and injects it via Publish.
type Settlement ¶
type Settlement struct {
ID string // deterministic: settlementID(from, nonce)
PayerOrg string
From string
Nonce string
Resource string
Payee string
PayeeOrg string
Amount string // exact 18-dp USD (money.Amount.IntString)
SettledVia string
TxHash string
CreatedAt int64
}
Settlement is the recorded outcome of one settled x402 authorization.
type Terms ¶
type Terms struct {
Amount money.Amount // exact 18-dp USD price
RecipientOrg string // the recipient wallet's org
RecipientWalletID string // the wallet that receives payment
Token string // optional per-resource token override
Network string // optional per-resource network override
ChainID int64 // optional per-resource chain override
}
Terms are a priced resource's payment terms: the price and the wallet that receives it. The marketplace registry OWNS the resource→Terms mapping; x402 resolves the recipient wallet (via wallets) and enforces payment against these.