AI

Cloudflare Monetization Gateway入門——AIエージェント決済をFirestoreで監査する

Cloudflare Monetization Gateway betaとx402を題材に、AIエージェント向けAPI課金をNext.js + Firestoreで監査する実践設計を解説します。

2026年10月1日
Cloudflarex402AIエージェントNext.jsFirestore
Cloudflare Monetization Gateway入門——AIエージェント決済をFirestoreで監査する

はじめに

AIエージェント向けにAPIやデータを公開しようとすると、こんな悩みが出てきます。

場面よくある困りごと
MCPツールや検索APIを外部エージェントに開きたいアカウント作成やAPIキー発行が重く、単発利用に向かない
リクエスト単位で少額課金したい従来のサブスクや前払いクレジットでは摩擦が大きい
支払い済みリクエストだけ処理したいアプリ側で決済検証と監査ログをどう残すか迷う

2026年9月30日、Cloudflareは Monetization Gateway beta を発表しました。HTTP 402 Payment Required とx402プロトコルを使い、AIエージェントがWebサイト、API、MCPツール、データセットへリクエスト単位で支払えるようにする仕組みです。

本記事では、架空の業務データAPI「InsightMeter」を題材に、Next.js App Router + Firestore + TypeScriptで「Cloudflare側で支払い検証されたリクエストだけを処理し、Firestoreへ監査台帳を残す」設計を作ります。ウォレット署名やx402クライアント実装は公式のx402対応クライアントに任せ、アプリ側が責任を持つ境界に集中します。

参考にした一次情報は次の通りです。

何が新しいのか

Monetization Gatewayは、Cloudflare配下のリソースに対して「どのリクエストを、いくらで、誰から課金するか」をルール化するゲートウェイです。公式ドキュメントでは、保護対象としてAPI、MCPツール、サイト、データセットが挙げられています。

従来の決済導線は、人間が画面を開き、チェックアウトへ進み、アカウントやカードを登録する前提でした。一方でAIエージェントは、タスクの途中で「この1回だけ検索したい」「この1件だけPDF化したい」のように、低額・短時間・単発のアクセスを必要とします。

Cloudflareの説明では、Monetization Gatewayはその場でHTTP 402 を返し、支払い要件を提示します。買い手側のx402対応クライアントは支払い承認に署名し、同じリクエストを再送します。Cloudflareが承認を検証できたら、オリジンへリクエストが届きます。

ここで大切なのは、Next.jsアプリが「決済事業者のように振る舞う」必要はないことです。支払い要件の提示、署名検証、決済処理はCloudflare側が担います。アプリ側は、Cloudflareから届く PAYMENT-CONTEXT を検証し、支払い済みの文脈として処理する責務を持ちます。

まず押さえる制約

2026年10月1日時点では、Monetization Gatewayはクローズドベータです。公式ドキュメントでは、買い手と売り手は米国ベースである必要があると説明されています。AI GatewayのMachine Paymentsも、米国ベースでクレジットカード登録済みのCloudflareアカウントが前提です。

また、AI Gatewayで機械決済を使う場合、Cloudflare固有の Payment-Method: x402 ヘッダーを付けます。対象エンドポイントは現時点で POST /ai/run、対象モデルも公式ドキュメントに列挙された一部モデルに限定されています。

観点2026-10-01時点の確認事項
提供状況Closed beta
課金対象API、MCPツール、サイト、データセット
ルール条件URL、ヘッダー、クエリ、呼び出し元属性など
価格方式固定価格は exact、変動価格は upto
オリジン検証PAYMENT-CONTEXT JWTを署名検証する

日本のサービスで今すぐ本番導入できる、という話ではありません。ただし、AIエージェント向けに「使った分だけ支払う」Webが来たとき、アプリ側で必要になる監査・集計・失敗時の扱いは今から設計できます。

ハンズオン1: 有料APIの境界を決める

InsightMeterでは、営業管理SaaS向けに「業界別の匿名ベンチマーク」を返すAPIを用意します。人間の管理画面は通常ログインで守り、AIエージェント向けにはCloudflare配下の /api/agent/benchmarks だけを有料リクエストとして公開します。

