Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

从编码工具发起支付

下面的示例展示如何为 AI 编码工具加入 x402 支付处理能力。当工具收到 402 响应时,会自动付款并重试。

两个示例都需要:

  • 一个钱包私钥(通过环境变量 X402_PRIVATE_KEY 设置)
  • x402 相关包: @x402/fetch@x402/evmviem

OpenCode 插件

OpenCode 插件向 agent 暴露工具。要创建一个能处理 402 响应的 x402-fetch 工具,请新建 .opencode/plugins/x402-payment.ts:

TypeScript


// Use base-sepolia for testing. Get test USDC from https://faucet.circle.com/

import type { Plugin } from "@opencode-ai/plugin";

import { tool } from "@opencode-ai/plugin";

import { x402Client, wrapFetchWithPayment } from "@x402/fetch";

import { registerExactEvmScheme } from "@x402/evm/exact/client";

import { privateKeyToAccount } from "viem/accounts";


export const X402PaymentPlugin: Plugin = async () => ({

  tool: {

    "x402-fetch": tool({

      description:

        "Fetch a URL with x402 payment. Use when webfetch returns 402.",

      args: {

        url: tool.schema.string().describe("The URL to fetch"),

        timeout: tool.schema.number().optional().describe("Timeout in seconds"),

      },

      async execute(args) {

        const privateKey = process.env.X402_PRIVATE_KEY;

        if (!privateKey) {

          throw new Error("X402_PRIVATE_KEY environment variable is not set.");

        }


        // Your human-in-the-loop confirmation flow...

        // const approved = await confirmPayment(args.url, estimatedCost);

        // if (!approved) throw new Error("Payment declined by user");


        const account = privateKeyToAccount(privateKey as `0x${string}`);

        const client = new x402Client();

        registerExactEvmScheme(client, { signer: account });

        const paidFetch = wrapFetchWithPayment(fetch, client);


        const response = await paidFetch(args.url, {

          method: "GET",

          signal: args.timeout

            ? AbortSignal.timeout(args.timeout * 1000)

            : undefined,

        });


        if (!response.ok) {

          throw new Error(`${response.status} ${response.statusText}`);

        }


        return await response.text();

      },

    }),

  },

});


当内置的 webfetch 返回 402 时,agent 会调用 x402-fetch 携带支付信息重试。

Claude Code 钩子

Claude Code 钩子(hook)可以拦截工具的执行结果。要透明地处理 402 响应,请创建脚本 .claude/scripts/handle-x402.mjs:

JavaScript


// Use base-sepolia for testing. Get test USDC from https://faucet.circle.com/

import { x402Client, wrapFetchWithPayment } from "@x402/fetch";

import { registerExactEvmScheme } from "@x402/evm/exact/client";

import { privateKeyToAccount } from "viem/accounts";


const input = JSON.parse(await readStdin());


const haystack = JSON.stringify(input.tool_response ?? input.error ?? "");

if (!haystack.includes("402")) process.exit(0);


const url = input.tool_input?.url;

if (!url) process.exit(0);


const privateKey = process.env.X402_PRIVATE_KEY;

if (!privateKey) {

  console.error("X402_PRIVATE_KEY not set.");

  process.exit(2);

}


try {

  // Your human-in-the-loop confirmation flow...

  // const approved = await confirmPayment(url);

  // if (!approved) process.exit(0);


  const account = privateKeyToAccount(privateKey);

  const client = new x402Client();

  registerExactEvmScheme(client, { signer: account });

  const paidFetch = wrapFetchWithPayment(fetch, client);


  const res = await paidFetch(url, { method: "GET" });

  const text = await res.text();


  if (!res.ok) {

    console.error(`Paid fetch failed: ${res.status}`);

    process.exit(2);

  }


  console.log(

    JSON.stringify({

      hookSpecificOutput: {

        hookEventName: "PostToolUse",

        additionalContext: `Paid for "${url}" via x402:\n${text}`,

      },

    }),

  );

} catch (err) {

  console.error(`x402 payment failed: ${err.message}`);

  process.exit(2);

}


function readStdin() {

  return new Promise((resolve) => {

    let data = "";

    process.stdin.on("data", (chunk) => (data += chunk));

    process.stdin.on("end", () => resolve(data));

  });

}


.claude/settings.json 中注册该钩子:


{

  "hooks": {

    "PostToolUse": [

      {

        "matcher": "WebFetch",

        "hooks": [

          {

            "type": "command",

            "command": "node .claude/scripts/handle-x402.mjs",

            "timeout": 30

          }

        ]

      }

    ]

  }

}


相关内容