diff --git a/src/Actions/Schedule/CollectOrders.php b/src/Actions/Schedule/CollectOrders.php new file mode 100644 index 0000000..edba458 --- /dev/null +++ b/src/Actions/Schedule/CollectOrders.php @@ -0,0 +1,58 @@ + 'id', + 'order' => 'ASC', + 'paginate' => true, + 'limit' => self::ORDERS_LIMIT, + 'return' => 'ids', + ], + ); + + for ($page = 1, $maxPages = 1; $page <= $maxPages; $page++) { + $query->set('page', $page); + $result = $query->get_orders(); + + $maxPages = $result->max_num_pages; + + $response = $this->cdekCoreApi->sendTaskData( + $this->taskId, + new TaskOutputData( + 'success', + [ + 'orders' => array_map( + static fn($order) => (string)$order, + $result->orders, + ) + ], + $page, + $maxPages + ) + ); + + $this->initData($response); + } + } + } +} diff --git a/src/Actions/Schedule/ReindexOrders.php b/src/Actions/Schedule/ReindexOrders.php new file mode 100644 index 0000000..ee46781 --- /dev/null +++ b/src/Actions/Schedule/ReindexOrders.php @@ -0,0 +1,61 @@ +initTaskData(); + } + + public static function getName(): string + { + return 'restore-order-uuids'; + } + + /** + * @return void + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + public function start(): void + { + if (empty($this->getTaskMeta())) { + throw new CdekScheduledTaskException( + '[CDEKDelivery] Failed to get orders meta info', + 'cdek_error.core.data', + ); + } + + foreach ($this->getTaskMeta() as $arOrder) { + OrderMetaData::updateMetaByOrderId( + $arOrder['external_id'], + [ + 'order_uuid' => $arOrder['id'], + ], + ); + } + + $this->initData($this->cdekCoreApi->sendTaskData( + $this->taskId, + new TaskOutputData('success'), + )); + } + } +} diff --git a/src/Cache/FileCache.php b/src/Cache/FileCache.php new file mode 100644 index 0000000..7c7b289 --- /dev/null +++ b/src/Cache/FileCache.php @@ -0,0 +1,67 @@ + Loader::getPluginPath() . DIRECTORY_SEPARATOR . self::CACHE_FILE_NAME], + true); + } + }else{ + if(!is_writable(Loader::getPluginPath())){ + throw new CdekApiException('[CDEKDelivery] Failed check directory rights', + 'cdek_error.cache.rights', + ['path' => Loader::getPluginPath()], + true); + } + } + + + $logFile = fopen( Loader::getPluginPath() . DIRECTORY_SEPARATOR . self::CACHE_FILE_NAME, 'w+'); + + fwrite($logFile, 'deliveryMethod = Helper::getActualShippingMethod($shippingInstanceId); - $this->apiUrl = $this->getApiUrl(); - - if (!isset($_ENV['CDEK_REST_API']) && $this->deliveryMethod->get_option('test_mode') === 'yes') { - $this->clientId = Config::TEST_CLIENT_ID; - $this->clientSecret = Config::TEST_CLIENT_SECRET; - } else { - $this->clientId = $this->deliveryMethod->get_option('client_id'); - $this->clientSecret = $this->deliveryMethod->get_option('client_secret'); - } - - $this->tokenStorage = $tokenStorage ?? new DBTokenStorage(); - } +namespace { - private function getApiUrl(): string - { - if ($this->deliveryMethod->get_option('test_mode') === 'yes') { - return $_ENV['CDEK_REST_API'] ?? Config::TEST_API_URL; - } + defined('ABSPATH') or exit; +} - return Config::API_URL; - } +namespace Cdek { - /** - * @throws \Cdek\Exceptions\CdekApiException - */ - final public function checkAuth(): bool - { - return (bool)$this->tokenStorage->getToken(); - } + use Cdek\Contracts\TokenStorageContract; + use Cdek\Enums\BarcodeFormat; + use Cdek\Exceptions\CdekApiException; + use Cdek\Exceptions\RestApiInvalidRequestException; + use Cdek\Helpers\DBTokenStorage; + use Cdek\Transport\HttpClient; + use WC_Shipping_Method; - /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException - */ - public function fetchToken(): string + class CdekApi { - $body = json_decode(HttpClient::sendRequest($this->getAuthUrl(), 'POST'), true); - if ($body === null || isset($body['error_description']) || isset($body['error'])) { - throw new CdekApiException('[CDEKDelivery] Failed to get the token', - 'cdek_error.token.auth', - $body, - true); + private const TOKEN_PATH = 'oauth/token'; + private const REGION_PATH = 'location/cities'; + private const ORDERS_PATH = 'orders/'; + private const PVZ_PATH = 'deliverypoints'; + private const CALC_LIST_PATH = 'calculator/tarifflist'; + private const CALC_PATH = 'calculator/tariff'; + private const WAYBILL_PATH = 'print/orders/'; + private const BARCODE_PATH = 'print/barcodes/'; + private const CALL_COURIER = 'intakes'; + + private string $apiUrl; + + private string $clientId; + private string $clientSecret; + private WC_Shipping_Method $deliveryMethod; + + private TokenStorageContract $tokenStorage; + + + public function __construct(?int $shippingInstanceId = null, ?TokenStorageContract $tokenStorage = null) + { + $this->deliveryMethod = Helper::getActualShippingMethod($shippingInstanceId); + $this->apiUrl = $this->getApiUrl(); + + if (!isset($_ENV['CDEK_REST_API']) && $this->deliveryMethod->get_option('test_mode') === 'yes') { + $this->clientId = Config::TEST_CLIENT_ID; + $this->clientSecret = Config::TEST_CLIENT_SECRET; + } else { + $this->clientId = $this->deliveryMethod->get_option('client_id'); + $this->clientSecret = $this->deliveryMethod->get_option('client_secret'); + } + + $this->tokenStorage = $tokenStorage ?? new DBTokenStorage(); } - return $body['access_token']; - } - private function getAuthUrl(): string - { - return sprintf('%s%s?%s', - $this->apiUrl, - self::TOKEN_PATH, - http_build_query([ - 'grant_type' => 'client_credentials', - 'client_id' => $this->clientId, - 'client_secret' => $this->clientSecret, - ])); - } + private function getApiUrl(): string + { + if ($this->deliveryMethod->get_option('test_mode') === 'yes') { + return $_ENV['CDEK_REST_API'] ?? Config::TEST_API_URL; + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - final public function getOrder(string $uuid) - { - $url = $this->apiUrl . self::ORDERS_PATH . $uuid; + return Config::API_URL; + } - return HttpClient::sendCdekRequest($url, 'GET', $this->tokenStorage->getToken()); - } + /** + * @throws \Cdek\Exceptions\CdekApiException + */ + final public function checkAuth(): bool + { + return (bool)$this->tokenStorage->getToken(); + } - /** - * @throws \Cdek\Exceptions\RestApiInvalidRequestException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function createOrder(array $params) - { - $url = $this->apiUrl . self::ORDERS_PATH; - $params['developer_key'] = Config::DEV_KEY; + /** + * @throws \Cdek\Exceptions\CdekApiException + * @throws \JsonException + */ + public function fetchToken(): string + { + $body = json_decode(HttpClient::sendRequest($this->getAuthUrl(), 'POST'), true); + if ($body === null || isset($body['error_description']) || isset($body['error'])) { + throw new CdekApiException('[CDEKDelivery] Failed to get the token', + 'cdek_error.token.auth', + $body, + true); + } + return $body['access_token']; + } - $result = json_decode(HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), $params), true); + private function getAuthUrl(): string + { + return sprintf('%s%s?%s', + $this->apiUrl, + self::TOKEN_PATH, + http_build_query([ + 'grant_type' => 'client_credentials', + 'client_id' => $this->clientId, + 'client_secret' => $this->clientSecret, + ])); + } - $request = $result['requests'][0]; + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + final public function getOrder(string $uuid) + { + $url = $this->apiUrl . self::ORDERS_PATH . $uuid; - if ($request['state'] === 'INVALID') { - throw new RestApiInvalidRequestException(self::ORDERS_PATH, $request['errors']); + return HttpClient::sendCdekRequest($url, 'GET', $this->tokenStorage->getToken()); } - return $result; - } + /** + * @throws \Cdek\Exceptions\RestApiInvalidRequestException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function createOrder(array $params) + { + $url = $this->apiUrl . self::ORDERS_PATH; + $params['developer_key'] = Config::DEV_KEY; - /** - * @throws \Cdek\Exceptions\CdekApiException - */ - public function getFileByLink($link) - { - return HttpClient::sendCdekRequest($link, 'GET', $this->tokenStorage->getToken(), null, true)['body']; - } + $result = json_decode(HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), $params), true); - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function createWaybill($orderUuid) - { - $url = $this->apiUrl . self::WAYBILL_PATH; + $request = $result['requests'][0]; - return HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), ['orders' => ['order_uuid' => $orderUuid]]); - } + if ($request['state'] === 'INVALID') { + throw new RestApiInvalidRequestException(self::ORDERS_PATH, $request['errors']); + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function createBarcode($orderUuid) - { - return HttpClient::sendCdekRequest($this->apiUrl . self::BARCODE_PATH, 'POST', $this->tokenStorage->getToken(), [ - 'orders' => ['order_uuid' => $orderUuid], - 'format' => BarcodeFormat::getByIndex($this->deliveryMethod->get_option('barcode_format', 0)), - ]); - } + return $result; + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function getBarcode($uuid) - { - return HttpClient::sendCdekRequest($this->apiUrl . self::BARCODE_PATH . $uuid, 'GET', $this->tokenStorage->getToken()); - } + /** + * @throws \Cdek\Exceptions\CdekApiException + */ + public function getFileByLink($link) + { + return HttpClient::sendCdekRequest($link, 'GET', $this->tokenStorage->getToken(), null, true)['body']; + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function getWaybill($uuid) - { - return HttpClient::sendCdekRequest($this->apiUrl . self::WAYBILL_PATH . $uuid, 'GET', $this->tokenStorage->getToken()); - } + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function createWaybill($orderUuid) + { + $url = $this->apiUrl . self::WAYBILL_PATH; - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function deleteOrder($uuid) - { - $url = $this->apiUrl . self::ORDERS_PATH . $uuid; + return HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), ['orders' => ['order_uuid' => $orderUuid]]); + } - return HttpClient::sendCdekRequest($url, 'DELETE', $this->tokenStorage->getToken()); - } + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function createBarcode($orderUuid) + { + return HttpClient::sendCdekRequest($this->apiUrl . self::BARCODE_PATH, 'POST', $this->tokenStorage->getToken(), [ + 'orders' => ['order_uuid' => $orderUuid], + 'format' => BarcodeFormat::getByIndex($this->deliveryMethod->get_option('barcode_format', 0)), + ]); + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function calculateTariffList($deliveryParam) - { - $url = $this->apiUrl . self::CALC_LIST_PATH; + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function getBarcode($uuid) + { + return HttpClient::sendCdekRequest($this->apiUrl . self::BARCODE_PATH . $uuid, 'GET', $this->tokenStorage->getToken()); + } - $request = [ - 'type' => $deliveryParam['type'], - 'from_location' => $deliveryParam['from'], - 'to_location' => $deliveryParam['to'], - 'packages' => $deliveryParam['packages'], - ]; + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function getWaybill($uuid) + { + return HttpClient::sendCdekRequest($this->apiUrl . self::WAYBILL_PATH . $uuid, 'GET', $this->tokenStorage->getToken()); + } - return HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), $request); - } + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function deleteOrder($uuid) + { + $url = $this->apiUrl . self::ORDERS_PATH . $uuid; - /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException - */ - public function calculateTariff($deliveryParam) - { - $url = $this->apiUrl . self::CALC_PATH; - - $request = [ - 'type' => $deliveryParam['type'], - 'from_location' => $deliveryParam['from'], - 'tariff_code' => $deliveryParam['tariff_code'], - 'to_location' => $deliveryParam['to'], - 'packages' => $deliveryParam['packages'], - 'services' => array_key_exists('services', - $deliveryParam) ? $deliveryParam['services'] : [], - ]; - - return HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), $request); - } + return HttpClient::sendCdekRequest($url, 'DELETE', $this->tokenStorage->getToken()); + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function getCityCode(string $city, ?string $postcode): int - { - $url = $this->apiUrl . self::REGION_PATH; + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function calculateTariffList($deliveryParam) + { + $url = $this->apiUrl . self::CALC_LIST_PATH; + + $request = [ + 'type' => $deliveryParam['type'], + 'from_location' => $deliveryParam['from'], + 'to_location' => $deliveryParam['to'], + 'packages' => $deliveryParam['packages'], + ]; - //по запросу к api v2 климовск записан как "климовск микрорайон" поэтому добавляем "микрорайон" - if (mb_strtolower($city) === 'климовск') { - $city .= ' микрорайон'; + return HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), $request); } - $cityData = json_decode(HttpClient::sendCdekRequest($url, 'GET', $this->tokenStorage->getToken(), - ['city' => $city, 'postal_code' => $postcode]), false, 512, - JSON_THROW_ON_ERROR); + /** + * @throws \Cdek\Exceptions\CdekApiException + * @throws \JsonException + */ + public function calculateTariff($deliveryParam) + { + $url = $this->apiUrl . self::CALC_PATH; + + $request = [ + 'type' => $deliveryParam['type'], + 'from_location' => $deliveryParam['from'], + 'tariff_code' => $deliveryParam['tariff_code'], + 'to_location' => $deliveryParam['to'], + 'packages' => $deliveryParam['packages'], + 'services' => array_key_exists('services', + $deliveryParam) ? $deliveryParam['services'] : [], + ]; - if (empty($cityData)) { - return -1; + return HttpClient::sendCdekRequest($url, 'POST', $this->tokenStorage->getToken(), $request); } - return $cityData[0]->code; - } + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function getCityCode(string $city, ?string $postcode): int + { + $url = $this->apiUrl . self::REGION_PATH; - /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException - */ - public function getOffices($filter) - { - $url = $this->apiUrl . self::PVZ_PATH; + //по запросу к api v2 климовск записан как "климовск микрорайон" поэтому добавляем "микрорайон" + if (mb_strtolower($city) === 'климовск') { + $city .= ' микрорайон'; + } - $result = HttpClient::sendCdekRequest($url, 'GET', $this->tokenStorage->getToken(), $filter, true); - if (!$result) { - return [ - 'success' => false, - 'message' => esc_html__("In this locality, delivery is available only for \"door-to-door\" tariffs. Select another locality to gain access to \"from warehouse\" tariffs.", 'cdekdelivery'), - ]; + $cityData = json_decode(HttpClient::sendCdekRequest($url, 'GET', $this->tokenStorage->getToken(), + ['city' => $city, 'postal_code' => $postcode]), false, 512, + JSON_THROW_ON_ERROR); + + if (empty($cityData)) { + return -1; + } + + return $cityData[0]->code; } - return $result; - } + /** + * @throws \Cdek\Exceptions\CdekApiException + * @throws \JsonException + */ + public function getOffices($filter) + { + $url = $this->apiUrl . self::PVZ_PATH; + + $result = HttpClient::sendCdekRequest($url, 'GET', $this->tokenStorage->getToken(), $filter, true); + if (!$result) { + return [ + 'success' => false, + 'message' => esc_html__("In this locality, delivery is available only for \"door-to-door\" tariffs. Select another locality to gain access to \"from warehouse\" tariffs.", 'cdekdelivery'), + ]; + } + + return $result; + } - /** - * @throws \JsonException - * @throws \Cdek\Exceptions\CdekApiException - */ - public function callCourier($param) - { - return HttpClient::sendCdekRequest($this->apiUrl . self::CALL_COURIER, 'POST', $this->tokenStorage->getToken(), $param); - } + /** + * @throws \JsonException + * @throws \Cdek\Exceptions\CdekApiException + */ + public function callCourier($param) + { + return HttpClient::sendCdekRequest($this->apiUrl . self::CALL_COURIER, 'POST', $this->tokenStorage->getToken(), $param); + } - /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException - */ - public function courierInfo($uuid) - { - return HttpClient::sendCdekRequest($this->apiUrl . self::CALL_COURIER . '/' . $uuid, 'GET', $this->tokenStorage->getToken()); - } + /** + * @throws \Cdek\Exceptions\CdekApiException + * @throws \JsonException + */ + public function courierInfo($uuid) + { + return HttpClient::sendCdekRequest($this->apiUrl . self::CALL_COURIER . '/' . $uuid, 'GET', $this->tokenStorage->getToken()); + } - /** - * @throws \Cdek\Exceptions\CdekApiException - * @throws \JsonException - */ - public function callCourierDelete($uuid) - { - return HttpClient::sendCdekRequest($this->apiUrl . self::CALL_COURIER . '/' . $uuid, - 'DELETE', - $this->tokenStorage->getToken()); + /** + * @throws \Cdek\Exceptions\CdekApiException + * @throws \JsonException + */ + public function callCourierDelete($uuid) + { + return HttpClient::sendCdekRequest($this->apiUrl . self::CALL_COURIER . '/' . $uuid, + 'DELETE', + $this->tokenStorage->getToken()); + } } } diff --git a/src/CdekCoreApi.php b/src/CdekCoreApi.php new file mode 100644 index 0000000..bdf1701 --- /dev/null +++ b/src/CdekCoreApi.php @@ -0,0 +1,243 @@ +coreClient = new HttpCoreClient(); + $this->generalTokenStorage = $tokenStorage ?? new DBTokenStorage(); + $this->tokenCoreStorage = $tokenCoreStorage ?? new DBCoreTokenStorage(); + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + public function fetchShopToken(): array + { + $response = $this->coreClient->sendCdekRequest( + Config::API_CORE_URL . self::SHOP, + 'POST', + $this->generalTokenStorage->getToken(), + [ + 'name' => get_bloginfo('name'), + 'url' => [ + 'rest' => rest_url(), + 'home' => home_url(), + 'admin' => admin_url(), + ], + ], + ); + + if (empty($response['body'])) { + throw new CdekScheduledTaskException('[CDEKDelivery] Register shop failed', + 'cdek_error.register.shop', + $response + ); + } + + $body = json_decode($response['body'], true); + + if (empty($body['data']['id'])) { + throw new CdekScheduledTaskException('[CDEKDelivery] Failed to get shop uuid', + 'cdek_error.uuid.auth', + $response, + ); + } + + $response = $this->coreClient->sendCdekRequest( + sprintf(Config::API_CORE_URL . self::TOKEN_PATH, $body['data']['id']), + 'POST', + $this->generalTokenStorage->getToken(), + ); + + $body = json_decode($response['body'], true); + + if ($body === null || !$body['success'] || empty($body['data'])) { + throw new CdekScheduledTaskException('[CDEKDelivery] Failed to get shop token', + 'cdek_error.shop_token.auth', + $body, + ); + } + + return ['tokens' => $body['data']]; + } + + /** + * @param string|null $next + * + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + public function taskManager(?string $next = null): array + { + $response = $this->coreClient->sendCdekRequest( + $this->getShopApiUrl() . '/' . self::TASKS . ($next === null ? '' : '?cursor=' . $next), + 'GET', + $this->tokenCoreStorage->getToken(), + ); + + return $this->initData($response); + } + + /** + * @param string $taskId + * @param TaskOutputData $data + * + * @throws \Cdek\Exceptions\CdekApiException + * @throws \Cdek\Exceptions\CdekScheduledTaskException + * @throws \JsonException + */ + public function taskInfo(string $taskId, TaskOutputData $data): array + { + $response = $this->coreClient->sendCdekRequest( + $this->getShopApiUrl() . '/' . self::TASKS . '/' . $taskId, + 'GET', + $this->tokenCoreStorage->getToken(), + [ + 'status' => $data->getStatus(), + 'result' => $data->getData(), + ], + ); + + return $this->initData($response); + } + + /** + * @param string $taskId + * @param TaskOutputData $data + * + * @throws \Cdek\Exceptions\CdekApiException + * @throws \Cdek\Exceptions\CdekScheduledTaskException + * @throws \JsonException + */ + public function sendTaskData(string $taskId, TaskOutputData $data): array + { + $response = $this->coreClient->sendCdekRequest( + $this->getShopApiUrl() . '/' . self::TASKS . '/' . $taskId, + 'PUT', + $this->tokenCoreStorage->getToken(), + [ + 'status' => $data->getStatus(), + 'result' => $data->getData(), + ], + [ + 'X-Current-Page' => $data->getCurrentPage(), + 'X-Total-Pages' => $data->getTotalPages(), + ], + ); + + return $this->initData($response); + } + + public function isServerError(): bool + { + return empty($this->status) || strpos($this->status, self::FATAL_ERRORS_FIRST_NUMBER) === 0; + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + private function getShopApiUrl(): string + { + return $this->tokenCoreStorage->getOrRefreshApiPath(); + } + + /** + * @param array $response + * + * @throws CdekScheduledTaskException + */ + private function initData(array $response): array + { + if($response['error']){ + throw new CdekScheduledTaskException( + '[CDEKDelivery] Failed to get core api response', + 'cdek_error.core.response_error', + $response, + ); + } + + $decodeResponse = json_decode($response['body'], true); + + $this->status = $response['response']['code']; + + if ( + !$this->isSuccessStatus() + && + !$this->isServerError() + || + ( + empty($decodeResponse['success']) + && + $this->status !== self::EMPTY_ANSWER + ) + ) { + throw new CdekScheduledTaskException( + '[CDEKDelivery] Failed to get core api response', + 'cdek_error.core.response', + $response, + ); + } + + return $decodeResponse ?? []; + } + + private function isSuccessStatus(): bool + { + if ($this->status === self::SUCCESS_STATUS) { + return true; + } + + if ($this->status === self::FINISH_STATUS) { + return true; + } + + if ($this->status === self::HAS_NEXT_INFO_STATUS) { + return true; + } + + if ($this->status === self::EMPTY_ANSWER) { + return true; + } + + return false; + } + } + +} diff --git a/src/CdekShippingMethod.php b/src/CdekShippingMethod.php index 4c8169a..33f2313 100644 --- a/src/CdekShippingMethod.php +++ b/src/CdekShippingMethod.php @@ -14,16 +14,16 @@ class CdekShippingMethod extends WC_Shipping_Method public function __construct($instance_id = 0) { parent::__construct($instance_id); - $this->id = Config::DELIVERY_NAME; - $this->instance_id = absint($instance_id); - $this->method_title = esc_html__('CDEK Shipping', 'cdekdelivery'); + $this->id = Config::DELIVERY_NAME; + $this->instance_id = absint($instance_id); + $this->method_title = esc_html__('CDEK Shipping', 'cdekdelivery'); $this->method_description = esc_html__('Official Shipping Method for Cdek', 'cdekdelivery'); - $this->supports = [ + $this->supports = [ 'settings', 'shipping-zones', 'instance-settings', ]; - $this->enabled = 'yes'; + $this->enabled = 'yes'; $this->init(); } @@ -31,7 +31,7 @@ final public function init(): void { $this->title = esc_html__('CDEK Shipping', 'cdekdelivery'); $this->init_settings(); - add_action('woocommerce_update_options_shipping_'.$this->id, [$this, 'process_admin_options']); + add_action('woocommerce_update_options_shipping_' . $this->id, [$this, 'process_admin_options']); $this->init_form_fields(); } @@ -51,7 +51,7 @@ final public function init_form_fields(): void $this->form_fields = [ 'auth_block_name' => [ - 'title' => '

'.esc_html__('Authorization', 'cdekdelivery').'

', + 'title' => '

' . esc_html__('Authorization', 'cdekdelivery') . '

', 'type' => 'title', 'class' => 'cdek_setting_block_name', ], @@ -88,7 +88,7 @@ final public function init_form_fields(): void ], ], 'seller_block_name' => [ - 'title' => '

'.esc_html__('Client', 'cdekdelivery').'

', + 'title' => '

' . esc_html__('Client', 'cdekdelivery') . '

', 'type' => 'title', 'class' => 'cdek_setting_block_name', ], @@ -189,7 +189,7 @@ final public function init_form_fields(): void 'date_format' => 'd.m.Y', ], 'delivery_block_name' => [ - 'title' => '

'.esc_html__('Delivery', 'cdekdelivery').'

', + 'title' => '

' . esc_html__('Delivery', 'cdekdelivery') . '

', 'type' => 'title', 'class' => 'cdek_delivery_block_name', ], @@ -197,7 +197,7 @@ final public function init_form_fields(): void 'title' => esc_html__('Automatically create orders in CDEK', 'cdekdelivery'), 'type' => 'checkbox', 'description' => esc_html__('If you have information about the dimensions and correctly filled in shipping addresses, the CDEK invoice will be created automatically', - 'cdekdelivery'), + 'cdekdelivery'), ], 'tariff_list' => [ 'title' => esc_html__('Tariff', 'cdekdelivery'), @@ -205,21 +205,22 @@ final public function init_form_fields(): void 'desc_tip' => true, 'options' => Tariff::getTariffList(), 'description' => esc_html__('To select multiple tariffs, hold down the "CTRL" key and select tariffs with the left mouse button.', - 'cdekdelivery'), + 'cdekdelivery'), 'css' => 'height: 400px;', ], 'tariff_name' => [ 'title' => esc_html__('Change tariff name', 'cdekdelivery'), 'type' => 'text', 'description' => sprintf(esc_html__('In the list of tariffs in the field "Tariffs" the tariff code is indicated in brackets.\n\r To change the name of the tariff, an entry in the code-name format is added to the field; for multiple changes,\n\r tariffs are separated by a semicolon, for example, an entry that will change the name of tariff 136 and 137 looks like this:%s If the value is not specified, the tariff names will be standard.', - 'cdekdelivery'), '136-Доставка до пвз;137-Доставка курьером
'), + 'cdekdelivery'), + '136-Доставка до пвз;137-Доставка курьером
'), ], 'has_packages_mode' => [ 'title' => esc_html__('Multi-seater', 'cdekdelivery'), 'type' => 'checkbox', 'desc_tip' => true, 'description' => esc_html__('When the "Multi-seat" mode is enabled, the detailed order page will display the ability to create several packages for one order and distribute goods among the created packages', - 'cdekdelivery'), + 'cdekdelivery'), 'default' => 'no', ], 'extra_day' => [ @@ -227,7 +228,7 @@ final public function init_form_fields(): void 'type' => 'number', 'desc_tip' => true, 'description' => esc_html__('Number of days will be added to the estimated delivery time', - 'cdekdelivery'), + 'cdekdelivery'), 'default' => 0, 'custom_attributes' => [ 'min' => 0, @@ -239,7 +240,7 @@ final public function init_form_fields(): void 'type' => 'checkbox', 'desc_tip' => true, 'description' => esc_html__('If this setting is enabled, then after selecting a pick-up point on the checkout page, the card will automatically close.', - 'cdekdelivery'), + 'cdekdelivery'), 'default' => 'no', ], 'map' => [ @@ -256,18 +257,20 @@ final public function init_form_fields(): void 'type' => 'hidden', ], 'package_setting_block_name' => [ - 'title' => '

'.esc_html__('Dimensions', 'cdekdelivery').'

', + 'title' => '

' . esc_html__('Dimensions', 'cdekdelivery') . '

', 'type' => 'title', 'class' => 'cdek_package_setting_block_name', ], 'product_weight_default' => [ - 'title' => esc_html__('Default weight of one item in', 'cdekdelivery'). - ' ('. - get_option('woocommerce_weight_unit'). + 'title' => esc_html__('Default weight of one item in', 'cdekdelivery') . + ' (' . + get_option('woocommerce_weight_unit') . ')', 'desc_tip' => true, 'description' => sprintf(esc_html__('All goods must have their weight indicated, if there are goods without %s a specified weight, then for such goods the value from this field will be substituted. %s This will affect the accuracy of the delivery calculation. The default value is 1 weight unit specified in the settings.', - 'cdekdelivery'), "
", "
"), + 'cdekdelivery'), + "
", + "
"), 'type' => 'number', 'default' => 1, 'custom_attributes' => [ @@ -311,20 +314,20 @@ final public function init_form_fields(): void 'product_package_default_toggle' => [ 'title' => esc_html__('Product dimensions on/off', 'cdekdelivery'), 'description' => esc_html__('Force the use of product dimensions (length, width and height) by default for all products', - 'cdekdelivery'), + 'cdekdelivery'), 'type' => 'checkbox', 'desc_tip' => true, 'default' => 'no', ], 'services_block_name' => [ - 'title' => '

'.esc_html__('Services', 'cdekdelivery').'

', + 'title' => '

' . esc_html__('Services', 'cdekdelivery') . '

', 'type' => 'title', 'class' => 'cdek_delivery_block_name', ], 'services_ban_attachment_inspection' => [ 'title' => esc_html__('Prohibition of inspection of attachment', 'cdekdelivery'), 'description' => esc_html__('This service is not available for tariffs to the parcel locker and is only available to clients with an IM type agreement.\n\r Also, the prohibition on inspecting the attachment does not work when the services of fitting at home and partial delivery are included.', - 'cdekdelivery'), + 'cdekdelivery'), 'type' => 'checkbox', 'default' => 'no', ], @@ -341,7 +344,7 @@ final public function init_form_fields(): void 'default' => 'no', ], 'delivery_price_block_name' => [ - 'title' => '

'.esc_html__('Delivery cost', 'cdekdelivery').'

', + 'title' => '

' . esc_html__('Delivery cost', 'cdekdelivery') . '

', 'type' => 'title', 'class' => 'cdek_delivery_price_block_name', ], @@ -361,13 +364,15 @@ final public function init_form_fields(): void 'title' => esc_html__('Cash on delivery settings', 'cdekdelivery'), 'type' => 'title', 'description' => esc_html__('Cash on delivery settings are applied only when sending an order from the admin panels and for the user on the checkout page are not displayed', - 'cdekdelivery'), + 'cdekdelivery'), ], 'percentcod' => [ 'title' => esc_html__('Extra charge on order as a percentage', 'cdekdelivery'), 'type' => 'number', 'description' => sprintf(esc_html__('Calculated from the cost of the order. Changes the total amount on the receipt.%s The surcharge will only appear on the receipt.%s Therefore, it is recommended to inform the user on the checkout page about extra charges when sending by cash on delivery.', - 'cdekdelivery'), "
", " "), + 'cdekdelivery'), + "
", + " "), 'custom_attributes' => [ 'min' => 100, 'step' => 1, @@ -400,14 +405,16 @@ public function get_option($key, $empty_value = null) if ($this->get_instance_option("use_$key", false) === 'yes') { return $instanceValue; } - } elseif (!empty($instanceValue) || strpos($key, 'use_') === 0) { + } else if (!empty($instanceValue) || strpos($key, 'use_') === 0) { return $instanceValue; } } // Return global option. - $option = apply_filters('woocommerce_shipping_'.$this->id.'_option', - WC_Settings_API::get_option($key, $empty_value), $key, $this); + $option = apply_filters('woocommerce_shipping_' . $this->id . '_option', + WC_Settings_API::get_option($key, $empty_value), + $key, + $this); return $option; } diff --git a/src/Config.php b/src/Config.php index d3b4b8c..f70a58f 100644 --- a/src/Config.php +++ b/src/Config.php @@ -13,8 +13,11 @@ class Config public const META_KEY = 'order_data'; public const ORDER_META_BOX_KEY = 'official_cdek_order'; public const ORDER_AUTOMATION_HOOK_NAME = 'cdekdelivery_automation'; - public const API_URL = 'https://api.cdek.ru/v2/'; - public const TEST_API_URL = 'https://api.edu.cdek.ru/v2/'; + public const TASK_MANAGER_HOOK_NAME = 'cdekdelivery_task_manager'; + public const API_CORE_URL = 'https://api.cdek.ru/'; + public const API_VERSION = 'v2/'; + public const API_URL = 'https://api.cdek.ru/' . self::API_VERSION; + public const TEST_API_URL = 'https://api.edu.cdek.ru/' . self::API_VERSION; public const TEST_CLIENT_ID = 'EMscd6r9JnFiQ3bLoyjJY6eM78JrJceI'; public const TEST_CLIENT_SECRET = 'PjLZkKBHEiLK3YsjtNrt3TGNG0ahs3kG'; public const GRAPHICS_TIMEOUT_SEC = 60; diff --git a/src/Contracts/TaskContract.php b/src/Contracts/TaskContract.php new file mode 100644 index 0000000..ce08523 --- /dev/null +++ b/src/Contracts/TaskContract.php @@ -0,0 +1,115 @@ +cdekCoreApi = new CdekCoreApi(); + $this->taskId = $taskId; + } + + abstract protected static function getName(): string; + + abstract function start(): void; + + public static function init(string $taskId): void + { + $taskManager = new static($taskId); + $taskManager->start(); + } + + /** + * @return void + */ + public static function registerAction(): void + { + add_action( + sprintf('%s-%s', Config::TASK_MANAGER_HOOK_NAME, static::getName()), + [static::class, 'init'], + 20, + 1, + ); + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + protected function getTaskMeta(): array + { + if(empty($this->taskMeta)){ + $this->initTaskData(); + } + + return $this->taskMeta ?? []; + } + + /** + * @throws CdekScheduledTaskException + * @throws CdekApiException + * @throws \JsonException + */ + protected function initTaskData(array $data = null): void + { + $this->initData($this->cdekCoreApi->taskInfo($this->taskId, new TaskOutputData('success', $data))); + } + + /** + * @param array $response + * + * @return void + */ + protected function initData(array $response): void + { + if($this->cdekCoreApi->isServerError()){ + $this->postponeTask(); + return; + } + + $this->taskMeta = $response['data']['meta'] ?? []; + } + + protected function postponeTask(): void + { + $hooks = as_get_scheduled_actions( + [ + 'hook' => sprintf('%s-%s', Config::TASK_MANAGER_HOOK_NAME, static::getName()), + 'status' => \ActionScheduler_Store::STATUS_PENDING, + ], + ); + + if(empty($hooks)){ + return; + } + + $hook = reset($hooks); + + if(!$hook->get_schedule() instanceof \ActionScheduler_CronSchedule){ + (new TaskData( + [ + 'id' => $this->taskId, + 'name' => sprintf('%s-%s', Config::TASK_MANAGER_HOOK_NAME, static::getName()), + ], + ))->createTaskWork(); + } + } + } +} diff --git a/src/Exceptions/CdekScheduledTaskException.php b/src/Exceptions/CdekScheduledTaskException.php new file mode 100644 index 0000000..f73cba8 --- /dev/null +++ b/src/Exceptions/CdekScheduledTaskException.php @@ -0,0 +1,31 @@ +add($code, $message, $data); + wp_die($error); + } + + parent::__construct($message); + } + } +} diff --git a/src/Helpers/DBCoreTokenStorage.php b/src/Helpers/DBCoreTokenStorage.php new file mode 100644 index 0000000..0b50c74 --- /dev/null +++ b/src/Helpers/DBCoreTokenStorage.php @@ -0,0 +1,128 @@ +getTokenFromCache(); + } + + if (empty($token)) { + $token = $this->updateToken(); + } + + return 'Bearer ' . $token; + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + public function getOrRefreshApiPath(): string + { + if(isset(static::$apiUrlString)){ + return static::$apiUrlString; + } + + $cache = FileCache::getVars(); + + if (!empty($cache['endpoint']['common'])) { + return static::$apiUrlString = $cache['endpoint']['common']; + } + + $this->updateToken(); + + if(!isset(static::$apiUrlString)){ + throw new CdekScheduledTaskException( + '[CDEKDelivery] Failed to get token path', + 'cdek_error.token.path' + ); + } + + return static::$apiUrlString; + } + + private function getTokenFromCache(): ?string + { + $cache = FileCache::getVars(); + + if (empty($cache['tokens'])) { + return null; + } + + self::$tokenAdmin = $cache['tokens']['admin']; + self::$tokenStatic = $cache['tokens']['common']; + self::$tokenFrontend = $cache['tokens']['frontend']; + return self::$tokenStatic; + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + final public function updateToken(): string + { + $tokenApi = $this->fetchTokenFromApi(); + + self::$tokenAdmin = $tokenApi['tokens']['admin']; + self::$tokenStatic = $tokenApi['tokens']['common']; + self::$tokenFrontend = $tokenApi['tokens']['frontend']; + + $tokenApi['endpoint']['admin'] = static::$adminUrlString = $this->getEndPointFromToken(self::$tokenAdmin); + $tokenApi['endpoint']['common'] = static::$apiUrlString = $this->getEndPointFromToken(self::$tokenStatic); + $tokenApi['endpoint']['frontend'] = static::$frontendUrlString = $this->getEndPointFromToken(self::$tokenFrontend); + + FileCache::putVars($tokenApi); + + return self::$tokenStatic; + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + final public function fetchTokenFromApi(): array + { + return (new CdekCoreApi)->fetchShopToken(); + } + + private function getEndPointFromToken(string $token): ?string + { + $arToken = explode('.', $token); + + return json_decode(base64_decode($arToken[count($arToken) - 1]), true)['endpoint'] ?? null; + } + + } +} diff --git a/src/Helpers/DeliveryCalc.php b/src/Helpers/DeliveryCalc.php index a556a31..d8aec52 100644 --- a/src/Helpers/DeliveryCalc.php +++ b/src/Helpers/DeliveryCalc.php @@ -181,8 +181,9 @@ static function ($carry, $item) use ($package) { $deliveryParam['type'] = Tariff::getTariffType($deliveryParam['tariff_code']); $serviceList = Helper::getServices($deliveryMethod, $deliveryParam['tariff_code']); + if (!empty($serviceList)) { - $deliveryParam['services'] = $serviceList; + $deliveryParam['services'] = array_merge($serviceList, $deliveryParam['services']); } $tariffInfo = $api->calculateTariff($deliveryParam); diff --git a/src/Loader.php b/src/Loader.php index 130bab2..6b41052 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -15,6 +15,7 @@ use Cdek\Actions\ProcessWoocommerceCreateShippingAction; use Cdek\Actions\RecalculateShippingAction; use Cdek\Actions\SaveCustomCheckoutFieldsAction; + use Cdek\Managers\TaskManager; use Cdek\Blocks\CheckoutMapBlock; use Cdek\Controllers\CourierController; use Cdek\Controllers\LocationController; @@ -81,6 +82,16 @@ public static function activate(): void } self::checkRequirements(); + TaskManager::addPluginScheduleEvents(); + } + + public static function deactivate() + { + foreach (TaskManager::getTasksHooks() as $hook){ + if (as_has_scheduled_action($hook) !== false) { + as_unschedule_action($hook); + } + } } /** @@ -117,8 +128,6 @@ public function __invoke(string $pluginMainFile): void { self::$pluginMainFilePath = $pluginMainFile; - add_action("activate_$pluginMainFile", [__CLASS__, 'activate']); - try { self::checkRequirements(); } catch (RuntimeException $e) { @@ -132,6 +141,9 @@ public function __invoke(string $pluginMainFile): void self::$pluginName = get_file_data(self::$pluginMainFilePath, ['Plugin Name'])[0]; self::$pluginMainFile = plugin_basename(self::$pluginMainFilePath); + add_action("activate_" . plugin_basename($pluginMainFile), [__CLASS__, 'activate']); + add_action("deactivate_" . plugin_basename($pluginMainFile), [__CLASS__, 'deactivate']); + self::declareCompatibility(); add_action('plugins_loaded', @@ -174,8 +186,12 @@ public function __invoke(string $pluginMainFile): void add_action('woocommerce_before_order_itemmeta', new AdminShippingFields, 10, 2); + add_action('upgrader_process_complete', [TaskManager::class, 'addPluginScheduleEvents']); + add_action(Config::ORDER_AUTOMATION_HOOK_NAME, new CreateOrderAction, 10, 2); + TaskManager::registerTasks(); + (new CdekWidget)(); (new Admin)(); (new Frontend)(); diff --git a/src/Managers/TaskManager.php b/src/Managers/TaskManager.php new file mode 100644 index 0000000..3f18834 --- /dev/null +++ b/src/Managers/TaskManager.php @@ -0,0 +1,179 @@ +getResponse(); + $this->initTasks(); + } + + public function start(): void + { + if(!isset($this->taskCollection)){ + return; + } + + foreach ($this->taskCollection as $task) { + $this->startTask($task); + } + } + + public static function init(): void + { + $taskManager = new self(); + $taskManager->start(); + } + + public static function getName(): string + { + return Config::TASK_MANAGER_HOOK_NAME; + } + + public static function registerAction(): void + { + add_action(static::getName(), [static::class, 'init']); + } + + public static function registerTasks(): void + { + self::registerAction(); + + foreach (self::TASK_CLASSES as $arTaskClass) { + if (is_callable([$arTaskClass, 'registerAction'])){ + $arTaskClass::registerAction(); + } + } + } + + public static function getTasksHooks(): array + { + $arNames = []; + + foreach (self::TASK_CLASSES as $class) { + if (is_callable([$class, 'getName'])) { + $arNames[] = sprintf( + '%s-%s', + Config::TASK_MANAGER_HOOK_NAME, + $class::getName(), + ); + } + } + + return $arNames; + } + + public static function addPluginScheduleEvents(): void + { + if (as_has_scheduled_action(Config::TASK_MANAGER_HOOK_NAME) !== false) { + as_unschedule_action(Config::TASK_MANAGER_HOOK_NAME); + } + + $dateTime = new \DateTime('now + 1 hour'); + + as_schedule_cron_action( + time(), + $dateTime->format('i') . ' ' . $dateTime->format('H') . ' * * *', + Config::TASK_MANAGER_HOOK_NAME, + [], + '', + true, + ); + + } + + private function startTask(TaskData $task): void + { + foreach (self::TASK_CLASSES as $class){ + if ( + is_callable([$class, 'getName']) + && + $task->getName() === $class::getName() + ) { + $task->createTaskWork(); + } + } + } + + /** + * @throws CdekApiException + * @throws CdekScheduledTaskException + * @throws \JsonException + */ + private function getResponse(): void + { + try { + $response = (new CdekCoreApi())->taskManager($this->taskCursor['next'] ?? null); + } catch (CdekScheduledTaskException $e) { + static::addPluginScheduleEvents(); + + throw new CdekScheduledTaskException( + $e->getMessage(), + 'cdek_error.task.manager' + ); + } + + if( + !isset($response['cursor']) + || + !array_key_exists('current', $response['cursor']) + || + !array_key_exists('previous', $response['cursor']) + || + !array_key_exists('next', $response['cursor']) + || + !array_key_exists('count', $response['cursor']) + ){ + throw new CdekScheduledTaskException('[CDEKDelivery] Not found cursor params', + 'cdek_error.cursor.params', + $response, + ); + } + + if (empty($this->errorCollection)) { + $this->taskData = $response['data']; + $this->taskCursor = $response['cursor']; + } + } + + private function initTasks(): void + { + foreach ($this->taskData as $data) { + $this->taskCollection[] = new TaskData($data); + } + + if(!empty($this->taskCursor['next'])){ + $this->getResponse(); + $this->initTasks(); + } + } + } +} diff --git a/src/Model/TaskData.php b/src/Model/TaskData.php new file mode 100644 index 0000000..abac114 --- /dev/null +++ b/src/Model/TaskData.php @@ -0,0 +1,71 @@ +id = $requestData['id']; + $this->name = $requestData['name']; + $this->schedule = $requestData['schedule']; + + $this->time = time(); + } + + public function createTaskWork(): void + { + $this->time += 5 * 60; + + if ($this->isScheduledTask()) { + if (false === as_has_scheduled_action($this->getName())) { + as_schedule_cron_action( + $this->time, + $this->getSchedule(), + sprintf('%s-%s', Config::TASK_MANAGER_HOOK_NAME, static::getName()), + [$this->getId()], + '', + true, + ); + } + } else { + as_enqueue_async_action( + sprintf('%s-%s', Config::TASK_MANAGER_HOOK_NAME, static::getName()), + [$this->getId()] + ); + } + } + + public function getId(): string + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function getSchedule(): ?string + { + return $this->schedule; + } + + public function isScheduledTask(): bool + { + return $this->schedule !== null; + } + + } +} diff --git a/src/Model/TaskOutputData.php b/src/Model/TaskOutputData.php new file mode 100644 index 0000000..f756b5a --- /dev/null +++ b/src/Model/TaskOutputData.php @@ -0,0 +1,47 @@ +status = $status; + $this->data = $data; + $this->currentPage = $currentPage; + $this->totalPages = $totalPages; + } + + public function getStatus(): string + { + return $this->status; + } + + public function getData(): ?array + { + return $this->data; + } + + public function getCurrentPage(): ?int + { + return $this->currentPage; + } + + public function getTotalPages(): ?int + { + return $this->totalPages; + } + } +} diff --git a/src/Transport/HttpClient.php b/src/Transport/HttpClient.php index 312c4cc..2306cc2 100644 --- a/src/Transport/HttpClient.php +++ b/src/Transport/HttpClient.php @@ -13,6 +13,15 @@ class HttpClient { + /** + * @param string $url + * @param string $method + * @param string $token + * @param array|null $data + * @param bool $plain + * + * @return array|string + */ public static function sendCdekRequest( string $url, string $method, @@ -35,6 +44,14 @@ public static function sendCdekRequest( return self::sendRequest($url, $method, $config, $plain); } + /** + * @param string $url + * @param string $method + * @param array $config + * @param bool $plain + * + * @return array|string + */ public static function sendRequest(string $url, string $method, array $config = [], bool $plain = false) { $pluginVersion = Loader::getPluginVersion(); diff --git a/src/Transport/HttpCoreClient.php b/src/Transport/HttpCoreClient.php new file mode 100644 index 0000000..698471e --- /dev/null +++ b/src/Transport/HttpCoreClient.php @@ -0,0 +1,95 @@ + $method, + 'headers' => [ + 'Content-Type' => 'application/json', + 'X-App-Name' => 'wordpress', + 'X-App-Version' => Loader::getPluginVersion(), + 'X-User-Locale' => get_user_locale(), + 'X-Correlation-Id' => self::generateUuid(), + 'user-agent' => 'wp/' . get_bloginfo('version'), + ] + $headers, + 'timeout' => 60, + ] + $config, + ); + + if (is_array($resp)) { + return $resp; + } + + $ip = @file_get_contents('https://ipecho.net/plain'); + + if (!headers_sent()) { + header("X-Requester-IP: $ip"); + } + + return ['error' => true, 'ip' => $ip]; + } + + private static function generateUuid(): string + { + return sprintf( + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + ); + } + } + +} diff --git a/src/UI/Admin.php b/src/UI/Admin.php index 715c837..56f576d 100644 --- a/src/UI/Admin.php +++ b/src/UI/Admin.php @@ -63,23 +63,9 @@ public static function registerAdminScripts(): void ]); } - public static function registerOrderScripts(): void - { - global $plugin_page, $pagenow; - - // Not on an Orders page. - if ('admin.php' !== $pagenow || 0 !== strpos($plugin_page, 'wc-orders')) { - return; - } - - Helper::enqueueScript('cdek-admin-create-order', 'cdek-create-order', true); - } - public function __invoke(): void { add_action('load-woocommerce_page_wc-settings', [__CLASS__, 'registerAdminScripts']); - - add_action('admin_enqueue_scripts', [__CLASS__, 'registerOrderScripts']); } } diff --git a/src/UI/MetaBoxes.php b/src/UI/MetaBoxes.php index 250b595..1648742 100644 --- a/src/UI/MetaBoxes.php +++ b/src/UI/MetaBoxes.php @@ -34,6 +34,8 @@ public static function registerMetaBoxes(string $post_type, $post): void return; } + add_action('admin_enqueue_scripts', [__CLASS__, 'registerOrderScripts']); + $cdekMethod = CheckoutHelper::getOrderShippingMethod($order); $selectedTariff = (int) ($cdekMethod->get_meta(MetaKeys::TARIFF_CODE) ?: $cdekMethod->get_meta('tariff_code')); @@ -87,7 +89,7 @@ public static function noAddressMetaBox(): void str_replace('', '', sprintf(esc_html__(/* translators: %s: Name of the plugin */ 'Select the correct sending address in the settings plugin named %s', 'cdekdelivery'), - esc_html($pluginName))). + esc_html($pluginName))). '

'; } @@ -104,7 +106,7 @@ public static function noOfficeMetaBox(): void str_replace('', '', sprintf(esc_html__(/* translators: %s: Name of the plugin */ 'Select the correct sending address in the settings plugin named %s', 'cdekdelivery'), - esc_html($pluginName))). + esc_html($pluginName))). '

'; } @@ -121,7 +123,7 @@ public static function noAuthMetaBox(): void str_replace('', '', sprintf(esc_html__(/* translators: %s: Name of the plugin */ 'Enter the correct client ID and secret key in the settings plugin named %s', 'cdekdelivery'), - esc_html($pluginName))). + esc_html($pluginName))). '

'; } @@ -144,8 +146,8 @@ public static function createOrderMetaBox($post): void $shipping = CheckoutHelper::getOrderShippingMethod($order); $hasPackages - = Helper::getActualShippingMethod($shipping->get_data()['instance_id']) - ->get_option('has_packages_mode') === 'yes'; + = Helper::getActualShippingMethod($shipping->get_data()['instance_id']) + ->get_option('has_packages_mode') === 'yes'; $orderNumber = $orderData['order_number'] ?? null; $orderUuid = $orderData['order_uuid'] ?? null; @@ -180,11 +182,16 @@ public static function notAvailableEditOrderData(): void echo '

CDEKDelivery: '. esc_html__('Editing the order is not available due to a change in the order status in the CDEK system', - 'cdekdelivery'). + 'cdekdelivery'). '

'; } + public static function registerOrderScripts(): void + { + Helper::enqueueScript('cdek-admin-create-order', 'cdek-create-order', true); + } + public function __invoke(): void { add_action('add_meta_boxes', [__CLASS__, 'registerMetaBoxes'], 100, 2); diff --git a/src/Uninstaller.php b/src/Uninstaller.php new file mode 100644 index 0000000..523fdb9 --- /dev/null +++ b/src/Uninstaller.php @@ -0,0 +1,18 @@ +