hipay/hipay-wallet-cashout-mirakl-library

View on GitHub
src/Vendor/Processor.php

Summary

Maintainability
F
6 days
Test Coverage
<?php

namespace HiPay\Wallet\Mirakl\Vendor;

use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Exception;
use Guzzle\Http\Exception\ClientErrorResponseException;
use HiPay\Wallet\Mirakl\Api\Factory as ApiFactory;
use HiPay\Wallet\Mirakl\Api\HiPay;
use HiPay\Wallet\Mirakl\Api\HiPay\Model\Rest\BankInfo;
use HiPay\Wallet\Mirakl\Api\HiPay\Model\Rest\UserAccount;
use HiPay\Wallet\Mirakl\Api\HiPay\Model\Status\BankInfo as BankInfoStatus;
use HiPay\Wallet\Mirakl\Api\Mirakl;
use HiPay\Wallet\Mirakl\Common\AbstractApiProcessor;
use HiPay\Wallet\Mirakl\Exception\BankAccountCreationFailedException;
use HiPay\Wallet\Mirakl\Exception\DispatchableException;
use HiPay\Wallet\Mirakl\Exception\InvalidBankInfoException;
use HiPay\Wallet\Mirakl\Exception\InvalidVendorException;
use HiPay\Wallet\Mirakl\Exception\WalletAccountUpdateException;
use HiPay\Wallet\Mirakl\Service\Validation\ModelValidator;
use HiPay\Wallet\Mirakl\Vendor\Event\AddBankAccount;
use HiPay\Wallet\Mirakl\Vendor\Event\CheckAvailability;
use HiPay\Wallet\Mirakl\Vendor\Event\CheckBankInfos;
use HiPay\Wallet\Mirakl\Vendor\Event\CreateWallet;
use HiPay\Wallet\Mirakl\Vendor\Model\DocumentInterface;
use HiPay\Wallet\Mirakl\Vendor\Model\DocumentManagerInterface;
use HiPay\Wallet\Mirakl\Vendor\Model\VendorManagerInterface;
use HiPay\Wallet\Mirakl\Vendor\Model\VendorInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use HiPay\Wallet\Mirakl\Api\HiPay\Wallet\AccountInfo;
use HiPay\Wallet\Mirakl\Notification\FormatNotification;
use HiPay\Wallet\Mirakl\Notification\Model\LogVendorsManagerInterface;
use HiPay\Wallet\Mirakl\Notification\Model\LogVendorsInterface;

/**
 * Vendor processor handling the wallet creation
 * and the bank info registration and verification.
 *
 * @author    HiPay <support.wallet@hipay.com>
 * @copyright 2017 HiPay
 */
class Processor extends AbstractApiProcessor
{
    /** @var VendorManagerInterface */
    protected $vendorManager;

    /** @var VendorManagerInterface */
    protected $logVendorManager;

    /**
     * @var DocumentManagerInterface
     */
    protected $documentManager;

    /**
     * @var FormatNotification class
     */
    protected $formatNotification;

    /**
     * @var array
     */
    protected $vendorsLogs;

    /**
     * Processor constructor.
     *
     * @param EventDispatcherInterface $dispatcherInterface
     * @param LoggerInterface $logger
     * @param ApiFactory $factory
     * @param VendorManagerInterface $vendorManager
     * @param DocumentManagerInterface $documentManager
     */
    public function __construct(
        EventDispatcherInterface $dispatcherInterface,
        LoggerInterface $logger,
        ApiFactory $factory,
        VendorManagerInterface $vendorManager,
        DocumentManagerInterface $documentManager,
        LogVendorsManagerInterface $logVendorManager
    ) {
        parent::__construct($dispatcherInterface, $logger, $factory);

        $this->vendorManager = $vendorManager;
        $this->documentManager = $documentManager;
        $this->logVendorManager = $logVendorManager;
        $this->formatNotification = new FormatNotification();
        $this->vendorsLogs = array();
    }

