custom/plugins/KlarnaPayment/src/Components/EventListener/OrderChangeEventListener.php line 92

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace KlarnaPayment\Components\EventListener;
  4. use KlarnaPayment\Components\Client\ClientInterface;
  5. use KlarnaPayment\Components\Client\Hydrator\Request\UpdateAddress\UpdateAddressRequestHydratorInterface;
  6. use KlarnaPayment\Components\Client\Hydrator\Request\UpdateOrder\UpdateOrderRequestHydratorInterface;
  7. use KlarnaPayment\Components\Client\Request\UpdateOrderRequest;
  8. use KlarnaPayment\Components\Helper\OrderFetcherInterface;
  9. use KlarnaPayment\Components\Helper\OrderValidator\OrderValidatorInterface;
  10. use KlarnaPayment\Components\Helper\RequestHasherInterface;
  11. use KlarnaPayment\Components\PaymentHandler\AbstractKlarnaPaymentHandler;
  12. use KlarnaPayment\Core\Framework\ContextScope;
  13. use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressDefinition;
  14. use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemDefinition;
  15. use Shopware\Core\Checkout\Order\OrderDefinition;
  16. use Shopware\Core\Checkout\Order\OrderEntity;
  17. use Shopware\Core\Defaults;
  18. use Shopware\Core\Framework\Context;
  19. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\UpdateCommand;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PostWriteValidationEvent;
  22. use Shopware\Core\Framework\Validation\WriteConstraintViolationException;
  23. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  24. use Symfony\Component\Validator\ConstraintViolation;
  25. use Symfony\Component\Validator\ConstraintViolationList;
  26. use Symfony\Contracts\Translation\TranslatorInterface;
  27. class OrderChangeEventListener implements EventSubscriberInterface
  28. {
  29.     /** @var OrderFetcherInterface */
  30.     private $orderFetcher;
  31.     /** @var UpdateAddressRequestHydratorInterface */
  32.     private $addressRequestHydrator;
  33.     /** @var UpdateOrderRequestHydratorInterface */
  34.     private $orderRequestHydrator;
  35.     /** @var ClientInterface */
  36.     private $client;
  37.     /** @var EntityRepositoryInterface */
  38.     private $orderRepository;
  39.     /** @var TranslatorInterface */
  40.     private $translator;
  41.     /** @var RequestHasherInterface */
  42.     private $updateOrderRequestHasher;
  43.     /** @var RequestHasherInterface */
  44.     private $updateAddressRequestHasher;
  45.     /** @var OrderValidatorInterface */
  46.     private $orderValidator;
  47.     public function __construct(
  48.         OrderFetcherInterface $orderFetcher,
  49.         UpdateAddressRequestHydratorInterface $addressRequestHydrator,
  50.         UpdateOrderRequestHydratorInterface $orderRequestHydrator,
  51.         ClientInterface $client,
  52.         EntityRepositoryInterface $orderRepository,
  53.         TranslatorInterface $translator,
  54.         RequestHasherInterface $updateOrderRequestHasher,
  55.         RequestHasherInterface $updateAddressRequestHasher,
  56.         OrderValidatorInterface $orderValidator
  57.     ) {
  58.         $this->orderFetcher               $orderFetcher;
  59.         $this->addressRequestHydrator     $addressRequestHydrator;
  60.         $this->orderRequestHydrator       $orderRequestHydrator;
  61.         $this->client                     $client;
  62.         $this->orderRepository            $orderRepository;
  63.         $this->translator                 $translator;
  64.         $this->updateOrderRequestHasher   $updateOrderRequestHasher;
  65.         $this->updateAddressRequestHasher $updateAddressRequestHasher;
  66.         $this->orderValidator             $orderValidator;
  67.     }
  68.     public static function getSubscribedEvents(): array
  69.     {
  70.         return [
  71.             PostWriteValidationEvent::class => 'validateKlarnaOrder',
  72.         ];
  73.     }
  74.     /**
  75.      * @see \KlarnaPayment\Components\Controller\Administration\OrderUpdateController::update Change accordingly to keep functionality synchronized
  76.      */
  77.     public function validateKlarnaOrder(PostWriteValidationEvent $event): void
  78.     {
  79.         if ($event->getContext()->getScope() === ContextScope::INTERNAL_SCOPE) {
  80.             // only check user generated changes
  81.             return;
  82.         }
  83.         if ($event->getContext()->getVersionId() !== Defaults::LIVE_VERSION) {
  84.             // No live data change, just draft versions
  85.             return;
  86.         }
  87.         $order $this->getOrderFromWriteCommands($event);
  88.         if (null === $order || !$this->orderValidator->isKlarnaOrder($order)) {
  89.             return;
  90.         }
  91.         $this->validateOrderAddress($order$event);
  92.         $this->validateLineItems($order$event);
  93.     }
  94.     private function validateLineItems(OrderEntity $orderEntityPostWriteValidationEvent $event): void
  95.     {
  96.         $request      $this->orderRequestHydrator->hydrate($orderEntity$event->getContext());
  97.         $customFields $orderEntity->getCustomFields();
  98.         $hashVersion $customFields['klarna_order_cart_hash_version'] ?? AbstractKlarnaPaymentHandler::CART_HASH_DEFAULT_VERSION;
  99.         $hash        $this->updateOrderRequestHasher->getHash($request$hashVersion);
  100.         if (!empty($customFields['klarna_order_cart_hash'])) {
  101.             $currentHash $customFields['klarna_order_cart_hash'];
  102.             if ($hash === $currentHash) {
  103.                 if ($hashVersion !== AbstractKlarnaPaymentHandler::CART_HASH_CURRENT_VERSION) {
  104.                     $this->updateOrderCartHash($request$orderEntity$event->getContext());
  105.                 }
  106.                 return;
  107.             }
  108.         }
  109.         $response $this->client->request($request$event->getContext());
  110.         if ($response->getHttpStatus() === 204) {
  111.             $this->updateOrderCartHash($request$orderEntity$event->getContext());
  112.             return;
  113.         }
  114.         $violation = new ConstraintViolation(
  115.             $this->translator->trans('KlarnaPayment.errorMessages.lineItemChangeDeclined'),
  116.             '',
  117.             [],
  118.             '',
  119.             '',
  120.             ''
  121.         );
  122.         $violations = new ConstraintViolationList([$violation]);
  123.         $event->getExceptions()->add(new WriteConstraintViolationException($violations));
  124.     }
  125.     private function validateOrderAddress(OrderEntity $orderEntityPostWriteValidationEvent $event): void
  126.     {
  127.         $request $this->addressRequestHydrator->hydrate($orderEntity$event->getContext());
  128.         $hash $this->updateAddressRequestHasher->getHash($request);
  129.         if (!empty($orderEntity->getCustomFields()['klarna_order_address_hash'])) {
  130.             $currentHash $orderEntity->getCustomFields()['klarna_order_address_hash'];
  131.             if ($hash === $currentHash) {
  132.                 return;
  133.             }
  134.         }
  135.         $response $this->client->request($request$event->getContext());
  136.         if ($response->getHttpStatus() === 204) {
  137.             $this->saveOrderAddressHash($hash$orderEntity$event->getContext());
  138.             return;
  139.         }
  140.         $violation = new ConstraintViolation(
  141.             $this->translator->trans('KlarnaPayment.errorMessages.addressChangeDeclined'),
  142.             '',
  143.             [],
  144.             '',
  145.             '',
  146.             ''
  147.         );
  148.         $violations = new ConstraintViolationList([$violation]);
  149.         $event->getExceptions()->add(new WriteConstraintViolationException($violations));
  150.     }
  151.     private function getOrderFromWriteCommands(PostWriteValidationEvent $event): ?OrderEntity
  152.     {
  153.         foreach ($event->getCommands() as $command) {
  154.             if (!($command instanceof UpdateCommand)) {
  155.                 continue;
  156.             }
  157.             $primaryKeys $command->getPrimaryKey();
  158.             if (!array_key_exists('id'$primaryKeys) || empty($primaryKeys['id'])) {
  159.                 continue;
  160.             }
  161.             if ($command->getDefinition()->getClass() === OrderAddressDefinition::class) {
  162.                 return $this->orderFetcher->getOrderFromOrderAddress($primaryKeys['id'], $event->getContext());
  163.             }
  164.             if ($command->getDefinition()->getClass() === OrderLineItemDefinition::class) {
  165.                 return $this->orderFetcher->getOrderFromOrderLineItem($primaryKeys['id'], $event->getContext());
  166.             }
  167.             if ($command->getDefinition()->getClass() === OrderDefinition::class) {
  168.                 return $this->orderFetcher->getOrderFromOrder($primaryKeys['id'], $event->getContext());
  169.             }
  170.         }
  171.         return null;
  172.     }
  173.     private function saveOrderAddressHash(string $hashOrderEntity $orderEntityContext $context): void
  174.     {
  175.         $customFields $orderEntity->getCustomFields();
  176.         $customFields['klarna_order_address_hash'] = $hash;
  177.         $update = [
  178.             'id'           => $orderEntity->getId(),
  179.             'customFields' => $customFields,
  180.         ];
  181.         $context->scope(ContextScope::INTERNAL_SCOPE, function (Context $context) use ($update): void {
  182.             $this->orderRepository->upsert([$update], $context);
  183.         });
  184.     }
  185.     private function updateOrderCartHash(UpdateOrderRequest $requestOrderEntity $orderEntityContext $context): void
  186.     {
  187.         $customFields $orderEntity->getCustomFields();
  188.         $newHash $this->updateOrderRequestHasher->getHash($requestAbstractKlarnaPaymentHandler::CART_HASH_CURRENT_VERSION);
  189.         $customFields['klarna_order_cart_hash']         = $newHash;
  190.         $customFields['klarna_order_cart_hash_version'] = AbstractKlarnaPaymentHandler::CART_HASH_CURRENT_VERSION;
  191.         $update = [
  192.             'id'           => $orderEntity->getId(),
  193.             'customFields' => $customFields,
  194.         ];
  195.         $context->scope(ContextScope::INTERNAL_SCOPE, function (Context $context) use ($update): void {
  196.             $this->orderRepository->upsert([$update], $context);
  197.         });
  198.     }
  199. }