Files
HRM-System/vendor/coingate/coingate-php/lib/BaseClient.php
2026-04-13 08:19:53 +08:00

362 lines
10 KiB
PHP

<?php
namespace CoinGate;
use CoinGate\Exception\Api\BadAuthToken;
use CoinGate\Exception\Api\BadRequest;
use CoinGate\Exception\Api\NotFound;
use CoinGate\Exception\Api\OrderIsNotValid;
use CoinGate\Exception\Api\OrderNotFound;
use CoinGate\Exception\Api\Unauthorized;
use CoinGate\Exception\Api\UnprocessableEntity;
use CoinGate\Exception\ApiErrorException;
use CoinGate\Exception\InvalidArgumentException;
use CoinGate\Exception\InternalServerError;
use CoinGate\Exception\RateLimitException;
use CoinGate\Exception\UnknownApiErrorException;
use CoinGate\HttpClient\ClientInterface as HttpClientInterface;
use CoinGate\HttpClient\CurlClient;
use CoinGate\Services\OrderService;
use CoinGate\Services\PublicService;
use Exception;
/**
* Client used to send requests to CoinGate's API
*
* @property OrderService $order
* @mixin PublicService
*/
class BaseClient implements ClientInterface
{
/**
* @var string
*/
public const VERSION = '4.1.0';
/**
* @var string default base URL for CoinBase's API
*/
public const DEFAULT_API_BASE = 'https://api.coingate.com';
/**
* @var string default base URL for CoinBase's API
*/
public const SANDBOX_DEFAULT_API_BASE = 'https://api-sandbox.coingate.com';
/**
* @var HttpClientInterface|null
*/
protected static $httpClient = null;
/**
* @var array<string, string>|null
*/
protected static $appInfo = null;
/**
* @var array<string, mixed>
*/
private $config;
/**
* Initializes a new instance of CoinGate's BaseClient class.
*
* The constructor takes a single argument. The argument can be a string, in which case it
* should be the API key. It can also be an array with various configuration settings.
*
* @param mixed $apiKey
* @param bool|false $useSandboxEnv
*/
public function __construct($apiKey = null, bool $useSandboxEnv = false)
{
$config = array_merge(
$this->getDefaultConfig(),
[
'api_key' => $apiKey,
'environment' => ! $useSandboxEnv ? 'live' : 'sandbox'
]
);
$this->validateConfig($config);
// check if trying to connect to sandbox environment
if ($useSandboxEnv) {
$config['api_base'] = self::SANDBOX_DEFAULT_API_BASE;
}
$this->config = $config;
}
/**
* Gets the API key used by the client to send requests.
*
* @return string|null
*/
public function getApiKey(): ?string
{
return $this->config['api_key'];
}
/**
* @param string|null $apiKey
* @return $this
*/
public function setApiKey(string $apiKey = null): BaseClient
{
$this->config['api_key'] = $apiKey;
$this->validateConfig($this->config);
return $this;
}
/**
* Gets the base URL for CoinGate's API.
*
* @return string
*/
public function getApiBase(): string
{
return $this->config['api_base'];
}
/**
* Gets the environment used to interact with CoinGate's API.
*
* @return string
*/
public function getEnvironment(): string
{
return $this->config['environment'];
}
/**
* @param string $environment
* @return $this
*/
public function setEnvironment(string $environment): BaseClient
{
$this->config['environment'] = $environment;
$this->validateConfig($this->config);
return $this;
}
/**
* @return array<string, mixed>
*/
private function getDefaultConfig(): array
{
return [
'api_key' => null,
'api_base' => self::DEFAULT_API_BASE,
'environment' => 'live'
];
}
/**
* @param array<string, mixed> $config
* @return void
*/
private function validateConfig(array $config = [])
{
if ($config['api_key'] !== null) {
if (! is_string($config['api_key'])) {
throw new InvalidArgumentException('api_key must be null or a string');
}
if (empty($config['api_key'])) {
throw new InvalidArgumentException('api_key cannot be empty string');
}
if (preg_match('/\s/', $config['api_key'])) {
throw new InvalidArgumentException('api_key cannot contain whitespace');
}
}
// api_base
if (! is_string($config['api_base'])) {
throw new InvalidArgumentException('api_base must be a string');
}
// environment
if (! in_array($config['environment'], ['live', 'sandbox'])) {
throw new InvalidArgumentException('Environment does not exist. Available environments: live, sandbox.');
}
// check absence of extra keys
$extraConfigKeys = array_diff(array_keys($config), array_keys($this->getDefaultConfig()));
if (! empty($extraConfigKeys)) {
$invalidKeys = "'" . implode("', '", $extraConfigKeys) . "'";
throw new InvalidArgumentException('Found unknown key(s) in configuration array: ' . $invalidKeys);
}
}
/**
* @param string $method
* @return string[]
*/
protected function getDefaultHeaders(string $method): array
{
$headers = [];
if (($apiKey = $this->getApiKey()) !== null) {
$headers[] = 'Authorization: Bearer ' . $apiKey;
}
if (in_array(strtolower($method), ['post', 'patch'])) {
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
}
if (($appInfo = self::getAppInfo()) !== null) {
$headers[] = 'User-Agent: CoinGate/v2 (PHP Library v' . self::VERSION . ', '
. $appInfo['name'] . (! empty($appInfo['version']) ? ' v' . $appInfo['version'] : '') . ')';
} else {
$headers[] = 'User-Agent: CoinGate/v2 (PHP Library v' . self::VERSION . ')';
}
return $headers;
}
/**
* @param mixed $apiKey
* @param bool $useSandboxEnv
* @return bool
*/
public static function testConnection($apiKey, bool $useSandboxEnv = false): bool
{
$client = new self($apiKey, $useSandboxEnv);
try {
$client->request('get', '/v2/auth/test');
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Send a request to CoinGate API.
*
* @param string $method the HTTP method
* @param string $path the path of the request
* @param array<string, string> $params the parameters of the request
*
* @throws ApiErrorException
*/
public function request(string $method, string $path, array $params = [])
{
// generate default headers
$headers = $this->getDefaultHeaders($method);
// generate absolute url
$absUrl = $this->getApiBase() . '/' . trim($path, '/');
[$responseBody, $httpStatus] = $this->getHttpClient()->request($method, $absUrl, $headers, $params);
$responseFormatted = json_decode($responseBody, true) ?: $responseBody;
if ($httpStatus !== 200) {
$this->handleErrorResponse($responseFormatted, $httpStatus);
}
return is_array($responseFormatted)
? (object)$responseFormatted
: $responseFormatted;
}
/**
* @throws ApiErrorException
*/
/**
* @param mixed $response
* @param int $httpStatus
* @return void
*
* @throws ApiErrorException
*/
public function handleErrorResponse($response, int $httpStatus)
{
$reason = $response['reason'] ?? null;
if ($httpStatus === 400) {
throw BadRequest::factory($response, $httpStatus);
} elseif ($httpStatus === 401) {
switch ($reason) {
case 'BadAuthToken':
throw BadAuthToken::factory($response, $httpStatus);
default:
throw Unauthorized::factory($response, $httpStatus);
}
} elseif ($httpStatus === 404) {
switch ($reason) {
case 'OrderNotFound':
throw OrderNotFound::factory($response, $httpStatus);
default:
throw NotFound::factory($response, $httpStatus);
}
} elseif ($httpStatus === 422) {
switch ($reason) {
case 'OrderIsNotValid':
throw OrderIsNotValid::factory($response, $httpStatus);
case 'OrderNotFound':
throw OrderNotFound::factory($response, $httpStatus);
default:
throw UnprocessableEntity::factory($response, $httpStatus);
}
} elseif ($httpStatus === 429) {
throw RateLimitException::factory($response, $httpStatus);
} elseif (in_array($httpStatus, [500, 504])) {
throw InternalServerError::factory($response, $httpStatus);
}
throw UnknownApiErrorException::factory($response, $httpStatus);
}
/**
* @param HttpClientInterface $httpClient
* @return void
*/
public static function setHttpClient(HttpClientInterface $httpClient)
{
self::$httpClient = $httpClient;
}
/**
* @return HttpClientInterface
*/
protected function getHttpClient(): HttpClientInterface
{
if (! self::$httpClient) {
self::$httpClient = CurlClient::instance();
}
return self::$httpClient;
}
/**
* @return array<string, string>|null The application's information
*/
public static function getAppInfo(): ?array
{
return self::$appInfo;
}
/**
* @param string $appName The application's name
* @param null|string $appVersion The application's version
*/
public static function setAppInfo(string $appName, string $appVersion = null): void
{
self::$appInfo = [];
self::$appInfo['name'] = trim($appName);
self::$appInfo['version'] = trim($appVersion);
}
}