Mirror of wowcheckout e0c349a. Same bug would bite xmrcheckout after any api container rebuild — nginx caches IP at startup and never re-resolves, so a rebuild that gives the api container a new IP leaves nginx 502'ing until manually restarted. Use 'resolver 127.0.0.11 valid=10s' + variable-based proxy_pass so nginx re-resolves every ~10s via Docker's internal DNS.
Two fixes:
1. _build_payload now emits amount (fiat), currency, and amount_crypto. Downstream processors like Medusa need the amount to capture the right value. Without it, Medusa was capturing 0 and leaving payments stuck 'authorized' forever despite the InvoiceSettled/InvoicePaymentSettled webhooks firing correctly at 10+ confirmations.
2. get_status() called set_daemon RPC without first opening a wallet. set_daemon requires an open wallet, so on backends holding no wallet (the common idle case with our 3-backend hash-sharded setup) it errored with RPC -13 'No wallet file' every 5 seconds, flooding logs with a dozens-per-minute stream of raw RPC error dumps from the python-monero library logger. Dropped the set_daemon call — _daemon_height() already validates daemon reachability directly and doesn't need wallet-rpc.
3. Silenced monero.backends.jsonrpc.wallet library logger at CRITICAL so expected transient -13 errors from our _ensure_wallet_open close_wallet call stop polluting the stream.
The checkout UI only redirected when status was 'confirmed' (after all
N confirmations). Now also redirects on 'payment_detected' (0-conf in
mempool). This matches BTCPay's behavior for BTC/LTC where the customer
is redirected to the store immediately on detection.
Merges xmrcheckout/xmrcheckout main (7 commits since 655d037):
- Add Monero system status and reconciler heartbeat
- Surface Monero health status in UI (dashboard + invoice pages)
- Harden reconciler status heartbeat startup
- Avoid false Monero outage states in UI
- Cache Monero connectivity in system status
- Next.js security patch (15.5.9 → 15.5.14)
- Bump Monero Docker defaults to 0.18.4.6
Resolved conflicts in reconciler.py (kept our advisory lock + upstream
heartbeat), routes.py (kept both imports), README.md (kept both sections).
- Advisory lock: wrap per-invoice processing in try/finally so lock is
ALWAYS released, even on exception. Previously, any error between lock
acquisition and release would leak the lock, causing the invoice to be
silently skipped forever on subsequent cycles.
- Error logging: wallet RPC errors now logged at ERROR level (was DEBUG),
making silent reconciliation failures visible in production logs.
- Transfer protection: don't delete stored transfers when wallet-rpc
returns an empty response (transient RPC glitch). Only prune when we
actually got transfer data back.
- Wallet restore height: new wallets generated from keys now look back
~2 days (1440 blocks) instead of starting at current height. Prevents
missing payments that arrived before the wallet was first opened on a
given wallet-rpc instance.
- Decimal formatting: _format_xmr_fixed() now strips trailing zeros
instead of always showing 12 decimal places (0.033554999694 → 0.033555).
- Invoice amount rounding: fiat-converted amounts now use ROUND_UP with
5 decimal places (was ROUND_DOWN with 12), preventing undercharging.
- Webhook retries: BTCPay webhook delivery now retries up to 3 times
with 1s/2s backoff on 5xx or network errors. Timeout increased 5s→10s.
Failures logged at ERROR level (was WARNING/DEBUG).
- POST/GET/DELETE /api/admin/users endpoints
- Admin UI at /admin with login, user list, create/delete
- Auth via ADMIN_API_KEY env var (separate from tenant auth)
- Tenants remain scoped to their own store only