diff --git a/src/app/baseclasses/BaseRepository.php b/src/app/baseclasses/BaseRepository.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf1b0efb077952582bc94c6712c9ca542e536b72
--- /dev/null
+++ b/src/app/baseclasses/BaseRepository.php
@@ -0,0 +1,231 @@
+<?php
+
+require_once SRC_ROOT_PATH . "/app/core/PDOHandler.php";
+
+abstract class BaseRepository
+{
+  protected static $instance;
+  protected $pdo;
+  protected $tableName = '';
+
+  protected function __construct()
+  {
+    $this->pdo = PDOInstance::getInstance()->getPDO();
+  }
+
+  public static function getInstance()
+  {
+    if (!isset(self::$instance)) {
+      self::$instance = new static();
+    }
+    return self::$instance;
+  }
+
+  public function getPDO()
+  {
+    return $this->pdo;
+  }
+
+  public function getAll()
+  {
+    $sql = "SELECT * FROM $this->tableName";
+    return $this->pdo->query($sql);
+  }
+
+  public function countRow($where = []) {
+    $sql = "SELECT COUNT(*) FROM $this->tableName";
+
+    if (count($where) > 0) {
+      $sql .= " WHERE ";
+      $sql .= implode(" AND ", array_map(function ($key, $value) {
+        if ($value[2] == 'LIKE') {
+          return "$key LIKE :$key";
+        }
+
+        return "$key = :$key";
+      }, array_keys($where), array_values($where)));
+    }
+    $stmt = $this->pdo->prepare($sql);
+    foreach ($where as $key => $value) {
+      $stmt->bindValue(":$key", $value[0], $value[1]);
+      if ($value[2] == 'LIKE') {
+        $stmt->bindValue(":$key", "%$value[0]%", $value[1]);
+      } else {
+        $stmt->bindValue(":$key", $value[0], $value[1]);
+      }
+    }
+
+    $stmt->execute();
+    return $stmt->fetchColumn();
+  }
+
+  public function findAll(
+    $where = [],
+    $order = null,
+    $pageNo = null,
+    $pageSize = null,
+    $isDesc = false,
+  ) {
+    $sql = "SELECT * FROM $this->tableName ";
+
+    if (count($where) > 0) {
+      $sql .= "WHERE ";
+      $sql .= implode(" AND ", array_map(function ($key, $value) {
+        if ($key == "judul") {
+          return "(judul LIKE :$key OR penyanyi LIKE :$key OR YEAR(tanggal_terbit) LIKE :$key)";
+        }
+
+        if ($value[2] == 'LIKE') {
+          return "$key LIKE :$key";
+        }
+
+        return "$key = :$key";
+      }, array_keys($where), array_values($where)));
+    }
+
+    if ($order) {
+      $sql .= " ORDER BY $order";
+    }
+
+    if ($isDesc) {
+      $sql .= " DESC";
+    }
+
+    if ($pageSize && $pageNo) {
+      $sql .= " LIMIT :pageSize";
+      $sql .= " OFFSET :offset";
+    }
+
+    $stmt = $this->pdo->prepare($sql);
+    foreach ($where as $key => $value) {
+      if ($value[2] == 'LIKE') {
+        $stmt->bindValue(":$key", "%$value[0]%", $value[1]);
+      } else {
+        $stmt->bindValue(":$key", $value[0], $value[1]);
+      }
+    }
+
+    if ($pageSize && $pageNo) {
+      $offset = ($pageNo - 1) * $pageSize;
+
+      $stmt->bindValue(":pageSize", $pageSize, PDO::PARAM_INT);
+      $stmt->bindValue(":offset", $offset, PDO::PARAM_INT);
+    }
+
+    
+    
+    
+
+    $stmt->execute();
+    return $stmt->fetchAll();
+  }
+
+  public function findOne($arrParams)
+  {
+    $sql = "SELECT * FROM $this->tableName WHERE ";
+    $i = 0;
+    foreach ($arrParams as $key => $value) {
+      $sql .= "$key = :$key";
+      if ($i < count($arrParams) - 1) {
+        $sql .= " AND ";
+      }
+      $i++;
+    }
+
+    $stmt = $this->pdo->prepare($sql);
+    foreach ($arrParams as $key => $value) {
+      $stmt->bindValue(":$key", $value[0], $value[1]);
+    }
+
+    $stmt->execute();
+    return $stmt->fetch();
+  }
+
+  public function insert($model, $arrParams)
+  {
+    $sql = "INSERT INTO $this->tableName (";
+    $i = 0;
+    foreach ($arrParams as $key => $value) {
+      $sql .= "$key";
+      if ($i < count($arrParams) - 1) {
+        $sql .= ", ";
+      }
+      $i++;
+    }
+    $sql .= ") VALUES (";
+    $i = 0;
+    foreach ($arrParams as $key => $value) {
+      $sql .= ":$key";
+      if ($i < count($arrParams) - 1) {
+        $sql .= ", ";
+      }
+      $i++;
+    }
+    $sql .= ")";
+
+    $stmt = $this->pdo->prepare($sql);
+    foreach ($arrParams as $key => $value) {
+      $stmt->bindValue(":$key", $model->get($key), $value);
+    }
+
+    $stmt->execute();
+    return $this->pdo->lastInsertId();
+  }
+
+  public function update($model, $arrParams)
+  {
+    $sql = "UPDATE $this->tableName SET ";
+    $i = 0;
+    foreach ($arrParams as $key => $value) {
+      $sql .= "$key = :$key";
+      if ($i < count($arrParams) - 1) {
+        $sql .= ", ";
+      }
+      $i++;
+    }
+    $primaryKey = $model->get('_primary_key');
+    $sql .= " WHERE $primaryKey = :primaryKey";
+
+    $stmt = $this->pdo->prepare($sql);
+    foreach ($arrParams as $key => $value) {
+      $stmt->bindValue(":$key", $model->get($key), $value);
+    }
+    $stmt->bindValue(":primaryKey", $model->get($primaryKey), PDO::PARAM_INT);
+
+    $stmt->execute();
+    return $stmt->rowCount();
+  }
+
+  public function delete($model)
+  {
+    $sql = "DELETE FROM $this->tableName WHERE ";
+    $primaryKey = $model->get('_primary_key');
+    $sql .= "$primaryKey = :primaryKey";
+
+    $stmt = $this->pdo->prepare($sql);
+    $stmt->bindValue(":primaryKey", $model->get($primaryKey), PDO::PARAM_INT);
+
+    $stmt->execute();
+    return $stmt->rowCount();
+  }
+
+  public function getNLastRow($N)
+  {
+    $sql = "SELECT COUNT(*) FROM $this->tableName";
+    $stmt = $this->pdo->prepare($sql);
+    $stmt->execute();
+    $count = $stmt->fetchColumn();
+
+    if ($count < $N) {
+      $N = $count;
+    }
+
+    $offset = $count - $N;
+    $sql = "SELECT * FROM $this->tableName LIMIT :limit OFFSET :offset";
+    $stmt = $this->pdo->prepare($sql);
+    $stmt->bindValue(":limit", $N, PDO::PARAM_INT);
+    $stmt->bindValue(":offset", $offset, PDO::PARAM_INT);
+    $stmt->execute();
+    return $stmt->fetchAll();
+  }
+}
\ No newline at end of file
diff --git a/src/app/baseclasses/BaseService.php b/src/app/baseclasses/BaseService.php
new file mode 100644
index 0000000000000000000000000000000000000000..92c31403602e59a44ee0f0183fed3f9feed81547
--- /dev/null
+++ b/src/app/baseclasses/BaseService.php
@@ -0,0 +1,16 @@
+<?php
+
+abstract class BaseService {
+  protected static $instance;
+  protected $repository;
+
+  protected function __construct() {
+  }
+
+  public static function getInstance() {
+    if (!isset(self::$instance)) {
+      self::$instance = new static();
+    }
+    return self::$instance;
+  }
+}
\ No newline at end of file
diff --git a/src/app/clients/SocmedSoapClient.php b/src/app/clients/SocmedSoapClient.php
new file mode 100644
index 0000000000000000000000000000000000000000..95a4988b1b3ea9cbf090a7bd55b8ba1364fa9885
--- /dev/null
+++ b/src/app/clients/SocmedSoapClient.php
@@ -0,0 +1,81 @@
+<?php
+
+require_once SRC_ROOT_PATH . "/utils/SoapWrapper.php";
+
+class SocmedSoapClient
+{
+    private static $instance;
+    private $client;
+
+    private function __construct()
+    {
+        $opts = array(
+            'http' => array(
+                'header' => 'Authorization: ' . getenv("MONOLITHIC_SOAP_API_KEY"))
+        );
+
+        $params = array(
+            'encoding' => 'UTF-8',
+            'soap_version' => SOAP_1_2,
+            'trace' => 1,
+            'exceptions' => 1,
+            'connection_timeout' => 180,
+            'stream_context' => stream_context_create($opts),
+        );
+ 
+        $this->client = new SoapWrapper($_ENV['WSDL_URL'], $params);
+    }
+
+    public static function getInstance()
+    {
+        if (!isset(self::$instance)) {
+            self::$instance = new static();
+        }
+        return self::$instance;
+    }
+
+    public function requestUnlocking($socmed_id, $link_code)
+    {
+        $res = $this->client->call("requestUnlocking", array(
+            'arg0' => $socmed_id,
+            'arg1' => null,
+            'arg2' => $link_code,
+        ));
+        return $res;
+    }
+
+    public function getUnlocking()
+    {
+        $res = $this->client->call("getUnlocking", null);
+        return $res;
+    }
+
+    public function getUnlockingBySocmedId($socmed_id)
+    {
+        $response = self::getInstance()->getUnlockingBySocmedId(
+            array(
+                "socmed_id" => $socmed_id,
+            )
+        );
+        return $response;
+    }
+
+    public function acceptUnlocking($socmed_id, $link_code)
+    {
+        $res = $this->client->call("acceptUnlocking", array(
+            'arg0' => $socmed_id,
+            'arg1' => null,
+            'arg2' => $link_code,
+        ));
+        return $res;
+    }
+
+    public function rejectUnlocking($socmed_id, $link_code)
+    {
+        $res = $this->client->call("rejectUnlocking", array(
+            'arg0' => $socmed_id,
+            'arg1' => $link_code,
+        ));
+        return $res;
+    }
+}
diff --git a/src/app/controllers/Unlocking/UnlockingController.php b/src/app/controllers/Unlocking/UnlockingController.php
new file mode 100644
index 0000000000000000000000000000000000000000..5b734d1d9725f2d20207b30492c225e095055062
--- /dev/null
+++ b/src/app/controllers/Unlocking/UnlockingController.php
@@ -0,0 +1,31 @@
+<?php
+
+require_once SRC_ROOT_PATH . "/app/services/UnlockingService.php";
+require_once SRC_ROOT_PATH . "/app/baseclasses/BaseController.php";
+
+class UnlockingController extends BaseController {
+  protected static $instance;
+
+  private function __construct($srv)
+  {
+    parent::__construct($srv);
+  }
+
+  public static function getInstance()
+  {
+    if (!isset(self::$instance)) {
+      self::$instance = new static(
+        UnlockingService::getInstance()
+      );
+    }
+    return self::$instance;
+  }
+
+  public function post($urlParams) {
+    
+    $socmed_id = $_SESSION['id'];
+    $link_code = $_SESSION['username'];
+
+    return $this->srv->requestUnlocking($socmed_id, $link_code);
+  }
+}
diff --git a/src/app/models/UnlockingModel.php b/src/app/models/UnlockingModel.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cd27407917eefb57dc56a763f42258b8e6520b7
--- /dev/null
+++ b/src/app/models/UnlockingModel.php
@@ -0,0 +1,33 @@
+<?php
+
+require_once SRC_ROOT_PATH . "/baseclasses/BaseModel.php";
+
+class SubscriptionModel extends BaseModel
+{
+  public $socmed_id;
+  public $dashboard_id;
+  public $link_code;
+
+  public function __construct()
+  {
+    $this->_primary_key = ["socmed_id"];
+    return $this;
+  }
+
+  public function constructFromArray($array)
+  {
+    $this->socmed_id = $array["socmed_id"];
+    $this->dashboard_id = $array["dashboard_id"];
+    $this->link_code = ($array["link_code"]);
+    return $this;
+  }
+
+  public function toResponse()
+  {
+    return array(
+      "socmed_id" => $this->socmed_id,
+      "dashboard_id" => $this->dashboard_id,
+      "link_code" => $this->link_code,
+    );
+  }
+}
diff --git a/src/app/repositories/UnlockingRespository.php b/src/app/repositories/UnlockingRespository.php
new file mode 100644
index 0000000000000000000000000000000000000000..9eae8c60479a3468cc217e5305918e4fa68c474e
--- /dev/null
+++ b/src/app/repositories/UnlockingRespository.php
@@ -0,0 +1,59 @@
+<?php
+
+require_once SRC_ROOT_PATH . "/baseclasses/BaseRepository.php";
+
+class UnlockingRepository extends BaseRepository{
+  protected static $instance;
+
+  private function __construct() {
+    parent::__construct();
+  }
+
+  public static function getInstance() {
+    if (!isset(self::$instance)) {
+      self::$instance = new static();
+    }
+    return self::$instance;
+  }
+
+  public function getAllUnlockings() {
+    $sql = "SELECT * FROM unlocking";
+    $stmt = $this->pdo->prepare($sql);
+    $stmt->execute();
+    return $stmt->fetchAll();
+  }
+
+  public function getUnlocking($socmed_id, $dashboard_id) {
+    $sql = "SELECT * FROM unlocking WHERE socmed_id = :socmed_id AND dashboard_id = :dashboard_id";
+    $stmt = $this->pdo->prepare($sql);
+
+    $stmt->bindParam(':socmed_id', $socmed_id, PDO::PARAM_INT);
+    $stmt->bindParam(':dashboard_id', $dashboard_id, PDO::PARAM_INT);
+
+    $stmt->execute();
+    return $stmt->fetchAll();
+  }
+
+  public function getAcceptedUnlockingBySocmedID($socmed_id) {
+    $sql = "SELECT * FROM unlocking WHERE socmed_id = :socmed_id and dashboard_id IS NOT NULL";
+    $stmt = $this->pdo->prepare($sql);
+
+    $stmt->bindParam(':socmed_id', $socmed_id, PDO::PARAM_INT);
+
+    $stmt->execute();
+    return $stmt->fetchAll();
+  }
+
+  public function insertUnlocking($socmed_id, $dashboard_id, $link_code) {
+    $sql = "INSERT INTO unlocking (socmed_id, dashboard_id, link_code) VALUES (:socmed_id, :subscriber_id, :link_code)";
+    $stmt = $this->pdo->prepare($sql);
+
+    $stmt->bindParam(':socmed_id', $socmed_id, PDO::PARAM_INT);
+    $stmt->bindParam(':dashboard_id', $dashboard_id, PDO::PARAM_INT);
+    $stmt->bindParam(':link_code', $link_code, PDO::PARAM_STR);
+
+    $stmt->execute();
+    return $stmt->rowCount();
+  }
+}
+
diff --git a/src/app/services/UnlockingService.php b/src/app/services/UnlockingService.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef823d69bec7fee9ca27ba2296d172038247e33b
--- /dev/null
+++ b/src/app/services/UnlockingService.php
@@ -0,0 +1,73 @@
+<?php
+
+require_once PROJECT_ROOT_PATH . "/src/bases/BaseService.php";
+require_once PROJECT_ROOT_PATH . "/src/utils/SoapWrapper.php";
+require_once PROJECT_ROOT_PATH . "/src/clients/SocmedSoapClient.php";
+require_once PROJECT_ROOT_PATH . "/src/models/UnlockingModel.php";
+require_once PROJECT_ROOT_PATH . "/src/repositories/UnlockingRepository.php";
+
+class UnlockingService extends BaseSrv {
+  protected static $instance;
+  private $client;
+  
+  private function __construct($client) {
+    parent::__construct();
+    $this->client = $client;
+  }
+
+  public static function getInstance() {
+    if (!isset(self::$instance)) {
+      self::$instance = new static(
+        SocmedSoapClient::getInstance()
+      );
+    }
+    return self::$instance;
+  }
+
+  public function getAcceptedUnlockingBySocmedId($socmedId) {
+    $unlocksSQL = UnlockingRepository::getInstance()->getAcceptedUnlockingBySocmedId($socmedId);
+
+    $unlocks = [];
+    foreach ($unlocksSQL as $unlockSQL) {
+      $unlock = new UnlockingModel();
+      $unlocks[] = $unlock->constructFromArray($unlockSQL);
+    }
+    return $unlocks;
+  }
+
+  public function update($socmed_id, $dashboard_id, $link_code) {
+    return UnlockingnRepository::getInstance()
+            ->updateUnlocking($socmed_id, $dashboard_id, $link_code);
+  }
+
+  public function requestUnlocking($socmed_id, $link_code) {
+    $resp = $this->client->requestUnlocking($socmed_id, $link_code);
+    UnlockingRepository::getInstance()->insertUnlocking($socmed_id, null, $link_code);
+
+    return $resp;
+  }
+
+  public function getSoapUnlocking() {
+    return $this->client->getUnlocking();
+  }
+
+  public function getUnlocking() {
+    $sqlRes = UnlockingRepository::getInstance()->getAllUnlockings();
+
+    $unlocks = [];
+    foreach ($sqlRes as $unlockSQL) {
+      $unlock = new UnlockingModel();
+      $unlocks[] = $unlock->constructFromArray($unlockSQL);
+    }
+
+    return $subs;
+  }
+
+  public function acceptUnlocking($socmed_id, $link_code) {
+    return $this->client->acceptUnlocking($socmed_id, $link_code);
+  }
+
+  public function rejectUnlocking($socmed_id, $link_code) {
+    return $this->client->rejectUnlocking($socmed_id, $link_code);
+  }
+}
diff --git a/src/app/utils/Logger.php b/src/app/utils/Logger.php
new file mode 100644
index 0000000000000000000000000000000000000000..308ddb757588c4651d2d39f901c05a4064e5211b
--- /dev/null
+++ b/src/app/utils/Logger.php
@@ -0,0 +1,174 @@
+<?php
+
+class Logger
+{
+  protected static $log_file;
+  protected static $file;
+  protected static $options = [
+    'dateFormat' => 'd-M-Y',
+    'logFormat' => 'H:i:s d-M-Y'
+  ];
+  private static $instance;
+  private static $logdir = PROJECT_ROOT_PATH . '/logs';
+
+  public static function createLogFile()
+  {
+    $time = date(static::$options['dateFormat']);
+    static::$log_file = static::$logdir . "/log-{$time}.txt";
+
+    if (!file_exists(static::$logdir)) {
+      mkdir(static::$logdir, 0777, true);
+    }
+
+    if (!file_exists(static::$log_file)) {
+      fopen(static::$log_file, 'w') or exit("Can't create {static::log_file}!");
+    }
+
+    if (!is_writable(static::$log_file)) {
+      throw new Exception("ERROR: Unable to write to file!", 1);
+    }
+  }
+
+  public static function setOptions($options = [])
+  {
+    static::$options = array_merge(static::$options, $options);
+  }
+
+  public static function info($message, array $context = [])
+  {
+    $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
+
+    static::writeLog([
+      'message' => $message,
+      'bt' => $bt,
+      'severity' => 'INFO',
+      'context' => $context
+    ]);
+  }
+
+  public static function notice($message, array $context = [])
+  {
+    $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
+
+    static::writeLog([
+      'message' => $message,
+      'bt' => $bt,
+      'severity' => 'NOTICE',
+      'context' => $context
+    ]);
+  }
+
+  public static function debug($message, array $context = [])
+  {
+    $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
+
+    static::writeLog([
+      'message' => $message,
+      'bt' => $bt,
+      'severity' => 'DEBUG',
+      'context' => $context
+    ]);
+  }
+
+  public static function warning($message, array $context = [])
+  {
+    $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
+
+    static::writeLog([
+      'message' => $message,
+      'bt' => $bt,
+      'severity' => 'WARNING',
+      'context' => $context
+    ]);
+  }
+
+  public static function error($message, array $context = [])
+  {
+    $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
+
+    static::writeLog([
+      'message' => $message,
+      'bt' => $bt,
+      'severity' => 'ERROR',
+      'context' => $context
+    ]);
+  }
+
+  public static function fatal($message, array $context = [])
+  {
+    $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
+
+    static::writeLog([
+      'message' => $message,
+      'bt' => $bt,
+      'severity' => 'FATAL',
+      'context' => $context
+    ]);
+  }
+
+  public static  function writeLog($args = [])
+  {
+    static::createLogFile();
+
+    if (!is_resource(static::$file)) {
+      static::openLog();
+    }
+
+    $time = date(static::$options['logFormat']);
+
+    $context = json_encode($args['context']);
+
+    $caller = array_shift($args['bt']);
+    $btLine = $caller['line'];
+    $btPath = $caller['file'];
+
+    $path = static::absToRelPath($btPath);
+
+    $timeLog = is_null($time) ? "[N/A] " : "[{$time}] ";
+    $pathLog = is_null($path) ? "[N/A] " : "[{$path}] ";
+    $lineLog = is_null($btLine) ? "[N/A] " : "[{$btLine}] ";
+    $severityLog = is_null($args['severity']) ? "[N/A]" : "[{$args['severity']}]";
+    $messageLog = is_null($args['message']) ? "N/A" : "{$args['message']}";
+    $contextLog = empty($args['context']) ? "" : "{$context}";
+
+    fwrite(static::$file, "{$timeLog}{$pathLog}{$lineLog}: {$severityLog} - {$messageLog} {$contextLog}" . PHP_EOL);
+
+    static::closeFile();
+  }
+
+  private static function openLog()
+  {
+    $openFile = static::$log_file;
+    static::$file = fopen($openFile, 'a') or exit("Can't open $openFile!");
+  }
+
+  public static function closeFile()
+  {
+    if (static::$file) {
+      fclose(static::$file);
+    }
+  }
+
+  public static function absToRelPath($pathToConvert)
+  {
+    $pathAbs = str_replace(['/', '\\'], '/', $pathToConvert);
+    $documentRoot = str_replace(['/', '\\'], '/', $_SERVER['DOCUMENT_ROOT']);
+    return $_SERVER['SERVER_NAME'] . str_replace($documentRoot, '', $pathAbs);
+  }
+
+  protected function __construct()
+  {
+  }
+
+  public static function getInstance()
+  {
+    if (is_null(self::$instance)) {
+      self::$instance = new self();
+    }
+    return self::$instance;
+  }
+
+  private function __destruct()
+  {
+  }
+}
diff --git a/src/app/utils/SoapWrapper.php b/src/app/utils/SoapWrapper.php
new file mode 100644
index 0000000000000000000000000000000000000000..d4d7511b17cb828811e8b4d5664fec0d1dfb5825
--- /dev/null
+++ b/src/app/utils/SoapWrapper.php
@@ -0,0 +1,33 @@
+<?php
+
+class SoapWrapper {
+  public function __construct($wsdl) {
+    try {
+      $this->soapClient = new SoapClient($wsdl,array(
+        "exceptions" => 0,
+        "trace" => 1,
+        "encoding" => "UTF-8",
+        'stream_context' => stream_context_create(array(
+          'http' => array(
+            'header' => 'Authorization: Bearer ' . $_ENV['API_KEY'],
+          ),
+        )),
+      ));
+    } catch (Exception $e) {
+      Logger::error("SoapWrapper: Error while creating SoapClient: " . $e->getMessage());
+      $this->soapClient = null;
+    }
+  }
+
+  public function call($functionName, $params) {
+    $res = null;
+
+    if (!isset($params)) {
+      $res = $this->soapClient->$functionName();
+    } else {
+      $res = $this->soapClient->$functionName($params);
+    }
+
+    return json_encode($res);
+  }
+}
\ No newline at end of file