x402 Micro Tollgate

io.github.kevin2003050666-coder由 GitHub 帳號 kevin2003050666-coder 發布v0.3.3更新於 Oct 24, 2026

x402 HTTP 402 + MCP paywall: charge USDC per agent call. Self-host via npx or Docker.

發布方自己的服務:Mu

已驗證STDIO僅桌面金融開發者工具

概覽

AI 產生的概覽

自架式的 x402 收費閘道,為你的 API 或 MCP 工具加上 USDC 微支付門檻,讓代理程式按次付費。

可以做什麼
提供免費的報價與探索介面:伺服器資訊、價格報價、商戶登錄表和給代理程式與爬蟲的探索清單,以及健康檢查和 OpenAPI、llms.txt 文件,方便在付費前了解目錄與價格。為付費路由設下門檻:未付費請求會收到 HTTP 402 價格標籤而非直接拒絕,USDC 結算完成後閘道會把請求代理到你的上游 API。擷取並轉換網頁:付費路由可擷取 HTML 頁面並以 Markdown 回傳,具備 SSRF 防護。透過 Coinbase CDP 結算:驗證並結算 EIP-3009 授權,具備重放防護,以及結算緩慢時的待處理與重試狀態。
適用情境
當你販售 API 或 MCP 工具,希望代理程式以 USDC 按次付費,又不想自建計費後台或按月席位收費時使用。適合想在既有上游 API 前部署輕量自架收費閘道的營運者,也可用示範模式在不進行鏈上結算的情況下體驗 402 流程。
執行需求
以 Node.js 22+ 本機程序執行(npx 或 Docker),或作為 HTTP 服務執行並在 8402 埠提供 MCP 端點。實際結算需要 Coinbase CDP 憑證(CDP_API_KEY_ID、CDP_API_KEY_SECRET)和收款地址(X402_PAY_TO 或 SELLER);缺少時會進入示範模式。網路相關設定包括 NETWORK、PRICE、UPSTREAM_URL 和 X402_ENVIRONMENT。
安裝前請注意
實際模式會轉移真實 USDC:收款地址(X402_PAY_TO 或 SELLER)必須正確,地址打錯會把資金送到錯誤位置。CDP_API_KEY_ID 與 CDP_API_KEY_SECRET 是密鑰;買方輔助工具和 PoC 腳本使用熱錢包私鑰(BUYER_PRIVATE_KEY),其中只應放少量資金。選用的 keeper 會呼叫 release() 並消耗 gas。付費請求會代理到你的上游,應以 UPSTREAM_SHARED_SECRET 保護上游。結算依賴單一供應商 Coinbase CDP。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 x402 Micro Tollgate,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

x402-micro-tollgate

Visa for AI agents that can’t hold a card.

Second-scale settlement bridge for agent traffic — the Web3 tollbooth that clears USDC micropayments and takes 0.1%.

为千万级无支付能力的 AI Agent 提供秒级过桥清算,抽成 0.1% 的 Web3 版 Visa 收费站

Security backed by Coinbase CDP. We don't touch your keys or settle payments on custom cryptography — EIP-3009 authorization nonces are single-use at the CDP facilitator (source of truth for on-chain uniqueness). The gateway hardens four buyer/seller trust surfaces: payment-proof replay/race, SSRF on URL fetch, upstream bypass (shared-secret trust header), and Base congestion / settle-latency (pending + retry same proof — never treat HTTP timeout alone as “payment failed”).

What it is

A thin self-hosted tollgate in front of your API or MCP tools. When an agent calls a paid route, it gets HTTP 402 as a price tag (not a hard deny). It pays a small USDC (or configured USDT) amount; you deliver. Default clearing is Base + USDC via Coinbase CDP; multi-network accepts are env-driven (see matrix below). Humans can still hit a free path; agents convert at the booth.

Self-host is free (MIT). Repo: github.com/kevin2003050666-coder/x402-micro-tollgate · Questions: [email protected]

Not an official Coinbase product. Not a full agent marketplace. Not a billing SaaS. A sharp Visa-style tollbooth. Not fiat custody.

Why this exists: MANIFESTO.md — pain, engineering, on-chain proof, MIT.

Network × asset matrix

