x402 Protocol — Episode 2: From Zero to First On-Chain Payment in 8 Hours
The pivot was decided. Korean crypto data on x402. Now I actually had to build the thing.
Here's what I started with: an Oracle ARM server in Tokyo (free tier — 4 CPUs, 24GB RAM, $0/month), a domain I already owned, and about a day to get something working. No budget. No team. Just Claude and a lot of terminal windows.
What This Post Covers
The entire build process for KR Crypto Intelligence — from checking whether Korean exchange APIs even work from a Tokyo server, to Cloudflare SSL setup, to FastAPI with x402 payment middleware, to seeing $0.001 USDC land in my wallet from the first on-chain payment. Eight hours, start to finish. Everything ran on free infrastructure.
Testing the Data Sources First
Before writing any code, I needed to know one thing: would Upbit and Bithumb respond to requests from a server in Tokyo? Korean exchanges sometimes block non-Korean IPs. If that happened, the entire project was dead before it started.
All three responded. Upbit returned BTC at ₩101,449,000. Bithumb at ₩101,480,000. Binance at $66,832.09. No IP blocks. No authentication required.
That was the green light.
Cloudflare + Oracle Firewall
x402 buyer agents need HTTPS. My Oracle server doesn't have SSL. The fix: put Cloudflare in front — free plan, automatic SSL.
DNS setup: pointed api.printmoneylab.com to the Oracle server's IP with Cloudflare's proxy enabled (the orange cloud icon). The blog's main domain stayed on DNS-only mode pointing to Google's Blogspot servers. One domain, two purposes, no conflicts.
One setting that'll ruin your day if you get it wrong: SSL mode must be "Flexible", not "Full." Full mode means Cloudflare expects SSL on the origin server too. Mine doesn't have it. Full mode = instant 525 error on every request. Flexible means Cloudflare handles SSL on the front, talks plain HTTP to the origin. Took me 20 minutes to figure out why everything was timing out before I remembered this.
Then the Oracle firewall. Oracle's default security blocks everything except SSH. You need to open ports 80 and 443 in two places — the VCN Security List in Oracle Cloud Console, and iptables on the server itself.
Here's the trap I almost fell into again: Oracle's iptables has a REJECT rule near the bottom. If you add ACCEPT rules after it with -A (append), they go below the REJECT and get ignored. You need -I INPUT 5 (insert) to put them above it.
One more thing: Cloudflare's Flexible SSL only forwards to port 80 on the origin. I initially set my API to run on port 8402 (because, you know, x402). Didn't work. Changed to port 80, which requires root privileges, so the systemd service runs as root. Not elegant, but it works.
Building the API
FastAPI for the framework. Five paid endpoints, three free ones. The paid ones:
/api/v1/kimchi-premium — Compares Upbit KRW price vs Binance USDT price using the live USD/KRW exchange rate. Returns the premium percentage and direction.
/api/v1/kr-prices — Upbit and Bithumb prices in KRW with 24-hour volume and change.
/api/v1/fx-rate — Live USD/KRW exchange rate.
/api/v1/stablecoin-premium — USDT/USDC price on Upbit vs official exchange rate. Shows whether capital is flowing into or out of the Korean crypto market.
All at $0.001 USDC per call.
The code has a bunch of safety measures I won't bore you with in detail, but a few are worth mentioning because they caused bugs when I didn't have them:
Bithumb returns HTTP 200 even when something's wrong — the actual error code is buried inside the JSON body (status "5300" and similar). If you just check the HTTP status, you'll think everything's fine while serving garbage data. I check body["status"] != "0000" now.
The exchange rate API (exchangerate-api.com) occasionally fails. My fallback: estimate USD/KRW by comparing BTC's KRW price on Upbit with its USDT price on Binance. But that calculation itself needs an exchange rate, which creates a loop. I added an estimated_from_crypto flag that breaks the cycle. Sounds niche, but it saved the API from hanging during an actual outage.
And because Cloudflare proxies all requests, every client looks like the same IP. Rate limiting by IP was useless until I switched to reading the CF-Connecting-IP header, which contains the real client IP.
Adding x402 Payments
This was the part I'd been waiting for. And honestly? It was easier than I expected.
The middleware wraps around your existing FastAPI routes. You define which endpoints are paid, set the price, specify your wallet address, and point to a facilitator (I use xpay — they handle the gas fees):
I opened a browser and hit https://api.printmoneylab.com/api/v1/kimchi-premium. The screen showed "402 Payment Required" with a wallet selection popup. That was the moment it clicked — the API was live, paywalled, and waiting for its first customer.
I also set up a Telegram bot for monitoring. Every minute, it sends a summary: how many requests came in, which endpoints were hit, how many succeeded or failed. Filtered to only track /api/v1/ paths because without that filter, bot scanners hitting /.env and /wp-admin were triggering alerts every few seconds.
The First $0.001
Time to be my own first customer. I ran a buyer script on my MacBook using a wallet that had some USDC on Base:
Response:
BTC was trading at a 0.23% premium on Upbit versus Binance. Real data, real calculation, real API response.
I checked Basescan. There it was: $0.001 USDC, transferred from the buyer wallet to mine, on Base mainnet. The first on-chain payment for Korean crypto data on x402.
Server cost: $0. API cost: $0. Cloudflare: $0. Gas fees: $0 (xpay covers it). Revenue: $0.001.
Not life-changing money. But after spending months on PriceVerifier where every job earned $0.008 minus $5/month hosting, getting paid anything on infrastructure that costs nothing felt different. The math doesn't start from a hole anymore.
What's Next
The API works and payments flow. But right now nobody knows it exists. Next episode covers the distribution side — building an MCP server so Claude and ChatGPT can call this API as a tool, getting listed on awesome-x402, registering on MCP directories, and adding the stablecoin premium endpoint that came from a random market research session.
← Previous: Episode 1: Why I Pivoted to Korean Crypto Data Next: Episode 3: MCP Server and Ecosystem Registration →
More updates on the way. If you're working on something similar or found a smarter way to do it, drop it in the comments — the more we share, the faster we all move.
Disclaimer: This blog documents my personal learning journey. Nothing here is financial advice.
Comments
Post a Comment