Добавлена отправка NotificationURL и SuccessURL

This commit is contained in:
Раис Юсупалиев
2026-04-18 00:58:13 +03:00
parent bddac60965
commit 78e9ad4fa9
18 changed files with 252 additions and 3 deletions
@@ -298,6 +298,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
+68 -1
View File
@@ -10,6 +10,7 @@ from app.adapters.tbank.base import (
TBankPaymentAdapterError,
TBankPaymentRequestError,
)
from app.config import TBankPaymentAuthConfig, TBankPaymentConfig
class SequenceHTTPClient:
@@ -48,6 +49,8 @@ class SequenceHTTPClient:
def _make_adapter(
http_client: SequenceHTTPClient,
*,
notification_url: str = "https://example.test/tbank/notify",
success_url: str = "https://example.test/payment/success",
retry_attempts: int = 0,
retry_backoff_seconds: float = 0.2,
sleep_calls: list[float] | None = None,
@@ -59,6 +62,8 @@ def _make_adapter(
return TBankAdapter(
http_client=http_client, # type: ignore[arg-type]
init_url="https://securepay.tinkoff.ru/v2/Init",
notification_url=notification_url,
success_url=success_url,
terminal_key="TBankTest",
password="test-password",
timeout_seconds=7.5,
@@ -85,7 +90,14 @@ def test_create_payment_link_posts_signed_payload_and_maps_payment_url() -> None
)
expected_token = hashlib.sha256(
"125000order-uuid-1test-passwordTBankTest".encode("utf-8")
(
"125000"
"https://example.test/tbank/notify"
"order-uuid-1"
"test-password"
"https://example.test/payment/success"
"TBankTest"
).encode("utf-8")
).hexdigest()
assert result == "https://securepay.test/pay/1"
assert http_client.calls == [
@@ -96,6 +108,8 @@ def test_create_payment_link_posts_signed_payload_and_maps_payment_url() -> None
"TerminalKey": "TBankTest",
"Amount": 125000,
"OrderId": "order-uuid-1",
"NotificationURL": "https://example.test/tbank/notify",
"SuccessURL": "https://example.test/payment/success",
"Token": expected_token,
},
"data": None,
@@ -108,6 +122,59 @@ def test_create_payment_link_posts_signed_payload_and_maps_payment_url() -> None
]
def test_from_config_posts_configured_urls_and_deterministic_token() -> None:
response = httpx.Response(
200,
json={"Success": True, "PaymentURL": "https://securepay.test/pay/2"},
request=httpx.Request("POST", "https://securepay.tinkoff.ru/v2/Init"),
)
http_client = SequenceHTTPClient([response])
config = TBankPaymentConfig(
init_url="https://securepay.tinkoff.ru/v2/Init",
notification_url="https://merchant.test/tbank/notification",
success_url="https://merchant.test/payment/success",
auth=TBankPaymentAuthConfig(
terminal_key="ConfigTerminal",
password="config-password",
),
timeout_seconds=6.25,
retry_attempts=0,
retry_backoff_seconds=0.1,
)
adapter = TBankAdapter.from_config(
http_client=http_client, # type: ignore[arg-type]
config=config,
)
result = asyncio.run(
adapter.create_payment_link(
order_uuid="order-uuid-2",
amount_kopecks=9900,
)
)
expected_token = hashlib.sha256(
(
"9900"
"https://merchant.test/tbank/notification"
"order-uuid-2"
"config-password"
"https://merchant.test/payment/success"
"ConfigTerminal"
).encode("utf-8")
).hexdigest()
assert result == "https://securepay.test/pay/2"
assert http_client.calls[0]["json"] == {
"TerminalKey": "ConfigTerminal",
"Amount": 9900,
"OrderId": "order-uuid-2",
"NotificationURL": "https://merchant.test/tbank/notification",
"SuccessURL": "https://merchant.test/payment/success",
"Token": expected_token,
}
assert http_client.calls[0]["timeout"] == 6.25
def test_create_payment_link_maps_4xx_to_request_error() -> None:
response = httpx.Response(
400,
@@ -17,6 +17,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://default.test/tbank/notification"
success_url: "https://default.test/payment/success"
auth:
terminal_key: "yaml-terminal-key"
password: "yaml-password"
@@ -25,6 +25,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
@@ -14,6 +14,8 @@ repository:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
@@ -25,6 +25,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
@@ -25,6 +25,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
@@ -25,6 +25,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
@@ -24,6 +24,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"
@@ -17,6 +17,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://override.test/tbank/notification"
success_url: "https://override.test/payment/success"
auth:
terminal_key: "override-terminal-key"
password: "override-password"
+93
View File
@@ -104,6 +104,56 @@ def _use_runtime_config_files(
get_settings.cache_clear()
def _write_tbank_url_validation_config(
tmp_path: Path,
*,
include_notification_url: bool = True,
include_success_url: bool = True,
) -> Path:
notification_url = (
' notification_url: "https://merchant.test/tbank/notification"\n'
if include_notification_url
else ""
)
success_url = (
' success_url: "https://merchant.test/payment/success"\n'
if include_success_url
else ""
)
config_file = tmp_path / "config.yaml"
config_file.write_text(
f"""
controller: {{}}
service: {{}}
business_logic:
provider_price_multiplier: 1.0
repository: {{}}
adapter: {{}}
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
{notification_url}{success_url} auth:
terminal_key: "test-terminal-key"
password: "test-password"
postgres:
dsn: "postgresql+asyncpg://postgres:postgres@localhost:5432/config_test"
address_suggestions: {{}}
observability:
service_name: "config-test"
otlp_endpoint: "http://collector:4317"
""".strip(),
encoding="utf-8",
)
return config_file
def test_configuration_sections_are_loaded_from_yaml_file(
monkeypatch: pytest.MonkeyPatch,
) -> None:
@@ -127,6 +177,11 @@ def test_configuration_sections_are_loaded_from_yaml_file(
assert settings.adapter.cdek_timeout_seconds == 10.0
assert settings.adapter.cdek_cache_ttl_seconds == 900
assert settings.tbank_payment.init_url == "https://securepay.tinkoff.ru/v2/Init"
assert (
settings.tbank_payment.notification_url
== "https://merchant.test/tbank/notification"
)
assert settings.tbank_payment.success_url == "https://merchant.test/payment/success"
assert settings.tbank_payment.auth.terminal_key == "test-terminal-key"
assert settings.tbank_payment.auth.password == "test-password"
assert settings.tbank_payment.timeout_seconds == 10.0
@@ -203,6 +258,10 @@ def test_get_settings_returns_cached_instance(monkeypatch: pytest.MonkeyPatch) -
assert first.service.provider_timeout_seconds == 10.0
assert first.business_logic.provider_price_multiplier == Decimal("1.0")
assert first.tbank_payment.auth.terminal_key == "test-terminal-key"
assert (
first.tbank_payment.notification_url
== "https://merchant.test/tbank/notification"
)
assert first.postgres.dsn.endswith("/g2s_aggregator_test")
assert first.address_suggestions.country_to_provider["RU"] == "dadata"
assert first.observability.service_name == "g2s-aggregator-test"
@@ -226,6 +285,11 @@ def test_get_settings_uses_config_test_yaml_in_pytest_environment(
assert settings.adapter.cdek_client_id == "test-id"
assert settings.tbank_payment.auth.terminal_key == "override-terminal-key"
assert settings.tbank_payment.auth.password == "override-password"
assert (
settings.tbank_payment.notification_url
== "https://override.test/tbank/notification"
)
assert settings.tbank_payment.success_url == "https://override.test/payment/success"
assert settings.tbank_payment.timeout_seconds == 4.25
assert settings.tbank_payment.retry_attempts == 1
assert settings.tbank_payment.retry_backoff_seconds == 0.05
@@ -283,6 +347,35 @@ def test_get_settings_fails_when_provider_price_multiplier_is_missing(
get_settings.cache_clear()
@pytest.mark.parametrize(
("include_notification_url", "include_success_url", "expected_location"),
(
(False, True, ("tbank_payment", "notification_url")),
(True, False, ("tbank_payment", "success_url")),
),
)
def test_get_settings_fails_when_tbank_payment_url_is_missing(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
include_notification_url: bool,
include_success_url: bool,
expected_location: tuple[str, str],
) -> None:
config_file = _write_tbank_url_validation_config(
tmp_path,
include_notification_url=include_notification_url,
include_success_url=include_success_url,
)
_use_runtime_config_files(monkeypatch, test_config_file=config_file)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert expected_location in locations
get_settings.cache_clear()
def test_get_settings_fails_when_observability_section_is_missing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
+2
View File
@@ -181,6 +181,8 @@ adapter:
tbank_payment:
init_url: "https://securepay.tinkoff.ru/v2/Init"
notification_url: "https://merchant.test/tbank/notification"
success_url: "https://merchant.test/payment/success"
auth:
terminal_key: "test-terminal-key"
password: "test-password"