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