Добавлен провайдер доставки CSE: SOAP-адаптер (Calc + SaveDocuments), маршрутизация init-payment по провайдеру, обобщение tariff_code до строки, география CSE в cities_map
Deploy / deploy (push) Failing after 52s

This commit is contained in:
Раис Юсупалиев
2026-05-31 20:13:52 +03:00
parent 6a2bf05ba5
commit 6c0f97adf6
41 changed files with 16113 additions and 4806 deletions
+96 -44
View File
@@ -22,9 +22,6 @@ from app.adapters.delivery_providers.base import (
ProviderClientError,
ProviderRequestError,
)
from app.adapters.delivery_providers.cdek.order_mapper import (
CDEKOrderRegistrationResult,
)
from app.adapters.tbank.base import (
TBankPaymentAdapterError,
TBankPaymentNotificationTokenError,
@@ -121,7 +118,7 @@ class PaymentPriceValidationAdapterProtocol(Protocol):
class OrderRegistrationAdapterProtocol(Protocol):
async def register_order(
self, request: InitPaymentRequest, order_uuid: str
) -> CDEKOrderRegistrationResult: ...
) -> object: ...
class OrderRepositoryProtocol(Protocol):
@@ -150,6 +147,13 @@ class OrderRepositoryProtocol(Protocol):
cdek_order_uuid: str,
) -> object | None: ...
async def mark_cse_order_registered(
self,
session: object,
order_uuid: str,
cse_order_number: str,
) -> object | None: ...
class EmailSenderProtocol(Protocol):
async def send_email(
@@ -177,11 +181,13 @@ class AggregatorService:
providers: Sequence[DeliveryProvider],
cache: PriceCacheProtocol | None = None,
payment_adapter: PaymentAdapterProtocol | None = None,
payment_price_validation_adapter: (
PaymentPriceValidationAdapterProtocol | None
payment_price_validation_adapters: (
Mapping[str, PaymentPriceValidationAdapterProtocol] | None
) = None,
order_repository: OrderRepositoryProtocol | None = None,
order_registration_adapter: OrderRegistrationAdapterProtocol | None = None,
order_registration_adapters: (
Mapping[str, OrderRegistrationAdapterProtocol] | None
) = None,
email_sender: EmailSenderProtocol | None = None,
address_suggestion_providers: Sequence[AddressSuggestionProvider] = (),
address_suggestion_country_to_provider: Mapping[str, str] | None = None,
@@ -194,9 +200,11 @@ class AggregatorService:
self._providers = tuple(providers)
self._cache = cache
self._payment_adapter = payment_adapter
self._payment_price_validation_adapter = payment_price_validation_adapter
self._payment_price_validation_adapters = dict(
payment_price_validation_adapters or {}
)
self._order_repository = order_repository
self._order_registration_adapter = order_registration_adapter
self._order_registration_adapters = dict(order_registration_adapters or {})
self._email_sender = email_sender
self._weight_round_scale = weight_round_scale
self._provider_price_multiplier = provider_price_multiplier
@@ -323,17 +331,22 @@ class AggregatorService:
request: InitPaymentRequest,
order_uuid: str,
) -> None:
if self._payment_price_validation_adapter is None:
raise InitPaymentUnavailableError(
"Payment price validation adapter is not configured."
provider = request.system_data.tariff.provider
validation_adapter = self._payment_price_validation_adapters.get(provider)
if validation_adapter is None:
logger.warning(
"init_payment_price_validation_provider_not_configured",
order_uuid=order_uuid,
provider=provider,
)
raise InvalidInitPaymentRequestError(
"Payment price validation is not configured for the tariff provider."
)
tariff_code = request.system_data.tariff.tariff_code
requested_price = request.system_data.tariff.price
try:
provider_price = await self._payment_price_validation_adapter.get_payment_price(
request
)
provider_price = await validation_adapter.get_payment_price(request)
except ProviderRequestError as exc:
logger.warning(
"init_payment_price_validation_request_rejected",
@@ -430,16 +443,17 @@ class AggregatorService:
if action is TBankPaymentNotificationAction.ACKNOWLEDGE_ONLY:
return "OK"
existing_cdek_order_uuid = getattr(order, "cdek_order_uuid", None)
if existing_cdek_order_uuid:
provider = self._resolve_order_provider(order)
if self._has_existing_registration(order, provider):
return "OK"
registration_result = await self._register_cdek_order(
order, order_uuid=notification.OrderId
registration_result = await self._register_provider_order(
order, order_uuid=notification.OrderId, provider=provider
)
await self._save_cdek_order_uuid(
await self._save_order_registration(
order_uuid=notification.OrderId,
cdek_order_uuid=registration_result.order_uuid,
provider=provider,
result=registration_result,
)
await self._send_payment_confirmation_email(
order, order_uuid=notification.OrderId
@@ -543,65 +557,102 @@ class AggregatorService:
"TBank payment notification order update failed."
) from exc
async def _register_cdek_order(
self, order: object, *, order_uuid: str
) -> CDEKOrderRegistrationResult:
if self._order_registration_adapter is None:
@staticmethod
def _resolve_order_provider(order: object) -> str:
provider = getattr(order, "provider", None)
if isinstance(provider, str) and provider:
return provider
request = AggregatorService._to_init_payment_request_from_order(order)
return request.system_data.tariff.provider
@staticmethod
def _has_existing_registration(order: object, provider: str) -> bool:
if provider == "cdek":
return bool(getattr(order, "cdek_order_uuid", None))
if provider == "cse":
return bool(getattr(order, "cse_order_number", None))
return False
@staticmethod
def _extract_registration_id(result: object) -> str:
for attr in ("order_uuid", "order_number"):
value = getattr(result, attr, None)
if isinstance(value, str) and value:
return value
raise TBankPaymentNotificationProcessingError(
"Order registration result is missing an identifier."
)
async def _register_provider_order(
self, order: object, *, order_uuid: str, provider: str
) -> object:
registration_adapter = self._order_registration_adapters.get(provider)
if registration_adapter is None:
raise TBankPaymentNotificationProcessingError(
"CDEK order registration adapter is not configured."
"Order registration adapter is not configured for the provider."
)
try:
request = self._to_init_payment_request_from_order(order)
return await self._order_registration_adapter.register_order(
request, order_uuid
)
return await registration_adapter.register_order(request, order_uuid)
except Exception as exc:
logger.exception(
"cdek_order_registration_failed",
"provider_order_registration_failed",
provider=provider,
order_uuid=getattr(order, "order_uuid", None),
)
raise TBankPaymentNotificationProcessingError(
"CDEK order registration failed."
"Provider order registration failed."
) from exc
async def _save_cdek_order_uuid(
async def _save_order_registration(
self,
*,
order_uuid: str,
cdek_order_uuid: str,
provider: str,
result: object,
) -> None:
if self._order_repository is None:
raise TBankPaymentNotificationProcessingError(
"Order repository is not configured."
)
registration_id = self._extract_registration_id(result)
try:
async with self._order_repository.session() as session:
order = await self._order_repository.mark_cdek_order_registered(
session,
order_uuid,
cdek_order_uuid,
)
if provider == "cse":
order = await self._order_repository.mark_cse_order_registered(
session,
order_uuid,
registration_id,
)
else:
order = await self._order_repository.mark_cdek_order_registered(
session,
order_uuid,
registration_id,
)
if order is None:
logger.warning(
"cdek_order_uuid_order_not_found",
"order_registration_order_not_found",
provider=provider,
order_uuid=order_uuid,
cdek_order_uuid=cdek_order_uuid,
registration_id=registration_id,
)
raise TBankPaymentNotificationProcessingError(
"Order was not found while saving CDEK order UUID."
"Order was not found while saving registration identifier."
)
except TBankPaymentNotificationProcessingError:
raise
except Exception as exc:
logger.exception(
"cdek_order_uuid_persistence_failed",
"order_registration_persistence_failed",
provider=provider,
order_uuid=order_uuid,
cdek_order_uuid=cdek_order_uuid,
registration_id=registration_id,
)
raise TBankPaymentNotificationProcessingError(
"CDEK order UUID persistence failed."
"Order registration persistence failed."
) from exc
async def _persist_order(
@@ -642,6 +693,7 @@ class AggregatorService:
payment_url=payment_url,
price=request.system_data.tariff.price,
tariff_code=request.system_data.tariff.tariff_code,
provider=request.system_data.tariff.provider,
account_email=request.account_email,
payload=request.model_dump(mode="json", by_alias=True),
)