= $block->getProduct()->getName() ?>
+= $block->getCustomAttribute() ?>
+diff --git a/app/code/Kai/Banner.zip b/app/code/Kai/Banner.zip
new file mode 100755
index 00000000..0a484283
Binary files /dev/null and b/app/code/Kai/Banner.zip differ
diff --git a/app/code/Kai/Banner/Api/BannerRepository.php b/app/code/Kai/Banner/Api/BannerRepository.php
new file mode 100755
index 00000000..9b52ab45
--- /dev/null
+++ b/app/code/Kai/Banner/Api/BannerRepository.php
@@ -0,0 +1,177 @@
+kaiBannerModel = $kaiBannerModel;
+        $this->kaiBannerResourceModel = $kaiBannerResourceModel;
+        $this->kaiBannerCollection = $kaiBannerCollection;
+        $this->authorization();
+    }
+
+    private function authorization()
+    {
+        $headers = apache_request_headers();
+        $apiKey = \Kai\Banner\Api\Helper::API_KEY;
+
+        foreach ($headers as $key => $value) {
+            $key = strtolower($key);
+            if ('api_key' === $key && $value === $apiKey) {
+                return true;
+            }
+        }
+
+        header('HTTP/1.1 401 Unauthorized');
+        header('Accept: application/json');
+        header('Content-Type: application/json');
+        die(
+            json_encode(['message' => 'unauthorized'])
+        );
+    }
+
+    private function responseOk(array $data)
+    {
+        header('HTTP/1.1 200 Ok');
+        header('Accept: application/json');
+        header('Content-Type: application/json');
+        die(json_encode($data));
+    }
+
+    private function responseFail(array $data)
+    {
+        header('HTTP/1.1 400 Bad request');
+        header('Accept: application/json');
+        header('Content-Type: application/json');
+        die(json_encode($data));
+    }
+
+    private function responseMethodFail()
+    {
+        header('HTTP/1.1 400 bad request');
+        header('Accept: application/json');
+        header('Content-Type: application/json');
+        die(json_encode([
+            'status' => false,
+            'message' => 'Param ?method=... not exist!'
+        ]));
+    }
+
+    public function getData()
+    {
+        $method = $_GET['method'] ?? null;
+
+        if ($method === 'show') {
+            $id = $_GET['id'] ?? 0;
+            return $this->responseOk([
+                'status' => true,
+                'data' => $this
+                    ->kaiBannerCollection
+                    ->getItemById($id)
+                    ->getData()
+            ]);
+        }
+
+        if ($method === 'all') {
+            return $this->responseOk([
+                'status' => true,
+                'data' => $this
+                    ->kaiBannerCollection
+                    ->getData()
+            ]);
+        }
+
+        if ($method === 'delete') {
+            $id = $_GET['id'] ?? 0;
+            $model = $this->kaiBannerModel->setId($id);
+
+            try {
+                $this->kaiBannerResourceModel->delete($model);
+                return $this->responseOk([
+                    'status' => true,
+                ]);
+            } catch (\Exception $ex) {
+                $this->kaiBannerResourceModel->rollBack();
+                return $this->responseFail([
+                    'status' => false,
+                    'message' => $ex->getMessage()
+                ]);
+            }
+        }
+
+        return $this->responseMethodFail();
+    }
+
+    public function postData()
+    {
+        $method = $_GET['method'] ?? null;
+        parse_str(
+            string: file_get_contents('php://input'),
+            result: $payload
+        );
+
+        if ($method === 'create') {
+            $model = $this->kaiBannerModel;
+            try {
+                foreach ($payload as $key => $value) {
+                    $model->setData($key, $value);
+                }
+                $this->kaiBannerResourceModel->save($model);
+
+                return $this->responseOk([
+                    'status' => true,
+                    'data' => $this
+                        ->kaiBannerCollection
+                        ->getItemById($model->getId())
+                        ->getData()
+                ]);
+            } catch (\Exception $ex) {
+                return $this->responseFail([
+                    'status' => false,
+                    'message' => $ex->getMessage()
+                ]);
+            }
+        }
+
+        if ($method === 'update') {
+            $id = $_GET['id'] ?? 0;
+            $model = $this->kaiBannerModel->setId($id);
+
+            try {
+                foreach ($payload as $key => $value) {
+                    $model->setData($key, $value);
+                }
+                $this->kaiBannerResourceModel->save($model);
+
+                return $this->responseOk([
+                    'status' => true,
+                    'data' => $this
+                    ->kaiBannerCollection
+                    ->getItemById($model->getId())
+                    ->getData()
+                ]);
+            } catch (\Exception $ex) {
+                return $this->responseFail([
+                    'status' => false,
+                    'message' => $ex->getMessage()
+                ]);
+            }
+        }
+
+        return $this->responseMethodFail();
+    }
+}
diff --git a/app/code/Kai/Banner/Api/BannerRepositoryInterface.php b/app/code/Kai/Banner/Api/BannerRepositoryInterface.php
new file mode 100755
index 00000000..bfb34914
--- /dev/null
+++ b/app/code/Kai/Banner/Api/BannerRepositoryInterface.php
@@ -0,0 +1,23 @@
+urlInterface = $urlInterface;
+        $this->kaiBanner = $kaiBanner;
+        $this->kaiBannerResourceModel = $kaiBannerResourceModel;
+        $this->kaiBannerCollection = $kaiBannerCollection;
+        parent::__construct($context, $data);
+    }
+
+    public function getSecretKey()
+    {
+        return $this->urlInterface->getSecretKey();
+    }
+
+    public function getApiKey()
+    {
+        return \Kai\Banner\Api\Helper::API_KEY;
+    }
+
+    public function getAll()
+    {
+        return $this->kaiBannerCollection->getData();
+    }
+}
diff --git a/app/code/Kai/Banner/Controller/Adminhtml/Index/Index.php b/app/code/Kai/Banner/Controller/Adminhtml/Index/Index.php
new file mode 100755
index 00000000..f194e530
--- /dev/null
+++ b/app/code/Kai/Banner/Controller/Adminhtml/Index/Index.php
@@ -0,0 +1,24 @@
+resultPageFactory = $resultPageFactory;
+        parent::__construct($context);
+    }
+
+    public function execute()
+    {
+        $resultPage = $this->resultPageFactory->create();
+        $resultPage->getConfig()->getTitle()->prepend(__('Banner'));
+        return $resultPage;
+    }
+}
diff --git a/app/code/Kai/Banner/Model/KaiBanner.php b/app/code/Kai/Banner/Model/KaiBanner.php
new file mode 100755
index 00000000..ecbc2642
--- /dev/null
+++ b/app/code/Kai/Banner/Model/KaiBanner.php
@@ -0,0 +1,36 @@
+_init(
+            resourceModel: KaiBannerResourceModel::class
+        );
+    }
+
+}
+
+final class KaiBannerFactory extends Factory
+{
+    protected $objectManager;
+    protected $instanceName;
+
+    public function __construct(ObjectManagerInterface $objectManager, $instanceName = KaiBanner::class)
+    {
+        $this->objectManager = $objectManager;
+        $this->instanceName = $instanceName;
+    }
+
+    public function create(array $arguments = [], AbstractDb $resource = null)
+    {
+        return $this->objectManager->create($this->instanceName, $arguments, $resource);
+    }
+}
diff --git a/app/code/Kai/Banner/Model/KaiBannerCollection.php b/app/code/Kai/Banner/Model/KaiBannerCollection.php
new file mode 100755
index 00000000..41e6233e
--- /dev/null
+++ b/app/code/Kai/Banner/Model/KaiBannerCollection.php
@@ -0,0 +1,17 @@
+_init(
+            model: KaiBanner::class,
+            resourceModel: KaiBannerResourceModel::class
+        );
+    }
+}
diff --git a/app/code/Kai/Banner/Model/KaiBannerResourceModel.php b/app/code/Kai/Banner/Model/KaiBannerResourceModel.php
new file mode 100755
index 00000000..b7e22a14
--- /dev/null
+++ b/app/code/Kai/Banner/Model/KaiBannerResourceModel.php
@@ -0,0 +1,15 @@
+_init(
+            mainTable:'kai_banner',
+            idFieldName:'id'
+        );
+    }
+}
diff --git a/app/code/Kai/Banner/Model/KaiBannerValues.php b/app/code/Kai/Banner/Model/KaiBannerValues.php
new file mode 100755
index 00000000..7073f97e
--- /dev/null
+++ b/app/code/Kai/Banner/Model/KaiBannerValues.php
@@ -0,0 +1,64 @@
+collection = $collection;
+    }
+
+    public function getAllOptions()
+    {
+        $data = $this->collection->getData();
+        $options = [
+            [
+                'label' => 'Select option!',
+                'value' => '',
+            ]
+        ];
+
+        foreach ($data as $record) {
+            $options[] = [
+                'label' => $record['id'],
+                'value' => $record['title'],
+            ];
+        };
+
+        $this->_options = $options;
+        return $options;
+    }
+
+    public function getOptionText($value)
+    {
+        foreach ($this->getAllOptions() as $option) {
+            if ($option['value'] == $value) {
+                return $option['label'];
+            }
+        }
+        return false;
+    }
+
+    public function getFlatColumns()
+    {
+        $attributeCode = $this->getAttribute()->getAttributeCode();
+        return [
+            $attributeCode => [
+                'unsigned' => false,
+                'default' => null,
+                'extra' => null,
+                'type' => Table::TYPE_INTEGER,
+                'nullable' => true,
+                'comment' => 'Kai Banner ' . $attributeCode . ' column',
+            ],
+        ];
+    }
+}
diff --git a/app/code/Kai/Banner/Model/Serialize/Serializer/Json.php b/app/code/Kai/Banner/Model/Serialize/Serializer/Json.php
new file mode 100755
index 00000000..9af00dd5
--- /dev/null
+++ b/app/code/Kai/Banner/Model/Serialize/Serializer/Json.php
@@ -0,0 +1,36 @@
+_eavSetupFactory = $eavSetupFactory;
+        $this->_attributeRepositoryInterface = $attributeRepositoryInterface;
+        $this->_attributeSetupFactory = $attributeSetupFactory;
+    }
+
+    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
+    {
+        try {
+            $this->_attributeRepositoryInterface->get(Product::ENTITY, 'kai_banner');
+        } catch (\Exception $ex) {
+
+            $setup->startSetup();
+            $eavSetup = $this->_eavSetupFactory->create(['setup' => $setup]);
+            $eavSetup->addAttribute(
+                Product::ENTITY,
+                'kai_banner',
+                [
+                    'input' => 'select',
+                    'type' => 'varchar',
+                    'label' => 'Banner',
+                    'required' => false,
+                    'visible' => true,
+                    'user_defined' => true,
+                    'searchable' => false,
+                    'filterable' => false,
+                    'comparable' => false,
+                    'used_in_product_listing' => true,
+                    'apply_to' => '',
+                    'source' => \Kai\Banner\Model\KaiBannerValues::class,
+                    'global' => ScopedAttributeInterface::SCOPE_GLOBAL,
+                    'group' => 'General',
+                ]
+            );
+            $setup->endSetup();
+        }
+    }
+}
diff --git a/app/code/Kai/Banner/Setup/UpgradeSchema.php b/app/code/Kai/Banner/Setup/UpgradeSchema.php
new file mode 100755
index 00000000..414a018b
--- /dev/null
+++ b/app/code/Kai/Banner/Setup/UpgradeSchema.php
@@ -0,0 +1,94 @@
+startSetup();
+
+        $tableName = $installer->getTable('kai_banner');
+
+        if ($installer->tableExists($tableName)) {
+            $installer->getConnection()->dropTable($tableName);
+        }
+
+        $table = $installer->getConnection()
+            ->newTable($tableName)
+            ->addColumn(
+                'id',
+                Table::TYPE_INTEGER,
+                null,
+                [
+                    'identity' => true,
+                    'unsigned' => true,
+                    'nullable' => false,
+                    'primary' => true,
+                ],
+                'ID'
+            )
+            ->addColumn(
+                'title',
+                Table::TYPE_TEXT,
+                null,
+                ['nullable' => false, 'default' => ''],
+                'Title'
+            )
+            ->addColumn(
+                'html',
+                Table::TYPE_TEXT,
+                null,
+                ['nullable' => true, 'default' => ''],
+                'htmlentities($str)'
+            )
+            ->addColumn(
+                'redirect',
+                Table::TYPE_TEXT,
+                null,
+                ['nullable' => true, 'default' => ''],
+                'URL redirect'
+            )
+            ->addColumn(
+                'created_at',
+                Table::TYPE_TIMESTAMP,
+                null,
+                [
+                    'nullable' => true,
+                    'default' => Table::TIMESTAMP_INIT,
+                ],
+                'Created At'
+            )
+            ->addColumn(
+                'updated_at',
+                Table::TYPE_TIMESTAMP,
+                null,
+                [
+                    'nullable' => true,
+                    'default' => Table::TIMESTAMP_INIT_UPDATE,
+                ],
+                'Updated At'
+            )
+            ->addColumn(
+                'status',
+                Table::TYPE_SMALLINT,
+                null,
+                [
+                    'nullable' => false,
+                    'default' => '0',
+                ],
+                'Status'
+            )->setComment('Kai Banner Module');
+        $installer->getConnection()->createTable($table);
+        $installer->endSetup();
+    }
+}
diff --git a/app/code/Kai/Banner/etc/adminhtml/acl.xml b/app/code/Kai/Banner/etc/adminhtml/acl.xml
new file mode 100755
index 00000000..7d4c29d1
--- /dev/null
+++ b/app/code/Kai/Banner/etc/adminhtml/acl.xml
@@ -0,0 +1,25 @@
+
+
| =$thead['label'] ?>+ + | |||||||
|---|---|---|---|---|---|---|---|
| =$row['id'] ?>+ | + =$row['title'] ?> ++ | + +
+                            =html_entity_decode($row['html']) ?>
+                        + | + =$row['redirect'] ?> ++ | + =$row['created_at'] ?> ++ | + =$row['updated_at'] ?> ++ | + =$row['status'] ?> ++ | + +
+                            
+                            
+                        + | 
= $block->getCustomAttribute() ?>
+Kai Widget Hello, I am a custom widget!
+ diff --git a/app/code/Kai/Widget/view/layout/my_module_index_index.xml b/app/code/Kai/Widget/view/layout/my_module_index_index.xml new file mode 100755 index 00000000..14ad8afd --- /dev/null +++ b/app/code/Kai/Widget/view/layout/my_module_index_index.xml @@ -0,0 +1,11 @@ +