import logging from fastapi import APIRouter, Request, HTTPException, Depends from sqlalchemy.ext.asyncio import AsyncSession from app.core.admin_database import get_admin_db from app.core.config import settings from app.services.billing import BillingService logger = logging.getLogger(__name__) router = APIRouter(prefix="/webhooks", tags=["webhooks"]) @router.post("/stripe") async def stripe_webhook( request: Request, db: AsyncSession = Depends(get_admin_db), ): """Stripe webhook handler. Public endpoint; signature verification is the only gate. Idempotency via stripe_events table. Returns 200 even when Stripe is not configured — keeps the receiver permissive for local dev. """ if not settings.stripe_enabled or not settings.STRIPE_WEBHOOK_SECRET: return {"status": "ok", "message": "Stripe not configured, event ignored"} payload = await request.body() sig_header = request.headers.get("stripe-signature") if not sig_header: raise HTTPException(status_code=400, detail="Missing stripe-signature header") try: import stripe stripe.api_key = settings.STRIPE_SECRET_KEY event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_WEBHOOK_SECRET ) except Exception as e: logger.warning("stripe webhook bad signature: %s", e) raise HTTPException(status_code=400, detail="Invalid signature") applied = await BillingService.apply_subscription_event( db, event_id=event["id"], event_type=event["type"], payload={"data": event["data"]}, ) return {"status": "ok", "applied": applied}