"""Checkout webhook + return routes (moved from cart/bp/cart/global_routes.py).""" from __future__ import annotations from quart import Blueprint, g, request, make_response from sqlalchemy import select from shared.models.order import Order from shared.browser.app.csrf import csrf_exempt from services.checkout import validate_webhook_secret, get_order_with_details from services.check_sumup_status import check_sumup_status def register() -> Blueprint: bp = Blueprint("checkout", __name__, url_prefix="/checkout") @csrf_exempt @bp.post("/webhook//") async def checkout_webhook(order_id: int): """Webhook endpoint for SumUp CHECKOUT_STATUS_CHANGED events.""" if not validate_webhook_secret(request.args.get("token")): return "", 204 try: payload = await request.get_json() except Exception: payload = None if not isinstance(payload, dict): return "", 204 if payload.get("event_type") != "CHECKOUT_STATUS_CHANGED": return "", 204 checkout_id = payload.get("id") if not checkout_id: return "", 204 result = await g.s.execute(select(Order).where(Order.id == order_id)) order = result.scalar_one_or_none() if not order: return "", 204 if order.sumup_checkout_id and order.sumup_checkout_id != checkout_id: return "", 204 try: await check_sumup_status(g.s, order) except Exception: pass return "", 204 @bp.get("/return//") async def checkout_return(order_id: int): """Handle the browser returning from SumUp after payment.""" from shared.sx.page import get_template_context from sx.sx_components import render_checkout_return_page order = await get_order_with_details(g.s, order_id) if not order: tctx = await get_template_context() html = await render_checkout_return_page(tctx, order=None, status="missing") return await make_response(html) if order.page_config_id: from shared.infrastructure.data_client import fetch_data from shared.contracts.dtos import CalendarEntryDTO, TicketDTO, dto_from_dict raw_pc = await fetch_data("blog", "page-config-by-id", params={"id": order.page_config_id}, required=False) post = await fetch_data("blog", "post-by-id", params={"id": raw_pc["container_id"]}, required=False) if raw_pc else None if post: g.page_slug = post["slug"] mps = await fetch_data( "market", "marketplaces-for-container", params={"type": "page", "id": post["id"]}, required=False, ) or [] if mps: g.market_slug = mps[0].get("slug") if order.sumup_checkout_id: try: await check_sumup_status(g.s, order) except Exception: pass status = (order.status or "pending").lower() from shared.infrastructure.data_client import fetch_data from shared.contracts.dtos import CalendarEntryDTO, TicketDTO, dto_from_dict raw_entries = await fetch_data("events", "entries-for-order", params={"order_id": order.id}, required=False) or [] calendar_entries = [dto_from_dict(CalendarEntryDTO, e) for e in raw_entries] raw_tickets = await fetch_data("events", "tickets-for-order", params={"order_id": order.id}, required=False) or [] order_tickets = [dto_from_dict(TicketDTO, t) for t in raw_tickets] await g.s.flush() tctx = await get_template_context() html = await render_checkout_return_page( tctx, order=order, status=status, calendar_entries=calendar_entries, order_tickets=order_tickets, ) return await make_response(html) return bp