Skip to content

Agent 支付(x402 协议)

AI Agent 可以使用 x402 协议直接从自己的钱包支付 API 调用费用 — 无需 API Key。服务器返回 HTTP 402 并附带支付要求,Agent 签名加密支付授权即可继续。

x402 vs API Key

JarvisClaw 支持两种认证方式 — 底层都使用 x402:

方式请求头工作原理
API KeyAuthorization: Bearer sk-...平台代你用 HD 钱包签名 x402(User HD → x402 → JC1)
私钥 (x402 直连)PAYMENT-SIGNATURE: <base64>你的 Agent 直接从自己的钱包签名 x402

X-PAYMENT 也可用

X-PAYMENTPAYMENT-SIGNATURE 的等价别名,承载相同的 base64 载荷 —— 如果你的 x402 客户端库 使用生态标准名称,可直接用它。两者同时传入时以 PAYMENT-SIGNATURE 为准。

两种方式都通过 x402 链上结算

使用 API Key 时,服务器自动从你的平台 HD 钱包执行 x402 支付流程 — 你无需处理 402 → 签名 → 重试循环。使用私钥时,你的 Agent 自行处理该循环(SDK 透明完成)。

该选哪个?

  • API Key — 最简集成。像任何标准 API 一样使用。你的 HD 钱包余额(Base USDC 或 Solana USDC)会自动扣费,支持多链 fallback。
  • 私钥 — 完全自主。你的 Agent 持有自己的密钥并按次支付。最适合管理自有资金的自主 Agent。

工作原理

使用私钥(直连 x402)

  1. Agent 发送无认证头的请求。
  2. 服务器返回 HTTP 402 并附带支付要求(金额、Token、链、收款地址)。
  3. Agent 用钱包私钥签名 x402 支付授权。
  4. Agent 携带 PAYMENT-SIGNATURE 头重发请求。
  5. 服务器通过 CDP facilitator 合约验证支付并返回响应。

使用 API Key(平台托管 x402)

  1. 用户携带 Authorization: Bearer sk-... 头发送请求。
  2. 服务器查找用户的 HD 钱包(Base 或 Solana)。
  3. 服务器自动从 HD 钱包执行 x402 结算(多链 fallback:Base → Solana,当 Base 余额不足时)。
  4. 成功时返回响应。结算失败时返回 402 并提示充值。

支持的链

网络 ID密钥格式Token状态
Base (EVM)eip155:8453十六进制私钥 (0x...)USDC✅ 完全支持
Solanasolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpBase58 密钥对USDC (SPL)✅ Python SDK

以上是全部支持的支付链。其他页面出现的链(例如 DEX 交换的目标链、RPC 的多链查询) 指的是被查询或交易的链,不是我们收款的链 —— 付费始终发生在 Base 或 Solana。

方法 1:Python SDK

Python SDK 自动处理 402 握手(获取价格 → 签名 → 重试),你只需提供私钥。

安装

shell
pip install jarvisclaw[agent]     # Base (EVM)
pip install jarvisclaw[solana]    # Solana
pip install jarvisclaw[all]       # 全部

EVM(Base 链)

python
from jarvisclaw import ChatClient, ImageClient, AudioClient, SearchClient

# 所有客户端都接受 private_key — SDK 处理 402 流程
chat = ChatClient(private_key="0x<your-hex-private-key>")
response = chat.complete("你好!")
print(response)

# 检查余额
print(f"余额: ${chat.get_balance():.2f} USDC")

# 其他客户端使用相同方式
image = ImageClient(private_key="0x<your-hex-private-key>")
result = image.generate("火星上的猫")

audio = AudioClient(private_key="0x<your-hex-private-key>")
result = audio.speech("你好世界", voice="sarah")

search = SearchClient(private_key="0x<your-hex-private-key>")
results = search.query("最新 AI 新闻")

Solana

python
from jarvisclaw import ChatClient

# 传入 Solana 密钥对(base58,从 Phantom/Solflare 导出)
chat = ChatClient(private_key="<your-base58-solana-keypair>")

# 相同 API — SDK 从密钥格式检测 Solana 并签名 SPL 转账
response = chat.complete("来自 Solana 的问候!")
print(response)

# 查看 SOL 链 USDC 余额
print(f"余额: ${chat.get_balance():.2f} USDC")

异步

python
from jarvisclaw.aio import ChatClient

async with ChatClient(private_key="0x<your-hex-private-key>") as chat:
    text = await chat.complete("说声你好")
    print(text)

方法 2:Go SDK

Go SDK 支持 Base (EVM) 上的 x402 支付,自动处理 402。

安装

shell
go get github.com/api-jarvisclaw/go-sdk/v2

EVM(Base 链)

go
package main

