@emdash-cms/x402パッケージは、Cloudflare上の任意のAstroサイトにx402決済プロトコルのサポートを追加します。スタンドアロンのAstro統合として動作し、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にエンフォーサーを配置します。ページの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モードの人間)。
ボット専用モード
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 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.redirect("/404");
const { x402 } = Astro.locals;
// Use the price from the CMS, falling back to a default
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>Full premium content here.</p>
) : (
<p>Subscribe for the full article.</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 mainnet | eip155:8453 |
| Base Sepolia | eip155:84532 |
| Ethereum | eip155:1 |
| Solana | solana:mainnet |
Enforceオプション
特定のページの設定デフォルトをオーバーライドします:
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
ネットワークをSolana識別子に設定し、Solanaのみを使用する場合はEVMを無効にします:
x402({
payTo: "YourSolanaAddress",
network: "solana:mainnet",
svm: true,
evm: false, // Disable EVM if only using Solana
});
仕組み
x402()統合は、エンフォーサーを作成してAstro.locals.x402に配置するミドルウェアを登録します- 設定はVite仮想モジュール(
virtual:x402/config)を介してミドルウェアに渡されます enforce()が呼び出されると、リクエストのpayment-signatureヘッダーをチェックします- 支払いヘッダーが存在しない場合、
PAYMENT-REQUIREDヘッダーに支払い指示を含む402 Payment Requiredレスポンスが返されます - 支払いヘッダーが存在する場合、ファシリテーターサービスを通じて検証され、決済されます
- 決済後、
applyHeaders()を介してレスポンスにPAYMENT-RESPONSEヘッダーが設定されます
リソースサーバーは最初のリクエストでレイジー初期化され、ワーカーのライフタイム中キャッシュされます。