A (BOB) Dobbscoin shop: vanilla PHP + SQLite, payments watched on your own node, USD prices at the live wBOB rate. Fork of xmr-cart. https://cart.dobbscoin.info
  • PHP 67.2%
  • JavaScript 23.7%
  • CSS 9.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-08 23:05:28 +00:00
admin rpc mode: keypool-low alert (encrypted node wallets) + keypool count on the dashboard 2026-10-08 23:05:28 +00:00
data Initial import of xmr-cart 2026-07-23 02:45:55 +00:00
lib rpc mode: keypool-low alert (encrypted node wallets) + keypool count on the dashboard 2026-10-08 23:05:28 +00:00
public dobbs-cart: port xmr-cart to Dobbscoin (BOB) 2026-10-08 22:35:53 +00:00
worker rpc mode: keypool-low alert (encrypted node wallets) + keypool count on the dashboard 2026-10-08 23:05:28 +00:00
.gitignore Initial import of xmr-cart 2026-07-23 02:45:55 +00:00
config.example.php rpc mode: keypool-low alert (encrypted node wallets) + keypool count on the dashboard 2026-10-08 23:05:28 +00:00
install.php dobbs-cart: port xmr-cart to Dobbscoin (BOB) 2026-10-08 22:35:53 +00:00
install.sh dobbs-cart: port xmr-cart to Dobbscoin (BOB) 2026-10-08 22:35:53 +00:00
LICENSE dobbs-cart: port xmr-cart to Dobbscoin (BOB) 2026-10-08 22:35:53 +00:00
LICENSE-THIRD-PARTY.md dobbs-cart: port xmr-cart to Dobbscoin (BOB) 2026-10-08 22:35:53 +00:00
README.md dobbs-cart: port xmr-cart to Dobbscoin (BOB) 2026-10-08 22:35:53 +00:00

dobbs-cart

A small (BOB) Dobbscoin shop for people who don't want WordPress underneath them. Vanilla PHP, SQLite, no framework. Payments are read from your own Dobbscoin node, prices are set in USD and converted at the live wBOB rate, and the store never needs a private key of its own.

A fork of xmr-cart, the Monero version: same storefront, cart, admin and notifications, with the payment and pricing layers rebuilt for a Bitcoin-style chain.

What it does

  • Catalog in batches: product photos with a gallery, share links, per-item #anchors, and a "Needs a shipping address" switch for digital items.
  • Cart: several items, one order, one payment. Stock is reserved atomically, so a limited run can't oversell.
  • One address per order. The buyer gets a payment page with a dobbscoin: QR code, a countdown, and a live progress bar that moves as confirmations arrive.
  • Payments seen at once: a payment shows up while still in the mempool, so a slow or stalled chain never expires an order that has been paid. It counts as paid at min_confirmations (6 by default, ~12 minutes).
  • Refunds tracked: checkout requires the buyer's (BOB) return address (masked, with a show toggle). Over- and underpayments show Refund due in the admin and email you.
  • Notifications: new order, paid, refund due, node down / back, price feed down / back, low stock / sold out to you; a payment receipt and a shipped email (with tracking links) to the buyer. Each once per event.
  • Privacy: buyer details are erased 30 days after shipping (7 after an order dies), photos are re-encoded (no EXIF/GPS), and the owner emails never carry buyer details.

Requirements

  • PHP 8.0+ with pdo_sqlite, curl, gd, gmp, mbstring
  • A Dobbscoin full node you run (dobbscoind) with server=1 and its wallet enabled. The store trusts it to report payments: a node you don't control could report a payment that never happened.
  • nginx or Apache that can serve PHP, and cron or a systemd timer

Install

git clone https://git.subgenius.finance/SubGeniusFinance/dobbs-cart.git /var/www/dobbs-cart
cd /var/www/dobbs-cart
sudo ./install.sh                   # or: sudo php install.php

The installer checks PHP, then asks for:

  1. Store name and public URL.
  2. Admin email, with [x] send all notifications here (or split new/paid orders, refunds, outages and stock to their own addresses), the From address, and whether buyers get a receipt and a shipping email.
  3. Your node: RPC URL, user and password. It calls the node and shows the block height, peers and whether the wallet is enabled.
  4. Where order addresses come from (see below).
  5. The price feed, which it fetches and shows the current rate from, or a fixed rate.

