fix
This commit is contained in:
+30
-2
@@ -6,7 +6,7 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from managers.task_manager import TaskParams
|
from managers.task_manager import TaskParams
|
||||||
@@ -309,6 +309,9 @@ class RubleBalanceStorage(_IntBalanceStorage):
|
|||||||
class PaymentStorage:
|
class PaymentStorage:
|
||||||
"""Хранилище ожидающих платежей."""
|
"""Хранилище ожидающих платежей."""
|
||||||
|
|
||||||
|
# Сколько держать обработанные платежи ради защиты от дублей вебхука
|
||||||
|
PROCESSED_TTL_DAYS = 7
|
||||||
|
|
||||||
def __init__(self, file_path: str = "data/payments.json"):
|
def __init__(self, file_path: str = "data/payments.json"):
|
||||||
self.file_path = Path(file_path)
|
self.file_path = Path(file_path)
|
||||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
@@ -327,9 +330,29 @@ class PaymentStorage:
|
|||||||
with open(self.file_path, "w", encoding="utf-8") as f:
|
with open(self.file_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
def _prune_processed(self, payments: dict) -> dict:
|
||||||
|
"""Удаляет обработанные платежи старше PROCESSED_TTL_DAYS.
|
||||||
|
|
||||||
|
Без этого файл рос бы бесконечно, а его читают при каждой
|
||||||
|
отрисовке кабинета.
|
||||||
|
"""
|
||||||
|
cutoff = datetime.now() - timedelta(days=self.PROCESSED_TTL_DAYS)
|
||||||
|
kept = {}
|
||||||
|
for pid, data in payments.items():
|
||||||
|
processed_at = data.get("processed_at")
|
||||||
|
if data.get("processed") and processed_at:
|
||||||
|
try:
|
||||||
|
if datetime.fromisoformat(processed_at) < cutoff:
|
||||||
|
continue
|
||||||
|
except ValueError:
|
||||||
|
pass # некорректная дата — запись оставляем
|
||||||
|
kept[pid] = data
|
||||||
|
return kept
|
||||||
|
|
||||||
async def save(self, payment_id: str, data: dict) -> None:
|
async def save(self, payment_id: str, data: dict) -> None:
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
payments = await asyncio.to_thread(self._load_sync)
|
payments = await asyncio.to_thread(self._load_sync)
|
||||||
|
payments = self._prune_processed(payments)
|
||||||
payments[payment_id] = data
|
payments[payment_id] = data
|
||||||
await asyncio.to_thread(self._save_sync, payments)
|
await asyncio.to_thread(self._save_sync, payments)
|
||||||
|
|
||||||
@@ -357,10 +380,15 @@ class PaymentStorage:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
async def get_by_user(self, user_id: int) -> Optional[dict]:
|
async def get_by_user(self, user_id: int) -> Optional[dict]:
|
||||||
|
"""Возвращает незакрытый счёт пользователя.
|
||||||
|
|
||||||
|
Обработанные платежи остаются в файле как защита от повторного
|
||||||
|
начисления по дублю вебхука, но открытым счётом уже не считаются.
|
||||||
|
"""
|
||||||
async with self._lock:
|
async with self._lock:
|
||||||
payments = await asyncio.to_thread(self._load_sync)
|
payments = await asyncio.to_thread(self._load_sync)
|
||||||
for pid, data in payments.items():
|
for pid, data in payments.items():
|
||||||
if data.get("user_id") == user_id:
|
if data.get("user_id") == user_id and not data.get("processed"):
|
||||||
return {**data, "payment_id": pid}
|
return {**data, "payment_id": pid}
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user