fetchInpostStatus($trackingNumber); } return $this->fetchAllegroEdgeStatus($trackingNumber); } private function fetchInpostStatus(string $trackingNumber): ?array { try { $token = $this->resolveInpostToken(); if ($token === null) { return null; } $settings = $this->inpostRepository->getSettings(); $env = (string) ($settings['environment'] ?? 'sandbox'); $baseUrl = strtolower(trim($env)) === 'production' ? self::INPOST_API_PRODUCTION : self::INPOST_API_SANDBOX; $url = $baseUrl . '/tracking/' . rawurlencode($trackingNumber); $response = $this->apiRequest($url, $token); $details = is_array($response['tracking_details'] ?? null) ? $response['tracking_details'] : []; if ($details === []) { return null; } $rawStatus = strtolower(trim((string) ($details[0]['status'] ?? ''))); if ($rawStatus === '') { return null; } return [ 'status' => DeliveryStatus::normalize('inpost', $rawStatus), 'status_raw' => $rawStatus, 'description' => DeliveryStatus::description('inpost', $rawStatus), ]; } catch (Throwable) { return null; } } private function fetchAllegroEdgeStatus(string $trackingNumber): ?array { try { $url = 'https://edge.allegro.pl/ad/tracking?packageNo=' . rawurlencode($trackingNumber); $response = $this->edgeApiRequest($url); $statuses = $response['status'] ?? []; if (!is_array($statuses) || $statuses === []) { return null; } $latest = end($statuses); $description = trim((string) ($latest['description'] ?? '')); if ($description === '') { return null; } $slug = DeliveryStatus::slugifyAllegroDescription($description); $normalized = DeliveryStatus::normalize('allegro_edge', $slug); if ($normalized === DeliveryStatus::UNKNOWN) { $normalized = DeliveryStatus::guessStatusFromDescription($description); error_log(sprintf( '[AllegroTracking] Niezmapowany status: "%s" (slug: %s, guessed: %s)', $description, $slug, $normalized )); } return [ 'status' => $normalized, 'status_raw' => $description, 'description' => $description, ]; } catch (Throwable) { return null; } } /** * @return array */ private function edgeApiRequest(string $url): array { $ch = curl_init($url); if ($ch === false) { return []; } $opts = [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_HTTPHEADER => [ 'Accept: application/vnd.allegro.internal.v1+json', 'Content-Type: application/vnd.allegro.internal.v1+json', ], ]; $caPath = SslCertificateResolver::resolve(); if ($caPath !== null) { $opts[CURLOPT_CAINFO] = $caPath; } curl_setopt_array($ch, $opts); $body = curl_exec($ch); $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $ch = null; if ($body === false || $httpCode < 200 || $httpCode >= 300) { return []; } $json = json_decode((string) $body, true); return is_array($json) ? $json : []; } private function resolveInpostToken(): ?string { try { $token = $this->inpostRepository->getDecryptedToken(); return ($token !== null && trim($token) !== '') ? trim($token) : null; } catch (Throwable) { return null; } } /** * @return array */ private function apiRequest(string $url, string $token): array { $ch = curl_init($url); if ($ch === false) { return []; } $opts = [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_CONNECTTIMEOUT => 5, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $token, 'Accept: application/json', ], ]; $caPath = SslCertificateResolver::resolve(); if ($caPath !== null) { $opts[CURLOPT_CAINFO] = $caPath; } curl_setopt_array($ch, $opts); $body = curl_exec($ch); $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $ch = null; if ($body === false || $httpCode < 200 || $httpCode >= 300) { return []; } $json = json_decode((string) $body, true); return is_array($json) ? $json : []; } }