workingCopy/ecomzone/classes/EcomZoneClient.php
2025-06-24 21:46:54 +02:00

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;
}
}