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 生命週期內快取。