update 20.10.2025
This commit is contained in:
parent
8c11130b5d
commit
a939cd51ef
616 changed files with 84821 additions and 4121 deletions
432
app/Services/DhlTrackingService.php
Normal file
432
app/Services/DhlTrackingService.php
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Acme\Dhl\Models\DhlShipment;
|
||||
use App\Http\Controllers\SettingController;
|
||||
use App\Jobs\TrackShipmentJob;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* DHL Tracking Service
|
||||
*
|
||||
* Handles DHL tracking using both Unified Tracking API and Parcel DE Tracking API
|
||||
* with support for synchronous and asynchronous tracking updates
|
||||
*/
|
||||
class DhlTrackingService
|
||||
{
|
||||
private string $apiKey;
|
||||
|
||||
private string $apiSecret;
|
||||
|
||||
private bool $isSandbox;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$settingController = new SettingController;
|
||||
$dhlConfig = $settingController->getDhlConfig();
|
||||
|
||||
$this->apiKey = $dhlConfig['api_key'] ?? config('dhl.api_key');
|
||||
$this->apiSecret = $dhlConfig['api_secret'] ?? config('dhl.legacy.api_secret');
|
||||
$this->isSandbox = ($dhlConfig['sandbox'] ?? config('dhl.legacy.sandbox', true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Track shipment using DHL Unified Tracking API (recommended for international)
|
||||
*/
|
||||
public function trackShipment(string $trackingNumber, array $options = []): array
|
||||
{
|
||||
try {
|
||||
Log::info('[DHL Tracking Service] Tracking shipment with Unified API', [
|
||||
'tracking_number' => $trackingNumber,
|
||||
'is_sandbox' => $this->isSandbox,
|
||||
]);
|
||||
|
||||
$response = Http::withHeaders([
|
||||
'DHL-API-Key' => $this->apiKey,
|
||||
'Accept' => 'application/json',
|
||||
])
|
||||
->withOptions([
|
||||
'verify' => config('dhl.ssl.verify_peer', true),
|
||||
'http_errors' => false,
|
||||
'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',
|
||||
]
|
||||
])
|
||||
->get('https://api.dhl.com/track/shipments', [
|
||||
'trackingNumber' => $trackingNumber,
|
||||
'service' => 'express,parcel',
|
||||
'requesterCountryCode' => 'DE',
|
||||
'originCountryCode' => 'DE',
|
||||
'language' => 'de',
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
|
||||
if (isset($data['shipments']) && count($data['shipments']) > 0) {
|
||||
$shipment = $data['shipments'][0];
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'tracking_number' => $shipment['id'],
|
||||
'status' => $shipment['status']['statusCode'] ?? 'unknown',
|
||||
'status_text' => $shipment['status']['status'] ?? 'Unbekannt',
|
||||
'description' => $shipment['status']['description'] ?? '',
|
||||
'last_update' => $shipment['status']['timestamp'] ?? null,
|
||||
'origin' => $shipment['origin']['address']['addressLocality'] ?? null,
|
||||
'destination' => $shipment['destination']['address']['addressLocality'] ?? null,
|
||||
'events' => $shipment['events'] ?? [],
|
||||
'api_used' => 'unified',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// If Unified API fails, try Parcel DE API
|
||||
return $this->trackShipmentDE($trackingNumber, $options);
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Tracking Service] Unified API failed', [
|
||||
'tracking_number' => $trackingNumber,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
// Fallback to Parcel DE API
|
||||
return $this->trackShipmentDE($trackingNumber, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track shipment using DHL Parcel DE Tracking API (optimized for Germany)
|
||||
*/
|
||||
public function trackShipmentDE(string $trackingNumber, array $options = []): array
|
||||
{
|
||||
try {
|
||||
Log::info('[DHL Tracking Service] Tracking shipment with Parcel DE API', [
|
||||
'tracking_number' => $trackingNumber,
|
||||
'is_sandbox' => $this->isSandbox,
|
||||
]);
|
||||
|
||||
$response = Http::withBasicAuth($this->apiKey, $this->apiSecret)
|
||||
->withHeaders([
|
||||
'Accept' => 'application/json',
|
||||
'dhl-api-key' => $this->apiKey,
|
||||
])
|
||||
->withOptions([
|
||||
'verify' => config('dhl.ssl.verify_peer', true),
|
||||
'http_errors' => false,
|
||||
'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',
|
||||
]
|
||||
])
|
||||
->get('https://api.dhl.com/parcel/de/tracking/v1/shipments', [
|
||||
'trackingNumber' => $trackingNumber,
|
||||
'language' => 'de',
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
|
||||
if (isset($data['shipments']) && count($data['shipments']) > 0) {
|
||||
$shipment = $data['shipments'][0];
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'tracking_number' => $shipment['id'],
|
||||
'status' => $shipment['status']['statusCode'] ?? 'unknown',
|
||||
'status_text' => $shipment['status']['description'] ?? 'Unbekannt',
|
||||
'description' => $shipment['status']['description'] ?? '',
|
||||
'last_update' => $shipment['status']['timestamp'] ?? null,
|
||||
'events' => $shipment['events'] ?? [],
|
||||
'api_used' => 'parcel_de',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Sendung nicht gefunden oder noch nicht im System erfasst.',
|
||||
'tracking_number' => $trackingNumber,
|
||||
'api_used' => 'parcel_de',
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Tracking Service] Parcel DE API failed', [
|
||||
'tracking_number' => $trackingNumber,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Abrufen der Tracking-Informationen: ' . $e->getMessage(),
|
||||
'tracking_number' => $trackingNumber,
|
||||
'api_used' => 'parcel_de',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track multiple shipments at once (up to 10 for Unified API)
|
||||
*/
|
||||
public function trackMultipleShipments(array $trackingNumbers): array
|
||||
{
|
||||
if (count($trackingNumbers) > 10) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Maximal 10 Sendungen können gleichzeitig getrackt werden.',
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders([
|
||||
'DHL-API-Key' => $this->apiKey,
|
||||
'Accept' => 'application/json',
|
||||
])
|
||||
->withOptions([
|
||||
'verify' => config('dhl.ssl.verify_peer', true),
|
||||
'http_errors' => false,
|
||||
'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',
|
||||
]
|
||||
])
|
||||
->get('https://api.dhl.com/track/shipments', [
|
||||
'trackingNumber' => implode(',', $trackingNumbers),
|
||||
'service' => 'parcel',
|
||||
'requesterCountryCode' => 'DE',
|
||||
'language' => 'de',
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$data = $response->json();
|
||||
$results = [];
|
||||
|
||||
foreach ($data['shipments'] ?? [] as $shipment) {
|
||||
$results[] = [
|
||||
'tracking_number' => $shipment['id'],
|
||||
'status' => $shipment['status']['statusCode'] ?? 'unknown',
|
||||
'status_text' => $shipment['status']['status'] ?? 'Unbekannt',
|
||||
'last_update' => $shipment['status']['timestamp'] ?? null,
|
||||
'events' => $shipment['events'] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'shipments' => $results,
|
||||
'api_used' => 'unified',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Abrufen der Tracking-Informationen.',
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Tracking Service] Multiple tracking failed', [
|
||||
'tracking_numbers' => $trackingNumbers,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Abrufen der Tracking-Informationen: ' . $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update tracking for a DHL shipment (sync or async based on config)
|
||||
*/
|
||||
public function updateTracking(DhlShipment $shipment, array $options = []): array
|
||||
{
|
||||
// Get DHL configuration
|
||||
$settingController = new SettingController;
|
||||
$dhlConfig = $settingController->getDhlConfig();
|
||||
|
||||
// Check if queue should be used
|
||||
$useQueue = $dhlConfig['use_queue'] ?? false;
|
||||
|
||||
if ($useQueue) {
|
||||
return $this->updateTrackingAsync($shipment, $options, $dhlConfig);
|
||||
} else {
|
||||
return $this->updateTrackingSync($shipment, $options, $dhlConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update tracking asynchronously using queue
|
||||
*/
|
||||
private function updateTrackingAsync(DhlShipment $shipment, array $options, array $dhlConfig): array
|
||||
{
|
||||
try {
|
||||
// Dispatch job with pre-loaded config
|
||||
TrackShipmentJob::dispatch($shipment, $options);
|
||||
|
||||
Log::info('[DHL Tracking Service] Tracking update dispatched to queue', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Tracking-Update wird verarbeitet. Sie erhalten eine Benachrichtigung, sobald die Informationen aktualisiert sind.',
|
||||
'queued' => true,
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Tracking Service] Failed to dispatch tracking update', [
|
||||
'error' => $e->getMessage(),
|
||||
'shipment_id' => $shipment->id,
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Einreihen des Tracking-Updates: ' . $e->getMessage(),
|
||||
'queued' => false,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update tracking synchronously using new DHL APIs
|
||||
*/
|
||||
private function updateTrackingSync(DhlShipment $shipment, array $options, array $dhlConfig): array
|
||||
{
|
||||
try {
|
||||
Log::info('[DHL Tracking Service] Updating tracking synchronously', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
]);
|
||||
|
||||
// Check if shipment has tracking number
|
||||
if (! $shipment->dhl_shipment_no) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Keine DHL-Sendungsnummer verfügbar für Tracking.',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
}
|
||||
|
||||
// Use new tracking API
|
||||
$result = $this->trackShipment($shipment->dhl_shipment_no);
|
||||
|
||||
if ($result['success']) {
|
||||
// Update shipment with tracking data
|
||||
$shipment->update([
|
||||
'status' => $this->mapDhlStatusToInternal($result['status']),
|
||||
'tracking_status' => $result['status_text'],
|
||||
'last_tracked_at' => now(),
|
||||
]);
|
||||
|
||||
Log::info('[DHL Tracking Service] Tracking updated successfully (sync)', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'dhl_shipment_no' => $shipment->dhl_shipment_no,
|
||||
'tracking_status' => $result['status'],
|
||||
'api_used' => $result['api_used'],
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => 'Tracking-Informationen erfolgreich aktualisiert!',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id,
|
||||
'tracking_status' => $result['status'],
|
||||
'tracking_details' => $result,
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $result['message'] ?? 'Fehler beim Abrufen der Tracking-Informationen.',
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::error('[DHL Tracking Service] Tracking update failed (sync)', [
|
||||
'shipment_id' => $shipment->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'Fehler beim Aktualisieren der Tracking-Informationen: ' . $e->getMessage(),
|
||||
'queued' => false,
|
||||
'shipment_id' => $shipment->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map DHL status codes to internal status
|
||||
*/
|
||||
private function mapDhlStatusToInternal(string $dhlStatus): string
|
||||
{
|
||||
$statusMap = [
|
||||
'pre-transit' => 'created',
|
||||
'transit' => 'in_transit',
|
||||
'out-for-delivery' => 'out_for_delivery',
|
||||
'delivered' => 'delivered',
|
||||
'failure' => 'failed',
|
||||
'returned' => 'returned',
|
||||
'exception' => 'exception',
|
||||
];
|
||||
|
||||
return $statusMap[$dhlStatus] ?? 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status description in German
|
||||
*/
|
||||
public function getStatusDescription(string $statusCode): string
|
||||
{
|
||||
$descriptions = [
|
||||
'pre-transit' => 'Auftrag elektronisch übermittelt',
|
||||
'transit' => 'Sendung in Zustellung',
|
||||
'out-for-delivery' => 'Wird heute zugestellt',
|
||||
'delivered' => 'Erfolgreich zugestellt',
|
||||
'failure' => 'Zustellung fehlgeschlagen',
|
||||
'returned' => 'Sendung wird zurückgeschickt',
|
||||
'exception' => 'Zustellausnahme',
|
||||
];
|
||||
|
||||
return $descriptions[$statusCode] ?? 'Unbekannter Status';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue