x402 扩展机制详解:基于 Go 的声明式扩展体系与 Bazaar 自动发现实现
2026/9/17 16:35:31 网站建设 项目流程

x402 扩展机制详解:基于 Go 的声明式扩展体系与 Bazaar 自动发现实现

【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402

x402 是一个构建在 HTTP 之上的互联网支付协议,本篇文章聚焦其Extensions(扩展)机制:扩展是附加在受支付保护资源上的可选元数据,用于让服务器、客户端与 Facilitator(支付协调器)之间以标准化方式交换额外信息。文章以 go/extensions/README.md 为核心骨架,结合 Go 扩展源码、协议规范 与测试用例,完整讲解扩展的声明、流转、提取全流程,并深入剖析已实现的 Bazaar(API 自动发现)扩展,读完你可以独立为 x402 资源服务器声明扩展、在 Facilitator 中提取扩展数据,甚至按照规范创建属于自己的扩展类型。

扩展是什么:附加在支付资源上的可选元数据

Extensions(扩展)是 x402 协议中的可选元数据,可以附加到受支付保护的资源上,以启用超越基础支付需求的功能。扩展为服务器、客户端和 Facilitator 三方提供了一种标准化的通信方式,用来传递关于资源的附加信息。

在 x402 v2 协议的消息结构中,扩展统一挂在extensions字段下。例如在 402 PaymentRequired 响应中:

