Files
malenia-backend/routes/orders.py

108 lines
3.4 KiB
Python

import math
from datetime import UTC, datetime
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from config import cfg
from core.deps import get_auth_context, get_pally_client
from db.models.orders import OrderStatus
from db.session import get_db
from external.pally import PallyClient
from repositories import AddonsRepository, PricingRepository
from repositories.invoices import InvoiceRepository
from repositories.orders import OrderRepository
from schemas.checkout import CheckoutResponse
from schemas.dto import AuthContext
from schemas.invoices import InvoiceStatus
from schemas.plans import OrderDetails
from services.plans import calculate_price, get_pricing_model
from services.subscriptions import (
apply_order_now,
deduct_order_balance,
queue_order_for_later,
should_apply_immediately,
)
router = APIRouter(prefix="/orders")
@router.post("/checkout", response_model=CheckoutResponse, status_code=201)
async def checkout(
order: OrderDetails,
ctx: AuthContext = Depends(get_auth_context),
session: AsyncSession = Depends(get_db),
pally: PallyClient = Depends(get_pally_client),
):
addons_repo = AddonsRepository(session)
pricing_repo = PricingRepository(session)
invoices_repo = InvoiceRepository(session)
orders_repo = OrderRepository(session)
pricing = await get_pricing_model(addons_repo, pricing_repo)
price = math.ceil(await calculate_price(addons_repo=addons_repo, order=order, pricing=pricing))
bonus_covered = min(ctx.user.balance, price)
amount_to_pay = round(max(price - bonus_covered, 0), 2)
now = datetime.now(UTC)
order_entry = await orders_repo.create(
user_id=ctx.user.id,
devices=order.devices,
duration_days=order.duration_days,
total_amount=price,
balance_amount=bonus_covered,
addons=order.addons,
)
if amount_to_pay > 0:
await invoices_repo.create(
creator_id=ctx.user.id,
order_id=order_entry.id,
amount=amount_to_pay,
status=InvoiceStatus.ACTIVE,
)
bill = await pally.bills.create(
amount=float(amount_to_pay),
shop_id=cfg.pally_shop_id,
order_id=str(order_entry.id),
)
payment_link = bill.link_page_url
if not payment_link:
raise HTTPException(500, detail="failed to create invoice")
else:
await deduct_order_balance(
session,
user=ctx.user,
order=order_entry,
description=f"order {order_entry.id} paid from balance",
)
order_entry.status = OrderStatus.PAID
if should_apply_immediately(
subscription=ctx.user.subscription,
order=order_entry,
pricing=pricing,
now=now,
):
await apply_order_now(
session,
user=ctx.user,
order=order_entry,
pricing=pricing,
now=now,
)
else:
await queue_order_for_later(
order=order_entry, subscription=ctx.user.subscription, now=now
)
await session.commit()
payment_link = None
return CheckoutResponse(
order_id=str(order_entry.id),
total_amount=price,
bonus_paid=bonus_covered,
amount_to_pay=amount_to_pay,
payment_link=payment_link,
)