mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-20 03:11:47 +00:00
Retry Pal24 bill creation with multiple method identifiers
This commit is contained in:
@@ -64,22 +64,42 @@ class Pal24PaymentMixin:
|
||||
}
|
||||
|
||||
normalized_payment_method = self._normalize_payment_method(payment_method)
|
||||
|
||||
payment_module = import_module("app.services.payment_service")
|
||||
|
||||
try:
|
||||
response = await service.create_bill(
|
||||
amount_kopeks=amount_kopeks,
|
||||
user_id=user_id,
|
||||
order_id=order_id,
|
||||
description=description,
|
||||
ttl_seconds=ttl_seconds,
|
||||
custom_payload=custom_payload,
|
||||
payer_email=payer_email,
|
||||
payment_method=normalized_payment_method,
|
||||
)
|
||||
except Pal24APIError as error:
|
||||
logger.error("Ошибка Pal24 API при создании счета: %s", error)
|
||||
response: Optional[Dict[str, Any]] = None
|
||||
pal24_payment_method: Optional[str] = None
|
||||
last_error: Optional[Exception] = None
|
||||
|
||||
for candidate in self._get_payment_method_candidates(normalized_payment_method):
|
||||
try:
|
||||
response = await service.create_bill(
|
||||
amount_kopeks=amount_kopeks,
|
||||
user_id=user_id,
|
||||
order_id=order_id,
|
||||
description=description,
|
||||
ttl_seconds=ttl_seconds,
|
||||
custom_payload=custom_payload,
|
||||
payer_email=payer_email,
|
||||
payment_method=candidate,
|
||||
)
|
||||
pal24_payment_method = candidate
|
||||
break
|
||||
except Pal24APIError as error:
|
||||
last_error = error
|
||||
logger.warning(
|
||||
"Pal24 отклонил способ оплаты %s для пользователя %s: %s",
|
||||
candidate or "<default>",
|
||||
user_id,
|
||||
error,
|
||||
)
|
||||
|
||||
if response is None:
|
||||
if last_error:
|
||||
logger.error(
|
||||
"Ошибка Pal24 API при создании счета (метод %s): %s",
|
||||
normalized_payment_method,
|
||||
last_error,
|
||||
)
|
||||
return None
|
||||
|
||||
if not response.get("success", True):
|
||||
@@ -148,6 +168,7 @@ class Pal24PaymentMixin:
|
||||
"links": metadata_links,
|
||||
"raw_response": response,
|
||||
"selected_method": normalized_payment_method,
|
||||
"provider_method": pal24_payment_method,
|
||||
}
|
||||
|
||||
payment = await payment_module.create_pal24_payment(
|
||||
@@ -191,6 +212,7 @@ class Pal24PaymentMixin:
|
||||
"transfer_url": transfer_url,
|
||||
"link_page_url": link_page_url,
|
||||
"payment_url": primary_link,
|
||||
"provider_payment_method": pal24_payment_method,
|
||||
}
|
||||
|
||||
async def process_pal24_postback(
|
||||
@@ -528,3 +550,28 @@ class Pal24PaymentMixin:
|
||||
|
||||
normalized = payment_method.strip().lower()
|
||||
return mapping.get(normalized, "sbp")
|
||||
|
||||
@staticmethod
|
||||
def _get_payment_method_candidates(normalized_method: str) -> list[Optional[str]]:
|
||||
mapping = {
|
||||
"sbp": [
|
||||
"fastpay",
|
||||
"fast_payment",
|
||||
"fastpayment",
|
||||
"sbp",
|
||||
"fast_payment_system",
|
||||
],
|
||||
"card": [
|
||||
"bank_card",
|
||||
"card",
|
||||
],
|
||||
}
|
||||
|
||||
candidates = [
|
||||
option
|
||||
for option in mapping.get(normalized_method, [])
|
||||
if option not in (None, "")
|
||||
]
|
||||
|
||||
candidates.append(None)
|
||||
return candidates
|
||||
|
||||
@@ -34,7 +34,13 @@ 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,
|
||||
fail_methods: Optional[set[Optional[str]]] = None,
|
||||
) -> None:
|
||||
self.is_configured = configured
|
||||
self.response = response or {
|
||||
"success": True,
|
||||
@@ -45,9 +51,12 @@ class StubPal24Service:
|
||||
}
|
||||
self.calls: list[Dict[str, Any]] = []
|
||||
self.raise_error: Optional[Exception] = None
|
||||
self.fail_methods = fail_methods or set()
|
||||
|
||||
async def create_bill(self, **kwargs: Any) -> Dict[str, Any]:
|
||||
self.calls.append(kwargs)
|
||||
if kwargs.get("payment_method") in self.fail_methods:
|
||||
raise Pal24APIError("invalid payment method")
|
||||
if self.raise_error:
|
||||
raise self.raise_error
|
||||
return self.response
|
||||
@@ -105,7 +114,106 @@ async def test_create_pal24_payment_success(monkeypatch: pytest.MonkeyPatch) ->
|
||||
assert result["link_url"] == "https://pal24/sbp"
|
||||
assert result["card_url"] == "https://pal24/card"
|
||||
assert stub.calls and stub.calls[0]["amount_kopeks"] == 50000
|
||||
assert stub.calls[0]["payment_method"] == "bank_card"
|
||||
assert "links" in captured_args["metadata"]
|
||||
assert captured_args["metadata"]["provider_method"] == "bank_card"
|
||||
|
||||
|
||||
@pytest.mark.anyio("asyncio")
|
||||
async def test_create_pal24_payment_default_method(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
stub = StubPal24Service()
|
||||
service = _make_service(stub)
|
||||
db = DummySession()
|
||||
|
||||
async def fake_create_pal24_payment(*args: Any, **kwargs: Any) -> DummyLocalPayment:
|
||||
return DummyLocalPayment(payment_id=111)
|
||||
|
||||
monkeypatch.setattr(
|
||||
payment_service_module,
|
||||
"create_pal24_payment",
|
||||
fake_create_pal24_payment,
|
||||
raising=False,
|
||||
)
|
||||
monkeypatch.setattr(settings, "PAL24_MIN_AMOUNT_KOPEKS", 1000, raising=False)
|
||||
monkeypatch.setattr(settings, "PAL24_MAX_AMOUNT_KOPEKS", 1_000_000, raising=False)
|
||||
|
||||
result = await service.create_pal24_payment(
|
||||
db=db,
|
||||
user_id=42,
|
||||
amount_kopeks=150000,
|
||||
description="Пополнение",
|
||||
language="ru",
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result["payment_method"] == "sbp"
|
||||
assert stub.calls and stub.calls[0]["payment_method"] == "fastpay"
|
||||
|
||||
|
||||
@pytest.mark.anyio("asyncio")
|
||||
async def test_create_pal24_payment_retries_on_invalid_method(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
stub = StubPal24Service(fail_methods={"fastpay"})
|
||||
service = _make_service(stub)
|
||||
db = DummySession()
|
||||
|
||||
async def fake_create_pal24_payment(*args: Any, **kwargs: Any) -> DummyLocalPayment:
|
||||
return DummyLocalPayment(payment_id=222)
|
||||
|
||||
monkeypatch.setattr(
|
||||
payment_service_module,
|
||||
"create_pal24_payment",
|
||||
fake_create_pal24_payment,
|
||||
raising=False,
|
||||
)
|
||||
monkeypatch.setattr(settings, "PAL24_MIN_AMOUNT_KOPEKS", 1000, raising=False)
|
||||
monkeypatch.setattr(settings, "PAL24_MAX_AMOUNT_KOPEKS", 1_000_000, raising=False)
|
||||
|
||||
result = await service.create_pal24_payment(
|
||||
db=db,
|
||||
user_id=77,
|
||||
amount_kopeks=250000,
|
||||
description="Пополнение",
|
||||
language="ru",
|
||||
payment_method="sbp",
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result["local_payment_id"] == 222
|
||||
assert len(stub.calls) == 2
|
||||
assert stub.calls[0]["payment_method"] == "fastpay"
|
||||
assert stub.calls[1]["payment_method"] == "fast_payment"
|
||||
assert result["provider_payment_method"] == "fast_payment"
|
||||
|
||||
|
||||
@pytest.mark.anyio("asyncio")
|
||||
async def test_create_pal24_payment_returns_none_if_all_methods_fail(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
stub = StubPal24Service(
|
||||
fail_methods={
|
||||
"fastpay",
|
||||
"fast_payment",
|
||||
"fastpayment",
|
||||
"fast_payment_system",
|
||||
"sbp",
|
||||
None,
|
||||
}
|
||||
)
|
||||
service = _make_service(stub)
|
||||
db = DummySession()
|
||||
|
||||
monkeypatch.setattr(settings, "PAL24_MIN_AMOUNT_KOPEKS", 1000, raising=False)
|
||||
monkeypatch.setattr(settings, "PAL24_MAX_AMOUNT_KOPEKS", 1_000_000, raising=False)
|
||||
|
||||
result = await service.create_pal24_payment(
|
||||
db=db,
|
||||
user_id=13,
|
||||
amount_kopeks=120000,
|
||||
description="Пополнение",
|
||||
language="ru",
|
||||
payment_method="sbp",
|
||||
)
|
||||
|
||||
assert result is None
|
||||
assert len(stub.calls) == 6
|
||||
|
||||
|
||||
@pytest.mark.anyio("asyncio")
|
||||
|
||||
Reference in New Issue
Block a user