# app/modules/billing/routes/api/store_addons.py """ Store add-on management endpoints. Provides: - List available add-ons - Get store's purchased add-ons - Purchase add-on - Cancel add-on All routes require module access control for the 'billing' module. """ import logging from fastapi import APIRouter, Depends, Query from pydantic import BaseModel from sqlalchemy.orm import Session from app.api.deps import get_current_store_api, require_module_access from app.core.config import settings from app.core.database import get_db from app.modules.billing.services import billing_service from app.modules.enums import FrontendType from app.modules.tenancy.schemas.auth import UserContext store_addons_router = APIRouter( prefix="/addons", dependencies=[Depends(require_module_access("billing", FrontendType.STORE))], ) logger = logging.getLogger(__name__) # ============================================================================ # Schemas # ============================================================================ class AddOnResponse(BaseModel): """Add-on product information.""" id: int code: str name: str description: str | None = None category: str price_cents: int billing_period: str quantity_unit: str | None = None quantity_value: int | None = None class StoreAddOnResponse(BaseModel): """Store's purchased add-on.""" id: int addon_code: str addon_name: str status: str domain_name: str | None = None quantity: int period_start: str | None = None period_end: str | None = None class AddOnPurchaseRequest(BaseModel): """Request to purchase an add-on.""" addon_code: str domain_name: str | None = None # For domain add-ons quantity: int = 1 class AddOnCancelResponse(BaseModel): """Response for add-on cancellation.""" message: str addon_code: str # ============================================================================ # Endpoints # ============================================================================ @store_addons_router.get("", response_model=list[AddOnResponse]) def get_available_addons( category: str | None = Query(None, description="Filter by category"), current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Get available add-on products.""" addons = billing_service.get_available_addons(db, category=category) return [ AddOnResponse( id=addon.id, code=addon.code, name=addon.name, description=addon.description, category=addon.category, price_cents=addon.price_cents, billing_period=addon.billing_period, quantity_unit=addon.quantity_unit, quantity_value=addon.quantity_value, ) for addon in addons ] @store_addons_router.get("/my-addons", response_model=list[StoreAddOnResponse]) def get_store_addons( current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Get store's purchased add-ons.""" store_id = current_user.token_store_id store_addons = billing_service.get_store_addons(db, store_id) return [ StoreAddOnResponse( id=va.id, addon_code=va.addon_product.code, addon_name=va.addon_product.name, status=va.status, domain_name=va.domain_name, quantity=va.quantity, period_start=va.period_start.isoformat() if va.period_start else None, period_end=va.period_end.isoformat() if va.period_end else None, ) for va in store_addons ] @store_addons_router.post("/purchase") def purchase_addon( request: AddOnPurchaseRequest, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Purchase an add-on product.""" store_id = current_user.token_store_id store = billing_service.get_store(db, store_id) # Build URLs base_url = settings.app_base_url.rstrip("/") success_url = f"{base_url}/store/{store.store_code}/billing?addon_success=true" cancel_url = f"{base_url}/store/{store.store_code}/billing?addon_cancelled=true" result = billing_service.purchase_addon( db=db, store_id=store_id, addon_code=request.addon_code, domain_name=request.domain_name, quantity=request.quantity, success_url=success_url, cancel_url=cancel_url, ) db.commit() return result @store_addons_router.delete("/{addon_id}", response_model=AddOnCancelResponse) def cancel_addon( addon_id: int, current_user: UserContext = Depends(get_current_store_api), db: Session = Depends(get_db), ): """Cancel a purchased add-on.""" store_id = current_user.token_store_id result = billing_service.cancel_addon(db, store_id, addon_id) db.commit() return AddOnCancelResponse( message=result["message"], addon_code=result["addon_code"], )