Commit Graph
28 Commits
Author SHA1 Message Date
jwinterm ad661ebad9 feat: expose settlement evidence in Greenfield API 2026-07-16 08:13:02 -04:00
jwinterm c9df923a2f analytics: config-driven cookieless Umami tag (NEXT_PUBLIC_UMAMI_*, GPC+DNT) 2026-06-12 13:50:08 -04:00
jwinterm 0aa19ca6bf feat(public): add GET /api/core/public/rate?currency=usd
Exposes the WOW→fiat spot rate the invoice path already uses
internally (Nonlogs WOW-BTC × Kraken BTC-USD, 60s cache). No auth.

Allows downstream services (Medusa storefront) to display a live
USD reference price for WOW-denominated products without hard-coding
a third-party rate provider on the storefront side.
2026-05-26 15:04:46 -04:00
jwinterm 083a51d3b7 fix(ui): widen DefaultQrLogoSection prop type to accept 'wownero'
Follow-up to the previous commit — page.tsx now passes
default_qr_logo of type "wownero" | "monero" | "none" | "custom" to
this component, but the prop was still typed as the narrower
"monero" | "none" | "custom" set. Next build failed with TS2322.
Widened initialLogo prop + the internal useState + buildInitialState
helper to match.
2026-05-18 12:39:54 -04:00
jwinterm 7cebe309aa fix(profile): accept 'wownero' for default_qr_logo (was xmrcheckout literal)
ProfileResponse schema's default_qr_logo literal was inherited from
xmrcheckout: Literal["monero", "none", "custom"]. The DB column
(models.py) defaults to "wownero" and main.py's migration backfills
NULL → ... but the backfill also wrote "monero", contradicting the
column default.

End result: new users got default_qr_logo="wownero" (from the
column default), but GET /api/core/profile blew up with a Pydantic
ResponseValidationError on serialization because "wownero" isn't in
the literal. UI showed "Unavailable" for both Store ID and Primary
Address because the entire profile response 500'd, leaving
profileData null on the dashboard. API key + webhook secret rendered
fine because those come from cookies, not profileData.

Fix:
- schemas.py: QrLogoMode now Literal["wownero", "monero", "none",
  "custom"] (keep "monero" for backward compat with any rows from
  before the migration ran)
- schemas.py: ProfileResponse.default_qr_logo default → "wownero"
- main.py: backfill UPDATE → 'wownero' (was 'monero'); migration
  column DEFAULT already correct as 'wownero'
- ui/dashboard/page.tsx: TS type for default_qr_logo widened to
  include "wownero"; fallback default → "wownero"
- ui/dashboard/actions.ts: DefaultQrLogoState.logo type widened;
  validation in updateDefaultQrLogo + initialMetadataPayload now
  accepts "wownero"

Suchshop didn't hit this because their wowcheckout user row was
created back when this UI path wasn't being used (they were
provisioned via direct SQL/API). The new shop.wowne.ro merchant
created via the UI today triggered the column default → exposed
the schema mismatch.
2026-05-18 12:35:55 -04:00
jwinterm 3a8d87e9c9 fix(ui): wowcheckout login-form was checking XMR address shape
isLikelyPrimaryAddress() required exactly 95 chars starting with "4"
— that's the Monero primary-address shape. Wownero primaries are 97
chars starting with "Wo". This was inherited from the xmrcheckout
fork and never updated.

Effect: pasting a valid Wownero address into the login form silently
failed isLikelyPrimaryAddress(), the validate API was never called,
no green VALID indicator rendered, canSubmit stayed false, the Sign
in button stayed greyed out. Suchshop sidestepped this because that
tenant was provisioned via direct SQL/API, not through this form.

Fix: 95 → 97, "4" → "Wo". One file, two lines.