import (
    "context"
    "fmt"
    "log"

    jarvisclaw "github.com/api-jarvisclaw/go-sdk/v2"
)

func main() {
    ctx := context.Background()

    // x402 钱包认证(无需 API Key)
    client, err := jarvisclaw.NewChatClient(
        jarvisclaw.WithPrivateKey("0x<your-hex-private-key>"),
    )
    if err != nil {
        log.Fatal(err)
    }

    text, err := client.Complete(ctx, "来自 Go x402 的问候!")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(text)

    // 查询链上 USDC 余额
    balance, _ := client.GetBalance(ctx)
    fmt.Printf("钱包余额: $%.4f USDC\n", balance)
}

构造函数返回 error

jarvisclaw.NewChatClient 返回 (*ChatClient, error)。忽略第二个返回值无法编译。

API Key(无 x402)

go
// 传统 API Key 认证 — 不触发 402 流程
client, _ := jarvisclaw.NewChatClient(
    jarvisclaw.WithAPIKey("sk-..."),
)

Go SDK 暂不支持 Solana

Go SDK 当前仅支持 EVM(Base 链)。如需 Solana x402 支付,请使用 Python SDK。


方法 3:原始 Python(手动 x402 握手)

如果不想使用 SDK,可以用 requestseth_account 手动实现 x402 支付流程。

依赖

shell
pip install requests eth_account

EVM(Base 链)逐步实现

python
"""手动 x402 支付流程 — 无需 SDK。"""
import base64
import json
import os
import time

import requests
from eth_account import Account
from eth_account.messages import encode_typed_data

BASE_URL = "https://api.jarvisclaw.ai"
PRIVATE_KEY = os.environ["WALLET_PRIVATE_KEY"]   # 0x...

account = Account.from_key(PRIVATE_KEY)

# ── 步骤 1:发送请求(无认证)→ 获得 402 ──
resp = requests.post(
    f"{BASE_URL}/v1/chat/completions",
    headers={"Content-Type": "application/json"},
    json={"model": "auto", "messages": [{"role": "user", "content": "你好!"}]},
)
if resp.status_code != 402:
    print(resp.json())
    raise SystemExit

# ── 步骤 2:解析支付要求 ──
body = resp.json()
payments = body.get("accepts", body.get("payments", []))

payment = next((p for p in payments if p.get("network", "").startswith("eip155:")), None)
if not payment:
    raise ValueError("402 响应中没有 EVM 支付选项")

pay_to = payment["payTo"]
amount = int(payment["amount"])              # 单位为微 USDC(6 位小数)
network = payment["network"]                 # 例如 "eip155:8453"
asset = payment["asset"]                     # USDC 合约地址
max_timeout = payment.get("maxTimeoutSeconds", 300)

# facilitator 是**顶层**字段,不在 payment["extra"] 里。
# EVM 选项的 extra 装的是 EIP-712 domain:{"name", "version"}。
facilitator = body.get("facilitator", "")
domain_name = payment.get("extra", {}).get("name", "USD Coin")
domain_version = payment.get("extra", {}).get("version", "2")

chain_id = int(network.split(":")[1])        # Base 主网为 8453

# ── 步骤 3:签名 EIP-712 TransferWithAuthorization(EIP-3009)──
valid_after = 0
valid_before = int(time.time()) + max_timeout
nonce = os.urandom(32)

domain = {
    "name": domain_name,
    "version": domain_version,
    "chainId": chain_id,
    "verifyingContract": asset,
}
message = {
    "from": account.address,
    "to": pay_to,
    "value": amount,
    "validAfter": valid_after,
    "validBefore": valid_before,
    "nonce": nonce,
}
types = {
    "TransferWithAuthorization": [
        {"name": "from", "type": "address"},
        {"name": "to", "type": "address"},
        {"name": "value", "type": "uint256"},
        {"name": "validAfter", "type": "uint256"},
        {"name": "validBefore", "type": "uint256"},
        {"name": "nonce", "type": "bytes32"},
    ],
}
signed = account.sign_message(encode_typed_data(domain, types, message))

# ── 步骤 4:构造 PAYMENT-SIGNATURE 载荷 ──
payload = {
    "x402Version": 2,
    "scheme": "exact",
    "network": network,
    "payload": {
        "signature": signed.signature.hex(),
        "authorization": {
            "from": account.address,
            "to": pay_to,
            "value": str(amount),
            "validAfter": str(valid_after),
            "validBefore": str(valid_before),
            "nonce": nonce.hex(),
        },
    },
    "extensions": {},
}
signature_header = base64.b64encode(
    json.dumps(payload, separators=(",", ":")).encode()
).decode()