Default deploy profile: Base USDC only (dev: Base Sepolia). Enable multi with NETWORKS + ASSETS or ACCEPTS_JSON / X402_* aliases. Browser HTML 402 shows a chain + asset picker when more than one accept is configured.

NetworkCAIP-2USDCUSDTStatusFacilitator / notes
Baseeip155:8453native CirclebridgedliveCDP exact. FeeSplitterFactory live (deployments/base.json)
Base Sepoliaeip155:84532test USDC—liveCDP testnet
Optimismeip155:10nativebridgedconfig-readyAddresses wired; not on current CDP matrix
Arbitrum Oneeip155:42161nativebridgedliveCDP exact; factory stub
Polygon PoSeip155:137native (not USDC.e)PoS USDTliveCDP exact; factory stub
BNB Smart Chaineip155:56peg 18 decpeg 18 decconfig-readyNot on CDP list
Ethereumeip155:1nativeTetherconfig-readyFacilitator-dependent
Avalanche C-Chaineip155:43114nativeUSDTconfig-readyIn @x402/evm defaults
Celo / Seieip155:42220 / 1329USDC—config-readyCatalog extras
Solanasolana:5eykt…SPL USDCSPL USDTexperimentalCDP lists Solana; gateway stubs + optional SVM paywall (SOLANA_PAY_TO)
TRONtron:mainnet—TRC-20plannedNo scheme in deps — never in accepts[]

USDT on EVM typically uses extra.assetTransferMethod: "permit2" (not EIP-3009). FeeSplitter production path remains Base + USDC; other chains are config stubs under contracts/deployments/.

Browser wallets

WalletRole
Coinbase Smart Wallet (Passkey)Primary CTA
MetaMaskVia wagmi injected target
InjectedOther browser wallets
WalletConnectWhen WALLETCONNECT_PROJECT_ID set
Solana paywallExperimental, behind Solana accepts / PAYWALL_SVM
TronLinkNot offered (TRON planned only)

Who it’s for

  • API sellers who want agents to pay per call — without building a billing console or selling monthly seats
  • Tool builders shipping paid MCP endpoints into Cursor / Claude
  • Operators who want Visa-like take-rate economics (0.1%), not hosting invoices

How money works

  1. Price tag — unpaid traffic gets HTTP 402 with the amount due (conversion, not rejection)
  2. Pay — the agent settles a USDC micropayment in seconds via Coinbase CDP
  3. Unlock — the tollgate proxies to your upstream; you keep ~99.9%, protocol take 0.1%

No protocol monthly fee. Monetization is the toll — like interchange on card rails.


1-minute quickstart

Requires Node.js 22+.

bash
git clone https://github.com/kevin2003050666-coder/x402-micro-tollgatecd x402-micro-tollgate && cp .env.example .env# set CDP_API_KEY_ID, CDP_API_KEY_SECRET, X402_PAY_TO (optional: PUBLIC_BASE_URL, UPSTREAM_URL)npm i && npm start# curl http://127.0.0.1:8402/health   → 200# curl http://127.0.0.1:8402/x402/discover → 200 (free agent yellow pages)# curl http://127.0.0.1:8402/v1/quote → 402# curl "http://127.0.0.1:8402/v1/fetch-md?url=https://example.com" → 402 (paid HTML→Markdown demo)

npx one-liners (no clone):

bash
npx [email protected]npx [email protected] --seller 0xYourReceivingAddress --stdio

--seller / -s sets X402_PAY_TO before boot (env vars still work). Default without --stdio is HTTP + /mcp on port 8402.

Agent / crawler docs: llms.txt (also GET /llms.txt and GET /.well-known/llms.txt) · OpenAPI 3.1: docs/openapi.yaml · well-known: GET /.well-known/x402.json, GET /.well-known/agent-card.json

Alternative one-liner: docker compose up --build

Without CDP keys the process runs in demo mode (protocol-shaped 402 / MCP PaymentRequired, no on-chain settle).

Cursor mcp.json:

json
{  "mcpServers": {    "x402-micro-tollgate": {      "url": "http://127.0.0.1:8402/mcp"    }  }}

Stdio:

json
{  "mcpServers": {    "x402-micro-tollgate": {      "command": "npx",      "args": [        "[email protected]",        "--seller",        "0xYourReceivingAddress",        "--stdio"      ],      "env": {        "CDP_API_KEY_ID": "...",        "CDP_API_KEY_SECRET": "...",        "PUBLIC_BASE_URL": "https://your.public.host"      }    }  }}

(X402_PAY_TO in env still works if you omit --seller.)

Library drop-in (permissionless seller)

ts
import express from "express";import { x402Tollgate } from "x402-micro-tollgate";
const app = express();app.use("/v1", await x402Tollgate({ seller: process.env.SELLER! }));

Or set SELLER / X402_SELLER in .env and run npm start. See Environment for the $10 threshold, factory address, and fee-release details.

Buyer / Agent client

Thin TypeScript helper that wraps official @x402/fetch + @x402/evm ExactEvmScheme. On HTTP 402 it parses PAYMENT-REQUIRED, checks budgets, signs EIP-3009 once, and retries once with PAYMENT-SIGNATURE (never loops).

ts
import { createX402Fetch } from "x402-micro-tollgate/client";const fetch402 = createX402Fetch({ privateKey: process.env.BUYER_KEY as `0x${string}` });const res = await fetch402("https://x402-micro-tollgate.onrender.com/v1/quote");

Defaults: maxSingleSpendUsdc = 0.05, maxTotalSpendUsdc = 1.00 (clear Error if exceeded). Circuit breaker (before sign): max 10 paid 402s / 0.05 USDC per rolling 60s, plus fingerprint dead-loop halt (maxPaidRequestsPerMinute, maxSpendUsdcPerMinute, enableFingerprintBreaker). Hot-wallet warning: keep only ~$5–$10 USDC on the signing key — treat it as an agent spend faucet, not a treasury.

Human-delegated agent credit (CLI)

Thin PoC: Human sets B_max → Agent loops createX402Fetch against a paid URL (auto 402 → pay once → retry). Chrome / Telegram / Session Key / Passkey UI are deferred.

bash
# SAFETY: fund BUYER_PRIVATE_KEY with ≤ $5–$10 USDC only. Never commit keys.export BUYER_PRIVATE_KEY=0xYourHotWalletKey# optional: TARGET_URL MAX_SINGLE_USDC MAX_TOTAL_USDC ROUNDSnpm run poc:buyer-agent# or: npx tsx scripts/buyer-agent-poc.ts
EnvDefaultNotes
BUYER_PRIVATE_KEY(required)0x… EOA used to sign EIP-3009
TARGET_URLhttps://x402-micro-tollgate.onrender.com/v1/quotePaid route to hit
MAX_SINGLE_USDC0.05Per-call cap (maxSingleSpendUsdc)
MAX_TOTAL_USDC1.00Session cap / human B_max
ROUNDS3Stop after N calls or when budget exhausted

The script imports the local client (../src/client) so repo CI does not need a live chain pay. For a live demo against the published package, swap the import to x402-micro-tollgate/client (same API as 0.3.2+). Each round prints status, autoPaid, and sessionSpendUsdc on stdout; the safety banner goes to stderr.


Deploy

[Deploy to Render]

Uses render.yaml: Node 22, npm start, health /health, env names from .env.example. Set CDP_API_KEY_ID, CDP_API_KEY_SECRET, and X402_PAY_TO in the dashboard for live settlement; set PUBLIC_BASE_URL to your https://….onrender.com origin for Bazaar.

Ops bump: redeploy discover + well-known (force Render GitHub auto-deploy so production picks up GET /x402/discover, GET /.well-known/x402.json, GET /.well-known/agent-card.json, and MCP fetch_md). If auto-deploy does not fire within a few minutes after this lands on main, use the Render dashboard → Manual Deploy → Deploy latest commit.

Self-host production: Docker — docker compose up --build (see Dockerfile / docker-compose.yml).


Environment