    /**
     * Main function to call who process the vendors
     * to create the wallets and register or verify the bank information.
     *
     * @param DateTime $lastUpdate
     * @param $tmpFilesPath
     *
     * @codeCoverageIgnore
     */
    public function process($tmpFilesPath, DateTime $lastUpdate = null)
    {
        try {
            $this->logger->info('Control Mirakl Settings', array('miraklId' => null, "action" => "Wallet creation"));

            // control mirakl settings
            $boolControl = $this->getControlMiraklSettings(
                array_merge($this->documentTypes, $this->bankInfoDocumentType)
            );

            if ($boolControl === false) {
                // log critical
                $title = $this->criticalMessageMiraklSettings;
                $message = $this->formatNotification->formatMessage($title);
                $this->logger->critical($message, array('miraklId' => null, "action" => "Wallet creation"));
            } else {
                $this->logger->info(
                    'Control Mirakl Settings OK',
                    array('miraklId' => null, "action" => "Wallet creation")
                );
            }

            $this->logger->info('Vendor Processing', array('miraklId' => null, "action" => "Wallet creation"));

            //Vendor data fetching from Mirakl
            $this->logger->info(
                'Vendors fetching from Mirakl',
                array('miraklId' => null, "action" => "Wallet creation")
            );
            $miraklData = $this->getVendors($lastUpdate);
            $this->logger->info(
                '[OK] Fetched vendors from Mirakl : ' . count($miraklData),
                array('miraklId' => null, "action" => "Wallet creation")
            );

            //Wallet creation
            $this->logger->info('Wallet creation', array('miraklId' => null, "action" => "Wallet creation"));
            $vendorCollection = $this->registerWallets($miraklData);
            $this->logger->info(
                '[OK] Wallets : ' . count($vendorCollection),
                array('miraklId' => null, "action" => "Wallet creation")
            );

            //Vendor saving
            $this->logger->info("Saving vendor", array('miraklId' => null, "action" => "Wallet creation"));
            $this->vendorManager->saveAll($vendorCollection);
            $this->logger->info("[OK] Vendor saved", array('miraklId' => null, "action" => "Wallet creation"));

            //File transfer
            $this->logger->info('Transfer files', array('miraklId' => null, "action" => "Wallet creation"));
            $this->transferFiles(
                array_keys($vendorCollection),
                $tmpFilesPath
            );

            $this->logVendorManager->saveAll($this->vendorsLogs);

            // Bank data updating
            $this->logger->info('Update bank data', array('miraklId' => null, "action" => "Wallet creation"));
            $this->handleBankInfo($vendorCollection, $miraklData, $tmpFilesPath);
            $this->logger->info('[OK] Bank info updated', array('miraklId' => null, "action" => "Wallet creation"));
        } catch (Exception $e) {
            try {
                // log critical
                $title = 'Error Vendor:Process ';
                $exceptionMsg = $e->getMessage();
                $message = $this->formatNotification->formatMessage($title, false, $exceptionMsg);
                $this->logger->critical($message);
            } catch (\Exception $ex) {
                $this->handleException($e, "critical", array('miraklId' => null, "action" => "Wallet creation"));
            }
        }
    }

    /**
     * Fetch the vendors from Mirakl.
     *
     * @param DateTime $lastUpdate
     *
     * @return array
     */
    public function getVendors(DateTime $lastUpdate = null)
    {
        $this->dispatcher->dispatch('before.vendor.get');
        $return = $this->mirakl->getVendors($lastUpdate);

        $return = array_filter($return, array($this, 'filterVendors'));

        $this->dispatcher->dispatch('after.vendor.get');
        return $return;
    }

    /**
     * Register wallets into HiPay.
     *
     * @param $miraklData
     *
     * @return VendorInterface[] an array of vendor to save
     */
    public function registerWallets($miraklData)
    {
        $vendorCollection = array();

        foreach ($miraklData as $vendorData) {
            $this->logger->debug(
                'Shop id : {shopId}',
                array(
                    'shopId' => $vendorData['shop_id'],
                    'miraklId' => $vendorData['shop_id'],
                    "action" => "Wallet creation"
                )
            );

            try {
                //Vendor recording
                $email = $vendorData['contact_informations']['email'];
                $miraklId = $vendorData['shop_id'];

                $vendor = $this->vendorManager->findByMiraklId($miraklId);

                if (!$vendor) {
                    if (!$this->hasWallet($email)) {
                        //Wallet create (call to HiPay)
                        $walletInfo = $this->createWallet($vendorData);
                        $this->logger->info(
                            '[OK] Created wallet for : ' .
                            $vendorData['shop_id'],
                            array('miraklId' => $vendorData['shop_id'], "action" => "Wallet creation")
                        );
                    } else {
                        //Fetch the wallet id from HiPay
                        $walletInfo = $this->getWalletUserInfo($vendorData);
                    }
                    $vendor = $this->createVendor(
                        $email,
                        $walletInfo->getUserAccountld(),
                        $walletInfo->getUserSpaceld(),
                        $walletInfo->getIdentified(),
                        $vendorData['shop_id'],
                        $vendorData['pro_details']['VAT_number'],
                        $walletInfo->getCallbackSalt(),
                        $vendorData
                    );
                } elseif ($vendor) {

                    //Fetch the wallet id from HiPay
                    $walletInfo = $this->getWalletUserInfo($vendorData, $vendor);

                    if ($vendor->getEmail() !== $email) {
                        $this->updateWalletEmail($email, $vendor);
                    }

                    $vendor->setVatNumber($vendorData['pro_details']['VAT_number']);
                    $vendor->setCallbackSalt($walletInfo->getCallbackSalt());
                    $vendor->setHiPayIdentified($walletInfo->getIdentified());
                    $vendor->setEnabled(true);
                    $vendor->setCountry($vendorData["contact_informations"]["country"]);
                    $vendor->setPaymentBlocked($vendorData["payment_details"]["payment_blocked"]);
                    $vendor->setEmail($email);
                }

                $this->logVendor(
                    $vendorData['shop_id'],
                    $walletInfo->getUserAccountld(),
                    $this->generateLogin($vendorData),
                    ($walletInfo->getIdentified())
                        ? LogVendorsInterface::WALLET_IDENTIFIED : LogVendorsInterface::WALLET_NOT_IDENTIFIED,
                    LogVendorsInterface::SUCCESS,
                    $walletInfo->getRequestMessage(),
                    0,
                    true,
                    $vendorData["contact_informations"]["country"],
                    $vendorData["payment_details"]["payment_blocked"]
                );

                $previousValues = $this->getImmutableValues($vendor);
                //Put more data into the vendor
                $this->vendorManager->update($vendor, $vendorData);

                if (!$this->vendorManager->isValid($vendor)) {
                    throw new InvalidVendorException($vendor);
                }

                ModelValidator::validate($vendor);

                ModelValidator::checkImmutability($vendor, $previousValues);

                $vendorCollection[$vendor->getMiraklId()] = $vendor;

                $this->logger->info(
                    '[OK] The vendor is treated',
                    array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
                );
            } catch (WalletAccountUpdateException $waue) {
                $this->handleException(
                    $waue,
                    'warning',
                    array('miraklId' => $vendorData['shop_id'], "action" => "Wallet creation")
                );
            } catch (Exception $e) {
                $this->logVendor(
                    $vendorData['shop_id'],
                    -1,
                    $this->generateLogin($vendorData),
                    LogVendorsInterface::WALLET_NOT_CREATED,
                    LogVendorsInterface::CRITICAL,
                    substr($e->getMessage(), 0, 255),
                    0,
                    false,
                    $vendorData["contact_informations"]["country"],
                    $vendorData["payment_details"]["payment_blocked"]
                );
                $this->handleException(
                    $e,
                    'warning',
                    array('miraklId' => $vendorData['shop_id'], "action" => "Wallet creation")
                );
            }
        }

        return $vendorCollection;
    }

