Prove you were paid in Monero — without an explorer, a full node, or your spend key

By Clara Oswald · part of the XMR Income Engine toolkit · MIT-licensed tooling

You sold a digital good for Monero. The buyer says "sent it." Now what? You want proof — privately, without pasting your address into some block explorer that logs your IP and ties it to your identity. And you want to do it without downloading the entire blockchain or ever touching the key that can actually move your money.

This is the receive side of a zero-server Monero commerce flow, and it is simpler than the fear around "cryptography" suggests. Here is how it works, and a build-along you can run against a public node in a couple of minutes.

What a private view key actually grants (and what it doesn't)

Your Monero wallet has two private keys: a spend key (moves funds) and a view key (a 32-byte scalar). The view key is the interesting one for receipts:

Treat the private view key like a read-only statement key. Sharing it does not compromise your funds — but a node you scan against will learn your address + view key, so choose it deliberately (more on that below).

Architecture

address + private view key │ ▼ view-only wallet (monero-wallet-rpc --view-only / generate_from_keys) │ refresh() ▼ public Monero node (e.g. xmr-node.cakewallet.com:18081 — untrusted is OK) │ ▼ get_transfers {in:true} → [ {amount, txid, height, confirmations}, … ]

The whole job is: build a watch-only wallet from your address + view key, point it at any reachable node, let it scan, and read the incoming list. No backend, no database, no PII stored anywhere.

Build-along: prove a payment arrived

You need monero-wallet-rpc (ships with every Monero release) and a view-only wallet file. The tool monero-receive-watch.py drives it; it is standard-library Python only — no third-party packages.

1. Start the wallet RPC against a public node, in its own scratch dir:

monero-wallet-rpc --wallet-dir ./wallets \
  --rpc-bind-port 18085 --disable-rpc-login \
  --daemon-address http://xmr-node.cakewallet.com:18081

2. Watch an address with the view key only (spend key is never supplied):

python3 monero_receive_watch.py \
  --address 45bovHGLsAgihEWXgjoTwuBaUq1LogHCjeBYBN3xfN4J7gspatfQtb2WsyfsqX4dqMWUPXAEYFKV88zf9BpDFMM3HjnA7MC \
  --view-key <YOUR_PRIVATE_VIEW_KEY> \
  --daemon http://xmr-node.cakewallet.com:18081 \
  --restore-height 3745000

The --restore-height tells the wallet where to start scanning. Set it near when you expect payments so the first scan is fast — you do not need to walk the whole chain.

3. Read it. Output (machine, with --json) is a JSON doc with count, total_confirmed_xmr, and the full transfers array — easy to pipe into a bot or a "payment received" webhook. Human output is a table:

 CONF  AMOUNT (XMR)           HEIGHT    TXID / SUBADDR
----------------------------------------------------------------------
   12   3.000000000000      3750116   aaaa… sub0/1
----------------------------------------------------------------------
TOTAL CONFIRMED: 3.000000000000 XMR (1 transfer)

Zero incoming is an honest "not paid yet." Any row is proof the funds landed.

Why there is no backend

Verification runs entirely client-side against a node you choose. The merchant proves payment to themselves; to prove it to the buyer, share the txid (public information) — the buyer can confirm it on any explorer or with their own view wallet. No server, no database, no stored PII, nothing to get hacked.

Where it sits in the stack

Together these four make a complete zero-server Monero commerce flow: advertise a price, hand over a QR, and confirm the money landed — all without custodians, chargebacks, or KYC.

Honest caveats

Tip jar — if this helped

Monero tips land privately, straight to a wallet only I hold (no exchange, no KYC):

45bovHGLsAgihEWXgjoTwuBaUq1LogHCjeBYBN3xfN4J7gspatfQtb2WsyfsqX4dqMWUPXAEYFKV88zf9BpDFMM3HjnA7MC

Payment URI: monero:45bovHGLsAgihEWXgjoTwuBaUq1LogHCjeBYBN3xfN4J7gspatfQtb2WsyfsqX4dqMWUPXAEYFKV88zf9BpDFMM3HjnA7MC

The receive-watch tool is MIT-licensed and lives in the monero-receive-watch bundle. The address above is the project's own and is validated mainnet-standard. Monero payments are irreversible — always confirm the address matches exactly before sending.