diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..a438a23148073273a7381dc83b25dbf1bdcb3eba
Binary files /dev/null and b/.DS_Store differ
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de5cdd5e509b31e9302f902adec4fb53deba29ed
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,27 @@
+image: shiplogic/ci-wp-plugin:node18
+
+variables:
+  GIT_SUBMODULE_STRATEGY: recursive
+
+stages:
+  - tagged_release
+
+tagged_deploy:
+  stage: tagged_release
+  only:
+    - tags
+  before_script:
+    # - AWS_ACCESS_KEY_ID_KEY=$(echo "$AWS_ACCESS_KEY_ID")
+    # - AWS_SECRET_ACCESS_KEY_KEY=$(echo "$AWS_SECRET_ACCESS_KEY")
+    # - AWS_ACCESS_KEY_ID=$(eval echo -e "\$$AWS_ACCESS_KEY_ID_KEY")
+    # - AWS_SECRET_ACCESS_KEY=$(eval echo -e "\$$AWS_SECRET_ACCESS_KEY_KEY")
+    # - export AWS_ACCESS_KEY_ID
+    # - export AWS_SECRET_ACCESS_KEY
+  script:
+    # - echo $AWS_ACCESS_KEY_ID
+    # - echo $AWS_SECRET_ACCESS_KEY
+    - wget https://gitlab.bob.co.za/bob-public-utils/bobgo-magento-extension/-/archive/"$CI_COMMIT_TAG"/bobgo-magento-extension-"$CI_COMMIT_TAG".tar.gz
+    - ls -al
+    - aws s3 cp bobgo-magento-extension-"$CI_COMMIT_TAG".tar.gz s3://bobgo-s3-magento-plugin/ --region=af-south-1
+  allow_failure: false
+  when: on_success
\ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
index d32d97b5a8a4ae98e384adb1dc4342f804d52c96..40f5f92e1a9598eb49f7477e8e687ffaf68f41cb 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -15,7 +15,7 @@
       <path value="$PROJECT_DIR$/vendor/composer" />
     </include_path>
   </component>
-  <component name="PhpProjectSharedConfiguration" php_language_level="8.1">
+  <component name="PhpProjectSharedConfiguration" php_language_level="7.1">
     <option name="suggestChangeDefaultLanguageLevel" value="false" />
   </component>
   <component name="PhpStanOptionsConfiguration">
diff --git a/Block/System/Config/Form/Field/Version.php b/Block/System/Config/Form/Field/Version.php
index 52a54e718795145f99d5722b39118853523f6d8b..2ebcbba8b10029a319b41a897bdca308050f8a9e 100644
--- a/Block/System/Config/Form/Field/Version.php
+++ b/Block/System/Config/Form/Field/Version.php
@@ -1,53 +1,66 @@
 <?php
+
 namespace BobGroup\BobGo\Block\System\Config\Form\Field;
 
 use Magento\Framework\Data\Form\Element\AbstractElement;
