diff -ruN vendor/magento/module-fedex/Model/Carrier.php vendor/magento/module-fedex/Model/Carrier.php
--- a/vendor/magento/module-fedex/Model/Carrier.php	2025-11-03 19:36:03.819611388 +0530
+++ b/vendor/magento/module-fedex/Model/Carrier.php	2025-11-03 20:43:59.456411400 +0530
@@ -9,10 +9,12 @@
 use Magento\Framework\App\ObjectManager;
 use Magento\Framework\DataObject;
 use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\HTTP\Client\CurlFactory;
 use Magento\Framework\Measure\Length;
 use Magento\Framework\Measure\Weight;
 use Magento\Framework\Module\Dir;
 use Magento\Framework\Serialize\Serializer\Json;
+use Magento\Framework\Url\DecoderInterface;
 use Magento\Framework\Webapi\Soap\ClientFactory;
 use Magento\Framework\Xml\Security;
 use Magento\Quote\Model\Quote\Address\RateRequest;
@@ -51,6 +53,41 @@
     public const RATE_REQUEST_SMARTPOST = 'SMART_POST';
 
     /**
+     * Web Service Type REST
+     *
+     * @var string
+     */
+    public const WEB_SERVICE_REST = 'REST';
+
+    /**
+     * REST end point for Rate API
+     *
+     * @var string
+     */
+    public const RATE_REQUEST_END_POINT = 'rate/v1/rates/quotes';
+
+    /**
+     * Authentication grant type for REST endpoints
+     *
+     * @var string
+     */
+    public const AUTHENTICATION_GRANT_TYPE = 'client_credentials';
+
+    /**
+     * Oauth End point to get Access Token
+     *
+     * @var string
+     */
+    public const OAUTH_REQUEST_END_POINT = 'oauth/token';
+
+    /**
+     * REST end point of Tracking API
+     *
+     * @var string
+     */
+    public const TRACK_REQUEST_END_POINT = 'track/v1/trackingnumbers';
+
+    /**
      * Code of the carrier
      *
      * @var string
@@ -58,6 +95,20 @@
     protected $_code = self::CODE;
 
     /**
+     * REST end point to Create Shipment
+     *
+     * @var string
+     */
+    public const SHIPMENT_REQUEST_END_POINT = '/ship/v1/shipments';
+
+    /**
+     * REST end point to cancel Shipment
+     *
+     * @var string
+     */
+    public const SHIPMENT_CANCEL_END_POINT = '/ship/v1/shipments/cancel';
+
+    /**
      * Types of rates, order is important
      *
      * @var array
@@ -165,6 +216,16 @@
     private $_rawTrackingRequest;
 
     /**
+     * @var CurlFactory
+     */
+    private $curlFactory;
+
+    /**
+     * @var DecoderInterface
+     */
+    private $decoderInterface;
+
+    /**
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
      * @param \Psr\Log\LoggerInterface $logger
@@ -183,6 +244,8 @@
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\Framework\Module\Dir\Reader $configReader
      * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
+     * @param CurlFactory $curlFactory
+     * @param DecoderInterface $decoderInterface
      * @param array $data
      * @param Json|null $serializer
      * @param ClientFactory|null $soapClientFactory
@@ -207,6 +270,8 @@
         \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Framework\Module\Dir\Reader $configReader,
         \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
+        CurlFactory $curlFactory,
+        DecoderInterface $decoderInterface,
         array $data = [],
         Json $serializer = null,
         ClientFactory $soapClientFactory = null
@@ -237,6 +302,8 @@
         $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v' . self::$trackServiceVersion . '.wsdl';
         $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
         $this->soapClientFactory = $soapClientFactory ?: ObjectManager::getInstance()->get(ClientFactory::class);
+        $this->curlFactory = $curlFactory;
+        $this->decoderInterface = $decoderInterface;
     }
 
     /**
@@ -431,6 +498,87 @@
     }
 
     /**
+     * Forming request for rate estimation depending to the purpose for REST API
+     *
+     * @param string $purpose
+     * @return array
+     */
+    protected function _formRestRateRequest($purpose): array
+    {
+        $r = $this->_rawRequest;
+        $ratesRequest = [
+            'accountNumber' => [
+                'value' => $r->getAccount()
+            ],
+            'requestedShipment' => [
+                'pickupType' => $this->getConfigData('pickup_type'),
+                'packagingType' => $r->getPackaging(),
+                'shipper' => [
+                    'address' => ['postalCode' => $r->getOrigPostal(), 'countryCode' => $r->getOrigCountry()],
+                ],
+                'recipient' => [
+                    'address' => [
+                        'postalCode' => $r->getDestPostal(),
+                        'countryCode' => $r->getDestCountry(),
+                        'residential' => (bool)$this->getConfigData('residence_delivery'),
+                    ],
+                ],
+                'customsClearanceDetail' => [
+                    'dutiesPayment' => [
+                        'payor' => [
+                            'responsibleParty' => [
+                                'accountNumber' => [
+                                    'value' => $r->getAccount()
+                                ],
+                                'address' => [
+                                    'countryCode' => $r->getOrigCountry()
+                                ]
+                            ]
+                        ],
+                        'paymentType' => 'SENDER',
+                    ],
+                    'commodities' => [
+                        [
+                            'customsValue' => ['amount' => $r->getValue(), 'currency' => $this->getCurrencyCode()]
+                        ]
+                    ]
+                ],
+                'rateRequestType' => ['LIST']
+            ]
+        ];
+
+        foreach ($r->getPackages() as $packageNum => $package) {
+            $ratesRequest['requestedShipment']['requestedPackageLineItems'][$packageNum]['subPackagingType'] =
+                'PACKAGE';
+            $ratesRequest['requestedShipment']['requestedPackageLineItems'][$packageNum]['groupPackageCount'] = 1;
+            $ratesRequest['requestedShipment']['requestedPackageLineItems'][$packageNum]['weight']['value']
+                = (double) $package['weight'];
+            $ratesRequest['requestedShipment']['requestedPackageLineItems'][$packageNum]['weight']['units']
+                = $this->getConfigData('unit_of_measure');
+            if (isset($package['price'])) {
+                $ratesRequest['requestedShipment']['requestedPackageLineItems'][$packageNum]['declaredValue']['amount']
+                    = (double) $package['price'];
+                $ratesRequest['requestedShipment']['requestedPackageLineItems'][$packageNum]['declaredValue']
+                ['currency'] = $this->getCurrencyCode();
+            }
+        }
+
+        $ratesRequest['requestedShipment']['totalPackageCount'] = count($r->getPackages());
+        if ($r->getDestCity()) {
+            $ratesRequest['requestedShipment']['recipient']['address']['city'] = $r->getDestCity();
+        }
+
+        if ($purpose == self::RATE_REQUEST_SMARTPOST) {
+            $ratesRequest['requestedShipment']['serviceType'] = self::RATE_REQUEST_SMARTPOST;
+            $ratesRequest['requestedShipment']['smartPostInfoDetail'] = [
+                'indicia' => (double)$r->getWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
+                'hubId' => $this->getConfigData('smartpost_hubid'),
+            ];
+        }
+        return $ratesRequest;
+    }
+
+    /**
      * Forming request for rate estimation depending to the purpose
      *
      * @param string $purpose
@@ -501,6 +649,82 @@
 
         return $ratesRequest;
     }
+    /**
+     * Send request for tracking
+     *
+     * @param string $tracking
+     * @return void
+     */
+    protected function _getTrackingInformation($tracking): void
+    {
+        $accessToken = $this->_getAccessToken();
+        if (!empty($accessToken)) {
+
+            $trackRequest = [
+                'includeDetailedScans' => true,
+                'trackingInfo' => [
+                    [
+                        'trackingNumberInfo' => [
+                            'trackingNumber'=> $tracking
+                        ]
+                    ]
+                ]
+            ];
+
+            $requestString = $this->serializer->serialize($trackRequest);
+            $response = $this->_getCachedQuotes($requestString);
+            $debugData = ['request' => $trackRequest];
+
+            if ($response === null) {
+                $response = $this->sendRequest(self::TRACK_REQUEST_END_POINT, $requestString, $accessToken);
+                $this->_setCachedQuotes($requestString, $response);
+            }
+            $debugData['result'] = $response;
+
+            $this->_debug($debugData);
+            $this->_parseTrackingResponse($tracking, $response);
+        } else {
+            $this->appendTrackingError(
+                $tracking,
+                __('Authorization Error. No Access Token found with given credentials.')
+            );
+            return;
+        }
+    }
+
+    /**
+     * Get Access Token for Rest API
+     *
+     * @return string|null
+     */
+    protected function _getAccessToken(): string|null
+    {
+        $apiKey = $this->getConfigData('api_key') ?? null;
+        $secretKey = $this->getConfigData('secret_key') ?? null;
+
+        if (!$apiKey || !$secretKey) {
+            $this->_debug(__('Authentication keys are missing.'));
+            return null;
+        }
+
+        $requestArray = [
+            'grant_type' => self::AUTHENTICATION_GRANT_TYPE,
+            'client_id' => $apiKey,
+            'client_secret' => $secretKey
+        ];
+
+        $request = http_build_query($requestArray);
+        $accessToken = null;
+        $response = $this->sendRequest(self::OAUTH_REQUEST_END_POINT, $request);
+
+        if (!empty($response['errors'])) {
+            $debugData = ['request_type' => 'Access Token Request', 'result' => $response];
+            $this->_debug($debugData);
+        } elseif (!empty($response['access_token'])) {
+            $accessToken = $response['access_token'];
+        }
+        return $accessToken;
+    }
 
     /**
      * Makes remote request to the carrier and returns a response
@@ -511,6 +735,17 @@
     protected function _doRatesRequest($purpose)
     {
         $ratesRequest = $this->_formRateRequest($purpose);
+        $accessToken = null;
+
+        if ($this->isRestConfiguration()) {
+            $response = null;
+            $accessToken = $this->_getAccessToken();
+            if (empty($accessToken)) {
+                return null;
+            }
+            $ratesRequest = $this->_formRestRateRequest($purpose);
+        }
+
         $ratesRequestNoShipTimestamp = $ratesRequest;
         unset($ratesRequestNoShipTimestamp['RequestedShipment']['ShipTimestamp']);
         $requestString = $this->serializer->serialize($ratesRequestNoShipTimestamp);
@@ -518,8 +753,14 @@
         $debugData = ['request' => $this->filterDebugData($ratesRequest)];
         if ($response === null) {
             try {
-                $client = $this->_createRateSoapClient();
-                $response = $client->getRates($ratesRequest);
+                if ($this->isRestConfiguration()) {
+                    $response = $this->sendRequest(self::RATE_REQUEST_END_POINT, $requestString, $accessToken);
+                    $debugData['type'] = 'REST';
+                } else {
+                    $client = $this->_createRateSoapClient();
+                    $response = $client->getRates($ratesRequest);
+                    $debugData['type'] = 'SOAP';
+                }
                 $this->_setCachedQuotes($requestString, $response);
                 $debugData['result'] = $response;
             } catch (\Exception $e) {
@@ -535,6 +776,73 @@
     }
 
     /**
+     * Send Curl Request
+     *
+     * @param string $endpoint
+     * @param string $request
+     * @param string|null $accessToken
+     * @return array|bool
+     */
+    protected function sendRequest($endpoint, $request, $accessToken = null): array|bool
+    {
+        if ($accessToken) {
+            $headers = [
+                'Content-Type' => 'application/json',
+                'Authorization' => 'Bearer '.$accessToken,
+                'X-locale' => 'en_US',
+
+            ];
+        } else {
+            $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
+        }
+
+        $curlClient = $this->curlFactory->create();
+        $url = $this->_getUrl($endpoint);
+        try {
+            $curlClient->setHeaders($headers);
+            if ($endpoint == self::SHIPMENT_CANCEL_END_POINT) {
+                $curlClient->setOptions([CURLOPT_ENCODING => 'gzip,deflate,sdch', CURLOPT_CUSTOMREQUEST => 'PUT']);
+            } else {
+                $curlClient->setOptions([CURLOPT_ENCODING => 'gzip,deflate,sdch']);
+            }
+            $curlClient->post($url, $request);
+            $response = $curlClient->getBody();
+            $debugData = ['curl_response' => $response];
+            $debugData['url'] = $url;
+            $this->_debug($debugData);
+        $writer = new \Zend_Log_Writer_Stream(BP . '/var/log/fedex.log');
+        $logger = new \Zend_Log();
+        $logger->addWriter($writer);
+        $logger->info('start');
+        $logger->info(print_r($response,true));
+        
+            return $this->serializer->unserialize($response);
+        } catch (\Exception $e) {
+            $this->_logger->critical($e);
+        }
+        return false;
+    }
+
+    /**
+     * Get Url for REST API
+     *
+     * @param string|null $endpoint
+     * @return string
+     */
+    protected function _getUrl($endpoint = null): string
+    {
+        $url = $this->getConfigFlag('sandbox_mode') ? $this->getConfigData('sandbox_webservices_url')
+            : $this->getConfigData('production_webservices_url');
+
+        if ($this->isRestConfiguration()) {
+            $url = $this->getConfigFlag('sandbox_mode') ? $this->getConfigData('rest_sandbox_webservices_url')
+                : $this->getConfigData('rest_production_webservices_url');
+        }
+
+        return $endpoint ? $url  . $endpoint : $url;
+    }
+
+    /**
      * Do remote request for and handle errors
      *
      * @return Result
@@ -571,13 +879,35 @@
      * @return Result
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
-    protected function _prepareRateResponse($response)
+    protected function _prepareRateResponse($response): Result
     {
         $costArr = [];
         $priceArr = [];
-        $errorTitle = 'For some reason we can\'t retrieve tracking info right now.';
+        $errorTitle = __('For some reason we can\'t retrieve tracking info right now.');
 
-        if (is_object($response)) {
+        if ($this->isRestConfiguration() && is_array($response)) {
+            if (!empty($response['errors'])) {
+                if (is_array($response['errors'])) {
+                    $notification = reset($response['errors']);
+                    $errorTitle = (string)$notification['message'];
+                } else {
+                    $errorTitle = (string)$response['errors']['message'];
+                }
+            } elseif (isset($response['output']['rateReplyDetails'])) {
+                $allowedMethods = explode(",", $this->getConfigData('allowed_methods'));
+                if (is_array($response['output']['rateReplyDetails'])) {
+                    foreach ($response['output']['rateReplyDetails'] as $rate) {
+                        $serviceName = (string)$rate['serviceType'];
+                        if (in_array($serviceName, $allowedMethods)) {
+                            $amount = $this->_getRateAmountOriginBased($rate);
+                            $costArr[$serviceName] = $amount;
+                            $priceArr[$serviceName] = $this->getMethodPrice($amount, $serviceName);
+                        }
+                    }
+                    asort($priceArr);
+                }
+            }
+        } elseif (is_object($response)) {
             if ($response->HighestSeverity == 'FAILURE' || $response->HighestSeverity == 'ERROR') {
                 if (is_array($response->Notifications)) {
                     $notification = array_pop($response->Notifications);
@@ -674,34 +1004,86 @@
      * @param \stdClass $rate
      * @return null|float
      */