    /**
     * Check if the vendor already has a wallet.
     *
     * Dispatch the event <b>before.availability.check</b>
     * before sending the data to HiPay
     *
     * @param string $email
     *
     * @return bool
     */
    public function hasWallet($email)
    {
        $event = new CheckAvailability($email);
        $this->dispatcher->dispatch('before.availability.check', $event);
        $result = $this->hipay->isAvailable($email, $event->getEntity());
        $this->dispatcher->dispatch('after.availability.check', $event);
        return !$result;
    }

    protected function updateWalletEmail($email, $vendor)
    {
        try {
            $this->hipay->updateEmail($email, $vendor);
        } catch (Exception $e) {
            throw new WalletAccountUpdateException($vendor, $e);
        }
    }

    /**
     * Create a HiPay wallet.
     *
     * Dispatch the event <b>before.wallet.create</b>
     * before sending the data to HiPay
     *
     * @param array $shopData
     *
     * @return AccountInfo the created account info
     */
    protected function createWallet(array $shopData)
    {
        $userAccount = new UserAccount($shopData);

        $event = new CreateWallet(
            $userAccount
        );

        $this->dispatcher->dispatch(
            'before.wallet.create',
            $event
        );

        $walletInfo = $this->hipay->createFullUseraccountV2(
            $event->getUserAccount()
        );

        $this->dispatcher->dispatch(
            'after.wallet.create',
            $event
        );

        return $walletInfo;
    }

    /**
     * Get a HiPay wallet.
     *
     * Dispatch the event <b>before.wallet.create</b>
     * before sending the data to HiPay
     *
     * @param array $shopData
     *
     * @return AccountInfo the get account info
     */
    protected function getWalletUserInfo(array $shopData, VendorInterface $vendor = null)
    {
        $userAccount = new UserAccount($shopData);

        $event = new CreateWallet(
            $userAccount
        );

        $walletInfo = $this->hipay->getWalletInfo($event->getUserAccount(), $vendor);

        return $walletInfo;
    }

