wahnsinn vibe
This commit is contained in:
106
backend/apps/checkout/__init__.py
Normal file
106
backend/apps/checkout/__init__.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.db import get_db
|
||||
from core.di import get_service
|
||||
from core.events import event_bus
|
||||
from core.redis_client import redis_client
|
||||
from core.security import get_current_user_id
|
||||
|
||||
from apps.cart.models import Cart, CartItem
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class AddressIn(BaseModel):
|
||||
name: str
|
||||
street: str
|
||||
zip: str
|
||||
city: str
|
||||
country: str = "DE"
|
||||
|
||||
|
||||
class CheckoutIn(BaseModel):
|
||||
address: AddressIn
|
||||
payment_method: str = "dummy"
|
||||
|
||||
|
||||
class CheckoutPreview(BaseModel):
|
||||
items: list[dict]
|
||||
subtotal: float
|
||||
total: float
|
||||
|
||||
|
||||
@router.post("/preview", response_model=CheckoutPreview)
|
||||
def preview(user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)):
|
||||
cart = db.query(Cart).filter_by(user_id=user_id).first()
|
||||
if not cart or not cart.items:
|
||||
raise HTTPException(400, "Cart is empty")
|
||||
items = []
|
||||
subtotal = 0.0
|
||||
for it in cart.items:
|
||||
raw = redis_client.get(f"product:{it.product_id}")
|
||||
if not raw:
|
||||
raise HTTPException(400, f"Product {it.product_id} no longer available")
|
||||
p = json.loads(raw)
|
||||
line = round(float(p["price"]) * it.qty, 2)
|
||||
subtotal += line
|
||||
items.append({"product_id": it.product_id, "name": p["name"], "qty": it.qty, "price": float(p["price"]), "line_total": line})
|
||||
return CheckoutPreview(items=items, subtotal=round(subtotal, 2), total=round(subtotal, 2))
|
||||
|
||||
|
||||
@router.post("/confirm")
|
||||
def confirm(body: CheckoutIn, user_id: int = Depends(get_current_user_id), db: Session = Depends(get_db)):
|
||||
cart = db.query(Cart).filter_by(user_id=user_id).first()
|
||||
if not cart or not cart.items:
|
||||
raise HTTPException(400, "Cart is empty")
|
||||
|
||||
# Snapshot items
|
||||
snapshot_items = []
|
||||
subtotal = 0.0
|
||||
for it in cart.items:
|
||||
raw = redis_client.get(f"product:{it.product_id}")
|
||||
if not raw:
|
||||
raise HTTPException(400, f"Product {it.product_id} not available")
|
||||
p = json.loads(raw)
|
||||
line = round(float(p["price"]) * it.qty, 2)
|
||||
subtotal += line
|
||||
snapshot_items.append(
|
||||
{
|
||||
"product_id": it.product_id,
|
||||
"sku": p.get("sku", ""),
|
||||
"name": p.get("name", {}),
|
||||
"price": float(p["price"]),
|
||||
"qty": it.qty,
|
||||
"line_total": line,
|
||||
}
|
||||
)
|
||||
total = round(subtotal, 2)
|
||||
|
||||
payment = get_service("PaymentProvider").charge(total, "EUR", body.payment_method)
|
||||
if payment["status"] != "paid":
|
||||
raise HTTPException(402, "Payment failed")
|
||||
|
||||
event_payload = {
|
||||
"user_id": user_id,
|
||||
"items": snapshot_items,
|
||||
"subtotal": round(subtotal, 2),
|
||||
"total": total,
|
||||
"currency": "EUR",
|
||||
"address": body.address.model_dump(),
|
||||
"payment": payment,
|
||||
}
|
||||
event_bus.publish("checkout.confirmed", event_payload, db=db)
|
||||
|
||||
# Clear cart
|
||||
for it in list(cart.items):
|
||||
db.delete(it)
|
||||
db.commit()
|
||||
|
||||
# Find order id from events handler (orders-app) — pull via redis key or return event_payload
|
||||
# Since the orders handler sets 'last_order_id' for this user in redis for convenience:
|
||||
last = redis_client.get(f"user:{user_id}:last_order_id")
|
||||
return {"ok": True, "order_id": int(last) if last else None, "payment": payment}
|
||||
Reference in New Issue
Block a user