yoti-8.x-2.5/sdk/Yoti/YotiClient.php

sdk/Yoti/YotiClient.php
<?php

namespace Yoti;

use Yoti\Entity\Receipt;
use Yoti\Exception\YotiClientException;
use Yoti\Http\Payload;
use Yoti\Http\AmlResult;
use Yoti\Http\RequestHandlerInterface;
use Yoti\Entity\AmlProfile;
use Yoti\Http\RequestBuilder;
use Yoti\Exception\AmlException;
use Yoti\Exception\ReceiptException;
use Yoti\Exception\ActivityDetailsException;
use Yoti\Exception\PemFileException;
use Yoti\Exception\ShareUrlException;
use Yoti\Http\Request;
use Yoti\Util\PemFile;
use Yoti\ShareUrl\DynamicScenario;
use Yoti\Http\ShareUrlResult;
use Yoti\Util\Validation;

/**
 * Class YotiClient
 *
 * @package Yoti
 * @author Yoti SDK <websdk@yoti.com>
 */
class YotiClient
{
    /** Request successful outcome */
    const OUTCOME_SUCCESS = 'SUCCESS';

    /** Default url for api (is passed in via constructor) */
    const DEFAULT_CONNECT_API = 'https://api.yoti.com:443/api/v1';

    /** Base url for connect page (user will be redirected to this page eg. baseurl/app-id) */
    const CONNECT_BASE_URL = 'https://www.yoti.com/connect';

    /** Yoti Hub login */
    const DASHBOARD_URL = 'https://hub.yoti.com';

    /** Aml check endpoint */
    const AML_CHECK_ENDPOINT = '/aml-check';

    /** Profile sharing endpoint */
    const PROFILE_REQUEST_ENDPOINT = '/profile/%s';

    /** Share URL endpoint */
    const SHARE_URL_ENDPOINT = '/qrcodes/apps/%s';

    /** Auth HTTP header key */
    const YOTI_AUTH_HEADER_KEY = 'X-Yoti-Auth-Key';

    /**
     * @var \Yoti\Util\PemFile
     */
    private $pemFile;

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

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

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

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

    /**
     * @var \Yoti\Http\RequestHandlerInterface
     */
    private $requestHandler;

    /**
     * YotiClient constructor.
     *
     * @param string $sdkId
     *   The SDK identifier generated by Yoti Hub when you create your app.
     * @param string $pem
     *   PEM file path or string
     * @param string $connectApi (optional)
     *   Connect API address
     * @param string $sdkIdentifier (optional)
     *   SDK or Plugin identifier - deprecated - use ::setSdkIdentifier() instead.
     *
     * @throws \Yoti\Exception\RequestException
     * @throws \Yoti\Exception\YotiClientException
     */
    public function __construct(
        $sdkId,
        $pem,
        $connectApi = self::DEFAULT_CONNECT_API,
        $sdkIdentifier = null
    ) {
        $this->checkRequiredModules();
        $this->extractPemContent($pem);
        $this->setSdkId($sdkId);

        $this->connectApi = $connectApi;

        if (isset($sdkIdentifier)) {
            $this->sdkIdentifier = $sdkIdentifier;
        }
    }

    /**
     * Get login url.
     *
     * @param string $appId
     *
     * @return string
     */
    public static function getLoginUrl($appId)
    {
        return self::CONNECT_BASE_URL . "/$appId";
    }

    /**
     * Return Yoti user profile.
     *
     * @param null|string $encryptedConnectToken
     *
     * @return \Yoti\ActivityDetails
     *
     * @throws \Yoti\Exception\ActivityDetailsException
     * @throws \Yoti\Exception\ReceiptException
     */
    public function getActivityDetails($encryptedConnectToken = null)
    {
        if (!$encryptedConnectToken && array_key_exists('token', $_GET)) {
            $encryptedConnectToken = $_GET['token'];
        }

        $receipt = $this->getReceipt($encryptedConnectToken);

        // Check response was successful
        if ($receipt->getSharingOutcome() !== self::OUTCOME_SUCCESS) {
            throw new ActivityDetailsException('Outcome was unsuccessful', 502);
        }

        return new ActivityDetails($receipt, $this->pemFile);
    }

    /**
     * Perform AML profile check.
     *
     * @param \Yoti\Entity\AmlProfile $amlProfile
     *
     * @return \Yoti\Http\AmlResult
     *
     * @throws \Yoti\Exception\AmlException
     * @throws \Yoti\Exception\RequestException
     */
    public function performAmlCheck(AmlProfile $amlProfile)
    {
        // Get payload data from amlProfile
        $amlPayload = new Payload($amlProfile->getData());

        $response = $this->sendRequest(
            self::AML_CHECK_ENDPOINT,
            Request::METHOD_POST,
            $amlPayload
        );

        // Get response data array
        $result = $this->processJsonResponse($response['response']);

        // Validate result
        $this->validateAmlResult($result, $response['http_code']);

        // Set and return result
        return new AmlResult($result);
    }

