shetabit/multipay

View on GitHub
src/Drivers/Jibit/JibitClient.php

Summary

Maintainability
C
7 hrs
Test Coverage
<?php
namespace Shetabit\Multipay\Drivers\Jibit;

use chillerlan\SimpleCache\CacheException;
use chillerlan\SimpleCache\CacheOptions;
use chillerlan\SimpleCache\FileCache;
use Psr\SimpleCache\InvalidArgumentException;
use Shetabit\Multipay\Exceptions\PurchaseFailedException;

class JibitClient
{
    /**
     * Access token
     * @var string
     */
    public $accessToken;

    /**
     * API key
     *
     * @var string
     */
    private $apiKey;

    /**
     * Secret key
     *
     * @var string
     */
    private $secretKey;

    /**
     * Refresh token
     * @var string
     */
    private $refreshToken;

    /**
     * Cache
     *
     * @var FileCache
     */
    private $cache;

    /**
     * Base URL
     *
     * @var string
     */
    public $baseUrl;


    /**
     * @throws CacheException
     */
    public function __construct($apiKey, $secretKey, $baseUrl, $cachePath)
    {
        $this->baseUrl = $baseUrl;
        $this->apiKey = $apiKey;
        $this->secretKey = $secretKey;
        $this->cache = new FileCache(
            new CacheOptions([
                'filestorage' => $cachePath,
            ])
        );
    }

    /**
     * Request payment
     *
     * @param int $amount
     * @param string $referenceNumber
     * @param string $userIdentifier
     * @param string $callbackUrl
     * @param string $currency
     * @param null $description
     * @param $additionalData
     * @return bool|mixed|string
     * @throws PurchaseFailedException
     */
    public function paymentRequest($amount, $referenceNumber, $userIdentifier, $callbackUrl, $currency = 'IRR', $description = null, $additionalData = null)
    {
        $this->generateToken();

        $data = [
            'additionalData' => $additionalData,
            'amount' => $amount,
            'callbackUrl' => $callbackUrl,
            'clientReferenceNumber' => $referenceNumber,
            'currency' => $currency,
            'userIdentifier' => $userIdentifier,
            'description' => $description,
        ];

        return $this->callCurl('/purchases', $data, true);
    }

    /**
     *
     * Get order by ID
     * @param $id
     * @return bool|mixed|string
     * @throws PurchaseFailedException
     */
    public function getOrderById($id)
    {
        return  $this->callCurl('/purchases?purchaseId=' . $id, [], true, 0, 'GET');
    }

    /**
     * Generate token
     *
     * @param bool $isForce
     * @return string
     * @throws PurchaseFailedException
     * @throws InvalidArgumentException
     */
    private function generateToken($isForce = false)
    {
        if ($isForce === false && $this->cache->has('accessToken')) {
            return $this->setAccessToken($this->cache->get('accessToken'));
        } elseif ($this->cache->has('refreshToken')) {
            $refreshToken = $this->refreshTokens();

            if ($refreshToken !== 'ok') {
                return $this->generateNewToken();
            }
        } else {
            return $this->generateNewToken();
        }

        throw new PurchaseFailedException('Token generation encountered an error.');
    }

    /**
     * Refresh tokens
     * @throws PurchaseFailedException
     * @throws InvalidArgumentException
     */
    private function refreshTokens()
    {
        $data = [
            'accessToken' => str_replace('Bearer ', '', $this->cache->get('accessToken')),
            'refreshToken' => $this->cache->get('refreshToken'),
        ];

        $result = $this->callCurl('/tokens/refresh', $data, false);

        if (empty($result['accessToken'])) {
            throw new PurchaseFailedException('Refresh token encountered an error.');
        }

        if (!empty($result['accessToken'])) {
            $this->cache->set('accessToken', 'Bearer ' . $result['accessToken'], 24 * 60 * 60 - 60);
            $this->cache->set('refreshToken', $result['refreshToken'], 48 * 60 * 60 - 60);

            $this->setAccessToken('Bearer ' . $result['accessToken']);
            $this->setRefreshToken($result['refreshToken']);

            return 'ok';
        }

        throw new PurchaseFailedException('Refresh token encountered an error.');
    }

    /**
     * Call curl
     *
     * @param $url
     * @param $arrayData
     * @param bool $haveAuth
     * @param int $try
     * @param string $method
     * @return bool|mixed|string
     * @throws PurchaseFailedException
     */
    private function callCurl($url, $arrayData, $haveAuth = false, $try = 0, $method = 'POST')
    {
        $data = $arrayData;
        $jsonData = json_encode($data);
        $accessToken = '';

        if ($haveAuth) {
            $accessToken = $this->getAccessToken();
        }

        $ch = curl_init($this->baseUrl . $url);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Jibit.class Rest Api');
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: ' . $accessToken,
            'Content-Length: ' . strlen($jsonData)
        ]);

        $result = curl_exec($ch);
        $err = curl_error($ch);
        $result = json_decode($result, true);
        curl_close($ch);

        if ($err) {
            throw new PurchaseFailedException('cURL Error #:' . $err);
        }

        if (empty($result['errors'])) {
            return $result;
        }

        if ($haveAuth === true && $result['errors'][0]['code'] === 'security.auth_required') {
            $this->generateToken(true);

            if ($try === 0) {
                return $this->callCurl($url, $arrayData, $haveAuth, 1, $method);
            }

            throw new PurchaseFailedException('Authentication encountered an error.');
        }

        return $result;
    }

    /**
     * Get access token
     *
     * @return mixed
     */
    public function getAccessToken()
    {
        return $this->accessToken;
    }

    /**
     * Set access token
     *
     * @param mixed $accessToken
     */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;
    }

    /**
     * Set refresh token
     *
     * @param mixed $refreshToken
     */
    public function setRefreshToken($refreshToken)
    {
        $this->refreshToken = $refreshToken;
    }

    /**
     * Generate new token
     *
     * @return string
     * @throws PurchaseFailedException
     * @throws InvalidArgumentException
     */
    private function generateNewToken()
    {
        $data = [
            'apiKey' => $this->apiKey,
            'secretKey' => $this->secretKey,
        ];

        $result = $this->callCurl('/tokens', $data);

        if (empty($result['accessToken'])) {
            throw new PurchaseFailedException('Token generation encoutered an error.');
        }

        if (! empty($result['accessToken'])) {
            $this->cache->set('accessToken', 'Bearer ' . $result['accessToken'], 24 * 60 * 60 - 60);
            $this->cache->set('refreshToken', $result['refreshToken'], 48 * 60 * 60 - 60);

            $this->setAccessToken('Bearer ' . $result['accessToken']);
            $this->setRefreshToken($result['refreshToken']);

            return 'ok';
        }

        throw new PurchaseFailedException('Token generation encoutered an error.');
    }

    /**
     * Verify payment
     *
     * @param string $purchaseId
     * @return bool|mixed|string
     * @throws PurchaseFailedException
     */
    public function paymentVerify($purchaseId)
    {
        $this->generateToken();
        $data = [];

        return $this->callCurl('/purchases/' . $purchaseId . '/verify', $data, true, 0, 'GET');
    }
}