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
+
+
+
+
+
+