使用 Cloudflare Workers 接受机器支付协议(MPP)付款。根据要保护的服务选择集成方式:
mpp-proxy↗ — 无需修改源站代码即可对 HTTP 内容收费。参阅为 HTTP 内容收费。- Worker 路由 — 向 Worker 应用添加
mppx支付中间件。 - MCP tool — 在 MCP tool 返回结果前要求完成支付。
创建 Cloudflare 账户。你还需要支付收款方和 MPP 密钥。
示例使用 testnet 上的稳定币支付方式。其他方式请参阅 MPP 支付方式 ↗。
当你控制 Worker 应用时,添加 mppx 中间件:
-
在 Worker 项目中安装 Hono 和
mppx:npm i hono mppxyarn add hono mppxpnpm add hono mppxbun add hono mppx -
将 MPP 签名密钥存储为 Worker 密钥:
npx wrangler secret put MPP_SECRET_KEYyarn wrangler secret put MPP_SECRET_KEYpnpm wrangler secret put MPP_SECRET_KEY -
在付费路由处理函数之前添加支付中间件:
src/index.jsjs import { env } from "cloudflare:workers"; import { Hono } from "hono"; import { Mppx, tempo } from "mppx/hono"; const workerEnv = env; const app = new Hono(); const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })], secretKey: workerEnv.MPP_SECRET_KEY, }); app.get( "/premium", mppx.charge({ amount: "0.01", currency: "0x20c0000000000000000000000000000000000000", description: "Premium API access", recipient: "<YOUR_WALLET_ADDRESS>", }), (c) => c.json({ access: "paid" }), ); export default app;src/index.tsts import { env } from "cloudflare:workers"; import { Hono } from "hono"; import { Mppx, tempo } from "mppx/hono"; const workerEnv = env as Env & { MPP_SECRET_KEY: string }; const app = new Hono(); const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })], secretKey: workerEnv.MPP_SECRET_KEY, }); app.get( "/premium", mppx.charge({ amount: "0.01", currency: "0x20c0000000000000000000000000000000000000", description: "Premium API access", recipient: "<YOUR_WALLET_ADDRESS>", }), (c) => c.json({ access: "paid" }), ); export default app; -
部署 Worker:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy -
在不携带支付的情况下请求付费路由:
curl -i https://<YOUR_WORKER>.workers.dev/premiumWorker 返回
402 Payment Required和WWW-Authenticate: Payment标头。完成支付后重试会到达处理函数,并返回Payment-Receipt标头。
为 Workers 上 MCP 服务器的工具调用收费。mppx 会通过 MCP SDK v1 请求上下文 (Transport.mcpSdk()) 读取支付凭据,因此此服务器使用 SDK v1 McpServer 和 createLegacyMcpHandler。此服务器仍为无状态:Worker 会为每个请求创建新的服务器,无需 Durable Object。
-
在 Agents 项目中安装所需软件包:
npm i agents mppx @modelcontextprotocol/sdk zodyarn add agents mppx @modelcontextprotocol/sdk zodpnpm add agents mppx @modelcontextprotocol/sdk zodbun add agents mppx @modelcontextprotocol/sdk zod -
配置 Worker:
{ "$schema": "./node_modules/wrangler/config-schema.json", "name": "mpp-server", "main": "src/index.ts", "upload_source_maps": true, // Set this to today's date "compatibility_date": "2026-11-14", "compatibility_flags": [ "nodejs_compat" ] }name = "mpp-server" main = "src/index.ts" upload_source_maps = true # Set this to today's date compatibility_date = "2026-11-14" compatibility_flags = ["nodejs_compat"] -
将
MPP_SECRET_KEY存储为 Worker 密钥:npx wrangler secret put MPP_SECRET_KEYyarn wrangler secret put MPP_SECRET_KEYpnpm wrangler secret put MPP_SECRET_KEY -
在返回 tool 结果前检查支付:
src/index.jsjs import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { createLegacyMcpHandler } from "agents/mcp"; import { Mppx, tempo, Transport } from "mppx/server"; import { z } from "zod"; function createServer(env) { const server = new McpServer({ name: "paid-search", version: "1.0.0" }); const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })], secretKey: env.MPP_SECRET_KEY, transport: Transport.mcpSdk(), }); server.registerTool( "premium_search", { description: "Search premium content", inputSchema: { query: z.string() }, }, async ({ query }, extra) => { const payment = await mppx.charge({ amount: "0.01", currency: "0x20c0000000000000000000000000000000000000", description: "Premium search", recipient: "<YOUR_WALLET_ADDRESS>", })(extra); if (payment.status === 402) throw payment.challenge; return payment.withReceipt({ content: [{ type: "text", text: `Results for: ${query}` }], }); }, ); return server; } export default { fetch(request, env, ctx) { return createLegacyMcpHandler(createServer(env))(request, env, ctx); }, };src/index.tsts import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { createLegacyMcpHandler } from "agents/mcp"; import { Mppx, tempo, Transport } from "mppx/server"; import { z } from "zod"; type PaymentEnv = Env & { MPP_SECRET_KEY: string }; function createServer(env: PaymentEnv) { const server = new McpServer({ name: "paid-search", version: "1.0.0" }); const mppx = Mppx.create({ methods: [tempo.charge({ testnet: true })], secretKey: env.MPP_SECRET_KEY, transport: Transport.mcpSdk(), }); server.registerTool( "premium_search", { description: "Search premium content", inputSchema: { query: z.string() }, }, async ({ query }, extra) => { const payment = await mppx.charge({ amount: "0.01", currency: "0x20c0000000000000000000000000000000000000", description: "Premium search", recipient: "<YOUR_WALLET_ADDRESS>", })(extra); if (payment.status === 402) throw payment.challenge; return payment.withReceipt({ content: [{ type: "text", text: `Results for: ${query}` }], }); }, ); return server; } export default { fetch(request, env, ctx) { return createLegacyMcpHandler(createServer(env))(request, env, ctx); }, } satisfies ExportedHandler<PaymentEnv>; -
部署 Worker:
npx wrangler deployyarn wrangler deploypnpm wrangler deploy未付费的
premium_search调用会返回 MPP Challenge。完成支付后重试会返回 tool 结果,并在_meta中附带 MPP Receipt。
要从 Cloudflare Agent 测试这两种支付流程,请参阅通过 Agents SDK 付款。生产环境计费模式请参阅 MPP 支付意图 ↗。