346 lines
12 KiB
PHP
346 lines
12 KiB
PHP
<?php
|
|
if (!defined("_PS_VERSION_")) {
|
|
exit();
|
|
}
|
|
|
|
class EcomZoneClient
|
|
{
|
|
private $apiToken;
|
|
private $apiUrl;
|
|
private $maxRetries = 3;
|
|
private $timeout = 60; // Increased timeout
|
|
|
|
public function __construct()
|
|
{
|
|
$this->apiToken = Configuration::get("ECOMZONE_API_TOKEN");
|
|
$this->apiUrl = Configuration::get("ECOMZONE_API_URL");
|
|
|
|
if (empty($this->apiUrl)) {
|
|
$this->apiUrl = 'https://dropship.ecomzone.eu/api'; // Default API URL
|
|
Configuration::updateValue("ECOMZONE_API_URL", $this->apiUrl);
|
|
}
|
|
|
|
EcomZoneLogger::log("API Client initialized", "INFO", [
|
|
"url" => $this->apiUrl,
|
|
]);
|
|
}
|
|
|
|
public function validateApiCredentials()
|
|
{
|
|
if (empty($this->apiToken)) {
|
|
throw new EcomZoneException(
|
|
"API token not configured",
|
|
EcomZoneException::API_ERROR
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function getCatalog($page = 1, $perPage = 100)
|
|
{
|
|
$this->validateApiCredentials();
|
|
|
|
$params = [
|
|
"page" => $page,
|
|
"per_page" => $perPage,
|
|
];
|
|
|
|
return $this->makeRequest("catalog", "GET", $params);
|
|
}
|
|
|
|
public function getProduct($sku)
|
|
{
|
|
$this->validateApiCredentials();
|
|
|
|
return $this->makeRequest("product/" . urlencode($sku), "GET");
|
|
}
|
|
|
|
private function makeRequest($endpoint, $method = "GET", $params = [], $data = null)
|
|
{
|
|
// Validate API credentials first
|
|
$this->validateApiCredentials();
|
|
|
|
$retryCount = 0;
|
|
$lastError = null;
|
|
$url = rtrim($this->apiUrl, '/') . '/' . ltrim($endpoint, '/');
|
|
|
|
// Add query parameters to URL if method is GET
|
|
if ($method === "GET" && !empty($params) && is_array($params)) {
|
|
$url .= (strpos($url, "?") === false ? "?" : "&") . http_build_query($params);
|
|
}
|
|
|
|
do {
|
|
try {
|
|
EcomZoneLogger::log("Making API request", "INFO", [
|
|
"method" => $method,
|
|
"url" => $url,
|
|
"retry" => $retryCount,
|
|
"params" => $params
|
|
]);
|
|
|
|
$curl = curl_init();
|
|
$options = [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_ENCODING => "",
|
|
CURLOPT_MAXREDIRS => 10,
|
|
CURLOPT_TIMEOUT => $this->timeout,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
|
CURLOPT_CUSTOMREQUEST => $method,
|
|
CURLOPT_HTTPHEADER => [
|
|
"Authorization: Bearer " . $this->apiToken,
|
|
"Accept: application/json",
|
|
"Content-Type: application/json",
|
|
"User-Agent: PrestaShop-EcomZone/1.0",
|
|
"Connection: close" // Avoid keep-alive issues
|
|
],
|
|
// SSL Options - disable verification for development
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => 0,
|
|
// Verbose debugging
|
|
CURLOPT_VERBOSE => true,
|
|
];
|
|
|
|
// Create a temporary file for curl verbose output
|
|
$verbose = fopen('php://temp', 'w+');
|
|
curl_setopt($curl, CURLOPT_STDERR, $verbose);
|
|
|
|
// Add POST data if provided
|
|
if ($data !== null) {
|
|
$jsonData = json_encode($data);
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $jsonData);
|
|
EcomZoneLogger::log("Request payload", "DEBUG", [
|
|
'data' => $jsonData
|
|
]);
|
|
}
|
|
// For POST requests with params, add them to the body
|
|
else if ($method !== "GET" && !empty($params)) {
|
|
$jsonData = json_encode($params);
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $jsonData);
|
|
EcomZoneLogger::log("Request params as payload", "DEBUG", [
|
|
'data' => $jsonData
|
|
]);
|
|
}
|
|
|
|
curl_setopt_array($curl, $options);
|
|
|
|
// Execute the request
|
|
$response = curl_exec($curl);
|
|
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
$contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
|
|
$error = curl_error($curl);
|
|
$errorNo = curl_errno($curl);
|
|
|
|
// Get verbose information
|
|
rewind($verbose);
|
|
$verboseLog = stream_get_contents($verbose);
|
|
fclose($verbose);
|
|
|
|
// Log detailed response information
|
|
EcomZoneLogger::log("API Response Details", "DEBUG", [
|
|
'http_code' => $httpCode,
|
|
'content_type' => $contentType,
|
|
'response_length' => strlen($response),
|
|
'curl_error_code' => $errorNo,
|
|
'curl_error' => $error,
|
|
'verbose_log' => $verboseLog,
|
|
'raw_response' => strlen($response) > 1000 ?
|
|
substr($response, 0, 1000) . '... [truncated]' :
|
|
$response
|
|
]);
|
|
|
|
curl_close($curl);
|
|
|
|
if ($errorNo > 0) {
|
|
throw new Exception("cURL Error ({$errorNo}): " . $error);
|
|
}
|
|
|
|
// Handle HTTP errors
|
|
if ($httpCode >= 400) {
|
|
$errorMessage = "HTTP Error: " . $httpCode;
|
|
|
|
// Try to extract error details from response
|
|
if (!empty($response)) {
|
|
try {
|
|
$errorData = json_decode($response, true);
|
|
if (json_last_error() === JSON_ERROR_NONE && !empty($errorData['error'])) {
|
|
$errorMessage .= " - " . $errorData['error'];
|
|
} else {
|
|
$errorMessage .= " - Response: " . substr($response, 0, 200);
|
|
}
|
|
} catch (Exception $e) {
|
|
$errorMessage .= " - Raw response: " . substr($response, 0, 200);
|
|
}
|
|
}
|
|
|
|
throw new Exception($errorMessage);
|
|
}
|
|
|
|
// Handle empty responses
|
|
if (empty($response)) {
|
|
throw new Exception("Empty response received from API");
|
|
}
|
|
|
|
// Decode JSON response
|
|
$decodedResponse = json_decode($response, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
throw new Exception(
|
|
"Invalid JSON response: " . json_last_error_msg() .
|
|
"\nRaw response: " . substr($response, 0, 500)
|
|
);
|
|
}
|
|
|
|
EcomZoneLogger::log("API request successful", "INFO", [
|
|
"http_code" => $httpCode,
|
|
"content_type" => $contentType
|
|
]);
|
|
|
|
return $decodedResponse;
|
|
} catch (Exception $e) {
|
|
$lastError = $e;
|
|
$retryCount++;
|
|
|
|
EcomZoneLogger::log("API request failed", "ERROR", [
|
|
"error" => $e->getMessage(),
|
|
"retry" => $retryCount,
|
|
"url" => $url,
|
|
"trace" => $e->getTraceAsString()
|
|
]);
|
|
|
|
if ($retryCount < $this->maxRetries) {
|
|
$sleepTime = pow(2, $retryCount);
|
|
EcomZoneLogger::log("Retrying request", "INFO", [
|
|
"retry_count" => $retryCount,
|
|
"sleep_time" => $sleepTime,
|
|
"url" => $url
|
|
]);
|
|
sleep($sleepTime);
|
|
continue;
|
|
}
|
|
|
|
throw new EcomZoneException(
|
|
"API request failed after {$this->maxRetries} retries: " .
|
|
$e->getMessage(),
|
|
EcomZoneException::API_ERROR,
|
|
$e
|
|
);
|
|
}
|
|
} while ($retryCount < $this->maxRetries);
|
|
|
|
// This should never be reached, but just in case
|
|
throw new EcomZoneException(
|
|
"Max retries reached: " . $lastError->getMessage(),
|
|
EcomZoneException::API_ERROR,
|
|
$lastError
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Download an image from the EcomZone API with proper authentication
|
|
*
|
|
* @param string $imageUrl The full URL of the image to download
|
|
* @return array Array containing image data and metadata
|
|
* @throws EcomZoneException If the download fails
|
|
*/
|
|
public function downloadImage(string $imageUrl): array
|
|
{
|
|
try {
|
|
EcomZoneLogger::log("Downloading image", "DEBUG", [
|
|
'url' => $imageUrl
|
|
]);
|
|
|
|
// Initialize cURL
|
|
$ch = curl_init();
|
|
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $imageUrl,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_MAXREDIRS => 5,
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => 0,
|
|
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
|
CURLOPT_HTTPHEADER => [
|
|
'Accept: image/webp,image/apng,image/*,*/*;q=0.8',
|
|
'Accept-Encoding: gzip, deflate, br',
|
|
'Authorization: Bearer ' . $this->getApiToken()
|
|
]
|
|
]);
|
|
|
|
$imageData = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
|
$error = curl_error($ch);
|
|
|
|
curl_close($ch);
|
|
|
|
// Check if the response is HTML instead of an image (authentication error)
|
|
$isHtml = stripos($contentType, 'text/html') !== false ||
|
|
(strlen($imageData) > 15 && stripos($imageData, '<!DOCTYPE html>') !== false);
|
|
|
|
if ($isHtml) {
|
|
throw new EcomZoneException(
|
|
"Authentication error: Received HTML instead of image data. Check API token.",
|
|
EcomZoneException::API_ERROR
|
|
);
|
|
}
|
|
|
|
if ($httpCode !== 200 || empty($imageData)) {
|
|
throw new EcomZoneException(
|
|
"Failed to download image (HTTP $httpCode): $error",
|
|
EcomZoneException::API_ERROR
|
|
);
|
|
}
|
|
|
|
EcomZoneLogger::log("Image download complete", "DEBUG", [
|
|
'url' => $imageUrl,
|
|
'content_type' => $contentType,
|
|
'size' => strlen($imageData)
|
|
]);
|
|
|
|
return [
|
|
'data' => $imageData,
|
|
'content_type' => $contentType,
|
|
'http_code' => $httpCode
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
EcomZoneLogger::log("Image download failed", "ERROR", [
|
|
'url' => $imageUrl,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
throw new EcomZoneException(
|
|
"Image download failed: " . $e->getMessage(),
|
|
EcomZoneException::API_ERROR,
|
|
$e
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the API token, either from the class property or from the configuration
|
|
*
|
|
* @return string The API token
|
|
*/
|
|
protected function getApiToken()
|
|
{
|
|
if (empty($this->apiToken)) {
|
|
$this->apiToken = Configuration::get("ECOMZONE_API_TOKEN");
|
|
}
|
|
|
|
if (empty($this->apiToken)) {
|
|
throw new EcomZoneException(
|
|
"API token not configured",
|
|
EcomZoneException::API_ERROR
|
|
);
|
|
}
|
|
|
|
return $this->apiToken;
|
|
}
|
|
}
|
|
|