    /**
     * To record a wallet in the database in the case there was an error.
     *
     * @param string $email
     * @param int $walletId
     * @param int $walletSpaceId
     * @param boolean $identified
     * @param int $miraklId
     * @param array $miraklData
     * @return VendorInterface
     */
    protected function createVendor(
        $email,
        $walletId,
        $walletSpaceId,
        $identified,
        $miraklId,
        $vatNumber,
        $callbackSalt,
        $miraklData
    ) {
        $this->logger->debug(
            "The wallet number is $walletId",
            array('miraklId' => $miraklId, "action" => "Wallet creation")
        );
        $vendor = $this->vendorManager->create(
            $email,
            $miraklId,
            $walletId,
            $walletSpaceId,
            $identified,
            $vatNumber,
            $callbackSalt,
            $miraklData
        );

        $vendor->setEmail($email);
        $vendor->setHiPayId($walletId);
        $vendor->setMiraklId($miraklId);
        $vendor->setHiPayUserSpaceId($walletSpaceId);
        $vendor->setHiPayIdentified($identified);
        $vendor->setVatNumber($vatNumber);
        $vendor->setCallbackSalt($callbackSalt);
        $vendor->setEnabled(true);
        $vendor->setCountry($miraklData["contact_informations"]["country"]);
        $vendor->setPaymentBlocked($miraklData["payment_details"]["payment_blocked"]);

        $this->logger->info('[OK] Wallet recorded', array('miraklId' => $miraklId, "action" => "Wallet creation"));

        return $vendor;
    }

    /**
     * Return the values who should't
     * change after the registration of the hipay wallet.
     * @param VendorInterface $vendor
     * @return mixed
     */
    protected function getImmutableValues(VendorInterface $vendor)
    {
        $previousValues['email'] = $vendor->getEmail();
        $previousValues['hipayId'] = $vendor->getHiPayId();
        $previousValues['miraklId'] = $vendor->getMiraklId();

        return $previousValues;
    }

    /**
     * Transfer the files from Mirakl to HiPay using REST endpoint.
     *
     * @param array $shopIds
     * @param $tmpFilePath
     * @throws Exception
     */
    public function transferFiles(array $shopIds, $tmpFilePath)
    {
        if (count($shopIds) > 0) {
            // Fetches all Mirakl file names
            $allMiraklFiles = array();

            foreach (array_chunk($shopIds, 50) as $someShopIds) {
                $allMiraklFiles = array_merge($allMiraklFiles, $this->mirakl->getFiles($someShopIds));
            }

            $docTypes = $this->documentTypes;

            // We only keep the files with types we know
            $files = array_filter(
                $allMiraklFiles,
                function ($aFile) use ($docTypes) {
                    return in_array($aFile['type'], array_keys($docTypes));
                }
            );

            $backFilesType = $this->backDocumentTypes;

            // We only keep the files with types we know
            $backFiles = array_filter(
                $allMiraklFiles,
                function ($aFile) use ($backFilesType) {
                    return in_array($aFile['type'], $backFilesType);
                }
            );

            foreach ($shopIds as $shopId) {
                $this->logger->info(
                    'Will check files for Mirakl shop ' . $shopId,
                    array('miraklId' => $shopId, "action" => "Wallet creation")
                );

                // Fetches documents already sent to HiPay Wallet
                $vendor = $this->vendorManager->findByMiraklId($shopId);
                $documents = $this->documentManager->findByVendor($vendor);

                // Keep Mirakl files for this shop only
                $theFiles = array_filter(
                    $files,
                    function ($file) use ($shopId) {
                        return $file['shop_id'] == $shopId;
                    }
                );

                if (!empty($backFiles)) {
                    $shopBackFilesType = array_filter(
                        $backFiles,
                        function ($file) use ($shopId) {
                            return $file['shop_id'] == $shopId;
                        }
                    );
                } else {
                    $shopBackFilesType = array();
                }

                $this->logger->info(
                    'Found ' . count($theFiles) . ' files on Mirakl for shop ' . $shopId,
                    array('miraklId' => $shopId, "action" => "Wallet creation")
                );

                // Check all files for current shop
                foreach ($theFiles as $theFile) {
                    $filesAlreadyUploaded = array_values(
                        array_filter(
                            $documents,
                            function (DocumentInterface $document) use ($theFile) {
                                return $document->getDocumentType() == $theFile['type'] &&
                                    $document->getMiraklDocumentId() == $theFile['id'];
                            }
                        )
                    );


                    try {
                        $this->checkExtensionFile($theFile['file_name'], $theFile, $shopId);

                        // File not uploaded (or outdated)
                        if (count($filesAlreadyUploaded) === 0) {
                            $this->logger->info(
                                'Document ' .
                                $theFile['id'] .
                                ' (type: ' .
                                $theFile['type'] .
                                ') for Mirakl for shop ' .
                                $shopId .
                                ' is not uploaded or not up to date. Will upload',
                                array('miraklId' => $shopId, "action" => "Wallet creation")
                            );

                            $validityDate = null;

                            if (in_array(
                                $this->documentTypes[$theFile['type']],
                                array(
                                    HiPay::DOCUMENT_SOLE_MAN_BUS_IDENTITY,
                                    HiPay::DOCUMENT_INDIVIDUAL_IDENTITY,
                                    HiPay::DOCUMENT_LEGAL_IDENTITY_OF_REPRESENTATIVE
                                )
                            )) {
                                $validityDate = new DateTime('+1 year');
                            }

                            $tmpFile = $tmpFilePath . '/' . preg_replace("/[^A-Za-z0-9\.]/", '', $theFile['file_name']);

                            file_put_contents(
                                $tmpFile,
                                $this->mirakl->downloadDocuments(array($theFile['id']))
                            );

                            $back = $this->getFileBack(
                                $theFile['type'],
                                $shopBackFilesType,
                                $theFile,
                                $shopId,
                                $tmpFilePath
                            );

                            $this->hipay->uploadDocument(
                                $vendor->getHiPayUserSpaceId(),
                                $vendor->getHiPayId(),
                                $this->documentTypes[$theFile['type']],
                                $tmpFile,
                                $validityDate,
                                ($back !== null) ? $back['filePath'] : null
                            );

                            $newDocument = $this->documentManager->create(
                                $theFile['id'],
                                new \DateTime($theFile['date_uploaded']),
                                $theFile['type'],
                                $vendor
                            );
                            $this->documentManager->save($newDocument);

                            if ($back !== null) {
                                $newDocument = $this->documentManager->create(
                                    $back['fileObject']['id'],
                                    new \DateTime($back['fileObject']['date_uploaded']),
                                    $back['fileObject']['type'],
                                    $vendor
                                );
                                $this->documentManager->save($newDocument);
                            }

                            $this->logger->info(
                                'Upload done. Document saved with ID: ' . $newDocument->getId(),
                                array('miraklId' => $shopId, "action" => "Wallet creation")
                            );
                        } else {
                            $this->logger->info(
                                'Document ' .
                                $theFile['id'] .
                                ' (type: ' .
                                $theFile['type'] .
                                ') for Mirakl for shop ' .
                                $shopId .
                                ' is already uploaded with ID ' .
                                $filesAlreadyUploaded[0]->getId(),
                                array('miraklId' => $shopId, "action" => "Wallet creation")
                            );
                        }
                    } // If this upload fails, we log the error but we continue for other files
                    catch (Exception $e) {
                        try {
                            // log critical
                            $title = 'The document ' .
                                $theFile['type'] .
                                ' for Mirakl shop ' .
                                $shopId .
                                ' could not be uploaded to HiPay Wallet for the following reason:';
                            $infos = array(
                                'shopId' => $shopId,
                                'HipayId' => $vendor->getHiPayId(),
                                'Email' => $vendor->getEmail(),
                                'Type' => 'Critical'
                            );
                            $exceptionMsg = $e->getMessage();
                            $message = $this->formatNotification->formatMessage($title, $infos, $exceptionMsg);
                            $this->logger->critical(
                                $message,
                                array('miraklId' => $shopId, "action" => "Wallet creation")
                            );
                        } catch (\Exception $ex) {
                            throw $ex;
                        }
                    }
                }
            }
        }
    }

