@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 mainnet
defaultPrice: "$0.01",
botOnly: true,
botScoreThreshold: 30,
}),
],
});
TypeScript が Astro.locals.x402 を認識するよう型参照を追加します。
/// <reference types="@emdash-cms/x402/locals" />
基本的な使い方
統合は Astro.locals.x402 にエンフォーサーを置きます。ページのフロントマターで 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 で人間としてスキップされました。
ボットのみモード
botOnly が true のとき、統合は request.cf.botManagement.score を読みます。
- しきい値未満のスコア(既定 30)→ ボットとして扱い、課金を強制
- しきい値以上 → 人間として扱い、強制をスキップ
- Bot Management データなし(ローカル開発、非 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 フィールドを追加してページ価格を持てます。特別なスキーマは不要で、通常の 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 ネットワーク ID(例: 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 mainnet | eip155:8453 |
| Base Sepolia | eip155:84532 |
| Ethereum | 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を設定します。
リソースサーバーは最初のリクエストで遅延初期化され、ワーカー寿命中キャッシュされます。