It generates cookie_secret and setup_key, writes config.php (640), makes data/ and the upload dirs writable by the web server, and prints the nginx block, the cron lines and your setup key. Open /admin/, enter the setup key and a passphrase, add a batch and a product, mark the batch live.

Node settings

dobbscoin.conf on the node needs at least:

server=1
rpcuser=…
rpcpassword=…           # long and random
rpcbind=127.0.0.1       # keep RPC off the internet
rpcallowip=127.0.0.1

Leave the wallet enabled (no disablewallet=1). Keep the node and the store on the same box, or reach the node over a private network, never over the public internet.

Payment addresses: two modes

Chosen at install (address_mode in config.php):

rpc (default) pool
Where addresses come from The node's wallet makes a fresh one per order (getnewaddress, labelled dobbs-cart #N) Addresses you generate in your own wallet and paste into the admin's Addresses tab
Keys on the server Yes: the node wallet holds them. Back it up and sweep it like any hot wallet None. Each address is imported watch-only; payments land in your wallet
Setup Nothing Keep the pool topped up; the admin warns below pool_low_warning (20) and checkout stops at 0

Either way the store reads payments from the node's wallet view of that address (listtransactions, watch-only included), so it sees a payment in the mempool and counts confirmations per block. A pool address must be fresh and used nowhere else: anything sent to it counts toward its order.

⚠ (BOB) addresses use the same format as Bitcoin addresses (they start with 1 or 3), so a BTC address passes as a valid (BOB) return address. The store can't tell them apart.

Pricing: the wBOB rate

Prices are set in USD. At checkout the total is converted to (BOB) at the live wBOB price on Gnosis and locked for the quote window (order_ttl_minutes, 60).

The default feed, https://subgenius.finance/pools.json, lists the wBOB pools; the store uses the deepest one (by TVL) that isn't stale. Any feed that returns {"price_usd": 0.0123} works too (price_feed_url).

wBOB liquidity is thin, so one trade can move the price. The store protects itself:

  • a feed older than price_feed_max_age (1 h) counts as no price;
  • a new price under half or over double the last good one (under 6 h old) is rejected;
  • with no trustworthy price, checkout pauses (and emails you) rather than mispricing.

Prefer to set the rate yourself? manual_coin_rate (USD per 1 BOB) overrides the feed.

Running the workers

* * * * * www-data php /var/www/dobbs-cart/worker/poll.php >> /var/log/dobbs-cart.log 2>&1
17 3 * * * www-data php /var/www/dobbs-cart/worker/purge_pii.php >> /var/log/dobbs-cart-pii.log 2>&1

poll.php checks every open order each minute and sends the node / price health alerts. Both exit quietly until config.php exists. No cron? Use a systemd timer that runs the same commands.

Demo mode

With no node_rpc_url, the store runs on fake addresses and a fake chain so you can try the whole flow: place an order, then use Simulate payment in the admin.

Resetting the admin passphrase

sqlite3 data/store.sqlite "DELETE FROM kv WHERE k='admin_pass_hash';"

The next /admin/ visit brings the first-run page back (it asks for setup_key).

Security posture

  • The admin is passphrase-gated (bcrypt) and rate-limit friendly; a passphrase change ends every session. The store refuses to run with a weak cookie_secret, and first-run setup needs the setup_key from config.php.
  • In pool mode the server holds no keys at all; in rpc mode the node wallet does.
  • Buyer details are erased on a schedule; owner emails never include them.
  • config.php (it holds the node RPC password) is git-ignored and written 640.

Hardening the admin

For a shop taking real orders, add a second layer at the web server so the passphrase form is never offered to the public internet:

  • Bind admin/ to a VPN or Tailscale interface (a separate server block that listens only on that IP), or
  • localhost + SSH port-forward (listen 127.0.0.1:8089;, then ssh -L 8089:127.0.0.1:8089 host), or
  • HTTP Basic auth in front of /admin/.

If admin/ stays public, at least rate-limit /admin/login.php (nginx limit_req, a few per minute).

Credit

Fork of xmr-cart (MIT). The QR code generator is Kazuhiko Arase's (MIT); see LICENSE-THIRD-PARTY.md.

License

MIT. See LICENSE.