MPP - 机器支付协议
MPP 是一个开放协议(由 Tempo 和 Stripe 共同制定),用于标准化 HTTP 402 Payment Required 的机器对机器支付。客户端在同一个 HTTP 请求中完成支付 - 无需账户、API 密钥或结账流程。
核心协议规范已作为 Payment HTTP Authentication Scheme 提交给 IETF。
本技能中的代码使用占位符代币名称(<USDC_TEMPO_MAINNET>, <PATHUSD_TESTNET>);实际地址位于 Tempo 文档 和 references/tempo-method.md 中。
核心架构
三个原语驱动每个 MPP 支付:
- Challenge - 服务器发出的支付要求(在
WWW-Authenticate: Payment头部中) - Credential - 客户端提交的支付证明(在
Authorization: Payment头部中) - Receipt - 服务器确认支付成功(在
Payment-Receipt头部中)
支付方式和意图
MPP 是与支付方式无关的。每种方式定义自己的结算轨道:
| 方式 | 轨道 | SDK 包 | 状态 |
|---|---|---|---|
| Tempo | Tempo 链上的 TIP-20 稳定币 | mppx(内置) | 生产就绪 |
| Stripe | 卡/钱包 (SPT) + 链上加密货币存款 | mppx(内置) | 生产就绪 |
| EVM | EIP-3009 稳定币授权(与 x402-exact 兼容) | mppx(内置) | 生产就绪 |
| Lightning | 通过闪电网络使用比特币 | @buildonspark/lightning-mpp-sdk | 生产就绪 |
| Stellar | Stellar 上的 SEP-41 代币,charge + channel | @stellar/mpp | 生产就绪(channel 协议规范仍在起草中 - 可能变更) |
| Solana | Solana 原生 charge + session(SOL, SPL, Token-2022) | @solana/mpp | 生产就绪 |
| Monad | Monad charge(ERC-3009,结算模式) | @monad-crypto/mpp | 生产就绪 |
| NEAR Intents | 通过 1Click 存款地址进行跨链 charge | @defuse-protocol/nearintents-mpp-sdk | 生产就绪(非无需信任 - 通过结算后端路由,methodDetails.settlementBackend: "near-intents" 用于按方法的风险策略) |
| RedotPay | RedotPay 余额 (rdt) 或稳定币证明,仅 charge | @redotpay/mpp | 生产就绪 |
| Card | 加密网络令牌 (Visa) | mpp-card | 生产就绪 |
| 自定义 | 任何轨道 | Method.from() + Method.toClient/toServer | 可扩展 |
各方法的深入介绍:references/tempo-method.md, references/stripe-method.md, references/lightning-method.md, references/custom-methods.md。
| 意图 | 模式 | 最适合 |
|---|---|---|
| charge | 每次请求一次性支付 | API 调用、内容访问、固定价格端点 |
| session | 通过支付通道按使用量付费 | LLM 流式传输、按量计费、高频 API |
| subscription | 通过授权密钥进行定期访问 (Tempo) - 参见 references/subscriptions.md | 访问与按请求计费分离的计划/层级 |
快速入门:服务端 (TypeScript)
import { Mppx, tempo } from 'mppx/server'
const mppx = Mppx.create({
methods: [tempo({
currency: '<PATHUSD_TESTNET>', // pathUSD 测试网
recipient: '0xYourAddress',
})],
})
export async function handler(request: Request) {
const result = await mppx.charge({ amount: '0.01' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
}
安装:npm install mppx viem (mppx 0.8.15 需要 viem >= 2.54.0)。
使用 npx mppx validate http://localhost:3000 端到端验证完成的服务端。
快速入门:客户端 (TypeScript)
import { privateKeyToAccount } from 'viem/accounts'
import { Mppx, tempo } from 'mppx/client'
// 填充 globalThis.fetch 以自动处理 402
Mppx.create({
methods: [tempo({ account: privateKeyToAccount('0x...') })],
})
const res = await fetch('https://api.example.com/paid')
// 当服务器返回 402 时,支付透明地进行
在浏览器中,mppx 0.6.0 更改了默认行为:填充的 fetch 仅向同源端点发送 Accept-Payment,因此跨源付费 API 需要 acceptPaymentPolicy('always' / { origins: [...] })。客户端 fetch 会重试增量挑战,最多重试 maxPaymentRetries(默认 3)。关于非全局替代方案(Fetch.from/polyfill/restore, Mppx.restore()),请参见 references/typescript-sdk.md。
快速入门:服务端 (Python)
from fastapi import FastAPI
from mpp import Credential, Receipt
from mpp.server import Mpp
from mpp.methods.tempo import tempo, ChargeIntent
app = FastAPI()
server = Mpp.create(method=tempo(
currency="<PATHUSD_TESTNET>",
recipient="0xYourAddress", intents={"charge": ChargeIntent()},
))
@app.get("/resource")
@server.pay(amount="0.50")
async def get_resource(request, credential: Credential, receipt: Receipt):
return {"data": "paid content", "payer": credential.source}
安装:pip install "pympp[tempo]"。有关完整模式,请参见 references/python-sdk.md。
快速入门:服务端 (Rust)
安装:cargo add mpp --features tempo,server。有关完整模式,请参见 references/rust-sdk.md。
框架中间件 (TypeScript)
每个框架都有自己的导入路径(mppx/nextjs, mppx/hono, mppx/express, mppx/elysia):
// Next.js
import { Mppx, tempo } from 'mppx/nextjs'
const mppx = Mppx.create({ methods: [tempo({ currency: '<PATHUSD_TESTNET>', recipient: '0x...' })] })
export const GET = mppx.charge({ amount: '0.1' })(() => Response.json({ data: '...' }))
// Hono
import { Mppx, tempo } from 'mppx/hono'
app.get('/resource', mppx.charge({ amount: '0.1' }), (c) => c.json({ data: '...' }))
关于 Express 和 Elysia 示例,请参见 references/typescript-sdk.md。
会话:按使用量付费流式传输
会话一次性打开一个支付通道,然后对每个请求使用链下凭证 - 不需要每个请求都进行区块链交易。延迟低于 100ms,每个请求的费用接近零。
Sessions v2(自 mppx 0.7.0 起默认): tempo.session() 是 TIP-1034 预编译通道流程;早期的托管合约实现是 Sessions v1,仍可作为已弃用的 tempo.sessionLegacy 使用。期望 v2 的客户端会拒绝 v1 会话并回退到 charge 路径,因此请确保客户端和服务端使用匹配的流程。两个客户端 API:tempo.session({ account, maxDeposit }) 使用 Mppx.create() 注册该方法(通过 fetch 透明处理 402),而 tempo.session.manager({ account, maxDeposit }) 返回一个托管客户端,用于直接生命周期控制 (.sse(), .close())。
// 服务端 - 带自动结算的会话端点
const mppx = Mppx.create({
methods: [tempo.session({
currency: '<PATHUSD_TESTNET>', recipient: '0x...',
store: Store.redis(redis),
settlementSchedule: { amount: '1.00', intervalMs: 300_000 },
bootstrap: true, // 允许返回的客户端在此路由上恢复其通道
})],
})
const result = await mppx.session({ amount: '0.001', unitType: 'token' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
// 服务端 - 按词计费的 SSE 流式传输
export const GET = mppx.session({ amount: '0.001', unitType: 'word' })(
async () => async function* (stream) {
for (const word of ['hello', 'world']) {
await stream.charge()
yield word
}
}
)
// 客户端 - 自动管理的通道会话
Mppx.create({ methods: [tempo({ account, maxDeposit: '1' })] })
const res = await fetch('http://localhost:3000/api/resource')
// 第 1 个请求:在链上打开通道;第 2+ 个请求:链下凭证
会话也可以通过 WebSocket 使用 Ws.serve() 进行流式传输。有关完整生命周期、结算、存储、SSE 和 WebSocket 模式以及通道恢复,请参见 references/sessions.md。
多方法支持
在单个端点上接受 Tempo 稳定币、Stripe 卡和 Lightning 比特币:
const mppx = Mppx.create({
methods: [
tempo({ currency: '<PATHUSD_TESTNET>', recipient: '0x...' }),
stripe.charge({ client: new Stripe(key), networkId: 'profile_...', paymentMethodTypes: ['card'] }),
spark.charge({ mnemonic: process.env.MNEMONIC! }),
],
})
使用 Mppx.compose() 在单个 402 响应中呈现多种方法,并支持按路由定价。在挑战站点和验证站点应用相同的分支,否则 402 会通告比服务器实际接受的更少的选项。参见 references/typescript-sdk.md。
支付链接 (HTML)
在支付方法配置上设置 html: true 会在浏览器中访问 402 端点时呈现一个浏览器友好的支付页面,支持主题定制、多方法组合标签页和 Solana 钱包支持。Service Worker 处理凭证提交,然后页面重新加载付费响应。
通过 mppx/html 导出项(Config, Text, Theme)进行自定义,并使用 Html.init(methodName) 构建自定义方法的支付链接。
零美元认证(身份凭证)
验证智能体身份而无需支付。客户端签署一个针对挑战 ID 的 EIP-712 证明,而不是创建交易 - 无需消耗 gas,无需转移资金。
// 服务端 - 零美元 charge,带存储以防止重放攻击
const mppx = Mppx.create({
methods: [tempo.charge({ currency: '<PATHUSD_TESTNET>', recipient: '0x...', store })],
})
const result = await mppx.charge({ amount: '0' })(request)
自 mppx 0.8.0 起,这些证明绑定到支付钱包:EIP-712 Proof 类型化数据(作为 tempo.Proof 公开)在域版本 3 上携带了一个 account 字段,因此为一个账户签名的证明将不再适用于另一个账户。
用例:身份验证、长时间运行的任务轮询、付费解锁后免费访问、多步骤智能体管道。参见 mpp.dev/advanced/identity。
支付代理
将现有 API 置于 MPP 支付之后:
// import { openai, Proxy } from 'mppx/proxy' - Proxy.create({ services: [...] }) 内的一个服务
openai({
apiKey: process.env.OPENAI_API_KEY,
routes: {
'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
'GET /v1/models': true, // 字面量 `true` 表示免费路由
},
})
内置预设 openai(), anthropic(), stripe(),以及用于任何上游服务的 custom()。参见 references/discovery-and-proxy.md 了解 Proxy.create()、它提供的发现端点以及用于非代理服务器的 discovery() 帮助函数。
MCP 传输
MCP 工具调用可以使用 JSON-RPC 错误代码 -32042 来要求支付(服务器也可能发出 -32043):
// 服务端 - 从 mppx/server 导入 tempo,而不是 mppx/tempo
import { McpServer } from 'mppx/mcp/server'
import { tempo } from 'mppx/server'
const server = McpServer.wrap(baseServer, { methods: [tempo.charge({ /* ... */ })], secretKey })
// 客户端 - 支持支付的 MCP 客户端(从 mppx/client 导入 tempo)
import { McpClient } from 'mppx/mcp/client'
import { tempo } from 'mppx/client'
const mcp = McpClient.wrap(client, { methods: [tempo({ account })] })
const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })
基于 HTTP 的 MCP 挑战会在同一个支持支付的 fetch 中完成结算,并且传输层可以通过双方的 Transport.from/http/mcp/mcpSdk 进行插拔。参见 references/transports.md。
Privy 服务端钱包
来自 @privy-io/node/viem 的 createViemAccount(需要 @privy-io/node >= 0.20.0)返回一个由 Privy 服务端钱包支持的 viem Account,因此它可以无缝地插入到通常使用本地账户的 tempo({ account }) 中。
服务端签名适用于应用拥有的服务端钱包;用户拥有的嵌入式钱包需要授权密钥或密钥仲裁。有关完整设置和手动 toAccount() 构造,请参见 references/typescript-sdk.md。
测试与 CLI
# 创建一个账户(存储在密钥链中),然后在测试网为其充值
npx mppx account create
npx mppx account fund --network testnet
# 发起一个付费请求
npx mppx http://localhost:3000/resource
# 解析一个挑战而不签名
npx mppx sign --dry-run --challenge '<www-authenticate value>'
# 端到端验证服务器实现
npx mppx validate http://localhost:3000
CLI 还涵盖 init, sessions(列出/查看/关闭), discover, services, mcp add 和 skills add。配置来自 MPPX_CONFIG 或显式的 --config - 没有来自工作目录的自动发现。完整参考:references/cli.md。
SDK 包
| 语言 | 包 | 安装命令 |
|---|---|---|
| TypeScript | mppx | npm install mppx |
| Python | pympp | pip install "pympp[tempo]" |
| Rust | mpp | cargo add mpp --features tempo,client,server |
| Ruby | mpp-rb(官方,由 Stripe 提供) | 请查看仓库了解 gem 名称 |
| Go | mpp-go(官方,由 Tempo 提供) | go get github.com/tempoxyz/mpp-go |
| Elixir | mpp(社区) | hex.pm/packages/mpp |
| Swift | mpp-swift(社区) | 请查看仓库 |
能力说明,已对照 SDK 源代码而非文档矩阵进行了检查(上游发布了两份不一致的文档):
- Session 意图:仅 TypeScript 和 Rust。
- 身份凭证(零美元认证):TypeScript
…
[为翻译而截断]