@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— 應繼續處理請求:已付款,或在僅機器人模式下對人類訪客略過檢查。
僅機器人模式
當 botOnly 為 true 時,整合會讀取 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>
)}
設定項說明
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
payTo | string | 必填 | 收款錢包位址 |
network | string | 必填 | CAIP-2 網路識別(如 eip155:8453) |
defaultPrice | Price | — | 預設價格,可依頁覆寫 |
facilitatorUrl | string | https://x402.org/facilitator | 付款協調服務 URL |
scheme | string | "exact" | 付款方案 |
maxTimeoutSeconds | number | 60 | 付款簽章最長有效時間(秒) |
evm | boolean | true | 啟用 EVM 鏈 |
svm | boolean | false | 啟用 Solana(需 @x402/svm) |
botOnly | boolean | false | 僅對機器人強制付費 |
botScoreThreshold | number | 30 | 機器人分數閾值(1–99,越低越傾向判定為機器人) |
價格寫法
價格可採多種形式:
- 美元字串 —
"$0.10"(會去掉$字首,數值原樣傳遞) - 數字字串 —
"0.10" - 數值 —
0.10 - 物件 —
{ amount: "100000", asset: "0x...", extra: {} }明確指定資產與金額
網路識別
網路使用 CAIP-2 格式:
| 網路 | 識別碼 |
|---|---|
| Base 主網 | eip155:8453 |
| Base Sepolia | eip155:84532 |
| 以太坊 | eip155:1 |
| Solana | solana: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,
});
運作原理
x402()整合註冊中介軟體,建立執行器並掛到Astro.locals.x402。- 設定透過 Vite 虛擬模組(
virtual:x402/config)傳入中介軟體。 - 呼叫
enforce()時檢查請求上的payment-signature標頭。 - 若無付款標頭,回傳
402 Payment Required,並在PAYMENT-REQUIRED標頭附帶說明。 - 若有付款標頭,透過協調服務驗證並完成結算。
- 結算後,
applyHeaders()在回應上設定PAYMENT-RESPONSE相關標頭。
資源伺服器在首次請求時延遲初始化,並在 Worker 生命週期內快取。