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

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\EntityRepository;
  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 EntityRepository */
  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.     /** @var string */
  48.     private $currentLocale;
  49.     public function __construct(
  50.         OrderFetcherInterface $orderFetcher,
  51.         UpdateAddressRequestHydratorInterface $addressRequestHydrator,
  52.         UpdateOrderRequestHydratorInterface $orderRequestHydrator,
  53.         ClientInterface $client,
  54.         EntityRepository $orderRepository,
  55.         TranslatorInterface $translator,
  56.         RequestHasherInterface $updateOrderRequestHasher,
  57.         RequestHasherInterface $updateAddressRequestHasher,
  58.         OrderValidatorInterface $orderValidator
  59.     ) {
  60.         $this->orderFetcher               $orderFetcher;
  61.         $this->addressRequestHydrator     $addressRequestHydrator;
  62.         $this->orderRequestHydrator       $orderRequestHydrator;
  63.         $this->client                     $client;
  64.         $this->orderRepository            $orderRepository;
  65.         $this->translator                 $translator;
  66.         $this->updateOrderRequestHasher   $updateOrderRequestHasher;
  67.         $this->updateAddressRequestHasher $updateAddressRequestHasher;
  68.         $this->orderValidator             $orderValidator;
  69.     }
  70.     public static function getSubscribedEvents(): array
  71.     {
  72.         return [
  73.             PostWriteValidationEvent::class => 'validateKlarnaOrder',
  74.         ];
  75.     }
  76.     /**
  77.      * @see \KlarnaPayment\Components\Controller\Administration\OrderUpdateController::update Change accordingly to keep functionality synchronized
  78.      */
  79.     public function validateKlarnaOrder(PostWriteValidationEvent $event): void
  80.     {
  81.         if ($event->getContext()->getScope() === ContextScope::INTERNAL_SCOPE) {
  82.             // only check user generated changes
  83.             return;
  84.         }
  85.         if ($event->getContext()->getVersionId() !== Defaults::LIVE_VERSION) {
  86.             // No live data change, just draft versions
  87.             return;
  88.         }
  89.         $order $this->getOrderFromWriteCommands($event);
  90.         if ($order === null || !$this->orderValidator->isKlarnaOrder($order)) {
  91.             return;
  92.         }
  93.         $this->setCurrentLocale($event);
  94.         $this->validateOrderAddress($order$event);
  95.         $this->validateLineItems($order$event);
  96.     }
  97.     private function setCurrentLocale(PostWriteValidationEvent $event): void
  98.     {
  99.         $languages           $event->getWriteContext()->getLanguages();
  100.         $this->currentLocale $languages[$event->getContext()->getLanguageId()]['code'] ?? 'en-GB';
  101.     }
  102.     private function validateLineItems(OrderEntity $orderEntityPostWriteValidationEvent $event): void
  103.     {
  104.         $request      $this->orderRequestHydrator->hydrate($orderEntity$event->getContext());
  105.         $customFields $orderEntity->getCustomFields();
  106.         $hashVersion $customFields['klarna_order_cart_hash_version'] ?? AbstractKlarnaPaymentHandler::CART_HASH_DEFAULT_VERSION;
  107.         $hash        $this->updateOrderRequestHasher->getHash($request$hashVersion);
  108.         if (!empty($customFields['klarna_order_cart_hash'])) {
  109.             $currentHash $customFields['klarna_order_cart_hash'];
  110.             if ($hash === $currentHash) {
  111.                 if ($hashVersion !== AbstractKlarnaPaymentHandler::CART_HASH_CURRENT_VERSION) {
  112.                     $this->updateOrderCartHash($request$orderEntity$event->getContext());
  113.                 }
  114.                 return;
  115.             }
  116.         }
  117.         $response $this->client->request($request$event->getContext());
  118.         if ($response->getHttpStatus() === 204) {
  119.             $this->updateOrderCartHash($request$orderEntity$event->getContext());
  120.             return;
  121.         }
  122.         $violation = new ConstraintViolation(
  123.             $this->translator->trans('KlarnaPayment.errorMessages.lineItemChangeDeclined', [], null$this->currentLocale),
  124.             '',
  125.             [],
  126.             '',
  127.             '',
  128.             ''
  129.         );
  130.         $violations = new ConstraintViolationList([$violation]);
  131.         $event->getExceptions()->add(new WriteConstraintViolationException($violations));
  132.     }
  133.     private function validateOrderAddress(OrderEntity $orderEntityPostWriteValidationEvent $event): void
  134.     {
  135.         $request $this->addressRequestHydrator->hydrate($orderEntity$event->getContext());
  136.         $hash $this->updateAddressRequestHasher->getHash($request);
  137.         if (!empty($orderEntity->getCustomFields()['klarna_order_address_hash'])) {
  138.             $currentHash $orderEntity->getCustomFields()['klarna_order_address_hash'];
  139.             if ($hash === $currentHash) {
  140.                 return;
  141.             }
  142.         }
  143.         $response $this->client->request($request$event->getContext());
  144.         if ($response->getHttpStatus() === 204) {
  145.             $this->saveOrderAddressHash($hash$orderEntity$event->getContext());
  146.             return;
  147.         }
  148.         $violation = new ConstraintViolation(
  149.             $this->translator->trans('KlarnaPayment.errorMessages.addressChangeDeclined', [], null$this->currentLocale),
  150.             '',
  151.             [],
  152.             '',
  153.             '',
  154.             ''
  155.         );
  156.         $violations = new ConstraintViolationList([$violation]);
  157.         $event->getExceptions()->add(new WriteConstraintViolationException($violations));
  158.     }
  159.     private function getOrderFromWriteCommands(PostWriteValidationEvent $event): ?OrderEntity
  160.     {
  161.         foreach ($event->getCommands() as $command) {
  162.             if (!($command instanceof UpdateCommand)) {
  163.                 continue;
  164.             }
  165.             $primaryKeys $command->getPrimaryKey();
  166.             if (!array_key_exists('id'$primaryKeys) || empty($primaryKeys['id'])) {
  167.                 continue;
  168.             }
  169.             if ($command->getDefinition()->getClass() === OrderAddressDefinition::class) {
  170.                 return $this->orderFetcher->getOrderFromOrderAddress($primaryKeys['id'], $event->getContext());
  171.             }
  172.             if ($command->getDefinition()->getClass() === OrderLineItemDefinition::class) {
  173.                 return $this->orderFetcher->getOrderFromOrderLineItem($primaryKeys['id'], $event->getContext());
  174.             }
  175.             if ($command->getDefinition()->getClass() === OrderDefinition::class) {
  176.                 return $this->orderFetcher->getOrderFromOrder($primaryKeys['id'], $event->getContext());
  177.             }
  178.         }
  179.         return null;
  180.     }
  181.     private function saveOrderAddressHash(string $hashOrderEntity $orderEntityContext $context): void
  182.     {
  183.         $customFields $orderEntity->getCustomFields();
  184.         $customFields['klarna_order_address_hash'] = $hash;
  185.         $update = [
  186.             'id'           => $orderEntity->getId(),
  187.             'customFields' => $customFields,
  188.         ];
  189.         $context->scope(ContextScope::INTERNAL_SCOPE, function (Context $context) use ($update): void {
  190.             $this->orderRepository->upsert([$update], $context);
  191.         });
  192.     }
  193.     private function updateOrderCartHash(UpdateOrderRequest $requestOrderEntity $orderEntityContext $context): void
  194.     {
  195.         $customFields $orderEntity->getCustomFields();
  196.         $newHash $this->updateOrderRequestHasher->getHash($requestAbstractKlarnaPaymentHandler::CART_HASH_CURRENT_VERSION);
  197.         $customFields['klarna_order_cart_hash']         = $newHash;
  198.         $customFields['klarna_order_cart_hash_version'] = AbstractKlarnaPaymentHandler::CART_HASH_CURRENT_VERSION;
  199.         $update = [
  200.             'id'           => $orderEntity->getId(),
  201.             'customFields' => $customFields,
  202.         ];
  203.         $context->scope(ContextScope::INTERNAL_SCOPE, function (Context $context) use ($update): void {
  204.             $this->orderRepository->upsert([$update], $context);
  205.         });
  206.     }
  207. }