hipay/hipay-wallet-cashout-mirakl-library

View on GitHub
src/Cashout/Withdraw.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

namespace HiPay\Wallet\Mirakl\Cashout;

use DateTime;
use Exception;
use HiPay\Wallet\Mirakl\Api\Factory;
use HiPay\Wallet\Mirakl\Api\HiPay\Model\Status\BankInfo as BankInfoStatus;
use HiPay\Wallet\Mirakl\Cashout\Model\Operation\ManagerInterface as OperationManager;
use HiPay\Wallet\Mirakl\Cashout\Model\Operation\OperationInterface;
use HiPay\Wallet\Mirakl\Cashout\Model\Operation\Status;
use HiPay\Wallet\Mirakl\Cashout\AbstractOperationProcessor;
use HiPay\Wallet\Mirakl\Exception\UnconfirmedBankAccountException;
use HiPay\Wallet\Mirakl\Exception\UnidentifiedWalletException;
use HiPay\Wallet\Mirakl\Exception\WalletNotFoundException;
use HiPay\Wallet\Mirakl\Exception\WrongWalletBalance;
use HiPay\Wallet\Mirakl\Vendor\Model\VendorManagerInterface as VendorManager;
use HiPay\Wallet\Mirakl\Vendor\Model\VendorInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use HiPay\Wallet\Mirakl\Notification\FormatNotification;
use HiPay\Wallet\Mirakl\Notification\Model\LogOperationsManagerInterface as LogOperationsManager;
use HiPay\Wallet\Mirakl\Exception\VendorDisabledException;
use HiPay\Wallet\Mirakl\Exception\PaymentBlockedException;

/**
 * Process withdraw
 *
 * @author    HiPay <support.wallet@hipay.com>
 * @copyright 2017 HiPay
 */
class Withdraw extends AbstractOperationProcessor
{
    protected $formatNotification;

    /**
     * Withdraw constructor.
     * @param EventDispatcherInterface $dispatcher
     * @param LoggerInterface $logger
     * @param Factory $factory
     * @param OperationManager $operationManager
     * @param VendorManager $vendorManager
     * @param VendorInterface $operator
     * @param LogOperationsManager $logOperationsManager
     */
    public function __construct(
        EventDispatcherInterface $dispatcher,
        LoggerInterface $logger,
        Factory $factory,
        OperationManager $operationManager,
        VendorManager $vendorManager,
        VendorInterface $operator,
        LogOperationsManager $logOperationsManager
    ) {
        parent::__construct(
            $dispatcher,
            $logger,
            $factory,
            $operationManager,
            $vendorManager,
            $logOperationsManager,
            $operator
        );

        $this->formatNotification = new FormatNotification();

        $this->logOperationsManager = $logOperationsManager;
    }

    /**
     * Process withdraw
     */
    public function process()
    {
        $this->logger->info("Withdraw operations", array('miraklId' => null, "action" => "Withdraw"));

        $this->withdrawOperations();
    }

    protected function withdrawOperations()
    {
        $toWithdraw = $this->getWithdrawableOperations();

        $this->logger->info(
            "Operation to withdraw : " . count($toWithdraw),
            array('miraklId' => null, "action" => "Withdraw")
        );

        /** @var OperationInterface $operation */
        foreach ($toWithdraw as $operation) {
            try {
                //Execute the withdrawal
                $this->withdraw($operation);

                //Set operation new data
                $this->logger->info(
                    "[OK] Withdraw operation " . $operation->getWithdrawId() . " executed",
                    array('miraklId' => $operation->getMiraklId(), "action" => "Withdraw")
                );
            } catch (Exception $e) {
                $this->logger->critical(
                    "[KO] Withdraw operation failed",
                    array('miraklId' => $operation->getMiraklId(), "action" => "Withdraw")
                );
                $this->handleException(
                    $e,
                    'critical',
                    array('miraklId' => $operation->getMiraklId(), "action" => "Withdraw")
                );
            }
        }
    }

