diff --git a/ViewModel/DataLayer.php b/ViewModel/DataLayer.php
new file mode 100644
index 0000000..8dcf6f5
--- /dev/null
+++ b/ViewModel/DataLayer.php
@@ -0,0 +1,149 @@
+taxHelper = $taxHelper;
+ $this->checkoutSession = $checkoutSession;
+ $this->request = $request;
+ $this->registry = $registry;
+ $this->helper = $helper;
+ }
+
+ public function getSiteId()
+ {
+ return $this->helper->getSiteId();
+ }
+
+ public function getData()
+ {
+ if (!($siteId = $this->getSiteId())) {
+ return json_encode([]);
+ }
+
+ /**
+ * The logic is functionally-equivalent to the _toHtml() function of the custom Salesfire block class herein.
+ * Additional comments have been added to highlight specifically where customisations have been made.
+ * @see SalesfireScript::_toHtml()
+ */
+
+ $formatter = new SalesfireFormatter($siteId);
+ $formatter->addPlatform('magento2');
+
+ if ($this->request->getFullActionName() == 'checkout_onepage_success'
+ && $order = $this->checkoutSession->getLastRealOrder() // customisation: use checkout session directly rather than class-level helper function
+ ) {
+ $transaction = new SalesfireTransactionType([
+ 'id' => $order->getIncrementId(),
+ 'shipping' => round((float)$order->getShippingAmount(), 2), // customisation: float type casting
+ 'currency' => $order->getOrderCurrencyCode(),
+ 'coupon' => $order->getCouponCode(),
+ ]);
+
+ foreach ($order->getAllVisibleItems() as $product) {
+ $variant = '';
+ $options = $product->getProductOptions();
+ $parent_product_id = $product_id = $product->getProductId();
+
+ if ($product->getHasChildren()) {
+ foreach ($product->getChildrenItems() as $child) {
+ $product_id = $child->getProductId();
+ }
+ }
+
+ if (!empty($options) && !empty($options['attribute_info'])) {
+ $variant = implode(', ', array_map(function ($item) {
+ return $item['label'].': '.$item['value'];
+ }, $options['attribute_info']));
+ }
+
+ $quantity = $product->getQtyOrdered() ?? 1;
+ $pricing = $this->calculateItemPriceAndTax($product, $quantity);
+
+ $transaction->addProduct(new SalesfireProductType([
+ 'sku' => $product_id,
+ 'parent_sku' => $parent_product_id,
+ 'name' => $product->getName(),
+ 'price' => round((float)$pricing['price'], 2), // customisation: float type casting
+ 'tax' => round((float)$pricing['tax'], 2), // customisation: float type casting
+ 'quantity' => round((float)$quantity, 2), // customisation: float type casting
+ 'variant' => $variant,
+ ]));
+ }
+
+ $formatter->addTransaction($transaction);
+ }
+
+ if ($product = $this->registry->registry('product')) { // customisation: use registry directly rather than class-level helper function
+ // Calculate product tax
+ $price = round((float)$this->taxHelper->getTaxPrice($product, $product->getFinalPrice(), false), 2); // customisation: float type casting
+ $tax = round((float)$this->taxHelper->getTaxPrice($product, $product->getFinalPrice(), true), 2) - $price; // customisation: float type casting
+
+ $formatter->addProductView(new SalesfireProductType([
+ 'sku' => $product->getId(),
+ 'parent_sku' => $product->getId(),
+ 'name' => $product->getName(),
+ 'price' => $price,
+ 'tax' => $tax,
+ ]));
+ }
+
+ // customisation: return only the JSON as the template is responsible for pushing the data to the sfDataLayer
+ return $formatter->toJson();
+ }
+
+ /**
+ * This function has been copied as-is from the custom Salesfire block class, save for the function visibility which
+ * has been set to public rather than private for improved extensibility.
+ */
+ public function calculateItemPriceAndTax($product, $quantity)
+ {
+ // Row totals represent all items in the order line (e.g., if qty=4, this is the total for all 4)
+ $rowTotal = $product->getRowTotal() ?: 0;
+ $rowTax = $product->getTaxAmount() ?: 0;
+
+ if ($quantity <= 0) {
+ return ['price' => 0, 'tax' => 0];
+ }
+
+ return [
+ 'price' => $rowTotal / $quantity,
+ 'tax' => $rowTax / $quantity,
+ ];
+ }
+}
diff --git a/view/frontend/layout/hyva_default.xml b/view/frontend/layout/hyva_default.xml
new file mode 100644
index 0000000..d113f80
--- /dev/null
+++ b/view/frontend/layout/hyva_default.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+ https://cdn.salesfire.co.uk/code/
+ false
+ true
+
+
+
+
+
diff --git a/view/frontend/templates/sf-cdn.phtml b/view/frontend/templates/sf-cdn.phtml
new file mode 100644
index 0000000..3f86fc5
--- /dev/null
+++ b/view/frontend/templates/sf-cdn.phtml
@@ -0,0 +1,32 @@
+getData('url'))) {
+ return;
+}
+
+/** @var ViewModelRegistry $viewModels */
+/** @var DataLayer $dataLayer */
+$dataLayer = $viewModels->require(DataLayer::class);
+$siteId = $dataLayer->getSiteId();
+
+if (!$siteId) {
+ return;
+}
+
+$url .= $siteId . '.js';
+?>
+
+registerInlineScript(); ?>
diff --git a/view/frontend/templates/sf-datalayer.phtml b/view/frontend/templates/sf-datalayer.phtml
new file mode 100644
index 0000000..4c4a0a1
--- /dev/null
+++ b/view/frontend/templates/sf-datalayer.phtml
@@ -0,0 +1,30 @@
+require(DataLayer::class);
+$data = $dataLayer->getData();
+
+if (!$data) {
+ return;
+}
+?>
+
+registerInlineScript(); ?>
diff --git a/view/frontend/templates/sfgetid.phtml b/view/frontend/templates/sfgetid.phtml
new file mode 100644
index 0000000..e008cb7
--- /dev/null
+++ b/view/frontend/templates/sfgetid.phtml
@@ -0,0 +1,42 @@
+
+
+registerInlineScript(); ?>