feat: per-page SumUp credentials in checkout flow (Phase 3)
- Add resolve_page_config() to determine PageConfig from cart/calendar context - Set page_config_id on Order during checkout - Pass page_config to SumUp create_checkout and build_sumup_reference - check_sumup_status uses order.page_config for per-page credential resolution - Fix: use session.flush() instead of g.s.flush() in check_sumup_status Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ from .services import (
|
||||
from .services.checkout import (
|
||||
find_or_create_cart_item,
|
||||
create_order_from_cart,
|
||||
resolve_page_config,
|
||||
build_sumup_description,
|
||||
build_sumup_reference,
|
||||
build_webhook_url,
|
||||
@@ -102,6 +103,17 @@ def register(url_prefix: str) -> Blueprint:
|
||||
if cart_total <= 0:
|
||||
return redirect(url_for("cart.view_cart"))
|
||||
|
||||
# Resolve per-page credentials
|
||||
try:
|
||||
page_config = await resolve_page_config(g.s, cart, calendar_entries)
|
||||
except ValueError as e:
|
||||
html = await render_template(
|
||||
"_types/cart/checkout_error.html",
|
||||
order=None,
|
||||
error=str(e),
|
||||
)
|
||||
return await make_response(html, 400)
|
||||
|
||||
# Create order from cart
|
||||
ident = current_cart_identity()
|
||||
order = await create_order_from_cart(
|
||||
@@ -114,9 +126,13 @@ def register(url_prefix: str) -> Blueprint:
|
||||
calendar_amount,
|
||||
)
|
||||
|
||||
# Set page_config on order if resolved
|
||||
if page_config:
|
||||
order.page_config_id = page_config.id
|
||||
|
||||
# Build SumUp checkout details
|
||||
redirect_url = url_for("cart.checkout_return", order_id=order.id, _external=True)
|
||||
order.sumup_reference = build_sumup_reference(order.id)
|
||||
order.sumup_reference = build_sumup_reference(order.id, page_config=page_config)
|
||||
description = build_sumup_description(cart, order.id)
|
||||
|
||||
webhook_base_url = url_for("cart.checkout_webhook", order_id=order.id, _external=True)
|
||||
@@ -127,6 +143,7 @@ def register(url_prefix: str) -> Blueprint:
|
||||
redirect_url=redirect_url,
|
||||
webhook_url=webhook_url,
|
||||
description=description,
|
||||
page_config=page_config,
|
||||
)
|
||||
await clear_cart_for_order(g.s, order)
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from suma_browser.app.payments.sumup import get_checkout as sumup_get_checkout
|
||||
from sqlalchemy import update
|
||||
from models.calendars import CalendarEntry # NEW
|
||||
from models.calendars import CalendarEntry
|
||||
|
||||
|
||||
async def check_sumup_status(session, order):
|
||||
checkout_data = await sumup_get_checkout(order.sumup_checkout_id)
|
||||
# Use order's page_config for per-page SumUp credentials
|
||||
page_config = getattr(order, "page_config", None)
|
||||
checkout_data = await sumup_get_checkout(order.sumup_checkout_id, page_config=page_config)
|
||||
order.sumup_status = checkout_data.get("status") or order.sumup_status
|
||||
sumup_status = (order.sumup_status or "").upper()
|
||||
|
||||
@@ -26,10 +28,9 @@ async def check_sumup_status(session, order):
|
||||
.where(*filters)
|
||||
.values(state="provisional")
|
||||
)
|
||||
# also clear cart for this user/session if it wasn't already
|
||||
elif sumup_status == "FAILED":
|
||||
order.status = "failed"
|
||||
else:
|
||||
order.status = sumup_status.lower() or order.status
|
||||
|
||||
await g.s.flush()
|
||||
await session.flush()
|
||||
|
||||
@@ -9,7 +9,9 @@ from sqlalchemy.orm import selectinload
|
||||
|
||||
from models.market import Product, CartItem
|
||||
from models.order import Order, OrderItem
|
||||
from models.calendars import CalendarEntry
|
||||
from models.calendars import CalendarEntry, Calendar
|
||||
from models.page_config import PageConfig
|
||||
from models.market_place import MarketPlace
|
||||
from config import config
|
||||
|
||||
|
||||
@@ -57,6 +59,44 @@ async def find_or_create_cart_item(
|
||||
return cart_item
|
||||
|
||||
|
||||
async def resolve_page_config(
|
||||
session: AsyncSession,
|
||||
cart: list[CartItem],
|
||||
calendar_entries: list[CalendarEntry],
|
||||
) -> Optional["PageConfig"]:
|
||||
"""Determine the PageConfig for this order.
|
||||
|
||||
Returns PageConfig or None (use global credentials).
|
||||
Raises ValueError if items span multiple pages.
|
||||
"""
|
||||
post_ids: set[int] = set()
|
||||
|
||||
# From cart items via market_place
|
||||
for ci in cart:
|
||||
if ci.market_place_id:
|
||||
mp = await session.get(MarketPlace, ci.market_place_id)
|
||||
if mp:
|
||||
post_ids.add(mp.post_id)
|
||||
|
||||
# From calendar entries via calendar
|
||||
for entry in calendar_entries:
|
||||
cal = await session.get(Calendar, entry.calendar_id)
|
||||
if cal and cal.post_id:
|
||||
post_ids.add(cal.post_id)
|
||||
|
||||
if len(post_ids) > 1:
|
||||
raise ValueError("Cannot checkout items from multiple pages")
|
||||
|
||||
if not post_ids:
|
||||
return None # global credentials
|
||||
|
||||
post_id = post_ids.pop()
|
||||
pc = (await session.execute(
|
||||
select(PageConfig).where(PageConfig.post_id == post_id)
|
||||
)).scalar_one_or_none()
|
||||
return pc
|
||||
|
||||
|
||||
async def create_order_from_cart(
|
||||
session: AsyncSession,
|
||||
cart: list[CartItem],
|
||||
@@ -139,10 +179,13 @@ def build_sumup_description(cart: list[CartItem], order_id: int) -> str:
|
||||
return f"Order {order_id} ({item_count} item{'s' if item_count != 1 else ''}): {summary}"
|
||||
|
||||
|
||||
def build_sumup_reference(order_id: int) -> str:
|
||||
def build_sumup_reference(order_id: int, page_config=None) -> str:
|
||||
"""Build a SumUp reference with configured prefix."""
|
||||
sumup_cfg = config().get("sumup", {}) or {}
|
||||
prefix = sumup_cfg.get("checkout_reference_prefix", "")
|
||||
if page_config and page_config.sumup_checkout_prefix:
|
||||
prefix = page_config.sumup_checkout_prefix
|
||||
else:
|
||||
sumup_cfg = config().get("sumup", {}) or {}
|
||||
prefix = sumup_cfg.get("checkout_reference_prefix", "")
|
||||
return f"{prefix}{order_id}"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user