From c420e11e738cad4d2fa023a6e90b212f4ebfb84f Mon Sep 17 00:00:00 2001 From: xqiu <1487053+xqiu@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:32:23 -0800 Subject: [PATCH 1/4] Add Fedex REST API usage for ship rate and tracking Fedex is currently in transition from WSDL to REST API. This change allows configurable usage of WSDL or REST API. Note: uses shipstream/fedex-rest-sdk, but currently it's not updated with fedex tracking API schema, so need to use a patch branch for it. Also composer installing shipstream/fedex-rest-sdk requires PHP 8.1+, so I've to change php version as well for this commit. --- .../Mage/Usa/Model/Shipping/Carrier/Fedex.php | 391 +++++++++++++++++- app/code/core/Mage/Usa/etc/config.xml | 2 + app/code/core/Mage/Usa/etc/system.xml | 32 ++ composer.json | 9 +- 4 files changed, 427 insertions(+), 7 deletions(-) diff --git a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php index d7832a788f7..d157b10539b 100644 --- a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php +++ b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php @@ -13,6 +13,19 @@ * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ + use ShipStream\FedEx\FedEx; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\FullSchemaQuoteRate; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\AccountNumber; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\RequestedShipment; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\RateParty; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\Address; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\RequestedPackageLineItem; + use ShipStream\FedEx\Api\RatesAndTransitTimesV1\Dto\Weight; + use ShipStream\FedEx\Enums\Endpoint; + use ShipStream\FedEx\Api\TrackV1\Dto\FullSchemaTrackingNumbers; + use ShipStream\FedEx\Api\TrackV1\Dto\TrackingInfo; + use ShipStream\FedEx\Api\TrackV1\Dto\TrackingNumberInfo; + /** * Fedex shipping implementation * @@ -63,6 +76,13 @@ class Mage_Usa_Model_Shipping_Carrier_Fedex extends Mage_Usa_Model_Shipping_Carr */ protected $_rawRequest = null; + /** + * REST API request data + * + * @var Varien_Object|null + */ + protected $_fedexRestRequestData = null; + /** * Rate result data * @@ -166,9 +186,14 @@ public function collectRates(Mage_Shipping_Model_Rate_Request $request) if (!$this->getConfigFlag($this->_activeFlag)) { return false; } - $this->setRequest($request); - - $this->_getQuotes(); + + if ($this->getConfigData('use_rest_api')) { + $this->setRestApiRequest($request); + $this->_getRestApiQuotes(); + } else { + $this->setRequest($request); + $this->_getQuotes(); + } $this->_updateFreeMethodQuote($request); @@ -778,6 +803,8 @@ public function getCode($type, $code = '') 'STANDARD_OVERNIGHT' => Mage::helper('usa')->__('Standard Overnight'), 'FEDEX_FREIGHT' => Mage::helper('usa')->__('Freight'), 'FEDEX_NATIONAL_FREIGHT' => Mage::helper('usa')->__('National Freight'), + 'FEDEX_INTERNATIONAL_ECONOMY' => Mage::helper('usa')->__('International Economy'), + 'FEDEX_INTERNATIONAL_PRIORITY' => Mage::helper('usa')->__('International Priority'), ], 'dropoff' => [ 'REGULAR_PICKUP' => Mage::helper('usa')->__('Regular Pickup'), @@ -814,6 +841,7 @@ public function getCode($type, $code = '') 'INTERNATIONAL_FIRST', 'INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY', + 'FEDEX_INTERNATIONAL_PRIORITY', ] ] ] @@ -840,6 +868,7 @@ public function getCode($type, $code = '') 'INTERNATIONAL_FIRST', 'INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY', + 'FEDEX_INTERNATIONAL_PRIORITY', ] ] ] @@ -848,7 +877,12 @@ public function getCode($type, $code = '') 'containers' => ['FEDEX_10KG_BOX', 'FEDEX_25KG_BOX'], 'filters' => [ 'within_us' => [], - 'from_us' => ['method' => ['INTERNATIONAL_PRIORITY']] + 'from_us' => [ + 'method' => [ + 'INTERNATIONAL_PRIORITY', + 'FEDEX_INTERNATIONAL_PRIORITY' + ] + ] ] ], [ @@ -885,6 +919,7 @@ public function getCode($type, $code = '') 'FEDEX_NATIONAL_FREIGHT', 'INTERNATIONAL_ECONOMY_FREIGHT', 'INTERNATIONAL_PRIORITY_FREIGHT', + 'FEDEX_INTERNATIONAL_PRIORITY' ] ] ] @@ -949,14 +984,21 @@ public function getCurrencyCode() */ public function getTracking($trackings) { - $this->setTrackingReqeust(); + if (!$this->getConfigData('use_rest_api')) { + $this->setTrackingReqeust(); + } if (!is_array($trackings)) { $trackings = [$trackings]; } foreach ($trackings as $tracking) { - $this->_getXMLTracking($tracking); + if ($this->getConfigData('use_rest_api')) { + $this->_getRestApiTracking($tracking); + } + else{ + $this->_getXMLTracking($tracking); + } } return $this->_result; @@ -1604,4 +1646,341 @@ public function getDeliveryConfirmationTypes(?Varien_Object $params = null) { return $this->getCode('delivery_confirmation_types'); } + + /** + * Prepare and set request to this instance + * + * @param Mage_Shipping_Model_Rate_Request $request + * @return $this + */ + public function setRestApiRequest(Mage_Shipping_Model_Rate_Request $request) + { + $this->_request = $request; + + // Step 1: Create an AccountNumber object + $accountNumber = new AccountNumber(value: $this->getConfigData('account')); + + // Step 2: Create a RequestedShipment object with shipment details + $requestedShipment = new RequestedShipment( + shipper: new RateParty( + address : new Address( + countryCode: Mage::getModel('directory/country')->load( + Mage::getStoreConfig( + Mage_Shipping_Model_Shipping::XML_PATH_STORE_COUNTRY_ID, + $request->getStoreId() + ) + )->getIso2Code(), + postalCode: Mage::getStoreConfig( + Mage_Shipping_Model_Shipping::XML_PATH_STORE_ZIP, + $request->getStoreId() + ) + )), + recipient: new RateParty( + address : new Address( + countryCode: Mage::getModel('directory/country')->load($request->getDestCountryId())->getIso2Code(), + postalCode: $request->getDestPostcode(), + )), + pickupType: 'CONTACT_FEDEX_TO_SCHEDULE', + requestedPackageLineItems: [ + new RequestedPackageLineItem( + weight: new Weight( + units: $this->getConfigData('unit_of_measure'), + value: $this->getTotalNumOfBoxes($request->getPackageWeight()), + )), + ], + rateRequestType: ['ACCOUNT'] //['LIST', 'ACCOUNT'] + ); + + // Step 3: Construct the FullSchemaQuoteRate object + $rateRequest = new FullSchemaQuoteRate( + accountNumber: $accountNumber, + requestedShipment: $requestedShipment, + ); + + $this->_fedexRestRequestData = $rateRequest; + + return $this; + } + + /** + * Do remote request for and handle errors + * + * @return Mage_Shipping_Model_Rate_Result + */ + protected function _getRestApiQuotes() + { + $this->_result = Mage::getModel('shipping/rate_result'); + + // Initialize FedEx SDK connector + $connector = new FedEx( + clientId: $this->getConfigData('key'), + clientSecret: $this->getConfigData('password'), + endpoint: $this->getConfigFlag('sandbox_mode') ? Endpoint::SANDBOX : Endpoint::PROD + ); + + try { + // Get the Rates and Transit Times API instance + $api = $connector->ratesTransitTimesV1(); + + $this->_debug($this->_fedexRestRequestData); + + // Perform the API call + $response = $api->rateAndTransitTimes($this->_fedexRestRequestData); + + // Process the response and convert it to Magento rate result format + $this->_prepareRestApiRateResponse($response); + } catch (Exception $e) { + $error = Mage::getModel('shipping/rate_result_error'); + $error->setCarrier($this->_code); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setErrorMessage($this->getConfigData('specificerrmsg')); + $this->_result->append($error); + } + } + + /** + * Return FeDex currency ISO code by Magento Base Currency Code + * + * @return string 3-digit currency code + */ + public function getOpenmageCurrencyCodeFromFedexCurrencyCode($currencyCode) + { + $codes = [ + 'RDD' => 'DOP', + 'ECD' => 'XCD', + 'ARN' => 'ARS', + 'SID' => 'SGD', + 'WON' => 'KRW', + 'JAD' => 'JMD', + 'SFR' => 'CHF', + 'JYE' => 'JPY', + 'KUD' => 'KWD', + 'UKL' => 'GBP', + 'DHS' => 'AED', + 'NMP' => 'MXN', + 'UYP' => 'UYU', + 'CHP' => 'CLP', + 'NTD' => 'TWD', + ]; + return $codes[$currencyCode] ?? $currencyCode; + } + + /** + * Prepare shipping rate result based on response + * + * @param mixed $response + * @return Mage_Shipping_Model_Rate_Result + */ + protected function _prepareRestApiRateResponse($response) + { + $rateResponseDetails = $response->dto()->output->rateReplyDetails ?? []; + + if (empty($rateResponseDetails)) { + $error = Mage::getModel('shipping/rate_result_error'); + $error->setCarrier($this->_code); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setErrorMessage($this->getConfigData('specificerrmsg')); + $this->_result->append($error); + return; + } + + $this->_debug($rateResponseDetails); + + foreach ($rateResponseDetails as $rateDetail) { + $serviceType = $rateDetail->serviceType ?? ''; + //remove FEDEX_ from serviceType and assign to secondType so that it's compatible with the mangento 1.9x web service allowed methods + $secondType = str_replace('FEDEX_', '', $serviceType); + + if (!array_key_exists($serviceType, $this->getAllowedMethods()) && !array_key_exists($secondType, $this->getAllowedMethods())) { + continue; + } + + $rate = $rateDetail->ratedShipmentDetails[0]->totalNetCharge; + + // if the currency is different with the store currency, calculate using the exchange rate + $currencyCode = (string)$rateDetail->ratedShipmentDetails[0]->shipmentRateDetail->currency; + $currencyCode = $this->getOpenmageCurrencyCodeFromFedexCurrencyCode($currencyCode); + $storeCurrencyCode = Mage::app()->getStore()->getBaseCurrencyCode(); + if($storeCurrencyCode != $currencyCode){ + $currencyStore = Mage::getModel('directory/currency')->load($storeCurrencyCode); + $currencyCurrent = Mage::getModel('directory/currency')->load($currencyCode); + $rate = round($rate / $currencyStore->getRate($currencyCurrent)); + } + + $method = Mage::getModel('shipping/rate_result_method'); + $method->setCarrier($this->_code); + $method->setCarrierTitle($this->getConfigData('title')); + $method->setMethod($serviceType); + $method->setMethodTitle($this->getCode('method', $serviceType)); + + $method->setCost($rate); + $method->setPrice($this->getFinalPriceWithHandlingFee($rate)); + + $this->_result->append($method); + } + } + + /** + * Send REST API request for tracking + * + * @param array $tracking + */ + protected function _getRestApiTracking($tracking) + { + $this->_result = Mage::getModel('shipping/tracking_result'); + + // Initialize FedEx SDK connector + $connector = new FedEx( + clientId: $this->getConfigData('rest_track_key'), + clientSecret: $this->getConfigData('rest_track_secrete'), + endpoint: $this->getConfigFlag('sandbox_mode') ? Endpoint::SANDBOX : Endpoint::PROD + ); + + try { + // Create a TrackingRequest instance + $trackRequest = new FullSchemaTrackingNumbers( + includeDetailedScans: true, + trackingInfo: [ + new TrackingInfo( + trackingNumberInfo: new TrackingNumberInfo(trackingNumber: $tracking), + ) + ], + ); + + $this->_debug($trackRequest); + + // Send tracking request + $api = $connector->trackV1(); + $response = $api->trackByTrackingNumber($trackRequest); + + // Parse the response and convert it to tracking result format + $this->_parseRestApiTrackingResponse($tracking, $response); + } catch (Exception $e) { + $error = Mage::getModel('shipping/tracking_result_error'); + $error->setCarrier($this->_code); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setTracking($tracking); + $error->setErrorMessage(Mage::helper('usa')->__('Unable to retrieve tracking')); + $this->_result->append($error); + } + } + + /** + * Parse tracking response + * + * @param array $trackingValue + * @param stdClass $response + */ + protected function _parseRestApiTrackingResponse($trackingValue, $response) + { + $this->_debug($response->body()); + + $trackInfo = $response->dto()->output->completeTrackResults[0]->trackResults[0] ?? null; + + if (!$trackInfo || !isset($trackInfo->latestStatusDetail)) { + $error = Mage::getModel('shipping/tracking_result_error'); + $error->setCarrier('fedex'); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setTracking($trackingValue); + $error->setErrorMessage(Mage::helper('usa')->__('Unable to retrieve tracking')); + $this->_result->append($error); + return; + } + + $resultArray = []; + $resultArray['status'] = (string)$trackInfo->latestStatusDetail->description; + $resultArray['service'] = (string)$trackInfo->serviceDetail->description; + + // Handle delivery date and time + if($trackInfo->dateAndTimes){ + if($trackInfo->dateAndTimes[0]->type == 'ACTUAL_DELIVERY' || $trackInfo->dateAndTimes[0]->type == 'ACTUAL_PICKUP' || + $trackInfo->dateAndTimes[0]->type == 'ESTIMATED_DELIVERY' || $trackInfo->dateAndTimes[0]->type == 'ESTIMATED_DELIVERY') { + $timestamp = strtotime((string) $trackInfo->dateAndTimes[0]->dateTime); + $resultArray['deliverydate'] = date('Y-m-d', $timestamp); + $resultArray['deliverytime'] = date('H:i:s', $timestamp); + } + + if($trackInfo->dateAndTimes[0]->type == 'ACTUAL_DELIVERY' || $trackInfo->dateAndTimes[0]->type == 'ACTUAL_PICKUP') { + $timestamp = strtotime((string) $trackInfo->dateAndTimes[0]->dateTime); + $resultArray['shippeddate'] = date('Y-m-d', $timestamp); + } + } + + // Handle delivery location + if (isset($trackInfo->lastUpdatedDestinationAddress)) { + $deliveryLocation = $trackInfo->lastUpdatedDestinationAddress; + $deliveryLocationArray = []; + if (isset($deliveryLocation->city)) { + $deliveryLocationArray[] = (string)$deliveryLocation->city; + } + if (isset($deliveryLocation->stateOrProvinceCode)) { + $deliveryLocationArray[] = (string)$deliveryLocation->stateOrProvinceCode; + } + if (isset($deliveryLocation->countryCode)) { + $deliveryLocationArray[] = (string)$deliveryLocation->countryCode; + } + if ($deliveryLocationArray) { + $resultArray['deliverylocation'] = implode(', ', $deliveryLocationArray); + } + } + + if(isset($trackInfo->deliveryDetails)) { + $resultArray['signedby'] = (string)($trackInfo->deliveryDetails->signedByName ?? ''); + } + + if (isset($trackInfo->packageDetails->weightAndDimensions->weight[0]->value) && isset($trackInfo->packageDetails->weightAndDimensions->weight[0]->unit)) { + $resultArray['weight'] = "{$trackInfo->packageDetails->weightAndDimensions->weight[0]->value} {$trackInfo->packageDetails->weightAndDimensions->weight[0]->unit}"; + } + + // Track package progress + $packageProgress = []; + foreach ($trackInfo->scanEvents ?? [] as $event) { + $tempArray = []; + $tempArray['activity'] = (string)$event->derivedStatus; + $timestamp = strtotime((string)$event->date); + if ($timestamp) { + $tempArray['deliverydate'] = date('Y-m-d', $timestamp); + $tempArray['deliverytime'] = date('H:i:s', $timestamp); + } + if (isset($event->scanLocation)) { + $addressArray = []; + if (isset($event->scanLocation->city)) { + $addressArray[] = (string)$event->scanLocation->city; + } + if (isset($event->scanLocation->stateOrProvinceCode)) { + $addressArray[] = (string)$event->scanLocation->stateOrProvinceCode; + } + if (isset($event->scanLocation->countryCode)) { + $addressArray[] = (string)$event->scanLocation->countryCode; + } + if ($addressArray) { + $tempArray['deliverylocation'] = implode(', ', $addressArray); + } + } + $packageProgress[] = $tempArray; + } + $resultArray['progressdetail'] = $packageProgress; + + // Prepare tracking result + if (!$this->_result) { + $this->_result = Mage::getModel('shipping/tracking_result'); + } + + if (isset($resultArray)) { + $tracking = Mage::getModel('shipping/tracking_result_status'); + $tracking->setCarrier('fedex'); + $tracking->setCarrierTitle($this->getConfigData('title')); + $tracking->setTracking($trackingValue); + $tracking->addData($resultArray); + $this->_result->append($tracking); + } else { + $error = Mage::getModel('shipping/tracking_result_error'); + $error->setCarrier('fedex'); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setTracking($trackingValue); + $error->setErrorMessage(Mage::helper('usa')->__('Unable to retrieve tracking')); + $this->_result->append($error); + } + } + } diff --git a/app/code/core/Mage/Usa/etc/config.xml b/app/code/core/Mage/Usa/etc/config.xml index 6925a94e3ab..314ff0a32eb 100644 --- a/app/code/core/Mage/Usa/etc/config.xml +++ b/app/code/core/Mage/Usa/etc/config.xml @@ -126,6 +126,8 @@ + + 0 0 0 diff --git a/app/code/core/Mage/Usa/etc/system.xml b/app/code/core/Mage/Usa/etc/system.xml index 2a2dcbf859e..ec340e23163 100644 --- a/app/code/core/Mage/Usa/etc/system.xml +++ b/app/code/core/Mage/Usa/etc/system.xml @@ -428,6 +428,35 @@ 1 0 + + + select + adminhtml/system_config_source_yesno + 15 + 1 + 1 + 0 + + + + obscure + adminhtml/system_config_backend_encrypted + 17 + 1 + 1 + 0 + Fedex RestAPI need a seperate project for tracking, it's different with project that collect rate. + + + + obscure + adminhtml/system_config_backend_encrypted + 18 + 1 + 1 + 0 + Fedex RestAPI need a seperate project for tracking, it's different with project that collect rate. + <label>Title</label> <sort_order>20</sort_order> @@ -456,6 +485,7 @@ <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> <depends><active>1</active></depends> + <comment>WebService Meter Number. Not used in Fedex RestAPI</comment> </meter_number> <key translate="label"> <label>Key</label> @@ -466,6 +496,7 @@ <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> <depends><active>1</active></depends> + <comment>WebService key. For Fedex RestAPI, this is the API Key</comment> </key> <password translate="label"> <label>Password</label> @@ -476,6 +507,7 @@ <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> <depends><active>1</active></depends> + <comment>WebService Password. For Fedex RestAPI, this is the Secrete Key</comment> </password> <sandbox_mode translate="label"> <label>Sandbox Mode</label> diff --git a/composer.json b/composer.json index bedc06e2a6a..6db2bc982cb 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "phpseclib/mcrypt_compat": "^2.0.3", "phpseclib/phpseclib": "^3.0.14", "shardj/zf1-future": "^1.24.1", + "shipstream/fedex-rest-sdk": "dev-patch-1", "symfony/polyfill-php74": "^1.31", "symfony/polyfill-php80": "^1.31", "symfony/polyfill-php81": "^1.31", @@ -55,6 +56,12 @@ "squizlabs/php_codesniffer": "^3.7", "symplify/vendor-patches": "^11.1" }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/xqiu/fedex-rest-php-sdk" + } + ], "conflict": { "n98/n98_layouthelper": "*" }, @@ -129,7 +136,7 @@ "cweagans/composer-patches": true }, "platform": { - "php": "7.4" + "php": "8.1.28" }, "sort-packages": true }, From 3627816090a49b3d261b34075de6b1cc8c4bcf39 Mon Sep 17 00:00:00 2001 From: xqiu <1487053+xqiu@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:55:56 -0800 Subject: [PATCH 2/4] Update composer.json shipstream/fedex-rest-sdk to ^1.1.2 --- composer.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 6db2bc982cb..d10f26dceb8 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "phpseclib/mcrypt_compat": "^2.0.3", "phpseclib/phpseclib": "^3.0.14", "shardj/zf1-future": "^1.24.1", - "shipstream/fedex-rest-sdk": "dev-patch-1", + "shipstream/fedex-rest-sdk": "^1.1.2", "symfony/polyfill-php74": "^1.31", "symfony/polyfill-php80": "^1.31", "symfony/polyfill-php81": "^1.31", @@ -56,12 +56,6 @@ "squizlabs/php_codesniffer": "^3.7", "symplify/vendor-patches": "^11.1" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/xqiu/fedex-rest-php-sdk" - } - ], "conflict": { "n98/n98_layouthelper": "*" }, From 10f3f3881d3a7fd162cb04457a1548f446380c21 Mon Sep 17 00:00:00 2001 From: xqiu <1487053+xqiu@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:30:44 -0800 Subject: [PATCH 3/4] Add @xqiu as a contributor --- .all-contributorsrc | 9 +++++++++ README.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index cb3551ec970..05db1cecb35 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1472,6 +1472,15 @@ "contributions": [ "code" ] + }, + { + "login": "xqiu", + "name": "Xinyang Qiu", + "avatar_url": "https://avatars.githubusercontent.com/u/1487053?v=4", + "profile": "https://github.com/xqiu", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7 diff --git a/README.md b/README.md index 3fed724a1de..dbb89ee65b8 100644 --- a/README.md +++ b/README.md @@ -613,6 +613,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d </tr> <tr> <td align="center" valign="top" width="14.28%"><a href="https://github.com/ragnese"><img src="https://avatars.githubusercontent.com/u/7927565?v=4" loading="lazy" width="100" alt=""/><br /><sub><b>Rob Agnese</b></sub></a></td> + <td align="center" valign="top" width="14.28%"><a href="https://github.com/xqiu"><img src="https://avatars.githubusercontent.com/u/1487053?v=4" loading="lazy" width="100" alt=""/><br /><sub><b>Xinyang Qiu</b></sub></a></td> </tr> </tbody> </table> From 17f8dcee358409be0f4472287545459d19c336a9 Mon Sep 17 00:00:00 2001 From: xqiu <1487053+xqiu@users.noreply.github.com> Date: Sun, 26 Jan 2025 10:02:14 -0800 Subject: [PATCH 4/4] Add retry logic with shipDatestamp increment for Fedex REST API quote --- .../Mage/Usa/Model/Shipping/Carrier/Fedex.php | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php index d157b10539b..2075059cfba 100644 --- a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php +++ b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php @@ -1718,23 +1718,51 @@ protected function _getRestApiQuotes() endpoint: $this->getConfigFlag('sandbox_mode') ? Endpoint::SANDBOX : Endpoint::PROD ); - try { - // Get the Rates and Transit Times API instance - $api = $connector->ratesTransitTimesV1(); - - $this->_debug($this->_fedexRestRequestData); + // need to retry 10 times if the request is failed, each time, set shipDatestamp to the next day, format is YYYY-MM-DD + $shipDatestamp = date('Y-m-d'); + $maxRetries = 10; + $attempt = 0; + while ($attempt < $maxRetries) { + try { + // Get the Rates and Transit Times API instance + $api = $connector->ratesTransitTimesV1(); + + // Create a FullSchemaQuoteRate request using the data prepared in setRequest + //$rateRequest = new FullSchemaQuoteRate(); + + $this->_debug('Quote request:'); + $this->_debug($this->_fedexRestRequestData); + + if($attempt > 0){ + $shipDatestamp = date('Y-m-d', strtotime($shipDatestamp . ' +1 day')); + $this->_fedexRestRequestData->requestedShipment->shipDateStamp = $shipDatestamp; + } - // Perform the API call - $response = $api->rateAndTransitTimes($this->_fedexRestRequestData); - - // Process the response and convert it to Magento rate result format - $this->_prepareRestApiRateResponse($response); - } catch (Exception $e) { - $error = Mage::getModel('shipping/rate_result_error'); - $error->setCarrier($this->_code); - $error->setCarrierTitle($this->getConfigData('title')); - $error->setErrorMessage($this->getConfigData('specificerrmsg')); - $this->_result->append($error); + // Perform the API call + $response = $api->rateAndTransitTimes($this->_fedexRestRequestData); + + // Process the response and convert it to Magento rate result format + $this->_prepareRestApiRateResponse($response); + break; // Exit loop on success + } catch (Exception $e) { + $attempt++; + if ($attempt >= $maxRetries) { + if (method_exists($e, 'getResponse') && $e->getResponse()) { + $this->_debug($e->getResponse()->body()); + Mage::logException(new Exception(print_r($this->_fedexRestRequestData, true) . ' returns ' . print_r($e->getResponse()->body(), true))); + } else { + Mage::logException($e); + } + $error = Mage::getModel('shipping/rate_result_error'); + $error->setCarrier($this->_code); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setErrorMessage($this->getConfigData('specificerrmsg')); + $this->_result->append($error); + } else { + // Optional: wait before retrying + sleep(1); + } + } } }