VariableDefaultNotes
PORT8402HTTP listen port
UPSTREAM_URL(unset → mock)Your API origin
UPSTREAM_SHARED_SECRET / X402_UPSTREAM_SECRET—Optional. After settle, tollgate injects X-Tollgate-Secret + X-Tollgate-Paid (HMAC). Upstream must require them and reject public direct hits (not mTLS)
X402_PAY_TO—EVM receive address (live mode SDK init)
SELLER / X402_SELLER—Permissionless seller EOA (EIP-55 validated; invalid → startup fail)
FACTORY_ADDRESS—Operator-set FeeSplitterFactory for CREATE2 predict when amount ≥ threshold (Base live address in contracts/deployments/base.json; do not hardcode secrets)
FEE_FREE_BELOW_USDC / X402_FEE_FREE_BELOW_USDC10000000Atomic USDC (6 decimals). < $10 → payTo=seller; ≥ $10 → FeeSplitter
CDP_API_KEY_ID / CDP_API_KEY_SECRET—CDP facilitator (+ Onramp session tokens)
CDP_CLIENT_API_KEY—Public CDP client key for browser Smart Wallet paywall (safe in frontend). Alias: CDP_CLIENT_KEY
X402_FACILITATOR_URL / CDP_FACILITATOR_URLCDP defaultOptional alternate facilitator base URL for MCP (createCdpFacilitatorClient({ baseUrl })). Single-vendor; no multi-facilitator routing yet
PRICE$0.001Network default USDC/USDT price tag
NETWORK / X402_NETWORKeip155:84532 / 8453Primary CAIP-2 (aliases: base, optimism, …)
NETWORKS / X402_NETWORKS(primary only)Comma/JSON list of networks for multi-accept
ASSETS / X402_ASSETSUSDCCross with NETWORKS → accepts (USDC, USDT)
ACCEPTS_JSON / X402_ACCEPTS_JSON—Explicit accepts array (overrides NETWORKS×ASSETS)
FACTORY_ADDRESSES—JSON map caip2 → FeeSplitterFactory (Base live; others optional)
SOLANA_PAY_TO—Base58 payTo for experimental Solana accepts
PAYWALL_SVMfalseForce @x402/paywall SVM UI (also auto if Solana in accepts)
WALLETCONNECT_PROJECT_ID—Enables WalletConnect in browser paywall
X402_MIN_PRICE_USDC—Optional static minimum accept amount (atomic USDC). Effective price = max(PRICE, this, gas floor)
X402_DYNAMIC_MIN_ENABLEDfalseWhen true, Base base-fee oracle may bump min accept / feeFreeBelow if gas would eat too much of the payment
X402_GAS_COST_MAX_FRACTION0.5Bump when estimated gas USD > this fraction of the payment
X402_GAS_ORACLE_TTL_MS30000baseFee cache TTL (clamped 15–60s)
X402_GAS_RPC_URL / BASE_RPC_URLpublic Base RPCJSON-RPC for eth_getBlockByNumber baseFee
X402_GAS_USED_ESTIMATE100000Rough L2 gas units for settle cost estimate
X402_ETH_USD4000Conservative ETH/USD floor (no live FX required)
X402_ENVIRONMENTdevelopmentor production
GATED_PREFIX/v1HTTP paths that require payment
PUBLIC_BASE_URLhttp://127.0.0.1:$PORTPublic https:// origin for Bazaar resource URLs
CONTACT_EMAIL[email protected]Landing contact mailto (not a SaaS CTA)
FEE_BPS10Documented operator fee (0.1%). Live settle still pays 100% to payTo until release()
FEE_COLLECTOR0xa922…7e30EFixed operator wallet for the 0.1% slice after FeeSplitter.release()
MERCHANTS_JSON—Optional hosted multi-tenant registry (not required when SELLER is set)
MERCHANTS_FILEmerchants.jsonFile path; falls back to merchants.example.json / built-in demo when no seller
DEFAULT_MERCHANTdemoUsed when ?merchant= / x-merchant-id omitted — agents SHOULD always send merchant id
REQUIRE_MERCHANTfalseWhen true, gated paths reject missing merchant id with 400 {error:"merchant_required"} (no demo fallback)
PAYMENT_DEDUPE_TTL_MS600000In-memory payment-proof idempotency window (10 min). CDP facilitator nonce remains source of truth
PAYMENT_DEDUPE_MAX_ENTRIES10000Max keys in the gateway PaymentDedupeStore LRU (no Redis; interface is Redis-ready)
X402_VERIFY_TIMEOUT_MS15000Documented verify-phase budget (local / facilitator verify)
X402_SETTLE_TIMEOUT_MS180000Wait for facilitator/on-chain settle before 202 payment_pending (default 3 min — Base congestion)
KEEPER_ENABLEDfalseOptional FeeSplitter release() keeper — never on by default
KEEPER_DRY_RUN(see notes)true logs keeper_would_release without sending txs
KEEPER_PRIVATE_KEY—EVM key for live release() only (never commit)
KEEPER_RPC_URLBase public RPCJSON-RPC endpoint for balance + release
KEEPER_INTERVAL_MS3600000Poll interval (1h)
KEEPER_MIN_USDC1000000Min USDC balance (atomic) before release() — default $1

