作者发现 x402 接口完成真实付款后仍未进入 Bazaar 目录,原因是部分买方客户端未将发现声明传入支付请求。目录读取的是 /verify 或 /settle 收到的载荷,官方 @x402/fetch 封装会回传相关字段。
利益披露:我运营 ScrapeMole,提供一小组按调用付费的 x402 API。这篇文章讲的是我们在自己的部署中遇到的一个服务发现问题;这个修复方法适用于任何 x402 卖家。
我们的 endpoint 已经在 Base mainnet 上线约一天。真实的外部钱包付费调用了 18 次,每次都通过 PayAI facilitator 完成结算,而且每个 402 响应都携带有效的 extensions.bazaar 声明。
然而,当我们用自己的收款地址查询 facilitator 的公开目录时:
curl "https://facilitator.payai.network/discovery/resources?payTo=0xYOUR_PAY_TO&limit=100"
# {"items":[], "pagination":{"total":0}}
结果为零。与此同时,我们的 testnet endpoint 却在目录中。
Bazaar 类型的目录(包括 PayAI 的目录,以及按 x402 规范实现的任何 facilitator 目录)不会抓取你的 402 响应。它们读取的是发送到 /verify 或 /settle 的支付 payload 中的服务发现声明。在 x402 v2 中,买家的客户端应当把你的 402 响应里的 extensions(以及 resource)复制到这个 payload 中。
许多买家客户端并没有这样做。因此,即使你已经完成了大量真实结算,也可能始终没有被收录。我们的 testnet endpoint 之所以能出现在目录里,只是因为我们自己的 e2e 测试客户端使用了官方的 @x402/fetch wrapper,而它确实会回传这个 extension。
PayAI 的文档明确说明了这一点:仅仅成功结算,并不会让资源进入目录;payload 必须携带 bazaar extension。现在,它们的 /verify 和 /settle 会返回 EXTENSION-RESPONSES header,让你能看出实际发生了什么:
你只需要让一个携带 extension 的 payload 到达 facilitator,调用 /verify 就够了。对于 GET 资源,只要目录自己对你的 URL 发起只读探测时收到 402 响应,就会收录它。
所以,具体做法是:用你自己的收款钱包签署一笔向自身付款的支付(即使真的发生结算,净额也为零),附上线上 402 响应中的 bazaar extension,然后只发送到 /verify,绝不发送到 /settle。
import { privateKeyToAccount } from 'viem/accounts';
import { x402Client } from '@x402/core/client';
import { ExactEvmScheme } from '@x402/evm';
const acct = privateKeyToAccount(process.env.PAY_TO_KEY); // your receive wallet
const client = new x402Client().register('eip155:8453', new ExactEvmScheme(acct));
for (const url of process.argv.slice(2)) {
const r = await fetch(url); // unpaid -> 402
const pr = JSON.parse(Buffer.from(r.headers.get('payment-required'), 'base64'));
const payload = await client.createPaymentPayload(pr);
payload.extensions ??= { bazaar: pr.extensions.bazaar }; // make sure it's echoed
payload.resource ??= pr.resource;
const v = await fetch('https://facilitator.payai.network/verify', {
method: 'POST', headers: { 'content-type': 'application/json' },
body: JSON.stringify({ x402Version: 2, paymentPayload: payload, paymentRequirements: payload.accepted ?? pr.accepts[0] }),
});
const ext = v.headers.get('extension-responses');
console.log(v.status, url, ext && Buffer.from(ext, 'base64').toString());
}
钱包的 USDC 余额需要至少达到调用价格,/verify 才会返回 isValid: true。不会发生任何资金转移,因为我们从不调用 /settle。
我们对自己的 40 个 mainnet GET endpoint 运行了这段代码。每个都返回了 {"bazaar":{"status":"processing"}},约一分钟后,上面的目录查询返回了 total: 40。
对你用于结算的每一个 facilitator,查询 /discovery/resources?payTo=<your address>。不要以为完成结算就意味着已经被收录。
在你自己的测试中,检查是否存在 EXTENSION-RESPONSES header。如果没有,说明你的客户端没有回传 extensions。
每个 facilitator 都有自己的目录。被 PayAI 的目录收录,并不代表也被 Coinbase CDP 的 Bazaar 收录;后者只能看到通过 CDP 结算的 payload。
添加 endpoint 后,重新执行 verify 步骤。新路由只有在携带其声明的某个 payload 到达 facilitator 后,才会出现在目录中。
我们查看了向我们付款的两个钱包(钱包活动在 Basescan 上公开可见),发现它们也在相同的几分钟内向其他几家 x402 卖家付款,大多通过 Coinbase facilitator。其中一个钱包曾直接向另一个钱包转入资金,所以很可能是同一个操作者在运行一个研究 Agent,调用许多付费 API。我们从中得到的教训是:买家会跨目录寻找服务,因此,缺席某个目录意味着损失销售机会,而不只是展示上少了一项。
如果你也在销售 x402 API,并且遇到过其他服务发现方面的坑,欢迎在评论区分享。
如果需要进一步采取行动,你可以考虑屏蔽此人和/或举报滥用行为。