From dbcb4eb74924ac5493699a08003d149a6eb14722 Mon Sep 17 00:00:00 2001
From: "@ChristelLoftus" <christel@bob.co.za>
Date: Wed, 2 Oct 2024 15:44:00 +0200
Subject: [PATCH] triggerWebhookTest

---
 Model/Carrier/BobGo.php           | 87 +++++++++++++++++++++++++++++++
 Model/Carrier/UData.php           |  2 +-
 Observer/ConfigChangeObserver.php | 20 +++++++
 Observer/OrderCreateWebhook.php   |  1 +
 Observer/OrderWebhookBase.php     | 24 +++++++--
 etc/adminhtml/system.xml          | 16 ++++++
 6 files changed, 145 insertions(+), 5 deletions(-)

diff --git a/Model/Carrier/BobGo.php b/Model/Carrier/BobGo.php
index 386e4cb..51cf36b 100644
--- a/Model/Carrier/BobGo.php
+++ b/Model/Carrier/BobGo.php
@@ -864,6 +864,11 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         return UData::RATES_ENDPOINT;
     }
 
+    private function getWebhookUrl(): string
+    {
+        return UData::WEBHOOK_URL;
+    }
+
     /**
      * Perform API Request to Bob Go API and return response.
      *
@@ -1310,4 +1315,86 @@ class BobGo extends AbstractCarrierOnline implements \Magento\Shipping\Model\Car
         }
         return false;
     }
+
+    public function isWebhookEnabled(): bool
+    {
+        $enabled = $this->scopeConfig->getValue(
+            'carriers/bobgo/enable_webhooks',
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+        );
+
+        // Cast the value to a boolean
+        return filter_var($enabled, FILTER_VALIDATE_BOOLEAN);
+    }
+
+
+    public function triggerWebhookTest(): bool
+    {
+        $webhookKey = $this->scopeConfig->getValue(
+            'carriers/bobgo/webhook_key',
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+        );
+
+        $isEnabled = $this->scopeConfig->getValue(
+            'carriers/bobgo/enable_webhooks',
+            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
+        );
+
+        // Convert the string to a boolean value
+        $isEnabled = filter_var($isEnabled, FILTER_VALIDATE_BOOLEAN);
+
+//        if (!$webhookKey) {
+//            $this->_logger->error('Webhook key not configured.');
+//            return false;
+//        }
+
+        $storeId = strval($this->_storeManager->getStore()->getId());
+
+        $payload = [
+            'event' => 'webhook_validation',
+            'channel_identifier' => $this->getBaseUrl(),
+            'store_id' => $storeId,
+            'webhooks_enabled' => $isEnabled,
+        ];
+
+        try {
+            // Generate the HMAC-SHA256 hash as raw binary data
+            $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true);
+            // Encode the binary data in Base64
+            $signature = base64_encode($rawSignature);
+            // Set headers and post the data
+            $this->curl->addHeader('Content-Type', 'application/json');
+            $this->curl->addHeader('X-M-Webhook-Signature', $signature);
+
+            $payloadJson = json_encode($payload);
+            $this->_logger->info('Webhooks payload: ' . $payloadJson);
+            if ($payloadJson === false) {
+                throw new \RuntimeException('Failed to encode payload to JSON.');
+            }
+
+            $this->curl->addHeader('Content-Type', 'application/json');
+            $this->curl->post($this->getWebhookUrl(), $payloadJson);
+            $statusCode = $this->curl->getStatus();
+            $this->_logger->info('Webhooks statuscode: ' . $statusCode);
+            $responseBody = $this->curl->getBody();
+            $this->_logger->info('Webhooks response: ' . $responseBody);
+
+            $response = json_decode($responseBody, true);
+
+            if ($statusCode == 200 && isset($response['success']) && $response['success'] === true) {
+                $this->_logger->info('Webhook validation successful.');
+//                throw new LocalizedException(__('Rates received but id field is empty or invalid.'));
+                return true;
+            } else {
+                $this->_logger->error('Webhook validation failed: ' . ($response['message'] ?? 'Unknown error'));
+//                throw new LocalizedException(__('Rates received but id field is empty or invalid.'));
+                return false;
+            }
+        } catch (\Exception $e) {
+            $this->_logger->error('Webhook validation exception: ' . $e->getMessage());
+//            throw new LocalizedException(__('Rates received but id field is empty or invalid.'));
+            return false;
+        }
+    }
+
 }
diff --git a/Model/Carrier/UData.php b/Model/Carrier/UData.php
index adf83a2..a7e5d2e 100644
--- a/Model/Carrier/UData.php
+++ b/Model/Carrier/UData.php
@@ -26,5 +26,5 @@ class UData
      *
      * @var string
      */
-    public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel/magento';
+    public const WEBHOOK_URL = 'https://api.dev.bobgo.co.za/webhook/channel';
 }
diff --git a/Observer/ConfigChangeObserver.php b/Observer/ConfigChangeObserver.php
index 1b6c072..528dd82 100644
--- a/Observer/ConfigChangeObserver.php
+++ b/Observer/ConfigChangeObserver.php
@@ -52,6 +52,7 @@ class ConfigChangeObserver implements ObserverInterface
     {
         $changedPaths = $observer->getEvent()->getData('changed_paths');
 
+        // Test for rates at checkout
         if (is_array($changedPaths) && in_array('carriers/bobgo/active', $changedPaths)) {
             if ($this->bobGo->isActive()) {
                 $result = $this->bobGo->triggerRatesTest();
@@ -70,5 +71,24 @@ class ConfigChangeObserver implements ObserverInterface
                 }
             }
         }
