Files
bakery-ia/services/tenant/app/api/webhooks.py

98 lines
3.5 KiB
Python
Raw Permalink Normal View History

2025-09-25 14:30:47 +02:00
"""
Webhook endpoints for handling payment provider events
These endpoints receive events from payment providers like Stripe
2026-01-13 22:22:38 +01:00
All event processing is handled by SubscriptionOrchestrationService
2025-09-25 14:30:47 +02:00
"""
import structlog
2026-01-11 07:50:34 +01:00
import stripe
2025-09-25 14:30:47 +02:00
from fastapi import APIRouter, Depends, HTTPException, status, Request
2026-01-13 22:22:38 +01:00
from sqlalchemy.ext.asyncio import AsyncSession
2025-09-25 14:30:47 +02:00
2026-01-13 22:22:38 +01:00
from app.services.subscription_orchestration_service import SubscriptionOrchestrationService
2026-01-11 07:50:34 +01:00
from app.core.config import settings
from app.core.database import get_db
2025-09-25 14:30:47 +02:00
logger = structlog.get_logger()
router = APIRouter()
2026-01-13 22:22:38 +01:00
def get_subscription_orchestration_service(
db: AsyncSession = Depends(get_db)
) -> SubscriptionOrchestrationService:
"""Dependency injection for SubscriptionOrchestrationService"""
2025-09-25 14:30:47 +02:00
try:
2026-01-13 22:22:38 +01:00
return SubscriptionOrchestrationService(db)
2025-09-25 14:30:47 +02:00
except Exception as e:
2026-01-13 22:22:38 +01:00
logger.error("Failed to create subscription orchestration service", error=str(e))
raise HTTPException(status_code=500, detail="Service initialization failed")
2025-09-25 14:30:47 +02:00
@router.post("/webhooks/stripe")
async def stripe_webhook(
request: Request,
2026-01-13 22:22:38 +01:00
orchestration_service: SubscriptionOrchestrationService = Depends(get_subscription_orchestration_service)
2025-09-25 14:30:47 +02:00
):
"""
Stripe webhook endpoint to handle payment events
2026-01-11 07:50:34 +01:00
This endpoint verifies webhook signatures and processes Stripe events
2025-09-25 14:30:47 +02:00
"""
try:
2026-01-11 07:50:34 +01:00
# Get the payload and signature
2025-09-25 14:30:47 +02:00
payload = await request.body()
sig_header = request.headers.get('stripe-signature')
2026-01-11 07:50:34 +01:00
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'))
2026-01-13 22:22:38 +01:00
# Use orchestration service to handle the event
result = await orchestration_service.handle_payment_webhook(event_type, event_data)
2026-01-11 07:50:34 +01:00
2026-01-13 22:22:38 +01:00
logger.info("Webhook event processed via orchestration service",
event_type=event_type,
actions_taken=result.get("actions_taken", []))
2026-01-11 07:50:34 +01:00
2026-01-13 22:22:38 +01:00
return {"success": True, "event_type": event_type, "actions_taken": result.get("actions_taken", [])}
2026-01-11 07:50:34 +01:00
except HTTPException:
raise
2025-09-25 14:30:47 +02:00
except Exception as e:
2026-01-11 07:50:34 +01:00
logger.error("Error processing Stripe webhook", error=str(e), exc_info=True)
2026-01-15 20:45:49 +01:00
# 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)
}