Files
wowcheckout/api/app/main.py
T
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

266 lines
9.4 KiB
Python

import logging
import os
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from sqlalchemy import text
from .db import Base, engine
from .config import DONATIONS_ENABLED
from .btcpay_routes import router as btcpay_router
from .routes import router
app = FastAPI(title="wowcheckout.com API", version="0.1.0")
logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO").upper())
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
)
openapi_schema.get("paths", {}).pop("/api/core/donations", None)
if not DONATIONS_ENABLED:
openapi_schema.get("paths", {}).pop("/api/core/public/donation/{invoice_id}", None)
openapi_schema.get("paths", {}).pop("/api/core/auth/validate", None)
openapi_schema.get("paths", {}).pop("/api/core/profile", None)
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
@app.on_event("startup")
def startup():
lock_acquired = False
lock_id = 894221741
with engine.begin() as connection:
try:
connection.execute(text("SELECT pg_advisory_lock(:lock_id)"), {"lock_id": lock_id})
lock_acquired = True
except Exception:
lock_acquired = False
try:
Base.metadata.create_all(bind=connection)
connection.execute(
text("ALTER TABLE invoices ADD COLUMN IF NOT EXISTS user_id UUID")
)
connection.execute(
text("ALTER TABLE invoices ADD COLUMN IF NOT EXISTS wallet_address VARCHAR")
)
connection.execute(
text("ALTER TABLE invoices ADD COLUMN IF NOT EXISTS subaddress_index INTEGER")
)
connection.execute(
text(
"ALTER TABLE invoices "
"ADD COLUMN IF NOT EXISTS confirmations INTEGER DEFAULT 0"
)
)
connection.execute(
text(
"ALTER TABLE invoices "
"ADD COLUMN IF NOT EXISTS total_paid_atomic BIGINT"
)
)
connection.execute(
text(
"ALTER TABLE invoices "
"ADD COLUMN IF NOT EXISTS paid_after_expiry BOOLEAN DEFAULT false"
)
)
connection.execute(
text(
"ALTER TABLE invoices "
"ADD COLUMN IF NOT EXISTS paid_after_expiry_at TIMESTAMP WITH TIME ZONE"
)
)
connection.execute(
text(
"ALTER TABLE invoices "
"ADD COLUMN IF NOT EXISTS archived_at TIMESTAMP WITH TIME ZONE"
)
)
connection.execute(
text("UPDATE invoices SET confirmations = 0 WHERE confirmations IS NULL")
)
connection.execute(
text("CREATE INDEX IF NOT EXISTS ix_invoices_user_id ON invoices (user_id)")
)
connection.execute(
text(
"CREATE INDEX IF NOT EXISTS ix_invoices_wallet_address ON invoices (wallet_address)"
)
)
connection.execute(text("ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS user_id UUID"))
connection.execute(
text("ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS event_urls JSON")
)
connection.execute(
text("ALTER TABLE webhooks ALTER COLUMN url DROP NOT NULL")
)
connection.execute(
text("CREATE INDEX IF NOT EXISTS ix_webhooks_user_id ON webhooks (user_id)")
)
connection.execute(
text(
"CREATE INDEX IF NOT EXISTS ix_webhook_deliveries_user_id "
"ON webhook_deliveries (user_id)"
)
)
connection.execute(
text(
"CREATE INDEX IF NOT EXISTS ix_webhook_deliveries_webhook_id "
"ON webhook_deliveries (webhook_id)"
)
)
connection.execute(
text(
"ALTER TABLE webhook_deliveries "
"ADD COLUMN IF NOT EXISTS invoice_id UUID"
)
)
connection.execute(
text(
"ALTER TABLE webhook_deliveries "
"ADD COLUMN IF NOT EXISTS invoice_address VARCHAR"
)
)
connection.execute(
text(
"ALTER TABLE webhook_deliveries "
"ADD COLUMN IF NOT EXISTS invoice_subaddress_index INTEGER"
)
)
connection.execute(
text(
"ALTER TABLE webhook_deliveries "
"ADD COLUMN IF NOT EXISTS invoice_amount_xmr NUMERIC(18, 12)"
)
)
connection.execute(
text(
"ALTER TABLE webhook_deliveries "
"ADD COLUMN IF NOT EXISTS invoice_status VARCHAR"
)
)
connection.execute(
text(
"ALTER TABLE webhook_deliveries "
"ADD COLUMN IF NOT EXISTS payload JSON"
)
)
connection.execute(
text("ALTER TABLE users ADD COLUMN IF NOT EXISTS payment_address VARCHAR")
)
connection.execute(
text("ALTER TABLE users ADD COLUMN IF NOT EXISTS view_key_encrypted VARCHAR")
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS webhook_secret_encrypted VARCHAR"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS next_subaddress_index INTEGER DEFAULT 1"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS subaddress_start_index INTEGER DEFAULT 0"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS default_confirmation_target INTEGER DEFAULT 1"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ALTER COLUMN default_confirmation_target SET DEFAULT 1"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS default_qr_logo VARCHAR DEFAULT 'wownero'"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS default_qr_logo_data_url VARCHAR"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS btcpay_checkout_style VARCHAR DEFAULT 'btcpay_classic'"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ALTER COLUMN btcpay_checkout_style SET DEFAULT 'btcpay_classic'"
)
)
connection.execute(
text(
"ALTER TABLE users "
"ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()"
)
)
connection.execute(
text(
"UPDATE users SET btcpay_checkout_style = 'btcpay_classic' "
"WHERE btcpay_checkout_style IS NULL"
)
)
connection.execute(
text(
"UPDATE users "
"SET btcpay_checkout_style = 'btcpay_classic' "
"WHERE btcpay_checkout_style = 'standard' "
"AND NOT EXISTS ("
" SELECT 1 FROM profile_history ph "
" WHERE ph.user_id = users.id AND ph.field_name = 'btcpay_checkout_style'"
")"
)
)
connection.execute(
text(
"UPDATE users SET default_confirmation_target = 1 "
"WHERE default_confirmation_target IS NULL"
)
)
connection.execute(
text(
"UPDATE users SET default_qr_logo = 'wownero' "
"WHERE default_qr_logo IS NULL"
)
)
connection.execute(
text("CREATE INDEX IF NOT EXISTS ix_users_payment_address ON users (payment_address)")
)
finally:
if lock_acquired:
connection.execute(
text("SELECT pg_advisory_unlock(:lock_id)"), {"lock_id": lock_id}
)
app.include_router(router)
app.include_router(btcpay_router)