diff --git a/.env.example b/.env.example
index 5cec9b4a1d78e9b6136e90785bd5c07691bcb62c..60c78b99cd6347e4bd43a5ed70b944666c8089c2 100644
--- a/.env.example
+++ b/.env.example
@@ -1,6 +1,13 @@
-MYSQL_USER=
-MYSQL_PASSWORD=
-MYSQL_ROOT_PASSWORD=
-MYSQL_DATABASE=
+MYSQL_USER=podcastify
+MYSQL_PASSWORD=podcastify
+MYSQL_ROOT_PASSWORD=podcastify
+MYSQL_DATABASE=podcastify
 MYSQL_PORT=3306
-MYSQL_HOST=podcastify-app-db
\ No newline at end of file
+MYSQL_HOST=podcastify-app-db
+
+REST_SERVICE_URL=http://host.docker.internal:4444/api/v1
+SOAP_SERVICE_URL=http://host.docker.internal:5555/api/v1
+
+APP_API_KEY=139cfa2f-3742-4b43-815e-f7bc6ecb8af5
+
+ENCRYPTION_KEY=c4a3f7b4e0c2460b8e4f8588dbf38f6a
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index ac3bc2bfd0031f60568ae11756e212d0d9f36d91..ac090f5c8cb21e9caebc7851ad5319449919cb81 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,7 +3,7 @@ FROM php:8.0-apache
 RUN apt-get update && \
     apt-get upgrade -y && \
     apt-get install -y libxml2-dev --no-install-recommends mediainfo && \
-    docker-php-ext-install mysqli pdo pdo_mysql soap
+    docker-php-ext-install mysqli pdo pdo_mysql soap php-gd
 
 WORKDIR /var/www/html
 
diff --git a/README.md b/README.md
index b2fe97c6c1f0d58c4a12dbdec44fab6c3cfbe16b..fe749c256a1b25d70edd1746c6906a518e671ca2 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,11 @@ Podcastify is your go-to application for daily podcasts. With a simple and intui
 3. <b>Podcast Player</b>: Each episode of the chosen podcast can be listened to directly within the app, providing a seamless listening experience.
 4. <b>Admin Panel</b>: Administrators have the ability to manage podcasts, episodes, and users, ensuring the content is up-to-date and the user experience is optimized.
 
+## Updates (17 November 2023)
+We’ve added some exciting new features:
+1. <b>Memberships</b>: Users can now subscribe to their favorite creators and gain access to premium episodes.
+2. <b>User Engagement Features</b>: Users can now engage more with creators by liking or commenting on episodes.
+
 ## Tech Stacks
 Podcastify is built using the following technologies:
 
@@ -64,6 +69,19 @@ Podcastify is built using the following technologies:
 | Navbars and Home Page               |                        |                            |
 | Responsiveness                      |                        |                            |
 
+### Updates (17 November 2023)
+### Server side
+| 13521055                            | 13521072                | 13521102                   |
+| ----------------------------------- | :---------------------: | -------------------------- |
+| Memberships                         | Episode Premium Details | Subscribe                  |
+| Additional Middleware               |                         | Push Notifications         |
+
+### Client Side
+| 13521055                            | 13521072                  | 13521102                   |
+| ----------------------------------- | :-----------------------: | -------------------------- |
+| Memberships Page                    | Episode Premium Page      |                            |
+|                                     | Episode Like & Comment UI |                            |
+
 ## Responsive Layouts & Lighthouse
 <div align="center">
     <h1>Responsive Layouts</h1>
@@ -161,3 +179,6 @@ Podcastify is built using the following technologies:
     <br/>
 </b>
 </div>
+
+## Copyright
+2023 © Podcastify. All Rights Reserved.
diff --git a/migration/init.sql b/migration/init.sql
index 4e9045df4e458da444336f8a1d550183bd6a0528..9ff0319e857bb084deca5c190f37b282477e08b8 100644
--- a/migration/init.sql
+++ b/migration/init.sql
@@ -104,14 +104,23 @@ INSERT INTO podcasts (title, description, creator_name, category_id) VALUES
 INSERT INTO episodes (podcast_id, category_id, title, description, duration, audio_url) VALUES
     (1, 1, 'KISAH WANITA BERMATA TUJUH!', 'Ada yang LASER!! Ghina Khansa', 3000, ''),
     (2, 4, 'Jalanin terus, mau sampe kapan?', 'Malam hari ini ada cerita dari Anka (namanya sudah disamarkan) Anka menceritakan hubungan tanpa status yang dijalani dengan sahabat dari mantan pacarnya. Seperti apa ceritanya? Selamat mendengarkan dan selamat tidur!', 1800, ''),
-    (3, 1, 'Zeppelin', 'Ledding a zepplin could be dangerous', 60, ''), 
-    (7, 1, 'Crying', 'Crying out loud is the best way to relief stress', 60, ''), 
-    (8, 1, 'Radio or Head', 'Radiohead it is!', 6000, ''), 
-    (6, 1, 'Daily Dose', 'Daily Dose Of Internet!', 60, ''), 
-    (7, 1, 'Serial The Killer', 'Jealousy, turn in things into ...', 1260, ''), 
-    (10, 1, '8EH? BEH?', 'Anything', 6060, ''), 
-    (5, 1, 'Dummy Ex Dummy', 'Dummy is for Dummies', 600, ''), 
-    (9, 1, 'Tim the human', 'Tim Ferries is french fries', 60, ''), 
-    (1, 1, 'Ice Cold', 'Is it her? Or him?', 60, ''), 
-    (2, 1, 'Eppy Call', 'Zzzzz Zzzzz Zzzz', 1560, ''), 
+    (3, 1, 'Zeppelin', 'Ledding a zepplin could be dangerous', 60, ''),
+    (7, 1, 'Crying', 'Crying out loud is the best way to relief stress', 60, ''),
+    (8, 1, 'Radio or Head', 'Radiohead it is!', 6000, ''),
+    (6, 1, 'Daily Dose', 'Daily Dose Of Internet!', 60, ''),
+    (7, 1, 'Serial The Killer', 'Jealousy, turn in things into ...', 1260, ''),
+    (10, 1, '8EH? BEH?', 'Anything', 6060, ''),
+    (5, 1, 'Dummy Ex Dummy', 'Dummy is for Dummies', 600, ''),
+    (9, 1, 'Tim the human', 'Tim Ferries is french fries', 60, ''),
+    (1, 1, 'Ice Cold', 'Is it her? Or him?', 60, ''),
+    (2, 1, 'Eppy Call', 'Zzzzz Zzzzz Zzzz', 1560, ''),
     (10, 1, 'Freakaholics', 'Holiicann', 4560, '');
+
+CREATE TABLE subscription_notifications (
+    creator_id          INT             NOT NULL,
+    creator_name        VARCHAR(255)    NOT NULL,
+    user_id             INT             NOT NULL,
+    status              VARCHAR(255)    NOT NULL,
+    has_seen            BOOL            NOT NULL DEFAULT FALSE,
+    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
+);
diff --git a/php.ini b/php.ini
index 39eae5df0ab67798d000a4769046c855fdf82405..1f25dad2fbbb3bd6551fcc1955efb856eafa8db8 100644
--- a/php.ini
+++ b/php.ini
@@ -1,2 +1,3 @@
 post_max_size=32M
