"""Author RAG — Billing webhook routes (R-173).""" import json from fastapi import APIRouter, Header, HTTPException, Request from sqlalchemy.ext.asyncio import AsyncSession from app.config import get_settings from app.dependencies import get_db from app.services.billing_service import BillingService, verify_stripe_signature from fastapi import Depends router = APIRouter() cfg = get_settings() @router.post("/billing/stripe/webhook", tags=["Billing"]) async def stripe_webhook( request: Request, stripe_signature: str = Header(default="", alias="Stripe-Signature"), db: AsyncSession = Depends(get_db), ): """Handle Stripe checkout.session.completed events.""" secret = getattr(cfg, "STRIPE_WEBHOOK_SECRET", None) or "" body = await request.body() if secret and not verify_stripe_signature(body, stripe_signature, secret): raise HTTPException(400, "Invalid webhook signature") event = json.loads(body) if event.get("type") != "checkout.session.completed": return {"received": True, "handled": False} session = event.get("data", {}).get("object", {}) email = (session.get("customer_details") or {}).get("email") or session.get("customer_email") plan = (session.get("metadata") or {}).get("plan", "monthly") if not email: raise HTTPException(400, "Missing customer email") result = await BillingService(db).grant_from_checkout( customer_email=email, plan=plan, external_id=session.get("id", ""), ) return {"received": True, "handled": True, **result}