Covivo/mobicoop

View on GitHub
api/src/DataProvider/Entity/WorldlineProvider.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/**
 * Copyright (c) 2021, MOBICOOP. All rights reserved.
 * This project is dual licensed under AGPL and proprietary licence.
 ***************************
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <gnu.org/licenses>.
 ***************************
 *    Licence MOBICOOP described in the file
 *    LICENSE
 */

namespace App\DataProvider\Entity;

use App\Carpool\Entity\Criteria;
use App\DataProvider\Service\DataProvider;
use App\Payment\Entity\CarpoolItem;
use App\User\Entity\User;
use App\User\Interfaces\ConsumptionFeedbackInterface;
use Psr\Log\LoggerInterface;

/**
 * Worldline Provider.
 *
 * @author Maxime Bardot <maxime.bardot@mobicoop.org>
 */
class WorldlineProvider implements ConsumptionFeedbackInterface
{
    public const GRANT_TYPE = 'client_credentials';
    public const CONSUMPTION_TYPE = 'FIXED_FEE';

    public const STEPS_TYPE = 'TRAVEL';
    public const STEPS_TRANSPORT_MODE = 'MOVICI';
    public const STEPS_IS_PM_CHARGEABLE = false;

    // const TEST_SSO_ACCOUNT_ID = "36";
    public const TEST_SSO_ACCOUNT_ID = null;

    public const ADDITIONAL_INFOS = [
        ['key' => 'TYPE', 'value' => 'CONSUMPTION'],
    ];

    private $clientId;
    private $clientSecret;
    private $baseUrlAuth;
    private $baseUrl;
    private $apiKey;
    private $appId;
    private $authChain;
    private $logger;

    /**
     * @var string
     */
    private $accessToken;

    /**
     * @var CarpoolItem
     */
    private $consumptionCarpoolItem;

    /**
     * @var User
     */
    private $consumptionUser;

    /**
     * @var array
     */
    private $requestBody;

    /**
     * @var string
     */
    private $externalActivityId;

    public function __construct(string $clientId, string $clientSecret, string $baseUrlAuth, string $baseUrl, string $apiKey, int $appId, LoggerInterface $logger)
    {
        $this->clientId = $clientId;
        $this->clientSecret = $clientSecret;
        $this->baseUrlAuth = $baseUrlAuth;
        $this->baseUrl = $baseUrl;
        $this->authChain = 'Basic '.base64_encode($clientId.':'.$clientSecret);
        $this->apiKey = $apiKey;
        $this->appId = $appId;
        $this->requestBody = [];
        $this->logger = $logger;
    }

    /**
     * Get the auth token.
     */
    public function auth()
    {
        $this->logger->info('Get Access Token');

        $dataProvider = new DataProvider($this->baseUrlAuth);

        $body['grant_type'] = self::GRANT_TYPE;

        $headers = [
            'Authorization' => $this->authChain,
        ];

        $this->logger->info('Authentification : '.$this->baseUrlAuth);

        $response = $dataProvider->postCollection($body, $headers, null, DataProvider::BODY_TYPE_FORM_PARAMS);

        $this->logger->info('Result Code : '.$response->getCode());

        if (200 == $response->getCode()) {
            $data = json_decode($response->getValue(), true);
            $this->setAccessToken($data['access_token']);
        } else {
            throw new \LogicException('Auth failed');
        }
    }

    /**
     * Send a consumption feedback.
     */
    public function sendConsumptionFeedback()
    {
        $this->setConsumptionUser($this->getConsumptionCarpoolItem()->getDebtorUser());
        if ($this->checkUserForSso() && 200 !== $this->getConsumptionCarpoolItem()->getDebtorConsumptionFeedbackReturnCode()) {
            $this->sendConsumptionFeedbackRequest();
        }

        $this->setConsumptionUser($this->getConsumptionCarpoolItem()->getCreditorUser());
        if ($this->checkUserForSso() && 200 !== $this->getConsumptionCarpoolItem()->getCreditorConsumptionFeedbackReturnCode()) {
            $this->sendConsumptionFeedbackRequest();
        }

        $this->setRequestBody([]);
        $this->setConsumptionUser(null);
    }

    // Getters / Setters