    /**
     * Put the money into the real bank account of the operator|seller.
     *
     * @param OperationInterface $operation
     * @return int
     * @throws Exception
     * @throws WrongWalletBalance
     */
    public function withdraw(OperationInterface $operation)
    {
        try {
            $vendor = $this->getVendor($operation);

            if (!$vendor) {
                throw new WalletNotFoundException($vendor);
            }

            if (!$this->hipay->isWalletExist($vendor->getHiPayId())) {
                throw new WalletNotFoundException($vendor);
            }

            if (!$this->checkOperationVendorEnabled($vendor, $operation)) {
                throw new VendorDisabledException($vendor->getMiraklId(), 'withdraw');
            }

            if (!$this->hipay->isIdentified($vendor)) {
                throw new UnidentifiedWalletException($vendor);
            }

            if ($vendor->isPaymentBlocked()) {
                throw new PaymentBlockedException($vendor->getMiraklId(), 'withdraw');
            }

            $bankInfoStatus = trim($this->hipay->bankInfosStatus($vendor));

            if ($bankInfoStatus != BankInfoStatus::VALIDATED) {
                throw new UnconfirmedBankAccountException(
                    new BankInfoStatus(BankInfoStatus::getLabel($bankInfoStatus)),
                    $operation->getMiraklId()
                );
            }

            try {
                $this->hasSufficientFunds($operation->getAmount(), $vendor);
                $amount = round(($operation->getAmount()), self::SCALE);
            } catch (WrongWalletBalance $ex) {
                if ($operation->getMiraklId() === null || !$operation->getMiraklId()) {
                    $amount = $ex->getBalance();
                    //Vendor operation
                } else {
                    throw $ex;
                }
            }

            $operation->setHiPayId($vendor->getHiPayId());

            $merchantUniqueId = is_null($operation->getMerchantUniqueId()) ? null : "WITHDRAWAL_" . $operation->getMerchantUniqueId();

            //Withdraw
            $withdrawId = $this->hipay->withdraw(
                $vendor,
                $amount,
                $this->operationManager->generateWithdrawLabel($operation),
                $merchantUniqueId
            );

            $operation->setWithdrawId($withdrawId);
            $operation->setStatus(new Status(Status::WITHDRAW_REQUESTED));
            $operation->setUpdatedAt(new DateTime());
            $operation->setWithdrawnAmount($amount);
            $this->operationManager->save($operation);

            $this->logOperation(
                $operation->getMiraklId(),
                $operation->getPaymentVoucher(),
                Status::WITHDRAW_REQUESTED,
                ""
            );

            return $withdrawId;
        } catch (VendorDisabledException $e) {
            $operation->setStatus(new Status(Status::WITHDRAW_VENDOR_DISABLED));
            $operation->setUpdatedAt(new DateTime());
            $this->operationManager->save($operation);

            $this->logOperation(
                $operation->getMiraklId(),
                $operation->getPaymentVoucher(),
                Status::WITHDRAW_VENDOR_DISABLED,
                $e->getMessage()
            );

            throw $e;
        } catch (WrongWalletBalance $e) {
            $operation->setStatus(new Status(Status::WITHDRAW_NEGATIVE));
            $operation->setUpdatedAt(new DateTime());
            $this->operationManager->save($operation);

            $this->logOperation(
                $operation->getMiraklId(),
                $operation->getPaymentVoucher(),
                Status::WITHDRAW_NEGATIVE,
                $e->getMessage()
            );

            throw $e;
        } catch (PaymentBlockedException $e) {
            $operation->setStatus(new Status(Status::WITHDRAW_PAYMENT_BLOCKED));
            $operation->setUpdatedAt(new DateTime());
            $this->operationManager->save($operation);

            $this->logOperation(
                $operation->getMiraklId(),
                $operation->getPaymentVoucher(),
                Status::WITHDRAW_PAYMENT_BLOCKED,
                $e->getMessage()
            );

            throw $e;
        } catch (Exception $e) {
            $operation->setStatus(new Status(Status::WITHDRAW_FAILED));
            $operation->setUpdatedAt(new DateTime());
            $this->operationManager->save($operation);

            $this->logOperation(
                $operation->getMiraklId(),
                $operation->getPaymentVoucher(),
                Status::WITHDRAW_FAILED,
                $e->getMessage()
            );

            throw $e;
        }
    }

    /**
     * Fetch the operation to withdraw from the storage
     *
     * @return OperationInterface[]
     */
    protected function getWithdrawableOperations()
    {
        $toWithdrawSuccess = $this->operationManager->findByStatus(new Status(Status::TRANSFER_SUCCESS));

        $toWithdrawFailed = $this->operationManager->findByStatus(new Status(Status::WITHDRAW_FAILED));

        $toWithdrawNegative = $this->operationManager->findByStatus(new Status(Status::WITHDRAW_NEGATIVE));

        $toWithdrawBlocked = $this->operationManager->findByStatus(new Status(Status::WITHDRAW_PAYMENT_BLOCKED));

        return array_merge($toWithdrawBlocked, $toWithdrawNegative, $toWithdrawFailed, $toWithdrawSuccess);
    }
}