{"slug": "verifying-0-05-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no", "title": "Verifying $0.05 USDC Payments On-Chain in 40 Lines of Python — No Stripe, No SDK, No KYC", "summary": "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.", "body_md": "Last week I wrote about [the French voiceover API that only accepts payment from robots](https://dev.to/marcuschen-dev/i-built-a-french-ai-voiceover-api-that-only-accepts-payment-from-robots-435k). 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?**\n\nThe answer: one Python function, ~40 lines, stdlib only. Here's the real production code.\n\nMy 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.\n\n``` python\nimport json, os, urllib.request\n\nWALLET_BASE = \"0x3f97...D074\"                       # where I receive\nUSDC_BASE   = \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\"  # USDC on Base\nBASE_RPC    = \"https://mainnet.base.org\"\nTRANSFER_TOPIC = \"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\"\n\ndef rpc(method, params):\n    req = urllib.request.Request(\n        BASE_RPC,\n        data=json.dumps({\"jsonrpc\": \"2.0\", \"id\": 1,\n                         \"method\": method, \"params\": params}).encode(),\n        headers={\"Content-Type\": \"application/json\"})\n    with urllib.request.urlopen(req, timeout=30) as r:\n        return json.load(r).get(\"result\")\n\ndef verify_payment(tx_hash, min_usdc):\n    # 1. format sanity\n    if not tx_hash.startswith(\"0x\") or len(tx_hash) != 66:\n        return False, \"bad hash format\"\n    # 2. anti-replay: one hash = one delivery\n    if tx_hash.lower() in load_used_txs():\n        return False, \"tx already used (replay)\"\n    # 3. fetch the receipt\n    receipt = rpc(\"eth_getTransactionReceipt\", [tx_hash])\n    if not receipt:\n        return False, \"tx not found on Base\"\n    if receipt.get(\"status\") != \"0x1\":\n        return False, \"tx failed on-chain\"\n    # 4. scan logs for a USDC Transfer TO my wallet\n    want_to = WALLET_BASE.lower().replace(\"0x\", \"\")\n    for log in receipt.get(\"logs\", []):\n        if log.get(\"address\", \"\").lower() != USDC_BASE.lower():\n            continue                          # not the USDC contract\n        topics = log.get(\"topics\", [])\n        if len(topics) != 3 or topics[0].lower() != TRANSFER_TOPIC:\n            continue                          # not a Transfer event\n        to     = topics[2][-40:].lower()      # last 20 bytes of topic[2]\n        amount = int(log[\"data\"], 16) / 1e6   # USDC has 6 decimals\n        if to == want_to and amount >= min_usdc:\n            mark_used(tx_hash)\n            return True, f\"{amount} USDC received\"\n    return False, \"no sufficient USDC transfer to me in this tx\"\n```\n\nThat's the whole payment gateway. No web3.py, no API key, no account anywhere.\n\n`Transfer`\n\nevent from a fake token contract. Only logs from the real USDC contract count.`Transfer(address,address,uint256)`\n\nhas exactly 3 topics (signature + from + to). The recipient is the last 20 bytes of `topics[2]`\n\n.`>=`\n\nlets a generous buyer overpay without getting rejected.`status == \"0x1\"`\n\nIf there's no `X-Payment-Proof`\n\nheader, 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:\n\n```\n{\n  \"error\": \"Payment Required\",\n  \"amount\": 0.05, \"currency\": \"USDC\", \"network\": \"BASE\",\n  \"address\": \"0x3f97...D074\",\n  \"proof\": \"send the Base tx hash in X-Payment-Proof\"\n}\n```\n\nAn 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.\n\nThe 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.\n\nLive endpoint (humans get the service card, agents get the 402 dance):\n\n```\ncurl http://187.77.111.249.sslip.io:8402/\ncurl -X POST http://187.77.111.249.sslip.io:8402/generate \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"product\":\"ivr\",\"text\":\"Bonjour et bienvenue\"}'\n# → 402 with payment instructions\n```\n\nHuman portfolio with free audio samples: [voixoff-fr.netlify.app](https://voixoff-fr.netlify.app)\n\n*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*", "url": "https://wpnews.pro/news/verifying-0-05-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no", "canonical_source": "https://dev.to/marcuschen-dev/verifying-005-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no-kyc-4f94", "published_at": "2026-08-30 18:23:34+00:00", "updated_at": "2026-08-30 18:52:59.678832+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "developer-tools"], "entities": ["USDC", "Base", "Python", "Stripe", "x402"], "alternates": {"html": "https://wpnews.pro/news/verifying-0-05-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no", "markdown": "https://wpnews.pro/news/verifying-0-05-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no.md", "text": "https://wpnews.pro/news/verifying-0-05-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no.txt", "jsonld": "https://wpnews.pro/news/verifying-0-05-usdc-payments-on-chain-in-40-lines-of-python-no-stripe-no-sdk-no.jsonld"}}