    public function getConsumptionUser(): ?User
    {
        return $this->consumptionUser;
    }

    public function setConsumptionUser(?User $consumptionUser)
    {
        $this->consumptionUser = $consumptionUser;
    }

    public function getConsumptionCarpoolItem(): ?CarpoolItem
    {
        return $this->consumptionCarpoolItem;
    }

    public function setConsumptionCarpoolItem(?CarpoolItem $consumptionCarpoolItem)
    {
        $this->consumptionCarpoolItem = $consumptionCarpoolItem;
    }

    public function getAccessToken(): ?string
    {
        return $this->accessToken;
    }

    public function setAccessToken(string $accessToken)
    {
        $this->accessToken = $accessToken;
    }

    public function getRequestBody(): ?array
    {
        return $this->requestBody;
    }

    public function setRequestBody(array $requestBody)
    {
        $this->requestBody = $requestBody;
    }

    public function getExternalActivityId(): ?string
    {
        return $this->externalActivityId;
    }

    public function setExternalActivityId(?string $externalActivityId)
    {
        $this->externalActivityId = $externalActivityId;
    }

    /**
     * Check if the User has been created by Sso.
     */
    private function checkUserForSso(): bool
    {
        $this->logger->info('Check User '.$this->getConsumptionUser()->getId());

        foreach ($this->getConsumptionUser()->getSsoAccounts() as $account) {
            if (!is_null($account->getSsoId()) && !is_null($account->getAppDelegate()) && $account->getAppDelegate()->getId() == $this->appId) {
                return true;
            }
        }

        return false;
    }