    /**
     * Register the bank account and verify the
     * synchronicity of the bank information in both platform.
     *
     * @param VendorInterface[] $vendorCollection
     * @param array[] $miraklDataCollection mirakl data
     * Expect one mirakl data for each vendor present in the vendorCollection
     */
    public function handleBankInfo($vendorCollection, $miraklDataCollection, $tmpFilePath)
    {
        //Index mirakl Data
        $miraklDataCollection = $this->indexMiraklData($miraklDataCollection);

        /** @var VendorInterface $vendor */
        foreach ($vendorCollection as $vendor) {
            $this->logger->debug(
                'Shop id : ' . $vendor->getMiraklId(),
                array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
            );

            try {
                //Check if there is data associated to the current vendor
                if (!isset($miraklDataCollection[$vendor->getMiraklId()])) {
                    $this->logger->notice(
                        "The vendor {$vendor->getMiraklId()} in the mirakl collection",
                        array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
                    );
                } else {
                    $bankInfoStatus = $this->getBankInfoStatus($vendor);

                    $miraklBankInfo = new BankInfo();


                    $this->logger->debug(
                        BankInfoStatus::getLabel($bankInfoStatus),
                        array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
                    );

                    $miraklBankInfo->setMiraklData(
                        $miraklDataCollection[$vendor->getMiraklId()],
                        $this->getBankDocument($vendor->getMiraklId(), $tmpFilePath)
                    );

                    if (
                        $bankInfoStatus === BankInfoStatus::BLANK
                        || !$this->isBankInfosSynchronised($vendor, $miraklBankInfo)
                    ) {
                        if ($this->sendBankAccount($vendor, $miraklBankInfo)) {
                            $this->blockPaymentVendor($vendor);
                            $this->logger->info(
                                '[OK] Created bank account for : ' .
                                $vendor->getMiraklId(),
                                array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
                            );
                        } else {
                            throw new BankAccountCreationFailedException($vendor, $miraklBankInfo);
                        }
                    } else {
                        $this->logger->info(
                            '[OK] Bank information are synchronized',
                            array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
                        );
                    }
                }
            } catch (Exception $e) {
                // log critical
                $shopId = $vendor->getHiPayId();
                $title = 'Exception Warning Bank Information for Mirakl shop ' . $shopId . ':';
                $infos = array(
                    'shopId' => $vendor->getMiraklId(),
                    'HipayId' => $shopId,
                    'Email' => $vendor->getEmail(),
                    'Type' => 'Warning'
                );
                $exceptionMsg = $e->getMessage();
                $message = $this->formatNotification->formatMessage($title, $infos, $exceptionMsg);
                $this->logger->warning(
                    $message,
                    array('miraklId' => $vendor->getMiraklId(), "action" => "Wallet creation")
                );
            }
        }
    }