Permissionless seller + $10 threshold (0.3.0)

0.3.0: set SELLER (or use x402Tollgate({ seller })) and FACTORY_ADDRESS for ≥ $10 CREATE2 routing. No MERCHANTS_JSON required.

Amount (USDC atomic, 6 decimals)accepts[].payTo
< 10_000_000 ($10)seller EOA — 0 protocol fee
≥ 10_000_000 ($10)CREATE2-predicted FeeSplitter for that seller

x402 exact + EIP-3009 only credits payTo — it does not execute FeeSplitter / factory code and does not split in the same transaction. For ≥ $10: set FACTORY_ADDRESS to the operator-deployed factory (Base reference: contracts/deployments/base.json), call getOrCreate(seller) before the first such settle, then release() later (99.9% / 0.1%). Address typos send funds to the wrong place — the gateway checksum-validates SELLER at startup. Do not hardcode private keys or operator secrets in source.

Merchant registry (optional hosted multi-tenant)

Operator FEE_COLLECTOR is fixed: 0xa922F38041B5ee227c96A547F106F1330447e30E. Each merchant gets their own FeeSplitter (seller = merchant wallet, feeCollector = operator, feeBps = 10). The registry maps merchantId → splitter address (payTo) + seller for display. When SELLER is set, the registry is optional; ?merchant= still works if you provide MERCHANTS_JSON.

Register a merchant:

  1. Deploy FeeSplitter with seller = merchant wallet, feeCollector = 0xa922F38041B5ee227c96A547F106F1330447e30E, feeBps = 10, and the chain’s native USDC as asset.
  2. Add an entry to MERCHANTS_JSON (or merchants.json / copy from merchants.example.json):
    json
    {  "acme": {    "seller": "0xMerchantWallet…",    "payTo": "0xDeployedFeeSplitter…",    "label": "Acme API"  }}
  3. Call gated APIs with ?merchant=acme or header x-merchant-id: acme (case-insensitive). Agents SHOULD always send merchant id. If omitted, the gateway falls back to DEFAULT_MERCHANT (demo) — that means traffic (and USDC) can silently land on the demo FeeSplitter. Set REQUIRE_MERCHANT=true to reject missing merchant with 400 { "error": "merchant_required" }. Unknown merchant on gated paths → 400 { "error": "unknown_merchant" }.
  4. Free listing: GET /merchants (also /v1/merchants).
  5. Agent yellow pages: GET /x402/discover (alias GET /discover) — stable JSON catalog derived from the same registry / SELLER / demo. JSON config stays supported; there is no on-chain Registry.sol $5 stake in this release.

Note: The Base demo splitter has seller = feeCollector = operator — fine for demo. Real merchants need their own splitter with their wallet as seller.

CDP createX402Server uses a single global payTo for SDK init (X402_PAY_TO or the default merchant splitter). Per-request merchant routing rewrites PAYMENT-REQUIRED accepts[].payTo to the resolved FeeSplitter (same pattern as the https resource.url rewrite).

If X402_PAY_TO is still an EOA and you only have one merchant, behavior stays simple. Multi-merchant production should point each registry payTo at a deployed splitter — x402/exact credits that splitter via EIP-3009; call release() later (manually or via the optional keeper) to send 99.9% / 0.1%. Same contract on Base / Arbitrum / Polygon — see the multi-chain FeeSplitter matrix. This is receive → later release(), not an atomic same-transaction split and not OpenZeppelin PaymentSplitter.