    /**
     * Build the body of the request of consumption feedback.
     */
    private function buildConsumptionFeedbackForUser()
    {
        $this->setExternalActivityId(null);
        if ($this->getConsumptionUser()->getId() == $this->getConsumptionCarpoolItem()->getAsk()->getMatching()->getProposalOffer()->getUser()->getId()) {
            $externalActivityId = $this->getConsumptionCarpoolItem()->getAsk()->getMatching()->getProposalOffer()->getId();
            $price = $this->getConsumptionCarpoolItem()->getAsk()->getCriteria()->getDriverComputedRoundedPrice();
        } elseif ($this->getConsumptionUser()->getId() == $this->getConsumptionCarpoolItem()->getAsk()->getMatching()->getProposalRequest()->getUser()->getId()) {
            $externalActivityId = $this->getConsumptionCarpoolItem()->getAsk()->getMatching()->getProposalRequest()->getId();
            $price = $this->getConsumptionCarpoolItem()->getAsk()->getCriteria()->getPassengerComputedRoundedPrice();
        } else {
            return [];
        }

        // Just a fail safe but if we have a carpool item, it's obviously a carpooled day
        $carpooled = false;

        // start date
        $askCriteria = $this->getConsumptionCarpoolItem()->getAsk()->getCriteria();

        $beginDate = $this->getConsumptionCarpoolItem()->getItemDate();
        if (Criteria::FREQUENCY_PUNCTUAL == $askCriteria->getFrequency()) {
            $beginDate->setTime($askCriteria->getFromTime()->format('H'), $askCriteria->getFromTime()->format('i'), $askCriteria->getFromTime()->format('s'));
            $endDate = clone $beginDate;
            $endDate = $endDate->modify('+'.$this->getConsumptionCarpoolItem()->getAsk()->getMatching()->getNewDuration().' second');
            $carpooled = true;
        } else {
            switch ($this->getConsumptionCarpoolItem()->getItemDate()->format('w')) {
                case 0:
                    if ($askCriteria->isSunCheck()) {
                        $beginDate->setTime($askCriteria->getSunTime()->format('H'), $askCriteria->getSunTime()->format('i'), $askCriteria->getSunTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                case 1:
                    if ($askCriteria->isMonCheck()) {
                        $beginDate->setTime($askCriteria->getMonTime()->format('H'), $askCriteria->getMonTime()->format('i'), $askCriteria->getMonTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                case 2:
                    if ($askCriteria->isTueCheck()) {
                        $beginDate->setTime($askCriteria->getTueTime()->format('H'), $askCriteria->getTueTime()->format('i'), $askCriteria->getTueTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                case 3:
                    if ($askCriteria->isWedCheck()) {
                        $beginDate->setTime($askCriteria->getWedTime()->format('H'), $askCriteria->getWedTime()->format('i'), $askCriteria->getWedTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                case 4:
                    if ($askCriteria->isThuCheck()) {
                        $beginDate->setTime($askCriteria->getThuTime()->format('H'), $askCriteria->getThuTime()->format('i'), $askCriteria->getThuTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                case 5:
                    if ($askCriteria->isFriCheck()) {
                        $beginDate->setTime($askCriteria->getFriTime()->format('H'), $askCriteria->getFriTime()->format('i'), $askCriteria->getFriTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                case 6:
                    if ($askCriteria->isSatCheck()) {
                        $beginDate->setTime($askCriteria->getSatTime()->format('H'), $askCriteria->getSatTime()->format('i'), $askCriteria->getSatTime()->format('s'));
                        $carpooled = true;
                    }

                    break;

                default: break;
            }

            if ($carpooled) {
                $endDate = clone $beginDate;
                $endDate = $endDate->modify('+'.$this->getConsumptionCarpoolItem()->getAsk()->getMatching()->getNewDuration().' second');
            }
        }

        if ($carpooled) {
            $this->setExternalActivityId((microtime(true) * 10000).'|'.$externalActivityId);
            $this->setRequestBody([
                'accountId' => (!is_null(self::TEST_SSO_ACCOUNT_ID)) ? self::TEST_SSO_ACCOUNT_ID : ''.$this->getConsumptionUser()->getId(),
                'consumptionType' => self::CONSUMPTION_TYPE,
                'externalActivityId' => $this->getExternalActivityId(),
                'steps' => [
                    [
                        'beginDate' => $beginDate->format('c'),
                        'endDate' => $endDate->format('c'),
                        'type' => self::STEPS_TYPE,
                        'transportMode' => self::STEPS_TRANSPORT_MODE,
                        'financialData' => [
                            'initialAmount' => round($price, 2),
                            'initialAmountExcdTax' => round($price / (1 + 0.20), 2),
                            'isPMChargeable' => self::STEPS_IS_PM_CHARGEABLE,
                        ],
                    ],
                ],
                'additionalInformations' => self::ADDITIONAL_INFOS,
            ]);
        }
    }

    /**
     * Send the consumption feedback to the API.
     */
    private function sendConsumptionFeedbackRequest()
    {
        $this->buildConsumptionFeedbackForUser();

        if (is_null($this->getRequestBody()) || 0 == count($this->getRequestBody())) {
            return;
        }

        $dataProvider = new DataProvider($this->baseUrl);

        $headers = [
            'Authorization' => 'Bearer '.$this->getAccessToken(),
            'x-apikey' => $this->apiKey,
        ];

        $this->logger->info('Send consumption feedback for User '.$this->getConsumptionUser()->getId().' externalActivityId : '.$this->getExternalActivityId());
        $this->logger->info(json_encode($this->getRequestBody()));

        $response = $dataProvider->putItem($this->getRequestBody(), $headers, null, DataProvider::BODY_TYPE_JSON);

        $this->logger->info('Result Code : '.$response->getCode());

        if (200 == $response->getCode()) {
            $data = json_decode($response->getValue(), true);
        } else {
            $this->logger->info('Request failed ! ');
        }

        // Store some data
        if ($this->getConsumptionCarpoolItem()->getDebtorUser()->getId() === $this->getConsumptionUser()->getId()) {
            $this->getConsumptionCarpoolItem()->setDebtorConsumptionFeedbackExternalId($this->getExternalActivityId());
            $this->getConsumptionCarpoolItem()->setDebtorConsumptionFeedbackDate(new \DateTime('now'));
            $this->getConsumptionCarpoolItem()->setDebtorConsumptionFeedbackReturnCode($response->getCode());
        } else {
            $this->getConsumptionCarpoolItem()->setCreditorConsumptionFeedbackExternalId($this->getExternalActivityId());
            $this->getConsumptionCarpoolItem()->setCreditorConsumptionFeedbackDate(new \DateTime('now'));
            $this->getConsumptionCarpoolItem()->setCreditorConsumptionFeedbackReturnCode($response->getCode());
        }
    }
}