'application/x-www-form-urlencoded' ]; $postdata = http_build_query([ "grant_type" => "client_credentials", "client_id" => env("fedex_apikey", ""), "client_secret" => env("fedex_secret", "") ]); $ch = curl_init(); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 45); //curl_setopt($ch, CURLOPT_URL, "https://apis-sandbox.fedex.com/oauth/token"); // TEST curl_setopt($ch, CURLOPT_URL, "https://apis.fedex.com/oauth/token"); // PROD curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); $response = curl_exec($ch); $data = json_decode($response, true); if (isset($data["access_token"])) { return [$data["access_token"], $data["expires_in"]]; } throw new Exception("Couldn't get FedEx access token."); } /** * * @param string $code * @return \TrackingInfo * @throws TrackingException */ public static function track(string $code, string $carrier = ""): TrackingInfo { global $memcache; $barcode = new TrackingBarcode($code); $fedexauthtoken = $memcache->get("logistics.tracking.fedex_auth_token"); if ($fedexauthtoken == false) { $tokendata = Tracking_FedEx::getAuthToken(); $memcache->set("logistics.tracking.fedex_auth_token", $tokendata[0], $tokendata[1] - 120); $fedexauthtoken = $tokendata[0]; } try { $trackrequest = [ "includeDetailedScans" => true, "trackingInfo" => [ [ //"shipDateBegin" => date("Y-m-d", strtotime("now - 3 months")), "trackingNumberInfo" => [ "trackingNumber" => $barcode->getCode() ] ] ] ]; $headers = [ "Authorization: Bearer $fedexauthtoken", 'X-locale: en_US', 'Content-Type: application/json' ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 45); //curl_setopt($ch, CURLOPT_URL, "https://apis-sandbox.fedex.com/track/v1/trackingnumbers"); // TEST curl_setopt($ch, CURLOPT_URL, "https://apis.fedex.com/track/v1/trackingnumbers"); // PROD curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($trackrequest)); curl_setopt($ch, CURLOPT_ENCODING, ""); $response = curl_exec($ch); // CHECK TO SEE IF WE GOT AN ERROR if ((curl_errno($ch)) && (curl_errno($ch) != 0)) { throw new TrackingException(curl_error($ch)); } $result = json_decode($response, true); if (isset($result["errors"]) || !empty($result["errors"])) { if (count($result["errors"]) > 0) { throw new TrackingException($result["errors"][0]["message"]); } throw new TrackingException("The FedEx tracking system responded with an error. Try again later."); } if (!empty($result["output"]["completeTrackResults"]) && !empty($result["output"]["completeTrackResults"][0]["trackResults"])) { $trackinfo = $result["output"]["completeTrackResults"][0]["trackResults"][0]; } else { throw new TrackingException("No tracking details found."); } if (!empty($result["output"]["completeTrackResults"][0]["trackResults"][0]["error"])) { throw new TrackingException($result["output"]["completeTrackResults"][0]["trackResults"][0]["error"]["message"]); } } catch (TrackingException $ex) { throw $ex; } catch (Exception $ex) { throw new TrackingException("There was a server problem. This code cannot be tracked right now."); } $info = new TrackingInfo(); $info->setCode($trackinfo["trackingNumberInfo"]["trackingNumber"]); $info->setCarrier("fedex"); $info->setService(new Service($trackinfo["serviceDetail"]["type"], $trackinfo["serviceDetail"]["description"])); // Set carrier info: Ground, Express, Freight, etc $carrierid = "fedex"; switch ($trackinfo["trackingNumberInfo"]["carrierCode"]) { case "FEDEX_GROUND": case "FEDEX_EXPRESS": case "FEDEX_FREIGHT": default: $carrierid = "fedex"; break; } $info->setCarrierAttributionText(CarrierAssets::getAttribution(Carriers::getCarrierCode($carrierid))); $info->setCarrierLogo(CarrierAssets::getLogo(Carriers::getCarrierCode($carrierid))); if (count($trackinfo["scanEvents"]) > 0) { $current = $trackinfo["scanEvents"][0]; $current_status = new TrackingEntry( TrackingStatus::FedExEventTypeToStatus($current["eventType"]), ($current["eventDescription"] ?? "Unknown") . (TrackingStatus::FedExEventTypeToStatus($current["eventType"]) == TrackingStatus::TRACKING_STATUS_UNKNOWN ? " " . $current["eventType"] : ""), date("c", strtotime($current["date"])) ); $current_location = new Location(); // FedEx docs say it should be $current["scanLocation"]["locationContactAndAddress"]["address"]["...}] but it's not I guess $current_location->city = $current["scanLocation"]["city"] ?? ""; $current_location->state = $current["scanLocation"]["stateOrProvinceCode"] ?? ""; $current_location->zip = $current["scanLocation"]["postalCode"] ?? ""; $current_location->country = $current["scanLocation"]["countryCode"] ?? ""; $current_status->setLocation($current_location); $info->setCurrentStatus($current_status); } $to = new Location(); $to->city = $trackinfo["recipientInformation"]["address"]["city"] ?? ""; $to->state = $trackinfo["recipientInformation"]["address"]["stateOrProvinceCode"] ?? ""; $to->zip = $trackinfo["recipientInformation"]["address"]["postalCode"] ?? ""; $to->country = $trackinfo["recipientInformation"]["address"]["countryCode"] ?? ""; $info->setTo($to); $from = new Location(); $from->city = $trackinfo["shipperInformation"]["address"]["city"] ?? ""; $from->state = $trackinfo["shipperInformation"]["address"]["stateOrProvinceCode"] ?? ""; $from->zip = $trackinfo["shipperInformation"]["address"]["postalCode"] ?? ""; $from->country = $trackinfo["shipperInformation"]["address"]["countryCode"] ?? ""; $info->setFrom($from); foreach ($trackinfo["scanEvents"] as $history) { $location = new Location(); $location->city = $history["scanLocation"]["city"] ?? ""; $location->state = $history["scanLocation"]["stateOrProvinceCode"] ?? ""; $location->zip = $history["scanLocation"]["postalCode"] ?? ""; $location->country = $history["scanLocation"]["countryCode"] ?? ""; if (!empty($history["date"])) { $datetimestring = date("c", strtotime($history["date"])); } else { $datetimestring = ""; } $info->appendHistoryEntry(new TrackingEntry( TrackingStatus::FedExEventTypeToStatus($history["eventType"]), ($history["eventDescription"] ?? "Unknown") . (TrackingStatus::FedExEventTypeToStatus($history["eventType"]) == TrackingStatus::TRACKING_STATUS_UNKNOWN ? " " . $history["eventType"] : ""), $datetimestring, $location)); } return $info; } }