# ── 步骤 5:携带 PAYMENT-SIGNATURE 头重试 ──
resp2 = requests.post(
    f"{BASE_URL}/v1/chat/completions",
    headers={"Content-Type": "application/json", "PAYMENT-SIGNATURE": signature_header},
    json={"model": "auto", "messages": [{"role": "user", "content": "你好!"}]},
)
print(resp2.json()["choices"][0]["message"]["content"])

402 响应结构

需要付费时服务端返回:

json
{
  "x402Version": 2,
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount": "1000",
      "payTo": "0x<seller-address>",
      "maxTimeoutSeconds": 300,
      "extra": { "name": "USD Coin", "version": "2" }
    }
  ],
  "facilitator": "https://api.cdp.coinbase.com/platform/v2/x402",
  "resource": {
    "url": "https://api.jarvisclaw.ai/v1/chat/completions",
    "description": "Chat completion",
    "mimeType": "application/json",
    "tag": "ai-model"
  },
  "extensions": {
    "bazaar": {
      "info": { "input": { "type": "http", "method": "POST", "discoverable": true } },
      "schema": { "properties": { "input": {}, "output": {} } }
    }
  }
}

amount 以代币最小单位计(USDC 为 6 位小数,故 1000 = $0.001)。

结构上有两点需要注意:

  • facilitator 是顶层字段,不在 accepts[].extra 内。
  • extra 是 EIP-712 domain:EVM 选项为 nameversion;Solana 选项为 feePayer

同一份 JSON 也会以 base64 编码放在 PAYMENT-REQUIRED 响应头中;extensions.bazaar 携带 x402scan 用于判定端点可调用的输入/输出 schema。

配置了 Solana 已知 fee payer 时,服务端会追加第二个支付选项。fee payer 未解析出来之前只列 Base —— 没有它无法构造 SVM 交易。

SDK 会依据钱包密钥格式自动选择:十六进制私钥(0x...)选 eip155:*,Base58 密钥对选 solana:*


费用与安全

  • SDK 内置单次请求安全上限(最高 100 USDC)。服务端要价超过该值时 SDK 会抛错。
  • client.get_spending() 查看本会话累计花费(估算值)。
  • client.get_balance() 查看链上剩余 USDC 余额。
  • 所有支付都是链上授权 —— 你的钱包只签名,不直接转出代币。facilitator 合约在服务端交付响应后 才执行转账。

环境变量

变量说明
JARVISCLAW_API_KEYBearer 认证用的 API Key(与钱包模式互斥)
JARVISCLAW_WALLET_KEY钱包私钥 —— EVM 用十六进制,Solana 用 base58(SDK 自动识别)
JARVISCLAW_BASE_URL覆盖 API 基础地址(默认 https://api.jarvisclaw.ai

HD 钱包系统

每个用户注册时自动生成 HD 钱包(Base + Solana)。当使用 API Key 调用时,平台从 HD 钱包余额自动执行 x402 结算。

充值方式

  1. USDC 转账 — 向你的 HD 钱包地址发送 USDC
  2. 信用卡/支付宝 — 通过 Airwallex 充值(自动转为链上 USDC)
  3. 直接持有 — Agent 模式下自行管理钱包余额

多链 Fallback

使用 API Key 且 HD 钱包在 Base 与 Solana 上都有 USDC 时,平台会按顺序自动尝试结算:

  1. 优先 Base (EVM) —— L2 手续费最低、确认最快。
  2. 若 Base 结算失败(余额不足、网络错误),自动回退到 Solana
  3. 两条链都失败则返回错误 —— 不会调用上游供应商

TIP

该回退对调用方透明。任一条链(或两条)有余额即可,平台自行选择最优路径,无需配置开关。

结算安全规则

平台强制执行一条规则:若用户钱包在所有链上都结算失败,绝不调用上游供应商。这避免平台为未成功 收费的请求垫付成本。

Solana ATA 预检

尝试 Solana x402 结算前,平台会先做关联代币账户(ATA)预检

检查项验证内容
发送方 ATA付款钱包已初始化 USDC SPL 代币账户
余额该 ATA 持有足额 USDC

若 ATA 不存在或余额为零,Solana 路径会被立即跳过(不做失败的交易模拟),转而回退到 Base 或返回错误。

常见问题

从未持有过 USDC 的新 Solana 钱包在链上没有初始化 ATA。你必须先收到至少一笔 USDC 转账 (哪怕 0.000001)来创建 ATA,之后 x402 才能在 Solana 上完成结算。

如何初始化 Solana USDC ATA

  1. 向你的 Solana 钱包地址发送任意数量的 USDC(SPL)—— 这会自动创建 ATA。
  2. 或用 Solana 钱包应用(Phantom、Solflare)添加 USDC 代币 —— 部分钱包会主动创建 ATA。
  3. 平台的 /v1/wallet/balance 端点会返回 solana 下的余额 —— 有返回值即表示 ATA 已就绪。