How We Built a Crypto Checkout That Verifies Payments On-Chain — with $0
We built a crypto checkout that accepts USDC, ETH, and WETH on Base, verifies every payment on-chain, and costs $0 in platform fees. This is the exact architecture, with code.
Why we built our own instead of using a processor
Every payment processor takes a cut. Stripe takes ~2.9% + $0.30. Gumroad takes 10% + $0.50. Paddle takes 5% + $0.50. On a $10 report, that's $0.60 to $1.50 gone — 6% to 15% of revenue.
On-chain verification costs nothing. You pay only gas (which the buyer covers), and the payment is cryptographically verified. No processor, no fee, no delay.
The entire checkout cost us $0 to build and $0 to operate. The only "fee" is Base gas, ~$0.02 per transfer, paid by the buyer.
The architecture
Three components:
- A wallet address — where buyers send payment
- A checkout page — shows the address, accepts the transaction hash
- A verification endpoint — checks the transaction on-chain
Step 1: The wallet
Any EVM wallet works. We use a plain keypair created with ethers.js — no smart contract, no multi-sig:
// Generate a wallet (ethers.js v6)
import { Wallet } from 'ethers';
const wallet = Wallet.createRandom();
console.log(wallet.address); // 0x5d08...0f23
Fund it on Base — the cheapest EVM L2, where gas is ~$0.02 per transfer.
Step 2: The checkout page
The page shows the wallet address (click-to-copy), lets the buyer pick their token (USDC/ETH/WETH), and submits a transaction hash. We also render a QR code using the EIP-681 payment URI so mobile wallets can pre-fill the address:
ethereum:0x5d08089e2F5f3C8bA653abDc143e60923c950f23@8453
The @8453 is the Base chain ID. Most wallets recognize this and pre-fill the network.
Step 3: On-chain verification
This is the important part. When the buyer submits a transaction hash, we query the Base RPC to verify:
// For native ETH: check the transaction's value and recipient
const tx = await fetch('https://mainnet.base.org', {
method: 'POST',
body: JSON.stringify({
jsonrpc: '2.0', id: 1,
method: 'eth_getTransactionByHash',
params: [txHash]
})
}).then(r => r.json());
// Verify: transaction sent to OUR wallet, with value > 0
if (tx.result.to === OUR_WALLET && parseInt(tx.result.value, 16) > 0) {
// Payment verified
}
For ERC-20 tokens (USDC, WETH), we check the Transfer event logs instead:
// For ERC-20: check the Transfer event in the receipt
const receipt = await fetch('https://mainnet.base.org', {
method: 'POST',
body: JSON.stringify({
jsonrpc: '2.0', id: 1,
method: 'eth_getTransactionReceipt',
params: [txHash]
})
}).then(r => r.json());
// Transfer event topic: keccak256("Transfer(address,address,uint256)")
const transferTopic = '0xddf252ad...';
const ourWalletPadded = '0x0000...' + OUR_WALLET.slice(2);
const verified = receipt.result.logs.some(log =>
log.topics[0] === transferTopic &&
log.topics[2].toLowerCase() === ourWalletPadded.toLowerCase()
);
The 10% discount logic
Because we pay $0 in fees, we pass savings to buyers: 10% off for crypto. A $10 report costs $9 in crypto. A $299/mo subscription costs $269/mo. The buyer saves, and we still keep more than we would with a 10% processor.
What this means for you
If you're a developer selling anything digital, this pattern works. You don't need Stripe, Gumroad, or Paddle. A wallet, a page, and 50 lines of verification code is the entire system.
And if you want to see it working, our store uses exactly this. Try the checkout — the code is live, not theoretical.
Need more detailed implementation? That's what our NEAR Price Tracking Bot report and How AI Agents Use Tools cover — real code, real architecture, field-tested.