diff --git a/app/code/Riskified/Decider/Api/Config.php b/app/code/Riskified/Decider/Api/Config.php index 67e3d75..afcefbd 100644 --- a/app/code/Riskified/Decider/Api/Config.php +++ b/app/code/Riskified/Decider/Api/Config.php @@ -275,6 +275,16 @@ public function getDeclineNotificationContent($scopeId = 0) ); } + /** + * @return bool + */ + public function getCustomerLoginHandleEnabled() + { + return (bool) $this->_scopeConfig->getValue( + 'riskified/riskified/connect_customer' + ); + } + /** * Sets store id. * @param $id diff --git a/app/code/Riskified/Decider/Api/Data/ClientDetailsInterface.php b/app/code/Riskified/Decider/Api/Data/ClientDetailsInterface.php new file mode 100644 index 0000000..286ee93 --- /dev/null +++ b/app/code/Riskified/Decider/Api/Data/ClientDetailsInterface.php @@ -0,0 +1,8 @@ +localeResolver = $localeResolver; + $this->httpHeader = $httpHeader; + } + + /** + * @return array + */ + public function getData() + { + return [ + 'accept_language' => $this->localeResolver->getLocale(), + 'user_agent' => $this->httpHeader->getHttpUserAgent() + ]; + } + + /** + * @return array + */ + public function getCleanData() + { + return array_filter($this->getData(), 'strlen'); + } +} diff --git a/app/code/Riskified/Decider/Model/Api/Data/SessionDetails.php b/app/code/Riskified/Decider/Model/Api/Data/SessionDetails.php new file mode 100644 index 0000000..e7c9a65 --- /dev/null +++ b/app/code/Riskified/Decider/Model/Api/Data/SessionDetails.php @@ -0,0 +1,55 @@ +session = $sessionManager; + $this->remoteAddress = $remoteAddress; + $this->httpHeader = $httpHeader; + } + + /** + * @return array + */ + public function getData() + { + $userAgent = $this->httpHeader->getHttpUserAgent(); + $isMobile = \Zend_Http_UserAgent_Mobile::match($userAgent, $_SERVER); + + return [ + 'created_at' => $this->formatDateAsIso8601(date('Y-m-d H:i:s')), + 'cart_token' => $this->session->getSessionId(), + 'browser_ip' => $this->remoteAddress->getRemoteAddress(), + 'source' => $isMobile ? 'mobile_web' : 'desktop_web' + ]; + } + + /** + * @return array + */ + public function getCleanData() + { + return array_filter($this->getData(), 'strlen'); + } +} diff --git a/app/code/Riskified/Decider/Model/DateFormatter.php b/app/code/Riskified/Decider/Model/DateFormatter.php new file mode 100644 index 0000000..dde083e --- /dev/null +++ b/app/code/Riskified/Decider/Model/DateFormatter.php @@ -0,0 +1,15 @@ +customerRepository = $customerRepository; + $this->clientDetails = $clientDetails; + $this->sessionDetails = $sessionDetails; + $this->api = $api; + $this->config = $config; + $this->log = $log; + } + + /** + * @param $subject + * @param \Closure $proceed + * @param mixed ...$args + * @throws EmailNotConfirmedException + * @throws InvalidEmailOrPasswordException + * @throws UserLockedException + */ + public function aroundAuthenticate($subject, \Closure $proceed, ...$args) + { + try { + $this->inputData = $args; + + $result = $proceed(...$args); + } catch (InvalidEmailOrPasswordException $e) { + $this + ->prepareFailedLoginCustomerObject($e) + ->callApi(); + + throw $e; + } catch (EmailNotConfirmedException $e) { + $this + ->prepareFailedLoginCustomerObject($e) + ->callApi(); + + throw $e; + } catch (UserLockedException $e) { + $this + ->prepareFailedLoginCustomerObject($e) + ->callApi(); + + throw $e; + } + + $this + ->prepareSuccessfulLoginCustomerObject() + ->callApi(); + + return $result; + } + + /** + * @return $this + */ + private function prepareSuccessfulLoginCustomerObject() + { + if (!$this->isEnabled()) { + return $this; + } + + return $this->prepareCustomerObject(true); + } + + /** + * @param \Exception $exception + * @return $this + */ + private function prepareFailedLoginCustomerObject(\Exception $exception) + { + if (!$this->isEnabled()) { + return $this; + } + + $type = false; + if ($exception instanceof InvalidEmailOrPasswordException) { + $type = 'wrong password'; + } + if ($exception instanceof EmailNotConfirmedException) { + $type = 'disabled account'; + } + if ($exception instanceof UserLockedException) { + $type = 'disabled account'; + } + + if ($type === false) { + $type = 'other'; + } + return $this->prepareCustomerObject(false, $type); + } + + /** + * @param bool $isSuccessful + * @param null $reason + * @return $this + */ + private function prepareCustomerObject(bool $isSuccessful, $reason = null) + { + try { + $this->payload = null; + + if (!isset($this->inputData[0])) { + throw new \Exception("Input data is missing."); + } + + $customer = $this->customerRepository->get($this->inputData[0]); + + if (!$customer->getId()) { + throw new \Exception("No customer."); + } + + $clientDetails = new \Riskified\OrderWebhook\Model\ClientDetails( + $this->clientDetails->getCleanData() + ); + + $sessionDetails = new \Riskified\OrderWebhook\Model\SessionDetails( + $this->sessionDetails->getCleanData() + ); + + $loginStatus = new \Riskified\OrderWebhook\Model\LoginStatus( + array_filter([ + 'login_status_type' => $isSuccessful ? 'success' : 'failure', + 'failure_reason' => !$isSuccessful ? $reason : null, + ], 'strlen') + ); + + $customerPayload = [ + 'customer_id' => $customer->getId(), + 'email' => $customer->getEmail(), + 'customer_created_at' => $this->formatDateAsIso8601($customer->getCreatedAt()), + 'login_status' => $loginStatus, + 'client_details' => $clientDetails, + 'session_details' => $sessionDetails + ]; + + $riskifiedCustomerObject = new \Riskified\OrderWebhook\Model\Login($customerPayload); + + $this->payload = $riskifiedCustomerObject; + } catch (\Exception $e) { + } + + return $this; + } + + /** + * Call Riskified API. + * + * @return $this + */ + private function callApi() + { + if ($this->payload === null) { + return $this; + } + + try { + $this->api->initSdk(); + $transport = $this->api->getTransport(); + + if ($this->isLoggingEnabled()) { + $this->log->log( + __("Sending new request to login endpoint.") + ); + + $this->log->log( + sprintf( + __("Login used during form submission: %s."), + $this->inputData[0] + ) + ); + + $this->log->log( + $this->payload->toJson() + ); + } + + $transport->login($this->payload); + } catch (\Exception $e) { + $this->log->log( + sprintf( + __("Occurred error when sending login info to Riskified API login input: %s."), + $this->inputData[0] + ) + ); + $this->log->log($e->getMessage()); + } + + return $this; + } + + /** + * Retrieve information if feature is enabled in admin config. + * + * @return bool + */ + private function isEnabled() + { + return $this->config->isEnabled() && $this->config->getCustomerLoginHandleEnabled(); + } + + /** + * Retrieve information when the log was enabled. + * + * @return bool + */ + private function isLoggingEnabled() + { + return $this->config->isLoggingEnabled(); + } +} diff --git a/app/code/Riskified/Decider/etc/adminhtml/system.xml b/app/code/Riskified/Decider/etc/adminhtml/system.xml index 76a65ee..cad7e58 100755 --- a/app/code/Riskified/Decider/etc/adminhtml/system.xml +++ b/app/code/Riskified/Decider/etc/adminhtml/system.xml @@ -151,6 +151,15 @@ 1 + + + + Magento\Config\Model\Config\Source\Yesno + + 1 + + diff --git a/app/code/Riskified/Decider/etc/di.xml b/app/code/Riskified/Decider/etc/di.xml index 0257f49..3dc4e47 100755 --- a/app/code/Riskified/Decider/etc/di.xml +++ b/app/code/Riskified/Decider/etc/di.xml @@ -33,5 +33,19 @@ + + + - \ No newline at end of file + + + + + +