    /**
     * Block user payment in connector and in Mirakl
     *
     * @param $vendor
     * @return void
     * @throws \Guzzle\Service\Exception\CommandTransferException
     */
    protected function blockPaymentVendor($vendor){
        $this->mirakl->updateOneVendor(
            array(
                'kyc' => array(
                    'reason' => '',
                    'status' => 'APPROVED'
                ),
                'shop_id' => $vendor->getMiraklId(),
                'payment_blocked' => true,
                'suspend' => false
            ));

        $vendor->setPaymentBlocked(true);

        $this->vendorManager->save($vendor);

        $logVendor = $this->logVendorManager->findByMiraklId($vendor->getMiraklId());

        if ($logVendor !== null) {
            $logVendor->setPaymentBlocked(true);
            $this->logVendorManager->save($logVendor);
        }
    }

    /**
     * Index mirakl data fetched with a call to S20 resource from their API
     *
     * @param $miraklData
     * @return array
     */
    protected function indexMiraklData($miraklData)
    {
        $indexedMiraklData = array();
        foreach ($miraklData as $data) {
            $indexedMiraklData[$data['shop_id']] = $data;
        }
        return $indexedMiraklData;
    }

    /**
     * Get bank info status from HiPay.
     *
     * @param VendorInterface $vendor
     *
     * @return string
     */
    public function getBankInfoStatus(VendorInterface $vendor)
    {
        $result = $this->hipay->bankInfosStatus($vendor);
        return $result;
    }

    /**
     * Add bank account information to HiPay
     * Dispatch the event <b>before.bankAccount.add</b>.
     *
     * @param VendorInterface $vendor
     * @param BankInfo $bankInfo
     *
     * @return bool
     */
    protected function sendBankAccount(VendorInterface $vendor, BankInfo $bankInfo)
    {
        $event = new AddBankAccount($bankInfo);

        $this->dispatcher->dispatch('before.bankAccount.add', $event);

        return $this->hipay->bankInfosRegister($vendor, $event->getBankInfo());
    }

    /**
     * Check that the bank information is the same in the two services.
     *
     * @param VendorInterface $vendor
     * @param BankInfo $miraklBankInfo
     *
     * @return bool
     */
    protected function isBankInfosSynchronised(VendorInterface $vendor, BankInfo $miraklBankInfo)
    {
        $hipayBankInfo = $this->getBankInfo($vendor);

        $event = new CheckBankInfos($miraklBankInfo, $hipayBankInfo);

        $ibanCheck = ($hipayBankInfo->getIban() == $miraklBankInfo->getIban());

        $this->dispatcher->dispatch('check.bankInfos.synchronicity', $event);

        return $ibanCheck && $event->isSynchrony();
    }

    /**
     * Return the bank info from HiPay.
     *
     * @param VendorInterface $vendor
     *
     * @return BankInfo
     */
    public function getBankInfo(VendorInterface $vendor)
    {
        return $this->hipay->bankInfosCheck($vendor);
    }

    /**
     * Return true if banking information is the same at Mirakl and HiPay
     * The soap call will fail if the bank info status at HiPay is not validated
     * @param VendorInterface $vendor
     * @param array|BankInfo $miraklBankInfo
     * @param boolean $checkBankStatus set to true if you also want to check to bank info status prior to fetch them
     * @return false|true is the status of the baking information is not validated
     * @throws InvalidBankInfoException the the information is not same by
     */
    public function isBankInfoUsable(VendorInterface $vendor, $miraklBankInfo, $checkBankStatus = false)
    {
        if ($checkBankStatus) {
            $bankInfoStatus = $this->getBankInfoStatus($vendor);

            if (trim($bankInfoStatus) == BankInfoStatus::VALIDATED) {
                return false;
            }
        }

        if (is_array($miraklBankInfo)) {
            $bankInfo = new BankInfo();
            $miraklBankInfo = $bankInfo->setMiraklData($miraklBankInfo);
        }

        return $this->isBankInfosSynchronised($vendor, $miraklBankInfo);
    }

