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 證明。