DHL Modul v0.5 Shipping Label ok

This commit is contained in:
Kevin Adametz 2025-08-22 18:18:26 +02:00
parent 480fdc65ed
commit 8fdaa0ba1d
122 changed files with 17938 additions and 2239 deletions

View file

@ -0,0 +1,198 @@
<?php
namespace Acme\Dhl\Support;
use Illuminate\Support\Facades\Http;
use Illuminate\Http\Client\RequestException;
use Illuminate\Http\Client\ConnectionException;
use Acme\Dhl\Exceptions\DhlApiException;
use Acme\Dhl\Exceptions\DhlAuthenticationException;
use Acme\Dhl\Exceptions\DhlValidationException;
use Exception;
use Illuminate\Support\Facades\Log;
/**
* DHL API Client for handling HTTP requests to DHL services
*/
class DhlClient
{
public function __construct(
protected string $baseUrl,
protected ?string $apiKey,
protected ?string $username,
protected ?string $password
) {}
/**
* Make HTTP request to DHL API
*
* @param string $method HTTP method (get, post, put, delete)
* @param string $uri API endpoint URI
* @param array $payload Request body data
* @param array $query Query parameters
* @return array Response data as array
* @throws Exception When API request fails or returns error
*/
public function request(string $method, string $uri, array $payload = [], array $query = []): array
{
try {
$request = Http::baseUrl($this->baseUrl)
->withHeaders($this->buildHeaders())
->timeout(30)
->retry(3, 300, function ($exception, $attempt) {
if ($exception instanceof RequestException && $exception->response->status() === 429) {
$delay = min(1000000 * $attempt, 10000000); // Max 10 seconds
usleep($delay); // Microseconds
return true;
}
return $exception instanceof ConnectionException ||
($exception instanceof RequestException && in_array($exception->response->status(), [500, 502, 503, 504]));
}, false);
// Add authentication if provided
if ($this->username && $this->password) {
$request = $request->withBasicAuth($this->username, $this->password);
}
// Make the request
$response = match (strtolower($method)) {
'get' => $request->get($uri, $query),
'post' => $request->post($uri . '?' . http_build_query($query), $payload),
'put' => $request->put($uri, $payload),
'delete' => $request->delete($uri),
default => throw new Exception("Unsupported HTTP method: {$method}")
};
// Handle response
if ($response->failed()) {
// Log additional debug info for 400 errors
if ($response->status() === 400) {
Log::error('[DHL API] HTTP 400 Bad Request Details', [
'method' => $method,
'uri' => $uri,
'request_payload' => $payload,
'response_body' => $response->body(),
'response_json' => $response->json(),
'status_code' => $response->status(),
'headers' => $response->headers()
]);
}
$this->handleErrorResponse($response, $method, $uri);
}
return $response->json() ?? [];
} catch (RequestException $e) {
Log::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()]);
// Re-throw our own exceptions
if ($e instanceof DhlApiException) {
throw $e;
}
throw new DhlApiException("DHL API error: {$e->getMessage()}", $e->getCode(), $e);
}
}
/**
* Build HTTP headers for DHL API requests
*/
private function buildHeaders(): array
{
$headers = [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'User-Agent' => 'acme-laravel-dhl/1.0'
];
if ($this->apiKey) {
$headers['dhl-api-key'] = $this->apiKey;
}
return $headers;
}
/**
* Handle error responses from DHL API
*/
private function handleErrorResponse($response, string $method, string $uri): void
{
$status = $response->status();
$body = $response->json();
$errorMessage = $this->extractErrorMessage($body) ?? "HTTP {$status} error";
match (true) {
$status === 401 => throw new DhlAuthenticationException("DHL API authentication failed: {$errorMessage}"),
$status === 403 => throw new DhlAuthenticationException("DHL API access forbidden: {$errorMessage}"),
$status === 404 => throw new DhlApiException("DHL API endpoint not found: {$method} {$uri}"),
$status === 422 => throw new DhlValidationException("DHL API validation error: {$errorMessage}"),
$status === 429 => throw new DhlApiException("DHL API rate limit exceeded. Please try again later."),
$status >= 500 => throw new DhlApiException("DHL API server error: {$errorMessage}"),
default => throw new DhlApiException("DHL API error ({$status}): {$errorMessage}")
};
}
/**
* Extract error message from DHL API response
*/
private function extractErrorMessage(?array $body): ?string
{
if (!$body) {
return null;
}
// Try different possible error message fields
return $body['message']
?? $body['error']
?? $body['detail']
?? data_get($body, 'errors.0.message')
?? data_get($body, 'error.message')
?? null;
}
/**
* Test connection to DHL API
*
* @return bool True if connection successful
*/
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('/');
// 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;
}
if ($response->status() === 403 && str_contains($response->body(), 'api-key')) {
Log::error('DHL API authentication failed: Invalid API key');
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;
}
}
}