单次支付#
HTTP 卖家 重点看:业务流程 → HTTP 卖家接入(scheme 选型 → exact 路径 / charge 路径 → syncSettle 决策)→ 进阶(分账、同时支持 exact + charge)
Agent 卖家 重点看:业务流程 → Agent 卖家接入(付款链接生成与分发)
定义、底层协议详见 核心概念 · 单次支付。本页聚焦接入。
适用场景#
| 你的业务 | 是否适合 |
|---|---|
| 单笔金额固定明确(一篇报告 / 一次推理 / 一次查询) | ✅ |
| 调用前价格已确定,调用后无后续消费 | ✅ |
| 资源不可撤回(如文件下载、报告生成) | ✅(推荐 syncSettle: true) |
| 单次价格极低 + 调用频率极高 | ⚠️ 改用 批量支付 |
| 单次消费量算不准 | ⚠️ 改用 按量支付 |
业务流程#
下图是抽象层流程——HTTP 卖家是"客户端请求触发",Agent 卖家是"Agent 在对话里主动生成付款链接触发",但Challenge / Credential 消息语义两者一致。具体载体差异见下文卖家接入。
KYT(链上风险审查)是 Broker Verify 阶段内部的合规检查,不是独立服务。
HTTP 卖家接入#
选 exact 还是 charge?#
exact 单收款方,同步 / 异步结算都支持。
charge 除了单收款方,还支持分账(Split),即单次支付多地址收款(≤10),默认且只支持同步结算。
| 维度 | exact | charge |
|---|---|---|
| 收款方 | 单收款方 | 单收款方 / 单次支付多地址收款(≤10) |
| 结算时序 | 同步 / 异步均可 | 默认且只支持同步 |
不确定走哪个?用 同时支持 exact + charge 同时挂上,让买家自己挑。
SDK 状态#
| Scheme | Node.js | Rust | Go | Java |
|---|---|---|---|---|
exact | ✅ | ✅ | ✅ | ✅ |
charge | ✅ | ✅ | ✅ | 💡 |
✅ 已上线 · 💡 即将推出
exact 路径#
每个 Tab 包含完整的安装命令 + 实现代码。架构由 4 个组件组合:Facilitator 客户端(带 OKX API Key)→ Resource Server(注册 scheme)→ Routes 配置(accepts 数组)→ 中间件挂载。
npm install express @okxweb3/x402-express @okxweb3/x402-core @okxweb3/x402-evm
npm install -D typescript tsx @types/express @types/node
import express from "express";
import {
paymentMiddleware,
x402ResourceServer,
} from "@okxweb3/x402-express";
import { ExactEvmScheme } from "@okxweb3/x402-evm/exact/server";
import { OKXFacilitatorClient } from "@okxweb3/x402-core";
const app = express();
const NETWORK = "eip155:196";
const PAY_TO = process.env.PAY_TO_ADDRESS || "0xYourSellerWallet";
const facilitatorClient = new OKXFacilitatorClient({
apiKey: "OKX_API_KEY",
secretKey: "OKX_SECRET_KEY",
passphrase: "OKX_PASSPHRASE",
});
const resourceServer = new x402ResourceServer(facilitatorClient);
resourceServer.register(NETWORK, new ExactEvmScheme());
app.use(
paymentMiddleware(
{
"GET /api/premium": {
accepts: [
{
scheme: "exact",
network: NETWORK,
payTo: PAY_TO,
price: "$0.10",
syncSettle: true, // 同步结算:等链上确认
},
],
description: "Premium API",
mimeType: "application/json",
},
},
resourceServer,
),
);
app.get("/api/premium", (_req, res) => {
res.json({ data: "付费资源内容" });
});
app.listen(4000, () => {
console.log("[Seller] listening at http://localhost:4000");
});
多 scheme 声明都通过
accepts: [...]数组完成;要追加aggr_deferred(批量支付)时,在数组里再加一项即可,详见 批量支付。
同步结算 vs 异步结算#
调用 /settle 后,Facilitator 将交易提交到链上。路由配置中的 syncSettle 字段控制结算行为:
| 模式 | 参数值 | Facilitator 行为 | 适用场景 |
|---|---|---|---|
| 同步 | true | 提交交易并等待链上确认后返回 txHash | 高价值交易,需确认到账后再交付资源 |
| 异步 | false | 提交交易后立即返回 txHash,不等确认 | 中等价值,对响应速度有要求 |
异步结算存在时序风险:资源可能在链上支付最终确认前就已交付。高价值交易建议使用同步结算。
chargescheme 默认且只支持同步。
charge 路径#
charge 不走 x402 的 accepts 数组,而是用一个 ChargeConfig 类型 + MppCharge<T> extractor 描述每个路由的价格——价格作为类型的关联函数返回,编译期就锁定,不会被路由配置层覆写。
package.json:
{
"type": "module",
"dependencies": {
"@okxweb3/mpp": "^0.1.0"
}
}
// server.ts
// 启动: node --env-file=.env --experimental-strip-types server.ts
// 或者: npx tsx --env-file=.env server.ts
import * as http from "node:http";
import { Mppx } from "@okxweb3/mpp";
import { charge } from "@okxweb3/mpp/evm/server";
import { SaApiClient } from "@okxweb3/mpp/evm";
// SA-API client (broadcasts EIP-3009 in transaction mode).
const saClient = new SaApiClient({
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
passphrase: process.env.OKX_PASSPHRASE!,
});
const mppx = Mppx.create({
methods: [charge({ saClient })],
realm: "test realm",
secretKey: process.env.MPP_SECRET_KEY!,
});
// Per-route price (base units; "100" = 0.0001 of a 6-decimal token).
// fee_payer = true → seller broadcasts the EIP-3009 (transaction mode).
const CHARGE = {
amount: "100",
currency: "0x...adb21711", // currency
recipient: "0x...378211", // receipt
description: "One premium API call",
methodDetails: { chainId: 196, feePayer: true }, // X Layer
} as const;
// Runs only after verify + settle.
async function premium(request: Request): Promise<Response> {
const result = await mppx.charge(CHARGE)(request);
if (result.status === 402) return result.challenge;
return result.withReceipt(Response.json({ data: "premium content" }));
}
// node:http ↔ Web Standards bridge (10 lines).
http.createServer(async (req, res) => {
const url = `http://${req.headers.host ?? "localhost:4000"}${req.url}`;
const webReq = new Request(url, {
method: req.method,
headers: new Headers(req.headers as Record<string, string>),
});
const webRes =
new URL(url).pathname === "/api/premium"
? await premium(webReq)
: new Response("not found", { status: 404 });
res.statusCode = webRes.status;
webRes.headers.forEach((v, k) => res.setHeader(k, v));
res.end(await webRes.text());
}).listen(4000);
进阶#
仅适用于 HTTP 卖家——下方 SDK 代码块都挂在 HTTP 中间件上;Agent 卖家通过 Skill 生成付款链接,不涉及这一层。
1. 分账(仅 charge)#
一笔支付自动拆给最多 10 个收款方。常见场景:平台抽成、多方分润、推广分佣。
硬约束:
sum(splits.amount) < ChargeConfig::amount()(分账总额必须严格小于主金额)splits.len() ≤ 10- 每个
recipient必须是 EIP-55 校验过的 40-hex 地址
签名负担:买家会为每一路 split 各签一笔 EIP-3009——主收款方 1 笔 + 每个 split 各 1 笔,由卖家在 /settle 时一并提交。
package.json:
{
"type": "module",
"dependencies": {
"@okxweb3/mpp": "^0.1.0"
}
}
// server.ts
// 启动: npx tsx --env-file=.env server.ts
import * as http from "node:http";
import { Mppx } from "@okxweb3/mpp";
import { charge } from "@okxweb3/mpp/evm/server";
import { SaApiClient } from "@okxweb3/mpp/evm";
const saClient = new SaApiClient({
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
passphrase: process.env.OKX_PASSPHRASE!,
});
const mppx = Mppx.create({
methods: [charge({ saClient })],
realm: "test realm",
secretKey: process.env.MPP_SECRET_KEY!,
});
// Total 100 base units; primary keeps 50, splits take 30 + 20.
// Constraints: sum(splits) < amount; splits.length <= 10;
// recipient must be 40-hex EIP-55.
// Buyer signs one EIP-3009 per split (one to primary + one per entry).
const splits = [
{ amount: "30", recipient: "0x....321a1308", memo: "partner-a" },
{ amount: "20", recipient: "0x....d31a6608", memo: "partner-b" },
];
// Only difference vs single charge: methodDetails.splits.
const CHARGE = {
amount: "100",
currency: "0x...adb21711", // currency
recipient: "0x...378211", // primary receipt
description: "One premium API call (split)",
methodDetails: { chainId: 196, feePayer: true, splits },
} as const;
async function premium(request: Request): Promise<Response> {
const result = await mppx.charge(CHARGE)(request);
if (result.status === 402) return result.challenge;
return result.withReceipt(Response.json({ data: "premium content" }));
}
http.createServer(async (req, res) => {
const url = `http://${req.headers.host ?? "localhost:4000"}${req.url}`;
const webReq = new Request(url, {
method: req.method,
headers: new Headers(req.headers as Record<string, string>),
});
const webRes =
new URL(url).pathname === "/api/premium"
? await premium(webReq)
: new Response("not found", { status: 404 });
res.statusCode = webRes.status;
webRes.headers.forEach((v, k) => res.setHeader(k, v));
res.end(await webRes.text());
}).listen(4000);
当前 charge split 落地为显式金额——每一路是绝对 base units,不是比例。
2. 同时支持 exact + charge#
package.json:
{
"type": "module",
"dependencies": {
"@okxweb3/payment-router": "^0.1.0",
"@okxweb3/mpp": "^0.1.0",
"@okxweb3/x402-core": "^0.1.0",
"@okxweb3/x402-evm": "^0.1.0"
}
}
// server.ts
// 启动: npx tsx --env-file=.env server.ts
import * as http from "node:http";
import { Mppx } from "@okxweb3/mpp";
import { charge as mppCharge } from "@okxweb3/mpp/evm/server";
import { SaApiClient } from "@okxweb3/mpp/evm";
import { OKXFacilitatorClient } from "@okxweb3/x402-core";
import {
x402HTTPResourceServer,
x402ResourceServer,
} from "@okxweb3/x402-core/server";
import { ExactEvmScheme } from "@okxweb3/x402-evm/exact/server";
import {
MppAdapter,
X402Adapter,
paymentRouter,
} from "@okxweb3/payment-router";
// —— MPP setup ——
const saClient = new SaApiClient({
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
passphrase: process.env.OKX_PASSPHRASE!,
});
const mppx = Mppx.create({
methods: [mppCharge({ saClient })],
realm: "test realm",
secretKey: process.env.MPP_SECRET_KEY!,
});
// —— x402 setup (facilitator + scheme; routes are declared on the router) ——
const NETWORK = "eip155:196"; // X Layer Mainnet
const x402Server = new x402ResourceServer(
new OKXFacilitatorClient({
apiKey: process.env.OKX_API_KEY!,
secretKey: process.env.OKX_SECRET_KEY!,
passphrase: process.env.OKX_PASSPHRASE!,
}),
).register(NETWORK, new ExactEvmScheme());
// Built-in priorities: MPP=10, x402=20 (MPP wins when both headers present).
// Custom adapters should start at priority ≥ 100.
const protect = paymentRouter({
adapters: [
new MppAdapter({ mppx }),
new X402Adapter({
resourceServer: x402Server,
httpResourceServerCtor: x402HTTPResourceServer,
}),
],
routes: {
"GET /generateImg": {
description: "AI Image Generation Service",
adapterConfigs: {
mpp: {
intent: "charge",
amount: "10000",
currency: "0x...adb21711", // currency
recipient: "0x...378211", // receipt
description: "AI Image Generation Service",
methodDetails: { chainId: 196, feePayer: true },
},
x402: {
scheme: "exact",
network: NETWORK,
payTo: "0x...378211", // receipt
price: "$0.01",
description: "AI Image Generation Service",
mimeType: "application/json",
},
},
},
},
});
// Protocol-agnostic. Runs only after one of the adapters has verified payment.
const handler = protect(async () =>
Response.json({
imageUrl: "https://placehold.co/512x512/png?text=AI+Generated",
prompt: "a sunset over mountains",
}),
);
http.createServer(async (req, res) => {
const url = `http://${req.headers.host ?? "localhost:4000"}${req.url}`;
const webReq = new Request(url, {
method: req.method,
headers: new Headers(req.headers as Record<string, string>),
});
const webRes =
new URL(url).pathname === "/generateImg" && req.method === "GET"
? await handler(webReq)
: new Response("not found", { status: 404 });
res.statusCode = webRes.status;
webRes.headers.forEach((v, k) => res.setHeader(k, v));
res.end(await webRes.text());
}).listen(4000);
Agent 卖家接入#
Agent 在对话里主动生成付款链接#
Agent 卖家不是"被动挂中间件等买家请求",而是Agent 在对话里需要收费时主动生成付款链接,发到消息通道(XMTP / Telegram 等)。
本节聚焦付款链接的生成与分发;关于两个 Agent 如何通过 Telegram / XMTP 等消息通道建立会话,详见 快速开始 · 我是 Agent 卖家 — 配置消息通道网关。
- 1安装 OnchainOS Skill
把以下提示词发给你的 AI Agent,跟随引导完成安装:
text请帮我安装 Onchain OS Payment Skill,让我的 Agent 能生成付款链接并对外收费。 我的收款钱包地址:0xYourSellerWallet详见 Agent 卖家快速开始。
- 2生成付款链接
卖家 Agent 在需要收费时调用 Skill 生成一次性付款链接:
text买家Agent:帮我翻译这份 3000 字文档 卖家Agent:好的,翻译服务 50 USD₮0。 [Skill 调用:createPayment({type:'charge', amount:'50000000', recipient:'0x...'})] 付款链接:https://pay.okx.com/p/a2a_01HZX8Q9RK3JWYV7M2N5T8P4AB每条链接是一次性的,默认 30 分钟到期自动失效。
- 3发送付款链接
Skill 返回的付款 URL(形如
https://pay.okx.com/p/a2a_xxx)作为文本消息发送给买家 Agent,对端解析 URL 后通过 Agentic Wallet 完成签名。 - 4轮询支付状态
Skill 自动轮询
GET /payment/{paymentId}/status,状态变completed后通知 Agent 交付服务。textSkill: 支付已完成(tx: 0xabc...) Agent: 收到付款,开始翻译...
买家接入#
买家通过给 Agent 安装 Onchain OS Skill 完成接入——安装 Skill 时会自动配置 Agentic Wallet 作为底层签名钱包,无需单独安装。Skill 自动识别 HTTP 402 响应或消息通道里的付款 URL,调用钱包完成签名后重放请求,全程无需买家手动介入。完整接入步骤见 Agent 买家。