x402集市:从服务发现到支付

一个实战演示:AI 代理发现 API、收到 402 响应、通过协调器支付、然后重试获取结果

x402集市:从服务发现到支付
一键发币: x402兼容 | Aptos | X Layer | SUI | SOL | BNB | ETH | BASE | ARB | OP | Polygon | Avalanche

大多数 API 市场仍然假设有人类参与其中——创建账户、添加银行卡、生成密钥、管理用量。这些摩擦是你的 AI 代理无法处理的。

Bazaar + x402 颠覆了这种模式。你的代理可以发现服务、收到带有支付详情的 HTTP 402(需要付费)响应、进行支付(通常使用 Base 上的 USDC)、携带支付证明重试相同的请求,然后 API 响应——无需账户,无需订阅

在本文结束时,你将拥有一个可运行的演示,展示完整的端到端流程:列表 → 发现 → 402 → 支付 → 重试 → 使用

1、你将构建什么

  • 🧭 发布一个最小的 Bazaar 风格的列表,让你的代理可以发现
  • 💳 模拟 x402 支付流程(HTTP 402 → 协调器结算 → 支付证明)
  • 🔁 携带支付证明重试相同的请求,获取 付费 响应
  • 🧪 所有操作在一个可在本地运行的 Node.js 脚本中完成

2、系统架构

Agent (buyer) ──> Bazaar catalog ──> Service endpoint
    │                                   │
    │ (Request)                          │ If unpaid:
    │──────────────────────────────────> │  returns HTTP 402 + payment details
    │                                    │
    │ --pay via Facilitator------------> Facilitator (verifies/settles, returns proof)
    │                                    │
    │ (Retry + proof header)            │
    └──────────────────────────────────> │ Returns the result

组件(快速概览)

  • Bazaar:简单的发现层,返回包含名称、价格、资产、网络的列表信息。
  • Service(服务):你的 API 端点,要求按请求付费。如果未支付,它会返回 HTTP 402 和支付详情。
  • x402 Facilitator(协调器):验证/结算客户端的支付,并颁发你的服务可以信任的支付证明
  • Agent(代理):一个精简的买方脚本,负责发现服务、调用服务、处理 402、通过协调器支付、携带证明重试并打印结果。

3、实现阶段

代码将分为两部分:卖方买方

3.1 项目设置

mkdir x402-baazar
cd x402-baazar

npm init -y

3.2 安装依赖

npm i @coinbase/cdp-sdk @coinbase/x402 dotenv express viem x402-express x402-fetch

我们将首先构建一个简单的石头-纸-剪刀端点。然后,我们将把它与 x402 集成,使每个请求都可以付费,并提供必要的配置以便在 Bazaar 中列出服务。创建一个名为 index.js 的文件。

3.3 使用 Express 设置端点

import express from "express";

const moves = ["rock", "paper", "scissors"];

function getOutcome(player, server) {
  if (player === server) return "draw";

  if (
    (player === "rock" && server === "scissors") ||
    (player === "paper" && server === "rock") ||
    (player === "scissors" && server === "paper")
  ) {
    return "win";
  }

  return "lose";
}

const app = express();
app.use(express.json());

app.post("/rps/play", (req, res) => {
  try {
    const { move } = req.body;

    if (!move || !moves.includes(move)) {
      res.status(400).send({
        error: "Move must be rock, paper, or scissors"
      });
      return;
    }

    const serverMove = moves[Math.floor(Math.random() * moves.length)];
    const outcome = getOutcome(move, serverMove);

    res.send({
      playerMove: move,
      serverMove,
      outcome
    });

  } catch (err) {
    res.status(500).send({ error: "Internal server error" });
  }
});

app.listen(4021, () => {
  console.log(`Server listening at http://localhost:4021`);
});

现在为上述端点设置 x402 和 Bazaar 所需的配置

// … existing imports
import { paymentMiddleware } from "x402-express";
import { facilitator } from "@coinbase/x402";

// after `app.use(express.json())`

app.use(
  paymentMiddleware(
    "0xB1De43C2Ca1195258FEE160adAcB1820c3776B7D",
    {
      "POST /rps/play": {
        price: "$0.001", // USDC testnet price
        network: "base",
        config: {
          name: "Rock-Paper-Scissors",
          description: "Pay to play a simple game of Rock-Paper-Scissors.",
          discoverable: true,
          inputSchema: {
            type: "object",
            properties: {
              move: {
                type: "string",
                enum: ["rock", "paper", "scissors"],
                description: "Your move"
              }
            },
            required: ["move"]
          },
          outputSchema: {
            type: "object",
            properties: {
              playerMove: { type: "string" },
              serverMove: { type: "string" },
              outcome: { type: "string" }
            }
          }
        }
      }
    },
    {
      facilitator
    }
  )
);

// before `app.post("/rps/play"…`

paymentMiddleware 将 x402 作为 Express 中间件集成到本项目中(在这里了解更多关于 Express 中间件的信息)。

它接受两个参数:

  1. 收款地址 — 接收该端点付款的钱包地址。
  2. Bazaar 配置 — 描述你在 Bazaar 目录中服务元数据,使 AI 代理可以发现它。