-    protected function _getRateAmountOriginBased($rate)
+    protected function _getRateAmountOriginBasedSoap($rate): null|float
     {
         $amount = null;
         $currencyCode = '';
         $rateTypeAmounts = [];
-        if (is_object($rate)) {
-            // The "RATED..." rates are expressed in the currency of the origin country
-            foreach ($rate->RatedShipmentDetails as $ratedShipmentDetail) {
-                $netAmount = (string)$ratedShipmentDetail->ShipmentRateDetail->TotalNetCharge->Amount;
-                $currencyCode = (string)$ratedShipmentDetail->ShipmentRateDetail->TotalNetCharge->Currency;
-                $rateType = (string)$ratedShipmentDetail->ShipmentRateDetail->RateType;
-                $rateTypeAmounts[$rateType] = $netAmount;
+
+        // The "RATED..." rates are expressed in the currency of the origin country
+        foreach ($rate->RatedShipmentDetails as $ratedShipmentDetail) {
+            $netAmount = (string)$ratedShipmentDetail->ShipmentRateDetail->TotalNetCharge->Amount;
+            $currencyCode = (string)$ratedShipmentDetail->ShipmentRateDetail->TotalNetCharge->Currency;
+            $rateType = (string)$ratedShipmentDetail->ShipmentRateDetail->RateType;
+            $rateTypeAmounts[$rateType] = $netAmount;
+        }
+
+        foreach ($this->_ratesOrder as $rateType) {
+            if (!empty($rateTypeAmounts[$rateType])) {
+                $amount = $rateTypeAmounts[$rateType];
+                break;
             }
+        }
 
-            foreach ($this->_ratesOrder as $rateType) {
-                if (!empty($rateTypeAmounts[$rateType])) {
-                    $amount = $rateTypeAmounts[$rateType];
-                    break;
-                }
+        if ($amount === null) {
+            $amount = (string)$rate->RatedShipmentDetails[0]->ShipmentRateDetail->TotalNetCharge->Amount;
+        }
+
+        return (float)$amount * $this->getBaseCurrencyRate($currencyCode);
+    }
+
+    /**
+     * Get origin based amount form response of rate estimation
+     *
+     * @param array $rate
+     * @return null|float
+     */
+    protected function _getRateAmountOriginBasedRest($rate): null|float
+    {
+        $amount = null;
+        $currencyCode = '';
+        $rateTypeAmounts = [];
+
+        // The "RATED..." rates are expressed in the currency of the origin country
+        foreach ($rate['ratedShipmentDetails'] as $ratedShipmentDetail) {
+            $netAmount = (string)$ratedShipmentDetail['totalNetCharge'];
+            $currencyCode = (string)$ratedShipmentDetail['shipmentRateDetail']['currency'];
+
+            if (!empty($ratedShipmentDetail['ratedPackages'])) {
+                $rateType = (string)reset($ratedShipmentDetail['ratedPackages'])
+                    ['packageRateDetail']['rateType'];
+                $rateTypeAmounts[$rateType] = $netAmount;
             }
+        }
 
-            if ($amount === null) {
-                $amount = (string)$rate->RatedShipmentDetails[0]->ShipmentRateDetail->TotalNetCharge->Amount;
+        foreach ($this->_ratesOrder as $rateType) {
+            if (!empty($rateTypeAmounts[$rateType])) {
+                $amount = $rateTypeAmounts[$rateType];
+                break;
             }
+        }
 
-            $amount = (float)$amount * $this->getBaseCurrencyRate($currencyCode);
+        if ($amount === null && !empty($rate['ratedShipmentDetails'][0]['totalNetCharge'])) {
+            $amount = (string)$rate['ratedShipmentDetails'][0]['totalNetCharge'];
         }
 
+        return (float)$amount * $this->getBaseCurrencyRate($currencyCode);
+    }
+
+    /**
+     * Get origin based amount form response of rate estimation
+     *
+     * @param \stdClass|array $rate
+     * @return null|float
+     */
+    protected function _getRateAmountOriginBased($rate): null|float
+    {
+        $amount = null;
+        if ($this->isRestConfiguration() && is_array($rate)) {
+            $amount = $this->_getRateAmountOriginBasedRest($rate);
+        } elseif (is_object($rate)) {
+            $amount = $this->_getRateAmountOriginBasedSoap($rate);
+        }
         return $amount;
     }
 
@@ -929,6 +1311,15 @@
                 'BUSINESS_SERVICE_CENTER' => __('Business Service Center'),
                 'STATION' => __('Station'),
             ],
+            'pickup_type' => [
+                'CONTACT_FEDEX_TO_SCHEDULE' => __('Contact Fedex to Schedule'),
+                'DROPOFF_AT_FEDEX_LOCATION' => __('DropOff at Fedex Location'),
+                'USE_SCHEDULED_PICKUP' => __('Use Scheduled Pickup'),
+                'ON_CALL' => __('On Call'),
+                'PACKAGE_RETURN_PROGRAM' => __('Package Return Program'),
+                'REGULAR_STOP' => __('Regular Stop'),
+                'TAG' => __('Tag'),
+            ],
             'packaging' => [
                 'FEDEX_ENVELOPE' => __('FedEx Envelope'),
                 'FEDEX_PAK' => __('FedEx Pak'),
@@ -938,6 +1329,10 @@
                 'FEDEX_25KG_BOX' => __('FedEx 25kg Box'),
                 'YOUR_PACKAGING' => __('Your Packaging'),
             ],
+            'web_service_type' => [
+                'SOAP' => __('SOAP'),
+                'REST' => __('REST')
+            ],
             'containers_filter' => [
                 [
                     'containers' => ['FEDEX_ENVELOPE', 'FEDEX_PAK'],
@@ -1088,14 +1483,23 @@
      */
     public function getTracking($trackings)
     {
-        $this->setTrackingReqeust();
+        if ($this->isRestConfiguration()) {
+            if (!is_array($trackings)) {
+                $trackings = [$trackings];
+            }
+            foreach ($trackings as $tracking) {
+                $this->_getTrackingInformation($tracking);
+            }
+        } else {
+            $this->setTrackingReqeust();
 
-        if (!is_array($trackings)) {
-            $trackings = [$trackings];
-        }
+            if (!is_array($trackings)) {
+                $trackings = [$trackings];
+            }
 
-        foreach ($trackings as $tracking) {
-            $this->_getXMLTracking($tracking);
+            foreach ($trackings as $tracking) {
+                $this->_getXMLTracking($tracking);
+            }
         }
 
         return $this->_result;
@@ -1119,7 +1523,7 @@
     /**
      * Send request for tracking
      *
-     * @param string[] $tracking
+     * @param string|string[] $tracking
      * @return void
      */
     protected function _getXMLTracking($tracking)
@@ -1173,25 +1577,41 @@
      * @param string $trackingValue
      * @param \stdClass $response
      * @return void
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
      */
     protected function _parseTrackingResponse($trackingValue, $response)
     {
-        if (!is_object($response) || empty($response->HighestSeverity)) {
-            $this->appendTrackingError($trackingValue, __('Invalid response from carrier'));
-            return;
-        } elseif (in_array($response->HighestSeverity, self::$trackingErrors)) {
-            $this->appendTrackingError($trackingValue, (string) $response->Notifications->Message);
-            return;
-        } elseif (empty($response->CompletedTrackDetails) || empty($response->CompletedTrackDetails->TrackDetails)) {
-            $this->appendTrackingError($trackingValue, __('No available tracking items'));
-            return;
-        }
-
-        $trackInfo = $response->CompletedTrackDetails->TrackDetails;
-
-        // Fedex can return tracking details as single object instead array
-        if (is_object($trackInfo)) {
-            $trackInfo = [$trackInfo];
+        if ($this->isRestConfiguration()) {
+            if (!is_array($response) || empty($response['output'])) {
+                $this->_debug($response);
+                $this->appendTrackingError($trackingValue, __('Invalid response from carrier'));
+                return;
+            } elseif (empty(reset($response['output']['completeTrackResults'])['trackResults'])) {
+                $this->_debug('No available tracking items');
+                $this->appendTrackingError($trackingValue, __('No available tracking items'));
+                return;
+            }
+            $trackInfo = reset($response['output']['completeTrackResults'])['trackResults'];
+        } else {
+            if (!is_object($response) || empty($response->HighestSeverity)) {
+                $this->appendTrackingError($trackingValue, __('Invalid response from carrier'));
+                return;
+            } elseif (in_array($response->HighestSeverity, self::$trackingErrors)) {
+                $this->appendTrackingError($trackingValue, (string) $response->Notifications->Message);
+                return;
+            } elseif (empty($response->CompletedTrackDetails)
+                            || empty($response->CompletedTrackDetails->TrackDetails)) {
+                $this->appendTrackingError($trackingValue, __('No available tracking items'));
+                return;
+            }
+
+            $trackInfo = $response->CompletedTrackDetails->TrackDetails;
+
+            // Fedex can return tracking details as single object instead array
+            if (is_object($trackInfo)) {
+                $trackInfo = [$trackInfo];
+            }
         }
 
         $result = $this->getResult();
@@ -1202,7 +1622,13 @@
             $tracking->setCarrier(self::CODE);
             $tracking->setCarrierTitle($carrierTitle);
             $tracking->setTracking($trackingValue);
-            $tracking->addData($this->processTrackingDetails($item));
+
+            if ($this->isRestConfiguration()) {
+                $tracking->addData($this->processRestTrackingDetails($item));
+            } else {
+                $tracking->addData($this->processTrackingDetails($item));
+            }
+
             $result->append($tracking);
             $counter ++;
         }
@@ -1344,136 +1770,268 @@
         $paymentType = $this->getPaymentType($request);
         $optionType = $request->getShippingMethod() == self::RATE_REQUEST_SMARTPOST
             ? 'SERVICE_DEFAULT' : $packageParams->getDeliveryConfirmation();
-        $requestClient = [
-            'RequestedShipment' => [
-                'ShipTimestamp' => time(),
-                'DropoffType' => $this->getConfigData('dropoff'),
-                'PackagingType' => $request->getPackagingType(),
-                'ServiceType' => $request->getShippingMethod(),
-                'Shipper' => [
-                    'Contact' => [
-                        'PersonName' => $request->getShipperContactPersonName(),
-                        'CompanyName' => $request->getShipperContactCompanyName(),
-                        'PhoneNumber' => $request->getShipperContactPhoneNumber(),
-                    ],
-                    'Address' => [
-                        'StreetLines' => [$request->getShipperAddressStreet()],
-                        'City' => $request->getShipperAddressCity(),
-                        'StateOrProvinceCode' => $request->getShipperAddressStateOrProvinceCode(),
-                        'PostalCode' => $request->getShipperAddressPostalCode(),
-                        'CountryCode' => $request->getShipperAddressCountryCode(),
+
+        if ($this->isRestConfiguration()) {
+            $requestClient = [
+                'requestedShipment' => [
+                    'shipDatestamp' => date('Y-m-d'),
+                    'pickupType' => $this->getConfigData('pickup_type'),
+                    'serviceType' => $request->getShippingMethod(),
+                    'packagingType' => $request->getPackagingType(),
+                    'shipper' => [
+                        'contact' => [
+                            'personName' => $request->getShipperContactPersonName(),
+                            'companyName' => $request->getShipperContactCompanyName(),
+                            'phoneNumber' => $request->getShipperContactPhoneNumber(),
+                        ],
+                        'address' => [
+                            'streetLines' => [$request->getShipperAddressStreet()],
+                            'city' => $request->getShipperAddressCity(),
+                            'stateOrProvinceCode' => $request->getShipperAddressStateOrProvinceCode(),
+                            'postalCode' => $request->getShipperAddressPostalCode(),
+                            'countryCode' => $request->getShipperAddressCountryCode(),
+                        ]
                     ],
-                ],
-                'Recipient' => [
-                    'Contact' => [
-                        'PersonName' => $request->getRecipientContactPersonName(),
-                        'CompanyName' => $request->getRecipientContactCompanyName(),
-                        'PhoneNumber' => $request->getRecipientContactPhoneNumber(),
+                    'recipients' => [
+                        [
+                            'contact' => [
+                                'personName' => $request->getRecipientContactPersonName(),
+                                'companyName' => $request->getRecipientContactCompanyName(),
+                                'phoneNumber' => $request->getRecipientContactPhoneNumber()
+                            ],
+                            'address' => [
+                                'streetLines' => [$request->getRecipientAddressStreet()],
+                                'city' => $request->getRecipientAddressCity(),
+                                'stateOrProvinceCode' => $request->getRecipientAddressStateOrProvinceCode(),
+                                'postalCode' => $request->getRecipientAddressPostalCode(),
+                                'countryCode' => $request->getRecipientAddressCountryCode(),
+                                'residential' => (bool)$this->getConfigData('residence_delivery'),
+                            ]
+                        ],
                     ],
-                    'Address' => [
-                        'StreetLines' => [$request->getRecipientAddressStreet()],
-                        'City' => $request->getRecipientAddressCity(),
-                        'StateOrProvinceCode' => $request->getRecipientAddressStateOrProvinceCode(),
-                        'PostalCode' => $request->getRecipientAddressPostalCode(),
-                        'CountryCode' => $request->getRecipientAddressCountryCode(),
-                        'Residential' => (bool)$this->getConfigData('residence_delivery'),
+                    'shippingChargesPayment' => [
+                        'paymentType' => $paymentType,
+                        'payor' => [
+                            'responsibleParty' => [
+                                'accountNumber' => ['value' => $this->getConfigData('account')]
+                            ],
+                        ],
                     ],
-                ],
-                'ShippingChargesPayment' => [
-                    'PaymentType' => $paymentType,
-                    'Payor' => [
-                        'AccountNumber' => $this->getConfigData('account'),
-                        'CountryCode' => $this->_scopeConfig->getValue(
-                            \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID,
-                            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
-                            $request->getStoreId()
-                        ),
+                    'labelSpecification' => [
+                        'labelFormatType' => 'COMMON2D',
+                        'imageType' => 'PNG',
+                        'labelStockType' => 'PAPER_85X11_TOP_HALF_LABEL',
                     ],
+                    'rateRequestType' => ['ACCOUNT'],
+                    'totalPackageCount' => 1
                 ],
-                'LabelSpecification' => [
-                    'LabelFormatType' => 'COMMON2D',
-                    'ImageType' => 'PNG',
-                    'LabelStockType' => 'PAPER_8.5X11_TOP_HALF_LABEL',
-                ],
-                'RateRequestTypes' => ['ACCOUNT'],
-                'PackageCount' => 1,
-                'RequestedPackageLineItems' => [
-                    'SequenceNumber' => '1',
-                    'Weight' => ['Units' => $weightUnits, 'Value' => $request->getPackageWeight()],
-                    'CustomerReferences' => [
-                        'CustomerReferenceType' => 'CUSTOMER_REFERENCE',
-                        'Value' => $referenceData,
+                'labelResponseOptions' => 'LABEL',
+                'accountNumber' => ['value' => $this->getConfigData('account')]
+            ];
+
+            // for international shipping
+            if ($request->getShipperAddressCountryCode() != $request->getRecipientAddressCountryCode()) {
+                $requestClient['requestedShipment']['customsClearanceDetail'] = [
+                    'totalCustomsValue' => ['currency' => $request->getBaseCurrencyCode(), 'amount' => $customsValue],
+                    'dutiesPayment' => [
+                        'paymentType' => $paymentType,
+                        'payor' => [
+                            'responsibleParty' => [
+                                'accountNumber' => ['value' => $this->getConfigData('account')],
+                                'address' => ['countryCode' => $this->_scopeConfig->getValue(
+                                    \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID,
+                                    \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                                    $request->getStoreId()
+                                )],
+                            ],
+                        ],
                     ],
-                    'SpecialServicesRequested' => [
-                        'SpecialServiceTypes' => 'SIGNATURE_OPTION',
-                        'SignatureOptionDetail' => ['OptionType' => $optionType],
+                    'commodities' => [
+                            [
+                                'weight' => ['units' => $weightUnits, 'value' => $request->getPackageWeight()],
+                                'numberOfPieces' => 1,
+                                'countryOfManufacture' => implode(',', array_unique($countriesOfManufacture)),
+                                'description' => implode(', ', $itemsDesc),
+                                'quantity' => ceil($itemsQty),
+                                'quantityUnits' => 'pcs',
+                                'unitPrice' => ['currency' => $request->getBaseCurrencyCode(), 'amount' => $unitPrice],
+                                'customsValue' => ['currency' => $request->getBaseCurrencyCode(),
+                                                        'amount' => $customsValue],
+                            ],
                     ],
+                ];
+            }
+
+            if ($request->getMasterTrackingId()) {
+                $requestClient['requestedShipment']['masterTrackingId']['trackingNumber'] =
+                    $request->getMasterTrackingId();
+            }
+
+            if ($request->getShippingMethod() == self::RATE_REQUEST_SMARTPOST) {
+                $requestClient['requestedShipment']['smartPostInfoDetail'] = [
+                    'indicia' => (double)$request->getPackageWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
+                    'hubId' => $this->getConfigData('smartpost_hubid'),
+                ];
+            }
+
+            $requestedPackageLineItems = [
+                'sequenceNumber' => '1',
+                'weight' => ['units' => $weightUnits, 'value' => $request->getPackageWeight()],
+                'customerReferences' => [
+                    [
+                        'customerReferenceType' => 'CUSTOMER_REFERENCE',
+                        'value' => $referenceData,
+                    ]
                 ],
-            ],
-        ];
+                'packageSpecialServices' => [
+                    'specialServiceTypes' => ['SIGNATURE_OPTION'],
+                    'signatureOptionType' => $optionType
+
+                ]
+            ];
 
-        // for international shipping
-        if ($request->getShipperAddressCountryCode() != $request->getRecipientAddressCountryCode()) {
-            $requestClient['RequestedShipment']['CustomsClearanceDetail'] = [
-                'CustomsValue' => ['Currency' => $request->getBaseCurrencyCode(), 'Amount' => $customsValue],
-                'DutiesPayment' => [
-                    'PaymentType' => $paymentType,
-                    'Payor' => [
-                        'AccountNumber' => $this->getConfigData('account'),
-                        'CountryCode' => $this->_scopeConfig->getValue(
-                            \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID,
-                            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
-                            $request->getStoreId()
-                        ),
+            // set dimensions
+            if ($length || $width || $height) {
+                $requestedPackageLineItems['dimensions'] = [
+                    'length' => $length,
+                    'width' => $width,
+                    'height' => $height,
+                    'units' => $packageParams->getDimensionUnits() == Length::INCH ? 'IN' : 'CM',
+                ];
+            }
+
+            $requestClient['requestedShipment']['requestedPackageLineItems'] = [$requestedPackageLineItems];
+            return $requestClient;
+        } else {
+            $requestClient = [
+                'RequestedShipment' => [
+                    'ShipTimestamp' => time(),
+                    'DropoffType' => $this->getConfigData('dropoff'),
+                    'PackagingType' => $request->getPackagingType(),
+                    'ServiceType' => $request->getShippingMethod(),
+                    'Shipper' => [
+                        'Contact' => [
+                            'PersonName' => $request->getShipperContactPersonName(),
+                            'CompanyName' => $request->getShipperContactCompanyName(),
+                            'PhoneNumber' => $request->getShipperContactPhoneNumber(),
+                        ],
+                        'Address' => [
+                            'StreetLines' => [$request->getShipperAddressStreet()],
+                            'City' => $request->getShipperAddressCity(),
+                            'StateOrProvinceCode' => $request->getShipperAddressStateOrProvinceCode(),
+                            'PostalCode' => $request->getShipperAddressPostalCode(),
+                            'CountryCode' => $request->getShipperAddressCountryCode(),
+                        ],
+                    ],
+                    'Recipient' => [
+                        'Contact' => [
+                            'PersonName' => $request->getRecipientContactPersonName(),
+                            'CompanyName' => $request->getRecipientContactCompanyName(),
+                            'PhoneNumber' => $request->getRecipientContactPhoneNumber(),
+                        ],
+                        'Address' => [
+                            'StreetLines' => [$request->getRecipientAddressStreet()],
+                            'City' => $request->getRecipientAddressCity(),
+                            'StateOrProvinceCode' => $request->getRecipientAddressStateOrProvinceCode(),
+                            'PostalCode' => $request->getRecipientAddressPostalCode(),
+                            'CountryCode' => $request->getRecipientAddressCountryCode(),
+                            'Residential' => (bool)$this->getConfigData('residence_delivery'),
+                        ],
+                    ],
+                    'ShippingChargesPayment' => [
+                        'PaymentType' => $paymentType,
+                        'Payor' => [
+                            'AccountNumber' => $this->getConfigData('account'),
+                            'CountryCode' => $this->_scopeConfig->getValue(
+                                \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID,
+                                \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                                $request->getStoreId()
+                            ),
+                        ],
+                    ],
+                    'LabelSpecification' => [
+                        'LabelFormatType' => 'COMMON2D',
+                        'ImageType' => 'PNG',
+                        'LabelStockType' => 'PAPER_8.5X11_TOP_HALF_LABEL',
+                    ],
+                    'RateRequestTypes' => ['ACCOUNT'],
+                    'PackageCount' => 1,
+                    'RequestedPackageLineItems' => [
+                        'SequenceNumber' => '1',
+                        'Weight' => ['Units' => $weightUnits, 'Value' => $request->getPackageWeight()],
+                        'CustomerReferences' => [
+                            'CustomerReferenceType' => 'CUSTOMER_REFERENCE',
+                            'Value' => $referenceData,
+                        ],
+                        'SpecialServicesRequested' => [
+                            'SpecialServiceTypes' => 'SIGNATURE_OPTION',
+                            'SignatureOptionDetail' => ['OptionType' => $optionType],
+                        ],
                     ],
-                ],
-                'Commodities' => [
-                    'Weight' => ['Units' => $weightUnits, 'Value' => $request->getPackageWeight()],
-                    'NumberOfPieces' => 1,
-                    'CountryOfManufacture' => implode(',', array_unique($countriesOfManufacture)),
-                    'Description' => implode(', ', $itemsDesc),
-                    'Quantity' => ceil($itemsQty),
-                    'QuantityUnits' => 'pcs',
-                    'UnitPrice' => ['Currency' => $request->getBaseCurrencyCode(), 'Amount' => $unitPrice],
-                    'CustomsValue' => ['Currency' => $request->getBaseCurrencyCode(), 'Amount' => $customsValue],
                 ],
             ];
-        }
 
-        if ($request->getMasterTrackingId()) {
-            $requestClient['RequestedShipment']['MasterTrackingId'] = $request->getMasterTrackingId();
-        }
+            // for international shipping
+            if ($request->getShipperAddressCountryCode() != $request->getRecipientAddressCountryCode()) {
+                $requestClient['RequestedShipment']['CustomsClearanceDetail'] = [
+                    'CustomsValue' => ['Currency' => $request->getBaseCurrencyCode(), 'Amount' => $customsValue],
+                    'DutiesPayment' => [
+                        'PaymentType' => $paymentType,
+                        'Payor' => [
+                            'AccountNumber' => $this->getConfigData('account'),
+                            'CountryCode' => $this->_scopeConfig->getValue(
+                                \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID,
+                                \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+                                $request->getStoreId()
+                            ),
+                        ],
+                    ],
+                    'Commodities' => [
+                        'Weight' => ['Units' => $weightUnits, 'Value' => $request->getPackageWeight()],
+                        'NumberOfPieces' => 1,
+                        'CountryOfManufacture' => implode(',', array_unique($countriesOfManufacture)),
+                        'Description' => implode(', ', $itemsDesc),
+                        'Quantity' => ceil($itemsQty),
+                        'QuantityUnits' => 'pcs',
+                        'UnitPrice' => ['Currency' => $request->getBaseCurrencyCode(), 'Amount' => $unitPrice],
+                        'CustomsValue' => ['Currency' => $request->getBaseCurrencyCode(), 'Amount' => $customsValue],
+                    ],
+                ];
+            }
 
-        if ($request->getShippingMethod() == self::RATE_REQUEST_SMARTPOST) {
-            $requestClient['RequestedShipment']['SmartPostDetail'] = [
-                'Indicia' => (double)$request->getPackageWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
-                'HubId' => $this->getConfigData('smartpost_hubid'),
-            ];
-        }
+            if ($request->getMasterTrackingId()) {
+                $requestClient['RequestedShipment']['MasterTrackingId'] = $request->getMasterTrackingId();
+            }
 
-        // set dimensions
-        if ($length || $width || $height) {
-            $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = [
-                'Length' => $length,
-                'Width' => $width,
-                'Height' => $height,
-                'Units' => $packageParams->getDimensionUnits() == Length::INCH ? 'IN' : 'CM',
-            ];
-        }
+            if ($request->getShippingMethod() == self::RATE_REQUEST_SMARTPOST) {
+                $requestClient['RequestedShipment']['SmartPostDetail'] = [
+                    'Indicia' => (double)$request->getPackageWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
+                    'HubId' => $this->getConfigData('smartpost_hubid'),
+                ];
+            }
 
-        return $this->_getAuthDetails() + $requestClient;
+            // set dimensions
+            if ($length || $width || $height) {
+                $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = [
+                    'Length' => $length,
+                    'Width' => $width,
+                    'Height' => $height,
+                    'Units' => $packageParams->getDimensionUnits() == Length::INCH ? 'IN' : 'CM',
+                ];
+            }
+            return $this->_getAuthDetails() + $requestClient;
+        }
     }
 
     /**
-     * Do shipment request to carrier web service, obtain Print Shipping Labels and process errors in response
+     * Do shipment request to carrier's SOAP web service, obtain Print Shipping Labels and process errors in response
      *
      * @param \Magento\Framework\DataObject $request
+     * @param \Magento\Framework\DataObject $result
      * @return \Magento\Framework\DataObject
      */
-    protected function _doShipmentRequest(\Magento\Framework\DataObject $request)
+    protected function _doShipmentRequestSoap($request, $result): \Magento\Framework\DataObject
     {
-        $this->_prepareShipmentRequest($request);
-        $result = new \Magento\Framework\DataObject();
         $client = $this->_createShipSoapClient();
         $requestClient = $this->_formShipmentRequest($request);
         $debugData['request'] = $this->filterDebugData($requestClient);
@@ -1503,7 +2061,77 @@
             $result->setErrors($debugData['result']['error']);
         }
         $result->setGatewayResponse($client->__getLastResponse());
+        return $result;
+    }
+
+    /**
+     * Do shipment request to carrier's REST web service, obtain Print Shipping Labels and process errors in response
+     *
+     * @param \Magento\Framework\DataObject $request
+     * @param \Magento\Framework\DataObject $result
+     * @return \Magento\Framework\DataObject
+     */
+    protected function _doShipmentRequestRest($request, $result): \Magento\Framework\DataObject
+    {
+        $response = null;
+        $accessToken = $this->_getAccessToken();
+        if (empty($accessToken)) {
+            return $result->setErrors(__('Authorization Error. No Access Token found with given credentials.'));
+        }
+
+        $requestClient = $this->_formShipmentRequest($request);
+        $requestString = $this->serializer->serialize($requestClient);
+
+        $debugData = ['request' => $this->filterDebugData($requestClient)];
+
+        $response = $this->sendRequest(self::SHIPMENT_REQUEST_END_POINT, $requestString, $accessToken);
+
+        $debugData['result'] = $response;
+
+        if (!empty($response['output']['transactionShipments'])) {
+            $shippingLabelContent = $this->getPackagingLabel(
+                reset($response['output']['transactionShipments'])['pieceResponses']
+            );
+
+            $trackingNumber = $this->getTrackingNumber(
+                reset($response['output']['transactionShipments'])['pieceResponses']
+            );
+            $result->setShippingLabelContent($this->decoderInterface->decode($shippingLabelContent));
+            $result->setTrackingNumber($trackingNumber);
+        } else {
+            $debugData['result'] = ['error' => '', 'code' => '', 'message' => $response];
+            if (is_array($response['errors'])) {
+                foreach ($response['errors'] as $notification) {
+                    $debugData['result']['code'] .= $notification['code'] . '; ';
+                    $debugData['result']['error'] .= $notification['message'] . '; ';
+                }
+            } else {
+                $debugData['result']['code'] = $response['errors']['code'] . ' ';
+                $debugData['result']['error'] = $response['errors']['message'] . ' ';
+            }
+
+            $result->setErrors($debugData['result']['error']);
+        }
+        $this->_debug($debugData);
+        return $result;
+    }
+
+    /**
+     * Do shipment request to carrier web service, obtain Print Shipping Labels and process errors in response
+     *
+     * @param \Magento\Framework\DataObject $request
+     * @return \Magento\Framework\DataObject
+     */
+    protected function _doShipmentRequest(\Magento\Framework\DataObject $request): \Magento\Framework\DataObject
+    {
+        $this->_prepareShipmentRequest($request);
+        $result = new \Magento\Framework\DataObject();
 
+        if ($this->isRestConfiguration()) {
+            $result = $this->_doShipmentRequestRest($request, $result);
+        } else {
+            $result = $this->_doShipmentRequestSoap($request, $result);
+        }
         return $result;
     }
 
@@ -1515,12 +2143,27 @@
      */
     private function getTrackingNumber($trackingIds)
     {
-        return is_array($trackingIds) ? array_map(
-            function ($val) {
-                return $val->TrackingNumber;
-            },
-            $trackingIds
-        ) : $trackingIds->TrackingNumber;
+        if ($this->isRestConfiguration()) {
+            return reset($trackingIds)['trackingNumber'];
+        } else {
+            return is_array($trackingIds) ? array_map(
+                function ($val) {
+                    return $val->TrackingNumber;
+                },
+                $trackingIds
+            ) : $trackingIds->TrackingNumber;
+        }
+    }
+
+    /**
+     * Return Packaging Label
+     *
+     * @param array|object $pieceResponses
+     * @return string
+     */
+    private function getPackagingLabel($pieceResponses): string
+    {
+        return reset(reset($pieceResponses)['packageDocuments'])['encodedLabel'];
     }
 
     /**
@@ -1532,12 +2175,33 @@
      */
     public function rollBack($data)
     {
-        $requestData = $this->_getAuthDetails();
-        $requestData['DeletionControl'] = 'DELETE_ONE_PACKAGE';
-        foreach ($data as &$item) {
-            $requestData['TrackingId'] = $item['tracking_number'];
-            $client = $this->_createShipSoapClient();
-            $client->deleteShipment($requestData);
+        if ($this->isRestConfiguration()) {
+            $accessToken = $this->_getAccessToken();
+            if (empty($accessToken)) {
+                $this->_debug('Authorization Error. No Access Token found with given credentials.');
+                return false;
+            }
+
+            $requestData['accountNumber'] = ['value' => $this->getConfigData('account')];
+            $requestData['deletionControl'] = 'DELETE_ALL_PACKAGES';
+
+            foreach ($data as &$item) {
+                $requestData['trackingNumber'] = $item['tracking_number'];
+                $requestString = $this->serializer->serialize($requestData);
+
+                $debugData = ['request' => $requestData];
+                $response = $this->sendRequest(self::SHIPMENT_CANCEL_END_POINT, $requestString, $accessToken);
+                $debugData['result'] = $response;
+                $this->_debug($debugData);
+            }
+        } else {
+            $requestData = $this->_getAuthDetails();
+            $requestData['DeletionControl'] = 'DELETE_ONE_PACKAGE';
+            foreach ($data as &$item) {
+                $requestData['TrackingId'] = $item['tracking_number'];
+                $client = $this->_createShipSoapClient();
+                $client->deleteShipment($requestData);
+            }
         }
 
         return true;
@@ -1627,7 +2291,7 @@
     /**
      * Recursive replace sensitive fields in debug data by the mask
      *
-     * @param string $data
+     * @param string|array $data
      * @return string
      */
     protected function filterDebugData($data)
@@ -1714,12 +2378,144 @@
     }
 
     /**
+     * Parse track details response from Fedex for REST API
+     *
+     * @param array $trackInfo
+     * @return array
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    private function processRestTrackingDetails($trackInfo): array
+    {
+        $result = [
+            'shippeddate' => null,
+            'deliverydate' => null,
+            'deliverytime' => null,
+            'deliverylocation' => null,
+            'weight' => null,
+            'progressdetail' => [],
+        ];
+
+        if (!empty($trackInfo['dateAndTimes']) && is_array($trackInfo['dateAndTimes'])) {
+            $datetime = null;
+            foreach ($trackInfo['dateAndTimes'] as $dateAndTimeInfo) {
+                if (!empty($dateAndTimeInfo['type']) && $dateAndTimeInfo['type'] == 'SHIP') {
+                    $datetime = $this->parseDate($dateAndTimeInfo['dateTime']);
+                    break;
+                }
+            }
+
+            if ($datetime) {
+                $result['shippeddate'] = gmdate('Y-m-d', $datetime->getTimestamp());
+            }
+        }
+
+        $result['signedby'] = !empty($trackInfo['deliveryDetails']['receivedByName']) ?
+            (string) $trackInfo['deliveryDetails']['receivedByName'] :
+            null;
+
+        $result['status'] = (!empty($trackInfo['latestStatusDetail']) &&
+            !empty($trackInfo['latestStatusDetail']['description'])) ?
+            (string) $trackInfo['latestStatusDetail']['description'] :
+            null;
+        $result['service'] = (!empty($trackInfo['serviceDetail']) &&
+            !empty($trackInfo['serviceDetail']['description'])) ?
+            (string) $trackInfo['serviceDetail']['description'] :
+            null;
+
+        $datetime = $this->getDeliveryDateTime($trackInfo);
+        if ($datetime) {
+            $result['deliverydate'] = gmdate('Y-m-d', $datetime->getTimestamp());
+            $result['deliverytime'] = gmdate('H:i:s', $datetime->getTimestamp());
+        }
+
+        $address = null;
+        if (!empty($trackInfo['deliveryDetails']['estimatedDeliveryAddress'])) {
+            $address = $trackInfo['deliveryDetails']['estimatedDeliveryAddress'];
+        } elseif (!empty($trackInfo['deliveryDetails']['actualDeliveryAddress'])) {
+            $address = $trackInfo['deliveryDetails']['actualDeliveryAddress'];
+        }
+
+        if (!empty($address)) {
+            $result['deliverylocation'] = $this->getDeliveryAddress($address);
+        }
+
+        if (!empty($trackInfo['packageDetails']['weightAndDimensions']['weight'])) {
+            $weightUnit = $this->getConfigData('unit_of_measure') ?? 'LB';
+            $weightValue = null;
+            foreach ($trackInfo['packageDetails']['weightAndDimensions']['weight'] as $weightInfo) {
+                if ($weightInfo['unit'] == $weightUnit) {
+                    $weightValue = $weightInfo['value'];
+                    break;
+                }
+            }
+
+            $result['weight'] = sprintf(
+                '%s %s',
+                (string) $weightValue,
+                (string) $weightUnit
+            );
+        }
+
+        if (!empty($trackInfo['scanEvents'])) {
+            $events = $trackInfo['scanEvents'];
+            if (is_object($trackInfo['scanEvents'])) {
+                $events = [$trackInfo['scanEvents']];
+            }
+            $result['progressdetail'] = $this->processTrackDetailsEvents($events);
+        }
+
+        return $result;
+    }
+
+    /**
      * Parse delivery datetime from tracking details
      *
-     * @param \stdClass $trackInfo
+     * @param \stdClass|array $trackInfo
+     * @return \Datetime|null
+     */
+    private function getDeliveryDateTime($trackInfo)
+    {
+        $timestamp = null;
+        if ($this->isRestConfiguration() && is_array($trackInfo)) {
+            $timestamp = $this->getDeliveryDateTimeRest($trackInfo);
+            return $timestamp ?: null;
+        } else {
+            $timestamp = $this->getDeliveryDateTimeSoap($trackInfo);
+            return $timestamp ? $this->parseDate($timestamp) : null;
+        }
+    }
+
+    /**
+     * Parse delivery datetime from tracking details for REST API
+     *
+     * @param array $trackInfo
      * @return \Datetime|null
      */
-    private function getDeliveryDateTime(\stdClass $trackInfo)
+    private function getDeliveryDateTimeRest($trackInfo): \Datetime|null
+    {
+        $timestamp = null;
+        if (!empty($trackInfo['dateAndTimes']) && is_array($trackInfo['dateAndTimes'])) {
+            foreach ($trackInfo['dateAndTimes'] as $dateAndTimeInfo) {
+                if (!empty($dateAndTimeInfo['type']) &&
+                    ($dateAndTimeInfo['type'] == 'ESTIMATED_DELIVERY' || $dateAndTimeInfo['type'] == 'ACTUAL_DELIVERY')
+                    && !empty($dateAndTimeInfo['dateTime'])
+                ) {
+                    $timestamp = $this->parseDate($dateAndTimeInfo['dateTime']);
+                    break;
+                }
+            }
+        }
+        return $timestamp ?: null;
+    }
+
+    /**
+     * Parse delivery datetime from tracking details for SOAP API
+     *
+     * @param \stdClass $trackInfo
+     * @return string|null
+     */
+    private function getDeliveryDateTimeSoap($trackInfo): string|null
     {
         $timestamp = null;
         if (!empty($trackInfo->EstimatedDeliveryTimestamp)) {
@@ -1727,30 +2523,42 @@
         } elseif (!empty($trackInfo->ActualDeliveryTimestamp)) {
             $timestamp = $trackInfo->ActualDeliveryTimestamp;
         }
-
-        return $timestamp ? $this->parseDate($timestamp) : null;
+        return $timestamp;
     }
 
     /**
      * Get delivery address details in string representation Return City, State, Country Code
      *
-     * @param \stdClass $address
+     * @param \stdClass|array $address
      * @return \Magento\Framework\Phrase|string
      */
-    private function getDeliveryAddress(\stdClass $address)
+    private function getDeliveryAddress($address): \Magento\Framework\Phrase|string
     {
         $details = [];
+        if ($this->isRestConfiguration() && is_array($address)) {
+            if (!empty($address['city'])) {
+                $details[] = (string) $address['city'];
+            }
 
-        if (!empty($address->City)) {
-            $details[] = (string) $address->City;
-        }
+            if (!empty($address['stateOrProvinceCode'])) {
+                $details[] = (string) $address['stateOrProvinceCode'];
+            }
 
-        if (!empty($address->StateOrProvinceCode)) {
-            $details[] = (string) $address->StateOrProvinceCode;
-        }
+            if (!empty($address['countryCode'])) {
+                $details[] = (string) $address['countryCode'];
+            }
+        } else {
+            if (!empty($address->City)) {
+                $details[] = (string) $address->City;
+            }
+
+            if (!empty($address->StateOrProvinceCode)) {
+                $details[] = (string) $address->StateOrProvinceCode;
+            }
 
-        if (!empty($address->CountryCode)) {
-            $details[] = (string) $address->CountryCode;
+            if (!empty($address->CountryCode)) {
+                $details[] = (string) $address->CountryCode;
+            }
         }
 
         return implode(', ', $details);
@@ -1764,13 +2572,62 @@
      * @param array $events
      * @return array
      */
-    private function processTrackDetailsEvents(array $events)
+    private function processTrackDetailsEvents(array $events): array
+    {
+        if ($this->isRestConfiguration()) {
+            return $this->processTrackDetailsEventsRest($events);
+        } else {
+            return $this->processTrackDetailsEventsSoap($events);
+        }
+    }
+
+    /**
+     * Parse tracking details events from response
+     * Return list of items in such format:
+     * ['activity', 'deliverydate', 'deliverytime', 'deliverylocation']
+     *
+     * @param array $events
+     * @return array
+     */
+    private function processTrackDetailsEventsRest(array $events): array
     {
         $result = [];
-        /** @var \stdClass $event */
         foreach ($events as $event) {
             $item = [
-                'activity' => (string) $event->EventDescription,
+                'activity' => (string) $event['eventDescription'],
+                'deliverydate' => null,
+                'deliverytime' => null,
+                'deliverylocation' => null
+            ];
+
+            $datetime = $this->parseDate(!empty($event['date']) ? $event['date'] : null);
+            if ($datetime) {
+                $item['deliverydate'] = gmdate('Y-m-d', $datetime->getTimestamp());
+                $item['deliverytime'] = gmdate('H:i:s', $datetime->getTimestamp());
+            }
+
+            if (!empty($event['scanLocation'])) {
+                $item['deliverylocation'] = $this->getDeliveryAddress($event['scanLocation']);
+            }
+            $result[] = $item;
+        }
+        return $result;
+    }
+
+    /**
+     * Parse tracking details events from response
+     * Return list of items in such format:
+     * ['activity', 'deliverydate', 'deliverytime', 'deliverylocation']
+     *
+     * @param array $events
+     * @return array
+     */
+    private function processTrackDetailsEventsSoap(array $events): array
+    {
+        $result = [];
+        foreach ($events as $event) {
+            $item = [
+                'activity' => (string)$event->EventDescription,
                 'deliverydate' => null,
                 'deliverytime' => null,
                 'deliverylocation' => null
@@ -1788,7 +2645,6 @@
 
             $result[] = $item;
         }
-
         return $result;
     }
 
@@ -1849,6 +2705,20 @@
     }
 
     /**
+     * Check if Admin configuration is REST or SOAP
+     *
+     * @return bool
+     */
+    private function isRestConfiguration(): bool
+    {
+        $serviceType = $this->getConfigData('web_service_type');
+        if ($serviceType && $serviceType == self::WEB_SERVICE_REST) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Creates packages for rate request.
      *
      * @param float $totalWeight
diff -ruN vendor/magento/module-fedex/Model/Config/Backend/FedexUrl.php vendor/magento/module-fedex/Model/Config/Backend/FedexUrl.php
--- a/vendor/magento/module-fedex/Model/Config/Backend/FedexUrl.php	1970-01-01 05:30:00.000000000 +0530
+++ b/vendor/magento/module-fedex/Model/Config/Backend/FedexUrl.php	2025-11-03 20:44:00.294318600 +0530
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+declare(strict_types=1);
+
+namespace Magento\Fedex\Model\Config\Backend;
+
+use Magento\Framework\App\Cache\TypeListInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\Config\Value;
+use Magento\Framework\Data\Collection\AbstractDb;
+use Magento\Framework\Exception\ValidatorException;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Framework\Model\Context;
+use Magento\Framework\Model\ResourceModel\AbstractResource;
+use Magento\Framework\Registry;
+use Magento\Framework\Validator\Url;
+
+/**
+ * Represents a config URL that may point to a Fedex endpoint
+ */
+class FedexUrl extends Value
+{
+    /**
+     * @var Url
+     */
+    private Url $url;
+    /**
+     * @param Context $context
+     * @param Registry $registry
+     * @param ScopeConfigInterface $config
+     * @param TypeListInterface $cacheTypeList
+     * @param AbstractResource|null $resource
+     * @param AbstractDb|null $resourceCollection
+     * @param Url $url
+     * @param array $data
+     */
+    public function __construct(
+        Context $context,
+        Registry $registry,
+        ScopeConfigInterface $config,
+        TypeListInterface $cacheTypeList,
+        AbstractResource $resource = null,
+        AbstractDb $resourceCollection = null,
+        Url $url,
+        array $data = []
+    ) {
+        parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
+        $this->url = $url;
+    }
+
+    /**
+     * @inheritDoc
+     *
+     * @return AbstractModel
+     * @throws ValidatorException
+     */
+    public function beforeSave(): AbstractModel
+    {
+        $isValid = $this->url->isValid($this->getValue(), ['http', 'https']);
+
+        if ($isValid) {
+            // phpcs:ignore Magento2.Functions.DiscouragedFunction
+            $host = parse_url((string)$this->getValue(), \PHP_URL_HOST);
+
+            if (!empty($host) && !preg_match('/(?:.+\.|^)fedex\.com$/i', $host)) {
+                throw new ValidatorException(__('Fedex API endpoint URL\'s must use fedex.com'));
+            }
+        }
+
+        return parent::beforeSave();
+    }
+}
diff -ruN vendor/magento/module-fedex/Model/Source/PickupType.php vendor/magento/module-fedex/Model/Source/PickupType.php
--- a/vendor/magento/module-fedex/Model/Source/PickupType.php	1970-01-01 05:30:00.000000000 +0530
+++ b/vendor/magento/module-fedex/Model/Source/PickupType.php	2025-11-03 20:44:03.755664800 +0530
@@ -0,0 +1,36 @@
+<?php
+/************************************************************************
+ *
+ * ADOBE CONFIDENTIAL
+ * ___________________
+ *
+ * Copyright 2023 Adobe
+ * All Rights Reserved.
+ *
+ * NOTICE: All information contained herein is, and remains
+ * the property of Adobe and its suppliers, if any. The intellectual
+ * and technical concepts contained herein are proprietary to Adobe
+ * and its suppliers and are protected by all applicable intellectual
+ * property laws, including trade secret and copyright laws.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Adobe.
+ * ************************************************************************
+ */
+
+declare(strict_types=1);
+
+namespace Magento\Fedex\Model\Source;
+
+/**
+ * Fedex pickupType source implementation
+ */
+class PickupType extends \Magento\Fedex\Model\Source\Generic
+{
+    /**
+     * Carrier code
+     *
+     * @var string
+     */
+    protected $_code = 'pickup_type';
+}
diff -ruN vendor/magento/module-fedex/Model/Source/WebServiceType.php vendor/magento/module-fedex/Model/Source/WebServiceType.php
--- a/vendor/magento/module-fedex/Model/Source/WebServiceType.php	1970-01-01 05:30:00.000000000 +0530
+++ b/vendor/magento/module-fedex/Model/Source/WebServiceType.php	2025-11-03 20:44:05.164576500 +0530
@@ -0,0 +1,33 @@
+<?php
+/************************************************************************
+ *
+ * ADOBE CONFIDENTIAL
+ * ___________________
+ *
+ * Copyright 2023 Adobe
+ * All Rights Reserved.
+ *
+ * NOTICE: All information contained herein is, and remains
+ * the property of Adobe and its suppliers, if any. The intellectual
+ * and technical concepts contained herein are proprietary to Adobe
+ * and its suppliers and are protected by all applicable intellectual
+ * property laws, including trade secret and copyright laws.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Adobe.
+ * ************************************************************************
+ */
+
+declare(strict_types=1);
+
+namespace Magento\Fedex\Model\Source;
+
+class WebServiceType extends \Magento\Fedex\Model\Source\Generic
+{
+    /**
+     * Carrier code
+     *
+     * @var string
+     */
+    protected $_code = 'web_service_type';
+}
diff -ruN vendor/magento/module-fedex/Test/Unit/Model/CarrierTest.php vendor/magento/module-fedex/Test/Unit/Model/CarrierTest.php
--- a/vendor/magento/module-fedex/Test/Unit/Model/CarrierTest.php	2025-11-03 19:36:03.819611388 +0530
+++ b/vendor/magento/module-fedex/Test/Unit/Model/CarrierTest.php	2025-11-03 20:44:45.889593600 +0530
@@ -18,10 +18,13 @@
 use Magento\Fedex\Model\Carrier;
 use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\HTTP\Client\Curl;
+use Magento\Framework\HTTP\Client\CurlFactory;
 use Magento\Framework\Module\Dir\Reader;
 use Magento\Framework\Pricing\PriceCurrencyInterface;
 use Magento\Framework\Serialize\Serializer\Json;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Framework\Url\DecoderInterface;
 use Magento\Framework\Xml\Security;
 use Magento\Quote\Model\Quote\Address\RateRequest;
 use Magento\Quote\Model\Quote\Address\RateResult\Error as RateResultError;
@@ -110,6 +113,24 @@
      */
     private $currencyFactory;
 
+    /**
+     * @var CurlFactory
+     */
+    private $curlFactory;
+
+    /**
+     * @var Curl
+     */
+    private $curlClient;
+
+    /**
+     * @var DecoderInterface
+     */
+    private $decoderInterface;
+
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
     protected function setUp(): void
     {
         $this->helper = new ObjectManager($this);
@@ -117,10 +138,6 @@
             ->disableOriginalConstructor()
             ->getMockForAbstractClass();
 
-        $this->scope->expects($this->any())
-            ->method('getValue')
-            ->willReturnCallback([$this, 'scopeConfigGetValue']);
-
         $countryFactory = $this->getCountryFactory();
         $rateFactory = $this->getRateFactory();
         $storeManager = $this->getStoreManager();
@@ -171,6 +188,21 @@
             ->disableOriginalConstructor()
             ->getMock();
 
+        $this->curlFactory = $this->getMockBuilder(CurlFactory::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->curlClient = $this->getMockBuilder(Curl::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['setHeaders', 'getBody', 'post'])
+            ->getMock();
+
+        $this->decoderInterface = $this->getMockBuilder(DecoderInterface::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['decode'])
+            ->getMock();
+
         $this->logger = $this->getMockForAbstractClass(LoggerInterface::class);
 
         $this->carrier = $this->getMockBuilder(Carrier::class)
@@ -195,6 +227,8 @@
                     'storeManager' => $storeManager,
                     'configReader' => $reader,
                     'productCollectionFactory' => $collectionFactory,
+                    'curlFactory' => $this->curlFactory,
+                    'decoderInterface' => $this->decoderInterface,
                     'data' => [],
                     'serializer' => $this->serializer,
                 ]
@@ -237,17 +271,365 @@
      * @param string $path
      * @return string|null
      */
-    public function scopeConfigGetValue(string $path)
+    public function scopeConfigGetValueRest(string $path)
     {
         $pathMap = [
             'carriers/fedex/showmethod' => 1,
             'carriers/fedex/allowed_methods' => 'ServiceType',
             'carriers/fedex/debug' => 1,
+            'carriers/fedex/web_service_type' => 'REST',
+            'carriers/fedex/api_key' => 'TestApiKey',
+            'carriers/fedex/secret_key' => 'TestSecretKey',
+            'carriers/fedex/rest_sandbox_webservices_url' => 'https://rest.sandbox.url/',
+            'carriers/fedex/rest_production_webservices_url' => 'https://rest.production.url/'
         ];
 
         return isset($pathMap[$path]) ? $pathMap[$path] : null;
     }
 
+     /**
+      * Callback function, emulates getValue function.
+      *
+      * @param string $path
+      * @return string|null
+      */
+    public function scopeConfigGetValueSoap(string $path)
+    {
+        $pathMap = [
+            'carriers/fedex/showmethod' => 1,
+            'carriers/fedex/allowed_methods' => 'ServiceType',
+            'carriers/fedex/debug' => 1
+        ];
+
+        return isset($pathMap[$path]) ? $pathMap[$path] : null;
+    }
+
+    /**
+     * get Access Token for Rest API
+     */
+    public function getAccessToken() : array
+    {
+        $accessTokenResponse = [
+            'access_token' => 'TestAccessToken',
+            'token_type'=>'bearer',
+            'expires_in' => 3600,
+            'scope'=>'CXS'
+        ];
+
+        $this->curlFactory->expects($this->any())->method('create')->willReturn($this->curlClient);
+        $this->curlClient->expects($this->any())->method('setHeaders')->willReturnSelf();
+        $this->curlClient->expects($this->any())->method('post')->willReturnSelf();
+        $this->curlClient->expects($this->any())->method('getBody')->willReturn(json_encode($accessTokenResponse));
+        return $accessTokenResponse;
+    }
+
+    /**
+     * Expected Rate Response
+     *
+     * @param string $amount
+     * @param string $currencyCode
+     * @param string $rateType
+     * @return array
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    public function getRateResponse($amount, $currencyCode, $rateType) : array
+    {
+        $rateResponse = '{"transactionId":"9eb0f436-8bb1-4200-b951-ae10442489f3","output":{"alerts":[{"code":
+        "ORIGIN.STATEORPROVINCECODE.CHANGED","message":"The origin state/province code has been changed.",
+        "alertType":"NOTE"},{"code":"DESTINATION.STATEORPROVINCECODE.CHANGED","message":
+        "The destination state/province code has been changed.","alertType":"NOTE"}],"rateReplyDetails":
+        [{"serviceType":"FIRST_OVERNIGHT","serviceName":"FedEx First Overnight®","packagingType":"YOUR_PACKAGING",
+        "ratedShipmentDetails":[{"rateType":"LIST","ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,"totalBaseCharge"
+        :276.19,"totalNetCharge":290.0,"totalNetFedExCharge":290.0,"shipmentRateDetail":{"rateZone":"05",
+        "dimDivisor":0,"fuelSurchargePercent":5.0,"totalSurcharges":13.81,"totalFreightDiscount":0.0,"surCharges":
+        [{"type":"FUEL","description":"Fuel Surcharge","amount":13.81}],"pricingCode":"PACKAGE","totalBillingWeight":
+        {"units":"KG","value":10.0},"currency":"USD","rateScale":"12"},"ratedPackages":[{"groupNumber":0,
+        "effectiveNetDiscount":0.0,"packageRateDetail":{"rateType":"PAYOR_LIST_PACKAGE","ratedWeightMethod":"ACTUAL",
+        "baseCharge":276.19,"netFreight":276.19,"totalSurcharges":13.81,"netFedExCharge":290.0,"totalTaxes":0.0,
+        "netCharge":290.0,"totalRebates":0.0,"billingWeight":{"units":"KG","value":10.0},"totalFreightDiscounts":0.0,
+        "surcharges":[{"type":"FUEL","description":"Fuel Surcharge","amount":13.81}],"currency":"USD"}}],
+        "currency":"USD"}],"operationalDetail":{"ineligibleForMoneyBackGuarantee":false,"astraDescription":"1ST OVR",
+        "airportId":"ELP","serviceCode":"06"},"signatureOptionType":"SERVICE_DEFAULT","serviceDescription":
+        {"serviceId":"EP1000000006","serviceType":"FIRST_OVERNIGHT","code":"06","names":[{"type":"long",
+        "encoding":"utf-8","value":"FedEx First Overnight®"},{"type":"long","encoding":"ascii","value":
+        "FedEx First Overnight"},{"type":"medium","encoding":"utf-8","value":"FedEx First Overnight®"},
+        {"type":"medium","encoding":"ascii","value":"FedEx First Overnight"},{"type":"short","encoding":
+        "utf-8","value":"FO"},{"type":"short","encoding":"ascii","value":"FO"},{"type":"abbrv","encoding":"ascii",
+        "value":"FO"}],"serviceCategory":"parcel","description":"First Overnight","astraDescription":"1ST OVR"}},
+        {"serviceType":"PRIORITY_OVERNIGHT","serviceName":"FedEx Priority Overnight®","packagingType":"YOUR_PACKAGING",
+        "ratedShipmentDetails":[{"rateType":"LIST","ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,
+        "totalBaseCharge":245.19,"totalNetCharge":257.45,"totalNetFedExCharge":257.45,"shipmentRateDetail":
+        {"rateZone":"05","dimDivisor":0,"fuelSurchargePercent":5.0,"totalSurcharges":12.26,"totalFreightDiscount":0.0,
+        "surCharges":[{"type":"FUEL","description":"Fuel Surcharge","amount":12.26}],"pricingCode":"PACKAGE",
+        "totalBillingWeight":{"units":"KG","value":10.0},"currency":"USD","rateScale":"1552"},"ratedPackages":
+        [{"groupNumber":0,"effectiveNetDiscount":0.0,"packageRateDetail":{"rateType":"PAYOR_LIST_PACKAGE",
+        "ratedWeightMethod":"ACTUAL","baseCharge":245.19,"netFreight":245.19,"totalSurcharges":12.26,"netFedExCharge":
+        257.45,"totalTaxes":0.0,"netCharge":257.45,"totalRebates":0.0,"billingWeight":{"units":"KG","value":10.0},
+        "totalFreightDiscounts":0.0,"surcharges":[{"type":"FUEL","description":"Fuel Surcharge","amount":12.26}],
+        "currency":"USD"}}],"currency":"USD"}],"operationalDetail":{"ineligibleForMoneyBackGuarantee":false,
+        "astraDescription":"P1","airportId":"ELP","serviceCode":"01"},"signatureOptionType":"SERVICE_DEFAULT",
+        "serviceDescription":{"serviceId":"EP1000000002","serviceType":"PRIORITY_OVERNIGHT","code":"01",
+        "names":[{"type":"long","encoding":"utf-8","value":"FedEx Priority Overnight®"},{"type":"long",
+        "encoding":"ascii","value":"FedEx Priority Overnight"},{"type":"medium","encoding":"utf-8","value":
+        "FedEx Priority Overnight®"},{"type":"medium","encoding":"ascii","value":"FedEx Priority Overnight"},
+        {"type":"short","encoding":"utf-8","value":"P-1"},{"type":"short","encoding":"ascii","value":"P-1"},
+        {"type":"abbrv","encoding":"ascii","value":"PO"}],"serviceCategory":"parcel","description":
+        "Priority Overnight","astraDescription":"P1"}},{"serviceType":"STANDARD_OVERNIGHT","serviceName":
+        "FedEx Standard Overnight®","packagingType":"YOUR_PACKAGING","ratedShipmentDetails":[{"rateType":"LIST",
+        "ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,"totalBaseCharge":235.26,"totalNetCharge":247.02,
+        "totalNetFedExCharge":247.02,"shipmentRateDetail":{"rateZone":"05","dimDivisor":0,"fuelSurchargePercent":5.0,
+        "totalSurcharges":11.76,"totalFreightDiscount":0.0,"surCharges":[{"type":"FUEL","description":"Fuel Surcharge",
+        "amount":11.76}],"pricingCode":"PACKAGE","totalBillingWeight":{"units":"KG","value":10.0},"currency":"USD",
+        "rateScale":"1349"},"ratedPackages":[{"groupNumber":0,"effectiveNetDiscount":0.0,"packageRateDetail":
+        {"rateType":"PAYOR_LIST_PACKAGE","ratedWeightMethod":"ACTUAL","baseCharge":235.26,"netFreight":235.26,
+        "totalSurcharges":11.76,"netFedExCharge":247.02,"totalTaxes":0.0,"netCharge":247.02,"totalRebates":0.0,
+        "billingWeight":{"units":"KG","value":10.0},"totalFreightDiscounts":0.0,"surcharges":[{"type":"FUEL",
+        "description":"Fuel Surcharge","amount":11.76}],"currency":"USD"}}],"currency":"USD"}],"operationalDetail":
+        {"ineligibleForMoneyBackGuarantee":false,"astraDescription":"STD OVR","airportId":"ELP","serviceCode":"05"},
+        "signatureOptionType":"SERVICE_DEFAULT","serviceDescription":{"serviceId":"EP1000000005","serviceType":
+        "STANDARD_OVERNIGHT","code":"05","names":[{"type":"long","encoding":"utf-8","value":"FedEx Standard Overnight®"}
+        ,{"type":"long","encoding":"ascii","value":"FedEx Standard Overnight"},{"type":"medium","encoding":"utf-8",
+        "value":"FedEx Standard Overnight®"},{"type":"medium","encoding":"ascii","value":"FedEx Standard Overnight"},
+        {"type":"short","encoding":"utf-8","value":"SOS"},{"type":"short","encoding":"ascii","value":"SOS"},{"type":
+        "abbrv","encoding":"ascii","value":"SO"}],"serviceCategory":"parcel","description":"Standard Overnight",
+        "astraDescription":"STD OVR"}},{"serviceType":"FEDEX_2_DAY_AM","serviceName":"FedEx 2Day® AM","packagingType":
+        "YOUR_PACKAGING","ratedShipmentDetails":[{"rateType":"LIST","ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,
+        "totalBaseCharge":142.78,"totalNetCharge":149.92,"totalNetFedExCharge":149.92,"shipmentRateDetail":{"rateZone":
+        "05","dimDivisor":0,"fuelSurchargePercent":5.0,"totalSurcharges":7.14,"totalFreightDiscount":0.0,"surCharges":
+        [{"type":"FUEL","description":"Fuel Surcharge","amount":7.14}],"pricingCode":"PACKAGE","totalBillingWeight":
+        {"units":"KG","value":10.0},"currency":"USD","rateScale":"10"},"ratedPackages":[{"groupNumber":0,
+        "effectiveNetDiscount":0.0,"packageRateDetail":{"rateType":"PAYOR_LIST_PACKAGE","ratedWeightMethod":"ACTUAL",
+        "baseCharge":142.78,"netFreight":142.78,"totalSurcharges":7.14,"netFedExCharge":149.92,"totalTaxes":0.0,
+        "netCharge":149.92,"totalRebates":0.0,"billingWeight":{"units":"KG","value":10.0},"totalFreightDiscounts":0.0,
+        "surcharges":[{"type":"FUEL","description":"Fuel Surcharge","amount":7.14}],"currency":"USD"}}],"currency":
+        "USD"}],"operationalDetail":{"ineligibleForMoneyBackGuarantee":false,"astraDescription":"2DAY AM","airportId":
+        "ELP","serviceCode":"49"},"signatureOptionType":"SERVICE_DEFAULT","serviceDescription":{"serviceId":
+        "EP1000000023","serviceType":"FEDEX_2_DAY_AM","code":"49","names":[{"type":"long","encoding":"utf-8","value":
+        "FedEx 2Day® AM"},{"type":"long","encoding":"ascii","value":"FedEx 2Day AM"},{"type":"medium","encoding":
+        "utf-8","value":"FedEx 2Day® AM"},{"type":"medium","encoding":"ascii","value":"FedEx 2Day AM"},{"type":"short",
+        "encoding":"utf-8","value":"E2AM"},{"type":"short","encoding":"ascii","value":"E2AM"},{"type":"abbrv",
+        "encoding":"ascii","value":"TA"}],"serviceCategory":"parcel","description":"2DAY AM","astraDescription":
+        "2DAY AM"}},{"serviceType":"FEDEX_2_DAY","serviceName":"FedEx 2Day®","packagingType":"YOUR_PACKAGING",
+        "ratedShipmentDetails":[{"rateType":"LIST","ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,"totalBaseCharge":
+        116.68,"totalNetCharge":122.51,"totalNetFedExCharge":122.51,"shipmentRateDetail":{"rateZone":"05","dimDivisor":
+        0,"fuelSurchargePercent":5.0,"totalSurcharges":5.83,"totalFreightDiscount":0.0,"surCharges":[{"type":"FUEL",
+        "description":"Fuel Surcharge","amount":5.83}],"pricingCode":"PACKAGE","totalBillingWeight":{"units":"KG",
+        "value":10.0},"currency":"USD","rateScale":"6046"},"ratedPackages":[{"groupNumber":0,"effectiveNetDiscount":0.0,
+        "packageRateDetail":{"rateType":"PAYOR_LIST_PACKAGE","ratedWeightMethod":"ACTUAL","baseCharge":116.68,
+        "netFreight":116.68,"totalSurcharges":5.83,"netFedExCharge":122.51,"totalTaxes":0.0,"netCharge":122.51,
+        "totalRebates":0.0,"billingWeight":{"units":"KG","value":10.0},"totalFreightDiscounts":0.0,"surcharges":
+        [{"type":"FUEL","description":"Fuel Surcharge","amount":5.83}],"currency":"USD"}}],"currency":"USD"}],
+        "operationalDetail":{"ineligibleForMoneyBackGuarantee":false,"astraDescription":"E2","airportId":"ELP",
+        "serviceCode":"03"},"signatureOptionType":"SERVICE_DEFAULT","serviceDescription":{"serviceId":"EP1000000003",
+        "serviceType":"FEDEX_2_DAY","code":"03","names":[{"type":"long","encoding":"utf-8","value":"FedEx 2Day®"},
+        {"type":"long","encoding":"ascii","value":"FedEx 2Day"},{"type":"medium","encoding":"utf-8","value":
+        "FedEx 2Day®"},{"type":"medium","encoding":"ascii","value":"FedEx 2Day"},{"type":"short","encoding":"utf-8",
+        "value":"P-2"},{"type":"short","encoding":"ascii","value":"P-2"},{"type":"abbrv","encoding":"ascii","value":
+        "ES"}],"serviceCategory":"parcel","description":"2Day","astraDescription":"E2"}},{"serviceType":
+        "FEDEX_EXPRESS_SAVER","serviceName":"FedEx Express Saver®","packagingType":"YOUR_PACKAGING",
+        "ratedShipmentDetails":[{"rateType":"LIST","ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,"totalBaseCharge"
+        :90.25,"totalNetCharge":94.76,"totalNetFedExCharge":94.76,"shipmentRateDetail":{"rateZone":"05","dimDivisor":0,
+        "fuelSurchargePercent":5.0,"totalSurcharges":4.51,"totalFreightDiscount":0.0,"surCharges":[{"type":"FUEL",
+        "description":"Fuel Surcharge","amount":4.51}],"pricingCode":"PACKAGE","totalBillingWeight":{"units":"KG",
+        "value":10.0},"currency":"USD","rateScale":"7173"},"ratedPackages":[{"groupNumber":0,"effectiveNetDiscount":0.0,
+        "packageRateDetail":{"rateType":"PAYOR_LIST_PACKAGE","ratedWeightMethod":"ACTUAL","baseCharge":90.25,
+        "netFreight":90.25,"totalSurcharges":4.51,"netFedExCharge":94.76,"totalTaxes":0.0,"netCharge":94.76,
+        "totalRebates":0.0,"billingWeight":{"units":"KG","value":10.0},"totalFreightDiscounts":0.0,"surcharges":
+        [{"type":"FUEL","description":"Fuel Surcharge","amount":4.51}],"currency":"USD"}}],"currency":"USD"}],
+        "operationalDetail":{"ineligibleForMoneyBackGuarantee":false,"astraDescription":"XS","airportId":"ELP",
+        "serviceCode":"20"},"signatureOptionType":"SERVICE_DEFAULT","serviceDescription":{"serviceId":"EP1000000013",
+        "serviceType":"FEDEX_EXPRESS_SAVER","code":"20","names":[{"type":"long","encoding":"utf-8","value":
+        "FedEx Express Saver®"},{"type":"long","encoding":"ascii","value":"FedEx Express Saver"},{"type":"medium",
+        "encoding":"utf-8","value":"FedEx Express Saver®"},{"type":"medium","encoding":"ascii","value":
+        "FedEx Express Saver"}],"serviceCategory":"parcel","description":"Express Saver","astraDescription":"XS"}},
+        {"serviceType":"ServiceType","serviceName":"FedEx Ground®","packagingType":"YOUR_PACKAGING",
+        "ratedShipmentDetails":[{"rateType":"LIST","ratedWeightMethod":"ACTUAL","totalDiscounts":0.0,"totalBaseCharge":
+        24.26,"totalNetCharge":'.$amount.',"totalNetFedExCharge":28.75,"shipmentRateDetail":{"rateZone":"5","dimDivisor"
+        :0,"fuelSurchargePercent":18.5,"totalSurcharges":4.49,"totalFreightDiscount":0.0,"surCharges":[{"type":"FUEL",
+        "description":"Fuel Surcharge","level":"PACKAGE","amount":4.49}],"totalBillingWeight":{"units":"LB","value":
+        23.0},"currency":"'.$currencyCode.'"},"ratedPackages":[{"groupNumber":0,"effectiveNetDiscount":0.0,
+        "packageRateDetail":{"rateType":"'.$rateType.'","ratedWeightMethod":"ACTUAL","baseCharge":24.26,"netFreight":
+        24.26,"totalSurcharges":4.49,"netFedExCharge":28.75,"totalTaxes":0.0,"netCharge":28.75,"totalRebates":0.0,
+        "billingWeight":{"units":"KG","value":10.43},"totalFreightDiscounts":0.0,"surcharges":[{"type":"FUEL",
+        "description":"Fuel Surcharge","level":"PACKAGE","amount":4.49}],"currency":"USD"}}],"currency":"USD"}],
+        "operationalDetail":{"ineligibleForMoneyBackGuarantee":false,"astraDescription":"FXG","airportId":"ELP",
+        "serviceCode":"92"},"signatureOptionType":"SERVICE_DEFAULT","serviceDescription":{"serviceId":"EP1000000134",
+        "serviceType":"FEDEX_GROUND","code":"92","names":[{"type":"long","encoding":"utf-8","value":"FedEx Ground®"},
+        {"type":"long","encoding":"ascii","value":"FedEx Ground"},{"type":"medium","encoding":"utf-8","value":"Ground®"}
+        ,{"type":"medium","encoding":"ascii","value":"Ground"},{"type":"short","encoding":"utf-8","value":"FG"},
+        {"type":"short","encoding":"ascii","value":"FG"},{"type":"abbrv","encoding":"ascii","value":"SG"}],
+        "description":"FedEx Ground","astraDescription":"FXG"}}],"quoteDate":"2023-07-13","encoded":false}}';
+        return json_decode($rateResponse, true);
+    }
+
+    /**
+     * Expected Track Response
+     *
+     * @param string $shipTimeStamp
+     * @param string $expectedDate
+     * @param string $expectedTime
+     * @return array
+     */
+    public function getTrackResponse($shipTimeStamp, $expectedDate, $expectedTime) : array
+    {
+        $trackResponse = '{"transactionId":"4d37cd0c-f4e8-449f-ac95-d4d3132f0572",
+        "output":{"completeTrackResults":[{"trackingNumber":"122816215025810","trackResults":[{"trackingNumberInfo":
+        {"trackingNumber":"122816215025810","trackingNumberUniqueId":"12013~122816215025810~FDEG","carrierCode":"FDXG"},
+        "additionalTrackingInfo":{"nickname":"","packageIdentifiers":[{"type":"CUSTOMER_REFERENCE","values":
+        ["PO#174724"],"trackingNumberUniqueId":"","carrierCode":""}],"hasAssociatedShipments":false},
+        "shipperInformation":{"address":{"city":"POST FALLS","stateOrProvinceCode":"ID","countryCode":"US",
+        "residential":false,"countryName":"United States"}},"recipientInformation":{"address":{"city":"NORTON",
+        "stateOrProvinceCode":"VA","countryCode":"US","residential":false,"countryName":"United States"}},
+        "latestStatusDetail":{"code":"DL","derivedCode":"DL","statusByLocale":"Delivered","description":"Delivered",
+        "scanLocation":{"city":"Norton","stateOrProvinceCode":"VA","countryCode":"US","residential":false,
+        "countryName":"United States"}},"dateAndTimes":[{"type":"ACTUAL_DELIVERY","dateTime":
+        "'.$expectedDate.'T'.$expectedTime.'"},{"type":"ACTUAL_PICKUP","dateTime":"2016-08-01T00:00:00-06:00"},
+        {"type":"SHIP","dateTime":"'.$shipTimeStamp.'"}],"availableImages":[{"type":"SIGNATURE_PROOF_OF_DELIVERY"}],
+        "specialHandlings":[{"type":"DIRECT_SIGNATURE_REQUIRED","description":"Direct Signature Required",
+        "paymentType":"OTHER"}],"packageDetails":{"packagingDescription":{"type":"YOUR_PACKAGING","description":
+        "Package"},"physicalPackagingType":"PACKAGE","sequenceNumber":"1","count":"1","weightAndDimensions":
+        {"weight":[{"value":"21.5","unit":"LB"},{"value":"9.75","unit":"KG"}],"dimensions":[{"length":22,"width":17,
+        "height":10,"units":"IN"},{"length":55,"width":43,"height":25,"units":"CM"}]},"packageContent":[]},
+        "shipmentDetails":{"possessionStatus":true},"scanEvents":[{"date":"'.$expectedDate.'T'.$expectedTime.'",
+        "eventType":"DL","eventDescription":"Delivered","exceptionCode":"","exceptionDescription":"","scanLocation":
+        {"streetLines":[""],"city":"Norton","stateOrProvinceCode":"VA","postalCode":"24273","countryCode":"US",
+        "residential":false,"countryName":"United States"},"locationType":"DELIVERY_LOCATION","derivedStatusCode":"DL",
+        "derivedStatus":"Delivered"},{"date":"2014-01-09T04:18:00-05:00","eventType":"OD","eventDescription":
+        "On FedEx vehicle for delivery","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":
+        [""],"city":"KINGSPORT","stateOrProvinceCode":"TN","postalCode":"37663","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0376","locationType":"VEHICLE","derivedStatusCode":"IT",
+        "derivedStatus":"In transit"},{"date":"2014-01-09T04:09:00-05:00","eventType":"AR","eventDescription":
+        "At local FedEx facility","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"KINGSPORT","stateOrProvinceCode":"TN","postalCode":"37663","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0376","locationType":"DESTINATION_FEDEX_FACILITY",
+        "derivedStatusCode":"IT","derivedStatus":"In transit"},{"date":"2014-01-08T23:26:00-05:00","eventType":"IT",
+        "eventDescription":"In transit","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"KNOXVILLE","stateOrProvinceCode":"TN","postalCode":"37921","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0379","locationType":"FEDEX_FACILITY","derivedStatusCode":"IT",
+        "derivedStatus":"In transit"},{"date":"2014-01-08T18:14:07-06:00","eventType":"DP","eventDescription":
+        "Departed FedEx location","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"NASHVILLE","stateOrProvinceCode":"TN","postalCode":"37207","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0371","locationType":"FEDEX_FACILITY","derivedStatusCode":"IT",
+        "derivedStatus":"In transit"},{"date":"2014-01-08T15:16:00-06:00","eventType":"AR","eventDescription":
+        "Arrived at FedEx location","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"NASHVILLE","stateOrProvinceCode":"TN","postalCode":"37207","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0371","locationType":"FEDEX_FACILITY","derivedStatusCode":"IT",
+        "derivedStatus":"In transit"},{"date":"2014-01-07T00:29:00-06:00","eventType":"AR","eventDescription":
+        "Arrived at FedEx location","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"CHICAGO","stateOrProvinceCode":"IL","postalCode":"60638","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0604","locationType":"FEDEX_FACILITY","derivedStatusCode":"IT",
+        "derivedStatus":"In transit"},{"date":"2014-01-03T19:12:30-08:00","eventType":"DP","eventDescription":
+        "Left FedEx origin facility","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"SPOKANE","stateOrProvinceCode":"WA","postalCode":"99216","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0992","locationType":"ORIGIN_FEDEX_FACILITY","derivedStatusCode":
+        "IT","derivedStatus":"In transit"},{"date":"2014-01-03T18:33:00-08:00","eventType":"AR","eventDescription":
+        "Arrived at FedEx location","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],
+        "city":"SPOKANE","stateOrProvinceCode":"WA","postalCode":"99216","countryCode":"US","residential":false,
+        "countryName":"United States"},"locationId":"0992","locationType":"FEDEX_FACILITY","derivedStatusCode":"IT",
+        "derivedStatus":"In transit"},{"date":"2014-01-03T15:00:00-08:00","eventType":"PU","eventDescription":
+        "Picked up","exceptionCode":"","exceptionDescription":"","scanLocation":{"streetLines":[""],"city":"SPOKANE",
+        "stateOrProvinceCode":"WA","postalCode":"99216","countryCode":"US","residential":false,"countryName":
+        "United States"},"locationId":"0992","locationType":"PICKUP_LOCATION","derivedStatusCode":"PU","derivedStatus":
+        "Picked up"},{"date":"2014-01-03T14:31:00-08:00","eventType":"OC","eventDescription":
+        "Shipment information sent to FedEx","exceptionCode":"","exceptionDescription":"","scanLocation":
+        {"streetLines":[""],"postalCode":"83854","countryCode":"US","residential":false,"countryName":"United States"},
+        "locationType":"CUSTOMER","derivedStatusCode":"IN","derivedStatus":"Initiated"}],"availableNotifications":
+        ["ON_DELIVERY"],"deliveryDetails":{"actualDeliveryAddress":{"city":"Norton","stateOrProvinceCode":"VA",
+        "countryCode":"US","residential":false,"countryName":"United States"},"locationType":"SHIPPING_RECEIVING",
+        "locationDescription":"Shipping/Receiving","deliveryAttempts":"0","receivedByName":"ROLLINS",
+        "deliveryOptionEligibilityDetails":[{"option":"INDIRECT_SIGNATURE_RELEASE","eligibility":"INELIGIBLE"},
+        {"option":"REDIRECT_TO_HOLD_AT_LOCATION","eligibility":"INELIGIBLE"},{"option":"REROUTE","eligibility":
+        "INELIGIBLE"},{"option":"RESCHEDULE","eligibility":"INELIGIBLE"},{"option":"RETURN_TO_SHIPPER","eligibility":
+        "INELIGIBLE"},{"option":"DISPUTE_DELIVERY","eligibility":"INELIGIBLE"},{"option":"SUPPLEMENT_ADDRESS",
+        "eligibility":"INELIGIBLE"}]},"originLocation":{"locationContactAndAddress":{"address":{"city":"SPOKANE",
+        "stateOrProvinceCode":"WA","countryCode":"US","residential":false,"countryName":"United States"}}},
+        "lastUpdatedDestinationAddress":{"city":"Norton","stateOrProvinceCode":"VA","countryCode":"US","residential":
+        false,"countryName":"United States"},"serviceDetail":{"type":"FEDEX_GROUND","description":"FedEx Ground",
+        "shortDescription":"FG"},"standardTransitTimeWindow":{"window":{"ends":"2016-08-01T00:00:00-06:00"}},
+        "estimatedDeliveryTimeWindow":{"window":{}},"goodsClassificationCode":"","returnDetail":{}}]}]}}';
+
+        return json_decode($trackResponse, true);
+    }
+
+    /**
+     * @param float $amount
+     * @param string $currencyCode
+     * @param string $baseCurrencyCode
+     * @param string $rateType
+     * @param float $expected
+     * @dataProvider collectRatesDataProvider
+     */
+    public function testCollectRatesRateAmountOriginBasedRest(
+        $amount,
+        $currencyCode,
+        $baseCurrencyCode,
+        $rateType,
+        $expected
+    ) {
+        $this->scope->expects($this->any())
+            ->method('isSetFlag')
+            ->willReturn(true);
+
+            $this->scope->expects($this->any())
+                ->method('getValue')
+                ->willReturnCallback([$this, 'scopeConfigGetValueRest']);
+
+        $accessTokenResponse = $this->getAccessToken();
+        $rateResponse = $this->getRateResponse($amount, $currencyCode, $rateType);
+
+        $this->serializer->method('serialize')
+            ->willReturn('CollectRateString' . $amount);
+
+        $rateCurrency = $this->getMockBuilder(Currency::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $rateCurrency->method('load')
+            ->willReturnSelf();
+        $rateCurrency->method('getAnyRate')
+            ->willReturnMap(
+                [
+                    ['USD', 1],
+                    ['EUR', 0.75],
+                    ['UNKNOWN', false]
+                ]
+            );
+
+        if ($baseCurrencyCode === 'UNKNOWN') {
+            $this->expectException(LocalizedException::class);
+        }
+
+        $this->currencyFactory->method('create')
+            ->willReturn($rateCurrency);
+
+        $baseCurrency = $this->getMockBuilder(Currency::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $baseCurrency->method('getCode')
+            ->willReturn($baseCurrencyCode);
+
+        $request = $this->getMockBuilder(RateRequest::class)
+            ->setMethods(['getBaseCurrency'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $request->method('getBaseCurrency')
+            ->willReturn($baseCurrency);
+
+        $this->curlFactory->expects($this->any())->method('create')->willReturn($this->curlClient);
+        $this->curlClient->expects($this->any())->method('getBody')->willReturnSelf();
+
+        $this->serializer->method('serialize')
+            ->willReturn('CollectRateString' . $amount);
+
+        $this->serializer
+        ->method('unserialize')
+        ->willReturnOnConsecutiveCalls($accessTokenResponse, $rateResponse);
+
+        $allRates1 = $this->carrier->collectRates($request)->getAllRates();
+        foreach ($allRates1 as $rate) {
+            $this->assertEquals($expected, $rate->getData('cost'));
+        }
+    }
+
     /**
      * @param float $amount
      * @param string $currencyCode
@@ -257,7 +639,7 @@
      * @param int $callNum
      * @dataProvider collectRatesDataProvider
      */
-    public function testCollectRatesRateAmountOriginBased(
+    public function testCollectRatesRateAmountOriginBasedSoap(
         $amount,
         $currencyCode,
         $baseCurrencyCode,
@@ -269,6 +651,10 @@
             ->method('isSetFlag')
             ->willReturn(true);
 
+            $this->scope->expects($this->any())
+                ->method('getValue')
+                ->willReturnCallback([$this, 'scopeConfigGetValueSoap']);
+
         // @codingStandardsIgnoreStart
         $netAmount = new \stdClass();
         $netAmount->Amount = $amount;
@@ -291,7 +677,7 @@
         // @codingStandardsIgnoreEnd
 
         $this->serializer->method('serialize')
-            ->willReturn('CollectRateString' . $amount);
+            ->willReturn('CollectRateStringSOAP' . $amount);
 
         $rateCurrency = $this->getMockBuilder(Currency::class)
             ->disableOriginalConstructor()
@@ -366,6 +752,10 @@
 
     public function testCollectRatesErrorMessage()
     {
+        $this->scope->expects($this->any())
+        ->method('getValue')
+        ->willReturnCallback([$this, 'scopeConfigGetValueSoap']);
+
         $this->scope->expects($this->once())
             ->method('isSetFlag')
             ->willReturn(false);
@@ -474,8 +864,12 @@
      * @param string $expectedTime
      * @dataProvider shipDateDataProvider
      */
-    public function testGetTracking($tracking, $shipTimeStamp, $expectedDate, $expectedTime, $callNum = 1)
+    public function testGetTrackingSoap($tracking, $shipTimeStamp, $expectedDate, $expectedTime, $callNum = 1)
     {
+        $this->scope->expects($this->any())
+        ->method('getValue')
+        ->willReturnCallback([$this, 'scopeConfigGetValueSoap']);
+
         // @codingStandardsIgnoreStart
         $response = new \stdClass();
         $response->HighestSeverity = 'SUCCESS';
@@ -519,6 +913,7 @@
         $this->assertCount(1, $tracks);
 
         $current = $tracks[0];
+
         $fields = [
             'signedby',
             'status',
@@ -536,6 +931,82 @@
     }
 
     /**
+     * Get Track Request
+     * @param string $tracking
+     * @response array
+     */
+    public function getTrackRequest(string $tracking) : array
+    {
+        return [
+            'includeDetailedScans' => true,
+            'trackingInfo' => [
+                [
+                    'trackingNumberInfo' => [
+                        'trackingNumber'=> $tracking
+                    ]
+                ]
+            ]
+        ];
+    }
+
+    /**
+     * @param string $tracking
+     * @param string $shipTimeStamp
+     * @param string $expectedDate
+     * @param string $expectedTime
+     * @dataProvider shipDateDataProvider
+     */
+    public function testGetTrackingRest($tracking, $shipTimeStamp, $expectedDate, $expectedTime)
+    {
+        $this->scope->expects($this->any())
+        ->method('getValue')
+        ->willReturnCallback([$this, 'scopeConfigGetValueRest']);
+
+        $trackRequest = $this->getTrackRequest($tracking);
+        $trackResponse = $this->getTrackResponse($shipTimeStamp, $expectedDate, $expectedTime);
+        $accessTokenResponse = $this->getAccessToken();
+
+        $this->serializer->method('serialize')->willReturn(json_encode($trackRequest));
+        $this->serializer->expects($this->any())
+            ->method('unserialize')
+            ->willReturnOnConsecutiveCalls($accessTokenResponse, $trackResponse);
+
+        $status = $this->helper->getObject(Status::class);
+        $this->statusFactory->method('create')
+            ->willReturn($status);
+
+        $tracks = $this->carrier->getTracking($tracking)->getAllTrackings();
+        $this->assertCount(1, $tracks);
+
+        $current = $tracks[0];
+        $fields = [
+            'signedby',
+            'status',
+            'service',
+            'deliverylocation',
+            'weight',
+        ];
+        array_walk($fields, function ($field) use ($current) {
+            $this->assertNotEmpty($current[$field]);
+        });
+
+        $this->assertEquals($tracking, $current['tracking']);
+        $this->assertEquals($expectedDate, $current['deliverydate']);
+        $this->assertEquals($expectedTime, $current['deliverytime']);
+
+        // assert track events
+        $this->assertNotEmpty($current['progressdetail']);
+
+        $event = $current['progressdetail'][0];
+        $fields = ['activity', 'deliverylocation'];
+        array_walk($fields, function ($field) use ($event) {
+            $this->assertNotEmpty($event[$field]);
+        });
+        $this->assertEquals($expectedDate, $event['deliverydate']);
+        $this->assertEquals($expectedTime, $event['deliverytime']);
+    }
+
+    /**
      * Gets list of variations for testing ship date.
      *
      * @return array
diff -ruN vendor/magento/module-fedex/etc/adminhtml/system.xml vendor/magento/module-fedex/etc/adminhtml/system.xml
--- a/vendor/magento/module-fedex/etc/adminhtml/system.xml	2025-11-03 19:36:03.829609220 +0530
+++ b/vendor/magento/module-fedex/etc/adminhtml/system.xml	2025-11-03 20:43:50.417457200 +0530
@@ -17,6 +17,10 @@
                 <field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                     <label>Title</label>
                 </field>
+                <field id="web_service_type" translate="label" type="select" sortOrder="25" showInDefault="1" showInWebsite="1" canRestore="1">
+                    <label>Web Service Type</label>
+                    <source_model>Magento\Fedex\Model\Source\WebServiceType</source_model>
+                </field>
                 <field id="account" translate="label comment" type="obscure" sortOrder="40" showInDefault="1" showInWebsite="1">
                     <label>Account ID</label>
                     <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
@@ -25,14 +29,37 @@
                 <field id="meter_number" translate="label" type="obscure" sortOrder="50" showInDefault="1" showInWebsite="1">
                     <label>Meter Number</label>
                     <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+                    <depends>
+                        <field id="web_service_type">SOAP</field>
+                    </depends>
                 </field>
                 <field id="key" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1">
                     <label>Key</label>
                     <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+                    <depends>
+                        <field id="web_service_type">SOAP</field>
+                    </depends>
                 </field>
                 <field id="password" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1">
                     <label>Password</label>
                     <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+                    <depends>
+                        <field id="web_service_type">SOAP</field>
+                    </depends>
+                </field>
+                <field id="api_key" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1">
+                    <label>Api Key</label>
+                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+                    <depends>
+                        <field id="web_service_type">REST</field>
+                    </depends>
+                </field>
+                <field id="secret_key" translate="label" type="obscure" sortOrder="70" showInDefault="1" showInWebsite="1">
+                    <label>Secret Key</label>
+                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+                    <depends>
+                        <field id="web_service_type">REST</field>
+                    </depends>
                 </field>
                 <field id="sandbox_mode" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" canRestore="1">
                     <label>Sandbox Mode</label>
@@ -41,12 +68,30 @@
                 <field id="production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1">
                     <label>Web-Services URL (Production)</label>
                     <depends>
+                        <field id="web_service_type">SOAP</field>
                         <field id="sandbox_mode">0</field>
                     </depends>
                 </field>
                 <field id="sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1">
                     <label>Web-Services URL (Sandbox)</label>
                     <depends>
+                        <field id="web_service_type">SOAP</field>
+                        <field id="sandbox_mode">1</field>
+                    </depends>
+                </field>
+                <field id="rest_production_webservices_url" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1" canRestore="1">
+                    <label>Web-Services URL (Production)</label>
+                    <backend_model>Magento\Fedex\Model\Config\Backend\FedexUrl</backend_model>
+                    <depends>
+                        <field id="web_service_type">REST</field>
+                        <field id="sandbox_mode">0</field>
+                    </depends>
+                </field>
+                <field id="rest_sandbox_webservices_url" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" canRestore="1">
+                    <label>Web-Services URL (Sandbox)</label>
+                    <backend_model>Magento\Fedex\Model\Config\Backend\FedexUrl</backend_model>
+                    <depends>
+                        <field id="web_service_type">REST</field>
                         <field id="sandbox_mode">1</field>
                     </depends>
                 </field>
@@ -61,6 +106,16 @@
                 <field id="dropoff" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1">
                     <label>Dropoff</label>
                     <source_model>Magento\Fedex\Model\Source\Dropoff</source_model>
+                    <depends>
+                        <field id="web_service_type">SOAP</field>
+                    </depends>
+                </field>
+                <field id="pickup_type" translate="label" type="select" sortOrder="130" showInDefault="1" showInWebsite="1" canRestore="1">
+                    <label>PickUp Type</label>
+                    <source_model>Magento\Fedex\Model\Source\PickupType</source_model>
+                    <depends>
+                        <field id="web_service_type">REST</field>
+                    </depends>
                 </field>
                 <field id="unit_of_measure" translate="label" type="select" sortOrder="135" showInDefault="1" showInWebsite="1" canRestore="1">
                     <label>Weight Unit</label>
diff -ruN vendor/magento/module-fedex/etc/config.xml vendor/magento/module-fedex/etc/config.xml
--- a/vendor/magento/module-fedex/etc/config.xml	2025-11-03 19:36:03.829609220 +0530
+++ b/vendor/magento/module-fedex/etc/config.xml	2025-11-03 20:43:48.653116500 +0530
@@ -9,19 +9,25 @@
     <default>
         <carriers>
             <fedex>
+                <web_service_type>SOAP</web_service_type>
                 <account backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
                 <meter_number backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
                 <key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
                 <password backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
+                <api_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
+                <secret_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
                 <sandbox_mode>0</sandbox_mode>
                 <production_webservices_url><![CDATA[https://ws.fedex.com:443/web-services/]]></production_webservices_url>
                 <sandbox_webservices_url><![CDATA[https://wsbeta.fedex.com:443/web-services/]]></sandbox_webservices_url>
+                <rest_production_webservices_url><![CDATA[https://apis.fedex.com/]]></rest_production_webservices_url>
+                <rest_sandbox_webservices_url><![CDATA[https://apis-sandbox.fedex.com/]]></rest_sandbox_webservices_url>
                 <shipment_requesttype>0</shipment_requesttype>
                 <active>0</active>
                 <sallowspecific>0</sallowspecific>
                 <allowed_methods>EUROPE_FIRST_INTERNATIONAL_PRIORITY,FEDEX_1_DAY_FREIGHT,FEDEX_2_DAY_FREIGHT,FEDEX_2_DAY,FEDEX_2_DAY_AM,FEDEX_3_DAY_FREIGHT,FEDEX_EXPRESS_SAVER,FEDEX_GROUND,FIRST_OVERNIGHT,GROUND_HOME_DELIVERY,INTERNATIONAL_ECONOMY,INTERNATIONAL_ECONOMY_FREIGHT,INTERNATIONAL_FIRST,INTERNATIONAL_GROUND,INTERNATIONAL_PRIORITY,INTERNATIONAL_PRIORITY_FREIGHT,PRIORITY_OVERNIGHT,SMART_POST,STANDARD_OVERNIGHT,FEDEX_FREIGHT,FEDEX_NATIONAL_FREIGHT</allowed_methods>
                 <cutoff_cost />
                 <dropoff>REGULAR_PICKUP</dropoff>
+                <pickup_type>DROPOFF_AT_FEDEX_LOCATION</pickup_type>
                 <free_method>FEDEX_GROUND</free_method>
                 <handling>0</handling>
                 <model>Magento\Fedex\Model\Carrier</model>
diff -ruN vendor/magento/module-fedex/etc/di.xml vendor/magento/module-fedex/etc/di.xml
--- a/vendor/magento/module-fedex/etc/di.xml	2025-11-03 19:36:03.829609220 +0530
+++ b/vendor/magento/module-fedex/etc/di.xml	2025-11-03 20:43:48.328787300 +0530
@@ -13,6 +13,8 @@
                 <item name="carriers/fedex/key" xsi:type="string">1</item>
                 <item name="carriers/fedex/meter_number" xsi:type="string">1</item>
                 <item name="carriers/fedex/password" xsi:type="string">1</item>
+                <item name="carriers/fedex/api_key" xsi:type="string">1</item>
+                <item name="carriers/fedex/secret_key" xsi:type="string">1</item>
                 <item name="carriers/fedex/production_webservices_url" xsi:type="string">1</item>
                 <item name="carriers/fedex/sandbox_webservices_url" xsi:type="string">1</item>
                 <item name="carriers/fedex/smartpost_hubid" xsi:type="string">1</item>
diff -ruN vendor/magento/module-fedex/i18n/en_US.csv vendor/magento/module-fedex/i18n/en_US.csv
--- a/vendor/magento/module-fedex/i18n/en_US.csv	2025-11-03 19:36:03.839607053 +0530
+++ b/vendor/magento/module-fedex/i18n/en_US.csv	2025-11-03 20:43:58.182891200 +0530
@@ -78,3 +78,13 @@
 "Show Method if Not Applicable","Show Method if Not Applicable"
 "Sort Order","Sort Order"
 "Can't convert a shipping cost from ""%1-%2"" for FedEx carrier.","Can't convert a shipping cost from ""%1-%2"" for FedEx carrier."
+"Fedex API endpoint URL\'s must use fedex.com","Fedex API endpoint URL\'s must use fedex.com"
+"Authentication keys are missing.","Authentication keys are missing."
+"Authorization Error. No Access Token found with given credentials.","Authorization Error. No Access Token found with given credentials."
+"Contact Fedex to Schedule","Contact Fedex to Schedule"
+"DropOff at Fedex Location","DropOff at Fedex Location"
+"Scheduled Pickup","Scheduled Pickup"
+"On Call","On Call"
+"Package Return Program","Package Return Program"
+"Regular Stop","Regular Stop"
+"Tag","Tag"