Discovery (live) + liquidity roadmap (planned)

Ship now — Discovery only. Free route GET /x402/discover (alias /discover) returns agent-readable yellow pages from existing sources (MERCHANTS_JSON / merchants file / SELLER / built-in demo) + PUBLIC_BASE_URL. No 402. Example shape:

json
{  "version": 1,  "network": "eip155:8453",  "updatedAt": "2026-09-03T00:00:00.000Z",  "source": "merchants",  "services": [{    "id": "demo",    "label": "demo (operator is also seller)",    "endpoint": "https://your-host/v1/quote?merchant=demo",    "mcp": "https://your-host/mcp",    "capabilities": ["quote", "proxy", "fetch-md"],    "price": "$0.001",    "asset": "USDC",    "payTo": "0x…",    "seller": "0x…",    "status": "demo"  }]}

status is health-honest: live only when CDP facilitator credentials + payTo are configured; otherwise demo (or config when accepts are non-live). This is not an on-chain Agent Registry.

Planned (documented only — do not treat as shipped):

TrackWhy not now
Flash Liquidity Pool (0-confirm advance across chains)Capital-intensive: a solo founder cannot hold multi-chain USDC float. 0-confirm advance = credit risk. Need a circuit-breaker when the hot wallet balance is insufficient. If Superchain / AggLayer gets sub-second native interoperability, cross-chain friction (S_{cross}) → 0 and this track may shrink.
Reverse Bounty (pay agents to call)Sybil drain if the seller subsidizes calls. Require rate-limit / per-agent identity / IP+fingerprint gates before any claimBounty. The current ~$20 Discord/X bounty stays a manual operator payout — not an on-chain claim.

Full notes: docs/ROADMAP-LIQUIDITY.md. No depositBounty / claimBounty on FeeSplitter and no fake “live” cross-chain clearing claims in this release.

发现层已上线(GET /x402/discover);闪电流动性池与反向赏金仅为规划,见 roadmap。

Gas vs micropayment floor (optional)

Micropayments ($0.001–$0.01) assume Base low fees. When L2 gas spikes, estimated settle cost can eat most of a tiny payment. Opt in with X402_DYNAMIC_MIN_ENABLED=true: a cached Base baseFee oracle (TTL 15–60s, no RPC spam) estimates gas USD (gasUsedEstimate * baseFee * X402_ETH_USD) and, if that exceeds X402_GAS_COST_MAX_FRACTION of the payment (default 50%), bumps the displayed/enforced minimum accept amount and may raise feeFreeBelow so FeeSplitter+release() isn’t used until larger amounts. Default is OFF so demo $0.001 still works under normal conditions. Operators can also set a static X402_MIN_PRICE_USDC. Current effective mins are on GET /health → gasFloor.

Security (gateway hardening)

Four defenses buyers and sellers should know about:

  1. Payment signature replay / race — Idempotency key = SHA-256(PAYMENT-SIGNATURE). An in-process mutex + PaymentDedupeStore (memory today; Redis-shaped interface) does set-if-absent before upstream. Concurrent losers get 409 { "error": "payment_already_used" }. Durable mark happens only after settle success; verify failure releases the pending reservation so honest retries are not bricked.
  2. SSRF on /v1/fetch-md — Only http/https; DNS resolve rejects private/bogon/link-local/metadata; DNS is re-checked before fetch (rebinding defense); redirects are disabled.
  3. Upstream bypass — Optional UPSTREAM_SHARED_SECRET: after payment, proxy injects X-Tollgate-Secret / X-Tollgate-Paid / X-Tollgate-Timestamp. Your upstream must require them (see snippet below). This is a shared-secret MVP, not mTLS.
  4. Base congestion / settle latency — X402_SETTLE_TIMEOUT_MS (default 3 minutes) is separate from the short verify budget. If settle is still in progress when the waiter expires → 202 { "error": "payment_pending", "retry_with_same_proof": true }. Do not create a new payment and do not treat the buyer as failed solely because HTTP timed out — retry the same proof; the gateway resumes without double-settling.

CDP facilitator is a single-vendor settle dependency by default; see SECURITY.md for X402_FACILITATOR_URL (alternate facilitator when available — no multi-facilitator routing yet).

