x402 Payments

本页内容

@emdash-cms/x402 包为服务端渲染的 Astro 站点添加了 x402 支付协议支持。它是一个独立的 Astro 集成,因此强制支付并不依赖 EmDash。当站点同时使用 EmDash 时,可以由内容字段为每个条目提供价格。

x402 是一种原生基于 HTTP 的支付协议。当客户端在未付款的情况下请求付费资源时,服务器会返回 402 Payment Required 以及机器可读的支付说明。理解 x402 的智能体和浏览器可以自动完成支付,并重试该请求。

选择强制执行模式

当对该路由的每个请求都必须出示有效支付时,请使用常规强制执行。此模式不依赖 Cloudflare 特有的请求元数据。

当站点运行在启用了 Bot Management 的 Cloudflare Workers 上,并且只有低分请求才需要付费时,请使用仅限机器人模式。该包会读取 request.cf.botManagement.score;当该值不存在时,它会把请求视为人类请求并跳过强制执行。如果缺失机器人数据时必须以失败关闭(fail closed)的方式处理,请不要使用仅限机器人模式。

hasPayment() 提供第三种行为,仅用于展示。它会报告请求是否带有支付标头,但不会验证或结算该支付。

安装包

使用你的包管理器安装该包:

pnpm

pnpm add @emdash-cms/x402

npm

npm install @emdash-cms/x402

yarn

yarn add @emdash-cms/x402

配置集成

请选择能够协同工作的钱包、网络和 facilitator。该集成默认支持以太坊虚拟机(EVM)网络,但 facilitator 也必须支持所配置的网络和资产。下面的示例使用 Base 主网,并对每个请求都强制支付:

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

export default defineConfig({
	integrations: [
		x402({
			payTo: "0xYourWalletAddress",
			network: "eip155:8453", // Base mainnet
			defaultPrice: "$0.01",
		}),
	],
});

添加类型引用,让 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: "Premium article",
});

// If the request has no valid payment, enforce() returns a 402 Response.
// Return it directly to send payment instructions to the client.
if (result instanceof Response) return result;

// Payment verified (or skipped in botOnly mode). Apply response headers
// so the client gets settlement proof.
x402.applyHeaders(result, Astro.response);
---

<article>
  <h1>Premium content</h1>
</article>

enforce() 方法返回以下两者之一:

  • Response(402)——客户端需要付款。请直接返回它。
  • EnforceResult——请求应当继续处理。内容已经付费,或者已跳过强制执行(仅限机器人模式下的人类请求)。

启用仅限机器人模式

在集成配置中启用 botOnly:

x402({
	payTo: "0xYourWalletAddress",
	network: "eip155:8453",
	defaultPrice: "$0.01",
	botOnly: true,
	botScoreThreshold: 30,
});

该集成会读取 Cloudflare 的 request.cf.botManagement.score 来对请求分类:

  • 分数低于阈值(默认 30)-> 视为机器人,强制支付
  • 分数达到或高于阈值 -> 视为人类,跳过强制执行
  • 没有 Bot Management 数据(本地开发、非 Cloudflare 部署)-> 视为人类

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 if payment was verified
// result.skipped — true if enforcement was skipped (human in botOnly mode)
// result.payer   — wallet address of payer (if paid)
---

从 EmDash 读取价格

使用 EmDash 时,可以在集合中添加一个普通的 number 字段用于按页面定价,并在请求时读取它:

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

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

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

const { x402 } = Astro.locals;

// Use the price from the CMS, falling back only when it is absent.
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 hasPaymentHeader = x402.hasPayment(Astro.request);
---

{hasPaymentHeader ? (
  <p>Payment supplied. Verification is still required.</p>
) : (
  <p>This page requires payment.</p>
)}

配置参考

选项类型默认值说明
payTostring必填收款钱包地址
networkstring必填CAIP-2 网络标识符(例如 eip155:8453)
defaultPricePrice—默认价格,可按页面覆盖
facilitatorUrlstringhttps://x402.org/facilitator支付 facilitator 的 URL
schemestring"exact"支付方案
maxTimeoutSecondsnumber60支付签名的最长超时时间
evmbooleantrue启用以太坊虚拟机网络支持
svmbooleanfalse启用 Solana 虚拟机支持(需要 @x402/svm)
botOnlybooleanfalse仅对机器人强制支付
botScoreThresholdnumber30机器人分数阈值(1-99,越低越可能是机器人)

价格格式

价格可以用多种格式指定:

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

网络标识符

CAIP-2 为区块链网络提供了无歧义的标识符,例如用 eip155:8453 表示 Base 主网。请使用 facilitator 所公布的确切标识符。启用 EVM 支持时,该包接受 eip155:* 系列;启用 Solana 虚拟机(SVM)支持时,接受 solana:* 系列;但这并不保证 facilitator 会接受该系列中的每一个网络。

覆盖单次请求

为特定页面覆盖配置的默认值:

await x402.enforce(Astro.request, {
	price: "$0.25", // Override price
	payTo: "0xDifferentWallet", // Override wallet
	network: "eip155:1", // Override network
	description: "Article: How x402 Works", // Resource description
	mimeType: "text/html", // MIME type hint
});

启用 Solana 支持

Solana 需要主动启用。安装 @x402/svm 并在配置中启用它:

pnpm add @x402/svm

设置 svm: true,使用 facilitator 所支持的 CAIP-2 Solana 标识符;如果站点只接受 Solana 支付,则请禁用 EVM。例如,@x402/svm 2.8 所使用的 Solana 主网标识符是 solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:

x402({
	payTo: "YourSolanaAddress",
	network: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
	svm: true,
	evm: false, // Disable EVM if only using Solana
});

请求流程

  1. 在页面运行之前,集成会把支付强制执行器放到 Astro.locals.x402 上。
  2. enforce() 检查是否带有 payment-signature 标头。
  3. 如果没有支付标头,enforce() 会返回 402 Payment Required 响应,其响应体和 PAYMENT-REQUIRED 标头描述了可接受的支付方式。
  4. 如果带有匹配的支付,所配置的 facilitator 会对其进行验证和结算。
  5. enforce() 返回付款方和结算结果。请调用 applyHeaders(),让页面响应包含 facilitator 的 PAYMENT-RESPONSE 证明。