import logging from datetime import datetime, timezone from fastapi import APIRouter, Depends, Request from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models import Subscription from app.schemas import SubscribeRequest, SubscribeResponse from app.services.signed_request import verify_signed_request router = APIRouter() logger = logging.getLogger(__name__) ACTION_SUBSCRIBE = "subscribe" @router.post("/subscribe", response_model=SubscribeResponse) async def subscribe(request: Request, db: AsyncSession = Depends(get_db)): """Activate a subscription. Optional `paper_mode` flag in the body lets new users try the system safely (no Hyperliquid call, simulated fills). Signed message body is the RAW dict so the canonical hash matches what the frontend signed — same pattern as set_user_settings. """ raw = await request.json() body = SubscribeRequest(**raw) wallet = body.wallet.lower().strip() # The signed body excludes the envelope fields. For backwards compatibility # with old clients that signed `body=None` (no paper_mode key), accept # either signature. paper_mode = bool(raw.get("paper_mode", False)) signed_body = {"paper_mode": paper_mode} if paper_mode else None verify_signed_request( action=ACTION_SUBSCRIBE, wallet=wallet, timestamp_ms=body.timestamp, signature=body.signature, body=signed_body, ) result = await db.execute( select(Subscription).where(Subscription.wallet_address == wallet) ) sub = result.scalar_one_or_none() now = datetime.now(timezone.utc).replace(tzinfo=None) if sub is None: sub = Subscription( wallet_address=wallet, active=True, subscribed_at=now, paper_mode=paper_mode, ) db.add(sub) else: sub.active = True if sub.subscribed_at is None: sub.subscribed_at = now # Re-subscribing is allowed to change paper mode (e.g. user wants to # promote from paper to live). Otherwise leave existing flag alone. if paper_mode != sub.paper_mode: sub.paper_mode = paper_mode await db.commit() logger.info("Subscription activated for %s (paper_mode=%s)", wallet, paper_mode) return SubscribeResponse(status="ok", wallet=wallet, paper_mode=paper_mode)