    /**
     * Add the bank information to a wallet
     * The call will fail if the bank information status is not blank
     * @param VendorInterface $vendor
     * @param array|BankInfo $miraklBankInfo
     * @param bool|false $checkBankStatus set to true if you also want to check to bank info status prior to add them
     * @return bool
     */
    public function addBankInformation($vendor, $miraklBankInfo, $checkBankStatus = false)
    {
        if ($checkBankStatus) {
            $bankInfoStatus = $this->getBankInfoStatus($vendor);

            if (trim($bankInfoStatus) == BankInfoStatus::BLANK) {
                return false;
            }
        }

        if (is_array($miraklBankInfo)) {
            $bankInfo = new BankInfo();
            $miraklBankInfo = $bankInfo->setMiraklData($miraklBankInfo);
        }

        return $this->sendBankAccount($vendor, $miraklBankInfo);
    }

    /**
     * Save a vendor in case there was an error.
     *
     * @param string $email
     * @param int $miraklId
     */
    public function recordVendor($email, $miraklId)
    {
        $miraklData = current($this->mirakl->getVendors(null, false, array($miraklId)));
        $hipayInfo = $this->hipay->getWalletInfo($miraklData['contact_informations']['email']);
        $hipayInfo->setVatNumber($miraklData['pro_details']['VAT_number']);
        $vendor = $this->createVendor(
            $email,
            $hipayInfo->getUserAccountld(),
            $hipayInfo->getUserSpaceld(),
            $hipayInfo->getIdentified(),
            $miraklId,
            $miraklData
        );
        $this->vendorManager->save($vendor);
    }

    /**
     * Returns the wallet registered at HiPay
     *
     * @param int $merchantGroupId
     * @param DateTime|null $pastDate
     *
     * @return array
     */
    public function getWallets($merchantGroupId, DateTime $pastDate = null)
    {
        if (!$pastDate) {
            $pastDate = new DateTime('1970-01-01');
        }
        return $this->hipay->getMerchantsGroupAccounts($merchantGroupId, $pastDate);
    }

    /**
     * Control if Mirakl Setting is ok with HiPay prerequisites
     */
    public function getControlMiraklSettings($docTypes)
    {
        $this->mirakl->controlMiraklSettings($docTypes);
    }

    /**
     * get login from Mirakl Id
     *
     * @param $shopId
     * @return type
     * @throws Exception
     */
    public function getLogin($shopId)
    {
        return $this->generateLogin($this->getVendorByShopId($shopId));
    }

    /**
     * @param $shopId
     * @return mixed
     * @throws Exception
     */
    public function getVendorByShopId($shopId)
    {
        $shop = $this->mirakl->getVendors(null, null, array($shopId));

        if (empty($shop)) {
            throw new \Exception("No mirakl data for this shop ID ($shopId)");
        }

        return $shop[0];
    }

    /**
     * log vendor creation/update
     *
     * @param $miraklId
     * @param $hipayId
     * @param $login
     * @param $statusWalletAccount
     * @param $status
     * @param $message
     * @param int $nbDoc
     * @param bool $enabled
     */
    private function logVendor(
        $miraklId,
        $hipayId,
        $login,
        $statusWalletAccount,
        $status,
        $message,
        $nbDoc = 0,
        $enabled = true,
        $country = null,
        $paymentBlocked = false
    ) {
        $logVendor = $this->logVendorManager->findByMiraklId($miraklId);

        if ($logVendor !== null) {
            $logVendor->setStatusWalletAccount($statusWalletAccount);
            $logVendor->setStatus($status);
            $logVendor->setMessage($message);
            $logVendor->setEnabled($enabled);
            $logVendor->setCountry($country);
            $logVendor->setPaymentBlocked($paymentBlocked);
            $this->vendorsLogs[] = $logVendor;
        } else {
            $this->vendorsLogs[] = $this->logVendorManager->create(
                $miraklId,
                $hipayId,
                $login,
                $statusWalletAccount,
                $status,
                $message,
                $nbDoc,
                $country,
                $paymentBlocked
            );
        }
    }

    /**
     * Generate hipay login from mirakl data
     * @param type $miraklData
     * @return type
     */
    private function generateLogin($miraklData)
    {
        return 'mirakl_' . preg_replace("/[^A-Za-z0-9]/", '', $miraklData['shop_name']) . '_' . $miraklData['shop_id'];
    }

    /**
     * Retrieve ALL_PROOF_OF_BANK_ACCOUNT document for $shopId
     * @param type $shopId
     * @param type $tmpFilePath
     * @return string
     */
    private function getBankDocument($shopId, $tmpFilePath)
    {
        $allMiraklFiles = $this->mirakl->getFiles(array($shopId));
        // We only keep the file type we want
        $files = array_filter(
            $allMiraklFiles,
            function ($aFile) {
                return in_array($aFile['type'], array(Mirakl::DOCUMENT_ALL_PROOF_OF_BANK_ACCOUNT));
            }
        );

        if (!empty($files)) {
            $file = end($files);
            $tmpFile = $tmpFilePath . '/' . preg_replace("/[^A-Za-z0-9\.]/", '', $file['file_name']);
            file_put_contents($tmpFile, $this->mirakl->downloadDocuments(array($file['id'])));

            return $tmpFile;
        }

        return null;
    }


