DEV Community

nanoempireai
nanoempireai

Posted on

HTTP 402 Implementation Guide: Complete Technical Reference

HTTP 402 Implementation Guide: Complete Technical Reference

Everything you need to implement HTTP 402 Payment Required in your API. Complete with code, flow diagrams, and error handling.

The HTTP 402 Flow

Client → GET /api/data
Server → 402 Payment Required
  {
    "price_usd": 0.05,
    "currency": "USDC",
    "network": "base",
    "wallet": "0x...",
    "nonce": "abc123",
    "expires_at": "2026-08-31T12:00:00Z"
  }
Client → Signs EIP-3009 permit (gasless)
Client → Retries with receipt header
X-402-Receipt: <signed_receipt>
Server → Verifies receipt → 200 OK
Enter fullscreen mode Exit fullscreen mode

Server Implementation (FastAPI)

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
import secrets
from datetime import datetime, timedelta, timezone

app = FastAPI()

# In-memory challenge store (use Redis in production)
_active_challenges = {}

TREASURY_WALLET = "0xYourUSDCWalletOnBase"
PRICE_USD = 0.05

@app.middleware("http")
async def x402_middleware(request: Request, call_next):
    # Skip free paths
    if request.url.path.startswith("/.well-known/") or \
       request.url.path.startswith("/health") or \
       request.url.path.startswith("/openapi"):
        return await call_next(request)

    receipt = request.headers.get("X-402-Receipt")
    if receipt:
        if verify_receipt(receipt):
            return await call_next(request)
        else:
            return JSONResponse(
                status_code=402,
                content={"error": "Invalid payment receipt"},
                headers={"X-402-Challenge": "true"}
            )

    # Issue challenge
    nonce = secrets.token_urlsafe(16)
    expires_at = datetime.now(timezone.utc) + timedelta(minutes=10)
    _active_challenges[nonce] = {"expires": expires_at, "path": request.url.path}

    return JSONResponse(
        status_code=402,
        content={
            "error": "Payment Required",
            "price_usd": PRICE_USD,
            "currency": "USDC",
            "network": "base",
            "wallet": TREASURY_WALLET,
            "nonce": nonce,
            "expires_at": expires_at.isoformat()
        },
        headers={"X-402-Challenge": "true"}
    )

def verify_receipt(receipt_header: str) -> bool:
    """Verify x402 receipt. Use nano-empire-tollbooth in production."""
    try:
        from nano_empire_tollbooth import verify_receipt
        return verify_receipt(receipt_header)
    except ImportError:
        # Fallback: basic validation
        return len(receipt_header) > 100 and "receipt" in receipt_header.lower()
Enter fullscreen mode Exit fullscreen mode

Client Implementation (Agent Side)

import httpx
import json

async def call_with_payment(url: str, payload: dict):
    async with httpx.AsyncClient() as client:
        # First attempt
        response = await client.post(url, json=payload)

        if response.status_code == 200:
            return response.json()

        if response.status_code == 402:
            challenge = response.json()

            # Sign EIP-3009 permit (gasless)
            receipt = sign_eip3009_permit(
                wallet=challenge["wallet"],
                amount=challenge["price_usd"],
                nonce=challenge["nonce"]
            )

            # Retry with receipt
            headers = {"X-402-Receipt": receipt}
            response = await client.post(url, json=payload, headers=headers)
            return response.json()

        raise Exception(f"Unexpected status: {response.status_code}")
Enter fullscreen mode Exit fullscreen mode

Receipt Verification (Production)

Use the SDK for production-grade verification:

pip install nano-empire-tollbooth
Enter fullscreen mode Exit fullscreen mode
from nano_empire_tollbooth import verify_receipt

@app.middleware("http")
async def x402_middleware(request: Request, call_next):
    receipt = request.headers.get("X-402-Receipt")
    if receipt and verify_receipt(receipt):
        return await call_next(request)
    # ... issue challenge
Enter fullscreen mode Exit fullscreen mode

Free Tier Quota

from collections import defaultdict
from datetime import datetime, timezone

_quota = defaultdict(int)
QUOTA_LIMIT = 5

def check_quota(client_ip: str) -> bool:
    today = datetime.now(timezone.utc).date().isoformat()
    key = f"{client_ip}:{today}"
    if _quota[key] >= 5:
        return False
    _quota[key] += 1
    return True
Enter fullscreen mode Exit fullscreen mode

Error Handling

Code Scenario Response
402 No receipt / invalid receipt Challenge issued
429 Free quota exceeded Upgrade prompt
400 Invalid receipt format Error details
500 Verification service down Retry later

Testing

# Free tier test (first 5 calls)
curl -X POST http://localhost:8000/api/endpoint \
  -H "Content-Type: application/json" \
  -d '{"data": "test"}'

# Should return 200 OK for first 5 calls
# 6th call returns 402 with challenge
Enter fullscreen mode Exit fullscreen mode

Production Checklist

  • [ ] Redis for quota tracking
  • [ ] Treasury wallet configured
  • [ ] Domain verified for Resend emails
  • [ ] Proof chain endpoint deployed
  • [ ] llms.txt / agents.json deployed
  • [ ] Submitted to Smithery & Glama
  • [ ] Monitoring + alerts configured

SDK: pip install nano-empire-tollbooth
Live proof: https://api.nanoempireai.com/proof/summary

Top comments (0)