Payment authorization, replay protection, and signed receipts for AI APIs.
Overview · How it works · Quick start · API and SDK · Development · Live demo
MicroAI Paygate is an open-source reference stack for payment-gated AI requests. It combines a Go API gateway, a Rust EIP-712 verifier, a Next.js wallet experience, configurable memory or Redis state, and a local TypeScript SDK.
An unsigned request receives HTTP 402 Payment Required. The client signs the returned payment context, retries with X-402-* headers, and receives an AI result plus a gateway-signed receipt.
Important
This project is x402-style, not an official x402 implementation. A valid signature proves wallet authorization for a payment context; it does not prove that USDC moved on-chain or that facilitator settlement occurred.
Try the Base Sepolia demo at microai-paygate.vercel.app.
Note
The gateway and verifier run on Render's free tier and can sleep after inactivity. The first request may take 30–50 seconds while both services wake.
- Explicit authorization — wallets sign EIP-712 payment contexts before the AI provider is called.
- Replay protection — the verifier supports process-local memory or shared Redis nonce claims.
- Signed receipts — the gateway signs request and response hashes and supports receipt lookup by ID.
- Operational controls — timeouts, CORS, rate limits, optional response caching, health checks, and Prometheus metrics are built in.
- Two client paths — use the browser wallet flow or the repo-local TypeScript SDK.
- Two AI providers — OpenRouter is the default; Ollama is available for local experiments.
sequenceDiagram
participant C as Client
participant G as Go gateway
participant V as Rust verifier
participant A as AI provider
participant R as Receipt store
C->>G: POST /api/ai/summarize
G-->>C: 402 + paymentContext
C->>C: Sign EIP-712 Payment
C->>G: Retry with X-402-* headers
G->>V: Verify signature and context
V-->>G: Recovered wallet or rejection
G->>A: Generate summary
A-->>G: Summary
G->>R: Store signed receipt
G-->>C: 200 result + X-402-Receipt
The signed context binds the recipient, token, amount, nonce, timestamp, and chain ID. The verifier rejects malformed signatures, wrong chains, stale or future timestamps, and replayed nonces before the gateway calls the AI provider.
Request-bound authorization v2 is being rolled out in stages. The verifier, browser, and TypeScript SDK can already validate the v2 audience, method, encoded resource, content type, exact serialized body hash, and claimed payer; the public gateway continues issuing v1 contexts until the cutover is deployed.
flowchart LR
Browser["Web app<br/>Next.js :3001"]
SDK["TypeScript SDK"]
Gateway["API gateway<br/>Go + Gin :3000"]
Verifier["Signature verifier<br/>Rust + Axum :3002"]
AI["OpenRouter or Ollama"]
Redis["Redis<br/>nonces, receipts, optional cache"]
Browser --> Gateway
SDK --> Gateway
Gateway --> Verifier
Gateway --> AI
Gateway <--> Redis
Verifier <--> Redis
| Component | Responsibility |
|---|---|
gateway/ |
Public API, payment challenges, verifier orchestration, provider calls, receipts, cache, limits, and metrics. |
verifier/ |
EIP-712 recovery, chain and timestamp enforcement, and memory- or Redis-backed nonce replay protection. |
web/ |
Wallet detection, chain switching, signing, paid retry UX, receipt display, and browser documentation. |
sdk/typescript/ |
Programmatic challenge handling, typed-data signing, retries, receipt decoding, and trusted-key verification. |
tests/ |
End-to-end unsigned challenge, signed retry, verifier acceptance, receipt, and replay checks. |
| Tool | Version |
|---|---|
| Bun | 1.3.13+ |
| Go | 1.24.x |
| Rust | Stable |
| Docker and Redis | Optional; used for the Compose stack and shared persistence |
git clone https://github.com/AnkanMisra/MicroAI-Paygate.git
cd MicroAI-Paygate
bun install
(cd web && bun install)
(cd gateway && go mod download)
(cd verifier && cargo build -q)
cp .env.example .envSet these development values in .env:
OPENROUTER_API_KEYwhen using the default OpenRouter provider.SERVER_WALLET_PRIVATE_KEYto an unfunded development key used only for receipt signing.RECIPIENT_ADDRESSto the payment recipient shown in challenges.CHAIN_IDandEXPECTED_CHAIN_IDto the same chain; the default is Base Sepolia (84532).
Never use a funded wallet, seed phrase, production key, or real secret in local examples.
The example environment intentionally selects Redis-backed production-style stores. Override both stores for the lightweight local stack:
RECEIPT_STORE=memory \
VERIFIER_NONCE_STORE=memory \
CACHE_ENABLED=false \
bun run stackOpen:
- Web app: http://localhost:3001
- Gateway: http://localhost:3000
- Swagger UI: http://localhost:3000/docs
- Verifier health: http://localhost:3002/health
The Compose stack starts all services with Redis-backed receipts and verifier nonce protection:
docker compose up --build| Endpoint | Purpose |
|---|---|
POST /api/ai/summarize |
Return a payment challenge or process a signed summarize request. |
GET /api/receipts/{id} |
Fetch a stored signed receipt before its TTL expires. |
GET /healthz |
Gateway liveness. |
GET /readyz |
Verifier, provider, Redis-when-required, and gateway readiness. |
GET /metrics |
Prometheus metrics when METRICS_ENABLED is enabled; configurable with METRICS_PATH. |
GET /openapi.yaml |
Raw OpenAPI contract. |
GET /docs |
Swagger UI. |
Signed retries include:
X-402-Signature: <wallet signature>
X-402-Nonce: <nonce from paymentContext>
X-402-Timestamp: <timestamp from paymentContext>Successful responses return the signed receipt as base64-encoded JSON in X-402-Receipt. See gateway/openapi.yaml for the complete contract.
The private repo-local package @microai/paygate-sdk automates the unsigned request, challenge signing, paid retry, receipt decoding, and trusted-key verification flow.
cd sdk/typescript
bun install
bun run typecheck
bun run testInstall the unpublished package in another local app before importing it:
cd /path/to/your-app
bun add /path/to/MicroAI-Paygate/sdk/typescriptimport { ethers } from "ethers";
import { PaygateClient } from "@microai/paygate-sdk";
const client = new PaygateClient({
gatewayUrl: "http://localhost:3000",
signer: new ethers.Wallet(process.env.EVM_PRIVATE_KEY!),
trustedServerPublicKey: process.env.PAYGATE_SERVER_PUBLIC_KEY,
});
const response = await client.summarize("Text to summarize");
console.log(response.data.result, response.receiptVerified);Read the SDK guide for local installation, error codes, receipt trust, and live testing.
The full local template is .env.example; production placeholders are in .env.production.example.
| Variable | Purpose |
|---|---|
AI_PROVIDER |
openrouter by default or ollama for a local provider. |
OPENROUTER_API_KEY, OPENROUTER_MODEL |
OpenRouter credentials and model selection. |
SERVER_WALLET_PRIVATE_KEY |
Signs gateway receipts; keep it secret and unfunded in demos. |
RECIPIENT_ADDRESS, PAYMENT_AMOUNT |
Values embedded in payment contexts. |
CHAIN_ID, EXPECTED_CHAIN_ID |
Gateway and verifier chain IDs; these must match. |
VERIFIER_URL |
Verifier base URL used by the gateway; required at startup. |
PAYGATE_AUDIENCE |
Public gateway origin reserved for request-bound authorization v2; configure it before the gateway cutover. |
VERIFIER_NONCE_STORE |
memory locally or redis for shared replay protection. |
RECEIPT_STORE |
memory locally or redis for restart-safe receipts. |
REDIS_URL |
Required by Redis nonce, receipt, or response-cache modes. |
CACHE_ENABLED |
Enables the optional Redis response cache; signed cache hits are still verified. |
NEXT_PUBLIC_GATEWAY_URL |
Browser-visible gateway URL compiled into the web app. |
Service-specific options are documented in the gateway, verifier, and web guides.
Run the checks for every component you change:
| Area | Commands |
|---|---|
| Gateway | cd gateway && go test -v ./... && go vet ./... |
| Verifier | cd verifier && cargo fmt -- --check && cargo clippy -- -D warnings && cargo test |
| Web | cd web && bun run lint && bun run typecheck && bun run test:unit && bun run build |
| SDK | cd sdk/typescript && bun run typecheck && bun run test |
| Unit suite | bun run test:unit |
| E2E | RECEIPT_STORE=memory VERIFIER_NONCE_STORE=memory CACHE_ENABLED=false bun run test:e2e — also requires OPENROUTER_API_KEY for the default provider |
Tip
Do not replace bun run test:e2e with plain bun test; the E2E script builds and starts the gateway and verifier first.
The demo deployment uses Render for the gateway and verifier, Vercel for the web app, and Upstash Redis for shared nonces and signed receipts. Follow DEPLOY.md for the platform-specific setup and secret checklist.
| Topic | Guide |
|---|---|
| Web documentation | Run cd web && bun run dev, then open /docs. |
| Public API | gateway/openapi.yaml |
| Contributor workflow | CONTRIBUTING.md |
| Security reporting | SECURITY.md |
| Support | SUPPORT.md |
| Repository rules | RULES.md |
| Benchmarks | bench/README.md |
- The protocol uses custom
X-402-*headers and has no official facilitator adapters. - Wallet signatures authorize payment contexts but do not prove on-chain settlement.
- Gateway rate limits are process-local; horizontally scaled deployments need distributed limits.
- Memory-backed nonces and receipts are single-process development modes; use Redis for shared or restart-safe state.
- The default demo chain is Base Sepolia; changing chains requires aligned gateway, verifier, web, SDK, test, and documentation configuration.
If this project helps you, consider starring the repository so more contributors can find it.