cd /news/artificial-intelligence/verifying-0-05-usdc-payments-on-chai… · home topics artificial-intelligence article
[ARTICLE · art-115944] src=dev.to ↗ pub= topic=artificial-intelligence verified=true sentiment=· neutral

Verifying $0.05 USDC Payments On-Chain in 40 Lines of Python — No Stripe, No SDK, No KYC

A developer built a payment verification system for a French neural TTS API that accepts micro-payments of $0.03–0.05 USDC on the Base network, using only Python's standard library and no payment processor or SDK. The system, based on the x402 pattern, verifies on-chain transactions by scanning logs for USDC transfers to the developer's wallet, enabling AI agents to pay and receive audio in two HTTP calls.

read3 min views2 publishedAug 30, 2026

Last week I wrote about the French voiceover API that only accepts payment from robots. Today: the part people actually asked me about — how do you verify a $0.05 payment on-chain with zero payment processor, zero SDK, and zero KYC?

The answer: one Python function, ~40 lines, stdlib only. Here's the real production code.

My endpoint sells French neural TTS voiceovers for $0.03–0.05 USDC. At that price, Stripe is a non-starter (their floor is ~$0.50 per charge) and any processor's KYC kills the "robots welcome" model. So payments go through the x402 pattern: client pays USDC on Base, sends me the transaction hash, I verify it myself against a public RPC before delivering.

import json, os, urllib.request

WALLET_BASE = "0x3f97...D074"                       # where I receive
USDC_BASE   = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"  # USDC on Base
BASE_RPC    = "https://mainnet.base.org"
TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

def rpc(method, params):
    req = urllib.request.Request(
        BASE_RPC,
        data=json.dumps({"jsonrpc": "2.0", "id": 1,
                         "method": method, "params": params}).encode(),
        headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.load(r).get("result")

def verify_payment(tx_hash, min_usdc):
    if not tx_hash.startswith("0x") or len(tx_hash) != 66:
        return False, "bad hash format"
    if tx_hash.lower() in load_used_txs():
        return False, "tx already used (replay)"
    receipt = rpc("eth_getTransactionReceipt", [tx_hash])
    if not receipt:
        return False, "tx not found on Base"
    if receipt.get("status") != "0x1":
        return False, "tx failed on-chain"
    want_to = WALLET_BASE.lower().replace("0x", "")
    for log in receipt.get("logs", []):
        if log.get("address", "").lower() != USDC_BASE.lower():
            continue                          # not the USDC contract
        topics = log.get("topics", [])
        if len(topics) != 3 or topics[0].lower() != TRANSFER_TOPIC:
            continue                          # not a Transfer event
        to     = topics[2][-40:].lower()      # last 20 bytes of topic[2]
        amount = int(log["data"], 16) / 1e6   # USDC has 6 decimals
        if to == want_to and amount >= min_usdc:
            mark_used(tx_hash)
            return True, f"{amount} USDC received"
    return False, "no sufficient USDC transfer to me in this tx"

That's the whole payment gateway. No web3.py, no API key, no account anywhere.

Transfer

event from a fake token contract. Only logs from the real USDC contract count.Transfer(address,address,uint256)

has exactly 3 topics (signature + from + to). The recipient is the last 20 bytes of topics[2]

.>=

lets a generous buyer overpay without getting rejected.status == "0x1"

If there's no X-Payment-Proof

header, the server answers with HTTP 402 (yes, the "Payment Required" status code that's been reserved since 1999 and almost never used) plus everything a machine needs to pay:

{
  "error": "Payment Required",
  "amount": 0.05, "currency": "USDC", "network": "BASE",
  "address": "0x3f97...D074",
  "proof": "send the Base tx hash in X-Payment-Proof"
}

An AI agent reading that response has all it needs: chain, token, amount, destination. Pay, retry with the hash, get the MP3. Total round-trip: two HTTP calls.

The obvious trade-off: this only works for buyers who already hold USDC on Base — which today means mostly other agents and crypto-natives. That's a feature for now, not a bug.

Live endpoint (humans get the service card, agents get the 402 dance):

curl http://187.77.111.249.sslip.io:8402/
curl -X POST http://187.77.111.249.sslip.io:8402/generate \
  -H 'Content-Type: application/json' \
  -d '{"product":"ivr","text":"Bonjour et bienvenue"}'

Human portfolio with free audio samples: voixoff-fr.netlify.app

Building in public, week 2. Scoreboard so far: 0 sales, 1 working payment rail, ~0 lines of payment-processor code. Previous posts: robot-only API · $0 avatar pipeline

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @usdc 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/verifying-0-05-usdc-…] indexed:0 read:3min 2026-08-30 ·