Добавлена валидация цены
This commit is contained in:
@@ -15,7 +15,11 @@ from app.adapters.address_suggestions.base import (
|
||||
AddressSuggestionProvider,
|
||||
AddressSuggestionRequestError,
|
||||
)
|
||||
from app.adapters.delivery_providers.base import DeliveryProvider, ProviderRequestError
|
||||
from app.adapters.delivery_providers.base import (
|
||||
DeliveryProvider,
|
||||
ProviderClientError,
|
||||
ProviderRequestError,
|
||||
)
|
||||
from app.adapters.tbank.base import (
|
||||
TBankPaymentAdapterError,
|
||||
TBankPaymentNotificationTokenError,
|
||||
@@ -29,7 +33,9 @@ from app.domain.price import (
|
||||
DEFAULT_PROVIDER_PRICE_MULTIPLIER,
|
||||
DEFAULT_WEIGHT_ROUND_SCALE,
|
||||
NormalizedDeliveryRequest,
|
||||
calculate_expected_payment_amount_kopecks,
|
||||
filter_and_sort_prices,
|
||||
is_init_payment_price_valid,
|
||||
normalize_delivery_request,
|
||||
)
|
||||
from app.repositories.order import OrderData
|
||||
@@ -95,6 +101,13 @@ class PaymentAdapterProtocol(Protocol):
|
||||
) -> None: ...
|
||||
|
||||
|
||||
class PaymentPriceValidationAdapterProtocol(Protocol):
|
||||
async def get_payment_price(
|
||||
self,
|
||||
request: InitPaymentRequest,
|
||||
) -> DeliveryPrice | None: ...
|
||||
|
||||
|
||||
class OrderRegistrationAdapterProtocol(Protocol):
|
||||
async def register_order(self, request: InitPaymentRequest) -> str: ...
|
||||
|
||||
@@ -142,6 +155,9 @@ class AggregatorService:
|
||||
providers: Sequence[DeliveryProvider],
|
||||
cache: PriceCacheProtocol | None = None,
|
||||
payment_adapter: PaymentAdapterProtocol | None = None,
|
||||
payment_price_validation_adapter: (
|
||||
PaymentPriceValidationAdapterProtocol | None
|
||||
) = None,
|
||||
order_repository: OrderRepositoryProtocol | None = None,
|
||||
order_registration_adapter: OrderRegistrationAdapterProtocol | None = None,
|
||||
address_suggestion_providers: Sequence[AddressSuggestionProvider] = (),
|
||||
@@ -154,6 +170,7 @@ class AggregatorService:
|
||||
self._providers = tuple(providers)
|
||||
self._cache = cache
|
||||
self._payment_adapter = payment_adapter
|
||||
self._payment_price_validation_adapter = payment_price_validation_adapter
|
||||
self._order_repository = order_repository
|
||||
self._order_registration_adapter = order_registration_adapter
|
||||
self._weight_round_scale = weight_round_scale
|
||||
@@ -237,6 +254,8 @@ class AggregatorService:
|
||||
if self._payment_adapter is None:
|
||||
raise InitPaymentUnavailableError("Payment adapter is not configured.")
|
||||
|
||||
await self._validate_init_payment_price(request)
|
||||
|
||||
try:
|
||||
payment_url = await self._payment_adapter.create_payment_link(
|
||||
order_uuid=request.order_uuid,
|
||||
@@ -266,6 +285,85 @@ class AggregatorService:
|
||||
await self._persist_order(request=request, payment_url=payment_url)
|
||||
return InitPaymentResponse(payment_url=payment_url)
|
||||
|
||||
async def _validate_init_payment_price(
|
||||
self,
|
||||
request: InitPaymentRequest,
|
||||
) -> None:
|
||||
if self._payment_price_validation_adapter is None:
|
||||
raise InitPaymentUnavailableError(
|
||||
"Payment price validation adapter is not configured."
|
||||
)
|
||||
|
||||
try:
|
||||
provider_price = await self._payment_price_validation_adapter.get_payment_price(
|
||||
request
|
||||
)
|
||||
except ProviderRequestError as exc:
|
||||
logger.warning(
|
||||
"init_payment_price_validation_request_rejected",
|
||||
order_uuid=request.order_uuid,
|
||||
tariff_code=request.tariff_code,
|
||||
requested_price_kopecks=request.price,
|
||||
error=str(exc),
|
||||
)
|
||||
raise InvalidInitPaymentRequestError(
|
||||
"Payment init request is invalid for CDEK price validation."
|
||||
) from exc
|
||||
except ProviderClientError as exc:
|
||||
logger.warning(
|
||||
"init_payment_price_validation_unavailable",
|
||||
order_uuid=request.order_uuid,
|
||||
tariff_code=request.tariff_code,
|
||||
requested_price_kopecks=request.price,
|
||||
error=str(exc),
|
||||
)
|
||||
raise InitPaymentUnavailableError(
|
||||
"Payment price validation is temporarily unavailable."
|
||||
) from exc
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
"init_payment_price_validation_unexpected_error",
|
||||
order_uuid=request.order_uuid,
|
||||
tariff_code=request.tariff_code,
|
||||
requested_price_kopecks=request.price,
|
||||
)
|
||||
raise InitPaymentUnavailableError(
|
||||
"Payment price validation is temporarily unavailable."
|
||||
) from exc
|
||||
|
||||
if provider_price is None:
|
||||
logger.warning(
|
||||
"init_payment_price_validation_tariff_not_found",
|
||||
order_uuid=request.order_uuid,
|
||||
tariff_code=request.tariff_code,
|
||||
requested_price_kopecks=request.price,
|
||||
)
|
||||
raise InvalidInitPaymentRequestError(
|
||||
"CDEK did not return the requested tariff for payment validation."
|
||||
)
|
||||
|
||||
expected_amount_kopecks = calculate_expected_payment_amount_kopecks(
|
||||
provider_price,
|
||||
price_multiplier=self._provider_price_multiplier,
|
||||
)
|
||||
if not is_init_payment_price_valid(
|
||||
request.price,
|
||||
provider_price,
|
||||
price_multiplier=self._provider_price_multiplier,
|
||||
):
|
||||
logger.warning(
|
||||
"init_payment_price_mismatch",
|
||||
order_uuid=request.order_uuid,
|
||||
tariff_code=request.tariff_code,
|
||||
requested_price_kopecks=request.price,
|
||||
expected_price_kopecks=expected_amount_kopecks,
|
||||
provider_currency=getattr(provider_price, "currency", None),
|
||||
provider_price=str(getattr(provider_price, "price", None)),
|
||||
)
|
||||
raise InvalidInitPaymentRequestError(
|
||||
"Payment amount does not match CDEK validated delivery price."
|
||||
)
|
||||
|
||||
async def handle_tbank_payment_notification(
|
||||
self,
|
||||
notification: TBankPaymentNotification,
|
||||
|
||||
Reference in New Issue
Block a user