Files
resolutionflow/backend/app/api/endpoints/webhooks.py

50 lines
1.6 KiB
Python

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}