
x402 Go/Gin 高级服务端实战动态定价、收款路由、生命周期钩子与 API 可发现性【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402本文是 x402 支付协议HTTP 之上构建的互联网支付协议Go 语言服务端的高级实践指南以 examples/go/servers/advanced/README.md 为骨架结合仓库内 7 个可独立运行的 Gin 示例与 Go SDK 源码系统讲解动态定价、按请求上下文路由收款地址、支付生命周期钩子、Bazaar API 可发现性、多网络支持与自定义代币等进阶模式。读完本文你将能够基于 Gin 搭建一个具备分层定价、多收款方、支付事件埋点与代币自定义能力的生产级 x402 资源服务器并理解PAYMENT-REQUIRED/PAYMENT-RESPONSE协议的完整交互过程。目录前置条件与快速开始示例总览六种进阶模式一览多网络支持同时接收 EVM 与 SVM 支付Bazaar 扩展让 API 可被客户端与 Agent 发现动态定价按请求上下文实时计算价格动态收款路由把支付路由到不同收款方生命周期钩子在验证与结算前后挂载业务逻辑自定义货币解析接受 USDC 之外的代币协议响应格式402 与 200 的完整报文源码级原理动态函数如何被解析与执行前置条件与快速开始本示例面向Go 1.21 及以上环境需要满足三个条件Go 1.21 或更高版本一个有效的EVM 收款地址EVM_PAYEE_ADDRESS用于接收付款一个支持目标支付网络的 Facilitator 端点 URLFACILITATOR_URL。Facilitator 负责撮合、验证与结算可参考项目生态中的 Facilitator 列表进行选择。以examples/go/servers/advanced目录为工作目录按以下三步启动第一步配置环境变量cp .env-example .env注仓库中该目录未附带.env-example文件可自行创建.env并填入下述变量示例代码使用godotenv.Load()读取。需要填写两个必需变量变量作用FACILITATOR_URLFacilitator 端点 URL如https://x402.org/facilitatorEVM_PAYEE_ADDRESS接收付款的 Ethereum 地址所有示例都会在启动时校验这两个变量缺失即打印错误并退出见各示例文件开头。all-networks示例额外支持可选变量SVM_PAYEE_ADDRESSSolana 收款地址。第二步安装依赖go mod download第三步运行示例每个示例都是独立程序直接运行即可默认监听:4021端口go run hooks.go示例总览六种进阶模式一览示例启动命令演示内容all-networksgo run all_networks.go支持所有已配置网络EVM/SVM网络可选按环境变量配置bazaargo run bazaar.go通过 Bazaar 扩展实现 API 可发现性hooksgo run hooks.go支付生命周期钩子dynamic-pricego run dynamic-price.go基于请求上下文的动态定价dynamic-pay-togo run dynamic-pay-to.go将支付路由到不同收款方custom-money-definitiongo run custom-money-definition.go接受替代代币目录中还包含一个 README 表格未列出的eip2612-gas-sponsoring.go演示 EIP-2612 燃料赞助场景可一并参考。它们共享同一套基础设施ginfw.Default()创建 Gin 引擎 →ginmw.X402Payment(...)挂载支付中间件 → 注册受保护路由处理器。所有示例均以GET /weather作为演示资源返回 JSON 天气数据。启动后可用任一 x402 客户端实测。Go 客户端示例位于 examples/go/clients/custom在examples/go/servers/advanced下运行时可用cd ../../clients/custom进入该客户端同样需要先配置好.env然后执行go run main.go发起带支付的请求。多网络支持同时接收 EVM 与 SVM 支付all-networks示例演示了如何在一个服务里同时支持EVM如 Base Sepolia与SVMSolana Devnet两条链上的 exact 支付。核心思路是按环境变量动态组装PaymentOptions与SchemesevmNetwork : x402.Network(eip155:84532) // Base Sepolia svmNetwork : x402.Network(solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1) // Solana Devnet paymentOptions : x402http.PaymentOptions{} if evmAddress ! { paymentOptions append(paymentOptions, x402http.PaymentOption{ Scheme: exact, Price: $0.001, Network: evmNetwork, PayTo: evmAddress, }) } if svmAddress ! { paymentOptions append(paymentOptions, x402http.PaymentOption{ Scheme: exact, Price: $0.001, Network: svmNetwork, PayTo: svmAddress, }) } schemes : []ginmw.SchemeConfig{} if evmAddress ! { schemes append(schemes, ginmw.SchemeConfig{Network: evmNetwork, Server: evm.NewExactEvmScheme()}) } if svmAddress ! { schemes append(schemes, ginmw.SchemeConfig{Network: svmNetwork, Server: svm.NewExactSvmScheme()}) }关键设计点最少配置原则两个地址至少提供一个即可启动未配置的网络不会被声明客户端拿到的accepts数组只包含实际可用的支付选项。Scheme 与网络一一绑定ginmw.SchemeConfig把NetworkCAIP-2 标识与具体的方案服务器evm.NewExactEvmScheme()/svm.NewExactSvmScheme()配对中间件据此选择验证与结算逻辑。健康检查免支付GET /health不经过支付中间件便于负载均衡器探活。该文件注释要求新增链时按网络前缀字母序追加eip155在solana之前便于维护。Bazaar 扩展让 API 可被客户端与 Agent 发现Bazaar 是 x402 的发现扩展见 go/extensions/bazaar。bazaar示例演示了如何在路由上声明机器可读的 API 文档——包括 HTTP 方法、查询参数、请求/响应 JSON Schema 与示例输出使客户端和 AI Agent 无需人工文档即可发现并调用你的付费服务。discoveryExtension, err : bazaar.DeclareDiscoveryExtension( bazaar.MethodGET, map[string]interface{}{city: San Francisco}, // 示例查询参数 types.JSONSchema{ properties: map[string]interface{}{ city: map[string]interface{}{ type: string, description: City name to get weather for, }, }, required: []string{city}, }, , // GET 请求无 body types.OutputConfig{ Example: map[string]interface{}{ city: San Francisco, weather: foggy, temperature: 60, }, Schema: types.JSONSchema{ properties: map[string]interface{}{ city: map[string]interface{}{type: string}, weather: map[string]interface{}{type: string}, temperature: map[string]interface{}{type: number}, }, required: []string{city, weather, temperature}, }, }, ) routes : x402http.RoutesConfig{ GET /weather: { Accepts: x402http.PaymentOptions{ { Scheme: exact, PayTo: evmPayeeAddress, Price: $0.001, Network: evmNetwork, }, }, Description: Weather data, MimeType: application/json, Extensions: map[string]interface{}{ types.BAZAAR: discoveryExtension, }, }, }DeclareDiscoveryExtension的参数含义参数说明MethodGET声明该 API 使用 GET 方法map[string]interface{}{city: ...}示例查询参数告知调用方应传什么参数types.JSONSchema{...}请求参数的 JSON Schema 约束type、description、requiredGET 请求无请求体POST 等场景可在此声明 body 的 JSON Schematypes.OutputConfig{...}响应示例与响应 JSON Schema适用场景客户端和 AI Agent 能自动发现你的服务。需要注意一个实现细节若路由模式使用通配符*如GET /weather/*且同时挂载 Bazaar 扩展go/http/server.go 的validateRouteConfiguration会打印警告——通配符路由会自动生成var1、var2这类参数名建议改用命名参数如/weather/:city以获得更准确的发现元数据。动态定价按请求上下文实时计算价格静态Price只能写死一个价格。dynamic-price示例把价格声明替换为函数让价格在每次请求时根据上下文计算可用于分层定价、按用户定价、按内容定价等场景dynamicPrice : func(ctx context.Context, reqCtx x402http.HTTPRequestContext) (x402.Price, error) { tier : standard // 实际可从 reqCtx.Adapter 的 query/header 中提取 if tier premium { return $0.005, nil // Premium 档0.5 美分 } return $0.001, nil // Standard 档0.1 美分 } routes : x402http.RoutesConfig{ GET /weather: { Accepts: x402http.PaymentOptions{ { Scheme: exact, PayTo: evmPayeeAddress, Price: x402http.DynamicPriceFunc(dynamicPrice), Network: evmNetwork, }, }, }, }把普通函数签名func(ctx, reqCtx) (x402.Price, error)用x402http.DynamicPriceFunc(...)包装后赋给Price字段即可。Price字段类型为interface{}SDK 会在处理请求时做类型断言见下文「源码级原理」。完整示例中dynamicPrice内部以tier变量区分档位并在 handler 里通过c.DefaultQuery(tier, standard)消费查询参数?tierpremium返回更详细的天气数据湿度、风速、降水?tierstandard只返回基础数据从而实现价格与内容同步分层。适用场景分层定价、基于用户的定价、基于内容的定价以及任何价格随请求变化的业务。动态收款路由把支付路由到不同收款方dynamic-pay-to示例解决的是钱付给谁的问题——在 Marketplace 中不同卖家的资源应把支付路由到对应卖家的地址。做法同样是把PayTo替换为函数addressLookup : map[string]string{ US: 0x..., UK: 0x..., // ... 每个国家/卖家一个地址 } dynamicPayTo : func(ctx context.Context, reqCtx x402http.HTTPRequestContext) (string, error) { country : US // 实际可从 reqCtx.Adapter 的 query/header 中提取 address, ok : addressLookup[country] if !ok { address defaultAddress // 未命中时回退到默认地址 } return address, nil } routes : x402http.RoutesConfig{ GET /weather: { Accepts: x402http.PaymentOptions{ { Scheme: exact, PayTo: x402http.DynamicPayToFunc(dynamicPayTo), Price: $0.001, Network: evmNetwork, }, }, }, }完整示例用国家代码US/UK/CA/AU/NZ/IE/FR作为键做地址查找表未命中时回退到EVM_PAYEE_ADDRESS默认地址。函数签名func(ctx context.Context, reqCtx x402http.HTTPRequestContext) (string, error)通过x402http.DynamicPayToFunc包装后赋给PayTo字段。适用场景Marketplace 应用根据被访问的资源把支付路由给不同的卖家、内容创作者或服务提供商。生产环境中地址查找表通常替换为数据库查询。生命周期钩子在验证与结算前后挂载业务逻辑hooks示例把支付流程拆成验证verify与结算settle两个阶段并在每个阶段的前、后、失败路径上各暴露一个钩子。SDK 层面所有钩子类型定义在 go/server_hooks.go核心代码如下facilitatorClient : x402http.NewHTTPFacilitatorClient(x402http.FacilitatorConfig{ URL: facilitatorURL, }) server : x402.Newx402ResourceServer( x402.WithFacilitatorClient(facilitatorClient), ). Register(evmNetwork, evm.NewExactEvmScheme()). OnBeforeVerify(func(ctx x402.VerifyContext) (*x402.BeforeHookResult, error) { fmt.Println(Before verify hook, ctx) // 返回 x402.BeforeHookResult{Abort: true, Reason: ...} 可中止验证 return nil, nil }). OnAfterSettle(func(ctx x402.SettleResultContext) error { // 支付成功入账写库、发通知等 db.RecordTransaction(ctx.Result.Transaction, ctx.Result.Payer) return nil }). OnSettleFailure(func(ctx x402.SettleFailureContext) (*x402.SettleFailureHookResult, error) { // 返回 x402.SettleFailureHookResult{Recovered: true, Result: x402.SettleResponse{...}} 可恢复失败 return nil, nil }) r : gin.Default() r.Use(ginmw.PaymentMiddleware(routes, server))注意这里使用的是x402.Newx402ResourceServerRegister 链式钩子注册再交给ginmw.PaymentMiddleware而其他示例使用的是ginmw.X402Payment(ginmw.Config{...})一体化配置。两种方式等价前者更便于精细编排钩子。可用钩子全景每个钩子的完整签名与语义见 go/server_hooks.go钩子触发时机能力OnBeforeVerify支付验证之前可中止返回BeforeHookResult{Abort: true, Reason: ...}OnAfterVerify验证成功之后副作用处理返回错误只记日志不影响验证结果OnVerifyFailure验证失败时可恢复返回VerifyFailureHookResult{Recovered: true, Result: ...}OnBeforeSettle结算之前可中止OnAfterSettle结算成功之后副作用处理错误只记日志OnSettleFailure结算失败时可恢复返回SettleFailureHookResult{Recovered: true, Result: ...}钩子上下文的语义VerifyContext/SettleContext通过视图接口PaymentPayloadView、PaymentRequirementsView提供版本无关的访问方式同时附带PayloadBytes/RequirementsBytes原始字节为 Bazaar 等扩展提供逃生舱。BeforeHookResult含Abort/Reason/Message三个字段失败恢复钩子的Recoveredtrue会让 SDK 直接用你提供的Result替代错误返回相关类型定义见 go/server_hooks.go。除链式.OnXxx()外还可用选项式x402.WithBeforeVerifyHook(...)等ResourceServerOption注册钩子见 go/server_hooks.go便于与函数式配置风格统一。hooks.go完整示例会打印六个钩子的执行日志 前置钩子 / 成功钩子 / 失败钩子运行后即可在控制台观察完整的支付生命周期。适用场景把支付事件写入数据库或监控系统在处理支付前做自定义校验如风控、黑白名单对失败支付实现重试或恢复逻辑支付成功后触发副作用通知、数据库更新、发放权益。自定义货币解析接受 USDC 之外的代币默认情况下 exact 方案的货币解析器把美元价格映射为 USDC。custom-money-definition示例通过RegisterMoneyParser注册自定义解析器按网络或金额条件选择代币evmScheme : evm.NewExactEvmScheme().RegisterMoneyParser( func(amount float64, network x402.Network) (*x402.AssetAmount, error) { // 在 Gnosis Chaineip155:100上使用 Wrapped XDAI if string(network) eip155:100 { return x402.AssetAmount{ Amount: fmt.Sprintf(%.0f, amount*1e18), // WXDAI 18 位小数 Asset: 0xe91d153e0b41518a2ce8dd3d7944fa863463a97d, // Gnosis 上 WXDAI 地址 Extra: map[string]interface{}{token: Wrapped XDAI}, }, nil } // 大额支付100 美元改用 DAI if amount 100 { return x402.AssetAmount{ Amount: fmt.Sprintf(%.0f, math.Round(amount*1e18)), // DAI 18 位小数 Asset: 0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb, // Base Sepolia 上 DAI 地址 Extra: map[string]interface{}{token: DAI, tier: large}, }, nil } return nil, nil // 返回 nil 走默认 USDC 解析器 }, ) r.Use(ginmw.X402Payment(ginmw.Config{ Routes: routes, Facilitator: facilitatorClient, Schemes: []ginmw.SchemeConfig{ {Network: evmNetwork, Server: evmScheme}, // 使用自定义 scheme }, }))解析器语义要点输入是美元金额float与目标网络输出*x402.AssetAmount——包含原子单位Amount按代币小数位换算如amount*1e18、合约地址Asset与Extra附加信息返回nil, nil表示回退到该网络默认解析器USDC实现默认 特例的叠加策略方案服务器evmScheme被整体注册进ginmw.SchemeConfig中间件验证时会使用自定义解析结果。示例中针对 Gnosis 使用 WXDAI、针对大额使用 DAI 两条分支仅为演示——注释明确提示 WXDAI 并不符合 EIP-3009 合规要求生产使用需确认代币与结算机制的兼容性。适用场景接受 USDC 之外的代币或按条件切换代币如大额用 DAI、特定网络用自定义代币。协议响应格式402 与 200 的完整报文理解响应格式是调试与二次开发的基础。以下报文来自 README 的完整记录与 SDK 实现一致。未支付请求HTTP 402 Payment RequiredHTTP/1.1 402 Payment Required Content-Type: application/json; charsetutf-8 PAYMENT-REQUIRED: base64-encoded JSON {}PAYMENT-REQUIRED头携带 base64 编码的 JSON 支付需求。注意amount是原子单位如1000 0.001 USDC因为 USDC 为 6 位小数{ x402Version: 2, error: Payment required, resource: { url: http://localhost:4021/weather, description: Weather data, mimeType: application/json }, accepts: [ { scheme: exact, network: eip155:84532, amount: 1000, asset: 0x036CbD53842c5426634e7929541eC2318f3dCF7e, payTo: 0x..., maxTimeoutSeconds: 300, extra: { name: USDC, version: 2, resourceUrl: http://localhost:4021/weather } } ] }支付成功HTTP 200 OKHTTP/1.1 200 OK Content-Type: application/json; charsetutf-8 PAYMENT-RESPONSE: base64-encoded JSON {report:{weather:sunny,temperature:70}}PAYMENT-RESPONSE头携带 base64 编码的结算详情{ success: true, transaction: 0x..., network: eip155:84532, payer: 0x..., requirements: { scheme: exact, network: eip155:84532, amount: 1000, asset: 0x036CbD53842c5426634e7929541eC2318f3dCF7e, payTo: 0x..., maxTimeoutSeconds: 300, extra: { name: USDC, version: 2, resourceUrl: http://localhost:4021/weather } } }从源码看go/http/server.go服务端对浏览器请求Accept含text/html且User-Agent含Mozilla返回 HTML paywall 页面而非 JSON对 API 客户端返回带PAYMENT-REQUIRED头的 402。HTML 生成遵循路由自定义 HTML 注册的PaywallProvider 内置 EVM/SVM 模板的降级链go/http/paywall.go内置模板把paymentRequired序列化注入window.x402全局对象供前端钱包逻辑使用。源码级原理动态函数如何被解析与执行这一节把前文的动态能力与协议流程落到源码方便你排查问题或扩展自定义能力。RoutesConfig 与 PaymentOption 的数据结构路由与支付选项的定义在 go/http/server.goPaymentOption单个支付选项含Scheme、PayTointerface{}可为字符串或DynamicPayToFunc、Priceinterface{}可为x402.Price或DynamicPriceFunc、Network、可选MaxTimeoutSeconds与ExtraRouteConfig路由级配置含Accepts支付选项数组、Resource、Description、MimeType、CustomPaywallHTML、Extensions与可选的UnpaidResponseBody回调可为未支付请求生成自定义响应体RoutesConfigmap[路由模式]RouteConfig模式形如GET /weather也支持*通配符。动态函数的运行时解析核心逻辑在BuildPaymentRequirementsFromOptionsgo/http/server.go遍历每个PaymentOption用类型断言判断字段是否为函数——若option.PayTo断言为DynamicPayToFunc则调用payToFunc(ctx, reqCtx)得到收款地址否则按字符串处理若option.Price断言为DynamicPriceFunc则调用priceFunc(ctx, reqCtx)得到价格否则按静态值处理解析结果组装成x402.ResourceConfig后交给BuildPaymentRequirementsFromConfig生成支付需求含金额换算与货币解析。这意味着动态函数每次请求都会执行且能拿到完整的HTTPRequestContextAdapter、Path、Method、PaymentHeader、RoutePattern见 go/http/server.go因此价格、收款方可以基于 query、header、用户会话等任意请求信息决定。请求处理主流程ProcessHTTPRequestgo/http/server.go的完整链路用编译好的路由正则匹配PathMethod未命中 →no-payment-required依次执行protectedRequestHooks可GrantAccess免支付放行或Abort返回 403解析PAYMENT-SIGNATURE头得到 V2 支付负载无头 → 返回 402 PAYMENT-REQUIRED构建所有支付需求触发动态函数解析→FindMatchingRequirements找到匹配项 →VerifyPayment验证签名/授权验证通过返回payment-verified由 Gin 中间件放行到业务 handler随后ProcessSettlementgo/http/server.go执行结算并生成PAYMENT-RESPONSE头结算失败时回写 402。Gin 中间件配置项ginmw.X402Payment(ginmw.Config{...})的完整配置go/http/gin/middleware.go配置项说明Routesx402http.RoutesConfig路由支付配置Facilitator/FacilitatorClientsFacilitator 客户端可用WithFacilitatorClient添加多个Schemes[]ginmw.SchemeConfig{Network, Server}声明各网络的方案服务器PaywallConfig浏览器 paywall 的AppName、AppLogo、CurrentURL、Testnet配置SyncFacilitatorOnStart启动时同步 Facilitator 支持信息动态价格/支付示例均设为trueTimeout支付操作的上下文超时示例为30 * time.SecondErrorHandler/SettlementHandler自定义错误与结算处理回调启动后建议观察各示例控制台输出hooks示例打印六个钩子的执行日志dynamic-price打印档位与价格dynamic-pay-to打印按国家路由的地址all-networks打印已启用的网络与监听地址。结合PAYMENT-REQUIRED/PAYMENT-RESPONSE头可用任意 HTTP 调试工具查看 base64 解码后的 JSON即可端到端验证本文所述的全部行为。【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考