17732a0370
CI ruff format --check failed on 13 files. Applied ruff format to bring them in line with project formatting (line wrapping, trailing commas, quote consistency). No functional changes.
160 lines
5.2 KiB
Python
160 lines
5.2 KiB
Python
"""CRUD операции для платежей Antilopay."""
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
import structlog
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database.models import AntilopayPayment
|
|
|
|
|
|
logger = structlog.get_logger(__name__)
|
|
|
|
|
|
async def create_antilopay_payment(
|
|
db: AsyncSession,
|
|
*,
|
|
user_id: int | None,
|
|
order_id: str,
|
|
amount_kopeks: int,
|
|
currency: str = 'RUB',
|
|
description: str | None = None,
|
|
payment_url: str | None = None,
|
|
payment_method: str | None = None,
|
|
antilopay_payment_id: str | None = None,
|
|
expires_at: datetime | None = None,
|
|
metadata_json: dict | None = None,
|
|
) -> AntilopayPayment:
|
|
"""Создает запись о платеже Antilopay."""
|
|
payment = AntilopayPayment(
|
|
user_id=user_id,
|
|
order_id=order_id,
|
|
amount_kopeks=amount_kopeks,
|
|
currency=currency,
|
|
description=description,
|
|
payment_url=payment_url,
|
|
payment_method=payment_method,
|
|
antilopay_payment_id=antilopay_payment_id,
|
|
expires_at=expires_at,
|
|
metadata_json=metadata_json,
|
|
status='pending',
|
|
is_paid=False,
|
|
)
|
|
db.add(payment)
|
|
await db.commit()
|
|
await db.refresh(payment)
|
|
logger.info('Создан платеж Antilopay', order_id=order_id, user_id=user_id)
|
|
return payment
|
|
|
|
|
|
async def get_antilopay_payment_by_order_id(db: AsyncSession, order_id: str) -> AntilopayPayment | None:
|
|
"""Получает платеж по order_id (internal)."""
|
|
result = await db.execute(select(AntilopayPayment).where(AntilopayPayment.order_id == order_id))
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
async def get_antilopay_payment_by_invoice_id(db: AsyncSession, antilopay_payment_id: str) -> AntilopayPayment | None:
|
|
"""Получает платеж по ID от Antilopay."""
|
|
result = await db.execute(
|
|
select(AntilopayPayment).where(AntilopayPayment.antilopay_payment_id == antilopay_payment_id)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
async def get_antilopay_payment_by_id(db: AsyncSession, payment_id: int) -> AntilopayPayment | None:
|
|
"""Получает платеж по ID."""
|
|
result = await db.execute(select(AntilopayPayment).where(AntilopayPayment.id == payment_id))
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
async def get_antilopay_payment_by_id_for_update(db: AsyncSession, payment_id: int) -> AntilopayPayment | None:
|
|
"""Получает платеж по ID с блокировкой FOR UPDATE."""
|
|
result = await db.execute(
|
|
select(AntilopayPayment)
|
|
.where(AntilopayPayment.id == payment_id)
|
|
.with_for_update()
|
|
.execution_options(populate_existing=True)
|
|
)
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
async def update_antilopay_payment_status(
|
|
db: AsyncSession,
|
|
payment: AntilopayPayment,
|
|
*,
|
|
status: str,
|
|
is_paid: bool | None = None,
|
|
antilopay_payment_id: str | None = None,
|
|
payment_method: str | None = None,
|
|
callback_payload: dict | None = None,
|
|
transaction_id: int | None = None,
|
|
) -> AntilopayPayment:
|
|
"""Обновляет статус платежа."""
|
|
payment.status = status
|
|
payment.updated_at = datetime.now(UTC)
|
|
|
|
if is_paid is not None:
|
|
payment.is_paid = is_paid
|
|
if is_paid:
|
|
payment.paid_at = datetime.now(UTC)
|
|
if antilopay_payment_id is not None:
|
|
payment.antilopay_payment_id = antilopay_payment_id
|
|
if payment_method is not None:
|
|
payment.payment_method = payment_method
|
|
if callback_payload is not None:
|
|
payment.callback_payload = callback_payload
|
|
if transaction_id is not None:
|
|
payment.transaction_id = transaction_id
|
|
|
|
await db.commit()
|
|
await db.refresh(payment)
|
|
logger.info(
|
|
'Обновлен статус платежа Antilopay',
|
|
order_id=payment.order_id,
|
|
status=status,
|
|
is_paid=payment.is_paid,
|
|
)
|
|
return payment
|
|
|
|
|
|
async def get_pending_antilopay_payments(db: AsyncSession, user_id: int) -> list[AntilopayPayment]:
|
|
"""Получает незавершенные платежи пользователя."""
|
|
result = await db.execute(
|
|
select(AntilopayPayment).where(
|
|
AntilopayPayment.user_id == user_id,
|
|
AntilopayPayment.status == 'pending',
|
|
AntilopayPayment.is_paid == False,
|
|
)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def get_expired_pending_antilopay_payments(
|
|
db: AsyncSession,
|
|
) -> list[AntilopayPayment]:
|
|
"""Получает просроченные платежи в статусе pending."""
|
|
now = datetime.now(UTC)
|
|
result = await db.execute(
|
|
select(AntilopayPayment).where(
|
|
AntilopayPayment.status == 'pending',
|
|
AntilopayPayment.is_paid == False,
|
|
AntilopayPayment.expires_at < now,
|
|
)
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def link_antilopay_payment_to_transaction(
|
|
db: AsyncSession,
|
|
*,
|
|
payment: AntilopayPayment,
|
|
transaction_id: int,
|
|
) -> AntilopayPayment:
|
|
"""Связывает платеж с транзакцией."""
|
|
payment.transaction_id = transaction_id
|
|
payment.updated_at = datetime.now(UTC)
|
|
await db.flush()
|
|
await db.refresh(payment)
|
|
return payment
|