+use BobGroup\BobGo\Helper\Data;
 
 /**
  * Displays Version number in System Configuration
- * @category   BobGroup
- * @package    bobgo_CustomShipping
- * @author     info@bob.co.za
- * @website    https://www.bob.co.za
+ *
+ * This block is responsible for displaying the version number of the BobGo extension
+ * in the system configuration settings.
+ *
+ * @website https://www.bobgo.co.za
  */
 class Version extends \Magento\Config\Block\System\Config\Form\Field
 {
-    const EXTENSION_URL = 'https://www.bobgo.co.za';
+    /**
+     * @var string
+     */
+    public const EXTENSION_URL = 'https://www.bobgo.co.za';
 
     /**
-     * @var \BobGroup\BobGo\Helper\Data $helper
+     * @var Data
      */
-    protected $_helper;
+    protected Data $_helper;
 
     /**
+     * Constructor
+     *
      * @param \Magento\Backend\Block\Template\Context $context
-     * @param \BobGroup\BobGo\Helper\Data $helper
+     * @param Data $helper
      */
     public function __construct(
         \Magento\Backend\Block\Template\Context $context,
-        \BobGroup\BobGo\Helper\Data $helper
+        Data $helper
     ) {
         $this->_helper = $helper;
         parent::__construct($context);
     }
 
-
     /**
+     * Get HTML for the element
+     *
      * @param AbstractElement $element
      * @return string
      */
-    protected function _getElementHtml(AbstractElement $element)
+    protected function _getElementHtml(AbstractElement $element): string
     {
-        $extensionVersion   = $this->_helper->getExtensionVersion();
-        $extensionTitle     = 'BobGo';
-        $versionLabel       = sprintf(
+        $extensionVersion = $this->_helper->getExtensionVersion();
+        $extensionTitle = 'BobGo';
+        $versionLabel = sprintf(
             '<a href="%s" title="%s" target="_blank">%s</a>',
             self::EXTENSION_URL,
             $extensionTitle,
             $extensionVersion
         );
-        $element->setValue($versionLabel);
 
-        return $element->getValue();
+        // Set the value using setData
+        $element->setData('value', $versionLabel);
+
+        // Ensure the return value is a string or provide a fallback
+        $value = $element->getData('value');
+        return is_string($value) ? $value : '';
     }
 }
diff --git a/Helper/Data.php b/Helper/Data.php
index d0627eefd27a235e7b0b5b498f8e22ab4fd1a215..0abc58eec1370f09d35a38d560ef4a051849f64f 100644
--- a/Helper/Data.php
+++ b/Helper/Data.php
@@ -1,4 +1,5 @@
 <?php
+
 namespace BobGroup\BobGo\Helper;
 
 use Magento\Framework\App\Helper\AbstractHelper;
@@ -7,16 +8,17 @@ use Magento\Framework\Module\ModuleListInterface;
 use Magento\Store\Model\ScopeInterface;
 
 /**
- * @category   Bob Go
- * @package    bobgo_CustomShipping
- * @author     Bob Go
- * @website    https://www.bobgo.co.za
+ * Helper class for BobGo module
+ *
+ * This class provides various helper functions used in the BobGo module, including
+ * configuration checks, version retrieval, and logging.
+ *
+ * @website https://www.bobgo.co.za
  */
 class Data extends AbstractHelper
 {
-
-    const XML_PATH_ENABLED = 'BobGroup_BobGo/general/enabled';
-    const XML_PATH_DEBUG   = 'BobGroup_BobGo/general/debug';
+    public const XML_PATH_ENABLED = 'BobGroup_BobGo/general/enabled';
+    public const XML_PATH_DEBUG = 'BobGroup_BobGo/general/debug';
 
     /**
      * @var \Psr\Log\LoggerInterface
@@ -26,9 +28,11 @@ class Data extends AbstractHelper
     /**
      * @var ModuleListInterface
      */
-    protected $_moduleList;
+    protected ModuleListInterface $_moduleList;
 
     /**
+     * Constructor
+     *
      * @param Context $context
      * @param ModuleListInterface $moduleList
      */
@@ -36,53 +40,69 @@ class Data extends AbstractHelper
         Context $context,
         ModuleListInterface $moduleList
     ) {
-        $this->_logger                  = $context->getLogger();
-        $this->_moduleList              = $moduleList;
-
+        $this->_logger = $context->getLogger();
+        $this->_moduleList = $moduleList;
         parent::__construct($context);
     }
 
     /**
-     * Check if enabled
+     * Check if the BobGo module is enabled
      *
      * @return string|null
      */
-    public function isEnabled()
+    public function isEnabled(): ?string
     {
-        return $this->scopeConfig->getValue(
+        $value = $this->scopeConfig->getValue(
             self::XML_PATH_ENABLED,
             ScopeInterface::SCOPE_STORE
         );
+
+        return is_string($value) ? $value : null;
     }
 
-    public function getDebugStatus()
+    /**
+     * Get the debug status of the BobGo module
+     *
+     * @return string|null
+     */
+    public function getDebugStatus(): ?string
     {
-        return $this->scopeConfig->getValue(
+        $value = $this->scopeConfig->getValue(
             self::XML_PATH_DEBUG,
             ScopeInterface::SCOPE_STORE
         );
+
+        return is_string($value) ? $value : null;
     }
 
-    public function getExtensionVersion()
+    /**
+     * Get the version of the BobGo extension
+     *
+     * @return string
+     */
+    public function getExtensionVersion(): string
     {
         $moduleCode = 'BobGroup_BobGo';
         $moduleInfo = $this->_moduleList->getOne($moduleCode);
-        return $moduleInfo['setup_version'];
+
+        return $moduleInfo['setup_version'] ?? 'N/A';
     }
 
     /**
+     * Log a debug message if debug mode is enabled
      *
-     * @param $message
-     * @param bool|false $useSeparator
+     * @param string $message
+     * @param bool $useSeparator
+     * @return void
      */
     public function log($message, $useSeparator = false)
     {
         if ($this->getDebugStatus()) {
             if ($useSeparator) {
-                $this->_logger->addDebug(str_repeat('=', 100));
+                $this->_logger->debug(str_repeat('=', 100));
             }
 
-            $this->_logger->addDebug($message);
+            $this->_logger->debug($message);
         }
     }
 }
diff --git a/Model/.DS_Store b/Model/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..a4fcd5f4ec98972dc1379b797b90b44f3200d488
Binary files /dev/null and b/Model/.DS_Store differ
diff --git a/Model/Carrier/AdditionalInfo.php b/Model/Carrier/AdditionalInfo.php
index 0b002a0ff7b8fdf82842f1adceef888dd8aaf052..215dc086b87335f05ad724bcc51fc4b730f7476a 100644
--- a/Model/Carrier/AdditionalInfo.php
+++ b/Model/Carrier/AdditionalInfo.php
@@ -2,79 +2,109 @@
 
 namespace BobGroup\BobGo\Model\Carrier;
 
+use Magento\Framework\App\Request\Http;
+use Magento\Directory\Model\CountryFactory;
+
 /**
- * Get the AdditionalInfo information from the request body and return it
+ * Handles the retrieval of additional information from the request body.
  */
 class AdditionalInfo
 {
+    /**
+     * @var CountryFactory
+     */
+    public $countryFactory;
 
     /**
-     * @return mixed|string
+     * @var Http
      */
-    public function getDestComp(): mixed
-    {
-        $data = json_decode(file_get_contents('php://input'), true);
+    protected $request;
 
-        if (isset($data['address']['company'])) {
-            $destComp = $data['address']['company'];
-        } else {
-            $destComp = '';
-        }
-        return $destComp;
+    /**
+     * Constructor
+     *
+     * @param CountryFactory $countryFactory
+     * @param Http $request
+     */
+    public function __construct(CountryFactory $countryFactory, Http $request)
+    {
+        $this->countryFactory = $countryFactory;
+        $this->request = $request;
     }
 
-    public function getSuburb(): mixed
+    /**
+     * Retrieve the destination company from the request body
+     *
+     * @return string
+     */
+    public function getDestComp(): string
     {
+        $data = $this->getRequestBody();
 
-        $data = json_decode(file_get_contents('php://input'), true);
-
-        if (isset($data['address']['custom_attributes'][0]['value'])) {
-            $destSub = $data['address']['custom_attributes'][0]['value'];
-            //print_r($destSub);
-        } else {
-            $destSub = '';
+        if (isset($data['address']) && is_array($data['address']) && isset($data['address']['company'])) {
+            return (string) $data['address']['company'];
         }
-        return $destSub;
+
+        return '';
     }
 
     /**
-     * @return mixed|string
+     * Retrieve the suburb from the request body
+     *
+     * @return string
      */
-    public function getDestTelephone(): mixed
+    public function getSuburb(): string
     {
-        $data = json_decode(file_get_contents('php://input'), true);
+        $data = $this->getRequestBody();
 
-        if (isset($data['address']['telephone'])) {
-            $destTelephone = $data['address']['telephone'];
-        } else {
-            $destTelephone = '';
+        if (isset($data['address']) && is_array($data['address']) &&
+            isset($data['address']['custom_attributes'][0]) && is_array($data['address']['custom_attributes'][0]) &&
+            isset($data['address']['custom_attributes'][0]['value'])
+        ) {
+            return (string) $data['address']['custom_attributes'][0]['value'];
         }
-        return $destTelephone;
-    }
 
+        return '';
+    }
 
     /**
-     * @var \BobGroup\BobGo\Model\Carrier\AdditionalInfo
+     * Retrieve the destination telephone number from the request body
+     *
+     * @return string
      */
-    public $countryFactory;
-
-    public function __construct($countryFactory)
+    public function getDestTelephone(): string
     {
-        $this->countryFactory = $countryFactory;
+        $data = $this->getRequestBody();
+
+        if (isset($data['address']) && is_array($data['address']) && isset($data['address']['telephone'])) {
+            return (string) $data['address']['telephone'];
+        }
+
+        return '';
     }
 
     /**
-     * country full name
+     * Get the full country name by country ID
      *
+     * @param string $countryId
      * @return string
      */
-    public function getCountryName($countryId): string
+    public function getCountryName(string $countryId): string
     {
-        $countryName = '';
         $country = $this->countryFactory->create()->loadByCode($countryId);
-        if ($country) {
-            $countryName = $country->getName();
-        }
-        return $countryName;
+        return $country->getName() ?: '';
+    }
+
+    /**
+     * Retrieve the request body as an array
+     *
+     * @return array<string, mixed>
+     */
+    private function getRequestBody(): array
+    {
+        $content = $this->request->getContent();
+        $data = json_decode($content, true);
+
+        return is_array($data) ? $data : [];
     }
 }
diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php
index 2c556743c3ae9985de18760c4ae0361293382a59..386e4cbc07cd86549a73eaed2cfa25fa22a35185 100644
--- a/Model/Carrier/BobGo.php
+++ b/Model/Carrier/BobGo.php
@@ -30,12 +30,11 @@ use Magento\Shipping\Model\Tracking\Result\StatusFactory;
 use Magento\Store\Model\ScopeInterface;
 use Magento\Store\Model\StoreManagerInterface;
 use Psr\Log\LoggerInterface;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\App\Request\Http as MagentoHttp;
 
 /**
  * Bob Go shipping implementation
- * @category   Bob Go
- * @package    bobgo_CustomShipping
- * @author     Bob Go
  * @website    https://www.bobgo.co.za
  * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -49,7 +48,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
      */
     public const CODE = 'bobgo';
 
-    const UNITS = 100;
+    /**
+     * Units constant
+     * @var int
+     */
+    public const UNITS = 100;
 
     /**
      * Code of the carrier
@@ -58,7 +61,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
      */
     protected $_code = self::CODE;
 
-
     /**
      * Rate request data
      *
@@ -81,36 +83,48 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     protected $_customizableContainerTypes = ['YOUR_PACKAGING'];
 
     /**
-     * @var \Magento\Store\Model\StoreManagerInterface
+     * @var StoreManagerInterface
      */
-    protected $_storeManager;
+    protected StoreManagerInterface $_storeManager;
 
     /**
-     * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
+     * @var CollectionFactory
      */
-    protected $_productCollectionFactory;
-
+    protected CollectionFactory $_productCollectionFactory;
 
     /**
      * @var DataObject
      */
-    private $_rawTrackingRequest;
+    private DataObject $_rawTrackingRequest;
 
     /**
      * @var \Magento\Framework\HTTP\Client\Curl
      */
     protected \Magento\Framework\HTTP\Client\Curl $curl;
 
+    /**
+     * @var ScopeConfigInterface
+     */
+    protected ScopeConfigInterface $scopeConfig;
 
     /**
-     * @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory
+     * @var JsonFactory
      */
     protected JsonFactory $jsonFactory;
-    private $cartRepository;
+
+    /**
+     * @var AdditionalInfo
+     */
     public AdditionalInfo $additionalInfo;
 
+    /**
+     * @var MagentoHttp
+     */
+    protected MagentoHttp $request;
 
     /**
+     * BobGo constructor.
+     *
      * @param ScopeConfigInterface $scopeConfig
      * @param ErrorFactory $rateErrorFactory
      * @param LoggerInterface $logger
@@ -127,40 +141,40 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
      * @param Data $directoryData
      * @param StockRegistryInterface $stockRegistry
      * @param StoreManagerInterface $storeManager
-     * @param Reader $configReader
      * @param CollectionFactory $productCollectionFactory
      * @param JsonFactory $jsonFactory
      * @param CurlFactory $curlFactory
-     * @param array $data
-     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+     * @param MagentoHttp $request
+     * @param array<string,mixed> $data
      */
     public function __construct(
-        \Magento\Framework\App\Config\ScopeConfigInterface             $scopeConfig,
-        \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory     $rateErrorFactory,
-        \Psr\Log\LoggerInterface                                       $logger,
-        Security                                                       $xmlSecurity,
-        \Magento\Shipping\Model\Simplexml\ElementFactory               $xmlElFactory,
-        \Magento\Shipping\Model\Rate\ResultFactory                     $rateFactory,
-        \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory    $rateMethodFactory,
-        \Magento\Shipping\Model\Tracking\ResultFactory                 $trackFactory,
-        \Magento\Shipping\Model\Tracking\Result\ErrorFactory           $trackErrorFactory,
-        \Magento\Shipping\Model\Tracking\Result\StatusFactory          $trackStatusFactory,
-        \Magento\Directory\Model\RegionFactory                         $regionFactory,
-        \Magento\Directory\Model\CountryFactory                        $countryFactory,
-        \Magento\Directory\Model\CurrencyFactory                       $currencyFactory,
-        \Magento\Directory\Helper\Data                                 $directoryData,
-        \Magento\CatalogInventory\Api\StockRegistryInterface           $stockRegistry,
-        \Magento\Store\Model\StoreManagerInterface                     $storeManager,
-        \Magento\Framework\Module\Dir\Reader                           $configReader,
-        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
-        JsonFactory                                                    $jsonFactory,
-        CurlFactory                                                    $curlFactory,
-        array                                                          $data = []
-    )
-    {
-
+        ScopeConfigInterface $scopeConfig,
+        ErrorFactory $rateErrorFactory,
+        LoggerInterface $logger,
+        Security $xmlSecurity,
+        ElementFactory $xmlElFactory,
+        ResultFactory $rateFactory,
+        MethodFactory $rateMethodFactory,
+        \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory,
+        \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory,
+        StatusFactory $trackStatusFactory,
+        RegionFactory $regionFactory,
+        CountryFactory $countryFactory,
+        CurrencyFactory $currencyFactory,
+        Data $directoryData,
+        StockRegistryInterface $stockRegistry,
+        StoreManagerInterface $storeManager,
+        CollectionFactory $productCollectionFactory,
+        JsonFactory $jsonFactory,
+        CurlFactory $curlFactory,
+        MagentoHttp $request,
+        array $data = []
+    ) {
+        $this->request = $request;
         $this->_storeManager = $storeManager;
         $this->_productCollectionFactory = $productCollectionFactory;
+        $this->scopeConfig = $scopeConfig;
+
         parent::__construct(
             $scopeConfig,
             $rateErrorFactory,
@@ -179,68 +193,86 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
             $stockRegistry,
             $data
         );
+
         $this->jsonFactory = $jsonFactory;
         $this->curl = $curlFactory->create();
-        $this->additionalInfo = new AdditionalInfo($countryFactory);
+        $this->additionalInfo = new AdditionalInfo($countryFactory, $this->request);
     }
 
-
-    /*
-     * Gets the base url of the store by stripping the http:// or https:// and wwww. from the url
-     * leaving just "example.com" since Bob Go API uses this format and not the full url as the Identifier
-     * @var \Magento\Store\Model\StoreManagerInterface $this->_storeManager
+    /**
+     * Gets the base URL of the store by stripping the http:// or https:// and www. from the URL.
+     *
      * @return string
      */
     public function getBaseUrl(): string
     {
-        $storeBase = $this->_storeManager->getStore()->getBaseUrl();
-        return parse_url($storeBase, PHP_URL_HOST);
-    }
+        /** @var Store $store */
+        $store = $this->_storeManager->getStore();
+        $storeBase = $store->getBaseUrl();
+
+        // Remove protocol (http:// or https://)
+        $host = preg_replace('#^https?://#', '', $storeBase);
 
+        // Ensure $host is a string before using it in explode
+        $host = $host ?? '';
+
+        // Remove everything after the host (e.g., paths, query strings)
+        $host = explode('/', $host)[0];
+
+        // If the host starts with 'www.', remove it
+        if (strpos($host, 'www.') === 0) {
+            $host = substr($host, 4);
+        }
+
+        return $host;
+    }
 
     /**
-     *  Make request to Bob Go API to get shipping rates for the cart
-     * After all the required data is collected, it makes a request to the Bob Go API to get the shipping rates
-     * @param $payload
-     * @return array
+     * Makes a request to the Bob Go API to get shipping rates for the cart.
+     *
+     * @param array<string,mixed> $payload
+     * @return array<int|string, mixed>
      */
-    public function getRates($payload): array
+    public function getRates(array $payload): array
     {
-        return $this->uRates($payload);
+        $rates = $this->uRates($payload);
+
+        // Ensure the return value is always an array, even if uRates returns null
+        return $rates ?? [];
     }
 
     /**
-     * Processing additional validation to check if carrier applicable.
+     * Processing additional validation to check if the carrier is applicable.
      *
-     * @param \Magento\Framework\DataObject $request
+     * @param DataObject $request
      * @return $this|bool|\Magento\Framework\DataObject
      */
-    public function processAdditionalValidation(\Magento\Framework\DataObject $request)
+    public function processAdditionalValidation(DataObject $request)
     {
-        if (!count($this->getAllItems($request))) {
+        /** @var RateRequest $rateRequest */
+        $rateRequest = $request;
+
+        if (!count($this->getAllItems($rateRequest))) {
             return false;
         }
 
-        $maxAllowedWeight = 500; //$this->getConfigData('max_package_weight');
+        $maxAllowedWeight = 500;
         $errorMsg = '';
         $configErrorMsg = $this->getConfigData('specificerrmsg');
         $defaultErrorMsg = __('The shipping module is not available.');
         $showMethod = $this->getConfigData('showmethod');
 
-        /** @var $item \Magento\Quote\Model\Quote\Item */
-        foreach ($this->getAllItems($request) as $item) {
+        /** @var Item $item */
+        foreach ($this->getAllItems($rateRequest) as $item) {
             $product = $item->getProduct();
             if ($product && $product->getId()) {
                 $weight = $product->getWeight();
-                $stockItemData = $this->stockRegistry->getStockItem(
-                    $product->getId(),
-                    $item->getStore()->getWebsiteId()
-                );
+                $websiteId = (int) $item->getStore()->getWebsiteId(); // Ensure $websiteId is an integer
+                $stockItemData = $this->stockRegistry->getStockItem($product->getId(), $websiteId);
                 $doValidation = true;
 
                 if ($stockItemData->getIsQtyDecimal() && $stockItemData->getIsDecimalDivided()) {
-                    if ($stockItemData->getEnableQtyIncrements() && $stockItemData->getQtyIncrements()
-                    ) {
+                    if ($stockItemData->getEnableQtyIncrements() && $stockItemData->getQtyIncrements()) {
                         $weight = $weight * $stockItemData->getQtyIncrements();
                     } else {
                         $doValidation = false;
@@ -249,7 +281,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
                     $weight = $weight * $item->getQty();
                 }
 
-
                 if ($doValidation && $weight > $maxAllowedWeight) {
                     $errorMsg = $configErrorMsg ? $configErrorMsg : $defaultErrorMsg;
                     break;
@@ -257,12 +288,12 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
             }
         }
 
-        if (!$errorMsg && !$request->getDestPostcode() && $this->isZipCodeRequired($request->getDestCountryId())) {
-
+        if (!$errorMsg && !$rateRequest->getDestPostcode()
+            && $this->isZipCodeRequired($rateRequest->getDestCountryId())) {
             $errorMsg = __('This shipping method is not available. Please specify the zip code.');
         }
 
-        if ($request->getDestCountryId() == 'ZA') {
+        if ($rateRequest->getDestCountryId() == 'ZA') {
             $errorMsg = '';
         } else {
             $errorMsg = $configErrorMsg ? $configErrorMsg : $defaultErrorMsg;
@@ -283,52 +314,62 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     }
 
     /**
-     * Collect and get rates for this shipping method based on information in $request
-     * This is a default function that is called by Magento to get the shipping rates for the cart
+     * Collect and get rates for this shipping method based on information in $request.
+     *
+     * This is a default function that is called by Magento to get the shipping rates for the cart.
+     *
      * @param RateRequest $request
      * @return Result|bool|null
      */
     public function collectRates(RateRequest $request)
     {
-        /*** Make sure that Shipping method is enabled*/
+        // Make sure that Shipping method is enabled
         if (!$this->isActive()) {
             return false;
         }
+
         /**
-         * Gets the destination company name from AdditionalInfo Name field in the checkout page
-         * This method is used is the last resort to get the company name since the company name is not available in _rateFactory
+         * Gets the destination company name from Company Name field in the checkout page.
+         * This method is used as the last resort to get the company name since the company name is
+         * not available in _rateFactory.
          */
         $destComp = $this->getDestComp();
         $destSuburb = $this->getDestSuburb();
-        $destPhone = $this->getDestTelephone();
 
         /** @var \Magento\Shipping\Model\Rate\Result $result */
-
         $result = $this->_rateFactory->create();
 
         $destination = $request->getDestPostcode();
-        $destCountryCode = $request->getDestCountryId();
+        $destCountry = $request->getDestCountryId();
         $destRegion = $request->getDestRegionCode();
         $destCity = $request->getDestCity();
         $destStreet = $request->getDestStreet();
 
-        $destCountry = $this->getCountryName($destCountryCode);
-
-        /**  Destination Information  */
+        /** Destination Information */
         [$destStreet1, $destStreet2, $destStreet3] = $this->destStreet($destStreet);
 
-
-        /**  Origin Information  */
-        [$originStreet, $originRegion, $originCountryCode, $originCity, $originStreet1, $originStreet2, $storeName, $baseIdentifier, $originSuburb, $weightUnit, $originPhone] = $this->storeInformation();
-
-        /**  Get all items in cart  */
+        /** Origin Information */
+        [
+            $originStreet,
+            $originRegion,
+            $originCountry,
+            $originCity,
+            $originStreet1,
+            $originStreet2,
+            $storeName,
+            $baseIdentifier,
+            $originSuburb,
+            $weightUnit
+        ] = $this->storeInformation();
+
+        // Ensure weightUnit is always a string
+        $weightUnit = $weightUnit ?? '';
+
+        /** Get all items in cart */
         $items = $request->getAllItems();
         $itemsArray = [];
         $itemsArray = $this->getStoreItems($items, $weightUnit, $itemsArray);
 
-        $originCountry = $this->getCountryName($originCountryCode);
-
-
         $payload = [
             'identifier' => $baseIdentifier,
             'rate' => [
@@ -339,11 +380,8 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
                     'city' => $originCity,
                     'suburb' => $originSuburb,
                     'province' => $originRegion,
-                    'country_code' => $originCountryCode,
+                    'country_code' => $originCountry,
                     'postal_code' => $originStreet,
-                    'telephone' => $originPhone,
-                    'country' => $originCountry,
-
                 ],
                 'destination' => [
                     'company' => $destComp,
@@ -352,10 +390,8 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
                     'suburb' => $destSuburb,
                     'city' => $destCity,
                     'province' => $destRegion,
-                    'country_code' => $destCountryCode,
+                    'country_code' => $destCountry,
                     'postal_code' => $destination,
-                    'telephone' => $destPhone,
-                    'country' => $destCountry
                 ],
                 'items' => $itemsArray,
             ]
@@ -367,64 +403,49 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     }
 
     /**
-     * @return array
+     * Retrieves store information including origin details.
+     *
+     * @return array<int, string|null>
      */
     public function storeInformation(): array
     {
         /** Store Origin details */
-        $originCountryCode = $this->_scopeConfig->getValue(
-            'general/store_information/country_id',
-            ScopeInterface::SCOPE_STORE
-        );
-        $originRegion = $this->_scopeConfig->getValue(
-            'general/store_information/region_id',
-            ScopeInterface::SCOPE_STORE
-        );
-        $originCity = $this->_scopeConfig->getValue(
-            'general/store_information/city',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $originStreet = $this->_scopeConfig->getValue(
-            'general/store_information/postcode',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $originStreet1 = $this->_scopeConfig->getValue(
-            'general/store_information/street_line1',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $originStreet2 = $this->_scopeConfig->getValue(
-            'general/store_information/street_line2',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $storeName = $this->_scopeConfig->getValue(
-            'general/store_information/name',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $originSuburb = $this->_scopeConfig->getValue(
-            'general/store_information/suburb',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $weightUnit = $this->_scopeConfig->getValue(
-            'general/locale/weight_unit',
-            ScopeInterface::SCOPE_STORE
-        );
-
-        $originTelephone = $this->_scopeConfig->getValue(
-            'general/store_information/phone',
-            ScopeInterface::SCOPE_STORE
-        );
-
+        $originCountry = $this->getStringValue('general/store_information/country_id');
+        $originRegion = $this->getStringValue('general/store_information/region_id');
+        $originCity = $this->getStringValue('general/store_information/city');
+        $originStreet = $this->getStringValue('general/store_information/postcode');
+        $originStreet1 = $this->getStringValue('general/store_information/street_line1');
+        $originStreet2 = $this->getStringValue('general/store_information/street_line2');
+        $storeName = $this->getStringValue('general/store_information/name');
+        $originSuburb = $this->getStringValue('general/store_information/suburb');
+        $weightUnit = $this->getStringValue('general/locale/weight_unit');
         $baseIdentifier = $this->getBaseUrl();
 
-        return array($originStreet, $originRegion, $originCountryCode, $originCity, $originStreet1, $originStreet2, $storeName, $baseIdentifier, $originSuburb, $weightUnit, $originTelephone);
+        return [
+            $originStreet,
+            $originRegion,
+            $originCountry,
+            $originCity,
+            $originStreet1,
+            $originStreet2,
+            $storeName,
+            $baseIdentifier,
+            $originSuburb,
+            $weightUnit,
+        ];
     }
 
+    /**
+     * Safely retrieve a configuration value as a string or null.
+     *
+     * @param string $path
+     * @return string|null
+     */
+    protected function getStringValue(string $path): ?string
+    {
+        $value = $this->_scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE);
+        return is_scalar($value) ? (string) $value : null;
+    }
 
     /**
      * Get result of request
@@ -439,7 +460,6 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         return $this->_result;
     }
 
-
     /**
      * Get final price for shipping method with handling fee per package
      *
@@ -474,13 +494,12 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         return $cost + $handlingFee;
     }
 
-
     /**
      * Get configuration data of carrier
      *
      * @param string $type
      * @param string $code
-     * @return array|false
+     * @return array<string, \Magento\Framework\Phrase>|string|false
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
     public function getCode($type, $code = '')
@@ -504,30 +523,29 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         if (!isset($codes[$type][$code])) {
             return false;
         } else {
-            return $codes[$type][$code];
+            return (string) $codes[$type][$code]; // Convert \Magento\Framework\Phrase to string
         }
     }
 
-
     /**
      * Get tracking
      *
      * @param string|string[] $trackings
-     * @return Result|null
+     * @return \Magento\Shipping\Model\Tracking\Result|null
      */
     public function getTracking($trackings)
     {
-        $this->setTrackingRequest();
+        $this->setTrackingRequest(); // Ensure this method is correctly defined
 
         if (!is_array($trackings)) {
             $trackings = [$trackings];
         }
 
         foreach ($trackings as $tracking) {
-            $this->_getXMLTracking($tracking);
+            $this->_getXMLTracking([$tracking]); // Ensure _getXMLTracking processes tracking correctly
         }
 
-        return $this->_result;
+        return $this->_result; // Ensure _result is a \Magento\Shipping\Model\Tracking\Result
     }
 
     /**
@@ -540,11 +558,21 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         $r = new \Magento\Framework\DataObject();
 
         $account = $this->getConfigData('account');
-        $r->setAccount($account);
+        $r->setData('account', $account); // Using setData with the key 'account'
 
         $this->_rawTrackingRequest = $r;
     }
 
+    /**
+     * Get tracking request
+     *
+     * @return \Magento\Framework\DataObject|null
+     */
+    protected function getTrackingRequest(): ?\Magento\Framework\DataObject
+    {
+        return $this->_rawTrackingRequest;
+    }
+
     /**
      * Send request for tracking
      *
@@ -559,7 +587,7 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     /**
      * Parse tracking response
      *
-     * @param string $trackingValue
+     * @param string|array<int,string> $trackingValue
      * @return void
      */
     protected function _parseTrackingResponse($trackingValue)
@@ -567,35 +595,33 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         $result = $this->getResult();
         $carrierTitle = $this->getConfigData('title');
         $counter = 0;
+
         if (!is_array($trackingValue)) {
             $trackingValue = [$trackingValue];
         }
+
         foreach ($trackingValue as $trackingReference) {
             $tracking = $this->_trackStatusFactory->create();
 
             $tracking->setCarrier(self::CODE);
             $tracking->setCarrierTitle($carrierTitle);
 
-            //Production
-            /*   $tracking->setUrl(sprintf(uData::TRACKING, $this->getBaseUrl(), $trackingReference));
-            $tracking->setTracking($trackingReference);
-            $tracking->addData($this->processTrackingDetails($trackingReference));
-           */
-
-            //Dev
-            $tracking->setUrl(uData::TRACKING . $trackingReference);
+            // Adjust as needed based on the environment
+            $tracking->setUrl(UData::TRACKING . $trackingReference);
             $tracking->setTracking($trackingReference);
             $tracking->addData($this->processTrackingDetails($trackingReference));
 
-            $result->append($tracking);
-            $counter++;
+            if ($result) {
+                $result->append($tracking);
+                $counter++;
+            }
         }
 
-        //Tracking Details Not Available
-        if (!$counter) {
+        // Tracking Details Not Available
+        if ($counter === 0) {
             $this->appendTrackingError(
-                $trackingValue,
-                __('For some reason we can\'t retrieve tracking info right now.')
+                $trackingValue[0] ?? '',
+                (string)__('For some reason we can\'t retrieve tracking info right now.')
             );
         }
     }
@@ -605,9 +631,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
      *
      * @return string
      */
-    public function getResponse()
+    public function getResponse(): string
     {
         $statuses = '';
+
+        // If $_result is of type \Magento\Shipping\Model\Tracking\Result, handle it
         if ($this->_result instanceof \Magento\Shipping\Model\Tracking\Result) {
             if ($trackings = $this->_result->getAllTrackings()) {
                 foreach ($trackings as $tracking) {
@@ -621,9 +649,14 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
                 }
             }
         }
-        // phpstan:ignore
-        if (empty($statuses)) {
-            $statuses = __('Empty response');
+
+//        // Handle \Magento\Shipping\Model\Rate\Result if needed
+//        if ($this->_result instanceof \Magento\Shipping\Model\Rate\Result) {
+//            // Implement the logic for Rate\Result if applicable
+//        }
+
+        if (trim($statuses) === '') {
+            $statuses = (string)__('Empty response');
         }
 
         return $statuses;
@@ -632,11 +665,16 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     /**
      * Get allowed shipping methods
      *
-     * @return array
+     * @return array<string, mixed>
      */
-    public function getAllowedMethods()
+    public function getAllowedMethods(): array
     {
-        $allowed = explode(',', $this->getConfigData('allowed_methods'));
+        $allowedMethods = $this->getConfigData('allowed_methods');
+        if ($allowedMethods === false) {
+            return []; // Return an empty array if no allowed methods are configured
+        }
+
+        $allowed = explode(',', $allowedMethods);
         $arr = [];
         foreach ($allowed as $k) {
             $arr[$k] = $this->getCode('method', $k);
@@ -645,37 +683,40 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         return $arr;
     }
 
-
     /**
-     * Do shipment request to carrier web service, obtain Print Shipping Labels and process errors in response
-     * Also another magic function that is required to be implemented by carrier model
+     * Do shipment request to carrier web service, obtain Print Shipping Labels, and process errors in response.
+     *
+     * Also another magic function that is required to be implemented by the carrier model.
+     *
      * @param \Magento\Framework\DataObject $request
-     * @return \Magento\Framework\DataObject
+     * @return \Magento\Framework\DataObject|null
      */
     protected function _doShipmentRequest(\Magento\Framework\DataObject $request)
     {
         return null;
-
     }
 
     /**
-     * For multi package shipments. Delete requested shipments if the current shipment request is failed
-     *
-     * @param array $data
+     * For multi-package shipments. Delete requested shipments if the current shipment request fails.
      *
+     * @param mixed $data
      * @return bool
      */
-    public function rollBack($data)
+    public function rollBack($data): bool
     {
+        // Return false if $data is not an array
+        if (!is_array($data)) {
+            return false;
+        }
+
         return true;
     }
 
     /**
-     * Return container types of carrier
+     * Return container types of carrier.
      *
      * @param \Magento\Framework\DataObject|null $params
-     *
-     * @return array|bool
+     * @return array<string, mixed>|false
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     public function getContainerTypes(\Magento\Framework\DataObject $params = null)
@@ -691,30 +732,36 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
             }
         }
 
-        return $result;
+        return !empty($result) ? $result : false;
     }
 
     /**
-     * Return delivery confirmation types of carrier
+     * Return delivery confirmation types of carrier.
      *
      * @param \Magento\Framework\DataObject|null $params
-     *
-     * @return array
+     * @return array<int|string, mixed>
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null)
+    public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null): array
     {
-        return $this->getCode('delivery_confirmation_types');
+        $types = $this->getCode('delivery_confirmation_types');
+
+        // Ensure it returns an array, even if getCode returns false
+        return is_array($types) ? $types : [];
     }
 
     /**
-     * Recursive replace sensitive fields in debug data by the mask
+     * Recursive replace sensitive fields in debug data by the mask.
      *
-     * @param string $data
-     * @return string
+     * @param mixed $data
+     * @return mixed
      */
     protected function filterDebugData($data)
     {
+        if (!is_array($data)) {
+            return $data; // Return early if $data is not an array.
+        }
+
         foreach (array_keys($data) as $key) {
             if (is_array($data[$key])) {
                 $data[$key] = $this->filterDebugData($data[$key]);
@@ -722,25 +769,27 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
                 $data[$key] = self::DEBUG_KEYS_MASK;
             }
         }
+
         return $data;
     }
 
     /**
-     * Parse track details response from Bob Go
+     * Parse track details response from Bob Go.
      *
-     * @return array
+     * @param string $trackInfo
+     * @return array<string, array<int, array<string, string>>>
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      * @SuppressWarnings(PHPMD.NPathComplexity)
-     **/
-    private function processTrackingDetails($trackInfo): array
+     */
+    private function processTrackingDetails(string $trackInfo): array
     {
         $result = [
-            'shippeddate' => null,
-            'deliverydate' => null,
-            'deliverytime' => null,
-            'deliverylocation' => null,
-            'weight' => null,
-            'progressdetail' => [],
+            'shippeddate' => [],  // Initializing as an array of arrays
+            'deliverydate' => [], // Initializing as an array of arrays
+            'deliverytime' => [], // Initializing as an array of arrays
+            'deliverylocation' => [], // Initializing as an array of arrays
+            'weight' => [], // Initializing as an array of arrays
+            'progressdetail' => [],  // This will be populated with an array of arrays
         ];
 
         $result = $this->_requestTracking($trackInfo, $result);
@@ -749,145 +798,216 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     }
 
     /**
-     * Append error message to rate result instance
+     * Append error message to rate result instance.
      *
      * @param string $trackingValue
      * @param string $errorMessage
+     * @return void
      */
-    private function appendTrackingError($trackingValue, $errorMessage)
+    private function appendTrackingError(string $trackingValue, string $errorMessage): void
     {
         $error = $this->_trackErrorFactory->create();
         $error->setCarrier(self::CODE);
         $error->setCarrierTitle($this->getConfigData('title'));
         $error->setTracking($trackingValue);
         $error->setErrorMessage($errorMessage);
+
         $result = $this->getResult();
-        $result->append($error);
+
+        if ($result !== null) {
+            $result->append($error);
+        } else {
+            // Handle the case where $result is null, such as logging an error
+            $this->_logger->error('Failed to append tracking error: Result object is null.');
+        }
     }
 
     /**
+     * Format a date to 'd M Y'.
+     *
      * @param string $date
      * @return string
      */
     public function formatDate(string $date): string
     {
-        return date('d M Y', strtotime($date));
+        $timestamp = strtotime($date);
+        if ($timestamp === false) {
+            // Handle the error or return a default value, for example:
+            return 'Invalid date';
+        }
+        return date('d M Y', $timestamp);
     }
 
     /**
+     * Format a time to 'H:i'.
+     *
      * @param string $time
      * @return string
      */
     public function formatTime(string $time): string
     {
-        return date('H:i', strtotime($time));
+        $timestamp = strtotime($time);
+        if ($timestamp === false) {
+            // Handle the error or return a default value, for example:
+            return 'Invalid time';
+        }
+        return date('H:i', $timestamp);
     }
 
     /**
+     * Get the API URL for Bob Go.
+     *
      * @return string
      */
     private function getApiUrl(): string
     {
-        return uData::RATES_ENDPOINT;
+        return UData::RATES_ENDPOINT;
     }
 
     /**
-     *  Perfom API Request to bobgo API and return response
-     * @param array $payload
-     * @param Result $result
+     * Perform API Request to Bob Go API and return response.
+     *
+     * @param array<string,mixed> $payload The payload for the API request.
+     * @param Result $result The result object to append the rates.
      * @return void
      */
     protected function _getRates(array $payload, Result $result): void
     {
-
         $rates = $this->uRates($payload);
 
-        $this->_formatRates($rates, $result);
-
+        // Ensure $rates is an array before passing it to _formatRates
+        if (is_array($rates)) {
+            $this->_formatRates($rates, $result);
+        } else {
+            $this->_logger->error('Bob Go API returned an invalid response');
+        }
     }
 
     /**
-     * Perform API Request for Shipment Tracking to Bob Go API and return response
-     * @param $trackInfo
-     * @param array $result
-     * @return array
+     * Perform API Request for Shipment Tracking to Bob Go API and return response.
+     *
+     * @param string $trackInfo The tracking information or tracking ID.
+     * @param array<string,array<int,array<string,string>>> $result The result array to be
+     * populated with tracking details.
+     * @return array<string, array<int, array<string, string>>> The updated result array with tracking details.
      */
-    private function _requestTracking($trackInfo, array $result): array
+    private function _requestTracking(string $trackInfo, array $result): array
     {
-        $response = $this->trackbobgoShipment($trackInfo);
+        $response = $this->trackBobGoShipment($trackInfo);
 
-        $result = $this->prepareActivity($response[0], $result);
+        // Validate that the response is an array and contains at least one element
+        if (is_array($response) && isset($response[0]) && is_array($response[0])) {
+            $result = $this->prepareActivity($response[0], $result);
+        }
 
         return $result;
     }
 
     /**
-     * Format rates from Bob Go API response and append to rate result instance of carrier
-     * @param mixed $rates
-     * @param Result $result
+     * Format rates from Bob Go API response and append to rate result instance of carrier.
+     *
+     * @param array<int|string,mixed> $rates The rates data from the API.
+     * @param Result $result The result object to append the rates.
      * @return void
      */
-    protected function _formatRates(mixed $rates, Result $result): void
+    protected function _formatRates(array $rates, Result $result): void
     {
-        if (!isset($rates['rates'])) {
+        if (empty($rates['rates']) || !is_array($rates['rates'])) {  // Validate that 'rates' exists and is an array
             $error = $this->_rateErrorFactory->create();
             $error->setCarrierTitle($this->getConfigData('title'));
             $error->setErrorMessage($this->getConfigData('specificerrmsg'));
 
             $result->append($error);
-        } else {
-            foreach ($rates['rates'] as $title) {
+            return;
+        }
 
-                $method = $this->_rateMethodFactory->create();
-                if (isset($title)) {
-                    $method->setCarrier(self::CODE);
+        foreach ($rates['rates'] as $rate) {
+            if (!is_array($rate)) {
+                continue;  // Skip if the rate is not an array
+            }
 
+            $method = $this->_rateMethodFactory->create();
 
-                    if ($this->getConfigData('additional_info') == 1) {
-                        $min_delivery_date = $this->getWorkingDays(date('Y-m-d'), $title['min_delivery_date']);
-                        $max_delivery_date = $this->getWorkingDays(date('Y-m-d'), $title['max_delivery_date']);
+            // Set the carrier code
+            $method->setCarrier(self::CODE);
 
-                        $this->deliveryDays($min_delivery_date, $max_delivery_date, $method);
+            // Strip out the redundant 'bobgo_' prefix if present
+            $serviceCode = $rate['service_code'] ?? '';
+            if (is_string($serviceCode) && strpos($serviceCode, 'bobgo_') === 0) {
+                $serviceCode = substr($serviceCode, strlen('bobgo_'));
+            }
 
-                    } else {
-                        $method->setCarrierTitle($this->getConfigData('title'));
-                    }
+            // Set the method with the modified service code
+            $method->setMethod($serviceCode);
 
-                }
+            // Set additional info if required
+            if ($this->getConfigData('additional_info') == 1) {
+                $min_delivery_date = isset($rate['min_delivery_date']) && is_string($rate['min_delivery_date'])
+                    ? $this->getWorkingDays(date('Y-m-d'), $rate['min_delivery_date'])
+                    : null;
 
-                $method->setMethod($title['service_code']);
-                $method->setMethodTitle($title['service_name']);
-                $method->setPrice($title['total_price'] / self::UNITS);
-                $method->setCost($title['total_price'] / self::UNITS);
+                $max_delivery_date = isset($rate['max_delivery_date']) && is_string($rate['max_delivery_date'])
+                    ? $this->getWorkingDays(date('Y-m-d'), $rate['max_delivery_date'])
+                    : null;
+
+                $this->deliveryDays($min_delivery_date, $max_delivery_date, $method);
+            }
 
-                $result->append($method);
+            // Set the method title, price, and cost
+            $service_name = $rate['service_name'] ?? '';
+            if (!is_string($service_name)) {
+                $service_name = '';
+            }
+            $method->setMethodTitle($service_name);
+
+            $price = $rate['total_price'] ?? 0;
+            if (!is_numeric($price)) {
+                $price = 0;
+            }
+            $cost = $rate['total_price'] ?? 0;
+            if (!is_numeric($cost)) {
+                $cost = 0;
             }
+
+            $method->setPrice((float)$price);
+            $method->setCost((float)$cost);
+
+            $result->append($method);
         }
     }
 
-
     /**
-     * Prepare received checkpoints and activity from Bob Go Shipment Tracking API
-     * @param $response
-     * @param array $result
-     * @return array
+     * Prepare received checkpoints and activity from Bob Go Shipment Tracking API.
+     *
+     * @param array<string,mixed> $response The API response containing tracking checkpoints.
+     * @param array<string,array<int,array<string,string>>> $result The result array to be
+     * populated with activity details.
+     * @return array<string, array<int, array<string, string>>> The updated result array with activity details.
      */
-    private function prepareActivity($response, array $result): array
+    private function prepareActivity(array $response, array $result): array
     {
-
-        foreach ($response['checkpoints'] as $checkpoint) {
-            $result['progressdetail'][] = [
-                'activity' => $checkpoint['status'],
-                'deliverydate' => $this->formatDate($checkpoint['time']),
-                'deliverytime' => $this->formatTime($checkpoint['time']),
-                //  'deliverylocation' => 'Unavailable',//TODO: remove this line
-            ];
+        if (isset($response['checkpoints']) && is_array($response['checkpoints'])) {
+            foreach ($response['checkpoints'] as $checkpoint) {
+                if (is_array($checkpoint) &&
+                    isset($checkpoint['status'], $checkpoint['time']) &&
+                    is_string($checkpoint['status']) &&
+                    is_string($checkpoint['time'])
+                ) {
+                    $result['progressdetail'][] = [
+                        'activity' => $checkpoint['status'],
+                        'deliverydate' => $this->formatDate($checkpoint['time']),
+                        'deliverytime' => $this->formatTime($checkpoint['time']),
+                    ];
+                }
+            }
         }
+
         return $result;
     }
 
     /**
-     *  Get Working Days between time of checkout and delivery date (min and max)
+     * Get Working Days between time of checkout and delivery date (min and max).
+     *
      * @param string $startDate
      * @param string $endDate
      * @return int
@@ -896,33 +1016,36 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     {
         $begin = strtotime($startDate);
         $end = strtotime($endDate);
-        if ($begin > $end) {
-//            echo "Start Date Cannot Be In The Future! <br />";
-            return 0;
-        } else {
-            $no_days = 0;
-            $weekends = 0;
-            while ($begin <= $end) {
-                $no_days++; // no of days in the given interval
-                $what_day = date("N", $begin);
-                if ($what_day > 5) { // 6 and 7 are weekend days
-                    $weekends++;
-                };
-                $begin += 86400; // +1 day
-            };
-            return $no_days - $weekends;
+
+        // Check if strtotime failed
+        if ($begin === false || $end === false || $begin > $end) {
+            return 0; // or throw an exception if preferred
         }
-    }
 
+        $no_days = 0;
+        $weekends = 0;
+
+        while ($begin <= $end) {
+            $no_days++; // number of days in the given interval
+            $what_day = date("N", $begin);
+            if ($what_day > 5) { // 6 and 7 are weekend days
+                $weekends++;
+            }
+            $begin += 86400; // +1 day
+        }
+
+        return $no_days - $weekends;
+    }
 
     /**
-     * Curl request to Bob Go Shipment Tracking API
-     * @param $trackInfo
-     * @return mixed
+     * Curl request to Bob Go Shipment Tracking API.
+     *
+     * @param string $trackInfo The tracking information or tracking ID.
+     * @return mixed The decoded API response.
      */
-    private function trackbobgoShipment($trackInfo): mixed
+    private function trackBobGoShipment(string $trackInfo): mixed
     {
-        $this->curl->get(uData::TRACKING . $trackInfo);
+        $this->curl->get(UData::TRACKING . $trackInfo);
 
         $response = $this->curl->getBody();
 
@@ -930,25 +1053,40 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     }
 
     /**
-     * Build The Payload for Bob Go API Request and return response
-     * @param array $payload
-     * @return mixed
+     * Build the payload for Bob Go API request and return the response.
+     *
+     * @param array<string,mixed> $payload The payload for the API request.
+     * @return array<int|string, mixed>|null The decoded response, or null if the response could not be decoded
+     * or is not an array.
      */
-    protected function uRates(array $payload): mixed
+    protected function uRates(array $payload): ?array
     {
-
         $this->curl->addHeader('Content-Type', 'application/json');
-        $this->curl->post($this->getApiUrl(), json_encode($payload));
+
+        $payloadJson = json_encode($payload);
+        if ($payloadJson === false) {
+            // Handle JSON encoding failure if necessary
+            return null; // or throw an exception
+        }
+
+        $this->curl->post($this->getApiUrl(), $payloadJson);
         $rates = $this->curl->getBody();
 
         $rates = json_decode($rates, true);
-        return $rates;
-    }
 
+        // Ensure that $rates is an array or return null
+        if (is_array($rates)) {
+            return $rates;
+        }
+
+        return null;
+    }
 
     /**
-     * @param string $destStreet
-     * @return string[]
+     * Splits a destination street address into up to three lines if it contains newline characters.
+     *
+     * @param string $destStreet The full street address.
+     * @return string[] An array containing up to three lines of the street address.
      */
     protected function destStreet(string $destStreet): array
     {
@@ -962,29 +1100,42 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
             $destStreet2 = '';
             $destStreet3 = '';
         }
-        return array($destStreet1, $destStreet2, $destStreet3);
+        return [$destStreet1, $destStreet2, $destStreet3];
     }
 
     /**
-     * @param int $min_delivery_date
-     * @param int $max_delivery_date
-     * @param $method
+     * Sets the carrier title with the estimated delivery days range based on minimum and maximum delivery dates.
+     *
+     * @param int|null $min_delivery_date Minimum estimated delivery date in days.
+     * @param int|null $max_delivery_date Maximum estimated delivery date in days.
+     * @param \Magento\Quote\Model\Quote\Address\RateResult\Method $method The shipping method instance
+     * to set the carrier title.
      * @return void
      */
-    protected function deliveryDays(int $min_delivery_date, int $max_delivery_date, $method): void
-    {
+    protected function deliveryDays(
+        ?int $min_delivery_date,
+        ?int $max_delivery_date,
+        \Magento\Quote\Model\Quote\Address\RateResult\Method $method
+    ): void {
+        if ($min_delivery_date === null || $max_delivery_date === null) {
+            return;
+        }
+
         if ($min_delivery_date !== $max_delivery_date) {
-            $method->setCarrierTitle('delivery in ' . $min_delivery_date . ' - ' . $max_delivery_date . ' days');
+            $method->setCarrierTitle('Delivery in ' . $min_delivery_date . ' - ' . $max_delivery_date . ' days');
         } else {
-            $method->setCarrierTitle('delivery in ' . $min_delivery_date . ' days');
             if ($min_delivery_date && $max_delivery_date == 1) {
-                $method->setCarrierTitle('delivery in ' . $min_delivery_date . ' day');
+                $method->setCarrierTitle('Delivery in ' . $min_delivery_date . ' day');
+            } else {
+                $method->setCarrierTitle('Delivery in ' . $min_delivery_date . ' days');
             }
         }
     }
 
     /**
-     * @return mixed|string
+     * Retrieves the destination company name from the additional information.
+     *
+     * @return mixed|string The destination company name.
      */
     public function getDestComp(): mixed
     {
@@ -992,7 +1143,9 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     }
 
     /**
-     * @return mixed|string
+     * Retrieves the destination suburb from the additional information.
+     *
+     * @return mixed|string The destination suburb.
      */
     public function getDestSuburb(): mixed
     {
@@ -1000,60 +1153,39 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
     }
 
     /**
-     * @return mixed|string
-     */
-    public function getDestTelephone(): mixed
-    {
-        return $this->additionalInfo->getDestTelephone();
-    }
-
-    /**
-     * @return mixed|string
-     */
-    public function getCountryName(mixed $country_id): mixed
-    {
-        return $this->additionalInfo->getCountryName($country_id);
-    }
-
-    /**
-     * @param mixed $weightUnit
-     * @param mixed $item
-     * @return float|int
+     * Calculates the item weight in grams based on the provided weight unit.
+     *
+     * @param string $weightUnit The unit of weight, either 'KGS' or another unit (assumed to be pounds).
+     * @param \Magento\Quote\Model\Quote\Item $item The item whose weight is to be calculated.
+     * @return float The weight of the item in grams.
      */
-    public function getItemWeight(mixed $weightUnit, mixed $item): int|float
+    public function getItemWeight(string $weightUnit, \Magento\Quote\Model\Quote\Item $item): float
     {
-        //1 lb = 453.59237 g exact. 1 kg = 1000 g. 1 lb = 0.45359237 kg
-        if ($weightUnit == 'KGS') {
+        // 1 lb = 453.59237 g exact. 1 kg = 1000 g. 1 lb = 0.45359237 kg
+        if ($weightUnit === 'KGS') {
             $mass = $item->getWeight() ? $item->getWeight() * 1000 : 0;
         } else {
-            $mass = $item->getWeight() ? $item->getWeight() * 0.45359237 * 1000 : 0;//Pound to Kilogram Conversion Formula
+            // Pound to Kilogram Conversion Formula
+            $mass = $item->getWeight() ? $item->getWeight() * 0.45359237 * 1000 : 0;
         }
         return $mass;
     }
 
     /**
-     * @param array $items
-     * @param mixed $weightUnit
-     * @param array $itemsArray
-     * @return array
+     * Processes the items in the cart, calculates their weights, and prepares an array of item details.
+     *
+     * @param \Magento\Quote\Model\Quote\Item[] $items The items in the cart.
+     * @param string $weightUnit The unit of weight used for the items.
+     * @param array<int,array<string,mixed>> $itemsArray The array to store the processed item details.
+     * @return array<int, array<string, mixed>> The array containing details of each item,
+     * including SKU, quantity, price, and weight.
      */
-    public function getStoreItems(array $items, mixed $weightUnit, array $itemsArray): array
-    {
+    public function getStoreItems(
+        array $items,
+        string $weightUnit,
+        array $itemsArray
+    ): array {
         foreach ($items as $item) {
-
-            if ($item->getParentItem()) continue;
-
-            $productID = $item->getProductId();
-            $product = $item->getProduct();             // Product Object
-            $description = "";
-
-            if ($product) {
-                $productFull = $product->load($productID);
-                // Remove meta tags from the description
-                $description = preg_replace('#<[^>]+>#', ' ', $productFull->getDescription());
-                $description = preg_replace('!\s+!', ' ', $description);
-            }
-
             $mass = $this->getItemWeight($weightUnit, $item);
 
             $itemsArray[] = [
@@ -1061,19 +1193,19 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
                 'quantity' => $item->getQty(),
                 'price' => $item->getPrice(),
                 'weight' => round($mass),
-                'name' => $item->getName(),
-                'description' => $description,
             ];
-
         }
+
         return $itemsArray;
     }
 
     /**
-     * Get the list of required data fields
-     * @return bool
+     * Checks if the required data fields are present in the request.
+     *
+     * @param \Magento\Framework\DataObject $request The data object containing the request information.
+     * @return bool True if all required fields are present, otherwise false.
      */
-    public function hasRequiredData(DataObject $request): bool
+    public function hasRequiredData(\Magento\Framework\DataObject $request): bool
     {
         $requiredFields = [
             'dest_country_id',
@@ -1087,4 +1219,95 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         }
         return true;
     }
+
+    /**
+     * Trigger a test for rates.
+     *
+     * @return array<int|string, mixed>|bool Returns an array of results or false on failure.
+     */
+    public function triggerRatesTest(): array|bool
+    {
+        // Check if the 'Show rates for checkout' setting is enabled
+        $isEnabled = $this->scopeConfig->getValue(
+            'carriers/bobgo/active',
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+        );
+
+        if ($isEnabled) {
+            // Sample test payload, replace with actual structure
+            $payload = [
+                'identifier' => $this->getBaseUrl(),
+                'rate' => [
+                    'origin' => [
+                        'company' => 'Jamie Ds Emporium',
+                        'address1' => '36 Marelu Street',
+                        'address2' => 'Six Fountains Estate',
+                        'city' => 'Pretoria',
+                        'suburb' => 'Pretoria',
+                        'province' => 'GT',
+                        'country_code' => 'ZA',
+                        'postal_code' => '0081',
+                    ],
+                    'destination' => [
+                        'company' => 'Test Company',
+                        'address1' => '456 Test Ave',
+                        'address2' => '',
+                        'suburb' => 'Test Suburb',
+                        'city' => 'Test City',
+                        'province' => 'Test Province',
+                        'country_code' => 'ZA',
+                        'postal_code' => '3000',
+                    ],
+                    'items' => [
+                        [
+                            'sku' => 'test-sku-1',
+                            'quantity' => 1,
+                            'price' => 100.00,
+                            'weight' => 500, // in grams
+                        ]
+                    ],
+                ]
+            ];
+
+            try {
+                // Perform the API request
+                $payloadJson = json_encode($payload);
+                if ($payloadJson === false) {
+                    throw new \RuntimeException('Failed to encode payload to JSON.');
+                }
+
+                $this->curl->addHeader('Content-Type', 'application/json');
+                $this->curl->post($this->getApiUrl(), $payloadJson);
+                $statusCode = $this->curl->getStatus();
+                $responseBody = $this->curl->getBody();
+
+                // Decode the response
+                $response = json_decode($responseBody, true);
+
+                if (!is_array($response)) {
+                    throw new LocalizedException(__('Invalid response format.'));
+                }
+
+                // Check if the response contains a 'message' (indicating an error)
+                if (isset($response['message'])) {
+                    throw new LocalizedException(__('Error from BobGo: %1', $response['message']));
+                }
+
+                // Check if the response contains rates with a valid id field
+                if (isset($response['rates']) && is_array($response['rates']) && !empty($response['rates'])) {
+                    foreach ($response['rates'] as $rate) {
+                        if (isset($rate['id']) && $rate['id'] !== null) {
+                            return $response; // Successful response with a valid id
+                        }
+                    }
+                    throw new LocalizedException(__('Rates received but id field is empty or invalid.'));
+                } else {
+                    throw new LocalizedException(__('Received response but no valid rates were found.'));
+                }
+            } catch (\Exception $e) {
+                return false;
+            }
+        }
+        return false;
+    }
 }
diff --git a/Model/Carrier/uData.php b/Model/Carrier/uData.php
index df3a1686e3fd01edd219516128a84d8b410cd56d..47b5d8b050f519a4e96f2ae432f3a0ea6ab70e0d 100644
--- a/Model/Carrier/uData.php
+++ b/Model/Carrier/uData.php
@@ -3,15 +3,21 @@
 namespace BobGroup\BobGo\Model\Carrier;
 
 /**
- * Class uData
- * @package BobGroup\BobGo\Model\Carrier
+ * Data class for managing API endpoints in the BobGo module.
  */
-class uData
+class UData
 {
+    /**
+     * Tracking Endpoint
+     *
+     * @var string
+     */
+    public const TRACKING = 'https://api.bobgo.co.za/tracking?channel=%s&tracking_reference=%s';
 
-    /** Tracking Endpoint */
-    public const TRACKING = 'https://api.dev.bobgo.co.za/tracking?channel=%s&tracking_reference=%s';
-
-    /*** RATES API Endpoint*/
-    public const RATES_ENDPOINT = 'https://api.dev.bobgo.co.za/rates-at-checkout/magento';
+    /**
+     * Rates API Endpoint
+     *
+     * @var string
+     */
+    public const RATES_ENDPOINT = 'https://api.bobgo.co.za/rates-at-checkout/magento';
 }
diff --git a/Model/Carrier/uSubs.php b/Model/Carrier/uSubs.php
index 5f7dcd8cd5835cd0a38d14e07f69776c02f6048f..da714ee7084762c424c47fac6b2653f563c282ab 100644
--- a/Model/Carrier/uSubs.php
+++ b/Model/Carrier/uSubs.php
@@ -2,22 +2,53 @@
 
 namespace BobGroup\BobGo\Model\Carrier;
 
-/** Get AdditionalInfo information if available from the Estimate Shipping Methods Request Body */
-class uSubs
+use Magento\Framework\App\Request\Http;
+
+/**
+ * Handles the retrieval of company information from the Estimate Shipping Methods request body.
+ */
+class USubs
 {
+    /**
+     * @var Http
+     */
+    protected $request;
+
+    /**
+     * Constructor
+     *
+     * @param Http $request
+     */
+    public function __construct(Http $request)
+    {
+        $this->request = $request;
+    }
 
     /**
-     * @return mixed|string
+     * Retrieve the destination company from the request body
+     *
+     * @return string
      */
-    public function getDestComp(): mixed
+    public function getDestComp(): string
     {
-        $data = json_decode(file_get_contents('php://input'), true);
+        $data = json_decode($this->getRequestBody(), true);
 
-        if (isset($data['address']['company'])) {
-            $destComp = $data['address']['company'];
-        } else {
-            $destComp = '';
+        // Ensure that $data is an array and has the expected structure
+        if (is_array($data) && isset($data['address']) && is_array($data['address'])
+            && isset($data['address']['company'])) {
+            return $data['address']['company'];
         }
-        return $destComp;
+
+        return '';
+    }
+
+    /**
+     * Retrieve the raw request body
+     *
+     * @return string
+     */
+    private function getRequestBody(): string
+    {
+        return $this->request->getContent();
     }
 }
diff --git a/Model/Source/Freemethod.php b/Model/Source/Freemethod.php
index 674f68706a72978fd580ecb1f6ebd157e78b9bfc..fa98d988d36ba4543b979c4af49d351628f6a599 100644
--- a/Model/Source/Freemethod.php
+++ b/Model/Source/Freemethod.php
@@ -8,11 +8,14 @@ namespace BobGroup\BobGo\Model\Source;
 class Freemethod extends Method
 {
     /**
-     * {@inheritdoc}
+     * Returns an array of options for the free method.
+     *
+     * @return array<int, array<string, string>>
      */
-    public function toOptionArray()
+    public function toOptionArray(): array
     {
-        //Returns an array of arrays, each of which has a 'value' and a 'label'. The 'value' is the code for the shipping method, and the 'label' is the name of the shipping method.
+        // Returns an array of arrays, each of which has a 'value' and a 'label'.
+        // The 'value' is the code for the shipping method, and the 'label' is the name of the shipping method.
         $arr = parent::toOptionArray();
         array_unshift($arr, ['value' => '', 'label' => __('None')]);
         return $arr;
diff --git a/Model/Source/Generic.php b/Model/Source/Generic.php
index f5eb3943cd1c7c0f71cc2af44e6361fb0adb1006..7ccb53caa0d3f5d8e9ba74ca756486553da2467a 100644
--- a/Model/Source/Generic.php
+++ b/Model/Source/Generic.php
@@ -2,12 +2,11 @@
 
 namespace BobGroup\BobGo\Model\Source;
 
-
 use Magento\Framework\Data\OptionSourceInterface;
 use BobGroup\BobGo\Model\Carrier\BobGo;
 
 /**
- * bobgo generic source implementation
+ * BobGo generic source implementation.
  */
 class Generic implements OptionSourceInterface
 {
@@ -18,6 +17,7 @@ class Generic implements OptionSourceInterface
 
     /**
      * Carrier code
+     *
      * @var string
      */
     protected string $_code = '';
@@ -31,19 +31,21 @@ class Generic implements OptionSourceInterface
     }
 
     /**
-     * Returns array to be used in multiselect on back-end
-     * @return array
+     * Returns array to be used in multiselect on back-end.
+     *
+     * @return array<int, array<string, string>>
      */
-    public function toOptionArray()
+    public function toOptionArray(): array
     {
         $configData = $this->_shippingBobGo->getCode($this->_code);
         $arr = [];
-        if ($configData) {
+
+        if (is_array($configData)) {
             $arr = array_map(
-                function ($code, $title) {
+                function ($code, $title): array {
                     return [
-                        'value' => $code,
-                        'label' => $title
+                        'value' => (string) $code,
+                        'label' => (string) $title
                     ];
                 },
                 array_keys($configData),
@@ -53,5 +55,4 @@ class Generic implements OptionSourceInterface
 
         return $arr;
     }
-
 }
diff --git a/Model/Source/Unitofmeasure.php b/Model/Source/Unitofmeasure.php
index be8d13b0455812d4f308f45a235ab4888eb19aa5..0d77d95b7094f7a95c1bb475742860e3647b7989 100644
--- a/Model/Source/Unitofmeasure.php
+++ b/Model/Source/Unitofmeasure.php
@@ -9,5 +9,4 @@ class Unitofmeasure extends Generic
      * @var string
      */
     protected string $_code = 'unit_of_measure';
-
 }
diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b6c07214501db7cd9ba19dc325fe3bbade643a5
--- /dev/null
+++ b/Observer/ConfigChangeObserver.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace BobGroup\BobGo\Observer;
+
+use Magento\Framework\Event\Observer;
+use Magento\Framework\Event\ObserverInterface;
+use Magento\Framework\Message\ManagerInterface;
+use Psr\Log\LoggerInterface;
+use BobGroup\BobGo\Model\Carrier\BobGo;
+
+class ConfigChangeObserver implements ObserverInterface
+{
+    /**
+     * @var BobGo
+     */
+    protected $bobGo;
+
+    /**
+     * @var LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * @var ManagerInterface
+     */
+    protected $messageManager;
+
+    /**
+     * Constructor
+     *
+     * @param BobGo $bobGo
+     * @param LoggerInterface $logger
+     * @param ManagerInterface $messageManager
+     */
+    public function __construct(
+        BobGo $bobGo,
+        LoggerInterface $logger,
+        ManagerInterface $messageManager
+    ) {
+        $this->bobGo = $bobGo;
+        $this->logger = $logger;
+        $this->messageManager = $messageManager;
+    }
+
+    /**
+     * Execute observer to handle configuration changes.
+     *
+     * @param Observer $observer
+     * @return void
+     */
+    public function execute(Observer $observer)
+    {
+        $changedPaths = $observer->getEvent()->getData('changed_paths');
+
+        if (is_array($changedPaths) && in_array('carriers/bobgo/active', $changedPaths)) {
+            if ($this->bobGo->isActive()) {
+                $result = $this->bobGo->triggerRatesTest();
+
+                if ($result !== false) {
+                    $this->messageManager->addSuccessMessage(
+                        __('Bob Go rates at checkout connected.')
+                    );
+                } else {
+                    $this->messageManager->addErrorMessage(
+                        __('Failed to connect to rates at checkout. Please check your internet connection
+                        and make sure Rates at checkout is enabled for your channel on Bob Go. Please visit Bob Go
+                        settings page to make sure your Magento channel is enabled to receive rates.
+                        https://my.bobgo.co.za/rates-at-checkout?tab=settings')
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/Plugin/.DS_Store b/Plugin/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..689f74aebb9f0ea93d13c40a72825a9e06df27b5
Binary files /dev/null and b/Plugin/.DS_Store differ
diff --git a/Plugin/AddWeightUnitToOrderPlugin.php b/Plugin/AddWeightUnitToOrderPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..f339b5dbb59ead50a28193adc090c3b5974d6079
--- /dev/null
+++ b/Plugin/AddWeightUnitToOrderPlugin.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace BobGroup\BobGo\Plugin;
+
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Psr\Log\LoggerInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Sales\Api\Data\OrderItemInterface;
+
+class AddWeightUnitToOrderPlugin
+{
+    /**
+     * @var LoggerInterface
+     */
+    protected $logger;
+
+    /**
+     * @var ScopeConfigInterface
+     */
+    protected $scopeConfig;
+
+    /**
+     * Constructor
+     *
+     * @param LoggerInterface $logger
+     * @param ScopeConfigInterface $scopeConfig
+     */
+    public function __construct(
+        LoggerInterface $logger,
+        ScopeConfigInterface $scopeConfig
+    ) {
+        $this->logger = $logger;
+        $this->scopeConfig = $scopeConfig;
+    }
+
+    /**
+     * Before save plugin to modify order items' weight based on the configured weight unit.
+     *
+     * @param OrderRepositoryInterface $subject
+     * @param OrderInterface $order
+     * @return array{0: OrderInterface}
+     */
+    public function beforeSave(
+        OrderRepositoryInterface $subject,
+        OrderInterface $order
+    ): array {
+        $weightUnit = $this->scopeConfig->getValue(
+            'general/locale/weight_unit',
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+        );
+
+        if ($weightUnit === 'lbs') {
+            foreach ($order->getItems() as $orderItem) {
+                // Get the current weight of the item
+                $weight = $orderItem->getWeight();
+
+                // Convert weight from lbs to kg
+                $convertedWeight = $weight * 0.45359237;
+
+                // Set the converted weight back to the item using the correct setter method
+                $orderItem->setWeight($convertedWeight);
+
+                // Assuming you want to store this in a custom field, you should add a custom attribute
+                // If you are using a custom attribute, ensure that it’s correctly added to the OrderItemInterface
+                // $orderItem->setData('custom_weight_attribute', $convertedWeight);
+            }
+        }
+
+        return [$order];
+    }
+}
diff --git a/Plugin/Block/.DS_Store b/Plugin/Block/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..17a3f4bbd7cabc8ac14985439ccbfd116d21bb32
Binary files /dev/null and b/Plugin/Block/.DS_Store differ
diff --git a/Plugin/Block/DataProviders/Tracking/ChangeTitle.php b/Plugin/Block/DataProviders/Tracking/ChangeTitle.php
index 03d6b4c431d49ef9588960332a9e4213a815173e..a035d238ef81b90a649f0b7ae33541f6b7c6e7ee 100644
--- a/Plugin/Block/DataProviders/Tracking/ChangeTitle.php
+++ b/Plugin/Block/DataProviders/Tracking/ChangeTitle.php
@@ -6,7 +6,6 @@ use BobGroup\BobGo\Model\Carrier;
 use Magento\Shipping\Model\Tracking\Result\Status;
 use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject;
 
-
 /**
  * Plugin to change delivery date title with bobgo customized value
  */
@@ -24,8 +23,8 @@ class ChangeTitle
      */
     public function afterGetTitle(Subject $subject, $result, Status $trackingStatus)
     {
-        if ($trackingStatus->getCarrier() === Carrier::CODE) {
-            $result = __('Expected Delivery:');
+        if ($trackingStatus->getCarrier() === \BobGroup\BobGo\Model\Carrier\BobGo::CODE) {
+            $result = __('Expected delivery:');
         }
         return $result;
     }
diff --git a/Plugin/Block/Tracking/PopUpDeliveryDate.php b/Plugin/Block/Tracking/PopUpDeliveryDate.php
index 18c07ebf94298a5a8703f3f80f3efe5cb44e0f01..bb375285de7c84f6104c1b4014689803c4e9365f 100644
--- a/Plugin/Block/Tracking/PopUpDeliveryDate.php
+++ b/Plugin/Block/Tracking/PopUpDeliveryDate.php
@@ -4,13 +4,17 @@ namespace BobGroup\BobGo\Plugin\Block\Tracking;
 
 use Magento\Shipping\Block\Tracking\Popup;
 use Magento\Shipping\Model\Tracking\Result\Status;
-use Magento\Shipping\Model\Carrier;
 
 /*
  * Plugin to update delivery date value in case if Bob Go is a carrier used
  */
 class PopupDeliveryDate
 {
+    /**
+     * Bob Go carrier code
+     */
+    private const BOB_GO_CARRIER_CODE = 'bobgo_carrier_code'; // Replace with your actual carrier code
+
     /**
      * Show only date for expected delivery in case if Bob Go is a carrier
      *
@@ -23,7 +27,7 @@ class PopupDeliveryDate
      */
     public function afterFormatDeliveryDateTime(Popup $subject, $result, $date, $time)
     {
-        if ($this->getCarrier($subject) === Carrier::CODE) {
+        if ($this->getCarrier($subject) === self::BOB_GO_CARRIER_CODE) {
             $result = $subject->formatDeliveryDate($date);
         }
         return $result;
@@ -40,7 +44,7 @@ class PopupDeliveryDate
         foreach ($subject->getTrackingInfo() as $trackingData) {
             foreach ($trackingData as $trackingInfo) {
                 if ($trackingInfo instanceof Status) {
-                    return $trackingInfo->getCarrier();
+                    return $trackingInfo->getCarrier() ?? '';
                 }
             }
         }
diff --git a/Plugin/Checkout/Block/LayoutProcessorPlugin.php b/Plugin/Checkout/Block/LayoutProcessorPlugin.php
index 1c32fb6afc4d8e9b0956ec0534ad9adc237f88ab..d5d302a7f298e317302e50b96b1932e0ccfe7736 100644
--- a/Plugin/Checkout/Block/LayoutProcessorPlugin.php
+++ b/Plugin/Checkout/Block/LayoutProcessorPlugin.php
@@ -1,54 +1,91 @@
 <?php
+
 namespace BobGroup\BobGo\Plugin\Checkout\Block;
 
 use Magento\Checkout\Block\Checkout\LayoutProcessor;
 
 /**
  * Class LayoutProcessorPlugin
- * @package BobGroup\BobGo\Plugin\Checkout\Block
- * This class is supposed to add a new field to the checkout page for the suburb. It is supposed to be used in conjunction with the SaveOrderBeforeSalesModelQuote observer.
- * At the moment, it overrides the 2 Address fields and adds a new 3 Address field to accommodate the suburb on the checkout page( including placeholders).
- * This is not the best way to do it, but it is the only way I could get it to work.
+ * Adds a new field to the checkout page for the suburb.
+ *
+ * This class is used in conjunction with the SaveOrderBeforeSalesModelQuote observer.
+ * It overrides the 2 Address fields and adds a new 3rd Address field to accommodate the suburb
+ * on the checkout page (including placeholders).
+ * Note: This implementation, though functional, may not be the most optimal way.
  */
 class LayoutProcessorPlugin
 {
     /**
-     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
-     * @param array $jsLayout
-     * @return array
+     * Modify checkout layout to add suburb field.
+     *
+     * @param LayoutProcessor $subject The subject being processed.
+     * @param array<string,mixed> $jsLayout The JS layout array to be modified.
+     * @return array<string, mixed> The modified JS layout array.
      */
-
     public function afterProcess(
-        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
-        array  $jsLayout
-    ) {
+        LayoutProcessor $subject,
+        array $jsLayout
+    ): array {
         $suburbAttribute = 'suburb';
-        $suburb = [
-            'component' => 'Magento_Ui/js/form/element/abstract',
-            'config' => [
-                'customScope' => 'shippingAddress.custom_attributes',
-                'customEntry' => null,
-                'template' => 'ui/form/field',
-                'elementTmpl' => 'ui/form/element/input',
-                'tooltip' => [
-                    'description' => 'Required for shipping accuracy',
-                ],
-            ],
-            'dataScope' => 'shippingAddress.custom_attributes' . '.' . $suburbAttribute,
-            'label' => 'Suburb',
-            'provider' => 'checkoutProvider',
-            'sortOrder' => 80,
-            'validation' => [
-                'required-entry' => true
-            ],
-            'options' => [],
-            'filterBy' => null,
-            'customEntry' => null,
-            'visible' => true,
-            'value' => '' // value field is used to set a default value of the attribute
-        ];
 
-        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$suburbAttribute] = $suburb;
+        if (isset($jsLayout['components'])
+            && is_array($jsLayout['components'])
+            && isset($jsLayout['components']['checkout'])
+            && is_array($jsLayout['components']['checkout'])
+            && isset($jsLayout['components']['checkout']['children'])
+            && is_array($jsLayout['components']['checkout']['children'])
+            && isset($jsLayout['components']['checkout']['children']['steps'])
+            && is_array($jsLayout['components']['checkout']['children']['steps'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
+            ['children'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
+                ['shippingAddress'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
+            ['children']['shippingAddress'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
+                ['shippingAddress']['children'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
+            ['children']['shippingAddress']['children'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
+                ['shippingAddress']['children']['shipping-address-fieldset'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
+            ['children']['shippingAddress']['children']['shipping-address-fieldset'])
+            && isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
+                ['shippingAddress']['children']['shipping-address-fieldset']['children'])
+            && is_array($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']
+            ['children']['shippingAddress']['children']['shipping-address-fieldset']['children'])) {
+
+            $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
+            ['shippingAddress']['children']['shipping-address-fieldset']['children'][$suburbAttribute] = [
+                'component' => 'Magento_Ui/js/form/element/abstract',
+                'config' => [
+                    'customScope' => 'shippingAddress.custom_attributes',
+                    'customEntry' => null,
+                    'template' => 'ui/form/field',
+                    'elementTmpl' => 'ui/form/element/input',
+                    'tooltip' => [
+                        'description' => 'Required for shipping accuracy',
+                    ],
+                ],
+                'dataScope' => 'shippingAddress.custom_attributes' . '.' . $suburbAttribute,
+                'label' => 'Suburb',
+                'provider' => 'checkoutProvider',
+                'sortOrder' => 80,
+                'validation' => [
+                    'required-entry' => true,
+                ],
+                'options' => [],
+                'filterBy' => null,
+                'customEntry' => null,
+                'visible' => true,
+                'value' => '' // Default value for the attribute
+            ];
+        }
 
         return $jsLayout;
     }
diff --git a/Readme.md b/Readme.md
index 7d691668472e29f39a24c24755fd7152166e1d11..b40426b67c4477173338def0a353a4fa418372cc 100644
--- a/Readme.md
+++ b/Readme.md
@@ -15,7 +15,7 @@ A complete guide to install Magento Bob Go Shipping extension in Magento 2.
 
 Run the following command in Magento 2 root folder:</br>
 
->_Note: You must have composer installed on your server & at this point this option_
+>_Note: You must have composer installed on your server for this option_
 
 #### 1. Execute the following command to install the module:
 
@@ -37,7 +37,7 @@ bin/magento setup:static-content:deploy
 
 1. Download the extension zip file from the link below: </br>
 
-    <a href="https://gitlab.bob.co.za/bobgo/bobgo-magento-extension/-/archive/main/bobgo-magento-extension-main.zip"> Download Magento 2 Bob Go Shipping Extension </a>
+    <a href="https://gitlab.bob.co.za/bob-public-utils/bobgo-magento-extension/-/archive/prod/bobgo-magento-extension-prod.zip"> Download Magento 2 Bob Go Shipping Extension </a>
 
 2. Unzip the file and copy contents
 
@@ -66,62 +66,33 @@ ________________________________________________________________________________
 
 ### ✓ Step 1: Create an account on Bob Go
 
-You need to create an account on Bob Go to get your Store Identified by the API. 
+You need to create an account on Bob Go to get your store identified by the API. 
 
 Please visit [Bob Go](https://bobgo.co.za) to create an account.
 
-### ✓ Step 2: Login to Magento Admin
+### ✓ Step 2: Integrate Magento and Bob Go
 
-1. Click on **Bob Go** > Bob Go > Enabled for Checkout > Yes </br>
-
-
-2. and go to `Stores > Configuration > Sales > Delivery Methods` to configure the extension.
-
-### ✓ Step 3: Configure Bob Go Shipping Extension
-
-
-1. Select `Bob Go` as shipping method.
-
-
-2. Select `Enable` to enable the extension.
+1. In the Magento admin portal click on `System > Integrations` (At this point Bob Go should be under Magento integrations)
+2. Click to edit the Bob Go integration in the magento admin portal
+3. Under Basic settings select `API`
+4. Make sure `All` is selected for `Resource Access`
+5. Click `Save` and enter your Magento admin portal password
+6. The `Integration Details` will be displayed at the bottom of the page
+8. On [Bob Go](https://bobgo.co.za) go to `Sales channels > Add channel > Magento` and enter the Magento integration details from step 6 and click `Grant access`
+9. On Bob Go go to `Rates at checkout > Settings > Installed channels` and enable your channel for rates at checkout
+10. Make sure you have service levels configured and enabled on Bob Go `Rates at checkout > Service levels`
 
+### ✓ Step 3: Login to Magento Admin
 
+1. In the Magento admin portal click on `Stores > Configuration > Sales > Delivery Methods` 
+2. Go to the Bob Go delivery method and enable Bob Go rates at checkout
 3.  Click `Save Config`
-
-
-### ✓ Step 4: Address Configurations
-#### Admin Configurations: Update Customer Address Fields*
-1. `Stores`>`Configurations`>`Customers`
-2. `Customer Configuration`>`Name and Address Options`
-3. In Input Field `Number of Lines in a Street Address` 
-4. Disable System Value `2` > 
-5. Lastly ,Change to `3` Lines.
+4. A message at the top of the screen will confirm that rates at checkout is enabled correctly: `Bob Go rates at checkout connected.`
 
 #### Admin Configurations: Update Store Information*
 
-When the extension is **installed** and **enabled**, a new field will be created in the `Store Information` : `Suburb` and Fill in necessary information;
+When the extension is **installed** and **enabled**, a new field will be created in `Store Information` : `Suburb`. Enter the necessary details:
 
 1. `Stores`> `Configuration`> `General`>`General`
-
 2. `Store Information`>`Suburb`
-
-## How it works
-
-## How to use Magento 2 Bob Go Shipping Extension (carrier) to Ship Orders
-
-### ✓ Step 1: Add products to cart(Checkout)
-
->1. Go to checkout page.
->2. Select shipping address.
->3. Bob Go will collect **Shipping Rates** From various couriers.
->4. Select shipping method.
->5. Place order.
-
-### ✓ Step 1: Create shipment (Admin)
-`Sales > Orders > View Order > Ship`
-
->1. Go to `Sales > Orders` in Magento Admin
->2. Select an order
->3. Click `Ship` button
->4. Select `Bob Go` as shipping method
->5. Click `Submit Shipment` button
+3. Update the store information and click `Save config`
diff --git a/Test/Unit/Block/System/Config/Form/Field/VersionTest.php b/Test/Unit/Block/System/Config/Form/Field/VersionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ad57e650ae7aa8d077d0755982bb19e8291f4317
--- /dev/null
+++ b/Test/Unit/Block/System/Config/Form/Field/VersionTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Block\System\Config\Form\Field;
+
+use BobGroup\BobGo\Block\System\Config\Form\Field\Version;
+use BobGroup\BobGo\Helper\Data;
+use Magento\Framework\Data\Form\Element\AbstractElement;
+use Magento\Backend\Block\Template\Context;
+use PHPUnit\Framework\TestCase;
+
+class VersionTest extends TestCase
+{
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $helperMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $contextMock;
+
+    /**
+     * @var Version
+     */
+    private $versionBlock;
+
+    protected function setUp(): void
+    {
+        $this->helperMock = $this->createMock(Data::class);
+        $this->contextMock = $this->createMock(Context::class);
+
+        // Instantiate the Version block
+        $this->versionBlock = new Version($this->contextMock, $this->helperMock);
+    }
+
+    /**
+     * @param object $object
+     * @param string $methodName
+     * @param array<int, mixed> $parameters
+     * @return mixed
+     */
+    private function callProtectedMethod(object $object, string $methodName, array $parameters = [])
+    {
+        $reflection = new \ReflectionClass($object);
+        $method = $reflection->getMethod($methodName);
+        $method->setAccessible(true);
+
+        return $method->invokeArgs($object, $parameters);
+    }
+
+    public function testGetElementHtml(): void
+    {
+        // Mock the AbstractElement
+        $elementMock = $this->createMock(AbstractElement::class);
+
+        // Set up the helper mock to return a specific version
+        $this->helperMock->method('getExtensionVersion')->willReturn('1.0.0');
+
+        // Set the expectation for the setData method to set the 'value'
+        $elementMock->expects($this->once())
+            ->method('setData')
+            ->with('value', '<a href="https://www.bobgo.co.za" title="BobGo" target="_blank">1.0.0</a>');
+
+        // Mock the getData method to return the value that was set
+        $elementMock->method('getData')
+            ->with('value')
+            ->willReturn('<a href="https://www.bobgo.co.za" title="BobGo" target="_blank">1.0.0</a>');
+
+        // Call the protected _getElementHtml method using reflection
+        $result = $this->callProtectedMethod($this->versionBlock, '_getElementHtml', [$elementMock]);
+
+        // Assert that the result is the expected HTML string
+        $expectedHtml = '<a href="https://www.bobgo.co.za" title="BobGo" target="_blank">1.0.0</a>';
+        $this->assertEquals($expectedHtml, $result);
+    }
+
+    public function testGetElementHtmlWithNonStringValue(): void
+    {
+        // Mock the AbstractElement
+        $elementMock = $this->createMock(AbstractElement::class);
+
+        // Set up the helper mock to return a specific version
+        $this->helperMock->method('getExtensionVersion')->willReturn('1.0.0');
+
+        // Return a non-string value for the 'value' key
+        $elementMock->method('getData')
+            ->with('value')
+            ->willReturn(['not_a_string']);
+
+        // Call the protected _getElementHtml method using reflection
+        $result = $this->callProtectedMethod($this->versionBlock, '_getElementHtml', [$elementMock]);
+
+        // Assert that the result is an empty string
+        $this->assertEquals('', $result);
+    }
+}
diff --git a/Test/Unit/Helper/DataTest.php b/Test/Unit/Helper/DataTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca45a68a744fcd77185afac7bf655905f75a8e38
--- /dev/null
+++ b/Test/Unit/Helper/DataTest.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Helper;
+
+use BobGroup\BobGo\Helper\Data;
+use Magento\Framework\App\Helper\Context;
+use Magento\Framework\Module\ModuleListInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Store\Model\ScopeInterface;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class DataTest extends TestCase
+{
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $contextMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $moduleListMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $scopeConfigMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $loggerMock;
+
+    /**
+     * @var Data
+     */
+    private $helper;
+
+    protected function setUp(): void
+    {
+        $this->contextMock = $this->createMock(Context::class);
+        $this->moduleListMock = $this->createMock(ModuleListInterface::class);
+        $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+
+        $this->contextMock->method('getLogger')->willReturn($this->loggerMock);
+        $this->contextMock->method('getScopeConfig')->willReturn($this->scopeConfigMock);
+
+        // Instantiate the Data helper
+        $this->helper = new Data($this->contextMock, $this->moduleListMock);
+    }
+
+    public function testIsEnabled(): void
+    {
+        // Mock the scopeConfig to return '1' when checking if the module is enabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE)
+            ->willReturn('1');
+
+        $result = $this->helper->isEnabled();
+        $this->assertEquals('1', $result);
+    }
+
+    public function testIsEnabledReturnsNullWhenDisabled(): void
+    {
+        // Mock the scopeConfig to return null when the module is disabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_ENABLED, ScopeInterface::SCOPE_STORE)
+            ->willReturn(null);
+
+        $result = $this->helper->isEnabled();
+        $this->assertNull($result);
+    }
+
+    public function testGetDebugStatus(): void
+    {
+        // Mock the scopeConfig to return '1' when checking if debug is enabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE)
+            ->willReturn('1');
+
+        $result = $this->helper->getDebugStatus();
+        $this->assertEquals('1', $result);
+    }
+
+    public function testGetDebugStatusReturnsNullWhenDisabled(): void
+    {
+        // Mock the scopeConfig to return null when debug is disabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE)
+            ->willReturn(null);
+
+        $result = $this->helper->getDebugStatus();
+        $this->assertNull($result);
+    }
+
+    public function testGetExtensionVersion(): void
+    {
+        // Mock the moduleList to return a specific version
+        $this->moduleListMock->method('getOne')
+            ->with('BobGroup_BobGo')
+            ->willReturn(['setup_version' => '1.2.3']);
+
+        $result = $this->helper->getExtensionVersion();
+        $this->assertEquals('1.2.3', $result);
+    }
+
+    public function testGetExtensionVersionReturnsNAWhenModuleNotFound(): void
+    {
+        // Mock the moduleList to return null (module not found)
+        $this->moduleListMock->method('getOne')
+            ->with('BobGroup_BobGo')
+            ->willReturn(null);
+
+        $result = $this->helper->getExtensionVersion();
+        $this->assertEquals('N/A', $result);
+    }
+
+    public function testLogWithDebugEnabled(): void
+    {
+        // Mock the scopeConfig to return '1' when checking if debug is enabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE)
+            ->willReturn('1');
+
+        // Expect the logger to be called with a specific message
+        $this->loggerMock->expects($this->once())
+            ->method('debug')
+            ->with('Test message');
+
+        $this->helper->log('Test message');
+    }
+
+    public function testLogWithDebugDisabled(): void
+    {
+        // Mock the scopeConfig to return null when debug is disabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE)
+            ->willReturn(null);
+
+        // Expect the logger not to be called
+        $this->loggerMock->expects($this->never())
+            ->method('debug');
+
+        $this->helper->log('Test message');
+    }
+
+    public function testLogWithSeparator(): void
+    {
+        // Mock the scopeConfig to return '1' when checking if debug is enabled
+        $this->scopeConfigMock->method('getValue')
+            ->with(Data::XML_PATH_DEBUG, ScopeInterface::SCOPE_STORE)
+            ->willReturn('1');
+
+        // Expect the logger to be called with a separator and the message
+        $this->loggerMock->expects($this->exactly(2))
+            ->method('debug')
+            ->withConsecutive(
+                [str_repeat('=', 100)],
+                ['Test message']
+            );
+
+        $this->helper->log('Test message', true);
+    }
+}
diff --git a/Test/Unit/Model/Carrier/AdditionalInfoTest.php b/Test/Unit/Model/Carrier/AdditionalInfoTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c143753a95f45dfba0234fac4d60dff151fdcdc
--- /dev/null
+++ b/Test/Unit/Model/Carrier/AdditionalInfoTest.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Model\Carrier;
+
+use BobGroup\BobGo\Model\Carrier\AdditionalInfo;
+use Magento\Framework\App\Request\Http;
+use Magento\Directory\Model\CountryFactory;
+use Magento\Directory\Model\Country;
+use PHPUnit\Framework\TestCase;
+
+class AdditionalInfoTest extends TestCase
+{
+    /** @var AdditionalInfo */
+    private $additionalInfo;
+
+    /** @var Http|\PHPUnit\Framework\MockObject\MockObject */
+    private $requestMock;
+
+    /** @var CountryFactory|\PHPUnit\Framework\MockObject\MockObject */
+    private $countryFactoryMock;
+
+    /** @var Country|\PHPUnit\Framework\MockObject\MockObject */
+    private $countryMock;
+
+    protected function setUp(): void
+    {
+        $this->requestMock = $this->createMock(Http::class);
+        $this->countryFactoryMock = $this->createMock(CountryFactory::class);
+        $this->countryMock = $this->createMock(Country::class);
+
+        $this->countryFactoryMock->method('create')
+            ->willReturn($this->countryMock);
+
+        $this->countryMock->method('loadByCode')
+            ->willReturn($this->countryMock);
+
+        $this->additionalInfo = new AdditionalInfo($this->countryFactoryMock, $this->requestMock);
+    }
+
+    public function testGetCountryName(): void
+    {
+        $countryId = 'US';
+
+        $this->countryMock->method('getName')
+            ->willReturn('United States');
+
+        $result = $this->additionalInfo->getCountryName($countryId);
+
+        $this->assertEquals('United States', $result);
+    }
+
+    public function testGetDestComp(): void
+    {
+        $requestBody = json_encode([
+            'address' => [
+                'company' => 'Test Company'
+            ]
+        ]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->additionalInfo->getDestComp();
+
+        $this->assertEquals('Test Company', $result);
+    }
+
+    public function testGetDestCompReturnsEmptyStringWhenNotSet(): void
+    {
+        $requestBody = json_encode([]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->additionalInfo->getDestComp();
+
+        $this->assertEquals('', $result);
+    }
+
+    public function testGetSuburb(): void
+    {
+        $requestBody = json_encode([
+            'address' => [
+                'custom_attributes' => [
+                    ['value' => 'Test Suburb']
+                ]
+            ]
+        ]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->additionalInfo->getSuburb();
+
+        $this->assertEquals('Test Suburb', $result);
+    }
+
+    public function testGetSuburbReturnsEmptyStringWhenNotSet(): void
+    {
+        $requestBody = json_encode([]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->additionalInfo->getSuburb();
+
+        $this->assertEquals('', $result);
+    }
+
+    public function testGetDestTelephone(): void
+    {
+        $requestBody = json_encode([
+            'address' => [
+                'telephone' => '123456789'
+            ]
+        ]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->additionalInfo->getDestTelephone();
+
+        $this->assertEquals('123456789', $result);
+    }
+
+    public function testGetDestTelephoneReturnsEmptyStringWhenNotSet(): void
+    {
+        $requestBody = json_encode([]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->additionalInfo->getDestTelephone();
+
+        $this->assertEquals('', $result);
+    }
+}
diff --git a/Test/Unit/Model/Carrier/BobGoTest.php b/Test/Unit/Model/Carrier/BobGoTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a40c30247f1825ac373fab6db0b7042dab157c69
--- /dev/null
+++ b/Test/Unit/Model/Carrier/BobGoTest.php
@@ -0,0 +1,201 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Model\Carrier;
+
+use BobGroup\BobGo\Model\Carrier\BobGo;
+use BobGroup\BobGo\Model\Carrier\AdditionalInfo;
+use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
+use Magento\CatalogInventory\Api\StockRegistryInterface;
+use Magento\Directory\Helper\Data;
+use Magento\Directory\Model\CountryFactory;
+use Magento\Directory\Model\CurrencyFactory;
+use Magento\Directory\Model\RegionFactory;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\Request\Http as MagentoHttp;
+use Magento\Framework\Controller\Result\JsonFactory;
+use Magento\Framework\HTTP\Client\Curl;
+use Magento\Framework\HTTP\Client\CurlFactory;
+use Magento\Framework\Xml\Security;
+use Magento\Quote\Model\Quote\Address\RateRequest;  // Correct class reference
+use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
+use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
+use Magento\Shipping\Model\Rate\ResultFactory;
+use Magento\Shipping\Model\Tracking\Result\StatusFactory;
+use Magento\Store\Model\ScopeInterface;
+use Magento\Store\Model\StoreManagerInterface;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class BobGoTest extends TestCase
+{
+    /** @var BobGo */
+    private $bobGo;
+
+    /** @var StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */
+    private $storeManagerMock;
+
+    /** @var ScopeConfigInterface|\PHPUnit\Framework\MockObject\MockObject */
+    private $scopeConfigMock;
+
+    /** @var Curl|\PHPUnit\Framework\MockObject\MockObject */
+    private $curlMock;
+
+    /** @var ResultFactory|\PHPUnit\Framework\MockObject\MockObject */
+    private $resultFactoryMock;
+
+    /** @var MethodFactory|\PHPUnit\Framework\MockObject\MockObject */
+    private $methodFactoryMock;
+
+    /** @var AdditionalInfo|\PHPUnit\Framework\MockObject\MockObject */
+    private $additionalInfoMock;
+
+    protected function setUp(): void
+    {
+        // Create mock objects for all dependencies
+        $this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
+        $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
+        $this->curlMock = $this->createMock(Curl::class); // Ensure Curl mock is initialized
+        $this->resultFactoryMock = $this->createMock(ResultFactory::class);
+        $this->methodFactoryMock = $this->createMock(MethodFactory::class);
+        $this->additionalInfoMock = $this->createMock(AdditionalInfo::class); // Correctly mock AdditionalInfo
+
+        // Create mock objects for other dependencies that aren't used directly
+        $jsonFactoryMock = $this->createMock(JsonFactory::class);
+        $rateErrorFactoryMock = $this->createMock(ErrorFactory::class);
+        $loggerMock = $this->createMock(LoggerInterface::class);
+        $xmlSecurityMock = $this->createMock(Security::class);
+        $xmlElFactoryMock = $this->createMock(\Magento\Shipping\Model\Simplexml\ElementFactory::class);
+        $trackFactoryMock = $this->createMock(\Magento\Shipping\Model\Tracking\ResultFactory::class);
+        $trackErrorFactoryMock = $this->createMock(\Magento\Shipping\Model\Tracking\Result\ErrorFactory::class);
+        $trackStatusFactoryMock = $this->createMock(StatusFactory::class);
+        $regionFactoryMock = $this->createMock(RegionFactory::class);
+        $countryFactoryMock = $this->createMock(CountryFactory::class);
+        $currencyFactoryMock = $this->createMock(CurrencyFactory::class);
+        $directoryDataMock = $this->createMock(Data::class);
+        $stockRegistryMock = $this->createMock(StockRegistryInterface::class);
+        $productCollectionFactoryMock = $this->createMock(CollectionFactory::class);
+
+        // Mock the CurlFactory to return the Curl mock
+        $curlFactoryMock = $this->createMock(CurlFactory::class);
+        $curlFactoryMock->method('create')->willReturn($this->curlMock);
+
+        $requestMock = $this->createMock(MagentoHttp::class);
+
+        // Instantiate the BobGo class with the mocked dependencies
+        $this->bobGo = new BobGo(
+            $this->scopeConfigMock,
+            $rateErrorFactoryMock,
+            $loggerMock,
+            $xmlSecurityMock,
+            $xmlElFactoryMock,
+            $this->resultFactoryMock,
+            $this->methodFactoryMock,
+            $trackFactoryMock,
+            $trackErrorFactoryMock,
+            $trackStatusFactoryMock,
+            $regionFactoryMock,
+            $countryFactoryMock,
+            $currencyFactoryMock,
+            $directoryDataMock,
+            $stockRegistryMock,
+            $this->storeManagerMock,
+            $productCollectionFactoryMock,
+            $jsonFactoryMock,
+            $curlFactoryMock, // Pass the CurlFactory mock here
+            $requestMock,
+            []
+        );
+
+        // Assign the mocked AdditionalInfo directly to the BobGo instance
+        $this->bobGo->additionalInfo = $this->additionalInfoMock;
+    }
+
+    public function testGetBaseUrl(): void
+    {
+        $storeMock = $this->createMock(\Magento\Store\Model\Store::class);
+        $this->storeManagerMock->method('getStore')->willReturn($storeMock);
+        $storeMock->method('getBaseUrl')->willReturn('https://www.example.com/');
+
+        $baseUrl = $this->bobGo->getBaseUrl();
+
+        $this->assertEquals('example.com', $baseUrl);
+    }
+
+    public function testGetDestComp(): void
+    {
+        $this->additionalInfoMock->method('getDestComp')->willReturn('Test Company');
+
+        $destComp = $this->bobGo->getDestComp();
+
+        $this->assertEquals('Test Company', $destComp);
+    }
+
+    public function testGetDestSuburb(): void
+    {
+        $this->additionalInfoMock->method('getSuburb')->willReturn('Test Suburb');
+
+        $destSuburb = $this->bobGo->getDestSuburb();
+
+        $this->assertEquals('Test Suburb', $destSuburb);
+    }
+
+    public function testGetRates(): void
+    {
+        $payload = [
+            'identifier' => 'example.com',
+            'rate' => [
+                'origin' => [
+                    'company' => 'Test Company',
+                    'address1' => '123 Test St',
+                    'city' => 'Test City',
+                    'country_code' => 'US',
+                    'postal_code' => '12345',
+                ],
+                'destination' => [
+                    'company' => 'Destination Company',
+                    'address1' => '456 Destination St',
+                    'city' => 'Destination City',
+                    'country_code' => 'ZA',
+                    'postal_code' => '67890',
+                ],
+                'items' => [
+                    [
+                        'sku' => 'item-1',
+                        'quantity' => 1,
+                        'price' => 100.00,
+                        'weight' => 500,
+                    ],
+                ],
+            ],
+        ];
+
+        $this->curlMock->method('getBody')->willReturn(json_encode(['rates' => [['id' => 'rate-1']]]));
+
+        $rates = $this->bobGo->getRates($payload);
+
+        $this->assertIsArray($rates);
+        $this->assertEquals('rate-1', $rates['rates'][0]['id']);
+    }
+
+    public function testProcessAdditionalValidation(): void
+    {
+        // Create a mock for Product
+        $productMock = $this->createMock(\Magento\Catalog\Model\Product::class);
+        $productMock->method('isVirtual')->willReturn(false);
+        $productMock->method('getWeight')->willReturn(1);
+
+        // Create a mock for Quote Item
+        $quoteItemMock = $this->createMock(\Magento\Quote\Model\Quote\Item::class);
+        $quoteItemMock->method('getProduct')->willReturn($productMock);
+
+        // Create a real RateRequest object from the correct namespace
+        $rateRequest = new RateRequest();
+        $rateRequest->setDestPostcode('12345');
+        $rateRequest->setDestCountryId('ZA');
+        $rateRequest->setAllItems([$quoteItemMock]);
+
+        $result = $this->bobGo->processAdditionalValidation($rateRequest);
+
+        $this->assertInstanceOf(BobGo::class, $result);
+    }
+}
diff --git a/Test/Unit/Model/Carrier/USubsTest.php b/Test/Unit/Model/Carrier/USubsTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..dc2d393d541183d64d35be3f4c49af1acd7a1cfc
--- /dev/null
+++ b/Test/Unit/Model/Carrier/USubsTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Model\Carrier;
+
+use BobGroup\BobGo\Model\Carrier\USubs;
+use Magento\Framework\App\Request\Http;
+use PHPUnit\Framework\TestCase;
+
+class USubsTest extends TestCase
+{
+    /**
+     * @var USubs
+     */
+    private $uSubs;
+
+    /**
+     * @var Http|\PHPUnit\Framework\MockObject\MockObject
+     */
+    private $requestMock;
+
+    protected function setUp(): void
+    {
+        $this->requestMock = $this->createMock(Http::class);
+
+        $this->uSubs = new USubs($this->requestMock);
+    }
+
+    public function testGetDestComp(): void
+    {
+        $requestBody = json_encode([
+            'address' => [
+                'company' => 'Test Company'
+            ]
+        ]);
+
+        // Ensure getContent returns a valid JSON string
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->uSubs->getDestComp();
+
+        $this->assertEquals('Test Company', $result);
+    }
+
+    public function testGetDestCompReturnsEmptyStringWhenNotSet(): void
+    {
+        // Return an empty JSON object
+        $this->requestMock->method('getContent')
+            ->willReturn('{}');
+
+        $result = $this->uSubs->getDestComp();
+
+        $this->assertEquals('', $result);
+    }
+
+    public function testGetDestCompReturnsEmptyStringWhenInvalidStructure(): void
+    {
+        // Test case where the JSON structure is invalid
+        $requestBody = json_encode([
+            'invalid' => 'structure'
+        ]);
+
+        $this->requestMock->method('getContent')
+            ->willReturn($requestBody);
+
+        $result = $this->uSubs->getDestComp();
+
+        $this->assertEquals('', $result);
+    }
+}
diff --git a/Test/Unit/Model/Source/FreemethodTest.php b/Test/Unit/Model/Source/FreemethodTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0dc63f485bcb14bcb151c33017ddea39f1aa7463
--- /dev/null
+++ b/Test/Unit/Model/Source/FreemethodTest.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Model\Source;
+
+use BobGroup\BobGo\Model\Carrier\BobGo;
+use BobGroup\BobGo\Model\Source\Freemethod;
+use PHPUnit\Framework\TestCase;
+
+class FreemethodTest extends TestCase
+{
+    /**
+     * @var Freemethod
+     */
+    private $freemethod;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $bobGoMock;
+
+    protected function setUp(): void
+    {
+        // Mock the BobGo dependency required by the parent class
+        $this->bobGoMock = $this->createMock(BobGo::class);
+
+        // Instantiate the Freemethod class with the required constructor arguments
+        $this->freemethod = new Freemethod($this->bobGoMock);
+    }
+
+    public function testToOptionArray(): void
+    {
+        // Call the method under test
+        $result = $this->freemethod->toOptionArray();
+
+        // Check that the first option is 'None'
+        $this->assertArrayHasKey(0, $result);
+        $this->assertEquals(['value' => '', 'label' => 'None'], $result[0]);
+
+        // Ensure the array is not empty after adding the 'None' option
+        $this->assertNotEmpty($result);
+    }
+}
diff --git a/Test/Unit/Model/Source/GenericTest.php b/Test/Unit/Model/Source/GenericTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..abb2441c42e38d292c64be7853dc302069bf93a0
--- /dev/null
+++ b/Test/Unit/Model/Source/GenericTest.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Model\Source;
+
+use BobGroup\BobGo\Model\Carrier\BobGo;
+use BobGroup\BobGo\Model\Source\Generic;
+use PHPUnit\Framework\TestCase;
+
+class GenericTest extends TestCase
+{
+    /**
+     * @var Generic
+     */
+    private $generic;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $bobGoMock;
+
+    protected function setUp(): void
+    {
+        // Mock the BobGo class dependency
+        $this->bobGoMock = $this->createMock(BobGo::class);
+
+        // Instantiate the Generic class with the required constructor arguments
+        $this->generic = new Generic($this->bobGoMock);
+
+        // Set the carrier code for the test
+        $reflection = new \ReflectionClass($this->generic);
+        $codeProperty = $reflection->getProperty('_code');
+        $codeProperty->setAccessible(true);
+        $codeProperty->setValue($this->generic, 'test_code');
+    }
+
+    public function testToOptionArray(): void
+    {
+        // Set up the expected return value from the BobGo's getCode method
+        $this->bobGoMock->method('getCode')->with('test_code')->willReturn([
+            'code1' => 'Title 1',
+            'code2' => 'Title 2',
+        ]);
+
+        // Call the method under test
+        $result = $this->generic->toOptionArray();
+
+        // The expected result
+        $expected = [
+            ['value' => 'code1', 'label' => 'Title 1'],
+            ['value' => 'code2', 'label' => 'Title 2'],
+        ];
+
+        $this->assertEquals($expected, $result);
+    }
+
+    public function testToOptionArrayWithEmptyConfig(): void
+    {
+        // Set up the getCode method to return null
+        $this->bobGoMock->method('getCode')->with('test_code')->willReturn(null);
+
+        // Call the method under test
+        $result = $this->generic->toOptionArray();
+
+        // The expected result is an empty array
+        $this->assertEquals([], $result);
+    }
+}
diff --git a/Test/Unit/Observer/ConfigChangeObserverTest.php b/Test/Unit/Observer/ConfigChangeObserverTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..695f03a60111ec5d2c363862944976106bc56516
--- /dev/null
+++ b/Test/Unit/Observer/ConfigChangeObserverTest.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Observer;
+
+use BobGroup\BobGo\Model\Carrier\BobGo;
+use BobGroup\BobGo\Observer\ConfigChangeObserver;
+use Magento\Framework\Event\Observer;
+use Magento\Framework\Message\ManagerInterface;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class ConfigChangeObserverTest extends TestCase
+{
+    /**
+     * @var ConfigChangeObserver
+     */
+    private $observer;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $bobGoMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $loggerMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $messageManagerMock;
+
+    protected function setUp(): void
+    {
+        // Mock the dependencies
+        $this->bobGoMock = $this->createMock(BobGo::class);
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+        $this->messageManagerMock = $this->createMock(ManagerInterface::class);
+
+        // Instantiate the ConfigChangeObserver with the mocked dependencies
+        $this->observer = new ConfigChangeObserver(
+            $this->bobGoMock,
+            $this->loggerMock,
+            $this->messageManagerMock
+        );
+    }
+
+    public function testExecuteWithActiveCarrierAndSuccessfulConnection(): void
+    {
+        // Set up the observer mock
+        $observerMock = $this->createMock(Observer::class);
+
+        // Mock the event data to simulate the carrier being active
+        $observerMock->method('getEvent')->willReturnSelf();
+        $observerMock->method('getData')->with('changed_paths')->willReturn(['carriers/bobgo/active']);
+
+        // Mock the BobGo object methods
+        $this->bobGoMock->method('isActive')->willReturn(true);
+        $this->bobGoMock->method('triggerRatesTest')->willReturn(true);
+
+        // Expect success message to be added
+        $this->messageManagerMock->expects($this->once())
+            ->method('addSuccessMessage')
+            ->with(__('Bob Go rates at checkout connected.'));
+
+        // Execute the observer
+        $this->observer->execute($observerMock);
+    }
+
+    public function testExecuteWithActiveCarrierAndFailedConnection(): void
+    {
+        // Set up the observer mock
+        $observerMock = $this->createMock(Observer::class);
+
+        // Mock the event data to simulate the carrier being active
+        $observerMock->method('getEvent')->willReturnSelf();
+        $observerMock->method('getData')->with('changed_paths')->willReturn(['carriers/bobgo/active']);
+
+        // Mock the BobGo object methods
+        $this->bobGoMock->method('isActive')->willReturn(true);
+        $this->bobGoMock->method('triggerRatesTest')->willReturn(false);
+
+        // Expect error message to be added
+        $this->messageManagerMock->expects($this->once())
+            ->method('addErrorMessage')
+            ->with(__('Failed to connect to rates at checkout. Please check your internet connection
+                        and make sure Rates at checkout is enabled for your channel on Bob Go. Please visit Bob Go
+                        settings page to make sure your Magento channel is enabled to receive rates.
+                        https://my.bobgo.co.za/rates-at-checkout?tab=settings'));
+
+        // Execute the observer
+        $this->observer->execute($observerMock);
+    }
+
+    public function testExecuteWithInactiveCarrier(): void
+    {
+        // Set up the observer mock
+        $observerMock = $this->createMock(Observer::class);
+
+        // Mock the event data to simulate the carrier being inactive
+        $observerMock->method('getEvent')->willReturnSelf();
+        $observerMock->method('getData')->with('changed_paths')->willReturn(['carriers/bobgo/active']);
+
+        // Mock the BobGo object method to return inactive
+        $this->bobGoMock->method('isActive')->willReturn(false);
+
+        // Ensure no messages are added
+        $this->messageManagerMock->expects($this->never())
+            ->method('addSuccessMessage');
+        $this->messageManagerMock->expects($this->never())
+            ->method('addErrorMessage');
+
+        // Execute the observer
+        $this->observer->execute($observerMock);
+    }
+}
diff --git a/Test/Unit/Plugin/AddWeightUnitToOrderPluginTest.php b/Test/Unit/Plugin/AddWeightUnitToOrderPluginTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d4bf6b6a7bf13d9d841da2c05dc13126e06467f2
--- /dev/null
+++ b/Test/Unit/Plugin/AddWeightUnitToOrderPluginTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Plugin;
+
+use BobGroup\BobGo\Plugin\AddWeightUnitToOrderPlugin;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
+class AddWeightUnitToOrderPluginTest extends TestCase
+{
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $loggerMock;
+
+    /**
+     * @var \PHPUnit\Framework\MockObject\MockObject
+     */
+    private $scopeConfigMock;
+
+    /**
+     * @var AddWeightUnitToOrderPlugin
+     */
+    private $plugin;
+
+    protected function setUp(): void
+    {
+        $this->loggerMock = $this->createMock(LoggerInterface::class);
+        $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
+
+        // Instantiate the plugin
+        $this->plugin = new AddWeightUnitToOrderPlugin(
+            $this->loggerMock,
+            $this->scopeConfigMock
+        );
+    }
+
+    public function testBeforeSaveWithLbsWeightUnit(): void
+    {
+        // Mock the OrderInterface
+        $orderMock = $this->createMock(OrderInterface::class);
+
+        // Mock the OrderItemInterface
+        $orderItemMock = $this->createMock(OrderItemInterface::class);
+        $orderItemMock->method('getWeight')->willReturn(10); // Assume 10 lbs
+
+        // Return a list of order items
+        $orderMock->method('getItems')->willReturn([$orderItemMock]);
+
+        // Set up the ScopeConfig mock to return 'lbs'
+        $this->scopeConfigMock->method('getValue')
+            ->with('general/locale/weight_unit', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)
+            ->willReturn('lbs');
+
+        // Expect that the weight will be converted and set back
+        $orderItemMock->expects($this->once())
+            ->method('setWeight')
+            ->with(4.5359237); // 10 lbs * 0.45359237 = 4.5359237 kg
+
+        // Call the plugin's beforeSave method
+        $result = $this->plugin->beforeSave($this->createMock(OrderRepositoryInterface::class), $orderMock);
+
+        // Assert that the returned order is the same as the original
+        $this->assertSame([$orderMock], $result);
+    }
+
+    public function testBeforeSaveWithNonLbsWeightUnit(): void
+    {
+        // Mock the OrderInterface
+        $orderMock = $this->createMock(OrderInterface::class);
+
+        // Mock the OrderItemInterface
+        $orderItemMock = $this->createMock(OrderItemInterface::class);
+
+        // Return a list of order items
+        $orderMock->method('getItems')->willReturn([$orderItemMock]);
+
+        // Set up the ScopeConfig mock to return 'kgs'
+        $this->scopeConfigMock->method('getValue')
+            ->with('general/locale/weight_unit', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)
+            ->willReturn('kgs');
+
+        // Expect that the setWeight method is never called since the unit is not 'lbs'
+        $orderItemMock->expects($this->never())
+            ->method('setWeight');
+
+        // Call the plugin's beforeSave method
+        $result = $this->plugin->beforeSave($this->createMock(OrderRepositoryInterface::class), $orderMock);
+
+        // Assert that the returned order is the same as the original
+        $this->assertSame([$orderMock], $result);
+    }
+}
diff --git a/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php b/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d8bd9d0a66447160396590cc4b878cb8acfad25
--- /dev/null
+++ b/Test/Unit/Plugin/Block/DataProviders/Tracking/ChangeTitleTest.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Plugin\Block\DataProviders\Tracking;
+
+use BobGroup\BobGo\Plugin\Block\DataProviders\Tracking\ChangeTitle;
+use BobGroup\BobGo\Model\Carrier\BobGo;
+use Magento\Shipping\Model\Tracking\Result\Status;
+use Magento\Shipping\Block\DataProviders\Tracking\DeliveryDateTitle as Subject;
+use PHPUnit\Framework\TestCase;
+
+class ChangeTitleTest extends TestCase
+{
+    /**
+     * @var ChangeTitle
+     */
+    private $plugin;
+
+    protected function setUp(): void
+    {
+        // Instantiate the ChangeTitle plugin
+        $this->plugin = new ChangeTitle();
+    }
+
+    public function testAfterGetTitleWithBobGoCarrier(): void
+    {
+        // Create a custom Status object with BobGo carrier
+        $status = $this->getMockBuilder(Status::class)
+            ->setMethods(['getCarrier'])
+            ->getMock();
+
+        $status->method('getCarrier')->willReturn(BobGo::CODE);
+
+        // Mock the Subject class
+        $subjectMock = $this->createMock(Subject::class);
+
+        // Call the plugin method afterGetTitle
+        $result = $this->plugin->afterGetTitle($subjectMock, 'Original Title', $status);
+
+        // Assert that the title was changed for BobGo carrier
+        $this->assertEquals('Expected delivery:', $result);
+    }
+
+    public function testAfterGetTitleWithOtherCarrier(): void
+    {
+        // Create a custom Status object with a different carrier
+        $status = $this->getMockBuilder(Status::class)
+            ->setMethods(['getCarrier'])
+            ->getMock();
+
+        $status->method('getCarrier')->willReturn('other_carrier_code');
+
+        // Mock the Subject class
+        $subjectMock = $this->createMock(Subject::class);
+
+        // Call the plugin method afterGetTitle
+        $originalTitle = 'Original Title';
+        $result = $this->plugin->afterGetTitle($subjectMock, $originalTitle, $status);
+
+        // Assert that the title was not changed for other carriers
+        $this->assertEquals($originalTitle, $result);
+    }
+}
diff --git a/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php b/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6c01770254cd04592f5fbafa8bfccacd4d6bfd27
--- /dev/null
+++ b/Test/Unit/Plugin/Block/Tracking/PopUpDeliveryDateTest.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace BobGroup\BobGo\Test\Unit\Plugin\Block\Tracking;
+
+use BobGroup\BobGo\Plugin\Block\Tracking\PopupDeliveryDate;
+use Magento\Shipping\Block\Tracking\Popup;
+use Magento\Shipping\Model\Tracking\Result\Status;
+use PHPUnit\Framework\TestCase;
+
+class PopupDeliveryDateTest extends TestCase
+{
+    /**
+     * @var PopupDeliveryDate
+     */
+    private $plugin;
+
+    protected function setUp(): void
+    {
+        // Instantiate the PopupDeliveryDate plugin
+        $this->plugin = new PopupDeliveryDate();
+    }
+
+    public function testAfterFormatDeliveryDateTimeWithBobGoCarrier(): void
+    {
+        // Create an instance of the Status class
+        $status = new Status();
+        $status->setCarrier('bobgo_carrier_code');
+
+        // Mock the Popup class
+        $popupMock = $this->createMock(Popup::class);
+        $popupMock->method('getTrackingInfo')->willReturn([
+            ['tracking_info' => $status],
+        ]);
+
+        // Mock the formatDeliveryDate method
+        $popupMock->method('formatDeliveryDate')
+            ->with('2024-08-19')
+            ->willReturn('Aug 19, 2024');
+
+        // Call the plugin method afterFormatDeliveryDateTime
+        $result = $this->plugin->afterFormatDeliveryDateTime(
+            $popupMock,
+            'Aug 19, 2024 10:00 AM',
+            '2024-08-19',
+            '10:00 AM'
+        );
+
+        // Assert that the time was stripped for BobGo carrier
+        $this->assertEquals('Aug 19, 2024', $result);
+    }
+
+    public function testAfterFormatDeliveryDateTimeWithOtherCarrier(): void
+    {
+        // Create an instance of the Status class
+        $status = new Status();
+        $status->setCarrier('other_carrier_code');
+
+        // Mock the Popup class
+        $popupMock = $this->createMock(Popup::class);
+        $popupMock->method('getTrackingInfo')->willReturn([
+            ['tracking_info' => $status],
+        ]);
+
+        // Call the plugin method afterFormatDeliveryDateTime
+        $result = $this->plugin->afterFormatDeliveryDateTime(
+            $popupMock,
+            'Aug 19, 2024 10:00 AM',
+            '2024-08-19',
+            '10:00 AM'
+        );
+
+        // Assert that the time remains unchanged for other carriers
+        $this->assertEquals('Aug 19, 2024 10:00 AM', $result);
+    }
+
+    public function testGetCarrierWithNoTrackingInfo(): void
+    {
+        // Mock the Popup class with no tracking info
+        $popupMock = $this->createMock(Popup::class);
+        $popupMock->method('getTrackingInfo')->willReturn([]);
+
+        // Call the getCarrier method directly
+        $reflection = new \ReflectionClass($this->plugin);
+        $method = $reflection->getMethod('getCarrier');
+        $method->setAccessible(true);
+
+        $result = $method->invokeArgs($this->plugin, [$popupMock]);
+
+        // Assert that an empty string is returned when no tracking info is available
+        $this->assertEquals('', $result);
+    }
+}
diff --git a/composer.json b/composer.json
index 330819819a5b54bc71a50c1b976e70eb60a8303d..c150da7dd92104726b030245a6ee9339960bd131 100644
--- a/composer.json
+++ b/composer.json
@@ -1,10 +1,6 @@
 {
-  "name": "bob-public-utils/bobgo-magento-extension",
-  "description": "Streamline your order fulfillments with Bob Go",
-  "require": {
-    "php": "~8.1.28",
-    "magento/framework": "*"
-  },
+    "name": "bob-public-utils/bobgo-magento-extension",
+    "description": "Smart shipping and order management solution in South Africa",
   "type": "magento2-module",
   "version": "1.0.0",
   "authors": [
@@ -15,15 +11,19 @@
     }
   ],
   "license": [
-   "AFL-3.0",
+    "AFL-3.0",
     "OSL-3.0"
   ],
-  "autoload": {
+    "require": {
+        "php": "^7.4 || ^8.0 || ^8.2"
+    },
+
+    "autoload": {
     "files": [
       "registration.php"
     ],
     "psr-4": {
-      "Plugin\\": ""
+        "BobGroup\\BobGo\\": "Model/Carrier/"
     }
   }
 }
diff --git a/etc/.DS_Store b/etc/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..a959c9c8a835e4f3e5b3131f6477abb5843156c1
Binary files /dev/null and b/etc/.DS_Store differ
diff --git a/etc/acl.xml b/etc/acl.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4abc946570172000c175f0e235d9ca2748bb621e
--- /dev/null
+++ b/etc/acl.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
+    <acl>
+        <resources>
+            <!-- Define at least one minimal resource -->
+            <resource id="Magento_Backend::admin">
+                <!-- You can leave this empty if you don't want to define specific permissions -->
+            </resource>
+        </resources>
+    </acl>
+</config>
diff --git a/etc/adminhtml/events.xml b/etc/adminhtml/events.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1fac000e33b429aac75757c8e86d6d04f863e7a4
--- /dev/null
+++ b/etc/adminhtml/events.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
+    <event name="admin_system_config_changed_section_carriers">
+        <observer name="bobgo_config_change_observer" instance="BobGroup\BobGo\Observer\ConfigChangeObserver" />
+    </event>
+
+</config>
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index 0d3fa3adc894f9aa28968138ba8856ee6379a46f..baf4aee5da038e561fcc799e3fd2de9a80b7a53f 100644
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -2,22 +2,6 @@
 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
     <system>
 
-        <tab id="bobgo" translate="label" sortOrder="2">
-            <label>Bob Go</label>
-        </tab>
-        <section id="BobGroup_BobGo"  showInDefault="1">
-            <tab>bobgo</tab>
-            <label>Settings</label>
-            <resource>Magento_Config::config</resource>
-            <group id="general"  showInDefault="1">
-                <label>Settings</label>
-                <field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
-                    <label>Extension enabled</label>
-                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
-                </field>
-            </group>
-        </section>
-
         <section id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
             <group id="store_information" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                 <field id="suburb" translate="label" type="text" sortOrder="50" showInDefault="1" showInWebsite="1" showInStore="0">
@@ -28,51 +12,21 @@
         </section>
 
         <section id="carriers" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
-            <group id="bobgo" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
+            <group id="bobgo" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
                 <label>Bob Go</label>
                 <field id="version" translate="label" type="label" sortOrder="0" showInDefault="1" showInWebsite="0" showInStore="0">
                     <label>Version</label>
                     <frontend_model>BobGroup\BobGo\Block\System\Config\Form\Field\Version</frontend_model>
                 </field>
                 <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0">
-                    <label>Show rates for checkout</label>
+                    <label>Enable Bob Go rates at checkout</label>
                     <comment>When this setting is enabled, your customers will be presented with shipping rates at checkout, as configured on the Bob Go platform under Rates at checkout.</comment>
                     <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                 </field>
-                <field id="specificerrmsg" translate="label" type="textarea" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1">
-                    <label>Displayed Error Message</label>
-                    <comment>The message displayed to the customer when no rates were available on Bob Go.</comment>
-                </field>
-
-                <field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0">
-                    <label>Ship to Applicable Countries</label>
-                    <frontend_class>shipping-applicable-country</frontend_class>
-                    <source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model>
-                </field>
-
-                <field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0">
-                    <label>Ship to Specific Countries</label>
-                    <source_model>Magento\Directory\Model\Config\Source\Country</source_model>
-                    <can_be_empty>1</can_be_empty>
-                </field>
-
-                <field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0">
-                    <label>Show Method if Not Applicable</label>
-                    <frontend_class>shipping-skip-hide</frontend_class>
-                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
-                </field>
-                <field id="unit_of_measure" translate="label" type="select" sortOrder="73" showInDefault="1" showInWebsite="1">
-                    <label>Weight Unit</label>
-                    <source_model>BobGroup\BobGo\Model\Source\Unitofmeasure</source_model>
-                </field>
-                <field id="max_package_weight" translate="label" type="text" sortOrder="90" showInDefault="1" showInWebsite="1">
-                    <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label>
-                    <validate>validate-number validate-zero-or-greater</validate>
-                </field>
                 <field id="additional_info" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1">
                     <label>Show additional rate information</label>
                     <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
-                    <comment>Displays the delivery timeframe and additional service level description as configured on Bob Go.</comment>
+                    <comment>Displays the delivery timeframe and additional service level description, as configured on Bob Go.</comment>
                 </field>
             </group>
         </section>
diff --git a/etc/config.xml b/etc/config.xml
index eb34051b8ece8d438e1e19c5dae9a533cd4e25a2..8630ff6ac0ac26af6b5d6dc276bad7151eb4f119 100644
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -1,23 +1,13 @@
 <?xml version="1.0"?>
 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
     <default>
-        <BobGroup_BobGo>
-            <general>
-                <debug>0</debug>
-            </general>
-        </BobGroup_BobGo>
-
         <carriers>
             <bobgo>
-                <active>0</active>
-<!--                <sallowspecific>1</sallowspecific>
-                <specificcountry>ZA</specificcountry>-->
+                <active>1</active>
                 <price>0.00</price>
                 <model>BobGroup\BobGo\Model\Carrier\BobGo</model>
-                <name>Fixed</name>
-                <max_package_weight>300</max_package_weight>
+                <name>Bob Go</name>
                 <title>Bob Go</title>
-                <specificerrmsg>We could not find any shipping rates for your delivery address. Please make sure your address is correct. If you are experiencing problems, please contact us.</specificerrmsg>
             </bobgo>
         </carriers>
 
diff --git a/etc/di.xml b/etc/di.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5d8128708181eb7593b6ab8ae4c4ae5c63923e73
--- /dev/null
+++ b/etc/di.xml
@@ -0,0 +1,6 @@
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
+    <type name="Magento\Sales\Api\OrderRepositoryInterface">
+        <plugin name="add_weight_unit_to_order_item" type="BobGroup\BobGo\Plugin\AddWeightUnitToOrderPlugin" />
+    </type>
+
+</config>
diff --git a/etc/events.xml b/etc/events.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bfb5e9669522f329879ecc96da2ca4a571ea8b2e
--- /dev/null
+++ b/etc/events.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
+</config>
diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml
index 2ee6f54f96f9264b803de3a8dd8f99b923988f76..bc1aaebb43890432e80d8e9d27bfd5838b7c8e8b 100644
--- a/etc/frontend/di.xml
+++ b/etc/frontend/di.xml
@@ -7,4 +7,10 @@
             </argument>
         </arguments>
     </type>
+    <type name="BobGroup\BobGo\Observer\ConfigChangeObserver">
+        <arguments>
+            <argument name="scopeConfig" xsi:type="object">Magento\Framework\App\Config\ScopeConfigInterface</argument>
+            <argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/etc/module.xml b/etc/module.xml
index d736136edb4535fca984abcd0b7d2b08fed15e6d..fe2b36da4d6bad5e35cb60c85d1f1ff05d245001 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -2,7 +2,7 @@
 <!--
 /**
  * @category   Bob Go
- * @package    bobgo_CustomShipping
+ * @package    BobGo
  * @author     Bob Go
  */
 -->
@@ -15,6 +15,8 @@
             <module name="Magento_Sales"/>
             <module name="Magento_Quote"/>
             <module name="Magento_SalesRule"/>
+            <module name="Magento_Config"/>
+            <module name="Magento_Shipping"/>
         </sequence>
     </module>
 </config>
diff --git a/view/frontend/.DS_Store b/view/frontend/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..073d5f170af0ac13acc7ea6f79c8547b36d9f88d
Binary files /dev/null and b/view/frontend/.DS_Store differ