Payment proof idempotency + settle pending

CDP x402 exact + EIP-3009 authorizations are single-use at the facilitator (nonce). The gateway additionally tracks each proof fingerprint as pending → settled → consumed:

Store stateClient sees
pending (settle in flight / after settle-wait timeout)202 payment_pending + retry_with_same_proof: true
settled (paid; upstream not delivered yet)Skip re-settle; proxy once; mark consumed
consumed409 payment_already_used

Process-local only by default (restart clears). Facilitator remains authoritative for on-chain nonce uniqueness.

Upstream trust header (Express example)

ts
import { createHmac, timingSafeEqual } from "node:crypto";import type { RequestHandler } from "express";
const SECRET = process.env.UPSTREAM_SHARED_SECRET!;
export const requireTollgate: RequestHandler = (req, res, next) => {  const secret = req.header("x-tollgate-secret") ?? "";  const paid = req.header("x-tollgate-paid") ?? "";  const ts = req.header("x-tollgate-timestamp") ?? "";  const expected = createHmac("sha256", SECRET)    .update(`${ts}.${req.method.toUpperCase()}.${req.path}`, "utf8")    .digest("hex");  const okSecret =    secret.length === SECRET.length &&    timingSafeEqual(Buffer.from(secret), Buffer.from(SECRET));  const okPaid =    paid.length === expected.length &&    timingSafeEqual(Buffer.from(paid), Buffer.from(expected));  if (!okSecret || !okPaid) {    res.status(401).json({ error: "tollgate_required" });    return;  }  next();};
// app.use(requireTollgate); // reject public direct hits

Or import verifyUpstreamTrustHeaders from x402-micro-tollgate in your upstream process.

Optional FeeSplitter release keeper

Scaffold in src/keeper.ts. Off by default (KEEPER_ENABLED unset/false). When enabled, every KEEPER_INTERVAL_MS (default 1h) it checks USDC balances of registry payTo FeeSplitter addresses and calls release() when balance ≥ KEEPER_MIN_USDC (default $1).

Warning: Gas for release() can exceed the 0.1% operator fee on sub-cent payments — hence the min-balance gate. Do not turn this on by default on Render. Prefer KEEPER_DRY_RUN=true (logs keeper_would_release) before using a real KEEPER_PRIVATE_KEY. Never commit keys.


Bazaar discovery (agents find sellers)

Bazaar is the x402 discovery catalog.

  • HTTP: createX402Server auto-injects bazaar; we override with discoverable: true, descriptions, and input/output schemas (GET /v1/quote + gated prefix proxies).
  • MCP: x402ResourceServer does not auto-declare Bazaar — we register bazaarResourceServerExtension and pass declareDiscoveryExtension({ toolName, inputSchema, … }) on get_quote / proxy_request / fetch_md. Resource URL is PUBLIC_BASE_URL/mcp (a real http(s) URL, not a display name).
  • Origin well-known: GET /.well-known/x402.json (alias /.well-known/x402) mirrors discover + lists live paid HTTP/MCP resources for facilitator-independent crawlers.

To appear in Bazaar:

  1. Set PUBLIC_BASE_URL to a public https origin (localhost listings are a no-op for real crawlers).
  2. Run with live CDP credentials + X402_PAY_TO.
  3. Complete one successful settlement through the CDP facilitator (empty-body probes on gated routes return 402, not 400).

Launch tip: share your /mcp or /v1/quote URL in Discord #x402 after that first settlement.


Pay in browser (Smart Wallet)

When a browser hits a gated route (Accept: text/html + Mozilla UA), the gateway returns a thin HTML paywall instead of JSON. Agents / MCP / curl keep the JSON 402 body.

What you get

  1. Connect / create a Coinbase Smart Wallet via the official @x402/paywall EVM UI (Coinbase Wallet connector — Passkey Smart Wallet, no MetaMask required).
  2. Sign the x402 exact EIP-3009 payment on Base (or Base Sepolia in development) and retry with PAYMENT-SIGNATURE.
  3. Get USDC (optional): when server CDP keys are set, the paywall shows a Get USDC button that calls POST /x402/session-token and opens Coinbase Onramp. Apple Pay / card appear only if Onramp supports them for your domain — this repo does not fake Apple Pay outside Onramp, and does not claim a guaranteed “10 second Apple Pay” without Portal production access.