配置中的 discoverable 标志设置为 true,这确保你的 API 会自动列在 Bazaar 目录中。

注意:要使你的 API 可被 Bazaar 索引,你必须将其部署到公有云(例如 Railway、AWS 等),并确保它可以被公开访问。

接下来,让我们创建一个模拟买方的简单函数:

  • 它查询 Bazaar 目录以列出可用的服务。
  • 选择我们的石头-纸-剪刀服务。
  • 在每次请求时支付 USDC 来玩游戏。

现在创建另一个名为 buyer.js 的文件,从列出目录开始:

const listCatalog = async () => {
  const response = await fetch(
    "https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources"
  );

  const res = await response.json();
  console.log(res);
};

const run = async () => {
  const catalog = await listCatalog();
};

run();

这将返回一个对象数组:

{
 "items": […]
}

现在我们有了服务列表,在发起调用之前还需要设置一些前置条件:

钱包设置 — 我们需要一个钱包来支付服务费用。最快的选择是使用 CDP 钱包,它将所有复杂性抽象为两行代码。或者你也可以使用自己的钱包,但需要自己处理完整的 viem 配置。

发起 API 调用 — 有两种方式:

  • 手动方式:使用 fetch 或 axios 调用 API,收到 402 Payment Required 响应,进行支付,然后重试。
  • 自动方式:使用 CDP 封装的 fetch 或 axios,它们会自动处理整个支付流程。

在本教程中,我们将使用 x402-fetch 来封装 fetch。

import { CdpClient } from "@coinbase/cdp-sdk";
import dotenv from "dotenv";
import {
  wrapFetchWithPayment,
  decodeXPaymentResponse
} from "x402-fetch";

dotenv.config();

// Setup CDP account or bring your own wallet
const cdp = new CdpClient();
const cdpAccount = await cdp.evm.getAccount({
  name: "buyer-account"
});

// feel free to enable this in testnet, perks for using CDP 😅
// await cdp.evm.requestFaucet({
//   address: cdpAccount.address,
//   network: "base-sepolia",
//   token: "usdc"
// });

// providing x402 super power to our good old `fetch`
const fetchWithPayment = wrapFetchWithPayment(fetch, cdpAccount);

// … previous listCatalog function

const run = async () => {
  try {
    const catalog = await listCatalog();

    // choosing a service, assuming that it's our RPS API
    const service = catalog.items[0];

    const response = await fetchWithPayment(service.resource, {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({ move })
    });

    const result = await response.json();
    console.log("Game result:", result);

    const paymentResponse = decodeXPaymentResponse(
      response.headers.get("x-payment-response")
    );

    console.log(paymentResponse);
  } catch (error) {
    console.log(error);
  }
};

run();

使用 fetchWithPayment 可以确保当 API 返回 402 Payment Required 时,支付会被自动处理并且请求会自动重试——因此你无需自己管理支付流程即可获得结果。

4、运行应用

卖方

node index.ts

买方

node buyer.js

你将看到:

  1. 服务器启动
  2. 买方发现服务
  3. 第一次调用返回 HTTP 402 及支付详情
  4. 测试网协调器颁发收据
  5. 携带 X-PAYMENT 头重试
  6. 服务器返回有效响应

5、从演示到生产环境的映射

  • Bazaar UI 和目录:在生产环境中,这是一个带有评分、定价、延迟和网络信息的托管目录。你的代理会选择匹配预算和政策约束的服务提供商。
  • x402:模式完全相同——服务对未支付请求返回 402,代理通过协调器完成结算,然后携带支付证明头重试完全相同的 HTTP 请求。
  • 协调器:在生产环境中,你会使用 Coinbase 的默认协调器处理 Base 上的 USDC(或者运行自己的协调器/选择社区协调器)。它的职责是:验证支付并颁发你的服务可以信任的支付证明。
  • 服务:你的 API 应该将 402 视为一份合约:明确指定金额、资产、网络、卖家和协调器 URL,然后在重试时验证协调器的支付证明后再返回数据。

6、加固与扩展

  • 重放保护:将支付证明绑定到 paymentRequestId + 方法 + 路径 + 查询参数,并设置快速过期。注意:CDP 协调器默认设计已考虑这一点——提供 60 秒的支付和重试窗口。
  • 分级响应:提供免费预览,对高分辨率/长时间运行收费。
  • 动态定价:在 402 响应体中根据输入大小(如音频秒数、token 数)报价。
  • 服务提供商切换:你的代理可以比较评分/价格/延迟,在工作流中途切换提供商,继续执行。
  • 链上收据:包含交易哈希和协调器证明,将链下工作与链上支付锚定。

7、结束语

你刚刚运行了一个完整的、对代理友好的 Bazaar + x402 循环:发现服务,调用并收到 402 Payment Required 状态,通过协调器支付携带证明重试,并获得付费结果——无需账户,无需订阅,按请求付费


原文链接: Ship a 402-Powered API Bazaar with x402 — From Discovery to Paid Response in One Script

DefiPlot翻译整理,转载请标明出处

免责声明:本站资源仅用于学习目的,也不应被视为投资建议,读者在采取任何行动之前应自行研究并对自己的决定承担全部责任。