Files
bakery-ia/services/tenant/app/api/webhooks.py
2026-01-15 20:45:49 +01:00

98 lines
3.5 KiB
Python

"""
Webhook endpoints for handling payment provider events
These endpoints receive events from payment providers like Stripe
All event processing is handled by SubscriptionOrchestrationService
"""
import structlog
import stripe
from fastapi import APIRouter, Depends, HTTPException, status, Request
from sqlalchemy.ext.asyncio import AsyncSession
from app.services.subscription_orchestration_service import SubscriptionOrchestrationService
from app.core.config import settings
from app.core.database import get_db
logger = structlog.get_logger()
router = APIRouter()
def get_subscription_orchestration_service(
db: AsyncSession = Depends(get_db)
) -> SubscriptionOrchestrationService:
"""Dependency injection for SubscriptionOrchestrationService"""
try:
return SubscriptionOrchestrationService(db)
except Exception as e:
logger.error("Failed to create subscription orchestration service", error=str(e))
raise HTTPException(status_code=500, detail="Service initialization failed")
@router.post("/webhooks/stripe")
async def stripe_webhook(
request: Request,
orchestration_service: SubscriptionOrchestrationService = Depends(get_subscription_orchestration_service)
):
"""
Stripe webhook endpoint to handle payment events
This endpoint verifies webhook signatures and processes Stripe events
"""
try:
# Get the payload and signature
payload = await request.body()
sig_header = request.headers.get('stripe-signature')
if not sig_header:
logger.error("Missing stripe-signature header")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing signature header"
)
# Verify the webhook signature
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except stripe.error.SignatureVerificationError as e:
logger.error("Invalid webhook signature", error=str(e))
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid signature"
)
except ValueError as e:
logger.error("Invalid payload", error=str(e))
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid payload"
)
# Get event type and data
event_type = event['type']
event_data = event['data']['object']
logger.info("Processing Stripe webhook event",
event_type=event_type,
event_id=event.get('id'))
# Use orchestration service to handle the event
result = await orchestration_service.handle_payment_webhook(event_type, event_data)
logger.info("Webhook event processed via orchestration service",
event_type=event_type,
actions_taken=result.get("actions_taken", []))
return {"success": True, "event_type": event_type, "actions_taken": result.get("actions_taken", [])}
except HTTPException:
raise
except Exception as e:
logger.error("Error processing Stripe webhook", error=str(e), exc_info=True)
# Return 200 OK even on processing errors to prevent Stripe retries
# Only return 4xx for signature verification failures
return {
"success": False,
"error": "Webhook processing error",
"details": str(e)
}