175 lines
5.5 KiB
Python
175 lines
5.5 KiB
Python
import hashlib
|
|
import hmac
|
|
import logging
|
|
import math
|
|
from datetime import UTC, datetime
|
|
|
|
from config import cfg
|
|
from db.models.orders import OrderStatus
|
|
from db.models.transactions import BalanceTransaction, BalanceTxType
|
|
from repositories import AddonsRepository
|
|
from repositories.invoices import InvoiceRepository
|
|
from repositories.orders import OrderRepository
|
|
from repositories.pricing import PricingRepository
|
|
from repositories.users import UserRepository
|
|
from schemas.invoices import InvoiceStatus
|
|
from services.plans import get_pricing_model
|
|
from services.rw_sync import enqueue_rw_sync
|
|
from services.subscriptions import (
|
|
apply_order_now,
|
|
deduct_order_balance,
|
|
queue_order_for_later,
|
|
should_apply_immediately,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def validate_pally_signature(out_sum: str, inv_id: str, signature_value: str) -> bool:
|
|
raw_string = f"{out_sum}:{inv_id}:{cfg.pally_token}"
|
|
expected_signature = hashlib.md5(raw_string.encode("utf-8")).hexdigest().upper()
|
|
return hmac.compare_digest(signature_value, expected_signature)
|
|
|
|
|
|
async def process_subscription_purchase( # noqa: PLR0911, PLR0912
|
|
session,
|
|
*,
|
|
invoice_id: int,
|
|
trs_id: str,
|
|
amount: int,
|
|
) -> None:
|
|
invoice_repo = InvoiceRepository(session)
|
|
orders_repo = OrderRepository(session)
|
|
users_repo = UserRepository(session)
|
|
pricing_repo = PricingRepository(session)
|
|
|
|
invoice = await invoice_repo.get_by_id(invoice_id)
|
|
|
|
if not invoice:
|
|
logger.critical("Bill %s not found in database for TrsId %s", invoice_id, trs_id)
|
|
return
|
|
|
|
if invoice.status != InvoiceStatus.ACTIVE:
|
|
logger.warning(
|
|
"Bill %s (TrsId: %s) is already processed with status: %s",
|
|
invoice.id,
|
|
trs_id,
|
|
invoice.status,
|
|
)
|
|
return
|
|
|
|
if invoice.amount != amount:
|
|
logger.error(
|
|
"Amount mismatch for bill %s (TrsId: %s) - Expected: %s, Received: %s",
|
|
invoice.id,
|
|
trs_id,
|
|
invoice.amount,
|
|
amount,
|
|
)
|
|
return
|
|
|
|
if invoice.order_id is None:
|
|
logger.critical("Invoice %s has no linked order for TrsId %s", invoice.id, trs_id)
|
|
return
|
|
|
|
order = await orders_repo.get_by_id(invoice.order_id)
|
|
if order is None:
|
|
logger.critical(
|
|
"Order not found for bill %s (TrsId: %s, user_id=%s, order_id=%s)",
|
|
invoice.id,
|
|
trs_id,
|
|
invoice.creator_id,
|
|
invoice.order_id,
|
|
)
|
|
return
|
|
|
|
if order.user_id != invoice.creator_id:
|
|
logger.critical(
|
|
"Order %s does not belong to invoice creator %s for TrsId %s",
|
|
order.id,
|
|
invoice.creator_id,
|
|
trs_id,
|
|
)
|
|
return
|
|
|
|
user = await users_repo.get_user_by_id(invoice.creator_id)
|
|
if user is None:
|
|
logger.critical("User %s not found for TrsId %s", invoice.creator_id, trs_id)
|
|
return
|
|
|
|
subscription = user.subscription
|
|
now = datetime.now(UTC)
|
|
pricing = await get_pricing_model(AddonsRepository(session), pricing_repo)
|
|
|
|
logger.info(
|
|
"Processing payment: bill_id=%s, order_id=%s, user_id=%s, amount=%s",
|
|
invoice.id,
|
|
order.id,
|
|
invoice.creator_id,
|
|
amount,
|
|
)
|
|
|
|
order.status = OrderStatus.PAID
|
|
invoice.status = InvoiceStatus.PAID
|
|
|
|
if order.balance_amount > 0:
|
|
await deduct_order_balance(
|
|
session,
|
|
user=user,
|
|
order=order,
|
|
description=f"order {order.id} partial payment from balance",
|
|
)
|
|
|
|
if should_apply_immediately(
|
|
subscription=subscription,
|
|
order=order,
|
|
pricing=pricing,
|
|
now=now,
|
|
):
|
|
applied_subscription = await apply_order_now(
|
|
session, user=user, order=order, pricing=pricing, now=now
|
|
)
|
|
await enqueue_rw_sync(session, applied_subscription.id)
|
|
else:
|
|
if subscription is None:
|
|
logger.critical(
|
|
"Cannot queue order %s without subscription for user %s", order.id, user.id
|
|
)
|
|
return
|
|
await queue_order_for_later(order=order, subscription=subscription, now=now)
|
|
|
|
referal_id = user.referal_id
|
|
if referal_id is not None:
|
|
referal_amount = math.floor(amount * (cfg.referal_bonus / 100))
|
|
if referal_amount > 0:
|
|
referal_user = await session.get(type(user), referal_id)
|
|
if referal_user is not None:
|
|
session.add(
|
|
BalanceTransaction(
|
|
user_id=referal_id,
|
|
amount=referal_amount,
|
|
tx_type=BalanceTxType.REFERRAL_BONUS,
|
|
balance_before=referal_user.balance,
|
|
balance_after=referal_user.balance + referal_amount,
|
|
description=f"referral reward for user {invoice.creator_id} (TrsId: {trs_id})",
|
|
)
|
|
)
|
|
referal_user.balance += referal_amount
|
|
|
|
logger.info(
|
|
"Referral bonus processed: referrer_id=%s, amount=%s",
|
|
referal_id,
|
|
referal_amount,
|
|
)
|
|
else:
|
|
logger.warning(
|
|
"Referrer %s not found for user_id=%s while processing TrsId %s",
|
|
referal_id,
|
|
invoice.creator_id,
|
|
trs_id,
|
|
)
|
|
|
|
await session.commit()
|
|
|
|
logger.info("Bill %s marked as PAID for TrsId %s", invoice_id, trs_id)
|