Display transaction numbers in admin payment list

This commit is contained in:
Egor
2025-10-26 10:28:32 +03:00
parent 9727e07464
commit 5b59ca3c4f
22 changed files with 2524 additions and 121 deletions

View File

@@ -50,6 +50,8 @@ class StubHeleketService:
self.info_response = info_response
self.calls: list[Dict[str, Any]] = []
self.info_calls: list[Dict[str, Optional[str]]] = []
self.list_response: Optional[Dict[str, Any]] = None
self.list_calls: list[Dict[str, Optional[str]]] = []
async def create_payment(self, payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
self.calls.append(payload)
@@ -64,6 +66,18 @@ class StubHeleketService:
self.info_calls.append({"uuid": uuid, "order_id": order_id})
return self.info_response
async def list_payments(
self,
*,
date_from: Optional[str] = None,
date_to: Optional[str] = None,
cursor: Optional[str] = None,
) -> Optional[Dict[str, Any]]:
self.list_calls.append(
{"date_from": date_from, "date_to": date_to, "cursor": cursor}
)
return self.list_response
def _make_service(stub: Optional[StubHeleketService]) -> PaymentService:
service = PaymentService.__new__(PaymentService) # type: ignore[call-arg]
@@ -249,3 +263,53 @@ async def test_sync_heleket_payment_status_without_response(monkeypatch: pytest.
assert result is payment
assert stub.info_calls == [{"uuid": payment.uuid, "order_id": payment.order_id}]
assert stub.list_calls # fallback to history should be attempted
@pytest.mark.anyio("asyncio")
async def test_sync_heleket_payment_status_history_fallback(monkeypatch: pytest.MonkeyPatch) -> None:
stub = StubHeleketService(response=None, info_response=None)
stub.list_response = {
"state": 0,
"result": {
"items": [
{
"uuid": "heleket-uuid",
"order_id": "order-123",
"status": "paid",
"payment_amount": "150.00",
}
],
"paginate": {"nextCursor": None},
},
}
service = _make_service(stub)
db = DummySession()
payment = SimpleNamespace(
id=77,
uuid="heleket-uuid",
order_id="order-123",
status="check",
user_id=8,
)
async def fake_get_by_id(db, payment_id):
assert payment_id == payment.id
return payment
captured: Dict[str, Any] = {}
async def fake_process(self, db, payload, *, metadata_key):
captured["payload"] = payload
captured["metadata_key"] = metadata_key
return SimpleNamespace(**payload)
monkeypatch.setattr(heleket_crud, "get_heleket_payment_by_id", fake_get_by_id, raising=False)
monkeypatch.setattr(PaymentService, "_process_heleket_payload", fake_process, raising=False)
result = await service.sync_heleket_payment_status(db, local_payment_id=payment.id)
assert result is not None
assert captured["payload"]["status"] == "paid"
assert stub.list_calls

View File

@@ -4,6 +4,7 @@ from pathlib import Path
from typing import Any, Dict, Optional
import sys
from datetime import datetime
from types import SimpleNamespace
import pytest
@@ -34,7 +35,12 @@ class DummyLocalPayment:
class StubPal24Service:
def __init__(self, *, configured: bool = True, response: Optional[Dict[str, Any]] = None) -> None:
def __init__(
self,
*,
configured: bool = True,
response: Optional[Dict[str, Any]] = None,
) -> None:
self.is_configured = configured
self.response = response or {
"success": True,
@@ -45,6 +51,12 @@ class StubPal24Service:
}
self.calls: list[Dict[str, Any]] = []
self.raise_error: Optional[Exception] = None
self.status_response: Optional[Dict[str, Any]] = {"status": "NEW"}
self.payment_status_response: Optional[Dict[str, Any]] = None
self.bill_payments_response: Optional[Dict[str, Any]] = None
self.status_calls: list[str] = []
self.payment_status_calls: list[str] = []
self.bill_payments_calls: list[str] = []
async def create_bill(self, **kwargs: Any) -> Dict[str, Any]:
self.calls.append(kwargs)
@@ -52,6 +64,18 @@ class StubPal24Service:
raise self.raise_error
return self.response
async def get_bill_status(self, bill_id: str) -> Optional[Dict[str, Any]]:
self.status_calls.append(bill_id)
return self.status_response
async def get_payment_status(self, payment_id: str) -> Optional[Dict[str, Any]]:
self.payment_status_calls.append(payment_id)
return self.payment_status_response
async def get_bill_payments(self, bill_id: str) -> Optional[Dict[str, Any]]:
self.bill_payments_calls.append(bill_id)
return self.bill_payments_response
def _make_service(stub: Optional[StubPal24Service]) -> PaymentService:
service = PaymentService.__new__(PaymentService) # type: ignore[call-arg]
@@ -198,3 +222,110 @@ async def test_create_pal24_payment_handles_api_errors(monkeypatch: pytest.Monke
language="ru",
)
assert result is None
@pytest.mark.anyio("asyncio")
async def test_get_pal24_payment_status_updates_from_remote(monkeypatch: pytest.MonkeyPatch) -> None:
stub = StubPal24Service()
stub.status_response = {"status": "SUCCESS"}
stub.payment_status_response = {
"success": True,
"id": "PAY-1",
"bill_id": "BILL-1",
"status": "SUCCESS",
"payment_method": "SBP",
"account_amount": "700.00",
"from_card": "676754******1234",
}
stub.bill_payments_response = {
"data": [
{
"id": "PAY-1",
"bill_id": "BILL-1",
"status": "SUCCESS",
"from_card": "676754******1234",
"payment_method": "SBP",
}
]
}
service = _make_service(stub)
db = DummySession()
payment = SimpleNamespace(
id=99,
bill_id="BILL-1",
payment_id=None,
payment_status="NEW",
payment_method=None,
balance_amount=None,
balance_currency=None,
payer_account=None,
status="NEW",
is_paid=False,
paid_at=None,
transaction_id=None,
user_id=1,
)
async def fake_get_by_id(db: DummySession, payment_id: int) -> SimpleNamespace:
assert payment_id == payment.id
return payment
async def fake_update_status(
db: DummySession,
payment_obj: SimpleNamespace,
*,
status: str,
**kwargs: Any,
) -> SimpleNamespace:
payment_obj.status = status
payment_obj.last_status = status
for key, value in kwargs.items():
setattr(payment_obj, key, value)
if "is_paid" in kwargs:
payment_obj.is_paid = kwargs["is_paid"]
await db.commit()
return payment_obj
async def fake_finalize(
self: PaymentService,
db: DummySession,
payment_obj: Any,
*,
payment_id: Optional[str] = None,
trigger: str,
) -> bool:
return False
monkeypatch.setattr(
payment_service_module,
"get_pal24_payment_by_id",
fake_get_by_id,
raising=False,
)
monkeypatch.setattr(
payment_service_module,
"update_pal24_payment_status",
fake_update_status,
raising=False,
)
monkeypatch.setattr(
PaymentService,
"_finalize_pal24_payment",
fake_finalize,
raising=False,
)
result = await service.get_pal24_payment_status(db, local_payment_id=payment.id)
assert result is not None
assert payment.status == "SUCCESS"
assert payment.payment_id == "PAY-1"
assert payment.payment_status == "SUCCESS"
assert payment.payment_method == "sbp"
assert payment.is_paid is True
assert stub.status_calls == ["BILL-1"]
assert stub.payment_status_calls in ([], ["PAY-1"])
assert result["remote_status"] == "SUCCESS"
assert result["remote_data"] and "bill_status" in result["remote_data"]

View File

@@ -6,7 +6,7 @@ from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace, ModuleType
from typing import Any, Dict
from typing import Any, Dict, Optional
import sys
import pytest
@@ -829,6 +829,21 @@ async def test_get_pal24_payment_status_auto_finalize(monkeypatch: pytest.Monkey
},
}
async def get_payment_status(self, payment_id: str) -> Optional[Dict[str, Any]]:
return None
async def get_bill_payments(self, bill_id: str) -> Optional[Dict[str, Any]]:
return {
"data": [
{
"id": "trs-auto-1",
"bill_id": bill_id,
"status": "SUCCESS",
"payment_method": "SBP",
}
]
}
service.pal24_service = DummyPal24Service()
fake_session = FakeSession()