    /**
     * @param $fileName
     * @param $file
     * @param $shopId
     * @throws Exception
     */
    private function checkExtensionFile($fileName, $file, $shopId)
    {

        if (!preg_match('/^.*\.(jpg|jpeg|png|gif|pdf)$/i', $fileName)) {
            throw new Exception(
                'Document ' .
                $file['id'] .
                ' (type: ' .
                $file['type'] .
                ') for Mirakl for shop ' .
                $shopId .
                ' will not be uploaded because extension is wrong, should be jpg, jpeg, png, gif or pdf'
            );
        }
    }

    /**
     * get file corresponding to the back of a file type
     *
     * @param $fileType
     * @param $allMiraklFiles
     * @param $frontFile
     * @param $shopId
     * @return array|null
     * @throws Exception
     */
    private function getFileBack($fileType, $allMiraklFiles, $frontFile, $shopId, $tmpFilePath)
    {

        try {
            $backType = $this->getFileBackType($fileType);

            // this file type has no backs
            if (!$backType) {
                return null;
            }

            $files = array_filter(
                $allMiraklFiles,
                function ($aFile) use ($backType) {
                    return $aFile['type'] == $backType;
                }
            );

            // No back has been found in Mirakl files
            if (empty($files)) {
                return null;
            }

            $file = end($files);
            // check if extension of file is correct
            $this->checkExtensionFile($file['file_name'], $file, $shopId);
            // save file
            $tmpFile = $tmpFilePath . '/' . time() . preg_replace("/[^A-Za-z0-9\.]/", '', $file['file_name']);
            file_put_contents($tmpFile, $this->mirakl->downloadDocuments(array($file['id'])));

            return array('filePath' => $tmpFile, 'fileObject' => $file);

        } catch (Exception $e) {
            throw new Exception(
                'Document ' .
                $frontFile['id'] .
                ' (type: ' .
                $fileType .
                ') for Mirakl for shop ' .
                $shopId .
                ' will not be uploaded because file of type ' .
                $backType .
                ' not uploaded in Mirakl or uploaded with wrong extension'
            );
        }
    }

    /**
     * return back of a document type
     *
     * @param $type
     * @return bool|string
     */
    private function getFileBackType($type)
    {
        switch ($type) {
            case Mirakl::DOCUMENT_LEGAL_IDENTITY_OF_REPRESENTATIVE:
                return Mirakl::DOCUMENT_LEGAL_IDENTITY_OF_REP_REAR;
            case Mirakl::DOCUMENT_SOLE_MAN_BUS_IDENTITY:
                return Mirakl::DOCUMENT_SOLE_MAN_BUS_IDENTITY_REAR;
            default:
                return false;
        }
    }

    /**
     * Filter function for Vendors array from Mirakl
     *
     * @param $element
     * @return bool
     */
    private function filterVendors($element)
    {
        $additionnalField = array('code' => 'hipay-process', 'type' => 'BOOLEAN', 'value' => 'true');

        if (isset($element['shop_additional_fields']) &&
            in_array($additionnalField, $element['shop_additional_fields'])
        ) {
            return true;
        } else {
            $this->logger->info(
                'Shop ' .
                $element['shop_id'] .
                ' will not be processed beacause additionnal field hipay-process set to false',
                array('miraklId' => $element['shop_id'], "action" => "Wallet creation")
            );

            $this->disableVendor($element);
        }
    }

    /**
     * If Vendor exist and HIPAY_PROCESS = NO, disable it
     *
     * @param $vendorData
     */
    private function disableVendor($vendorData)
    {

        $vendor = $this->vendorManager->findByMiraklId($vendorData['shop_id']);
        $logVendor = $this->logVendorManager->findByMiraklId($vendorData['shop_id']);

        if ($vendor) {
            $this->logger->info(
                'Shop ' . $vendorData['shop_id'] . ' found in database',
                array('miraklId' => $vendorData['shop_id'], "action" => "Wallet creation")
            );
            if ($vendor->getEnabled() || $vendor->getEnabled() === null) {
                $vendor->setEnabled(false);
                $this->vendorManager->save($vendor);
                if ($logVendor !== null) {
                    $logVendor->setEnabled(false);
                    $this->logVendorManager->save($logVendor);
                }
                $this->logger->info(
                    'Shop ' . $vendorData['shop_id'] . ' disabled',
                    array('miraklId' => $vendorData['shop_id'], "action" => "Wallet creation")
                );
            } else {
                $this->logger->info(
                    'Shop ' . $vendorData['shop_id'] . ' already disabled',
                    array('miraklId' => $vendorData['shop_id'], "action" => "Wallet creation")
                );
            }
        }
    }
}