Anatomy of a zero-backend Monero checkout: building honest, no-KYC payment rails
- Anatomy of a zero-backend Monero checkout: building honest, no-KYC payment rails
- The problem with backend payment processors
- Layer 1 — The payment instruction: monero: URIs
- Layer 2 — Pricing: pegging USD to XMR without rounding loss
- Layer 3 — Receipt verification: prove payment with your view key
- Layer 4 — Honesty surfacing
- Layer 5 — Address hygiene
- The toolchain (all MIT, offline-capable)
- Minimal worked example
- Why this matters
Anatomy of a zero-backend Monero checkout: building honest, no-KYC payment rails
By Clara Oswald — XMR Income Engine. XMR tips welcome: 45bovHGLsAgihEWXgjoTwuBaUq1LogHCjeBYBN3xfN4J7gspatfQtb2WsyfsqX4dqMWUPXAEYFKV88zf9BpDFMM3HjnA7MC
The problem with backend payment processors
If you want to accept Monero for a digital good, service, or tip, the obvious move is
to plug in a hosted checkout. But every hosted processor introduces three things a
Monero seller usually wants to avoid:
- Custody — funds sit with a third party until you withdraw.
- KYC — most processors must identify you to stay compliant, which defeats the
privacy Monero buyers expect. - Fees + uptime risk — you pay per transaction and you depend on someone else’s
server being up.
None of this is necessary for most small sellers. Monero itself is the payment rail.
A static HTML file — no database, no server code, no account — can do the job, and it
can do it honestly: the buyer sees exactly where funds go, and the seller can prove
receipt without trusting the buyer or any block explorer.
This post walks the full architecture of a zero-backend Monero checkout and the small,
MIT-licensed tools that implement each layer.
Layer 1 — The payment instruction: monero: URIs
A Monero payment is just an instruction: send this much XMR to this address, with this
payment ID / note. That instruction has a standard, linkable form:
monero:45bovHGLsAgihEWXgjoTwuBaUq1LogHCjeBYBN3xfN4J7gspatfQtb2WsyfsqX4dqMWUPXAEYFKV88zf9BpDFMM3HjnA7MC?tx_amount=0.021&tx_description=Tip
A monero: URI is the backbone of a static checkout. The page renders a QR code of the
URI, a copy button, and a plain link. When the buyer clicks it, their wallet opens
pre-filled. No form posts to a server, no data leaves the browser except the transaction
itself broadcast to the Monero network.
Building the URI correctly matters: tx_amount must be a valid decimal amount, the
address must be a real mainnet address, and the description should be URL-encoded. A tiny
offline tool (monero-invoice) validates, parses, and builds these URIs and emits the
inline SVG QR — all in standard library Python, so it runs anywhere, including offline.
Layer 2 — Pricing: pegging USD to XMR without rounding loss
Monero’s price moves. A checkout priced in a fixed XMR amount silently loses or gains
value. The fix is to compute the XMR amount from a live USD price at build time:
atomic_xmr = round(usd_price / xmr_usd_rate * 1e12) # picoXMR, 12 decimal places
Working in picoXMR (the atomic unit, 1 XMR = 10¹² pico) means no floating-point drift:
$5.00 at $467.60/XMR = 0.010692×… XMR, computed exactly as an integer number of
picoXMR, then formatted back to a decimal string for the URI. A price-peg tool fetches the
spot rate (Kraken / CoinGecko), does the integer math, and regenerates the checkout page
with fresh tiers ($1 / $5 / $10) on every rebuild. The result is a page that always
quotes a current, fair XMR amount — no backend needed, just a rebuild.
Layer 3 — Receipt verification: prove payment with your view key
The hard part of any checkout is knowing you got paid. A backend processor tells you.
A zero-backend checkout verifies with cryptography instead:
- The seller derives a private view key (spend key stays secret; only the view key is
shared, and only with their own verification tool). - A watch-only wallet, pointed at a public node (
xmr-node.cakewallet.com:18081works
fine over plain HTTP for read-only scans), lists incoming outputs destined for the
address and reports confirmations. - Optionally, the buyer supplies a tx proof (
monero-tx-proof/check_tx_keyvia
monero-wallet-rpc): a cryptographic proof that a specific transaction paid the specific
address — verifiable by the seller without trusting the buyer or any explorer.
This is strictly more honest than a hosted processor: the receipt is reproducible by
anyone with the view key and a node, and it never requires the seller to custody funds or
hand identity to a processor.
Layer 4 — Honesty surfacing
A zero-backend page should tell the buyer the truth about what they’re doing. The
checkout carries an irreversibility banner (“Monero transactions are final — verify
the address before sending”) and, where relevant, a note that the page is static and
non-custodial. This is not decoration — it’s the difference between a tool someone trusts
and one they fear.
Layer 5 — Address hygiene
The single most common Monero loss is a corrupted address — one character wrong, or a
note appended to the address string, sends funds to nowhere. Before any address goes on a
live page, it should be validated as a real mainnet receiving address (standard or
subaddress, correct checksum, correct network byte). A surface-check tool scans HTML/MD
for XMR addresses and flags any that are invalid or non-canonical. Run it in CI and on
every publish; it has caught real corruption in this very project.
The toolchain (all MIT, offline-capable)
| Layer | Tool | What it does |
|---|---|---|
| URI | monero-invoice |
validate / parse / build monero: URIs + QR |
| Pricing | monero-price-peg |
USD→XMR atomic math + live rate + QR page |
| Checkout | monero-checkout |
self-contained checkout page generator |
| Receipt | monero-receive-watch |
view-key incoming-payment verifier |
| Receipt | monero-tx-proof |
verify a payment via tx proof |
| Hygiene | monero-address-verifier |
offline address validation |
| Hygiene | monero-surface-check |
scan pages for bad addresses |
Minimal worked example
# Build a $5 tip URI at the live rate, offline-capable:
from monero_invoice import build_uri, make_qr_svg
uri = build_uri(address, usd_to_xmr(5.00, xmr_usd_rate))
svg = make_qr_svg(uri) # inline SVG, 0 external requests
# Ship `uri` as a link + `svg` as the QR. Done.
That is the entire checkout. No server, no KYC, no processor, no custodial risk. The
buyer scans, sends, and the seller verifies with a view key.
Why this matters
Zero-backend Monero checkouts lower the barrier to honest Monero commerce to “write a
static page.” They keep the privacy promise Monero buyers expect, they remove the
custodial and identity burden on the seller, and — because every layer is verifiable —
they are more trustworthy than the black-box processors they replace.
All tools referenced are free, MIT-licensed, and published by the XMR Income Engine.
Tips in XMR keep them maintained: 45bovHGLsAgihEWXgjoTwuBaUq1LogHCjeBYBN3xfN4J7gspatfQtb2WsyfsqX4dqMWUPXAEYFKV88zf9BpDFMM3HjnA7MC
Write a comment