The backend cryptographic validation in routes.py:
_validate_payment_address_and_view_key() was correct all along — it
checks the 2-byte Wownero varint prefix (byte 0 == 178). The UI just
wasn't asking it.
2026-05-18 12:07:01 -04:00
jwinterm f7a39c0bdc fix: re-apply Wownero netbyte validator (regression from 696b04e)
Commit 696b04e ported routes.py changes from upstream xmrcheckout
and silently reverted the Wownero-specific address validation
introduced in 20eff23. Result: every genuine Wownero address has
been rejected with "Primary address is invalid" since that port,
making /admin/merchants registration impossible for new merchants.

Re-applies the 2-byte varint prefix decoder + keccak checksum +
ed25519 view-key verification from 20eff23, and drops the now-unused
`from monero.address import Address, IntegratedAddress, SubAddress`
line so future upstream pulls do not silently re-introduce the
footgun (the import is the load-bearing tell that something has
re-pulled the upstream Monero-only code path).
2026-05-08 17:03:16 -04:00
jwinterm e0c349af52 fix: nginx — re-resolve upstream hostname via Docker DNS
Previously nginx resolved service names (api, ui) to IPs once at startup and cached them forever. When the api container is rebuilt (e.g. for a code fix) Docker assigns it a new IP and nginx keeps hitting the old dead one, returning 502 Bad Gateway on every request until nginx is manually restarted.

Actual impact tonight: after deploying the Wownero 10^11 decimals fix, wowcheckout-api-1 came up at 172.20.0.6 but nginx kept trying 172.20.0.9. Medusa's provider.authorizePayment call via the LAN URL 502'd. Medusa treated that as 'payment not authorized', refused to create the order, and the customer's WOW payment silently vanished even though the reconciler detected it correctly on the xmrcheckout side.

Use 'resolver 127.0.0.11 valid=10s' + 'set $api_upstream …' so nginx re-resolves via Docker's internal DNS every ~10s. Stale-IP window is now bounded to 10s max.
2026-04-19 16:08:53 -04:00
jwinterm f9159f5877 fix: Wownero atomic unit is 10^11, not 10^12
The codebase was forked from xmrcheckout and kept Monero's 10^12 atomic-unit constant everywhere. Wownero uses 11 decimal places, not 12.

Effect: _xmr_to_atomic(amount) produced a required_atomic value 10× larger than what wallet-rpc actually reports for the subaddress. Invoices flagged total_paid_atomic correctly (that comes from the daemon) but is_paid = total_paid >= required was always false, so the reconciler never flipped status pending → payment_detected, webhook never fired, Medusa never saw the payment.

Fix all four places: reconciler._xmr_to_atomic, btcpay_routes._xmr_to_atomic, btcpay_routes._atomic_to_xmr, btcpay_routes._format_xmr_fixed, routes.py invoice-status response. Extracted a WOW_ATOMIC_UNITS constant so this doesn't get lost again when we touch these files.
2026-04-19 15:51:45 -04:00
jwinterm 240c2b2b9f fix: include fiat amount in BTCPay webhook payload; quiet wallet-rpc spam
Mirror of xmrcheckout commit eb15c52. Same two bugs, same fix.

1. _build_payload now emits amount (fiat), currency, and amount_crypto so Medusa's capture workflow captures the right value instead of 0 (which leaves payments authorized forever).

2. get_status() no longer calls set_daemon without an open wallet (was flooding logs with RPC -13). _daemon_height() probe is sufficient for daemon reachability.

3. Silenced monero.backends.jsonrpc.wallet logger for expected transient close_wallet errors.
2026-04-18 16:31:44 -04:00
jwinterm eb0075f507 docs: add extra_hosts comment for LAN webhook delivery 2026-04-13 10:27:57 -04:00
jwinterm cc20773dda fix: auto-redirect on payment detection (0-conf), not just confirmation
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.
2026-04-13 10:03:53 -04:00
jwinterm 7f7f7f5b3e fix: use get_wow_rate instead of get_xmr_rate (copy error from upstream port) 2026-03-31 10:49:28 -04:00
jwinterm 696b04ec6d feat: port upstream system status, health monitoring, connectivity caching
Ports xmrcheckout upstream features (7 commits since 655d037):
- SystemStatus model for reconciler heartbeat tracking
- Monero connectivity caching in system status
- Health status endpoints in routes
- Hardened reconciler status updates with SQLAlchemyError handling
- Status schema additions

