feat(sub): handling upgrade/downgrade

rw integration and /me endpoints are left
This commit is contained in:
2026-08-02 14:16:57 +07:00
parent 24f857ef1b
commit 97a4e819d6
10 changed files with 401 additions and 58 deletions

View File

@@ -1,8 +1,12 @@
from datetime import UTC, datetime
import math
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
@@ -13,6 +17,12 @@ 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")
@@ -29,32 +39,68 @@ async def checkout(
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(
ctx.user.id,
order.devices,
order.duration_days,
user_id=ctx.user.id,
devices=order.devices,
duration_days=order.duration_days,
total_amount=price,
balance_amount=bonus_covered,
addons=order.addons,
)
pricing = await get_pricing_model(addons_repo, pricing_repo)
price = await calculate_price(addons_repo=addons_repo, order=order, pricing=pricing)
bonus_covered = ctx.user.balance
amount_to_pay = max(price - bonus_covered, cfg.minimal_deposit) if bonus_covered < price else 0
if amount_to_pay > 0:
invoice = await invoices_repo.create(
creator_id=ctx.user.id, amount=amount_to_pay, status=InvoiceStatus.ACTIVE
await invoices_repo.create(
creator_id=ctx.user.id,
order_id=order_entry.id,
amount=amount_to_pay,
status=InvoiceStatus.ACTIVE,
)
with open("log.txt", "w") as f:
f.write(
" ".join((str(price), str(amount_to_pay), cfg.pally_shop_id, str(order_entry.id)))
)
bill = await pally.bills.create(
float(amount_to_pay), cfg.pally_shop_id, order_id=str(order_entry.id)
)
bill = await pally.bills.create(amount_to_pay, cfg.pally_shop_id, order_id=invoice.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=order_entry.id,
order_id=str(order_entry.id),
total_amount=price,
bonus_paid=bonus_covered,
amount_to_pay=amount_to_pay,