Arag / app /api /billing_router.py
AuthorBot
Implement Enterprise Worth Roadmap (R-139–R-173).
df480e4
Raw
History Blame Contribute Delete
1.57 kB
"""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}