- Implemented SubscriptionRepository with methods to get, create, and update subscriptions. - Added BalanceTXRepository for creating and retrieving balance transactions. - Introduced a test script for simulating Pally webhook for local testing.
107 lines
3.5 KiB
Python
107 lines
3.5 KiB
Python
import hashlib
|
|
import logging
|
|
import math
|
|
|
|
from aiogram import Bot
|
|
from fastapi import Depends, Form, HTTPException
|
|
from fastapi.routing import APIRouter
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from api.core.deps import get_bot, get_db, get_deps
|
|
from api.core.notifications import successful_payment_notification
|
|
from config import settings
|
|
from db.models.bills import DepositStatus
|
|
from db.models.transactions import BalanceTxType
|
|
from misc.pally import BillStatus
|
|
from schemas.di import APIDependenciesDTO
|
|
|
|
router = APIRouter(prefix="/checkout/pally")
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@router.post("/callback")
|
|
async def pally_callback(
|
|
id: str = Form(...),
|
|
bill_id: str = Form(...),
|
|
status: str = Form(...),
|
|
req_amount: str = Form(...),
|
|
currency: str = Form(...),
|
|
order_id: str | None = Form(None),
|
|
signature: str = Form(...),
|
|
session: AsyncSession = Depends(get_db),
|
|
bot: Bot = Depends(get_bot),
|
|
deps: APIDependenciesDTO = Depends(get_deps),
|
|
):
|
|
oid = order_id or ""
|
|
raw_string = f"{id}{status}{req_amount}{currency}{oid}{settings.pally_token}"
|
|
md5_hash = hashlib.md5(raw_string.encode("utf-8")).hexdigest()
|
|
expected_signature = hashlib.sha1(md5_hash.encode("utf-8")).hexdigest()
|
|
|
|
if signature != expected_signature:
|
|
logger.critical("Invalid signature for bill %s", bill_id)
|
|
raise HTTPException(403, detail="Invalid signature.")
|
|
|
|
if status != BillStatus.SUCCESS or not req_amount.isdigit():
|
|
logger.info("Bill %s skipped: status=%s, req_amount=%s", bill_id, status, req_amount)
|
|
return "OK"
|
|
|
|
logger.info("Received successfully paid bill %s", bill_id)
|
|
|
|
if not oid.isdigit():
|
|
logger.critical("Invalid order_id format for bill %s", bill_id)
|
|
return "OK"
|
|
|
|
bill = await deps.bills_repository.get_bill_by_id(session, int(oid))
|
|
|
|
if not bill:
|
|
logger.critical("Bill %s is not found in db", bill_id)
|
|
return "OK"
|
|
|
|
if bill.status != DepositStatus.PENDING:
|
|
logger.warning("bill=%s is already paid according to the db", bill.id)
|
|
return "OK"
|
|
|
|
try:
|
|
amount = int(req_amount)
|
|
|
|
if bill.amount != amount:
|
|
logger.warning("requested amount for bill=%s is not equal to db entry", bill.id)
|
|
return "OK"
|
|
|
|
await deps.user_repository.increase_user_balance(
|
|
session,
|
|
bill.creator_id,
|
|
amount=amount,
|
|
tx_type=BalanceTxType.DEPOSIT,
|
|
description="personal balance deposit via PALLY",
|
|
)
|
|
await successful_payment_notification(bot, bill.creator_id, amount)
|
|
|
|
user = await deps.user_repository.get_user_by_id(session, bill.creator_id)
|
|
|
|
referal = user.referal_id if user else None
|
|
if referal is not None:
|
|
referal_amount = math.floor(amount * (settings.referal_bonus / 100))
|
|
await deps.user_repository.increase_user_balance(
|
|
session,
|
|
referal,
|
|
referal_amount,
|
|
tx_type=BalanceTxType.REFERRAL_BONUS,
|
|
description=f"referal reward for referal_id={bill.creator_id}",
|
|
)
|
|
await successful_payment_notification(bot, referal, referal_amount)
|
|
|
|
except Exception:
|
|
logger.exception(
|
|
"Exception caught while processing balances for bill %s (Creator: %s)",
|
|
bill_id,
|
|
bill.creator_id,
|
|
)
|
|
|
|
await deps.bills_repository.update_bill_status(
|
|
session, int(bill.id), status=DepositStatus.SUCCESS
|
|
)
|
|
|
|
return "OK"
|