Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/boxberry.delivery/lib/boxberry/api/ |
Upload File : |
<?php namespace Boxberry\Api; use Bitrix\Main\Application; use Bitrix\Main\ArgumentException; use Bitrix\Main\Config\Option; use Bitrix\Main\Error; use Bitrix\Main\IO\Directory; use Bitrix\Main\IO\File; use Bitrix\Main\Result; use Bitrix\Main\Text\StringHelper; use Bitrix\Main\Web\Http\ClientException; use Bitrix\Main\Web\Http\FormStream; use Bitrix\Main\Web\Http\Method; use Bitrix\Main\Web\Http\Request; use Bitrix\Main\Web\Http\Stream; use Bitrix\Main\Web\Uri; use Boxberry\Api\Entity\CompanyApiSettings; use Boxberry\Api\Entity\DadataResult; use Boxberry\Api\Entity\DeliveryCalculationParams; use Boxberry\Api\Entity\DeliveryCalculationResult; use Boxberry\Api\Entity\GetKeyIntegration; use Boxberry\Api\Entity\GetLastStatusData; use Boxberry\Api\Entity\ParcelInfoResult; use Boxberry\Api\Entity\ParcelParams; use Boxberry\Api\Entity\ParcelResult; use Boxberry\Api\Entity\ParselSendResult; use Boxberry\Api\Entity\PointsDescriptionResult; use Boxberry\Api\Entity\ZipCheck; use Boxberry\Api\Exceptions\BoxberryApiException; use Bitrix\Main\Data\Cache; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Text\Encoding; use Bitrix\Main\Web\HttpClient; use Bitrix\Main\Web\Json; use Boxberry\Api\Entity\WidgetSettings; use Boxberry\Bitrix\Helpers\Delivery; use Boxberry\Bitrix\Helpers\Logger; use Boxberry\Bitrix\Helpers\Options; use Boxberry\Client\Response; use Psr\Http\Message\ResponseInterface; use Psr\Log\LogLevel; Loc::loadMessages(__FILE__); class Client { public const TEST_TOKEN = 'b050ebfcdb91b9117932f89d05d40bb4'; public const YANDEX_TEST_TOKEN = '0f779ef5a201ac33d30c571a961e4604'; public const SOURCE_BOXBERRY = 'BOXBERRY'; public const SOURCE_YANDEX = 'YANDEX'; public const TEST_WIDGET_KEY = '1$1WxHaMxOqq-BN1iQI9vQTIQJe9r1VoC_'; public const YANDEX_TEST_WIDGET_KEY = '1$bf44ea16088a8abb821c3ca623644c6a'; private const DADATA_TOKEN = 'Token a105367bc6479ffb2a355fad7536e0fb504c1b97'; private const API_URL = 'https://api.boxberry.ru/json.php'; public const MODULE_UPDATE_URL = 'https://bitrix.boxberry.ru/new_module_updates/checkModuleUpdate.php'; private const DADATA_API_URL = 'https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address'; private const CACHE_DIR = '/bb_api/'; public const FILE_UPLOAD_DIR = '/upload/boxberry/'; private array $defaultHeaders = [ 'Content-Type' => 'application/json' ]; private string $token; private bool $logEnabled; private string $logFilePath; private int $logFileSize; private HttpClient $client; private Cache $cache; private ?Logger $logger = null; private ?string $docRoot; private Uri $uri; private string $requestType; private int $responseCode = 0; private bool $yaDelivery = false; private bool $authorization = false; private string $source = self::SOURCE_BOXBERRY; public function __construct($token, $socketTimeout = 60, $streamTimeout = 60) { $this->client = new HttpClient(['socketTimeout' => $socketTimeout, 'streamTimeout' => $streamTimeout]); $this->cache = Cache::createInstance(); $this->token = trim($token); $this->uri = new Uri(static::API_URL); $this->setLogConfig(); if ($this->logEnabled) { $this->docRoot = Application::getDocumentRoot(); } } public function getLogger(): ?Logger { return $this->logger; } public function initSource(): void { try { $companyApiSettings = $this->companyApiSettings(); $this->yaDelivery = $companyApiSettings->isYaDelivery(); } catch (\Exception $e) { $this->yaDelivery = false; } $this->yaDelivery ? $this->setSource(self::SOURCE_YANDEX) : $this->setSource(self::SOURCE_BOXBERRY); } public function isSourceBoxberry(): bool { return $this->getSource() === self::SOURCE_BOXBERRY; } public function isSourceYandex(): bool { return $this->getSource() === self::SOURCE_YANDEX; } protected function setLogConfig(): void { $this->logEnabled = Options::isApiLogEnabled(); $this->logFilePath = Options::getApiLogFilePath(); $this->logFileSize = Options::getApiLogFileSize(); } public function log(string $level, string $message): void { if ($this->logEnabled) { if ($this->logger === null) { $this->logger = new Logger($this->docRoot . $this->logFilePath, $this->logFileSize * 1024 * 1024); } try { $this->logger->log($level, $message); } catch (\Throwable $e) { return; } } } public function getResponseHeaders(): array { return $this->client->getHeaders()->getHeaders(); } public function getRequestHeaders(): array { return $this->client->getRequestHeaders()->getHeaders(); } public static function convertEncoding($data, $targetCharset = 'UTF-8'): mixed { if (is_array($data)) { $convertedData = []; foreach ($data as $key => $value) { $convertedData[$key] = Client::convertEncoding($value, $targetCharset); } } else { $sourceCharset = Encoding::detectUtf8($data) ? 'UTF-8' : 'CP1251'; $convertedData = Encoding::convertEncoding($data, $sourceCharset, $targetCharset); } return $convertedData; } protected function setHeaders(array $headers): void { $this->client->setHeaders($headers); } protected function setResult(array $params): void { if ($this->isPost()) { $this->client->post($this->uri->getLocator(), Json::encode($params, JSON_UNESCAPED_UNICODE)); } if ($this->isGet()) { $this->uri->addParams($params); $this->client->get($this->uri->getUri()); } } protected function isGet(): bool { return $this->requestType === HttpClient::HTTP_GET; } protected function isPost(): bool { return $this->requestType === HttpClient::HTTP_POST; } /** * @throws BoxberryApiException */ public function apiRequest($requestType = HttpClient::HTTP_POST, $params = [], $headers = [], $cacheTime = 86400) { $credentials = [ 'token' => $this->token, ]; if (isset($params['method'])) { $params['method'] = ucfirst($params['method']); } $this->requestType = $requestType; $headers = empty($headers) ? $this->defaultHeaders : $headers; $params = Client::convertEncoding(array_merge($credentials, $params)); $cacheKey = md5($this->uri->getLocator() . $requestType . serialize($params) . serialize($headers)); $cacheInitDir = self::CACHE_DIR . StringHelper::camel2snake($params['method']) . DIRECTORY_SEPARATOR; if ($this->cache->initCache($cacheTime, $cacheKey, $cacheInitDir)) { $this->log(LogLevel::INFO, $this->makeApiLogMessage(true, $requestType, $params, $headers, $this->cache->getVars())); $this->responseCode = 200; $this->authorization = true; return $this->cache->getVars(); } $this->setHeaders($headers); $this->setResult($params); try { $responseBody = Json::decode($this->client->getResult()); } catch (\Exception $e) { $this->log(LogLevel::ERROR, $e->getMessage()); throw new BoxberryApiException($e->getMessage()); } $this->responseCode = $this->client->getStatus(); $this->responseCode === 200 ? $this->authorization = true : $this->authorization = false; if ($this->client->getStatus() === 200 || $this->client->getStatus() === 401) { $this->cache->startDataCache($cacheTime, $cacheKey, $cacheInitDir); $this->cache->endDataCache($responseBody); $this->log(LogLevel::INFO, $this->makeApiLogMessage(false, $requestType, $params, $headers, $responseBody)); return $responseBody; } if ($errors = $this->client->getError()) { foreach ($errors as $errorCode => $errorMessage) { $this->log(LogLevel::ERROR, $errorMessage); } } if ($responseBody) { $this->log(LogLevel::ERROR, $this->makeApiLogMessage(false, $requestType, $params, $headers, $responseBody)); throw new BoxberryApiException($responseBody); } throw new BoxberryApiException(Loc::getMessage('BB_INCORRECT_HTTP_RESULT_STATUS_CODE') . ':' . $this->client->getStatus() . ' | ' . $params['method']); } private function makeApiLogMessage($fromCache, $method, $params, $headers, $responseBody): string { $time = date('d.m.Y H:i:s'); $logUrl = Client::convertEncoding(self::API_URL); $logMethod = Client::convertEncoding($method); $logParams = Client::convertEncoding($params); $logHeaders = Client::convertEncoding($headers); $logResponseBody = Client::convertEncoding($responseBody); $logMessage = "From cache: " . ($fromCache ? 'yes' : 'no') . "\n"; $logMessage .= "Time: $time\n"; $logMessage .= "URL: $logUrl\n"; $logMessage .= "Method: $logMethod\n"; $logMessage .= "Headers: " . print_r($logHeaders, true) . "\n"; $logMessage .= "Parameters: " . print_r($logParams, true) . "\n"; $logMessage .= "Response Body: " . print_r($logResponseBody, true) . "\n"; return $logMessage; } /** * @throws BoxberryApiException */ public function companyApiSettings(): CompanyApiSettings { $params = [ 'method' => __FUNCTION__ ]; $result = $this->apiRequest(HttpClient::HTTP_GET, $params, [], 300); return new CompanyApiSettings($result); } /** * @throws BoxberryApiException */ public function parcelInfo($trackNumber): ParcelInfoResult { $params = [ 'method' => __FUNCTION__, 'parcels' => [ [ 'track' => $trackNumber ] ] ]; $result = $this->apiRequest(HttpClient::HTTP_POST, $params, [], 0); return new ParcelInfoResult($result); } /** * @throws BoxberryApiException */ public function getKeyIntegration(): GetKeyIntegration { $params = [ 'method' => __FUNCTION__ ]; $key = $this->apiRequest(HttpClient::HTTP_GET, $params, [], 0); return new GetKeyIntegration($key); } /** * @throws BoxberryApiException */ public function deliveryCalculation(DeliveryCalculationParams $deliveryCalculationParams): DeliveryCalculationResult { $costs = $this->apiRequest(HttpClient::HTTP_POST, $deliveryCalculationParams->toArray()); return new DeliveryCalculationResult($costs); } /** * @param DeliveryCalculationParams[] $deliveryCalculationParamsArray * @throws BoxberryApiException */ public function deliveryCalculationMulti(array $deliveryCalculationParamsArray): array { $results = []; foreach ($deliveryCalculationParamsArray as $deliveryCalculationParams) { $costs = $this->apiRequest(HttpClient::HTTP_POST, $deliveryCalculationParams->toArray()); $results[] = new DeliveryCalculationResult($costs); } return $results; } /** * @throws BoxberryApiException */ public function listCitiesFull($countryCode = '') { $params = [ 'method' => __FUNCTION__, ]; if ($countryCode) { $params['CountryCode'] = $countryCode; } return $this->apiRequest(HttpClient::HTTP_GET, $params, [], 0); } /** * @throws BoxberryApiException */ public function widgetSettings(): WidgetSettings { $params = [ 'method' => __FUNCTION__ ]; $widgetSettings = $this->apiRequest(HttpClient::HTTP_GET, $params); return new WidgetSettings($widgetSettings); } /** * @throws BoxberryApiException */ public function zipCheck(string $zip): ZipCheck { $params = [ 'method' => __FUNCTION__, 'Zip' => $zip ]; $zipCheck = $this->apiRequest(HttpClient::HTTP_GET, $params); return new ZipCheck($zipCheck); } /** * @throws BoxberryApiException */ public function pointsForParcels() { $params = [ 'method' => __FUNCTION__, ]; return $this->apiRequest(HttpClient::HTTP_GET, $params, [], 0); } /** * @throws BoxberryApiException */ public function pointsDescription(string $code, $photo = 0): PointsDescriptionResult { $params = [ 'method' => __FUNCTION__, 'code' => $code, 'photo' => $photo ]; $data = $this->apiRequest(HttpClient::HTTP_GET, $params, [], 0); return new PointsDescriptionResult($data); } /** * @throws BoxberryApiException */ public function parselCreate(ParcelParams $parcelParams): ParcelResult { $data = $this->apiRequest(HttpClient::HTTP_POST, $parcelParams->toArray(), [], 0); return new ParcelResult($data); } /** * @throws BoxberryApiException */ public function parselSend(array $trackNumbers): ParselSendResult { $params = [ 'method' => __FUNCTION__, 'ImIds' => implode(',', $trackNumbers) ]; $data = $this->apiRequest(HttpClient::HTTP_GET, $params, [], 0); return new ParselSendResult($data); } /** * @throws BoxberryApiException */ public function getLastStatusData(array $trackNumbers): GetLastStatusData { $params = [ 'method' => __FUNCTION__, 'trackNumbers' => $trackNumbers ]; $statuses = $this->apiRequest(HttpClient::HTTP_POST, $params, [], 0); return new GetLastStatusData($statuses); } /** * @throws BoxberryApiException */ public function cancelOrder(string $trackNumber, string $cancelType = '1') { $params = [ 'method' => __FUNCTION__, 'track' => $trackNumber, 'cancelType' => $cancelType ]; return $this->apiRequest(HttpClient::HTTP_POST, $params, [], 0); } /** * @throws BoxberryApiException */ public function changeOrderStorageDate($storageDate, $trackNumber) { $params = [ 'method' => __FUNCTION__, 'storageDate' => $storageDate, 'track' => $trackNumber ]; return $this->apiRequest(HttpClient::HTTP_GET, $params, [], 0); } public function getToken(): string { return $this->token; } public function setToken(string $token): void { $this->token = $token; } public function setTestToken(): void { $this->token = self::TEST_TOKEN; } public function getClient(): HttpClient { return $this->client; } public function setClient(HttpClient $client): void { $this->client = $client; } public function getCache(): Cache { return $this->cache; } public function setCache(Cache $cache): void { $this->cache = $cache; } public function getDefaultHeaders(): array { return $this->defaultHeaders; } public function setDefaultHeaders(array $defaultHeaders): void { $this->defaultHeaders = $defaultHeaders; } public static function getTestToken(): string { return self::TEST_TOKEN; } public static function getApiUrl(): string { return self::API_URL; } public function checkApiToken(): Result { $result = new Result(); try { $key = $this->getKeyIntegration(); $this->initSource(); $result->setData(['key' => $key->getKey(), 'source' => $this->source]); } catch (BoxberryApiException $e) { $result->addError(new Error($e->getMessage())); } return $result; } public static function downloadFile($url, $fileName): Result { $httpClient = new HttpClient(); $result = new Result(); $absolutePath = Application::getDocumentRoot() . self::FILE_UPLOAD_DIR; $filePath = $absolutePath . $fileName; if (!Directory::isDirectoryExists($absolutePath)) { Directory::createDirectory($absolutePath); } if ($httpClient->download($url, $filePath)) { $fileUrl = self::FILE_UPLOAD_DIR . $fileName; $result->setData([ 'message' => Loc::getMessage('BB_FILE_DOWNLOADED_SUCCESSFULLY'), 'path' => $fileUrl ]); } else { $result->addError(new Error(Loc::getMessage('BB_ERROR_DOWNLOAD_FILE') . $url, 0, ['url' => $url])); } return $result; } public static function getFilePathByrOrderId($orderId, $fileType = 'label'): ?string { $absolutePath = Application::getDocumentRoot() . self::FILE_UPLOAD_DIR; $fileName = $fileType . '_' . $orderId . '.pdf'; $filePath = $absolutePath . $fileName; if (File::isFileExists($filePath)) { return self::FILE_UPLOAD_DIR . $fileName; } return null; } public static function getFilePathByName($fileName): ?string { $absolutePath = Application::getDocumentRoot() . self::FILE_UPLOAD_DIR; $filePath = $absolutePath . $fileName; if (File::isFileExists($filePath)) { return self::FILE_UPLOAD_DIR . $fileName; } return null; } public static function getParselSendNotIncludedTracks($errorMessage): array { $startPos = strpos($errorMessage, ':'); if ($startPos !== false) { $idsStr = substr($errorMessage, $startPos + 1); $ids = explode(',', $idsStr); return array_map(function($id) { return trim($id, ' !'); }, $ids); } return []; } public static function makeActFileNameByOrderIds($orderIds): string { return 'act_' . md5(implode('_', $orderIds)) . '.pdf'; } public static function getModuleUpdate(string $url, $version, $archiveFormat): bool|string { $path = Application::getDocumentRoot() . Client::FILE_UPLOAD_DIR . 'updates/' . $version . '/boxberry' . $archiveFormat; if ((new HttpClient())->download($url, $path)) { return $path; } return false; } public static function checkModuleUpdate(): Result { return self::updateRequest(json_encode(self::getCheckUpdateArray())); } public static function logSuccessUpdate(): Result { return self::updateRequest(json_encode(array_merge(self::getCheckUpdateArray(), ['status' => 'success']))); } private static function updateRequest($postData): Result { $result = new Result(); $httpClient = new HttpClient(); $httpClient->setHeader('Content-Type', 'application/json'); $httpClient->post(self::MODULE_UPDATE_URL, $postData); $response = $httpClient->getResult(); $statusCode = $httpClient->getStatus(); try { $response = Json::decode($response); } catch (\Exception $e) { $result->addError(new Error($e->getMessage())); } if ($statusCode === 200 && $response) { $result->setData($response); } else { $result->addError(new Error($response['error'], $statusCode)); } return $result; } private static function getCheckUpdateArray(): array { return [ 'client_version' => Options::getModuleVersion(), 'client_encoding' => LANG_CHARSET === 'windows-1251' ? 'cp1251' : 'utf8', 'client_host' => $_SERVER['HTTP_HOST'], 'archive_format' => \CBXArchive::GetAvailableFormats() ]; } /** * @throws BoxberryApiException * @throws ArgumentException */ public static function daDataRequest($address): DadataResult { $cache = Cache::createInstance(); $cacheTime = 86400; $cacheId = md5($address); if ($cache->initCache($cacheTime, $cacheId, self::CACHE_DIR . 'dadata' . DIRECTORY_SEPARATOR)) { $result = $cache->getVars(); } else { $params = [ 'query' => $address, 'locations' => [ [ 'country' => '*', ] ] ]; $headers = [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', 'Authorization' => Options::isEnableDadataUserToken() ? 'Token ' . Options::getDadataUserToken() : self::DADATA_TOKEN ]; $httpClient = new HttpClient(); $httpClient->setHeaders($headers); $httpClient->post(self::DADATA_API_URL, json_encode(Client::convertEncoding($params), JSON_UNESCAPED_UNICODE)); $responseBody = $httpClient->getResult(); $result = new DadataResult(Json::decode($responseBody)); if ($cache->startDataCache()) { $cache->endDataCache($result); } } return $result; } public function getSource(): string { return $this->source; } public function setSource(string $source): void { $this->source = $source; Option::set(Options::getModuleId(), substr($this->token, 0, 8) . '_' . 'SOURCE', $source); } public function getResponseCode(): int { return $this->responseCode; } public function setResponseCode(int $responseCode): void { $this->responseCode = $responseCode; } public function isYaDelivery(): bool { return $this->yaDelivery; } public function setYaDelivery(bool $yaDelivery): void { $this->yaDelivery = $yaDelivery; } public function isAuthorization(): bool { return $this->authorization; } public function setAuthorization(bool $authorization): void { $this->authorization = $authorization; } }