+
+        // Test for webhooks
+        if (is_array($changedPaths) && in_array('carriers/bobgo/enable_webhooks', $changedPaths)) {
+//            if ($this->bobGo->isWebhookEnabled()) {
+                $this->logger->info('Webhooks test start: ');
+                $result = $this->bobGo->triggerWebhookTest();
+                $this->logger->info('Webhooks test end: ' . $result);
+
+                if ($result) {
+                    $this->messageManager->addSuccessMessage(
+                        __('Webhook validation successful.')
+                    );
+                } else {
+                    $this->messageManager->addErrorMessage(
+                        __('Webhook validation failed. Please check the webhook key and try again.')
+                    );
+                }
+//            }
+        }
     }
 }
diff --git a/Observer/OrderCreateWebhook.php b/Observer/OrderCreateWebhook.php
index 74f1ce4..f605801 100644
--- a/Observer/OrderCreateWebhook.php
+++ b/Observer/OrderCreateWebhook.php
@@ -15,5 +15,6 @@ class OrderCreateWebhook extends OrderWebhookBase
 
         // Extract order data and send to the webhook URL
         $this->sendWebhook($order);
+        $this->logger->info('Webhooks sent');
     }
 }
diff --git a/Observer/OrderWebhookBase.php b/Observer/OrderWebhookBase.php
index eeb106e..db27d61 100644
--- a/Observer/OrderWebhookBase.php
+++ b/Observer/OrderWebhookBase.php
@@ -3,6 +3,7 @@
 namespace BobGroup\BobGo\Observer;
 
 use BobGroup\BobGo\Model\Carrier\UData;
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\Event\ObserverInterface;
 use Magento\Framework\HTTP\Client\Curl;
 use Magento\Store\Model\StoreManagerInterface;
@@ -13,18 +14,29 @@ abstract class OrderWebhookBase implements ObserverInterface
     protected Curl $curl;
     protected LoggerInterface $logger;
     protected StoreManagerInterface $storeManager;
+    protected ScopeConfigInterface $scopeConfig;
 
-    public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager)
+    public function __construct(LoggerInterface $logger, Curl $curl, StoreManagerInterface $storeManager, ScopeConfigInterface $scopeConfig)
     {
         $this->logger = $logger;
         $this->curl = $curl;
         $this->storeManager = $storeManager;
+        $this->scopeConfig = $scopeConfig;
     }
 
     protected function sendWebhook($order)
     {
+        $enabled = $this->scopeConfig->getValue('carriers/bobgo/enable_webhooks', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
+
+        // Return early if webhooks is disabled
+        if (!$enabled) {
+            $this->logger->info('Webhooks are disabled. Exiting webhook process for order: ' . $order->getIncrementId());
+            return;
+        }
+
         // Webhook URL
         $url = $this->getWebhookUrl();
+        $this->logger->info('Webhooks url: ' . $url);
 
         $storeId = $this->getStoreId();
 
@@ -54,14 +66,17 @@ abstract class OrderWebhookBase implements ObserverInterface
 
         // Send the webhook
         $this->makeHttpPostRequest($url, $data, $storeId);
+        $this->logger->info('Webhooks sent');
     }
 
     private function makeHttpPostRequest($url, $data, $storeId)
     {
-        // Generate the signature using a secret key and the payload (example using HMAC SHA256)
-        $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A';
+        // Generate the signature using the webhook key saved in config
+        $webhookKey = $this->scopeConfig->getValue('carriers/bobgo/webhook_key', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
+        $this->logger->info('Webhooks - key: ' . $webhookKey);
+//        $secretKey = 'KaJGW2cxx1-6z_qjGhSq5Hj4qh_OXl0R1tUPurVs66A';
         // Generate the HMAC-SHA256 hash as raw binary data
-        $rawSignature = hash_hmac('sha256', $storeId, $secretKey, true);
+        $rawSignature = hash_hmac('sha256', $storeId, $webhookKey, true);
 
         // Encode the binary data in Base64
         $signature = base64_encode($rawSignature);
@@ -79,6 +94,7 @@ abstract class OrderWebhookBase implements ObserverInterface
         // Set headers and post the data
         $this->curl->addHeader('Content-Type', 'application/json');
         $this->curl->post($url, $payloadJson);
+        $this->logger->info('Webhooks payload: ' . $payloadJson);
     }
 
     private function getWebhookUrl(): string
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index 410c2c2..3f4b31f 100644
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -28,6 +28,22 @@
                     <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>
                 </field>
+
+                <!-- Enable Webhooks Checkbox -->
+                <field id="enable_webhooks" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0">
+                    <label>Enable webhooks</label>
+                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
+                    <comment>Enable or disable the webhook functionality for Bob Go.</comment>
+                </field>
+
+                <!-- Webhook Key Input Field -->
+                <field id="webhook_key" translate="label" type="text" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="0">
+                    <label>Webhook key</label>
+                    <comment>Enter the webhook key for authentication.</comment>
+                    <depends>
+                        <field id="enable_webhooks">1</field>
+                    </depends>
+                </field>
             </group>
         </section>
     </system>
-- 
GitLab