state * so the API can return a more complete response. */ public const STATELESS_CITIES = [ "LOS ANGELES" => "CA", "SAN FRANCISCO" => "CA", "NEW YORK" => "NY", "CHICAGO" => "IL", "MIAMI" => "FL" ]; /** * * @param string $code * @return \TrackingInfo * @throws TrackingException */ public static function track(string $code, string $carrier = ""): TrackingInfo { $barcode = new TrackingBarcode($code); try { $resp = USPSAPIs::getAPIRequest("tracking/v3/tracking/$code?expand=DETAIL"); $resp = str_replace("reg;", "®", $resp); $resp = str_replace("®", "®", $resp); $resp = str_replace("", "™", $resp); $json = json_decode($resp, true); if (!empty($json["error"])) { if (!empty($json["error"]["errors"])) { $msg = $json["error"]["errors"][0]["title"]; $msg = preg_replace("/\s?-\s?$/", "", $msg); // Remove weird dash at end of message switch ($json["error"]["errors"][0]["code"]) { case "150001": // Tracking number not found throw new TrackingException(str_replace("12: ", "", $msg)); case "150002": // Incorrect tracking number throw new TrackingException(str_replace("7: ", "", $msg)); } } else if (!empty($json["error"]["errors"]) && !empty($json["error"]["errors"][0]["code"])) { throw new TrackingException("The USPS tracking system is having problems: \"" . trim($json["error"]["message"]) . "\" (" . $json["error"]["errors"][0]["code"] . ")"); } else if (!empty($json["error"]["message"])) { throw new TrackingException("The USPS tracking system is having problems: \"" . trim($json["error"]["message"]) . "\""); } throw new TrackingException("The USPS tracking system is having problems. Try again later."); } $trackinfo = $json; } catch (TrackingException $ex) { throw $ex; } catch (Exception $ex) { throw new TrackingException("There was a server error. This code cannot be tracked right now. Try again later."); } $info = new TrackingInfo(); try { $info->setCode($trackinfo["trackingNumber"]); } catch (Exception $ex) { throw new TrackingException("The USPS tracking system returned an invalid response. Try again later."); } $info->setCarrier("usps"); $info->setService(new Service((string) $trackinfo["mailClass"], (string) $trackinfo["mailClass"])); $info->setCarrierAttributionText(CarrierAssets::getAttribution(Carriers::getCarrierCode($info->getCarrier()))); $info->setCarrierLogo(CarrierAssets::getLogo(Carriers::getCarrierCode($info->getCarrier()))); // Current status if (count($trackinfo["trackingEvents"]) > 0) { $index = 0; $evt = $trackinfo["trackingEvents"][$index]; $current_status = new TrackingEntry( TrackingStatus::USPSEventCodeToStatus($evt["eventCode"]), ($evt["eventType"] ?? "Unknown") . (TrackingStatus::USPSEventCodeToStatus($evt["eventCode"]) == TrackingStatus::TRACKING_STATUS_UNKNOWN ? " " . $evt["eventCode"] : ""), date("Y-m-d\TH:i:s", strtotime($evt["eventTimestamp"])), null, TrackingStatus::isUSPSEventCodeContainerScan($evt["eventCode"]) ); $current_location = new Location(); $current_location->city = (string) $evt["eventCity"] ?? ""; $current_location->state = (string) $evt["eventState"] ?? ""; $current_location->zip = (string) $evt["eventZIP"] ?? ""; $current_location->country = (string) $evt["eventCountry"] ?? ""; /* * Fill in state from list above when it's missing from the API response */ if ($current_location->state == "" && $current_location->zip == "") { if (array_key_exists(strtoupper($current_location->city), self::STATELESS_CITIES)) { $current_location->state = self::STATELESS_CITIES[$current_location->city]; } } } else { $current_status = new TrackingEntry( TrackingStatus::TRACKING_STATUS_PRE_TRANSIT, ($trackinfo["statusSummary"] ?? "Unknown"), date("Y-m-d\TH:i:s", strtotime("now")), null, false ); $current_location = new Location(); } $current_status->setLocation($current_location); $info->setCurrentStatus($current_status); $from = new Location(); $from->city = (string) (isset($trackinfo["originCity"]) ? $trackinfo["originCity"] : ""); $from->state = (string) (isset($trackinfo["originState"]) ? $trackinfo["originState"] : ""); $from->zip = (string) (isset($trackinfo["originZIP"]) ? $trackinfo["originZIP"] : ""); $from->country = (string) (isset($trackinfo["originCountry"]) ? $trackinfo["originCountry"] : ""); $info->setFrom($from); $to = new Location(); $to->city = (string) $trackinfo["destinationCity"] ?? ""; $to->state = (string) $trackinfo["destinationState"] ?? ""; $to->zip = (string) $trackinfo["destinationZIP"] ?? ""; $to->country = (string) (isset($trackinfo["destinationCountry"]) ? $trackinfo["destinationCountry"] : ""); $info->setTo($to); for ($i = 0; $i < count($trackinfo["trackingEvents"]); $i++) { $history = $trackinfo["trackingEvents"][$i]; $location = new Location(); $location->city = (string) $history["eventCity"] ?? ""; $location->state = (string) $history["eventState"] ?? ""; $location->zip = (string) $history["eventZIP"] ?? ""; $location->country = (string) $history["eventCountry"] ?? ""; /* * Fill in state from list above when it's missing from the API response */ if ($location->state == "" && $location->zip == "") { if (array_key_exists(strtoupper($location->city), self::STATELESS_CITIES)) { $location->state = self::STATELESS_CITIES[$location->city]; } } $datetime = $history["eventTimestamp"]; if (empty($history["eventTimestamp"])) { if ($i > 0 && !empty($trackinfo["trackingEvents"][$i - 1]["eventTimestamp"])) { $datetime = $trackinfo["trackingEvents"][$i - 1]["eventTimestamp"]; } else if ($i < count($trackinfo["trackingEvents"]) - 1 && !empty($trackinfo["trackingEvents"][$i + 1]["eventTimestamp"])) { $datetime = $trackinfo["trackingEvents"][$i + 1]["eventTimestamp"]; } else { $datetime = "1970-01-01"; } } $info->appendHistoryEntry(new TrackingEntry( TrackingStatus::USPSEventCodeToStatus((string) $history["eventCode"]), $history["eventType"] . (TrackingStatus::USPSEventCodeToStatus((string) $history["eventCode"]) == TrackingStatus::TRACKING_STATUS_UNKNOWN ? " " . (string) $history["eventCode"] : ""), $datetime, $location, TrackingStatus::isUSPSEventCodeContainerScan((string) $history["eventCode"]))); } return $info; } }