    /**
     * Get Share URL for provided dynamic scenario.
     *
     * @param \Yoti\ShareUrl\DynamicScenario $dynamicScenario
     *
     * @return \Yoti\Http\ShareUrlResult
     *
     * @throws \Yoti\Exception\ShareUrlException
     * @throws \Yoti\Exception\RequestException
     */
    public function createShareUrl(DynamicScenario $dynamicScenario)
    {
        $response = $this->sendConnectRequest(
            sprintf(self::SHARE_URL_ENDPOINT, $this->sdkId),
            Request::METHOD_POST,
            new Payload($dynamicScenario)
        );

        $httpCode = $response->getStatusCode();
        if (!$this->isResponseSuccess($httpCode)) {
            throw new ShareUrlException("Server responded with {$httpCode}");
        }

        $result = $this->processJsonResponse($response->getBody());

        return new ShareUrlResult($result);
    }

    /**
     * Set SDK identifier.
     *
     * Allows plugins to declare their identifier.
     *
     * @param string $sdkIdentifier
     *   SDK or Plugin identifier
     */
    public function setSdkIdentifier($sdkIdentifier)
    {
        $this->sdkIdentifier = $sdkIdentifier;
    }

    /**
     * Set SDK version.
     *
     * Allows plugins to declare their version.
     *
     * @param string $sdkVersion
     *   SDK or Plugin version
     */
    public function setSdkVersion($sdkVersion)
    {
        $this->sdkVersion = $sdkVersion;
    }

    /**
     * Set a custom request handler.
     *
     * @param \Yoti\Http\RequestHandlerInterface $requestHandler
     */
    public function setRequestHandler(RequestHandlerInterface $requestHandler)
    {
        $this->requestHandler = $requestHandler;
    }

    /**
     * Make REST request to Connect API.
     * This method allows to stub the request call in test mode.
     *
     * @param string $endpoint
     * @param string $httpMethod
     * @param Payload|null $payload
     *
     * @return \Yoti\Http\Response
     *
     * @throws \Yoti\Exception\RequestException
     */
    private function sendConnectRequest($endpoint, $httpMethod, Payload $payload = null, $headers = [])
    {
        $requestBuilder = (new RequestBuilder())
            ->withBaseUrl($this->connectApi)
            ->withEndpoint($endpoint)
            ->withMethod($httpMethod)
            ->withPemString((string) $this->pemFile)
            ->withQueryParam('appId', $this->sdkId);

        if (isset($payload)) {
            $requestBuilder->withPayload($payload);
        }

        if (isset($this->sdkIdentifier)) {
            $requestBuilder->withSdkIdentifier($this->sdkIdentifier);
        }

        if (isset($this->sdkVersion)) {
            $requestBuilder->withSdkVersion($this->sdkVersion);
        }

        if (isset($this->requestHandler)) {
            $requestBuilder->withHandler($this->requestHandler);
        }

        foreach ($headers as $name => $value) {
            $requestBuilder->withHeader($name, $value);
        }

        return $requestBuilder->build()->execute();
    }

    /**
     * Make REST request to Connect API.
     * This method allows to stub the request call in test mode.
     *
     * @deprecated 3.0.0 replaced by ::sendConnectRequest()
     *
     * @param string $endpoint
     * @param string $httpMethod
     * @param Payload|null $payload
     *
     * @return array
     *
     * @throws \Yoti\Exception\RequestException
     */
    protected function sendRequest($endpoint, $httpMethod, Payload $payload = null, $headers = [])
    {
        $response = $this->sendConnectRequest($endpoint, $httpMethod, $payload, $headers);

        return [
            'response' => $response->getBody(),
            'http_code' => $response->getStatusCode()
        ];
    }

    /**
     * Handle request result.
     *
     * @param array $responseArr
     * @param int $httpCode
     *
     * @throws \Yoti\Exception\AmlException
     */
    private function validateAmlResult(array $responseArr, $httpCode)
    {
        if ($this->isResponseSuccess((int) $httpCode)) {
            // The request is successful - nothing to do
            return;
        }

        $errorMessage = $this->getErrorMessage($responseArr);
        $errorCode = isset($responseArr['code']) ? $responseArr['code'] : 'Error';

        // Throw the error message that's included in the response
        if (!empty($errorMessage)) {
            throw new AmlException("$errorCode - {$errorMessage}", $httpCode);
        }

        // Throw a general error message
        throw new AmlException("{$errorCode} - Server responded with {$httpCode}", $httpCode);
    }