-upload_max_filesize=32M
\ No newline at end of file
+upload_max_filesize=32M
+extension=gd
\ No newline at end of file
diff --git a/src/app/controllers/MembershipController.php b/src/app/controllers/MembershipController.php
new file mode 100644
index 0000000000000000000000000000000000000000..56dea3d6ff244a1f12ee707283df738eb3a99c94
--- /dev/null
+++ b/src/app/controllers/MembershipController.php
@@ -0,0 +1,616 @@
+<?php
+
+require_once SERVICES_DIR . 'auth/index.php';
+require_once SERVICES_DIR . 'user/index.php';
+require_once COMPONENTS_PRIVATES_DIR . 'membership/cards.php';
+require_once COMPONENTS_PRIVATES_DIR . 'membership/tables.php';
+require_once COMPONENTS_PRIVATES_DIR . 'episode/episode_detail.php';
+
+class MembershipController extends BaseController
+{
+  public function index()
+  {
+    try {
+      Middleware::checkIsLoggedIn();
+      Middleware::checkIsNotAdmin();
+
+      switch ($_SERVER['REQUEST_METHOD']) {
+        case "GET":
+          $this->cards();
+          break;
+      }
+    } catch (Exception $e) {
+      if ($e->getCode() == ResponseHelper::HTTP_STATUS_UNAUTHORIZED) {
+        $this->view('layouts/error');
+        return;
+      }
+
+      http_response_code($e->getCode());
+      $response = array("success" => false, "error_message" => $e->getMessage());
+      echo json_encode($response);
+      exit;
+    }
+  }
+
+  private function cards()
+  {
+    try {
+      Middleware::checkIsLoggedIn();
+      Middleware::checkIsNotAdmin();
+
+      switch ($_SERVER['REQUEST_METHOD']) {
+        case "GET":
+          $userId = $_SESSION['user_id'];
+
+          $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
+
+          $encryptedUserId = openssl_encrypt($userId, 'aes-256-cbc', ENCRYPTION_KEY, OPENSSL_RAW_DATA, $iv);
+
+          // Concatenate IV and encrypted user ID
+          $combinedData = $iv . $encryptedUserId;
+
+          // Encode to send safely in headers
+          $base64CombinedData = base64_encode($combinedData);
+
+          $headers = [
+            'x-api-key: ' . APP_API_KEY,
+            'Authorization: Bearer ' . $base64CombinedData,
+            'Content-Type: application/json'
+          ];
+
+          $data['currentPage'] = 1;
+
+          if (isset($_GET['page'])) {
+            $data['currentPage'] = $_GET['page'];
+          }
+
+          $apiUrl = REST_SERVICE_URL . '/creator?page=' . $data['currentPage'] . '&limit=8';
+
+          $ch = curl_init($apiUrl);
+          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+          curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+          $response = curl_exec($ch);
+          $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+          curl_close($ch);
+
+          if ($httpCode === ResponseHelper::HTTP_STATUS_OK) {
+            // Successful API response, handle the data
+            $responseData = json_decode($response, true);
+
+            if ($responseData !== null) {
+              $data['currentPage'] = $responseData['data']['current_page'];
+              $data['totalPages'] = $responseData['data']['last_page'];
+              $data['creators'] = $responseData['data']['data'];
+
+              if (isset($_GET['page'])) {
+                return renderCreatorCardList($data['creators']);
+              }
+            } else {
+              echo "Error decoding JSON response";
+            }
+          } else {
+            throw new Exception('Bad Request', $httpCode);
+          }
+
+          $this->view('layouts/default', $data);
+          break;
+        default:
+          ResponseHelper::responseNotAllowedMethod();
+          break;
+      }
+    } catch (Exception $e) {
+      if ($e->getCode() == ResponseHelper::HTTP_STATUS_UNAUTHORIZED) {
+        $this->view('layouts/error');
+        return;
+      }
+
+      http_response_code($e->getCode());
+      $response = array("success" => false, "error_message" => $e->getMessage());
+      echo json_encode($response);
+      exit;
+    }
+  }
+
+  public function creator($creatorId)
+  {
+    if ($creatorId !== null) {
+      $creatorId = filter_var($creatorId, FILTER_SANITIZE_NUMBER_INT);
+      try {
+        Middleware::checkIsLoggedIn();
+        Middleware::checkIsNotAdmin();
+
+        switch ($_SERVER['REQUEST_METHOD']) {
+          case "GET":
+            $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
+
+            $encryptedUserId = openssl_encrypt($_SESSION['user_id'], 'aes-256-cbc', ENCRYPTION_KEY, OPENSSL_RAW_DATA, $iv);
+
+            // Concatenate IV and encrypted user ID
+            $combinedData = $iv . $encryptedUserId;
+
+            // Encode to send safely in headers
+            $base64CombinedData = base64_encode($combinedData);
+
+            $headers = [
+              'x-api-key: ' . APP_API_KEY,
+              'Authorization: Bearer ' . $base64CombinedData,
+              'Content-Type: application/json'
+            ];
+
+            $data['currentPage'] = 1;
+
+            if (isset($_GET['page'])) {
+              $data['currentPage'] = $_GET['page'];
+            }
+
+            // Get list of prem episodes
+            $apiUrl = REST_SERVICE_URL . '/episode/creator/' . $creatorId . '?page=' . $data['currentPage'] . '&limit=10';
+
+            $ch = curl_init($apiUrl);
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+            $response = curl_exec($ch);
+            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+            curl_close($ch);
+
+            if ($httpCode === ResponseHelper::HTTP_STATUS_OK) {
+              // Successful API response, handle the data
+              $responseData = json_decode($response, true);
+
+              if ($responseData !== null) {
+                $data['currentPage'] = $responseData['data']['current_page'];
+                $data['totalPages'] = $responseData['data']['last_page'];
+                $data['episodes'] = $responseData['data']['data']; // Fetch episodes data
+
+                // Get Files
+                $i = 0;
+                foreach ($data['episodes'] as $episode) {
+                  $apiUrlImage = REST_SERVICE_URL . '/episode/downloadImage/' . $episode['episode_id'];
+
+                  if ($episode['image_url']) {
+                    $ch = curl_init($apiUrlImage);
+                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+                    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+                    $responseImage = curl_exec($ch);
+                    $httpCodeImage = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+                    $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
+
+                    curl_close($ch);
+
+                    if ($httpCodeImage === ResponseHelper::HTTP_STATUS_OK) {
+                      $base64 = base64_encode($responseImage);
+                      $imageDataUrl = 'data:' . $contentType . ';base64,' . $base64;
+
+                      $data['episodes'][$i]['imageFile'] = $imageDataUrl;
+                      // file_put_contents(STORAGES_DIR . 'episode/images/' . $data['episode']['image_url'], $responseImage);
+                    } else {
+                      $data['episodes'][$i]['imageFile'] = null;
+                    }
+                  } else {
+                    $data['episodes'][$i]['imageFile'] = null;
+                  }
+
+                  $i++;
+                }
+              } else {
+                echo "Error decoding JSON response";
+              }
+            } else {
+              throw new Exception('Bad Request', $httpCode);
+            }
+
+            // Get creator
+            $apiUrl2 = REST_SERVICE_URL . '/creator/' . $creatorId;
+            $ch2 = curl_init($apiUrl2);
+            curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($ch2, CURLOPT_HTTPHEADER, $headers);
+
+            $response2 = curl_exec($ch2);
+            $httpCode2 = curl_getinfo($ch2, CURLINFO_HTTP_CODE);
+
+            curl_close($ch2);
+
+            if ($httpCode2 === ResponseHelper::HTTP_STATUS_OK) {
+              // Successful API response, handle the data
+              $responseData = json_decode($response2, true);
+
+              if ($responseData !== null) {
+                $data['creator'] = $responseData['data'];
+
+                if (isset($_GET['page'])) {
+                  return renderEpisodesTable($data['episodes'], $data['creator']['status'], $data['currentPage']);
+                }
+
+              } else {
+                echo "Error decoding JSON response";
+              }
+            } else {
+              throw new Exception('Bad Request', $httpCode);
+            }
+
+            $this->view('layouts/default', $data);
+            break;
+          default:
+            ResponseHelper::responseNotAllowedMethod();
+            break;
+        }
+      } catch (Exception $e) {
+        if ($e->getCode() == ResponseHelper::HTTP_STATUS_UNAUTHORIZED) {
+          $this->view('layouts/error');
+          return;
+        }
+
+        http_response_code($e->getCode());
+        $response = array("success" => false, "error_message" => $e->getMessage());
+        echo json_encode($response);
+        exit;
+      }
+    } else {
+      $this->view('layouts/error');
+      return;
+    }
+  }
+
+  public function subscribe() {
+
+    try {
+        switch ($_SERVER['REQUEST_METHOD']) {
+            case "POST":
+                Middleware::checkIsLoggedIn();
+                Middleware::checkIsNotAdmin();
+
+                $data = json_decode(file_get_contents('php://input'), true);
+                $creator_id = $data['creator_id'];
+                $creator_name = $data['creator_name'];
+                $subscriber_id = $_SESSION['user_id'];
+                $subscriber_name = $_SESSION['username'];
+
+                $apiUrl = SOAP_SERVICE_URL . '/subscription';
+                $envelope = <<<EOT
+                <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.podcastify.com/">
+                    <soapenv:Header/>
+                    <soapenv:Body>
+                    <ser:subscribe>
+                        <subscriber_id>$subscriber_id</subscriber_id>
+                        <creator_id>$creator_id</creator_id>
+                        <subscriber_name>$subscriber_name</subscriber_name>
+                        <creator_name>$creator_name</creator_name>
+                    </ser:subscribe>
+                    </soapenv:Body>
+                </soapenv:Envelope>
+                EOT;
+
+                $ch = curl_init();
+
+                // Setup to send request to SOAP service
+                curl_setopt($ch, CURLOPT_URL, $apiUrl);
+                curl_setopt($ch, CURLOPT_POST, true);
+                curl_setopt($ch, CURLOPT_HTTPHEADER, array(
+                    'Content-Type: text/xml',
+                    'x-api-key: ' . APP_API_KEY,
+                ));
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $envelope);
+
+                // Return the response instead of outputting it
+                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+
+                $response = curl_exec($ch);
+                curl_close($ch);
+
+                // Get response status
+                $xml = new SimpleXMLElement($response);
+
+                // Register the namespaces
+                $xml->registerXPathNamespace('ns2', 'http://service.podcastify.com/');
+
+                // Extract the statusCode and message
+                $statusCode = $xml->xpath('//ns2:subscribeResponse/return/statusCode')[0];
+                if ($statusCode == 202) {
+                    $response = array("success" => true, "message" => "Succesfully sent a subscription request");
+                    http_response_code(ResponseHelper::HTTP_STATUS_OK);
+                    header('Content-Type: application/json');
+
+                    echo json_encode($response);
+                } else {
+                    throw new Exception();
+                }
+                break;
+
+                default:
+                    ResponseHelper::responseNotAllowedMethod();
+                    break;
+        }
+    } catch (Exception $e) {
+        $response = array("success" => false, "message" => "Failed to sent a subscription request");
+        http_response_code(ResponseHelper::HTTP_STATUS_BAD_REQUEST);
+        header('Content-Type: application/json');
+
+        echo json_encode($response);
+    }
+  }
+
+  public function prem_episode($episode_id) {
+    if ($episode_id !== null) {
+      $episode_id = filter_var($episode_id, FILTER_SANITIZE_NUMBER_INT);
+      try {
+        Middleware::checkIsLoggedIn();
+        Middleware::checkIsNotAdmin();
+
+        switch ($_SERVER['REQUEST_METHOD']) {
+          case "GET":
+            $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
+
+            $encryptedUserId = openssl_encrypt($_SESSION['user_id'], 'aes-256-cbc', ENCRYPTION_KEY, OPENSSL_RAW_DATA, $iv);
+
+            // Concatenate IV and encrypted user ID
+            $combinedData = $iv . $encryptedUserId;
+
+            // Encode to send safely in headers
+            $base64CombinedData = base64_encode($combinedData);
+
+            $headers = [
+              'x-api-key: ' . APP_API_KEY,
+              'Authorization: Bearer ' . $base64CombinedData,
+              'Content-Type: application/json'
+            ];
+
+            // Get prem episode
+            $apiUrl = REST_SERVICE_URL . "/episode/" . $episode_id;
+
+            $ch = curl_init($apiUrl);
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+            $response = curl_exec($ch);
+            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+            curl_close($ch);
+
+            if ($httpCode === ResponseHelper::HTTP_STATUS_OK) {
+              // Successful API response, handle the data
+              $responseData = json_decode($response, true);
+
+              if ($responseData !== null) {
+                $data['episode'] = $responseData['data'];
+
+                if (isset($responseData['data']['episodeComments'])) {
+                  foreach ($responseData['data']['episodeComments'] as $key => $comment) {
+                    if (isset($comment['comment_text']) && is_string($comment['comment_text'])) {
+                      $sanitizedCommentText = htmlspecialchars(strip_tags($comment['comment_text']));
+                      $responseData['data']['episodeComments'][$key]['comment_text'] = $sanitizedCommentText;
+                    }
+                  }
+                }
+
+                // Assign sanitized comments to $data['episode']['episodeComments']
+                $data['episode']['episodeComments'] = $responseData['data']['episodeComments'];
+
+                // Get Files
+                $apiUrlImage = REST_SERVICE_URL . '/episode/downloadImage/' . $data['episode']['episode_id'];
+
+                if ($data['episode']['image_url']) {
+                  $ch = curl_init($apiUrlImage);
+                  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+                  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+                  $responseImage = curl_exec($ch);
+                  $httpCodeImage = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+                  $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
+
+                  curl_close($ch);
+
+                  if ($httpCodeImage === ResponseHelper::HTTP_STATUS_OK) {
+                    $base64 = base64_encode($responseImage);
+                    $imageDataUrl = 'data:' . $contentType . ';base64,' . $base64;
+                    $data['episode']['imageFile'] = $imageDataUrl;
+                  } else {
+                    $data['episode']['imageFile'] = null;
+                  }
+                } else {
+                  $data['episode']['imageFile'] = null;
+                }
+
+                $apiUrlAudio = REST_SERVICE_URL . '/episode/downloadAudio/' . $data['episode']['episode_id'];
+
+                if ($data['episode']['audio_url']) {
+                  $ch = curl_init($apiUrlAudio);
+                  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+                  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+
+                  $responseAudio = curl_exec($ch);
+                  $httpCodeAudio = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+                  $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
+
+                  curl_close($ch);
+
+                  if ($httpCodeAudio === ResponseHelper::HTTP_STATUS_OK) {
+                    $base64 = base64_encode($responseAudio);
+                    $audioDataUrl = 'data:' . $contentType . ';base64,' . $base64;
+                    $data['episode']['audioFile'] = $audioDataUrl;
+                  } else {
+                    $data['episode']['audioFile'] = null;
+                  }
+                } else {
+                  $data['episode']['audioFile'] = null;
+                }
+              } else {
+                echo "Error decoding JSON response";
+              }
+            } else {
+              throw new Exception('Bad Request', $httpCode);
+            }
+
+            $this->view('layouts/default', $data);
+            break;
+          default:
+            ResponseHelper::responseNotAllowedMethod();
+            break;
+        }
+      } catch (Exception $e) {
+        if ($e->getCode() == ResponseHelper::HTTP_STATUS_UNAUTHORIZED) {
+          $this->view('layouts/error');
+          return;
+        }
+
+        http_response_code($e->getCode());
+        $response = array("success" => false, "error_message" => $e->getMessage());
+        echo json_encode($response);
+        $this->view('layouts/error');
+        exit;
+      }
+    } else {
+      $this->view('layouts/error');
+      return;
+    }
+  }
+
+  public function comment() {
+    try {
+        switch ($_SERVER['REQUEST_METHOD']) {
+            case "POST":
+                Middleware::checkIsLoggedIn();
+                Middleware::checkIsNotAdmin();
+
+                $postData = json_decode(file_get_contents('php://input'), true);
+                $episode_id = $postData['episode_id'];
+                $username = $postData['username'];
+                $comment_text = $postData['comment_text'];
+
+                $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
+
+                $encryptedUserId = openssl_encrypt($_SESSION['user_id'], 'aes-256-cbc', ENCRYPTION_KEY, OPENSSL_RAW_DATA, $iv);
+
+                // Concatenate IV and encrypted user ID
+                $combinedData = $iv . $encryptedUserId;
+
+                // Encode to send safely in headers
+                $base64CombinedData = base64_encode($combinedData);
+
+                $headers = [
+                  'x-api-key: ' . APP_API_KEY,
+                  'Authorization: Bearer ' . $base64CombinedData,
+                  'Content-Type: application/json'
+                ];
+
+                $postData = [
+                  'episode_id' => $episode_id,
+                  'username' => $username,
+                  'comment_text' => $comment_text,
+                ];
+
+                $jsonData = json_encode($postData);
+                // var_dump($jsonData);
+                // Get list of prem episodes
+                $apiUrl = REST_SERVICE_URL . "/episode/comment";
+
+                $ch = curl_init($apiUrl);
+                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+                curl_setopt($ch, CURLOPT_POST, 1);
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
+
+                $response = curl_exec($ch);
+                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+                curl_close($ch);
+
+                if ($httpCode === ResponseHelper::HTTP_STATUS_CREATED) {
+                    $response = array("success" => true, "message" => "Comment Submitted");
+                    http_response_code(ResponseHelper::HTTP_STATUS_OK);
+                    header('Content-Type: application/json');
+
+                    echo json_encode($response);
+                } else {
+                  throw new Exception('Bad Request', $httpCode);
+                }
+              break;
+
+            default:
+              ResponseHelper::responseNotAllowedMethod();
+              break;
+        }
+    } catch (Exception $e) {
+        $response = array("success" => false, "message" => "Failed to sent a comment request");
+        http_response_code(ResponseHelper::HTTP_STATUS_BAD_REQUEST);
+        header('Content-Type: application/json');
+
+        echo json_encode($response);
+    }
+  }
+
+  public function like() {
+    try {
+        switch ($_SERVER['REQUEST_METHOD']) {
+            case "POST":
+                Middleware::checkIsLoggedIn();
+                Middleware::checkIsNotAdmin();
+
+                $postData = json_decode(file_get_contents('php://input'), true);
+                $episode_id = $postData['episode_id'];
+
+                $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
+
+                $encryptedUserId = openssl_encrypt($_SESSION['user_id'], 'aes-256-cbc', ENCRYPTION_KEY, OPENSSL_RAW_DATA, $iv);
+
+                // Concatenate IV and encrypted user ID
+                $combinedData = $iv . $encryptedUserId;
+
+                // Encode to send safely in headers
+                $base64CombinedData = base64_encode($combinedData);
+
+                $headers = [
+                  'x-api-key: ' . APP_API_KEY,
+                  'Authorization: Bearer ' . $base64CombinedData,
+                  'Content-Type: application/json'
+                ];
+
+                $postData = [
+                  'episode_id' => $episode_id,
+                ];
+
+                $jsonData = json_encode($postData);
+                // Get list of prem episodes
+                $apiUrl = REST_SERVICE_URL . "/episode/like";
+
+                $ch = curl_init($apiUrl);
+                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+                curl_setopt($ch, CURLOPT_POST, 1);
+                curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
+
+                $response = curl_exec($ch);
+                $responseInfo = json_decode($response, true);
+
+                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+                curl_close($ch);
+
+                if ($httpCode === ResponseHelper::HTTP_STATUS_OK) {
+                    $response = array("success" => true, "message" => $responseInfo['message']);
+                    http_response_code(ResponseHelper::HTTP_STATUS_OK);
+                    header('Content-Type: application/json');
+
+                    echo json_encode($response);
+                } else {
+                  throw new Exception('Bad Request', $httpCode);
+                }
+              break;
+
+            default:
+              ResponseHelper::responseNotAllowedMethod();
+              break;
+        }
+    } catch (Exception $e) {
+        $response = array("success" => false, "message" => "Failed to sent a like request");
+        http_response_code(ResponseHelper::HTTP_STATUS_BAD_REQUEST);
+        header('Content-Type: application/json');
+
+        echo json_encode($response);
+    }
+  }
+}
diff --git a/src/app/controllers/PodcastController.php b/src/app/controllers/PodcastController.php
index 6b679deb658aedeb9215022913e9a92fd400e8ea..371347ce61ffafcca49825d07e6b07fe4a6ae620 100644
--- a/src/app/controllers/PodcastController.php
+++ b/src/app/controllers/PodcastController.php
@@ -171,9 +171,9 @@ class PodcastController extends BaseController
 
     // /edit/{id}
     public function edit($id) {
-        Middleware::checkIsAdmin();
-
         try {
+            Middleware::checkIsAdmin();
+            Middleware::checkIsLoggedIn();
             $id = filter_var($id, FILTER_SANITIZE_NUMBER_INT);
 
             switch ($_SERVER["REQUEST_METHOD"]) {
@@ -231,9 +231,10 @@ class PodcastController extends BaseController
 
     // /add
     public function add() {
-        Middleware::checkIsAdmin();
 
         try {
+            Middleware::checkIsAdmin();
+            Middleware::checkIsLoggedIn();
             switch ($_SERVER["REQUEST_METHOD"]) {
                 case "GET":
                     $data["type"] = "create";
diff --git a/src/app/controllers/SubscriptionController.php b/src/app/controllers/SubscriptionController.php
new file mode 100644
index 0000000000000000000000000000000000000000..453e0dd7ff5619a1b90428ca6338a76fa7e46b03
--- /dev/null
+++ b/src/app/controllers/SubscriptionController.php
@@ -0,0 +1,55 @@
+<?php
+require_once SERVICES_DIR . "/subscription/index.php";
+
+class SubscriptionController extends BaseController
+{
+
+    private $subscription_service;
+
+    public function __construct() {
+        $this->subscription_service = new SubscriptionService();
+    }
+
+    public function index() {
+        try {
+            switch ($_SERVER['REQUEST_METHOD']) {
+                case "GET":
+                    Middleware::checkIsLoggedIn();
+
+                    $userID = $_SESSION['user_id'];
+
+                    $newNotification = $this->subscription_service->getAllNewNotification($userID);
+
+                    $response = array("success" => true, "data" => $newNotification);
+                    http_response_code(ResponseHelper::HTTP_STATUS_OK);
+                    header('Content-Type: application/json');
+
+                    echo json_encode($response);
+                    break;
+
+                case "POST":
+                    // Parse the incoming request
+                    parse_str(file_get_contents("php://input"),$post_vars);
+
+                    $userID = $post_vars['subscriber_id'];
+                    $creatorID = $post_vars['creator_id'];
+                    $creatorName = $post_vars['creator_name'];
+                    $status = $post_vars['status'];
+
+                    $this->subscription_service->addNewNotification($userID, $creatorID, $creatorName, $status);
+
+                    break;
+                default:
+                    ResponseHelper::responseNotAllowedMethod();
+                    return;
+
+            }
+        } catch (Exception $e) {
+            $response = array("success" => false, "data" => []);
+            header('Content-Type: application/json');
+
+            echo json_encode($response);
+            exit;
+        }
+    }
+}
diff --git a/src/app/controllers/UserController.php b/src/app/controllers/UserController.php
index cdd792a60b281903a7780e8f98559328e8031739..80abd4bd92ac716b6803ff827724109bc65f61a6 100644
--- a/src/app/controllers/UserController.php
+++ b/src/app/controllers/UserController.php
@@ -9,7 +9,7 @@ class UserController extends BaseController {
     {
         try {
             Middleware::checkIsLoggedIn();
-            
+
             switch ($_SERVER['REQUEST_METHOD']) {
                 case "GET":
                     if ($userId !== null) {
@@ -53,10 +53,10 @@ class UserController extends BaseController {
                         $currentAvatarURL = $userService->getUserAvatar($userId);
 
                         $userService->updatePersonalInfo(
-                            $userId, 
-                            $email, 
-                            $username, 
-                            $firstName, 
+                            $userId,
+                            $email,
+                            $username,
+                            $firstName,
                             $lastName,
                             $imageUrl
                         );
@@ -253,4 +253,31 @@ class UserController extends BaseController {
         $hashedPassword = password_hash($password, PASSWORD_BCRYPT);
         return $hashedPassword;
     }
-}
\ No newline at end of file
+
+    public function self() {
+        try {
+            Middleware::checkIsLoggedIn();
+
+            switch ($_SERVER['REQUEST_METHOD']) {
+                case "GET":
+                    $userID = $_SESSION['user_id'];
+                    $username = $_SESSION['username'];
+
+                    $response = array("success" => true, "data" => ["user_id" => $userID, "username" => $username]);
+                    http_response_code(ResponseHelper::HTTP_STATUS_OK);
+                    header('Content-Type: application/json');
+
+                    echo json_encode($response);
+                    break;
+                default:
+                    ResponseHelper::responseNotAllowedMethod();
+                    break;
+            }
+        } catch (Exception $e) {
+            $response = array("success" => true, "data" => ["user_id" => "-1", "username" => ""]);
+            header('Content-Type: application/json');
+
+            echo json_encode($response);
+        }
+    }
+}
diff --git a/src/app/middlewares/Middleware.php b/src/app/middlewares/Middleware.php
index 454ba24d63b10e370e9ce5a6abfab682508c856c..6d7986bae6d82f504ebe0d5c7169737dcc126ac2 100644
--- a/src/app/middlewares/Middleware.php
+++ b/src/app/middlewares/Middleware.php
@@ -42,6 +42,14 @@ class Middleware {
         return;
     }
 
+    public static function checkIsNotAdmin()
+    {
+        if (isset($_SESSION['role']) && $_SESSION['role'] == 'admin') {
+            throw new Exception('Unauthorized', ResponseHelper::HTTP_STATUS_UNAUTHORIZED);
+        }
+        return;
+    }
+
     public static function checkIsLoggedIn()
     {
         if (!isset($_SESSION['username']) && !isset($_SESSION['user_id'])) {
diff --git a/src/app/models/Subscription.php b/src/app/models/Subscription.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a663200e695a121d36964d05828ca20139d80ef
--- /dev/null
+++ b/src/app/models/Subscription.php
@@ -0,0 +1,70 @@
+<?php
+
+require_once BASE_URL . '/src/config/database.php';
+
+class Subscription {
+    private $db;
+
+    public function __construct()
+    {
+        $this->db = new Database();
+    }
+
+    public function findAllNewNotifications($userID)
+    {
+        $query = "SELECT * FROM subscription_notifications WHERE user_id = :user_id AND has_seen = 0";
+        $this->db->query($query);
+
+        $this->db->bind("user_id", $userID);
+
+        $result = $this->db->fetchAll();
+
+        return $result;
+    }
+
+    public function setAllToSeen($userID)
+    {
+        $query = "UPDATE subscription_notifications SET has_seen = 1 WHERE user_id = :user_id";
+
+        $this->db->query($query);
+        $this->db->bind("user_id", $userID);
+        $this->db->execute();
+    }
+
+    public function deleteRejectedNotification($userID, $creatorID) {
+        $query = "DELETE FROM subscription_notifications WHERE status = 'REJECTED' AND user_id = :user_id AND creator_id = :creator_id";
+
+        $this->db->query($query);
+
+        $this->db->bind(":user_id", $userID);
+        $this->db->bind(":creator_id", $creatorID);
+
+        $this->db->execute();
+    }
+
+    public function deleteAllRejectedNotification($userID) {
+        $query = "DELETE FROM subscription_notifications WHERE status = 'REJECTED' AND user_id = :user_id";
+
+        $this->db->query($query);
+
+        $this->db->bind(":user_id", $userID);
+
+        $this->db->execute();
+    }
+
+    public function addNewNotification($userID, $creatorID, $creatorName, $status) {
+        $query = "INSERT INTO subscription_notifications (user_id, creator_id, creator_name, status) VALUES (:user_id, :creator_id, :creator_name, :status)";
+        $this->db->query($query);
+
+        $this->db->bind(":user_id", $userID);
+        $this->db->bind(":creator_id", $creatorID);
+        $this->db->bind(":creator_name", $creatorName);
+        $this->db->bind(":status", $status);
+
+        $this->db->execute();
+
+        if ($this->db->rowCount() == 0) {
+            throw new Exception("Failed to create a notification", 500);
+        }
+    }
+}
diff --git a/src/app/services/category/CategoryService.php b/src/app/services/category/CategoryService.php
index c353de9a45d16cc88121df909d932e0e003609c1..db9f6d11482de86fb499b87994418509ca45598b 100644
--- a/src/app/services/category/CategoryService.php
+++ b/src/app/services/category/CategoryService.php
@@ -38,8 +38,4 @@ class CategoryService {
 
     return $categories;
   }
-
-  public function getCategoryOfEpisode($episodeId){
-    return $this->category_model->getCategoryOfEpisode($episodeId);
-  }
 }
\ No newline at end of file
diff --git a/src/app/services/subscription/index.php b/src/app/services/subscription/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..69a158575507a7aa686b14f06720735a75d72fa5
--- /dev/null
+++ b/src/app/services/subscription/index.php
@@ -0,0 +1,27 @@
+<?php
+
+require_once MODELS_DIR . 'Subscription.php';
+
+class SubscriptionService {
+    private $subscription_model;
+
+    public function __construct(){
+        $this->subscription_model = new Subscription();
+    }
+
+    public function getAllNewNotification($userID) {
+        $notifications_data = $this->subscription_model->findAllNewNotifications($userID);
+        $notifications = array();
+        foreach ($notifications_data as $data) {
+          array_push($notifications, $data);
+        }
+
+        $this->subscription_model->setAllToSeen($userID);
+        $this->subscription_model->deleteAllRejectedNotification($userID);
+        return $notifications;
+    }
+
+    public function addNewNotification($userID, $creatorID, $creatorName, $status) {
+        $this->subscription_model->addNewNotification($userID, $creatorID, $creatorName, $status);
+    }
+}
diff --git a/src/app/views/components/privates/episode/episode_comment.php b/src/app/views/components/privates/episode/episode_comment.php
new file mode 100644
index 0000000000000000000000000000000000000000..1c836d07019b04bc8af92102d9638046ebac05bf
--- /dev/null
+++ b/src/app/views/components/privates/episode/episode_comment.php
@@ -0,0 +1,38 @@
+<?php 
+function episode_comment($comment = null)
+{
+  echo '
+  <div class="episode-comment-card">
+  <div class="episode-row-1">
+    <div class="episode-comment-card-image-container">
+      <img
+        class="episode-comment-card-image"
+        src="' . IMAGES_DIR . "avatar-template.png" . '"
+        alt=""
+      />
+    </div>
+    <div class=" episode-comment-card-profile">
+      <p class="episode-username">' . $comment['username'] . '</p>
+      <span>•</span>
+      <p class="episode-date">' . formatDateDetail($comment['created_at']) . '</p>
+    </div>
+  </div>
+  <div class="episode-row-2">
+    <div class="episode-comment-fill-div"></div>
+    <div class="episode-comment-content">
+      <p class="episode-text"> ' . $comment['comment_text'] . '</p>
+    </div>
+  </div>
+</div>
+  ';
+}
+
+function comment_list($comments = null) 
+{
+echo '<div class="comment-list-container">';
+  foreach($comments as $comment) {
+    episode_comment($comment);
+  }
+}
+echo '</div';
+?>
\ No newline at end of file
diff --git a/src/app/views/components/privates/episode/episode_detail.php b/src/app/views/components/privates/episode/episode_detail.php
index 8944b7e3cf3ad59163f0f2f1b6d2754d2ce316d0..9276334bfcd5c945da942b91fd72c2f0fc561dbf 100644
--- a/src/app/views/components/privates/episode/episode_detail.php
+++ b/src/app/views/components/privates/episode/episode_detail.php
@@ -1,21 +1,36 @@
 <?php
-function episode_detail($episode = null)
+function episode_detail($episode = null, $premium = false)
 {
-  $id = $episode ? $episode->episode_id : '';
-  $creator_id = $episode ? $episode->podcast_id : '';
-  $poster = is_file(Storage::EPISODE_IMAGE_PATH . $episode->image_url) ? Storage::getFileUrl(Storage::EPISODE_IMAGE_PATH, $episode->image_url) : IMAGES_DIR . "episode-template.png";
-  $title = $episode ? $episode->title : '';
-  $duration = $episode ? $episode->duration / 60 : '';
-  $upload_date = $episode ? formatDate($episode->created_at) : '';
-  $description = $episode ? $episode->description : '';
-
-  $creator_img = $episode->podcast_img ? Storage::getFileUrl(Storage::EPISODE_IMAGE_PATH, $episode->creator_img) : IMAGES_DIR . "podcast-template.png";
-  $podcast_title = $episode ? $episode->podcast_title : '';
-  $audio_file = $episode->audio_url ? Storage::getFileUrl(Storage::EPISODE_AUDIO_PATH, $episode->audio_url) : "/src/public/assets/audio/spotify-ad.mp3";
-
-  echo"
+  require_once VIEWS_DIR . "/components/shares/buttons/baseButton.php";
+
+  $id = $episode ? ($premium ? $episode['episode_id'] : $episode->episode_id) : '';
+  $creator_id = $episode ? ($premium ? $episode['creator_id'] : $episode->podcast_id) : '';
+
+  $poster = $episode ? ($premium ? ($episode['imageFile'] ?? IMAGES_DIR . "episode-template.png") : ($episode->image_url ? Storage::getFileUrl(Storage::EPISODE_IMAGE_PATH, $episode->image_url) : IMAGES_DIR . "episode-template.png")) : IMAGES_DIR . "episode-template.png";
+  $title = $episode ? ($premium ? $episode['title'] : $episode->title) : '';
+  $duration = $episode ? ($premium ? round($episode['duration'] / 60) : round($episode->duration / 60)) : '';
+  $upload_date = $episode ? ($premium ? formatDateDetail($episode['created_at']) : formatDateDetail($episode->created_at)) : '';
+  $description = $episode ? ($premium ? $episode['description'] : $episode->description) : '';
+
+  $audio_file = $episode ? ($premium ? ($episode['audioFile'] ?? AUDIO_DIR . "spotify-ad.mp3") : ($episode->audio_url ? Storage::getFileUrl(Storage::EPISODE_AUDIO_PATH, $episode->audio_url) : AUDIO_DIR . "spotify-ad.mp3")) : AUDIO_DIR . "spotify-ad.mp3";
+
+  $podcast_title = $episode
+    ? ($premium ? ($episode['user']['first_name'] . " " . $episode['user']['last_name'] ?? '')
+      : $episode->podcast_title)
+    : '';
+
+  $creator_img = $episode ? ($premium
+    ? IMAGES_DIR . "avatar-template.png"
+    : ($episode->podcast_img
+      ? Storage::getFileUrl(Storage::EPISODE_IMAGE_PATH, $episode->creator_img)
+      : IMAGES_DIR . "podcast-template.png"))
+    : IMAGES_DIR . "avatar-template.png";
+
+  $likes = $premium ? $episode['episodeLikesCount'] : '';
+
+  echo "
   <head>
-  <title>$title</title> 
+  <title>Podcastify | $title</title> 
   </head>";
   echo "
       <div class=\"episode-detail-head\">
@@ -38,12 +53,36 @@ function episode_detail($episode = null)
         <img alt=\"play-button\" class=\"\" id=\"button-image\" src=\"" . ICONS_DIR . "play.svg\" />
         </button>
       ";
-  if (Middleware::isAdmin()) {
+  if (Middleware::isAdmin() && !$premium) {
     echo "       
         <button class=\"episode-detail-head-edit-button\" onclick=\"showEditEpisode($id)\">
         <img id=\"edit-button-image\" alt=\"edit-button\" src=\"" . ICONS_DIR . "/edit.svg\" />
         </button>";
   }
+
+  if ($premium) {
+
+    echo "       
+        <div class=\"episode-detail-head-like-button\">
+        ";
+    if ($episode['episodeLiked']) {
+      echo "
+        <img id=\"like-button-image\" alt=\"like-button\" src=\"" . ICONS_DIR . "heart-fill.svg\" />
+        ";
+    } else {
+      echo "
+        <img id=\"like-button-image\" alt=\"like-button\" src=\"" . ICONS_DIR . "heart.svg\" />
+        ";
+    }
+
+    echo "
+    </div>
+        <div class=\"like-info\">
+        <p id=\"like-count\"> " . $likes . "</p>
+        <p id=\"like-text\">" . ($likes === 1 ? 'like' : 'likes') . "</p>
+        </div>
+        ";
+  }
   echo "
       </div>
 
@@ -59,18 +98,54 @@ function episode_detail($episode = null)
         <input id=\"audio-file\" value=\"$audio_file\" hidden>
         <input id=\"creator_id\" value=\"$creator_id\" hidden>
           <img alt=\"creator-image\" class=\"episode-detail-foot-creator-image\" src=\"$creator_img\">
+          ";
+  if ($premium) {
+    echo "
+          <p id=\"episode-detail-foot-creator-name\" class=\"episode-detail-foot-creator-name\" onclick=\"window.location.href='/membership/creator/$creator_id'\">$podcast_title</p>
+        ";
+  } else {
+    echo "
           <p id=\"episode-detail-foot-creator-name\" class=\"episode-detail-foot-creator-name\" onclick=\"window.location.href='/podcast/show/$creator_id'\">$podcast_title</p>
+        ";
+  }
+
+  echo "    
         </div>
 
       </div>
     ";
+  if ($premium) {
+    echo '
+      <div>
+        <h2 class="episode-comment-title">Comment</h2>
+        <div class="episode-comment">
+          <div id="comment-user-image-container" class="comment-user-image-container" >
+          <img class="user-image" src=' . IMAGES_DIR . "avatar-template.png" . ' alt="avatar">
+          </div>
+          <input id="episode_id" value="' . $id . '" hidden/>
+          <div id="comment-input-container" class="comment-input-container" >
+            <input type="text" onclick="onClickComment()" placeholder="Enter your comment" class="comment-input" id="comment-input" aria-label="comment-input"/>
+            <div id="comment-buttons" class="comment-buttons">
+            ';
+    baseButton("Cancel", "comment-cancel");
+    baseButton("Comment", "comment-submit", "submit");
+    // <button class="comment-cancel" onclick="onCancleComment()">Cancel</button>
+    //         <button id="" class="btn secondary comment-submit">Comment</button>
+    echo '
+              
+            </div>
+          </div>
+        </div>
+      </div>
+      ';
+  }
 
   echo '
         <script src="' . JS_DIR . 'episode/episode.js"></script>
   ';
 };
 
-function formatDate($dateString)
+function formatDateDetail($dateString)
 {
   $dateTime = new DateTime($dateString);
 
diff --git a/src/app/views/components/privates/membership/cards.php b/src/app/views/components/privates/membership/cards.php
new file mode 100644
index 0000000000000000000000000000000000000000..88d63960886bb579cb40992c919546d3c53e6c5f
--- /dev/null
+++ b/src/app/views/components/privates/membership/cards.php
@@ -0,0 +1,34 @@
+<?php
+// Membership box component
+
+function baseCardsBox($membership = null, $click_evt = "")
+{
+  $creator_name = $membership['first_name'] . ' ' . $membership['last_name'];
+  $status = $membership['status'];
+  $img_url = IMAGES_DIR . "podcast-template.png";
+
+  echo
+  "
+    <div class=\"membership-content-container\" onclick=\"$click_evt\">
+      <div class=\"membership-img-placeholder\">
+        <img src=\"$img_url\" alt=\"membershipImage\" class=\"membership-img\">
+      </div>
+    ";
+  echo
+  "
+    <p class=\"membership-creator\">{$creator_name}</p>
+    <div class=\"membership-creator-container\">
+        <p class=\"membership-status\">
+          {$status}
+        </p>
+      </div>
+    </div>
+    ";
+}
+
+function renderCreatorCardList($creators) {
+  foreach ($creators as $creator) {
+    $userId = $creator['user_id'];
+    baseCardsBox($creator, "showCreator($userId)");
+  }
+}
\ No newline at end of file
diff --git a/src/app/views/components/privates/membership/creator_detail.php b/src/app/views/components/privates/membership/creator_detail.php
new file mode 100644
index 0000000000000000000000000000000000000000..043bc8d8657e4c4ec0ca28739a79d67814754a1f
--- /dev/null
+++ b/src/app/views/components/privates/membership/creator_detail.php
@@ -0,0 +1,55 @@
+<?php
+function creator_detail($creator = null)
+{
+  require_once VIEWS_DIR . "/components/shares/buttons/baseButton.php";
+
+  $creator_id = $creator['user_id'];
+  $poster = IMAGES_DIR . "avatar-template.png";
+  $email = $creator['email'];
+  $title = $creator['first_name'] . " " . $creator['last_name'];
+  $status = $creator['status'];
+
+  echo "
+  <head>
+  <title>Podcastify Membership | $title</title>
+  </head>
+   <input type=\"hidden\" id=\"creator-id\" value=\"$creator_id\" />
+   <input type=\"hidden\" id=\"creator-name\" value=\"$title\" />
+   ";
+
+  echo "
+      <div class=\"creator-detail-head\">
+    ";
+  if ($poster) {
+    echo
+    "<div>
+      <img alt=\"poster-image\" id=\"creator-detail-head-image\" class=\"creator-detail-head-image\" src=\"$poster\">
+      </div>";
+  } else {
+    echo
+    "
+      <div class=\"creator-detail-head-image-empty\"></div>
+      ";
+  }
+  echo "
+        <p id=\"creator-detail-head-title\" class=\"creator-detail-head-title\">$title</p>
+        <p class=\"creator-email\">$email</p>
+        <p class=\"creator-detail-head-status\">Status : $status</p>
+      ";
+
+  if ($status == "Not Subscribed") {
+    baseButton("Subscribe", "subscribe", "submit");
+  }
+
+  echo "
+      </div>
+
+      <div class=\"creator-detail-line\">
+
+      </div>
+    ";
+
+  echo '
+      <script src="' . JS_DIR . 'membership/creator.js"></script>
+  ';
+};
diff --git a/src/app/views/components/privates/membership/tables.php b/src/app/views/components/privates/membership/tables.php
new file mode 100644
index 0000000000000000000000000000000000000000..c98c7deb993158458d372734c134e60a553e6e64
--- /dev/null
+++ b/src/app/views/components/privates/membership/tables.php
@@ -0,0 +1,41 @@
+<?php
+
+function renderEpisodesTable($episodes, $status, $currentPage)
+{
+  require_once VIEWS_DIR . "/components/shares/tables/primary.php";
+  require_once COMPONENTS_SHARES_DIR . 'utility/utility.php';
+
+  $dataHeader = ["Title", "Genre", "Duration"];
+  echoTableHeader($dataHeader, [60, 20, 15]);
+
+  $index = 1 + (($currentPage - 1) * 10);
+  $status = "'" . $status . "'";
+
+  foreach ($episodes as $episode) {
+    $dataContext = [
+      "number",
+      "avatar",
+      "title",
+      "fulllname",
+      "genre",
+      "duration"
+    ];
+
+    $data = [
+      $index,
+      $episode['imageFile'] ? $episode['imageFile'] : IMAGES_DIR . "avatar-template.png",
+      $episode['title'],
+      $episode['user']['first_name'] . " " . $episode['user']['last_name'],
+      $episode['category']['name'],
+      secondsToMinutesSeconds($episode['duration'])
+    ];
+
+    $episode_id = $episode['episode_id'];
+
+    echoTableContent($dataContext, $data,  "handleEpisode($status, $episode_id)", "premium-episode-$episode_id");
+
+    $index++;
+  }
+
+  echoClosingTag();
+}
diff --git a/src/app/views/components/shares/navbars/sidebar.php b/src/app/views/components/shares/navbars/sidebar.php
index 38fe235aeaf8dea0d6a572c8b2adbdd035818da6..f80f5a9ebc8e67110bdfd541745acf01dc7db95a 100644
--- a/src/app/views/components/shares/navbars/sidebar.php
+++ b/src/app/views/components/shares/navbars/sidebar.php
@@ -38,6 +38,17 @@ echo '
       </a>
 ';
 
+if (Middleware::isLoggedIn() && !Middleware::isAdmin()) {
+  echo '
+    <a href="/membership" class="icon-text">
+      <div id="membership-nav" class="link-flex">
+        <img src="' . ICONS_DIR . 'membership.svg" alt="Membership" />
+        <div>Join Membership</div>
+      </div>
+    </a>
+  ';
+}
+
 if (Middleware::isAdmin() && Middleware::isLoggedIn()) {
   echo '
       <a href="/podcast/add" class="icon-text">
diff --git a/src/app/views/components/shares/navbars/topbar.php b/src/app/views/components/shares/navbars/topbar.php
index f425c0a6ff4dcffc68f9f71aa229798c07143670..15e3f7d37703c526a6227f36fb9d30cce877d264 100644
--- a/src/app/views/components/shares/navbars/topbar.php
+++ b/src/app/views/components/shares/navbars/topbar.php
@@ -2,15 +2,6 @@
 
 echo '<link rel="stylesheet" href="' . CSS_DIR .'navbars/topbar.css">';
 
-
-// <div class="btn-flex">
-//       <button class="btn-topbar">
-//         <img src="' . ICONS_DIR . 'left-arrow.svg" />
-//       </button>
-//       <button class="btn-topbar">
-//         <img src="' . ICONS_DIR . 'right-arrow.svg" />
-//       </button>
-//     </div>
 echo '
   <div class="topbar">
     <div class="btn-flex">
@@ -72,6 +63,15 @@ if (Middleware::isAdmin()) {
             </button>
           </li>
   ';
+} else {
+  echo '
+    <li>
+      <button onclick="window.location.href = \'/membership\'">
+        <img src="' . ICONS_DIR . 'membership.svg" alt="Membership" />
+        Membership
+      </button>
+    </li>
+  ';
 }
 
 echo '
diff --git a/src/app/views/components/shares/paginations/primary.php b/src/app/views/components/shares/paginations/primary.php
index f3bb075be98fd01150853aa0f4fcda08fb2917c4..2ea0f8bdb986c29f141dd2bffec239e5b33cf582 100644
--- a/src/app/views/components/shares/paginations/primary.php
+++ b/src/app/views/components/shares/paginations/primary.php
@@ -5,8 +5,9 @@ echo '<link rel="stylesheet" href="' . CSS_DIR . 'base_components/base_paginatio
 // paginationId, currentPage, totalpages, onClick event
 function echoPaginationNav($paginationId, $currentPage, $totalPages, $onClick = [])
 {
-  echo
-  '
+  if ($totalPages > 0) {
+    echo
+    '
     <div class="list-nav">
       <button name="nav-btn" class="nav-btn" onclick="' . $onClick[0] . '">
         <img alt="nav-btn" src="' . ICONS_DIR . 'skip_previous.svg" width="20px" />
@@ -25,4 +26,5 @@ function echoPaginationNav($paginationId, $currentPage, $totalPages, $onClick =
       </button>
     </div>
     ';
+  }
 }
diff --git a/src/app/views/components/shares/utility/utility.php b/src/app/views/components/shares/utility/utility.php
index 2c7373fe35e5425d4f39a3978c3935169320dc52..323fa7a78c5a2a019d14a41d4963580b6c12cd13 100644
--- a/src/app/views/components/shares/utility/utility.php
+++ b/src/app/views/components/shares/utility/utility.php
@@ -28,4 +28,8 @@ function timeAgo($timestamp)
 
   // If none of the above conditions are met, return the formatted date
   return date('Y-m-d H:i:s', $timestamp);
+}
+
+function secondsToMinutesSeconds($seconds) {
+  return gmdate("i:s", $seconds);
 }
\ No newline at end of file
diff --git a/src/app/views/pages/episode/index.php b/src/app/views/pages/episode/index.php
index b69d802a971c7c54c192d6002f3983a6426693f7..883a60f05a92501b970f4c822bb83b9133eda1e2 100644
--- a/src/app/views/pages/episode/index.php
+++ b/src/app/views/pages/episode/index.php
@@ -9,6 +9,7 @@
     // require_once VIEWS_DIR . "components/privates/episode/episode_card.php";
     require_once VIEWS_DIR . "components/shares/paginations/primary.php";
     require_once VIEWS_DIR . "components/shares/tables/primary.php";
+    require_once COMPONENTS_SHARES_DIR . 'utility/utility.php';
     if (count($data['episodes'])) {
       echoTableHeader(["Title", "Duration", "Upload Date"], [60, 20, 15]);
       $ctr = 1;
@@ -16,7 +17,7 @@
         $id = $episode ? $episode->episode_id : '';
         $title = $episode ? $episode->title : '';
         $creator = $episode ? $episode->podcast_title : '';
-        $duration = $episode ? $episode->duration / 60 : '';
+        $duration = $episode ? secondsToMinutesSeconds($episode->duration) : '';
         $image = is_file(Storage::EPISODE_IMAGE_PATH . $episode->image_url) ? Storage::getFileUrl(Storage::EPISODE_IMAGE_PATH, $episode->image_url) : IMAGES_DIR . "episode-template.png";
         $upload_date = $episode->created_at ? formatDate($episode->created_at) : '';
 
diff --git a/src/app/views/pages/membership/index.php b/src/app/views/pages/membership/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8edcbb08ee74fd0e7a1fdf445239be5ff386355
--- /dev/null
+++ b/src/app/views/pages/membership/index.php
@@ -0,0 +1,43 @@
+<head>
+  <link rel="stylesheet" href="<?= CSS_DIR ?>membership/membership.css">
+  <title>Podcastify | Membership</title>
+</head>
+
+<div id="template">
+  <div class="title">
+    <h2>Creators Membership</h2>
+    <p>Get more from creators that you love</p>
+  </div>
+
+  <?php
+  require_once VIEWS_DIR . "components/shares/paginations/primary.php";
+  require_once VIEWS_DIR . "components/privates/membership/cards.php";
+
+  $creators = $data['creators'];
+  $currentPage = $data['currentPage'] ?? 1;
+  $totalPages = $data['totalPages'] ?? 1;
+
+  // Membership container view
+  echo "<div id=\"membership-container\" class=\"membership-container\">";
+  if (count($creators) > 0) {
+    echo "<div id=\"creator-cards\" class=\"membership-box-area\">";
+    renderCreatorCardList($creators);
+    echo "</div>";
+
+    $function = [
+      "loadCreatorList(true, false, false, false, $totalPages)",
+      "loadCreatorList(false, true, false, false, $totalPages)",
+      "loadCreatorList(false, false, true, false, $totalPages)",
+      "loadCreatorList(false, false, false, true, $totalPages)"
+    ];
+
+    echoPaginationNav("creator", $currentPage, $totalPages, $function);
+  } else {
+    echo "<div class=\"membership-info-wrapper\"><h1 class=\"no-membership-info\">No Membership Available</h1></div>";
+  }
+  echo "</div>";
+  ?>
+</div>
+
+<script src="<?= JS_DIR ?>/membership/pagination.js"></script>
+<script src="<?= JS_DIR ?>/membership/creator.js"></script>
\ No newline at end of file
diff --git a/src/app/views/pages/membership/prem_ep_detail.php b/src/app/views/pages/membership/prem_ep_detail.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ce7007aab1bd0ac5b8fe8a75a97aa0a26434799
--- /dev/null
+++ b/src/app/views/pages/membership/prem_ep_detail.php
@@ -0,0 +1,17 @@
+<head>
+    <link rel="stylesheet" href="<?= CSS_DIR ?>episode/episode_detail.css">
+    <link rel="stylesheet" href="<?= CSS_DIR ?>episode/episode_comment.css">
+</head>
+<div id="template">
+  <div class="episode-detail-container">
+  <?php 
+    require_once VIEWS_DIR . "components/privates/episode/episode_detail.php";
+    require_once VIEWS_DIR . "components/privates/episode/episode_comment.php";
+    // && is_array($data['comment']) && is_array($data['like'])
+    if (is_array($data['episode'])) {
+      episode_detail($data['episode'], true);
+      comment_list($data['episode']['episodeComments']);
+    } 
+  ?>
+  </div>
+</div>
\ No newline at end of file
diff --git a/src/app/views/pages/membership/prem_eps_list.php b/src/app/views/pages/membership/prem_eps_list.php
new file mode 100644
index 0000000000000000000000000000000000000000..d630c2414e4668c2290cc01dee15a713c2625b65
--- /dev/null
+++ b/src/app/views/pages/membership/prem_eps_list.php
@@ -0,0 +1,47 @@
+<head>
+  <link rel="stylesheet" href="<?= CSS_DIR ?>membership/creator_detail.css">
+</head>
+
+<div id="template">
+  <div class="creator-detail-container">
+    <?php
+    require_once VIEWS_DIR . "components/privates/membership/creator_detail.php";
+    creator_detail($data['creator']);
+    ?>
+  </div>
+  <h3 class="episode-list-title">Premium Episodes</h3>
+  <div id="episodes-table">
+    <?php
+    require_once COMPONENTS_PRIVATES_DIR . 'membership/tables.php';
+
+    $episodes = $data['episodes'];
+    $currentPage = $data['currentPage'];
+
+    if (count($episodes) > 0) {
+      renderEpisodesTable($episodes, $data['creator']['status'] , $currentPage);
+    } else {
+      echo '<h3 class="no-episodes">No premium episodes yet from this creator</h3>';
+    }
+    ?>
+  </div>
+
+  <div class="pagination-wrapper">
+    <?php
+    require_once VIEWS_DIR . "components/shares/paginations/primary.php";
+
+    $currentPage = $data['currentPage'] ?? 1;
+    $totalPages = $data['totalPages'];
+    $function = [
+      "loadEpisodeList(true, false, false, false, $totalPages)",
+      "loadEpisodeList(false, true, false, false, $totalPages)",
+      "loadEpisodeList(false, false, true, false, $totalPages)",
+      "loadEpisodeList(false, false, false, true, $totalPages)"
+    ];
+
+    echoPaginationNav("episode", $currentPage, $totalPages, $function);
+    ?>
+  </div>
+</div>
+
+<script src="<?= JS_DIR ?>/membership/pagination.js"></script>
+<script src="<?= JS_DIR ?>/membership/episode.js"></script>
\ No newline at end of file
diff --git a/src/app/views/view_config.php b/src/app/views/view_config.php
index 56c5598011d6488092cc2cc98c65b78cbd265b48..09fa348457dd77dd05088c9b39569b43c165a876 100644
--- a/src/app/views/view_config.php
+++ b/src/app/views/view_config.php
@@ -53,9 +53,9 @@ if (!$pathArray[0]) {
                         include VIEWS_DIR . "pages/episode/add_episode.php";
                         break;
                     default:
-                        if(isset($_GET['edit']) && $_GET['edit'] === 'true'){
+                        if (isset($_GET['edit']) && $_GET['edit'] === 'true') {
                             include VIEWS_DIR . "pages/episode/edit_episode.php";
-                        } else{
+                        } else {
                             include VIEWS_DIR . "pages/episode/detail_episode.php";
                         }
                         break;
@@ -65,9 +65,26 @@ if (!$pathArray[0]) {
                 include VIEWS_DIR . "pages" . $path . "/index.php";
                 break;
             }
+        case 'membership':
+            if (isset($pathArray[1])) {
+                switch ($pathArray[1]) {
+                    case 'creator':
+                        include VIEWS_DIR . "pages/membership/prem_eps_list.php";
+                        break;
+                    case 'prem_episode':
+                        include VIEWS_DIR . "pages/membership/prem_ep_detail.php";
+                    default:
+                        break;
+                }
+                break;
+            } else {
+                include VIEWS_DIR . "pages/membership/index.php";
+                break;
+            }
+        case 'subscription':
+            break;
         default:
             include VIEWS_DIR . "pages/errors/404.php";
             break;
     }
-}
-
+}
\ No newline at end of file
diff --git a/src/config/config.php b/src/config/config.php
index dc81e183988ed2b6a6568a9034d6d540b39ed899..7ce875b0ed193936f673ef3a544bb85050f2aca9 100644
--- a/src/config/config.php
+++ b/src/config/config.php
@@ -9,6 +9,14 @@ define('MYSQL_USER', $_ENV['MYSQL_USER']);
 define('MYSQL_PASSWORD', $_ENV['MYSQL_PASSWORD']);
 define('MYSQL_DATABASE', $_ENV['MYSQL_DATABASE']);
 
+// API KEY
+define("APP_API_KEY", $_ENV['APP_API_KEY']);
+define("ENCRYPTION_KEY", $_ENV['ENCRYPTION_KEY']);
+
+// SERVICE URL
+define("REST_SERVICE_URL", $_ENV['REST_SERVICE_URL']);
+define("SOAP_SERVICE_URL", $_ENV['SOAP_SERVICE_URL']);
+
 // Directory
 define("MODELS_DIR", BASE_URL . "/src/app/models/");
 define("CONTROLLERS_DIR", BASE_URL . "/src/app/controllers/");
@@ -20,6 +28,7 @@ define("STORAGES_DIR", BASE_URL . "/src/storage/");
 
 define("ICONS_DIR", "/src/public/assets/icons/");
 define("IMAGES_DIR", "/src/public/assets/images/");
+define("AUDIO_DIR", "/src/public/assets/audio/");
 define("FONTS_DIR", "/src/public/assets/fonts/");
 define("CSS_DIR", "/src/public/css/");
 define("JS_DIR", "/src/public/js/");
diff --git a/src/init.php b/src/init.php
index bfdab7440552166502fbc7922b029e011765c774..489a36d59617123f4cc53f5b5177a7adc72b1864 100644
--- a/src/init.php
+++ b/src/init.php
@@ -13,4 +13,6 @@ require_once 'app/controllers/UserController.php';
 require_once 'app/controllers/PodcastController.php';
 require_once 'app/controllers/LoginController.php';
 require_once 'app/controllers/EpisodeController.php';
-require_once 'app/controllers/ProfileController.php';
\ No newline at end of file
+require_once 'app/controllers/ProfileController.php';
+require_once 'app/controllers/MembershipController.php';
+require_once 'app/controllers/SubscriptionController.php';
diff --git a/src/public/assets/icons/heart-fill.svg b/src/public/assets/icons/heart-fill.svg
new file mode 100644
index 0000000000000000000000000000000000000000..19cb3462041a035eeb8aefff560fecaf2cdb260b
--- /dev/null
+++ b/src/public/assets/icons/heart-fill.svg
@@ -0,0 +1,7 @@
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
+<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512.00 512.00" xml:space="preserve" fill="#1ed760" stroke="#1ed760" stroke-width="0.00512">
+
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
+
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
+
<g id="SVGRepo_iconCarrier"> <path style="fill:#1ed760;" d="M474.655,74.503C449.169,45.72,413.943,29.87,375.467,29.87c-30.225,0-58.5,12.299-81.767,35.566 c-15.522,15.523-28.33,35.26-37.699,57.931c-9.371-22.671-22.177-42.407-37.699-57.931c-23.267-23.267-51.542-35.566-81.767-35.566 c-38.477,0-73.702,15.851-99.188,44.634C13.612,101.305,0,137.911,0,174.936c0,44.458,13.452,88.335,39.981,130.418 c21.009,33.324,50.227,65.585,86.845,95.889c62.046,51.348,123.114,78.995,125.683,80.146c2.203,0.988,4.779,0.988,6.981,0 c2.57-1.151,63.637-28.798,125.683-80.146c36.618-30.304,65.836-62.565,86.845-95.889C498.548,263.271,512,219.394,512,174.936 C512,137.911,498.388,101.305,474.655,74.503z"/> <path style="fill:#1ed760E35336;" d="M160.959,401.243c-36.618-30.304-65.836-62.565-86.845-95.889 c-26.529-42.083-39.981-85.961-39.981-130.418c0-37.025,13.612-73.631,37.345-100.433c21.44-24.213,49.775-39.271,81.138-43.443 c-5.286-0.786-10.653-1.189-16.082-1.189c-38.477,0-73.702,15.851-99.188,44.634C13.612,101.305,0,137.911,0,174.936 c0,44.458,13.452,88.335,39.981,130.418c21.009,33.324,50.227,65.585,86.845,95.889c62.046,51.348,123.114,78.995,125.683,80.146 c2.203,0.988,4.779,0.988,6.981,0c0.689-0.308,5.586-2.524,13.577-6.588C251.254,463.709,206.371,438.825,160.959,401.243z"/> </g>
+
</svg>
\ No newline at end of file
diff --git a/src/public/assets/icons/heart.svg b/src/public/assets/icons/heart.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a9bc663a58f7e605ea0d93e3b1e4ecb8147e2bc6
--- /dev/null
+++ b/src/public/assets/icons/heart.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<svg fill="#a7a7a7" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
+	 viewBox="0 0 471.701 471.701" xml:space="preserve">
+<g>
+	<path d="M433.601,67.001c-24.7-24.7-57.4-38.2-92.3-38.2s-67.7,13.6-92.4,38.3l-12.9,12.9l-13.1-13.1
+		c-24.7-24.7-57.6-38.4-92.5-38.4c-34.8,0-67.6,13.6-92.2,38.2c-24.7,24.7-38.3,57.5-38.2,92.4c0,34.9,13.7,67.6,38.4,92.3
+		l187.8,187.8c2.6,2.6,6.1,4,9.5,4c3.4,0,6.9-1.3,9.5-3.9l188.2-187.5c24.7-24.7,38.3-57.5,38.3-92.4
+		C471.801,124.501,458.301,91.701,433.601,67.001z M414.401,232.701l-178.7,178l-178.3-178.3c-19.6-19.6-30.4-45.6-30.4-73.3
+		s10.7-53.7,30.3-73.2c19.5-19.5,45.5-30.3,73.1-30.3c27.7,0,53.8,10.8,73.4,30.4l22.6,22.6c5.3,5.3,13.8,5.3,19.1,0l22.4-22.4
+		c19.6-19.6,45.7-30.4,73.3-30.4c27.6,0,53.6,10.8,73.2,30.3c19.6,19.6,30.3,45.6,30.3,73.3
+		C444.801,187.101,434.001,213.101,414.401,232.701z"/>
+</g>
+</svg>
\ No newline at end of file
diff --git a/src/public/assets/icons/membership.svg b/src/public/assets/icons/membership.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2ace421176b0edcccca979854745b812e67ce230
--- /dev/null
+++ b/src/public/assets/icons/membership.svg
@@ -0,0 +1,15 @@
+<svg
+  width="24"
+  height="24"
+  viewBox="0 0 24 24"
+  xmlns="http://www.w3.org/2000/svg"
+  fill="rgba(0, 0, 0, 0)"
+>
+  <path
+    stroke="#f9f9f9"
+    stroke-linecap="round"
+    stroke-linejoin="round"
+    stroke-width="2"
+    d="M5 18h14M5 14h14l1-9-4 3-4-5-4 5-4-3 1 9Z"
+  />
+</svg>
diff --git a/src/public/css/base_components/base_tables.css b/src/public/css/base_components/base_tables.css
index c41731761bde1a9ee1ff634ac555bd538ac0df65..6317410a6dc1df031484b2987d391ae07c630574 100644
--- a/src/public/css/base_components/base_tables.css
+++ b/src/public/css/base_components/base_tables.css
@@ -134,7 +134,7 @@
 
 @media (max-width: 547px) {
   .tables td, .tables th {
-    padding: 2px; /* Reduce cell padding for smaller screens */
+    padding: 4px; /* Reduce cell padding for smaller screens */
 }
   .tables td:nth-child(4), th:nth-child(4) {
     display: none;
diff --git a/src/public/css/episode/episode_comment.css b/src/public/css/episode/episode_comment.css
new file mode 100644
index 0000000000000000000000000000000000000000..0eb50fad39d38e8e5b314e9e13e9210fb1a4d75d
--- /dev/null
+++ b/src/public/css/episode/episode_comment.css
@@ -0,0 +1,67 @@
+/* grid grid-cols-10 grid-rows-2 pb-9 */
+.episode-comment-card {
+  display: grid;
+  grid-template-rows: repeat(2, minmax(0, 1fr));
+  gap: 6px;
+}
+
+.episode-row-1 {
+  grid-row: span 1 / span 2;
+  display: flex;
+  gap: 10px;
+}
+
+.episode-row-2 {
+  grid-row: span 1 / span 2;
+  display: flex;
+}
+
+/* flex items-center justify-center */
+.episode-comment-card-image-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+/* w-8 rounded-full col-span-1 row-span-2 */
+.episode-comment-card-image {
+  width: 2rem;
+  border-radius: 9999px;
+  /* grid-column: span 1 / span 10; */
+  grid-row: span 2 / span 2;
+}
+
+/* flex gap-2 col-span-9 items-center text-clr-text-secondary-darken */
+.episode-comment-card-profile {
+  display: flex;
+  gap: 10px;
+  align-items: center;
+  /* color: var(--clr-text-secondary-darken); */
+}
+
+.episode-comment-card-profile p, span {
+  font-weight: 50;
+  color: var(--clr-text-secondary-darken);
+}
+
+/* col-span-1 */
+.episode-comment-fill-div {
+  width: 42px;
+}
+
+/* h-px bg-clr-background-highlight-two col-span-10 mt-4 */
+.episode-comment-divider {
+  height: 1px;
+  background: var(--clr-background-highlight-two);
+  margin-top: 4rem;
+}
+
+@media (max-width: 402px) {
+  .episode-username, .episode-date  {
+    font-size: 12px;
+  }
+
+  .episode-text {
+    font-size: 13px;
+  }
+}
\ No newline at end of file
diff --git a/src/public/css/episode/episode_detail.css b/src/public/css/episode/episode_detail.css
index 8025d6c79c5f91bf780b758ee96824bb43ad44c0..4df8a4602bc842fbaed63a41bc1d06ee045758a0 100644
--- a/src/public/css/episode/episode_detail.css
+++ b/src/public/css/episode/episode_detail.css
@@ -1,7 +1,3 @@
-p {
-  color: var(--clr-text-primary);
-}
-
 .episode-detail-container {
   display: flex;
   flex-direction: column;
@@ -58,6 +54,12 @@ p {
   height: 100%;
 }
 
+#like-button-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
 .episode-detail-head-edit-button {
   background-color: transparent;
   border: none;
@@ -66,10 +68,25 @@ p {
   transition: all 0.3s ease;
 }
 
+.episode-detail-head-like-button {
+  border: none;
+  cursor: pointer;
+  z-index: 10;
+  transition: all 0.3s ease;
+  width: 2rem;
+  height: 2rem;
+}
+
+.like-info {
+  display: flex;
+  gap: 5px;
+}
+
 .episode-detail-line {
   width: auto;
   height: 2px;
   background-color: #000000;
+  padding: auto 10px;
 }
 
 .episode-detail-foot {
@@ -92,7 +109,7 @@ p {
   width: 200px;
   display: flex;
   flex-direction: row;
-  border: 2px solid #ffffff;
+  border: 1px solid #ffffff;
   border-radius: 10px;
   flex-wrap: wrap;
   margin-top: 100px;
@@ -119,6 +136,74 @@ p {
   text-decoration: underline;
 }
 
+.episode-comment {
+  display: flex;
+  width: 100%;
+  margin: 20px auto;
+  max-height: 3rem;
+  gap: 10px;
+  padding: auto 4rem;
+}
+
+.comment-user-image-container {
+  height: inherit;
+  margin-left: 1.5rem;
+}
+
+.user-image {
+  width: 42px;
+  height: 42px;
+  object-fit: cover;
+  border-radius: var(--rounded-full);
+}
+
+.comment-input-container {
+  width: 100%;
+}
+
+.comment-input {
+  padding: 14px 12px;
+  background: var(--clr-background-base-two);
+  border: 0;
+  box-shadow: 0 0 0 1px var(--essential-subdued, #878787);
+  border-radius: var(--rounded);
+  transition: box-shadow 0.3s ease;
+  outline: none;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.comment-buttons {
+  display: none;
+  justify-content: end;
+  gap: 10px;
+  margin: 10px auto;
+}
+
+.comment-list-container {
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+/*
+.comment-submit {
+  font-weight: 700;
+  transition: transform 0.2s ease;
+  padding: 0.5rem 2rem;
+  width: min-content;
+}
+
+.comment-submit:hover {
+  transform: scale(1.05);
+}
+
+.comment-cancel {
+  background-color: transparent;
+  outline: none;
+  border: none;
+  color: var(--clr-text-secondary);
+} */
+
 @media (max-width: 402px) {
   .episode-detail-head-image, .episode-detail-head-image-empty  {
     width: 100%;
@@ -140,6 +225,7 @@ p {
   
   .episode-detail-foot-description {
     font-size: 10px;
+    line-height: 1rem;
   }
 
   .episode-detail-foot-creator {
@@ -150,6 +236,25 @@ p {
   .episode-detail-foot-creator-name {
     font-size: 14px;
   }
+
+  .episode-detail-head-like-button {
+    width: 1.5rem;
+    height: 1.5rem;
+  }
+
+  .episode-comment-title {
+    font-size: 1rem;
+  }
+
+  .user-image {
+    width: 30px;
+    height: 30px;
+  }
+
+  .comment-input {
+    padding: 10px;
+    font-size: 12px;
+  }
 }
 
 @media (max-width:374px) {
diff --git a/src/public/css/membership/creator_detail.css b/src/public/css/membership/creator_detail.css
new file mode 100644
index 0000000000000000000000000000000000000000..738cd3ac85dcc44492b24faae2d34a8412a44ba8
--- /dev/null
+++ b/src/public/css/membership/creator_detail.css
@@ -0,0 +1,66 @@
+p {
+  color: var(--clr-text-primary);
+}
+
+.creator-detail-container {
+  display: flex;
+  flex-direction: column;
+  flex-wrap: wrap;
+  gap: 12px;
+}
+
+.creator-detail-head {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+  margin-bottom: 10px;
+}
+
+.creator-detail-head-image {
+  width: 200px;
+  height: 200px;
+  border-radius: 10px;
+}
+
+.creator-detail-head-image-empty {
+  background-color: var(--clr-background-card);
+}
+
+.creator-detail-head-title {
+  font-size: 20px;
+  text-align: center;
+}
+
+.creator-email {
+  color: var(--clr-text-secondary);
+  font-size: 14px;
+  margin-bottom: 0.5rem;
+}
+
+.creator-detail-head-status {
+  font-size: 17px;
+  font-weight: lighter;
+}
+
+.episode-list-title {
+  margin: 0;
+  padding: 30px 20px;
+  text-align: left;
+  width: 100%;
+}
+
+.no-episodes {
+  margin: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 10px 0;
+}
+
+.creator-detail-line {
+  width: auto;
+  height: 2px;
+  background-color: var(--clr-text-primary);
+}
\ No newline at end of file
diff --git a/src/public/css/membership/membership.css b/src/public/css/membership/membership.css
new file mode 100644
index 0000000000000000000000000000000000000000..d5638e4c119614f0604dcd585b09645157c916fd
--- /dev/null
+++ b/src/public/css/membership/membership.css
@@ -0,0 +1,121 @@
+p {
+  padding-top: 4px;
+  font-size: 14px;
+  color: var(--clr-background-highlight-three);
+  padding-left: 2px;
+}
+
+h2 {
+  font-size: 3rem;
+  font-weight: 700;
+}
+
+.title {
+  padding: 1rem 0.5rem 2rem;
+}
+
+.membership-container {
+  width: 100%;
+  height: 100%;
+  position: relative;
+
+  .no-membership-info {
+    margin-top: 150px;
+    text-align: center;
+  }
+
+  .membership-box-area{
+    display: grid;
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+    gap: 1.5rem;
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+    overflow: hidden;
+    @media (max-width: 860px) {
+      grid-template-columns: repeat(2, minmax(0, 1fr));
+    }
+
+    &.hidden {
+      visibility: hidden;
+    }
+
+    &.visible {
+      visibility: visible;
+    }
+  }
+
+  .membership-box-area {
+    position: relative;
+  }
+}
+
+.membership-content-container {
+  background-color: var(--clr-background-card);
+  padding: 1rem;
+  border-radius: 0.5rem;
+  height: fit-content;
+  box-shadow: var(--shadow);
+  cursor: pointer;
+  transition: all 0.3s;
+
+  display: flex;
+  flex-direction: column;
+
+  &:hover {
+    background-color: var(--clr-background-highlight-two);
+  }
+
+  .membership-img-placeholder {
+    width: 100%;
+    padding-bottom: 100%;
+    position: relative;
+  }
+
+  .membership-img {
+    width: 100%;
+    height: 100%;
+    border-radius: 0.35rem;
+    box-shadow: var(--shadow-xl);
+    object-fit: cover;
+    object-position: center center;
+    position: absolute;
+    top: 0;
+    left: 0;
+  }
+
+  .membership-creator {
+    margin-top: 6px;
+    font-size: 1rem;
+    line-height: 1.5rem;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    color: var(--clr-text-white);
+
+    @media (max-width: 640px) {
+      font-size: 12px;
+    }
+  }
+
+  .membership-creator-container {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    position: relative;
+  }
+
+  .membership-status {
+    font-size: 0.8rem;
+    opacity: 0.5;
+    line-height: 1.5rem;
+    font-weight: 500;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+
+    @media (max-width: 768px) {
+      font-size: 10px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/public/js/auth/login.js b/src/public/js/auth/login.js
index 70eff3a444eddf8678cc610bd28777ff3423e398..e0c44d99fd013e3b87b31ae8ce7b5449d458c9d1 100644
--- a/src/public/js/auth/login.js
+++ b/src/public/js/auth/login.js
@@ -33,7 +33,7 @@ const submitLoginForm = async (e) => {
             location.replace(response.redirect_url);
           }, 3000);
         }
-      } else {
+      } else {  
         showNotificationDanger(response.error_message);
       }
     };
diff --git a/src/public/js/default.js b/src/public/js/default.js
index b12063f3f692a3602dac7ae837390bd710952211..284ed49097e484cc0cd540567feb13fb3a92a455 100644
--- a/src/public/js/default.js
+++ b/src/public/js/default.js
@@ -2,20 +2,20 @@
 const sliderEls = document.querySelectorAll("#range");
 const sliderVolumeEl = document.querySelector("#volume-slider");
 
-sliderEls.forEach(sliderEl => {
+sliderEls.forEach((sliderEl) => {
   function updateSliderBackground(value) {
     const progress = (value / sliderEl.max) * 100;
     sliderEl.style.background = `linear-gradient(to right, #1bd760 ${progress}%, #a7a7a7 ${progress}%)`;
   }
-  
+
   function updateVolumeSlider(value) {
     const progress = (value / sliderVolumeEl.max) * 100;
     sliderVolumeEl.style.background = `linear-gradient(to right, #f9f9f9 ${progress}%, #808080 ${progress}%)`;
   }
-  
+
   sliderEl.addEventListener("input", (event) => {
     const tempSliderValue = event.target.value;
-  
+
     requestAnimationFrame(() => {
       updateSliderBackground(tempSliderValue);
     });
@@ -23,16 +23,65 @@ sliderEls.forEach(sliderEl => {
   // Initial update
   updateSliderBackground(sliderEl.value);
   audioPlayer.addEventListener("timeupdate", function () {
-  updateSliderBackground(sliderSmall.value);
-  updateSliderBackground(sliderBig.value);
-})
-})
-
+    updateSliderBackground(sliderSmall.value);
+    updateSliderBackground(sliderBig.value);
+  });
+});
 
-sliderVolumeEl.addEventListener("input", (event)=> {
+sliderVolumeEl.addEventListener("input", (event) => {
   const tempSliderValue = event.target.value;
   requestAnimationFrame(() => {
     updateVolumeSlider(tempSliderValue);
   });
-})
+});
+
+// Poll for new notification
+const SUBSCRIPTION_NOTIFICATION_BASE_URL = "/subscription";
+
+const subscriptionNotificationText = (creatorName, fail = false) => {
+  return `Your subscription request to ${creatorName} has been ${
+    fail ? "rejected" : "accepted"
+  }`;
+};
+
+const fetchNewNotification = () => {
+  var xhr = new XMLHttpRequest();
+
+  xhr.open(
+    "GET",
+    SUBSCRIPTION_NOTIFICATION_BASE_URL,
+    true
+  );
+  xhr.onload = async () => {
+    if (xhr.status === 200) {
+      const response = JSON.parse(xhr.responseText);
+      const data = response.data;
+
+      const delay = (ms) => new Promise((res) => setTimeout(res, ms));
+
+      if (data.length > 0) {
+        for (let status of data) {
+          if (status.status === "ACCEPTED") {
+            showNotificationSuccess(
+              subscriptionNotificationText(status.creator_name)
+            );
+          } else if (status.status === "REJECTED") {
+            showNotificationDanger(
+              subscriptionNotificationText(status.creator_name, true)
+            );
+          }
+          // add a delay before showing the next notification
+          if (data.length > 1) {
+            await delay(2500);
+          }
+        }
+      }
+    }
+    // Poll again after a delay (5 - 7 seconds)
+    var delay = Math.random() * 2000 + 5000;
+    setTimeout(fetchNewNotification, delay);
+  };
+  xhr.send();
+};
 
+fetchNewNotification();
diff --git a/src/public/js/episode/episode.js b/src/public/js/episode/episode.js
index 704383f67c1925632791a0745986f1f124431225..fc743011a7f879b59662bb464af638886b8abbba 100644
--- a/src/public/js/episode/episode.js
+++ b/src/public/js/episode/episode.js
@@ -51,3 +51,118 @@ const showEditEpisode = (episodeId) => {
   }
   
 };
+
+//COMMENT
+let commentClicked = true;
+const onClickComment = () => {
+    if(commentClicked){
+    const commentButtons = document.getElementById("comment-buttons");
+    commentButtons.style.display = 'flex'
+    commentClicked = false;
+  }
+}
+
+if(document.getElementById('comment-cancel')){
+  document.getElementById('comment-cancel').addEventListener("click", () => {
+    const commentButtons = document.getElementById("comment-buttons");
+    commentButtons.style.display = 'none';
+    commentClicked = true;
+  })
+}
+
+if(document.getElementById("comment-submit")){
+  document.getElementById("comment-submit").addEventListener('click', async () => {
+    const username = (await getSelf()).username;
+    const episode_id = document.getElementById('episode_id').value;
+    const text = document.getElementById('comment-input').value;
+
+    const request = {
+      episode_id : parseInt(episode_id),
+      username: username,
+      comment_text: text,
+    }
+
+    const xhr = new XMLHttpRequest();
+
+    xhr.open("POST", "/membership/comment", true);
+    xhr.setRequestHeader("Content-Type", "application/json");
+
+    xhr.onload = () => {
+      const response = JSON.parse(xhr.responseText);
+      if (xhr.status === 200) {
+        if (response.success) {
+          showNotificationSuccess(response.message);
+
+          setTimeout(() => {
+            location.replace("/membership/prem_episode/" + episode_id);
+          }, 1000);
+        } else {
+            showNotificationDanger(response.message);
+        }
+      } else {
+        showNotificationDanger(response.message);
+      }
+    };
+
+    xhr.send(JSON.stringify(request));
+  })
+}
+
+//LIKE
+let likeClicked = false;
+if(document.getElementById("like-button-image")){
+  document.getElementById("like-button-image").addEventListener("click", () => {
+    const likeBtn = document.getElementById("like-button-image");
+    const likeCtr = parseInt(document.getElementById("like-count").innerHTML);
+    const likeText = document.getElementById("like-text").innerHTML;
+
+    const fileName = likeBtn.src.split('/').pop();
+
+    if(fileName === 'heart-fill.svg') {
+      likeBtn.src = "/src/public/assets/icons/heart.svg";
+      document.getElementById('like-count').innerHTML = likeCtr - 1;
+      
+      if (likeCtr - 1 === 1) {
+        document.getElementById("like-text").innerHTML = "like";
+      } else {
+        document.getElementById("like-text").innerHTML = "likes";
+      }
+    } else {
+      likeBtn.src = "/src/public/assets/icons/heart-fill.svg";
+      document.getElementById('like-count').innerHTML = likeCtr + 1;
+
+      if (likeCtr + 1 === 1) {
+        document.getElementById("like-text").innerHTML = "like";
+      } else {
+        document.getElementById("like-text").innerHTML = "likes";
+      }
+    }
+
+    const episode_id = document.getElementById('episode_id').value;
+
+    const request = {
+      episode_id : parseInt(episode_id),
+    }
+
+    const xhr = new XMLHttpRequest();
+
+    xhr.open("POST", "/membership/like", true);
+    xhr.setRequestHeader("Content-Type", "application/json");
+
+    xhr.onload = () => {
+      const response = JSON.parse(xhr.responseText);
+      if (xhr.status === 200) {
+        if (response.success) {
+          showNotificationSuccess(response.message);
+        } else {
+          showNotificationDanger(response.message);
+        }
+      } else {
+        showNotificationDanger(response.message);
+      }
+    };
+
+    xhr.send(JSON.stringify(request));
+
+  })
+}
\ No newline at end of file
diff --git a/src/public/js/membership/creator.js b/src/public/js/membership/creator.js
new file mode 100644
index 0000000000000000000000000000000000000000..217c58d5cc71dba2002680c8e7cbd4f00046d674
--- /dev/null
+++ b/src/public/js/membership/creator.js
@@ -0,0 +1,39 @@
+const showCreator = (creatorId) => {
+  window.location.href = "membership/creator/" + creatorId;
+};
+
+if (document.getElementById("subscribe")) {
+  document.getElementById("subscribe").addEventListener("click", () => {
+    const creator_id = document.getElementById("creator-id").value;
+    const creator_name = document.getElementById("creator-name").value;
+
+    const request = {
+      creator_id: creator_id,
+      creator_name: creator_name,
+    };
+
+    const xhr = new XMLHttpRequest();
+
+    xhr.open("POST", "/membership/subscribe", true);
+    xhr.setRequestHeader("Content-Type", "application/json");
+    xhr.onload = () => {
+      const response = JSON.parse(xhr.responseText);
+
+      if (xhr.status === 200) {
+        if (response.success) {
+          showNotificationSuccess(response.message);
+
+          setTimeout(() => {
+            location.replace("/membership/creator/" + creator_id);
+          }, 3000);
+        } else {
+            showNotificationDanger(response.message);
+        }
+      } else {
+        showNotificationDanger(response.message);
+      }
+    };
+
+    xhr.send(JSON.stringify(request));
+  });
+}
diff --git a/src/public/js/membership/episode.js b/src/public/js/membership/episode.js
new file mode 100644
index 0000000000000000000000000000000000000000..7dde5b70587d5ef0afb110dd917e2f7246741350
--- /dev/null
+++ b/src/public/js/membership/episode.js
@@ -0,0 +1,7 @@
+const handleEpisode = (status, episode_id) => {
+  if (status == 'Subscribed') {
+    window.location.href='/membership/prem_episode/' + episode_id;
+  } else {
+    showNotificationDanger("You're not subscribed to the creator");
+  }
+}
\ No newline at end of file
diff --git a/src/public/js/membership/pagination.js b/src/public/js/membership/pagination.js
new file mode 100644
index 0000000000000000000000000000000000000000..8641d83f4d7ec15b6c1e8e7d59f69a140aaca0aa
--- /dev/null
+++ b/src/public/js/membership/pagination.js
@@ -0,0 +1,90 @@
+const loadCreatorList = (
+  start = false,
+  prev = false,
+  next = false,
+  end = false,
+  totalPages
+) => {
+  let currentPage = document.getElementById("creator-list-page-num").innerText;
+
+  let pageNumber = currentPage;
+  if (next) {
+    if (pageNumber >= totalPages) return;
+    pageNumber++;
+  } else if (end) {
+    if (pageNumber >= totalPages) return;
+    pageNumber = totalPages;
+  } else if (start) {
+    if (pageNumber <= 1) return;
+    pageNumber = 1;
+  } else if (prev) {
+    if (pageNumber <= 1) return;
+    pageNumber--;
+  } else {
+    return;
+  }
+
+  document.getElementById("creator-list-page-num").textContent = pageNumber;
+
+  let xhr = new XMLHttpRequest();
+  const url = "membership?page=" + pageNumber;
+  xhr.open("GET", url, true);
+
+  xhr.onload = () => {
+    if (xhr.status >= 200 && xhr.status < 300) {
+      document.getElementById("creator-cards").innerHTML =
+        xhr.responseText;
+    } else {
+      const response = JSON.parse(xhr.responseText);
+      console.error(response.error_message);
+    }
+  };
+
+  xhr.send();
+};
+
+const loadEpisodeList = (
+  start = false,
+  prev = false,
+  next = false,
+  end = false,
+  totalPages
+) => {
+  let currentPage = document.getElementById(
+    "episode-list-page-num"
+  ).innerText;
+
+  let pageNumber = currentPage;
+  if (next) {
+    if (pageNumber >= totalPages) return;
+    pageNumber++;
+  } else if (end) {
+    if (pageNumber >= totalPages) return;
+    pageNumber = totalPages;
+  } else if (start) {
+    if (pageNumber <= 1) return;
+    pageNumber = 1;
+  } else if (prev) {
+    if (pageNumber <= 1) return;
+    pageNumber--;
+  } else {
+    return;
+  }
+
+  document.getElementById("episode-list-page-num").textContent = pageNumber;
+
+  let xhr = new XMLHttpRequest();
+  const url = `/membership/creator/${2}?page=` + pageNumber;
+  xhr.open("GET", url, true);
+
+  xhr.onload = () => {
+    if (xhr.status >= 200 && xhr.status < 300) {
+      document.getElementById("episodes-table").innerHTML = xhr.responseText;
+    } else {
+      const response = JSON.parse(xhr.responseText);
+      console.error(response.error_message);
+    }
+  };
+
+  xhr.send();
+};
diff --git a/src/public/js/podcast/handle_upload.js b/src/public/js/podcast/handle_upload.js
index c289d4befe4efe71d6a85bf016460c813465931d..a45f347ef04903ad52aa626a2bee515673360bb1 100644
--- a/src/public/js/podcast/handle_upload.js
+++ b/src/public/js/podcast/handle_upload.js
@@ -1,5 +1,7 @@
 const PODCAST_MANAGE_BASE_URL = "/podcast";
 
+let isSubmittingForm = false;
+
 const uploadPodcastImage = (url, async = true, data = null) => {
   let xhr = new XMLHttpRequest();
   xhr.open("POST", PODCAST_MANAGE_BASE_URL + url, async);
@@ -66,7 +68,9 @@ const handleFormSubmit = (formId, callback) => {
   if (formElement) {
     formElement.addEventListener("submit", function (event) {
       event.preventDefault();
-      callback(this);
+      if (!isSubmittingForm) {
+        callback(this);
+      }
     });
   }
 };
@@ -104,6 +108,9 @@ handleFormSubmit("create-podcast", function () {
 
   // Send form when okay button is clicked
   createModal.addEventListener("okayClicked", () => {
+    if (isSubmittingForm) return;
+    isSubmittingForm = true;
+
     let form = document.getElementById("create-podcast");
     let formData = new FormData(form);
 
@@ -157,6 +164,7 @@ handleFormSubmit("create-podcast", function () {
 
         showNotificationDanger(response.error_message);
       }
+      isSubmittingForm = false;
     };
   });
 });
@@ -167,6 +175,9 @@ handleFormSubmit("update-form", function () {
 
   // Send form when okay button is clicked
   saveChangesModal.addEventListener("okayClicked", () => {
+    if (isSubmittingForm) return;
+    isSubmittingForm = true;
+
     const sendPodcastEditPayload = (payloadForm) => {
       let data = {};
       let podcastId = 0;
@@ -191,6 +202,7 @@ handleFormSubmit("update-form", function () {
 
           showNotificationDanger(response.error_message);
         }
+        isSubmittingForm = false;
       };
     };
 
diff --git a/src/public/js/sidebar.js b/src/public/js/sidebar.js
index f4d0e9ed0dbf4dc47adbf4ce4b8d386dd54600a5..ec555cafa47a191498e5d36cf351a65d94545f2c 100644
--- a/src/public/js/sidebar.js
+++ b/src/public/js/sidebar.js
@@ -50,6 +50,12 @@ var navItems = [
     imgActive: "/user.svg",
     imgInactive: "/user.svg",
   },
+  {
+    id: "membership-nav",
+    href: "/membership",
+    imgActive: "/membership.svg",
+    imgInactive: "/membership.svg",
+  },
 ];
 
 for (var i = 0; i < navItems.length; i++) {