vendor/lnb/shopware6-fraud-prevention/src/Subscriber/CartConvertedSubscriber.php line 52

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Lnb\Shopware6\FraudPrevention\Subscriber;
  4. use Lnb\Shopware6\FraudPrevention\Checkout\FraudPreventionCheckoutContext;
  5. use Lnb\Shopware6\FraudPrevention\Checkout\FraudPreventionCheckoutContextFactory;
  6. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\AddressIsPackstation\AddressIsPackstation;
  7. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\CustomerGroup\CustomerGroup;
  8. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\CustomerPhone\CustomerPhone;
  9. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\FraudContextValidation;
  10. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\LineItemCount\LineItemCount;
  11. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\OrderInPeriod\OrderInPeriod;
  12. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\QuantityValidator\LineItemQuantity;
  13. use Lnb\Shopware6\FraudPrevention\Checkout\Validation\ZipCode\Zipcode;
  14. use Lnb\Shopware6\FraudPrevention\Installer\StateInstaller;
  15. use Shopware\Core\Checkout\Cart\Order\CartConvertedEvent;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. use Symfony\Component\Validator\Constraint;
  18. use Symfony\Component\Validator\ConstraintViolation;
  19. use Symfony\Component\Validator\ConstraintViolationInterface;
  20. use Symfony\Component\Validator\ConstraintViolationListInterface;
  21. use Symfony\Component\Validator\Validator\ValidatorInterface;
  22. class CartConvertedSubscriber implements EventSubscriberInterface
  23. {
  24.     private FraudPreventionCheckoutContextFactory $checkoutStructFactory;
  25.     private ValidatorInterface $validator;
  26.     /**
  27.      * @var array<string>
  28.      */
  29.     private array $logMessage = [];
  30.     public function __construct(
  31.         FraudPreventionCheckoutContextFactory $checkoutStructFactory,
  32.         ValidatorInterface $validator
  33.     ) {
  34.         $this->checkoutStructFactory $checkoutStructFactory;
  35.         $this->validator $validator;
  36.     }
  37.     public static function getSubscribedEvents(): array
  38.     {
  39.         return [
  40.             CartConvertedEvent::class => 'onCartConvertedEvent',
  41.         ];
  42.     }
  43.     public function onCartConvertedEvent(CartConvertedEvent $event): void
  44.     {
  45.         $this->validator->startContext();
  46.         $this->logMessage = [];
  47.         $checkoutFraudStruct $this->checkoutStructFactory->createFromCartConvertedEvent($event);
  48.         $fraudContextValidation = new FraudContextValidation($checkoutFraudStruct);
  49.         if ($fraudContextValidation->hasFreePartnerMeetingRegistration()) {
  50.             $this->setOrderComplete($event$checkoutFraudStruct);
  51.             return;
  52.         }
  53.         //check countries on black list
  54.         if ($fraudContextValidation->hasCountryOnBlacklist()) {
  55.             $this->logError('The country is on blacklist''EmailBlackList');
  56.             $this->setFraudPreventedState($event$checkoutFraudStruct);
  57.         }
  58.         // check if transaction is open and payment has to be checked
  59.         if ($fraudContextValidation->isPaymentStateOpenAndHasToBeChecked()) {
  60.             $this->logError('The PaymentState is open and the PaymentMethod is configured to be checked''PaymentStateOpen');
  61.             $this->setFraudPreventedState($event$checkoutFraudStruct);
  62.         }
  63.         //fraud prevention is disabled
  64.         if (! $checkoutFraudStruct->getConfigServiceAdapter()->isFraudPreventionActive()) {
  65.             $this->saveLog($event);
  66.             return;
  67.         }
  68.         $validationBeforePrice $this->validator->validate(
  69.             $checkoutFraudStruct,
  70.             [
  71.                 new CustomerGroup(),
  72.                 new OrderInPeriod(),
  73.                 new LineItemQuantity(),
  74.                 new LineItemCount(),
  75.             ]
  76.         );
  77.         // check if paymentMethod has not to be checked extensive
  78.         if (! $fraudContextValidation->hasPaymentMethodForExtensiveCheck()) {
  79.             $this->saveValidatorLog($validationBeforePrice$event$checkoutFraudStruct);
  80.             return;
  81.         }
  82.         $validationBeforePrice->addAll($this->validator->validate(
  83.             $checkoutFraudStruct,
  84.             new Zipcode()
  85.         ));
  86.         $this->saveValidatorLog($validationBeforePrice$event$checkoutFraudStruct);
  87.         if ($checkoutFraudStruct->getPrice()->getTotalPrice() <= $checkoutFraudStruct->getConfigServiceAdapter()->getMiddleOrderPrice()) {
  88.             $this->saveLog($event);
  89.             return;
  90.         }
  91.         if ($checkoutFraudStruct->getPrice()->getTotalPrice() >= $checkoutFraudStruct->getConfigServiceAdapter()->getMaximalOrderPrice()) {
  92.             $this->logError('Total price is over maximal order price''TotalPriceOverMaximum');
  93.             $this->setFraudPreventedState($event$checkoutFraudStruct);
  94.             $this->saveLog($event);
  95.             return;
  96.         }
  97.         if (! $fraudContextValidation->hasEmailOnBlacklist()) {
  98.             $this->saveLog($event);
  99.             return;
  100.         }
  101.         $this->logError('Email domain on blacklist''emailDomainOnBlacklist');
  102.         $validationAfterPrice $this->validator->validate(
  103.             $checkoutFraudStruct,
  104.             [
  105.                 new CustomerPhone(),
  106.                 new AddressIsPackstation(),
  107.             ]
  108.         );
  109.         $this->saveValidatorLog($validationAfterPrice$event$checkoutFraudStruct);
  110.     }
  111.     protected function setFraudPreventedState(CartConvertedEvent $eventFraudPreventionCheckoutContext $context): void
  112.     {
  113.         if (empty($this->logMessage)) {
  114.             throw new \RuntimeException('Order ist set to fraud without a reason');
  115.         }
  116.         $converted $event->getConvertedCart();
  117.         $converted['stateId'] = strtolower(StateInstaller::FRAUD_PREVENTED_STATE_ID);
  118.         //todo check if a custom tag can be set to the order
  119.         //@todo add test for this
  120.         $tagId $context->getFraudOrderTag(); //@todo get tag id from config
  121.         if (! empty($tagId)) {
  122.             $converted['tags'] = [
  123.                 [
  124.                     'id' => $tagId,
  125.                 ],
  126.             ];
  127.         }
  128.         $event->setConvertedCart($converted);
  129.     }
  130.     protected function logError(string $messagestring $key null): void
  131.     {
  132.         if ($key === null) {
  133.             $this->logMessage[] = $message;
  134.             return;
  135.         }
  136.         $this->logMessage[$key] = $message;
  137.     }
  138.     protected function saveLog(CartConvertedEvent $event): void
  139.     {
  140.         if (empty($this->logMessage)) {
  141.             return;
  142.         }
  143.         $converted $event->getConvertedCart();
  144.         $converted['customFields']['fraudLog'] = $this->logMessage;
  145.         $event->setConvertedCart($converted);
  146.     }
  147.     private function setOrderComplete(CartConvertedEvent $eventFraudPreventionCheckoutContext $checkoutFraudStruct): void
  148.     {
  149.         $converted $event->getConvertedCart();
  150.         $openState $checkoutFraudStruct->getOrderCompleteStateId();
  151.         $converted['stateId'] = strtolower($openState);
  152.         $event->setConvertedCart($converted);
  153.     }
  154.     private function saveValidatorLog(ConstraintViolationListInterface $violationListCartConvertedEvent $eventFraudPreventionCheckoutContext $context): void
  155.     {
  156.         if ($violationList->count() > 0) {
  157.             foreach ($violationList as $error) {
  158.                 $this->logError((string) $error->getMessage(), $this->getConstraintNameOfViolation($error));
  159.             }
  160.             $this->setFraudPreventedState($event$context);
  161.         }
  162.         $this->saveLog($event);
  163.     }
  164.     private function getConstraintNameOfViolation(ConstraintViolationInterface $error): ?string
  165.     {
  166.         if ($error instanceof ConstraintViolation && $error->getConstraint() instanceof Constraint) {
  167.             return (new \ReflectionClass(get_class($error->getConstraint())))->getShortName();
  168.         }
  169.         return null;
  170.     }
  171. }