108 lines
3.4 KiB
Python
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,
|
|
)
|