パス用途課金方針
/dashboard人間向け管理画面既存ログイン
/api/internal/*社内バッチ課金対象外
/api/agent/benchmarksエージェント向けデータAPIMonetization Gatewayで保護

Cloudflare側のルールでは、対象URL、HTTPメソッド、価格、課金するオーディエンスを設定します。公式ドキュメントでは、固定価格の exact と変動価格の upto が説明されています。InsightMeterのように1リクエストの原価がほぼ一定なら、まずは固定価格が扱いやすいです。

ハンズオン2: PAYMENT-CONTEXTを検証する

CloudflareのPayment validation docsでは、オリジンが PAYMENT-CONTEXT ヘッダーを受け取り、JWTとして検証する必要があるとされています。署名を検証せずに中身だけ読むのは避けます。TypeScriptでは公式推奨ライブラリとして jose が挙げられています。

// src/lib/monetization/payment-context.ts
import { createRemoteJWKSet, jwtVerify } from "jose";

type PaymentContext = {
  aud: string;
  scheme: "exact" | "upto";
  amount: string;
};

export async function verifyPaymentContext(
  headerValue: string | null,
  expectedAudience: string,
) {
  const jwksUrl = process.env.CLOUDFLARE_PAYMENT_JWKS_URL;

  if (!headerValue) {
    return { ok: false, reason: "missing_payment_context" as const };
  }

  if (!jwksUrl) {
    return { ok: false, reason: "jwks_not_configured" as const };
  }

  try {
    const jwks = createRemoteJWKSet(new URL(jwksUrl));
    const { payload } = await jwtVerify(headerValue, jwks, {
      algorithms: ["EdDSA"],
      audience: expectedAudience,
    });

    const context = {
      aud: String(payload.aud),
      scheme: payload.scheme,
      amount: String(payload.amount),
    } as PaymentContext;

    if (context.scheme !== "exact" && context.scheme !== "upto") {
      return { ok: false, reason: "unsupported_scheme" as const };
    }

    return { ok: true, context } as const;
  } catch {
    return { ok: false, reason: "invalid_payment_context" as const };
  }
}

CLOUDFLARE_PAYMENT_JWKS_URL には、Cloudflare公式ドキュメントが案内しているpinned JWKSのURLを設定します。expectedAudience には、有料APIとして公開している完全なURLを渡します。環境ごとにホスト名が変わる場合は、MONETIZATION_AUDIENCE のような環境変数で明示します。

ハンズオン3: Firestoreへ監査台帳を残す

次に、Next.js Route Handlerで支払い済みリクエストだけを処理します。ここでは、エージェント名やリクエストIDをヘッダーから受け取り、Firestoreの agentPaymentEvents へ保存します。

// src/app/api/agent/benchmarks/route.ts
import { getApps, initializeApp } from "firebase-admin/app";
import { FieldValue, getFirestore } from "firebase-admin/firestore";
import { NextRequest, NextResponse } from "next/server";
import { verifyPaymentContext } from "@/lib/monetization/payment-context";

if (!getApps().length) {
  initializeApp();
}

const db = getFirestore();

export async function GET(request: NextRequest) {
  const audience = process.env.MONETIZATION_AUDIENCE;

  if (!audience) {
    return NextResponse.json(
      { error: "monetization audience is not configured" },
      { status: 500 },
    );
  }

  const verification = await verifyPaymentContext(
    request.headers.get("payment-context"),
    audience,
  );

  if (!verification.ok) {
    return NextResponse.json({ error: "payment required" }, { status: 402 });
  }

  const agentName = request.headers.get("user-agent") ?? "unknown-agent";

  await db.collection("agentPaymentEvents").add({
    path: "/api/agent/benchmarks",
    agentName,
    scheme: verification.context.scheme,
    authorizedAmount: verification.context.amount,
    requestId: request.headers.get("cf-ray") ?? null,
    createdAt: FieldValue.serverTimestamp(),
  });

  return NextResponse.json({
    industry: "sample-retail",
    medianLeadTimeDays: 4.2,
    p75LeadTimeDays: 7.8,
    source: "anonymized-demo",
  });
}

このコードは、Cloudflareの決済処理そのものを再実装していません。見るべきポイントは、PAYMENT-CONTEXT がないリクエストを有料データへ進ませないこと、署名検証後の金額・方式・呼び出し元を監査ログに残すことです。

ハンズオン4: GitHub Actionsで日次集計する

最後に、AIエージェント向けAPIが想定外に使われていないかを毎朝確認します。Firestoreから前日の agentPaymentEvents を集計する社内APIを用意し、GitHub Actionsから呼び出します。

name: agent-payment-audit

on:
  schedule:
    - cron: "0 22 * * *"
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - name: Summarize paid agent traffic
        run: |
          curl --fail --request POST "${APP_URL}/api/internal/agent-payment-audit" \
            --header "Authorization: Bearer ${AUDIT_SECRET}"
        env:
          APP_URL: ${{ secrets.APP_URL }}
          AUDIT_SECRET: ${{ secrets.AUDIT_SECRET }}

集計では、リクエスト数だけでなく「同じエージェントから急増していないか」「upto の上限に近い請求が続いていないか」「無料扱いにすべき社内バッチが誤って課金対象に入っていないか」を見ます。

const snapshot = await db
  .collection("agentPaymentEvents")
  .where("createdAt", ">=", yesterdayStart)
  .where("createdAt", "<", todayStart)
  .orderBy("createdAt", "asc")
  .get();

Firestoreで範囲条件と並び替えを組み合わせる場合、インデックス作成が必要になることがあります。開発時にFirebaseコンソールの作成リンクが出たら、必要なものだけ追加します。

運用で気をつけたいこと

Monetization Gatewayは、ログイン済みユーザー向けの課金を置き換える万能な決済基盤ではありません。特にクローズドベータ中は、対象地域、対象ユースケース、価格設計を公式ドキュメントで確認しながら扱う必要があります。

一方で、AIエージェント向けAPIにはよく合います。アカウント作成前の単発アクセス、MCPツールの1回実行、検索APIの1クエリ、推論APIの1リクエストなど、消費単位が明確だからです。

アプリ側では、次の3点を最初から決めておくと運用しやすくなります。

決めること理由
有料パスを限定する人間向け画面や社内APIを誤って課金対象にしない
監査ログをFirestoreへ残す後からエージェント別・パス別に利用を追える
失敗時はデータを返さないPAYMENT-CONTEXT が欠けた状態で有料データを出さない

まとめ

Cloudflare Monetization Gateway betaは、「AIエージェントもWebの顧客になる」という流れをかなり具体的にしました。HTTP 402 とx402を使うことで、エージェントはチェックアウト画面に寄り道せず、リクエストの中で支払い要件を受け取り、承認して、目的のリソースへ到達できます。

Next.js + Firestoreのアプリでまず準備すべきなのは、ウォレット実装を急ぐことではありません。有料にする境界を小さく決めること、PAYMENT-CONTEXT を検証すること、支払い済みアクセスを監査できる台帳を作ることです。

AIエージェント向けのAPI公開は、これから「認証するか、ブロックするか」だけでは足りなくなります。「使うなら払える」入口を設計できるかどうかが、次のWeb API設計の差になりそうです。