update 20.10.2025

This commit is contained in:
Kevin Adametz 2025-10-20 17:42:08 +02:00
parent 8c11130b5d
commit a939cd51ef
616 changed files with 84821 additions and 4121 deletions

View file

@ -23,6 +23,14 @@ class DhlClient
protected ?string $password
) {}
/**
* Get DHL-specific logger
*/
private function getDhlLogger()
{
return Log::channel('dhl');
}
/**
* Make HTTP request to DHL API
*
@ -39,6 +47,20 @@ class DhlClient
$request = Http::baseUrl($this->baseUrl)
->withHeaders($this->buildHeaders())
->timeout(30)
->withOptions([
'verify' => config('dhl.ssl.verify_peer', true), // SSL certificate verification
'http_errors' => false, // Don't throw exceptions on HTTP error codes
'curl' => [
CURLOPT_SSL_VERIFYPEER => config('dhl.ssl.verify_peer', true),
CURLOPT_SSL_VERIFYHOST => config('dhl.ssl.verify_host', true) ? 2 : 0,
CURLOPT_SSLVERSION => $this->getSslVersion(),
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_CONNECTTIMEOUT => config('dhl.ssl.connect_timeout', 10),
CURLOPT_TIMEOUT => config('dhl.ssl.timeout', 30),
CURLOPT_USERAGENT => 'acme-laravel-dhl/1.0',
]
])
->retry(3, 300, function ($exception, $attempt) {
if ($exception instanceof RequestException && $exception->response->status() === 429) {
$delay = min(1000000 * $attempt, 10000000); // Max 10 seconds
@ -67,7 +89,7 @@ class DhlClient
if ($response->failed()) {
// Log additional debug info for 400 errors
if ($response->status() === 400) {
Log::error('[DHL API] HTTP 400 Bad Request Details', [
$this->getDhlLogger()->error('[DHL API] HTTP 400 Bad Request Details', [
'method' => $method,
'uri' => $uri,
'request_payload' => $payload,
@ -83,10 +105,10 @@ class DhlClient
return $response->json() ?? [];
} catch (RequestException $e) {
Log::error('DHL API request failed', ['error' => $e->getMessage(), 'method' => $method, 'uri' => $uri]);
$this->getDhlLogger()->error('DHL API request failed', ['error' => $e->getMessage(), 'method' => $method, 'uri' => $uri]);
throw new DhlApiException("DHL API request failed: {$e->getMessage()}", $e->getCode(), $e);
} catch (Exception $e) {
Log::error('DHL API error', ['error' => $e->getMessage()]);
$this->getDhlLogger()->error('DHL API error', ['error' => $e->getMessage()]);
// Re-throw our own exceptions
if ($e instanceof DhlApiException) {
throw $e;
@ -152,6 +174,53 @@ class DhlClient
?? null;
}
/**
* Get SSL version constant based on configuration
*/
private function getSslVersion(): int
{
$sslVersion = config('dhl.ssl.ssl_version', 'TLSv1_2');
return match ($sslVersion) {
'TLSv1_0' => CURL_SSLVERSION_TLSv1_0,
'TLSv1_1' => CURL_SSLVERSION_TLSv1_1,
'TLSv1_2' => CURL_SSLVERSION_TLSv1_2,
'TLSv1_3' => defined('CURL_SSLVERSION_TLSv1_3') ? CURL_SSLVERSION_TLSv1_3 : CURL_SSLVERSION_TLSv1_2,
default => CURL_SSLVERSION_TLSv1_2,
};
}
/**
* Log detailed server environment information for debugging
*/
public function logServerEnvironment(): void
{
$info = [
'php_version' => PHP_VERSION,
'curl_version' => curl_version(),
'openssl_version' => OPENSSL_VERSION_TEXT ?? 'Unknown',
'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown',
'dhl_config' => [
'base_url' => $this->baseUrl,
'has_api_key' => !empty($this->apiKey),
'has_username' => !empty($this->username),
'has_password' => !empty($this->password),
'ssl_verify_peer' => config('dhl.ssl.verify_peer', true),
'ssl_verify_host' => config('dhl.ssl.verify_host', true),
'ssl_version' => config('dhl.ssl.ssl_version', 'TLSv1_2'),
'timeout' => config('dhl.ssl.timeout', 30),
'connect_timeout' => config('dhl.ssl.connect_timeout', 10),
],
'environment' => [
'APP_ENV' => config('app.env'),
'APP_DEBUG' => config('app.debug'),
'APP_URL' => config('app.url'),
]
];
$this->getDhlLogger()->info('DHL Server Environment Debug Info', $info);
}
/**
* Test connection to DHL API
*
@ -159,40 +228,370 @@ class DhlClient
*/
public function testConnection(): bool
{
try {
// Check basic connectivity and authentication
// Use the simplest possible endpoint to minimize permission issues
$response = Http::baseUrl($this->baseUrl)
->withHeaders($this->buildHeaders())
->withBasicAuth($this->username, $this->password)
->timeout(10)
->get('/');
// Log server environment for debugging
$this->logServerEnvironment();
// If we get any response (even 404), the connection and auth are working
if ($response->status() === 401) {
Log::error('DHL API authentication failed: Invalid username/password');
return false;
// Try multiple connection methods for better compatibility
$methods = [
'method1' => 'Laravel HTTP with enhanced SSL',
'method2' => 'Laravel HTTP with relaxed SSL',
'method3' => 'Direct cURL fallback'
];
foreach ($methods as $methodKey => $methodName) {
try {
$this->getDhlLogger()->info("DHL API connection test - trying {$methodName}", [
'method' => $methodKey,
'base_url' => $this->baseUrl
]);
$success = $this->testConnectionWithMethod($methodKey);
if ($success) {
$this->getDhlLogger()->info("DHL API connection test successful with {$methodName}", [
'method' => $methodKey,
'base_url' => $this->baseUrl
]);
return true;
}
} catch (Exception $e) {
$this->getDhlLogger()->warning("DHL API connection test failed with {$methodName}", [
'method' => $methodKey,
'error' => $e->getMessage(),
'base_url' => $this->baseUrl
]);
}
}
if ($response->status() === 403 && str_contains($response->body(), 'api-key')) {
Log::error('DHL API authentication failed: Invalid API key');
$this->getDhlLogger()->error('DHL API connection test failed with all methods', [
'base_url' => $this->baseUrl,
'tried_methods' => array_keys($methods)
]);
return false;
}
/**
* Test connection with specific method
*/
private function testConnectionWithMethod(string $method): bool
{
switch ($method) {
case 'method1':
return $this->testConnectionEnhanced();
case 'method2':
return $this->testConnectionRelaxed();
case 'method3':
return $this->testConnectionCurl();
default:
return false;
}
// Any other response code (including 404, 403 for endpoint access) means connection works
Log::info('DHL API connection test successful', [
'status' => $response->status(),
'has_api_key' => !empty($this->apiKey),
'has_credentials' => !empty($this->username) && !empty($this->password)
]);
return true;
} catch (Exception $e) {
Log::error('DHL API connection test failed', [
'error' => $e->getMessage(),
'base_url' => $this->baseUrl
]);
return false;
}
}
/**
* Enhanced SSL connection test
*/
private function testConnectionEnhanced(): bool
{
// Detect cURL version for compatibility
$curlVersion = curl_version();
$isOldCurl = version_compare($curlVersion['version'], '8.0.0', '<');
$curlOptions = [
CURLOPT_SSL_VERIFYPEER => config('dhl.ssl.verify_peer', true),
CURLOPT_SSL_VERIFYHOST => config('dhl.ssl.verify_host', true) ? 2 : 0,
CURLOPT_SSLVERSION => $this->getSslVersion(),
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_CONNECTTIMEOUT => config('dhl.ssl.connect_timeout', 10),
CURLOPT_TIMEOUT => config('dhl.ssl.connect_timeout', 10),
CURLOPT_USERAGENT => 'acme-laravel-dhl/1.0',
CURLOPT_VERBOSE => true, // Enable verbose output
CURLOPT_STDERR => fopen('php://temp', 'w+'), // Capture verbose output
];
// Only use HTTP/2 for newer cURL versions
if (!$isOldCurl && defined('CURL_HTTP_VERSION_2_0')) {
$curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
} else {
$curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
}
// Additional options for older cURL versions
if ($isOldCurl) {
$curlOptions[CURLOPT_TCP_NODELAY] = true;
$curlOptions[CURLOPT_TCP_KEEPALIVE] = 1;
$curlOptions[CURLOPT_TCP_KEEPIDLE] = 10;
$curlOptions[CURLOPT_TCP_KEEPINTVL] = 1;
}
// Log complete cURL request details
$this->getDhlLogger()->info('DHL Enhanced Connection Test - Complete Request Details', [
'method' => 'Enhanced SSL',
'url' => $this->baseUrl . '/',
'headers' => $this->buildHeaders(),
'auth' => [
'username' => $this->username,
'password' => '***hidden***'
],
'curl_options' => $this->formatCurlOptions($curlOptions),
'timeout' => 10,
'ssl_config' => [
'verify_peer' => config('dhl.ssl.verify_peer', true),
'verify_host' => config('dhl.ssl.verify_host', true),
'ssl_version' => config('dhl.ssl.ssl_version', 'TLSv1_2'),
]
]);
$response = Http::baseUrl($this->baseUrl)
->withHeaders($this->buildHeaders())
->withBasicAuth($this->username, $this->password)
->timeout(10)
->withOptions([
'verify' => config('dhl.ssl.verify_peer', true),
'http_errors' => false,
'curl' => $curlOptions
])
->get('/');
// Log response details
$this->getDhlLogger()->info('DHL Enhanced Connection Test - Response Details', [
'status_code' => $response->status(),
'headers' => $response->headers(),
'body' => $response->body(),
'success' => $this->validateResponse($response)
]);
return $this->validateResponse($response);
}
/**
* Relaxed SSL connection test (fallback)
*/
private function testConnectionRelaxed(): bool
{
// Detect cURL version for compatibility
$curlVersion = curl_version();
$isOldCurl = version_compare($curlVersion['version'], '8.0.0', '<');
$curlOptions = [
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSLVERSION => CURL_SSLVERSION_DEFAULT,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 15,
CURLOPT_USERAGENT => 'acme-laravel-dhl/1.0',
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, // Force HTTP/1.1
CURLOPT_VERBOSE => true, // Enable verbose output
CURLOPT_STDERR => fopen('php://temp', 'w+'), // Capture verbose output
];
// Additional options for older cURL versions
if ($isOldCurl) {
$curlOptions[CURLOPT_TCP_NODELAY] = true;
$curlOptions[CURLOPT_TCP_KEEPALIVE] = 1;
$curlOptions[CURLOPT_TCP_KEEPIDLE] = 10;
$curlOptions[CURLOPT_TCP_KEEPINTVL] = 1;
$curlOptions[CURLOPT_FRESH_CONNECT] = true;
$curlOptions[CURLOPT_FORBID_REUSE] = true;
}
// Log complete cURL request details
$this->getDhlLogger()->info('DHL Relaxed Connection Test - Complete Request Details', [
'method' => 'Relaxed SSL',
'url' => $this->baseUrl . '/',
'headers' => $this->buildHeaders(),
'auth' => [
'username' => $this->username,
'password' => '***hidden***'
],
'curl_options' => $this->formatCurlOptions($curlOptions),
'timeout' => 15,
'ssl_config' => [
'verify_peer' => false,
'verify_host' => false,
'ssl_version' => 'DEFAULT',
]
]);
$response = Http::baseUrl($this->baseUrl)
->withHeaders($this->buildHeaders())
->withBasicAuth($this->username, $this->password)
->timeout(15)
->withOptions([
'verify' => false, // Disable SSL verification as fallback
'http_errors' => false,
'curl' => $curlOptions
])
->get('/');
// Log response details
$this->getDhlLogger()->info('DHL Relaxed Connection Test - Response Details', [
'status_code' => $response->status(),
'headers' => $response->headers(),
'body' => $response->body(),
'success' => $this->validateResponse($response)
]);
return $this->validateResponse($response);
}
/**
* Direct cURL connection test (last resort)
*/
private function testConnectionCurl(): bool
{
$ch = curl_init();
$curlOptions = [
CURLOPT_URL => $this->baseUrl . '/',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'User-Agent: acme-laravel-dhl/1.0'
],
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $this->username . ':' . $this->password,
CURLOPT_SSL_VERIFYPEER => config('dhl.ssl.verify_peer', true),
CURLOPT_SSL_VERIFYHOST => config('dhl.ssl.verify_host', true) ? 2 : 0,
CURLOPT_SSLVERSION => $this->getSslVersion(),
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_VERBOSE => true,
CURLOPT_STDERR => fopen('php://temp', 'w+'),
];
// Log complete cURL request details
$this->getDhlLogger()->info('DHL Direct cURL Connection Test - Complete Request Details', [
'method' => 'Direct cURL',
'url' => $this->baseUrl . '/',
'headers' => [
'Accept: application/json',
'User-Agent: acme-laravel-dhl/1.0'
],
'auth' => [
'username' => $this->username,
'password' => '***hidden***',
'auth_type' => 'CURLAUTH_BASIC'
],
'curl_options' => $this->formatCurlOptions($curlOptions),
'timeout' => 15,
'ssl_config' => [
'verify_peer' => config('dhl.ssl.verify_peer', true),
'verify_host' => config('dhl.ssl.verify_host', true),
'ssl_version' => config('dhl.ssl.ssl_version', 'TLSv1_2'),
]
]);
curl_setopt_array($ch, $curlOptions);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
$curlInfo = curl_getinfo($ch);
// Get verbose output
rewind($curlOptions[CURLOPT_STDERR]);
$verboseOutput = stream_get_contents($curlOptions[CURLOPT_STDERR]);
fclose($curlOptions[CURLOPT_STDERR]);
curl_close($ch);
// Log response details
$this->getDhlLogger()->info('DHL Direct cURL Connection Test - Response Details', [
'status_code' => $httpCode,
'response_body' => $response,
'curl_info' => $curlInfo,
'verbose_output' => $verboseOutput,
'curl_error' => $error,
'success' => $httpCode >= 200 && $httpCode < 500
]);
if ($error) {
throw new Exception("cURL error: {$error}");
}
// For cURL, we check HTTP status codes
return $httpCode >= 200 && $httpCode < 500; // Accept 2xx, 3xx, 4xx (but not 5xx)
}
/**
* Format cURL options for logging
*/
private function formatCurlOptions(array $curlOptions): array
{
$formatted = [];
foreach ($curlOptions as $option => $value) {
$optionName = $this->getCurlOptionName($option);
// Hide sensitive values
if (in_array($option, [CURLOPT_USERPWD, CURLOPT_STDERR])) {
$formatted[$optionName] = '***hidden***';
} elseif (is_resource($value)) {
$formatted[$optionName] = 'resource';
} elseif (is_array($value)) {
$formatted[$optionName] = $value;
} else {
$formatted[$optionName] = $value;
}
}
return $formatted;
}
/**
* Get human-readable cURL option name
*/
private function getCurlOptionName(int $option): string
{
$optionNames = [
CURLOPT_URL => 'CURLOPT_URL',
CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER',
CURLOPT_TIMEOUT => 'CURLOPT_TIMEOUT',
CURLOPT_CONNECTTIMEOUT => 'CURLOPT_CONNECTTIMEOUT',
CURLOPT_HTTPHEADER => 'CURLOPT_HTTPHEADER',
CURLOPT_HTTPAUTH => 'CURLOPT_HTTPAUTH',
CURLOPT_USERPWD => 'CURLOPT_USERPWD',
CURLOPT_SSL_VERIFYPEER => 'CURLOPT_SSL_VERIFYPEER',
CURLOPT_SSL_VERIFYHOST => 'CURLOPT_SSL_VERIFYHOST',
CURLOPT_SSLVERSION => 'CURLOPT_SSLVERSION',
CURLOPT_FOLLOWLOCATION => 'CURLOPT_FOLLOWLOCATION',
CURLOPT_MAXREDIRS => 'CURLOPT_MAXREDIRS',
CURLOPT_VERBOSE => 'CURLOPT_VERBOSE',
CURLOPT_STDERR => 'CURLOPT_STDERR',
CURLOPT_USERAGENT => 'CURLOPT_USERAGENT',
CURLOPT_HTTP_VERSION => 'CURLOPT_HTTP_VERSION',
CURLOPT_TCP_NODELAY => 'CURLOPT_TCP_NODELAY',
CURLOPT_TCP_KEEPALIVE => 'CURLOPT_TCP_KEEPALIVE',
CURLOPT_TCP_KEEPIDLE => 'CURLOPT_TCP_KEEPIDLE',
CURLOPT_TCP_KEEPINTVL => 'CURLOPT_TCP_KEEPINTVL',
CURLOPT_FRESH_CONNECT => 'CURLOPT_FRESH_CONNECT',
CURLOPT_FORBID_REUSE => 'CURLOPT_FORBID_REUSE',
];
return $optionNames[$option] ?? "CURLOPT_UNKNOWN_{$option}";
}
/**
* Validate HTTP response for connection test
*/
private function validateResponse($response): bool
{
if ($response->status() === 401) {
$this->getDhlLogger()->error('DHL API authentication failed: Invalid username/password');
return false;
}
if ($response->status() === 403 && str_contains($response->body(), 'api-key')) {
$this->getDhlLogger()->error('DHL API authentication failed: Invalid API key');
return false;
}
// Any other response code (including 404, 403 for endpoint access) means connection works
return true;
}
}