    /**
     * Get error message from the response array.
     *
     * @param array $result
     *
     * @return null|string
     */
    private function getErrorMessage(array $result)
    {
        $errorMessage = '';
        if (isset($result['errors'][0]['property']) && isset($result['errors'][0]['message'])) {
            $errorMessage = $result['errors'][0]['property'] . ': ' . $result['errors'][0]['message'];
        }
        return $errorMessage;
    }

    /**
     * Decrypt and return receipt data.
     *
     * @param string $encryptedConnectToken
     *
     * @return \Yoti\Entity\Receipt
     *
     * @throws \Yoti\Exception\ActivityDetailsException
     * @throws \Yoti\Exception\ReceiptException
     * @throws \Yoti\Exception\RequestException
     */
    private function getReceipt($encryptedConnectToken)
    {
        // Decrypt connect token
        $token = $this->decryptConnectToken($encryptedConnectToken);
        if (!$token) {
            throw new ActivityDetailsException('Could not decrypt connect token.', 401);
        }

        // Request endpoint
        $endpoint = sprintf(self::PROFILE_REQUEST_ENDPOINT, $token);
        $response = $this->sendRequest(
            $endpoint,
            Request::METHOD_GET,
            null,
            [self::YOTI_AUTH_HEADER_KEY => $this->pemFile->getAuthKey()]
        );

        $httpCode = (int) $response['http_code'];
        if (!$this->isResponseSuccess($httpCode)) {
            throw new ActivityDetailsException("Server responded with {$httpCode}", $httpCode);
        }

        $result = $this->processJsonResponse($response['response']);
        $this->checkForReceipt($result);

        return new Receipt($result['receipt']);
    }

    /**
     * @param string $json
     *
     * @return mixed the decoded JSON result.
     *
     * @throws \Yoti\Exception\YotiClientException
     */
    private function processJsonResponse($json)
    {
        // Get decoded response data
        $result = json_decode($json, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new YotiClientException('JSON response was invalid', 502);
        }

        return $result;
    }

    /**
     * @param array $response
     *
     * @throws \Yoti\Exception\ReceiptException
     */
    private function checkForReceipt(array $responseArr)
    {
        // Check receipt is in response
        if (!array_key_exists('receipt', $responseArr)) {
            throw new ReceiptException('Receipt not found in response', 502);
        }
    }

    /**
     * @param int $httpCode
     *
     * @return boolean
     */
    private function isResponseSuccess($httpCode)
    {
        Validation::isInteger($httpCode, 'httpCode');
        return $httpCode >= 200 && $httpCode < 300;
    }

    /**
     * Decrypt connect token.
     *
     * @param string $encryptedConnectToken
     *
     * @return mixed
     */
    private function decryptConnectToken($encryptedConnectToken)
    {
        $tok = base64_decode(strtr($encryptedConnectToken, '-_,', '+/='));
        openssl_private_decrypt($tok, $token, (string) $this->pemFile);

        return $token;
    }

    /**
     * Validate and set PEM file content.
     *
     * @deprecated 3.0.0 this will be replaced by \Yoti\Util\PemFile.
     *
     * @param string $pem
     *   PEM file path or string
     *
     * @throws \Yoti\Exception\YotiClientException
     */
    private function extractPemContent($pem)
    {
        // Check PEM passed
        if (!$pem) {
            throw new YotiClientException('PEM file is required', 400);
        }

        // Assert file exists if user passed PEM file path using file:// stream wrapper.
        if (strpos($pem, 'file://') === 0 && !is_file($pem)) {
            throw new YotiClientException('PEM file was not found.', 400);
        }

        try {
            if (is_file($pem)) {
                $this->pemFile = PemFile::fromFilePath($pem);
            } else {
                $this->pemFile = PemFile::fromString($pem);
            }
        } catch (PemFileException $e) {
            throw new YotiClientException('PEM file path or content is invalid', 400);
        }
    }

    /**
     * Validate and set SDK ID.
     *
     * @param string $sdkId
     *
     * @throws \Yoti\Exception\YotiClientException
     */
    private function setSdkId($sdkId)
    {
        // Check SDK ID passed
        if (!$sdkId) {
            throw new YotiClientException('SDK ID is required', 400);
        }
        $this->sdkId = $sdkId;
    }

    /**
     * Check PHP required modules.
     *
     * @throws \Yoti\Exception\YotiClientException
     */
    private function checkRequiredModules()
    {
        $requiredModules = ['curl', 'json'];
        foreach ($requiredModules as $mod) {
            if (!extension_loaded($mod)) {
                throw new YotiClientException("PHP module '$mod' not installed", 501);
            }
        }
    }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc