Dogecoin Payment Button
A self-hosted "Pay with Dogecoin" button for any web page.
A tiny Flask backend that issues per-order invoices against your own node (fresh address + QR code), plus a drop-in JavaScript snippet that shows payment status live. No processor, no percentage, no permission needed.
What you'll have when it works
- POST /invoice endpoint: amount in, {address, invoice_id} out
- GET /invoice/<id> status endpoint: pending / seen / paid
- Embeddable button + modal with address, QR code, and live status polling
- The lesson-7 watcher loop crediting invoices at 2 confirmations
The prompt
Paste this into Claude Code in an empty project folder. It's a full spec, not a wish – the constraints in it (integer koinu, idempotent crediting, testnet asserts) are the difference between a demo and something trustworthy. Read what comes back before you run it.
Build a self-hosted "Pay with Dogecoin" button system for TESTNET. Python 3 + Flask + SQLite backend, vanilla JS frontend snippet. Read the whole spec first.
CONTEXT
- Dogecoin Core testnet node on localhost, RPC port 44555, credentials from env vars DOGE_RPC_USER / DOGE_RPC_PASS. Talk JSON-RPC with the requests library (POST with basic auth; body {"jsonrpc":"1.0","id":"btn","method":M,"params":P}; raise if response "error" is non-null).
BACKEND (app.py)
- SQLite invoices table: id (uuid4 hex), address TEXT UNIQUE, due_koinu INTEGER, status TEXT ('pending'|'paid'), created_at, paid_at. Integer koinu everywhere (1 DOGE = 1e8 koinu).
- POST /api/invoice {"amount_doge": 50} -> creates invoice: RPC getnewaddress, insert row, return {id, address, amount_doge, doge_uri: "dogecoin:<address>?amount=<amount>"}. Reject amounts <= 0 or > 100000.
- GET /api/invoice/<id> -> {status, confirmations_required: 2, received_doge}. received comes from RPC getreceivedbyaddress(address, 2). Also expose received at 0 conf as "seen_doge" (getreceivedbyaddress with minconf 0) so the UI can show "payment detected, confirming...".
- Background thread every 10s: for pending invoices, if received(2 conf) >= due, flip to paid with UPDATE ... WHERE status='pending' (idempotent guard), stamp paid_at. Invoices older than 60 minutes stop being checked (status stays pending; the UI shows expired).
- CORS: allow configurable origin for the two API routes only.
FRONTEND (button.js + demo.html)
- A <script> snippet a site owner pastes with data-amount and data-api attributes. Renders a gold "Pay with Dogecoin" button. On click: calls POST /api/invoice, opens a small modal (no framework) showing the amount, the address (click to copy), a QR code of the doge_uri (generate the QR client-side - implement or inline a tiny QR encoder, no external CDN), and live status text driven by polling GET /api/invoice/<id> every 5s: waiting -> detected (0-conf) -> PAID (green). Stop polling at paid or after 60 min (show expired).
- demo.html shows the button wired to localhost for testing.
DELIVERABLES
app.py, static/button.js, demo.html, requirements.txt, README (setup, dogecoin.conf, how to test with a faucet, and a clearly marked section on what must change before mainnet). On startup assert RPC getblockchaininfo chain == "test". Then walk me through one full test payment.
The walkthrough
- Have the testnet node from Track 1 running and funded via a faucet.
- Scaffold with the prompt; run app.py and open demo.html.
- Click the button, pay the shown address from your node: dogecoin-cli sendtoaddress ADDR AMT.
- Watch the modal go waiting -> detected -> PAID as confirmations land.
- Read app.py's watcher and find the idempotency guard - it's the load-bearing line.
Before this touches real DOGE
- Before mainnet: HTTPS in front of Flask (reverse proxy), rate-limit invoice creation, and set real confirmation tiers per price.
- Decide your under/overpayment policy from lesson 7 and implement it explicitly.
- If you'd rather run one service than write one: this exact job is GigaWallet's purpose - see that lesson.
The lessons behind this kit: Detect a payment · Transactions, UTXOs & fees · Taking Dogecoin payments to production