Setup (buyer UX)

bash
# .env — server secrets stay on the hostCDP_API_KEY_ID=…CDP_API_KEY_SECRET=…X402_PAY_TO=0x…          # or SELLER=0x…
# Public client key only (browser-safe)CDP_CLIENT_API_KEY=…     # from portal.cdp.coinbase.com
# Optional but recommended for live demo / BazaarPUBLIC_BASE_URL=https://your.hostX402_ENVIRONMENT=development   # Base Sepolia (eip155:84532)# X402_ENVIRONMENT=production  # Base mainnet (eip155:8453)
PieceRole
CDP_CLIENT_API_KEYInjected into paywall HTML as window.x402.cdpClientKey
CDP_API_KEY_ID + CDP_API_KEY_SECRETFacilitator settle and POST /x402/session-token (Onramp JWT)
POST /x402/session-tokenFree path; body { "addresses": [{ "address": "0x…", "blockchains": ["base"] }], "assets": ["USDC"] } → { token }

Honesty / production notes

  • MVP networks: Base Sepolia (development) and Base mainnet (production / NETWORK=eip155:8453).
  • Onramp production: enable Onramp in CDP Portal, allowlist your domain, and complete any Apple Pay domain verification Coinbase requires. Until then, Smart Wallet pay + sign + retry still works; Get USDC may error or omit card rails.
  • CSP: the paywall ships inline scripts (same as upstream @x402/paywall). Prefer not to set a strict script-src without nonces on 402 HTML responses.
  • Security: wallet private keys never touch the frontend; only the public client key is embedded. Session tokens are minted server-side.

Try it: open https://your-host/v1/quote in Chrome after configuring the keys above.


What it does

Agents / clients   ├─ HTTP  /v1/*          → x402 402 JSON (agents) or Smart Wallet HTML paywall (browsers)   ├─ GET   /v1/fetch-md   → paid HTML→Markdown demo (same x402 gate)   ├─ GET   /x402/discover → free agent yellow pages (alias /discover; from merchants JSON)   ├─ GET   /.well-known/x402.json → free Bazaar-friendly origin manifest (alias /.well-known/x402)   ├─ GET   /.well-known/agent-card.json → free agent card (alias /.well-known/agent.json)   ├─ GET   /llms.txt      → free AI-crawler summary (alias /.well-known/llms.txt)   ├─ GET   /openapi.yaml  → free OpenAPI 3.1 (alias /docs/openapi.yaml)   ├─ POST  /x402/session-token → free Onramp session token (when CDP server keys set)   ├─ GET   /merchants     → free merchant registry (id, label, seller, payTo)   ├─ GET   /health        → free (+ paywall config flags)   ├─ GET   /              → developer landing (EN / 中文)   └─ MCP   /mcp           → server_info (free), get_quote + proxy_request + fetch_md (paid + bazaar)
SurfaceStack
HTTPcreateX402Server + paymentMiddlewareFromHTTPServer + @x402/paywall (browser)
MCPx402ResourceServer + createCdpFacilitatorClient + createPaymentWrapper + Bazaar extension

CLI: npx [email protected] | --seller 0x… / -s | --stdio | --port N

Agent SEO: llms.txt · docs/openapi.yaml


Publishing

Package identity (must stay in sync):

  • package.json mcpName = io.github.kevin2003050666-coder/x402-micro-tollgate
  • server.json name = same string

Flow (needs tokens on a machine that has them — not this VM):

  1. Public GitHub mirror at kevin2003050666-coder/x402-micro-tollgate
  2. npm publish (or tag v* → .github/workflows/publish-mcp.yml)
  3. mcp-publisher publish (OIDC) → official MCP Registry
  4. PulseMCP auto-ingests from the official registry — do not submit to PulseMCP directly

Update server.json remotes[0].url to your real public /mcp before publishing remotes.


Tests

bash
npm test

No live CDP credentials required.


License

MIT · See CONTRIBUTING.md · SECURITY.md

來源:README.md,提交 55ac3b5

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.3.3最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分

其他 Micro 整合