MicroAI Paygate demonstrates a decentralized payment layer for AI services. Instead of traditional subscriptions, it utilizes the HTTP 402 (Payment Required) status code to enforce per-request crypto micropayments. The system has been re-architected from a monolithic Node.js application into a distributed microservices stack to ensure maximum throughput, type safety, and cryptographic security.
- x402 Protocol Implementation: Native handling of the HTTP 402 status code to gate resources.
- Distributed Architecture: Decoupled services for routing (Go), verification (Rust), and presentation (Next.js).
- EIP-712 Typed Signatures: Industry-standard secure signing for payment authorization.
- Micropayments: Low-cost transactions (0.001 USDC) on the Base L2 network.
- High Concurrency: Go-based gateway for handling thousands of simultaneous connections.
- Memory Safety: Rust-based verification service for secure cryptographic operations.
- Token Bucket Rate Limiting: Configurable per-IP and per-wallet rate limits with tiered access control.
Most AI monetization platforms rely on Web2 subscription models (Stripe, monthly fees) or centralized credit systems. These approaches introduce friction, require user registration, and create central points of failure.
MicroAI Paygate is designed to be frictionless and trustless:
- No Registration: Users connect a wallet and pay only for what they use.
- Stateless Verification: The verification logic is purely cryptographic and does not require database lookups for session management.
- Polyglot Performance: We use the right tool for the job—Go for I/O bound routing, Rust for CPU-bound cryptography, and TypeScript for UI.
- Standard Compliance: Fully compliant with EIP-712, ensuring users know exactly what they are signing.
The migration to a polyglot microservices architecture resulted in significant performance improvements across key metrics.
| Metric | Monolithic Stack (Node.js) | Microservices Stack (Go/Rust) | Improvement |
|---|---|---|---|
| Request Latency (P99) | 120ms | 15ms | 8x Faster |
| Verification Time | 45ms | 2ms | 22x Faster |
| Concurrent Connections | ~3,000 | ~50,000+ | 16x Scale |
| Memory Footprint | 150MB | 25MB (Combined) | 6x More Efficient |
| Cold Start | 1.5s | <100ms | Instant |
flowchart TB
subgraph Clients[Client Layer]
WEB[Web Frontend - Next.js]
AGENT[Agent Bot - TypeScript]
CLI[CLI / cURL]
end
subgraph Gateway[API Gateway Layer]
GW[Gateway - Go/Gin :3000]
end
subgraph Verification[Verification Layer]
VER[Verifier - Rust/Axum :3002]
end
subgraph External[External Services]
AI[OpenRouter API]
CHAIN[Base L2 - USDC]
end
WEB --> GW
AGENT --> GW
CLI --> GW
GW <--> VER
GW --> AI
WEB -.-> CHAIN
AGENT -.-> CHAIN
| Service | Technology | Port | Responsibility |
|---|---|---|---|
| Gateway | Go + Gin | 3000 |
Traffic routing, x402 enforcement, AI proxying |
| Verifier | Rust + Axum | 3002 |
EIP-712 signature recovery, ECDSA validation |
| Web | Next.js | 3001 |
React frontend with MetaMask integration |
The x402 protocol enables trustless, per-request payments using cryptographic signatures:
sequenceDiagram
autonumber
participant C as Client
participant G as Gateway
participant V as Verifier
participant AI as OpenRouter
Note over C,G: Phase 1 - Payment Challenge
C->>G: POST /api/ai/summarize
G-->>C: 402 Payment Required + paymentContext
Note over C: Phase 2 - User Signs Payment
C->>C: Sign EIP-712 TypedData with Wallet
Note over C,AI: Phase 3 - Verified Request
C->>G: POST with X-402-Signature + X-402-Nonce
G->>V: Verify signature
V->>V: Recover signer via ECDSA
V-->>G: is_valid + recovered_address
G->>AI: Forward to AI provider
AI-->>G: AI response
G-->>C: 200 OK + result
When a 402 Payment Required response is returned, it includes the payment context:
{
"error": "Payment Required",
"message": "Please sign the payment context",
"paymentContext": {
"recipient": "0x2cAF48b4BA1C58721a85dFADa5aC01C2DFa62219",
"token": "USDC",
"amount": "0.001",
"nonce": "9c311e31-eb30-420a-bced-c0d68bc89cea",
"chainId": 8453
}
}The client signs this data using EIP-712 and resends with headers:
X-402-Signature: The cryptographic signatureX-402-Nonce: The nonce from the payment context
The Gateway service utilizes Go's lightweight goroutines to handle high-throughput HTTP traffic. Unlike the Node.js event loop which can be blocked by CPU-intensive tasks, the Go scheduler efficiently distributes requests across available CPU cores.
- Framework: Gin (High-performance HTTP web framework)
- Concurrency Model: CSP (Communicating Sequential Processes)
- Proxy Logic: Uses
httputil.ReverseProxyfor zero-copy forwarding.
The Verifier is a specialized computation unit designed for one task: Elliptic Curve Digital Signature Algorithm (ECDSA) recovery.
- Safety: Rust's ownership model guarantees memory safety without a garbage collector.
- Cryptography: Uses
ethers-rsbindings tok256for hardware-accelerated math. - Isolation: Running as a separate binary ensures that cryptographic load never impacts the API gateway's latency.
Prerequisites
- Bun
- Go 1.24+
- Rust/Cargo (latest stable)
- Node.js 20+ (for Next.js 16.x tooling)
Clone & Install
git clone https://github.com/AnkanMisra/MicroAI-Paygate.git
cd MicroAI-Paygate
bun install
go mod tidy -C gateway
cargo build -q -C verifierConfigure Environment
Copy .env.example to .env and fill values (see next section).
Run the Stack
bun run stackRun Tests
bun run test:unit # Go + Rust unit tests
bun run test:go # Gateway tests only
bun run test:rust # Verifier tests only
bun run test:e2e # E2E (starts services automatically)
bun run test:all # Full test suite with E2ENote: Do NOT use
bun testdirectly - it triggers bun's native test runner without starting services.
Create a .env (or use .env.example) with at least:
OPENROUTER_API_KEY— API key for OpenRouter (required - validated at startup)OPENROUTER_MODEL— model name (default:z-ai/glm-4.5-air:free)SERVER_WALLET_PRIVATE_KEY— private key for the server wallet (recipient of payments)RECIPIENT_ADDRESS— wallet address for receiving paymentsCHAIN_ID— chain used in signatures (default:8453for Base)
Note: The gateway validates required environment variables at startup. If
OPENROUTER_API_KEYis missing, the server will exit with a helpful error message.
Optional Configuration:
USDC_TOKEN_ADDRESS— USDC contract address (default: Base USDC)PAYMENT_AMOUNT— cost per request in USDC (default:0.001)VERIFIER_URL— URL of verifier service (default:http://127.0.0.1:3002)
Ensure ports 3000 (gateway), 3001 (web), and 3002 (verifier) are free.
MicroAI Paygate implements token bucket rate limiting to prevent abuse and protect API quotas.
Features:
- Token bucket algorithm with burst support
- Tiered limits (anonymous, authenticated, verified)
- Per-IP and per-wallet tracking
- Standard
X-RateLimit-*headers
Default Limits:
| Tier | Requests/Minute | Burst | Identification |
|---|---|---|---|
| Anonymous | 10 | 5 | IP address |
| Standard | 60 | 20 | Signed requests (wallet nonce) |
| Verified | 120 | 50 | Premium users (future) |
Configuration:
Add to your .env file:
# Rate Limiting
RATE_LIMIT_ENABLED=true
# Anonymous users (IP-based, no signature)
RATE_LIMIT_ANONYMOUS_RPM=10
RATE_LIMIT_ANONYMOUS_BURST=5
# Standard users (signed requests)
RATE_LIMIT_STANDARD_RPM=60
RATE_LIMIT_STANDARD_BURST=20
# Verified users (future: premium tier)
RATE_LIMIT_VERIFIED_RPM=120
RATE_LIMIT_VERIFIED_BURST=50
# Cleanup interval for stale buckets (seconds)
RATE_LIMIT_CLEANUP_INTERVAL=300Response Headers:
X-RateLimit-Limit: Max requests per minute for your tierX-RateLimit-Remaining: Requests remainingX-RateLimit-Reset: Unix timestamp when limit resetsRetry-After: Seconds until reset (on 429 response)
The gateway implements context-based request timeouts to prevent slow/hanging requests from consuming resources.
Features:
- Global request timeout (default: 60s)
- Per-endpoint configurable timeouts
- Context cancellation for downstream calls
- Buffered response to prevent race conditions
- Returns
504 Gateway Timeoutwhen exceeded
Default Timeouts:
| Endpoint | Timeout | Purpose |
|---|---|---|
| Global | 60s | Maximum request duration |
| AI endpoints | 30s | OpenRouter calls |
| Verifier | 2s | Signature verification |
| Health check | 2s | /healthz endpoint |
Configuration:
# Request Timeouts
REQUEST_TIMEOUT_SECONDS=60
AI_REQUEST_TIMEOUT_SECONDS=30
VERIFIER_TIMEOUT_SECONDS=2
HEALTH_CHECK_TIMEOUT_SECONDS=2For production environments, we provide a containerized setup using Docker Compose. This orchestrates all three services in an isolated network.
-
Configure Environment
cp .env.example .env # Edit .env with your API keys and wallet configuration -
Build and Run
docker-compose up --build -d
-
Verify Status
docker-compose ps
-
Logs
docker-compose logs -f
For rapid development, use the unified stack command which runs services on the host machine.
-
Install Prerequisites
- Bun, Go 1.24+, Rust/Cargo
-
Run Stack
bun run stack
We maintain a comprehensive test suite covering all layers of the stack, from unit tests for individual microservices to full end-to-end (E2E) integration tests.
The E2E tests simulate a real client interaction:
- Sending a request to the Gateway.
- Receiving a
402 Payment Requiredchallenge. - Signing the challenge with an Ethereum wallet.
- Resubmitting the request with the signature.
- Verifying the successful AI response.
Run E2E Tests:
bun run test:e2ePrerequisites: Bun, Go, and Rust toolchains installed. This command uses run_e2e.sh to build and start the Go Gateway and Rust Verifier before executing tests.
If OPENROUTER_API_KEY is missing, the signature path will pass but the final AI call may return 500 after verification.
Gateway (Go): Tests the HTTP handlers and routing logic.
cd gateway
go test -vVerifier (Rust): Tests the cryptographic verification logic and EIP-712 implementation.
cd verifier
cargo test- Port already in use: ensure 3000/3001/3002 are free or export alternative ports in env and update client config.
- Missing OpenRouter key: E2E tests may pass signature validation but fail on AI response with 500.
- Network errors inside Docker: use service names (
gateway:3000,verifier:3002) instead of localhost.
- HTTP 402 Payment Required (MDN): https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/402
- RFC 7231 Section 6.5.2 (Payment Required): https://www.rfc-editor.org/rfc/rfc7231#section-6.5.2
- EIP-712 Typed Structured Data: https://eips.ethereum.org/EIPS/eip-712
We welcome contributions! Please read CONTRIBUTING.md for guidelines and check the GitHub Issues for open tasks.
This project is licensed under the MIT License.
MicroAI Paygate issues cryptographic receipts for every successful API request. These receipts are tamper-proof, independently verifiable, and stored for 24 hours by default.
Every successful request returns a receipt in the response body and X-402-Receipt header:
{
"result": "AI summary...",
"receipt": {
"receipt": {
"id": "rcpt_a1b2c3d4e5f6",
"version": "1.0",
"timestamp": "2026-01-06T10:30:00Z",
"payment": {
"payer": "0x742d35Cc...",
"recipient": "0x2cAF48b4...",
"amount": "0.001",
"token": "USDC",
"chainId": 8453,
"nonce": "9c311e31-..."
},
"service": {
"endpoint": "/api/ai/summarize",
"request_hash": "sha256:abc123...",
"response_hash": "sha256:def456..."
}
},
"signature": "0x1234...",
"server_public_key": "0xabcd..."
}
}Use the provided verification library to verify receipts client-side:
import { verifyReceipt, fetchReceipt } from './web/src/lib/verify-receipt';
// After receiving a response
const response = await fetch('/api/ai/summarize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-402-Signature': signature,
'X-402-Nonce': nonce,
},
body: JSON.stringify({ text: 'Your text here' }),
});
const data = await response.json();
// Verify the receipt signature
const isValid = await verifyReceipt(data.receipt);
console.log(`Receipt valid: ${isValid}`); // true
// Store receipt for future reference
localStorage.setItem(
`receipt_${data.receipt.receipt.id}`,
JSON.stringify(data.receipt)
);Retrieve stored receipts by ID:
# Fetch receipt
curl http://localhost:3000/api/receipts/rcpt_a1b2c3d4e5f6
# Response (200 OK)
{
"receipt": { ... },
"signature": "0x...",
"server_public_key": "0x...",
"status": "valid"
}
# Not found (404)
{
"error": "Receipt not found",
"message": "Receipt may have expired or never existed"
}sequenceDiagram
participant C as Client
participant G as Gateway
participant V as Verifier
participant AI as OpenRouter
C->>G: POST /api/ai/summarize + signature
G->>V: Verify signature
V-->>G: Valid ✓
G->>AI: Get AI response
AI-->>G: Response
Note over G: Generate Receipt
G->>G: Hash request/response
G->>G: Sign with server key
G-->>C: Response + Receipt
Note over C: Verify Receipt
C->>C: Check signature matches
C->>C: Validate server public key
C->>C: Store receipt
- ECDSA Signatures: Receipts signed using Keccak256 + secp256k1 (Ethereum-compatible)
- Tamper-Proof: Any modification invalidates the signature
- Content Hashes: Only SHA-256 hashes stored, not full request/response
- Rate Limited: Receipt lookup limited to 10 requests/minute (anonymous tier)
- Auto-Expiry: Receipts expire after 24 hours (configurable via
RECEIPT_TTL)
Add to .env:
# Required: Server's private key for signing receipts
SERVER_WALLET_PRIVATE_KEY=your_private_key_hex
# Optional: Receipt TTL in seconds (default: 86400 = 24 hours)
RECEIPT_TTL=86400Description Proxies a text summarization request to the AI provider, enforcing payment via the x402 protocol.
Request Headers
| Header | Type | Required | Description |
|---|---|---|---|
Content-Type |
string | Yes | Must be application/json |
X-402-Signature |
hex string | Yes | The EIP-712 signature signed by the user's wallet. |
X-402-Nonce |
uuid | Yes | The nonce received from the initial 402 response. |
Request Body
{
"text": "The content to be summarized..."
}Response Codes
| Status Code | Meaning | Payload Structure |
|---|---|---|
200 OK |
Success | { "result": "Summary text..." } |
402 Payment Required |
Payment Needed | { "paymentContext": { "nonce": "...", "amount": "0.001", ... } } |
403 Forbidden |
Invalid Signature | { "error": "Invalid Signature", "details": "..." } |
500 Internal Error |
Server Failure | { "error": "Service unavailable" } |
Description Internal endpoint used by the Gateway to verify signatures with the Rust service. Not exposed publicly.
Body
{
"context": { ... },
"signature": "0x..."
}