PaymentRequired (Server → Client): { "accepts": [...], "resource": {...}, "extensions": { "bazaar": { "info": {...}, // 实际的扩展数据 "schema": {...} // 校验 info 结构的 JSON Schema } } } PaymentPayload (Client → Server → Facilitator): { "payload": {...}, "extensions": { "bazaar": {...} // 客户端从 PaymentRequired 中复制扩展 } }

这一消息结构在 specs/extensions/bazaar.md 中有完整的 JSON 示例:extensions是一个 map,key 是扩展标识符(如bazaar),value 由info(实际数据)与schema(JSON Schema)两部分组成。

核心概念:Helpers,而非 Implementations

这是理解 x402 扩展体系最重要的一条原则:扩展包提供的只是「声明」与「检测」扩展支持的辅助函数(helpers),它们并不规定应用如何实现扩展。

扩展 helper 促成的是三方之间的对话:

  • **服务器(Servers)**声明「我支持这个扩展,这是我的元数据」;
  • **客户端(Clients)**检测「这个资源带有这样的扩展数据」;
  • Facilitator提取「让我来登记这条扩展信息」。

拿到扩展数据后具体做什么,完全是应用层的决策,不被这些 helper 强制。这一点在扩展哲学中被反复强调:扩展应当Enable新能力、Facilitate参与者之间的对话、Provide structure(通过 helper 与 schema)、Remain optional(绝不成为核心功能的必需项)、Stay flexible(支持多种实现方式)。

扩展的工作流程:声明 → 流转 → 提取

1. 服务器声明扩展

服务器使用 helper 将扩展元数据挂到支付要求上。以 SERVER.md 中的RoutesConfig为例:

import ( "github.com/x402-foundation/x402/go/extensions/bazaar" "github.com/x402-foundation/x402/go/extensions/types" ) discoveryExt, _ := bazaar.DeclareDiscoveryExtension( bazaar.MethodGET, map[string]interface{}{"city": "San Francisco"}, &types.InputConfig{...}, "", &types.OutputConfig{...}, ) routes := x402http.RoutesConfig{ "GET /weather": { Accepts: x402http.PaymentOptions{ {Scheme: "exact", PayTo: "0x...", Price: "$0.001", Network: "eip155:84532"}, }, Extensions: map[string]interface{}{ types.BAZAAR: discoveryExt, }, }, }

RouteConfig结构(定义于 go/http/server.go,文档见 SERVER.md)包含Accepts(支付选项)、DescriptionMimeTypeExtensions字段,后者即为扩展注入点。

2. 扩展随协议自动流转

扩展元数据会随支付协议自动传递:服务器在PaymentRequired中携带扩展,客户端在处理 402 响应时将扩展原样复制进自己的PaymentPayload,随后随支付请求发送给服务器,服务器再转发给 Facilitator 进行结算。

3. 接收方检测并使用扩展

接收方(客户端、Facilitator)通过 helper 提取扩展数据:

import "github.com/x402-foundation/x402/go/extensions/bazaar" // Facilitator 在 hook 上下文中从客户端支付中提取 discovered, _ := bazaar.ExtractDiscoveredResourceFromPaymentPayload( payloadBytes, requirementsBytes, true, ) // 客户端从 402 响应中提取 discovered, _ := bazaar.ExtractDiscoveredResourceFromPaymentRequired( paymentRequiredBytes, true, ) if discovered != nil { // 应用自行决定如何处理这些数据 // 例如:写入数据库目录、生成 API 文档、 // 构建发现索引、通过搜索 API 暴露等 }

扩展架构:info + schema 的两段式模式

所有 x402 扩展遵循统一的两段式结构:

Part 1:声明(info+schema

  • info:实际扩展数据(值、示例、元数据);
  • schema:校验info结构的 JSON Schema。

这种模式带来三个收益:自校验的扩展、机器可读的元数据、跨扩展类型一致的结构。在 go/extensions/types/types.go 中,JSONSchema被定义为map[string]interface{},生成的 schema 统一使用 JSON Schema Draft 2020-12("$schema": "https://json-schema.org/draft/2020-12/schema")。

Part 2:为每个角色提供 helper

扩展包为三类角色分别提供 helper:

服务器 Helper—— 声明扩展支持:

  • 目的:让服务器轻松附加扩展元数据;
  • 示例:DeclareDiscoveryExtension()生成格式正确的发现元数据;
  • 集成:与资源服务器的Extensions字段配合。

Facilitator Helper—— 提取并校验扩展数据:

  • 目的:让 Facilitator 轻松解析扩展元数据;
  • 示例:ExtractDiscoveredResourceFromPaymentPayload()从客户端支付负载中提取已发现的资源;
  • 校验:可选的 JSON Schema 校验。

客户端 Helper—— 从服务器响应中提取扩展数据:

  • 目的:帮助客户端从 402 响应中理解服务器能力;
  • 示例:ExtractDiscoveredResourceFromPaymentRequired()从 PaymentRequired 响应中提取已发现的资源;
  • 用例:构建自动发现 UI、生成客户端代码、API 探索工具。

已实现扩展:Bazaar(API 自动发现)

Bazaar是当前仓库中已完整实现的扩展,是一个「服务器 → Facilitator」方向的扩展,用于 API 自动发现与编目。导入路径为github.com/x402-foundation/x402/go/extensions/bazaar

目的:

  • 服务器声明其 API 应如何被调用(输入/输出 schema);
  • Facilitator 可以编目和索引可发现的 API;
  • 支持构建 API 市场与搜索引擎。

提供的函数:

  • DeclareDiscoveryExtension()—— 服务器 helper,声明发现元数据;
  • ExtractDiscoveredResourceFromPaymentPayload()—— Facilitator helper,从客户端支付中提取已发现的资源;
  • ExtractDiscoveredResourceFromPaymentRequired()—— 客户端 helper,从 402 响应中提取已发现的资源;
  • ValidateDiscoveryExtension()—— 校验 helper;
  • 用于结构校验的 JSON Schema 类型。

它不规定的内容:

  • 不规定 Facilitator 如何编目数据;
  • 不规定使用什么数据库或存储;
  • 不规定如何暴露已编目数据;
  • 不规定是否使用这些数据。

Bazaar 只是促进服务器与 Facilitator 之间的数据交换——实现完全由你决定。正如 bazaar/doc.go 开篇所述:「Enables facilitators to automatically catalog and index x402-enabled resources by following the server's provided discovery instructions.」

声明 HTTP 端点的发现扩展

DeclareDiscoveryExtension是声明任意 HTTP 方法端点发现信息的总入口(实现见 resource_service.go)。其参数语义为:

参数说明
methodHTTP 方法(GET、POST、PUT、PATCH、DELETE、HEAD),支持QueryParamMethods/BodyMethods常量或字符串
input示例输入数据(GET/HEAD/DELETE 为查询参数,POST/PUT/PATCH 为请求体)
inputSchema输入的 JSON Schema
bodyTypePOST/PUT/PATCH 的请求体类型(可选,默认为json
output输出配置(可选,OutputConfig{Example, Schema}
opts可选参数,目前支持PathParamsSchema(路径参数 Schema)

函数内部先判断方法类别,再分别调用createQueryDiscoveryExtension(查询参数方法)或createBodyDiscoveryExtension(请求体方法)。对方法做了严格校验:既不属查询方法也不属请求体方法时,返回unsupported HTTP method错误(测试用例见 bazaar_test.go)。

GET 端点声明示例:

extension, err := bazaar.DeclareDiscoveryExtension( bazaar.MethodGET, map[string]interface{}{"query": "example"}, bazaar.JSONSchema{ "properties": map[string]interface{}{ "query": map[string]interface{}{"type": "string"}, }, "required": []string{"query"}, }, "", nil, )

POST 端点(JSON 请求体 + 输出示例)声明示例:

extension, err := bazaar.DeclareDiscoveryExtension( bazaar.MethodPOST, map[string]interface{}{"name": "John", "age": 30}, bazaar.JSONSchema{ "properties": map[string]interface{}{ "name": map[string]interface{}{"type": "string"}, "age": map[string]interface{}{"type": "number"}, }, "required": []string{"name"}, }, bazaar.BodyTypeJSON, &bazaar.OutputConfig{ Example: map[string]interface{}{"success": true, "id": "123"}, }, )

请求体类型支持三种常量(定义于 types.go):BodyTypeJSON("json")、BodyTypeFormData("form-data")、BodyTypeText("text")。若不传bodyType,默认回退为json(resource_service.go),对应测试见 bazaar_test.go。

声明 MCP 工具的发现扩展

Bazaar 扩展还支持MCP(Model Context Protocol)工具的发现。DeclareMcpDiscoveryExtension(resource_service.go)接收一个DeclareMcpDiscoveryConfig

extension, err := bazaar.DeclareMcpDiscoveryExtension(bazaar.DeclareMcpDiscoveryConfig{ ToolName: "weather_lookup", Description: "Look up weather for a city", Transport: bazaar.TransportStreamableHTTP, InputSchema: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "city": map[string]interface{}{"type": "string"}, }, "required": []string{"city"}, }, Example: map[string]interface{}{"city": "San Francisco"}, })

MCP 传输协议支持两种常量(types.go):TransportStreamableHTTP("streamable-http")与TransportSSE("sse")。配置要求toolNameinputSchema必填,否则返回错误;生成 schema 时会对transport使用enum约束,并对input对象设置additionalProperties: false

从支付负载中提取已发现资源(Facilitator 侧)

ExtractDiscoveredResourceFromPaymentPayload(facilitator.go)是 Facilitator 处理支付 hook 时的核心提取函数,签名与逻辑如下:

func ExtractDiscoveredResourceFromPaymentPayload( payloadBytes []byte, // 客户端支付的原始 JSON 字节 requirementsBytes []byte, // 客户端接受的支付要求原始 JSON 字节 validate bool, // 是否按 schema 校验(默认 true) ) (*DiscoveredResource, error)

函数首先探测x402Version字段以决定反序列化方式:

  • V2:读取PaymentPayload.extensions[bazaar]PaymentPayload.resource
  • V1:读取PaymentRequirements.outputSchemaPaymentRequirements.resource

提取成功后返回DiscoveredResource结构,包含ResourceURLMethod/ToolNameX402VersionDiscoveryInfoDescriptionMimeTypeRouteTemplate等字段。完整流程测试覆盖了 v1/v2、query/body、URL 规范化等场景,见 bazaar_test.go。

从 402 响应中提取已发现资源(客户端侧)

ExtractDiscoveredResourceFromPaymentRequired(facilitator.go)供客户端/接收方解析 402 PaymentRequired 响应:

  • V2:优先检查PaymentRequired.extensions[bazaar],未找到时回退到PaymentRequired.accepts[0]的扩展;资源 URL 取自PaymentRequired.resource
  • V1:检查PaymentRequired.accepts[0].outputSchema;资源 URL 取自PaymentRequired.accepts[0].resource

当未发现任何发现信息时,函数返回nil, nil(不是错误,只是该资源不可发现),这一行为在 facilitator.go 及测试 bazaar_test.go 中均有体现。

扩展校验与安全防护

ValidateDiscoveryExtension(facilitator.go)使用gojsonschemainfoschema序列化后执行 JSON Schema 校验,返回ValidationResult{Valid, Errors},错误信息包含具体的 JSON 上下文路径。另有组合函数ValidateAndExtract一次完成「校验 + 提取」。

值得关注的是routeTemplate 安全校验isValidRouteTemplate,facilitator.go):由于 Facilitator 是信任边界,客户端可篡改支付负载中的routeTemplate造成「目录投毒」(catalog poisoning)。该校验强制:

  • 必须是非空字符串且以/开头;
  • 只能包含安全 URL 路径字符(字母数字、_:/.-~%);
  • 不得包含..(路径穿越,且先解码百分号编码以拦截%2e%2e);
  • 不得包含://(URL 注入)。

对应测试覆盖了空输入、相对路径、..://、空格、百分号编码穿越等攻击面(facilitator_test.go)。

URL 规范化:编目的 canonical 形态

normalizeResourceURL(facilitator.go)负责生成用于发现编目的 canonical URL:若存在routeTemplate(动态路由),则用模板替换 URL 路径并剥离查询串与 fragment;否则仅剥离查询串与 fragment。测试验证了https://api.example.com/users/123?foo=bar#frag会规范化为https://api.example.com/users/:userId(facilitator_test.go)。

动态路由处理:[param]:param*通配符

Bazaar 的服务器侧扩展实现了EnrichDeclaration钩子(server.go),在请求时原子性地将动态路由信息同时注入infoschema。它支持三种路由语法:

  • [param](Next.js 风格);
  • :param(Express 风格);
  • *通配符(自动规范化为:var1:var2等,见normalizeWildcardPattern)。

extractDynamicRouteInfo会把参数化路由模式统一转换为:param模板(routeTemplate),并从实际 URL 路径提取具体参数值(pathParams)。为提升性能,捕获正则与参数名按路由模式缓存在sync.MappatternCache)中,避免每个请求重复编译正则(server.go)。

查询 Facilitator 的发现目录

Bazaar 扩展还提供客户端查询能力:WithBazaar包装HTTPFacilitatorClient,新增ListDiscoveryResources方法(facilitator_client.go),用于查询 Facilitator 的/discovery/resources端点:

client := bazaar.WithBazaar(x402http.NewHTTPFacilitatorClient(nil)) resources, err := client.ListDiscoveryResources(ctx, &bazaar.ListDiscoveryResourcesParams{ Type: "http", Limit: 20, Offset: 0, })

ListDiscoveryResourcesParams支持按协议类型(http/mcp)过滤以及limit/offset分页;返回结构DiscoveryResourcesResponse包含Items列表与Pagination(limit、offset、total)。请求会自动附加认证头(若配置了AuthProvider)。

v1 协议兼容:outputSchema 的转换提取

v1 协议中,发现信息存放在 PaymentRequirements 的outputSchema字段。go/extensions/v1包提供了转换层(v1/facilitator.go):

  • ExtractDiscoveryInfoV1—— 从 v1 PaymentRequirements 提取发现信息并转换为 v2DiscoveryInfo格式。它做了「智能假设」以归一化数据:GET/HEAD/DELETE 查找queryParams/query/params字段;POST/PUT/PATCH 查找bodyFields/body/data/properties字段并归一化bodyType(同时兼容 camelCase 与 snake_case 字段名);还会提取可选的 headers;
  • IsDiscoverableV1—— 判断 v1 支付要求是否包含有效的发现信息;
  • ExtractResourceMetadataV1—— 从 v1 支付要求中提取资源元数据(url、description、mimeType)。

v1 的outputSchema.input中通过"discoverable": true/false显式标记端点是否可被发现(默认视为 true)。两个高层提取函数(ExtractDiscoveredResourceFromPaymentPayload/ExtractDiscoveredResourceFromPaymentRequired)都会自动处理 v1 格式,无需调用方关心版本差异。v1 转换测试覆盖了 GET/POST、bodyFields/bodyParams/properties、snake_case 字段、headerFields、非 discoverable 等场景(bazaar_test.go)。

扩展的类型:按通信方向划分

扩展可以服务于不同的通信模式:

Server ↔ Facilitator 扩展(服务器与 Facilitator 之间):

  • Bazaar(已实现):API 发现与编目;
  • Rate Limiting(规划中):策略声明;
  • Analytics(规划中):用量跟踪元数据。

Server ↔ Client 扩展(服务器与客户端之间):

  • Caching(规划中):缓存策略提示;
  • Retry Policy(规划中):重试指引。

Client ↔ Facilitator 扩展(客户端与 Facilitator 之间):

  • Sign in with X(规划中):身份与认证;
  • Payment Preferences(规划中):代币或网络偏好。

所有扩展遵循相同的 helper 模式,而实现细节留给应用决定。

Types 包:共享类型定义

go/extensions/types子目录(types.go)提供跨扩展的共享定义:

  • 扩展标识符BAZAAR(由x402.NewFacilitatorExtension("bazaar")创建)、PAYMENT_IDENTIFIER("payment-identifier");
  • 方法常量:查询参数方法MethodGET/MethodHEAD/MethodDELETE,请求体方法MethodPOST/MethodPUT/MethodPATCH
  • BodyType 常量json/form-data/text
  • MCP 传输常量streamable-http/sse
  • 联合类型DiscoveryInfo:通过自定义UnmarshalJSON依据type字段判别输入是McpInputtype: "mcp")、BodyInput(有bodyType)还是QueryInput(默认),实现了协议层面「判别联合」(discriminated union)的编解码;
  • 辅助函数IsQueryMethodIsBodyMethod
  • 共享正则ColonParamRegex:param风格路由段),被http/server.goextensions/bazaar/server.go共用以避免漂移。

bazaar/types.go将这些类型全部 re-export,方便使用方通过单一导入路径github.com/x402-foundation/x402/go/extensions/bazaar访问所有常量与类型。

创建新扩展:规范与指南

新扩展类型通过 PR 欢迎贡献。创建新扩展时需要提供:

  1. Specification(规范):定义扩展携带什么元数据以及为什么;
  2. Declaration Helpers(声明辅助):帮助服务器/客户端声明扩展支持的函数;
  3. Extraction Helpers(提取辅助):帮助接收方解析扩展数据的函数;
  4. Validation(校验):JSON Schema 或其他校验机制;
  5. Documentation(文档):说明目的与推荐用法模式(不要求实现)。

应做与不应做

DO(应做):

  • ✅ 提供声明与提取的 helper;
  • ✅ 尽可能使用 JSON Schema 校验;
  • ✅ 文档化扩展的目的与用例;
  • ✅ 保持 helper 的通用性与可复用性;
  • ✅ 在适用处同时支持 v1 与 v2 协议;
  • ✅ 给出可能的实现示例(非要求);
  • ✅ 说明扩展可供哪些角色使用(服务器、客户端、Facilitator)。

DON'T(不应做):

  • ❌ 强制具体的实现选择;
  • ❌ 要求特定的数据库、存储或框架;
  • ❌ 规定应用必须如何使用数据;
  • ❌ 与特定技术建立强耦合;
  • ❌ 让扩展成为基础功能的强制项。

扩展哲学

扩展是促进通信的 helper,而不是实现。扩展应当:通过元数据交换Enable新能力;促进协议参与者之间的conversation;通过 helper 与 schemaprovide structure;始终保持optional(永不成为核心功能必需);保持flexible(支持多种实现路径)。

示例:Bazaar 扩展帮助服务器声明「这是我的 API 结构」,帮助 Facilitator 提取「我收到了 API 元数据」,但它不规定 Facilitator 必须如何编目、存储或暴露这些数据。目标就是让协议参与者能轻松交换结构化信息,同时把实现决策留给应用开发者。

贡献新扩展的步骤

  1. 提议扩展:在 issue 中描述用例;
  2. 定义元数据:扩展携带什么信息;
  3. 创建 helper:服务器声明函数与提取函数;
  4. 添加校验:JSON Schema 或等价物;
  5. 文档化用法:说明目的与推荐模式;
  6. 提供示例:展示服务器与 Facilitator 用法。

具体贡献规范见仓库根目录的 CONTRIBUTING.md。

目录结构与源码导览

go/extensions/的目录结构如下:

go/extensions/ ├── README.md - 本文档(扩展体系总览) ├── types/ - 共享类型定义 │ └── types.go - 扩展常量与公共类型 ├── bazaar/ - Bazaar 发现扩展(示例实现) │ ├── doc.go - 包文档与用法示例 │ ├── server.go - 服务器侧 helper(EnrichDeclaration、动态路由) │ ├── facilitator.go - Facilitator 侧 helper(提取、校验、安全) │ ├── facilitator_client.go - 发现目录查询客户端 │ ├── types.go - Bazaar 专用类型 re-export │ ├── resource_service.go - 声明 helper(HTTP 与 MCP) │ ├── bazaar_test.go - 扩展测试 │ ├── facilitator_test.go - 内部辅助函数测试 │ └── facilitator_client_test.go ├── v1/ - v1 协议扩展支持 │ └── facilitator.go - v1 提取 helper └── [未来扩展] - 欢迎通过 PR 贡献! ├── eip2612gassponsor/ - EIP-2612 燃气赞助扩展 ├── erc20approvalgassponsor/ - ERC-20 授权燃气赞助扩展 ├── paymentidentifier/ - 支付标识符扩展 ├── sign-in-with-x/ - (规划中)身份扩展 ├── rate-limiting/ - (规划中)限流策略 └── caching/ - (规划中)缓存策略

从源码结构看,除 Bazaar 外,仓库还实现了三个功能型扩展包(eip2612gassponsorerc20approvalgassponsorpaymentidentifier),它们与 Bazaar 一起构成了当前扩展生态的实例。

注意:Bazaar 只是扩展如何工作的一个示例。每个新扩展都有自己的子目录,遵循「声明 helper + 提取 helper + 校验」的相同模式。

关联文档

  • Bazaar 扩展规范—— 协议级完整 JSON 示例与字段定义;
  • Bazaar 扩展实现—— 声明、提取、校验与客户端查询源码;
  • Main README—— Go 包总览;
  • SERVER.md—— 在资源服务器中使用扩展;
  • FACILITATOR.md—— 在 Facilitator 中提取扩展。

【免费下载链接】x402A payments protocol for the internet. Built on HTTP.项目地址: https://gitcode.com/GitHub_Trending/x4/x402

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询