HTTP 402状态码返回了,协议看起来完成了。但x402scan目录拒绝了这个来源——问题出在两个不同的bug上。
Tek Labs团队在开发x402-linter服务时遇到了这个情况。这个服务本身由Payment Required机制保护:POST /lint广告10000个原子USDC(0.01美元),POST /health广告1000个(0.001美元)。两者都使用精确方案,网络为eip155:8453(Base链),资产为Base USDC,支付对象指向指定地址。
第一版构建仅限本地运行。没有钱包密钥,没有协调器,没有结算。缺少PAYMENT-SIGNATURE时总是返回402,除非设置了本地绕过头。任务从来不是"发布付费产品",而是:返回有效的v2 Payment Required响应,然后看看公共目录(x402scan)是否接受这个来源。这两个检查并不相同。
第一个bug:402只是开始
未付费的POST /lint已经返回HTTP 402,附带JSON格式的PaymentRequired响应体。本地对自己的/health端点进行冒烟测试,状态检查通过了。这让人感觉协议已经完成——但事实并非如此。
自己的linter将同一个402评为不完整。状态402只是发现之一。v2 HTTP传输还要求PAYMENT-REQUIRED头:与响应体相同的JSON的base64编码。缺少这个头,lint报告会标记为missing_header。跨源客户端除非CORS暴露,否则永远看不到自定义头。即使响应体看起来正确,只返回402而不带这个头也是协议bug。
第二个bug:隧道不是列表
团队将trycloudflare主机名指向同一进程,以便目录可以获取它。x402scan没有对x402Version或accepts提出异议,而是直接拒绝了来源:
"隧道URL是临时的,代理无法可靠发现。请将API部署到永久URL进行注册。"
来源是https://arranged-players-computation-pipeline.trycloudflare.com——这是一个隧道,不是列表条目。registerFromOrigin永远不会在一个随进程消亡的主机名上生效。
正确的402在localhost上,甚至公共隧道上的正确402,仍然无法实现真正想要的目标:持久发现。
团队把402当作检查清单,而不是状态码。
状态:未付费的POST /lint和POST /health必须是402。其他任何状态都是http_status,直接硬性扣分。这部分已经通过了。
头:linter读取payment-required头(HTTP不区分大小写)。它对该值进行base64解码并JSON.parse。如果失败,发现项为header_b64。如果成功,同一个对象会像响应体一样被遍历:x402Version必须等于2,resource.url必须存在,accepts必须是非空数组,第一个accept必须使用精确方案,CAIP-2网络(必须带冒号),金额为字符串。
这次调试的教训很直接:协议合规不等于可被发现。一个返回正确402的临时隧道,和一个能被目录接受的持久端点,是两件完全不同的事。检查清单上的每一项都通过,不代表整个系统就能工作。
热门跟贴