Adapted for Wownero: 3-decimal precision (0.001 WOW) in amount rounding.
2026-03-28 12:22:33 -04:00
jwinterm 11974276c4 fix: reconciler hardening, decimal formatting, webhook retries
- 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.

- Invoice amount rounding: fiat-converted amounts now use ROUND_UP with
  3 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).
2026-03-28 11:51:04 -04:00
jwinterm 2b8cb798b9 docs: add webhook and extra_hosts documentation 2026-03-18 13:18:27 -04:00
jwinterm 46e3f2f116 fix: correct API_BASE_URL default (was double-prefixing /api)
The default was http://nginx/api but all fetch calls already append
/api/... resulting in http://nginx/api/api/... (404). Changed to
http://nginx so the full path is http://nginx/api/admin/auth/verify.
2026-03-15 11:31:19 -04:00
jwinterm 00e04c74bb security: harden admin auth, reconciler locking, and connection pooling
- Use secrets.compare_digest for timing-safe admin key comparison
- Strip whitespace from ADMIN_API_KEY env var
- Add PostgreSQL advisory locks per-invoice in reconciler to prevent
  duplicate processing during deployments
- Add wallet-RPC retry with backoff (3 attempts: 0.5s, 1s, 2s)
- Raise MAX_SUBADDRESS_INDEX from 100 to 300
- Tune DB connection pool (pool_size=20, max_overflow=10, recycle=3600)
- Add 1-hour admin session timeout
- Regenerate UI lockfile
2026-03-15 10:49:55 -04:00
jwinterm 20eff23335 fix: use correct Wownero network bytes and 2-byte varint prefix for address validation
The monero Python library assumes 1-byte network prefixes (Monero format).
Wownero uses 2-byte varint prefixes (base58 prefix 4146 for primary, 12208
for subaddress), so Address() was rejecting valid Wownero addresses with
"Primary address is invalid".

Replace monero lib Address usage with native Wownero validation:
- Correct netbytes: 0xb2 (primary), 0xb0 (subaddress), 0xb3 (integrated)
- 2-byte varint prefix offset for key extraction (bytes 2-33, 34-65)
- 2-byte varint prefix in subaddress derivation encoding
2026-03-13 14:57:57 -04:00
jwinterm 2ccfee4caa Add admin panel docs to README, simplify nginx Dockerfile 2026-03-10 11:34:30 -04:00
jwinterm 3da9a21b83 Remove TLS from nginx — NPM handles TLS on LAN 2026-03-10 11:32:18 -04:00
jwinterm d85e26fbbd Add admin panel for merchant management
- 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
2026-03-10 11:25:02 -04:00
jwinterm a8fcde59eb Send signup requests to apps@such.software, fix default site name 2026-03-09 13:12:10 -04:00
jwinterm 2f09c7b94f Fix turnstile and resend package versions 2026-03-09 13:04:33 -04:00
jwinterm 341d83196b Add OPEN_REGISTRATION gate, signup request form, favicon 2026-03-09 13:02:30 -04:00
jwinterm 97b7aaa623 Add Such Software footer, pink/yellow theme, fix GitHub URLs 2026-03-09 11:54:40 -04:00
jwinterm cccbb4425c Update logo and README with pricing, fork info, port docs 2026-03-09 11:25:49 -04:00
jwinterm f8f9b4424c Fork xmrcheckout for Wownero (WOW)
Minimal changes from upstream xmrcheckout to support Wownero:
- Rate provider: Nonlogs WOW-BTC via Kraken BTC/USD
- Subaddress derivation: Wownero network byte overrides (53/63/54)
- Payment URI scheme: wownero:
- Dockerfile: Wownero binaries from Codeberg
- Display text: Monero→Wownero, XMR→WOW throughout UI
- Default daemon: 192.168.88.235:34568
- Added wownero-logo.svg

File and folder structure kept identical to upstream for easy sync.
2026-03-09 11:08:55 -04:00