x402 支付

本页内容

@emdash-cms/x402 为部署在 Cloudflare 上的任意 Astro 站点增加 x402 支付协议 支持。可独立使用,不依赖 EmDash 核心;与 EmDash 的 CMS 字段配合,便于按页面定价。

x402 是原生基于 HTTP 的支付协议。客户端在未付款的情况下请求付费资源时,服务器返回 402 Payment Required 及机器可读的支付说明。支持 x402 的代理与浏览器可自动完成支付并重试请求。

适用场景

最常见的是 仅机器人付费:对 AI 代理与爬虫收取内容访问费用,人类访客免费阅读。借助 Cloudflare Bot Management 区分人与机器人。

也可要求所有访客付费,或仅检测支付请求头而不强制拦截(条件渲染)。

安装

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

配置

在 Astro 配置中加入该集成:

import { defineConfig } from "astro/config";
import { x402 } from "@emdash-cms/x402";

export default defineConfig({
	integrations: [
		x402({
			payTo: "0xYourWalletAddress",
			network: "eip155:8453", // Base 主网
			defaultPrice: "$0.01",
			botOnly: true,
			botScoreThreshold: 30,
		}),
	],
});

添加类型引用,使 TypeScript 识别 Astro.locals.x402

/// <reference types="@emdash-cms/x402/locals" />

基本用法

集成会在 Astro.locals.x402 上放置执行器。在页面 frontmatter 中调用 enforce(),将内容置于支付门槛之后:

---
const { x402 } = Astro.locals;

const result = await x402.enforce(Astro.request, {
  price: "$0.05",
  description: "付费文章",
});

// 若无有效支付,enforce() 返回 402 Response。
// 直接返回即可向客户端下发支付说明。
if (result instanceof Response) return result;

// 已验证支付(或在 botOnly 下已跳过)。通过响应头传递结算凭证。
x402.applyHeaders(result, Astro.response);
---

<article>
  <h1>付费内容</h1>
</article>

enforce() 的返回值要么是:

  • Response(402)— 需要付款,直接返回;
  • EnforceResult — 应继续处理请求:已付款,或在仅机器人模式下对人类访客跳过校验。

仅机器人模式

botOnlytrue 时,集成会读取 request.cf.botManagement.score

  • 分数低于阈值(默认 30)→ 视为机器人,强制付费;
  • 分数达到或高于阈值 → 视为人类,跳过强制;
  • 无机器人管理数据(本地开发、非 CF 部署)→ 视为人类。

EnforceResult 中的 skipped 可区分「无需付费」与「已付费」:

---
const result = await x402.enforce(Astro.request, { price: "$0.01" });
if (result instanceof Response) return result;

x402.applyHeaders(result, Astro.response);

// result.paid    — 已验证支付时为 true
// result.skipped — 在 botOnly 下因人类而跳过时为 true
// result.payer   — 付款人钱包地址(若已付)
---

与 EmDash 按页定价

使用 EmDash 时,可在集合中添加 number 字段表示页面价格,无需特殊 schema,普通 CMS 字段即可:

---
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;
const { entry } = await getEmDashEntry("posts", slug);

if (!entry) return Astro.redirect("/404");

const { x402 } = Astro.locals;

const result = await x402.enforce(Astro.request, {
  price: entry.data.price || "$0.01",
  description: entry.data.title,
});
if (result instanceof Response) return result;

x402.applyHeaders(result, Astro.response);
---

<article>
  <h1>{entry.data.title}</h1>
</article>

不强制,仅检测是否带支付头

使用 hasPayment() 可检查请求是否包含支付相关头,而不验证或强制。适合条件渲染 — 向已付与未付访客展示不同内容:

---
const { x402 } = Astro.locals;

const hasPaid = x402.hasPayment(Astro.request);
---

{hasPaid ? (
  <p>此处为完整付费内容。</p>
) : (
  <p>订阅后可阅读全文。</p>
)}

配置项说明

选项类型默认值说明
payTostring必填收款钱包地址
networkstring必填CAIP-2 网络标识(如 eip155:8453
defaultPricePrice默认价格,可按页覆盖
facilitatorUrlstringhttps://x402.org/facilitator支付协调服务 URL
schemestring"exact"支付方案
maxTimeoutSecondsnumber60支付签名最长有效时间(秒)
evmbooleantrue启用 EVM 链
svmbooleanfalse启用 Solana(需 @x402/svm
botOnlybooleanfalse仅对机器人强制付费
botScoreThresholdnumber30机器人分数阈值(1–99,越低越倾向判定为机器人)

价格写法

价格可采用多种形式:

  • 美元字符串"$0.10"(会去掉 $ 前缀,数值原样传递)
  • 数字字符串"0.10"
  • 数值0.10
  • 对象{ amount: "100000", asset: "0x...", extra: {} } 显式指定资产与金额

网络标识

网络使用 CAIP-2 格式:

网络标识符
Base 主网eip155:8453
Base Sepoliaeip155:84532
以太坊eip155:1
Solanasolana:mainnet

enforce 选项

针对单页覆盖全局配置:

await x402.enforce(Astro.request, {
	price: "$0.25",
	payTo: "0xDifferentWallet",
	network: "eip155:1",
	description: "文章:x402 如何工作",
	mimeType: "text/html",
});

Solana 支持

Solana 为可选。安装 @x402/svm 并在配置中启用:

pnpm add @x402/svm
x402({
	payTo: "YourSolanaAddress",
	network: "solana:mainnet",
	svm: true,
	evm: false,
});

工作原理

  1. x402() 集成注册中间件,创建执行器并挂到 Astro.locals.x402
  2. 配置通过 Vite 虚拟模块(virtual:x402/config)传入中间件。
  3. 调用 enforce() 时检查请求上的 payment-signature 头。
  4. 若无支付头,返回 402 Payment Required,并在 PAYMENT-REQUIRED 头中附带说明。
  5. 若有支付头,通过协调服务验证并完成结算。
  6. 结算后,applyHeaders() 在响应上设置 PAYMENT-RESPONSE 相关头。

资源服务器在首次请求时惰性初始化,并在 Worker 生命周期内缓存。