diff --git a/app/controllers/Detail.php b/app/controllers/Detail.php
index 6f796914932a56833e5ac6c36d92552bde122913..6799ca97ed749df26564eacf81de0bc0f3756c9a 100644
--- a/app/controllers/Detail.php
+++ b/app/controllers/Detail.php
@@ -66,9 +66,9 @@ class Detail extends Controller
         $entityBody = json_decode(file_get_contents('php://input'), true);
         //TODO MASUKKIN KARTU NASABAH ID
         $user = $model_user->readUserById($user_id);
-        $orderid = $soap->buyBook($_COOKIE['bookid'], $entityBody['total'], $user['no_kartu']);
+        $orderid = $soap->buyBook($_COOKIE['bookid'], $entityBody['total'], $user['no_kartu'], $entityBody['token']);
         if ($orderid != -1) {
-            $orderid = $model->createOrder($_COOKIE['bookid'], $user_id, $orderid);
+            $model->createOrder($_COOKIE['bookid'], $user_id, $orderid);
         }
         echo $orderid;
 
diff --git a/app/controllers/History.php b/app/controllers/History.php
index e356fd44fc19fbeec20df3d84381fc9985e72ea8..352d8d54ed1926f299d9ad8b588a6f7ebc50ddbb 100644
--- a/app/controllers/History.php
+++ b/app/controllers/History.php
@@ -8,7 +8,8 @@ class History extends Controller
         }
         
         if (isset($_COOKIE['access_token'])) {
-            if ($this->model('Token')->validateToken($_COOKIE['access_token'])) {
+            $id_user = $this->model('Token')->validateToken($_COOKIE['access_token']);
+            if ($id_user) {
                 $access_valid = true;
             } else {
                 $access_valid = false;
@@ -22,7 +23,7 @@ class History extends Controller
             exit();
         }
         else {
-            $data['data'] = $this->model("Order")->readHistoryByUserId($_COOKIE['id']);
+            $data['data'] = $this->model("Order")->readHistoryByUserId($id_user);
             if (count($data) > 0){
             	$data["navigation"] = "History";
                 $this->view("history", $data);
diff --git a/app/controllers/Home.php b/app/controllers/Home.php
index f8a8b63d316009c9e6165ca252ec29168c6aa71a..b81fd73e584bac9d47743d34b044fcb5a7e19e8a 100644
--- a/app/controllers/Home.php
+++ b/app/controllers/Home.php
@@ -9,7 +9,8 @@ class Home extends Controller
         }
         
         if (isset($_COOKIE['access_token'])) {
-            if ($this->model('Token')->validateToken($_COOKIE['access_token'])) {
+            $id_user = $this->model('Token')->validateToken($_COOKIE['access_token']);
+            if ($id_user) {
                 $access_valid = true;
             } else {
                 $access_valid = false;
@@ -23,7 +24,7 @@ class Home extends Controller
             exit();
         }
         else {
-            $data = $this->model("User")->readUserById($_COOKIE['id']);
+            $data = $this->model("User")->readUserById($id_user);
             $data["navigation"] = "Browse";
             $this->view('home' ,$data);
         }
@@ -62,7 +63,6 @@ class Home extends Controller
         }
         session_destroy();
         $temp = $this->model("Token")->deleteToken($_COOKIE['access_token']);
-        setcookie("id", "", time() - 3600,'/');
         setcookie("access_token","",time()-3600,'/');
         header("location: /login");
         exit();
diff --git a/app/controllers/Login.php b/app/controllers/Login.php
index a4c018ff9ecb302d2e0ae32eee4a5a1c3cd63669..7e000f57994ba57cdedcc31d5c5b9b94f6c7b14e 100644
--- a/app/controllers/Login.php
+++ b/app/controllers/Login.php
@@ -38,7 +38,6 @@ class Login extends Controller
             if (password_verify($password, $temp["password"]) or ($password==$temp['password'])){
                 session_start();
                 $id = $model->readUserIdByUsername($temp['username'])['userID'];
-                setcookie('id', $id, time() + 1800, '/');
                 $token = substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1).substr(md5(time()),1);
 
                 setcookie('access_token', $token , time() + 1800, '/');
@@ -58,4 +57,28 @@ class Login extends Controller
             echo "<script>window.location.href='/login'; alert('Unknown Username'); </script>";
         }
     }
+
+    public function tokensignin()
+    {
+        header("Access-Control-Allow-Methods: POST");
+        header("Content-Type: application/json; charset=UTF-8");
+        $id_token = json_decode(file_get_contents("php://input"))->id_token;
+        $payload = json_decode(file_get_contents('https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' . $id_token));
+
+        $user_model = $this->model('User');
+        if ($user = $user_model->readUserByEmail($payload->email)) {
+            $token = substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1).substr(md5(time()),1);
+            setcookie('access_token', $token , time() + 1800, '/');
+            $model_token = $this->model('Token');
+            $temp_token = $model_token->insertToken($user['userID'], $token);
+
+            if (!isset($_SESSION)) {
+                session_start();
+            }
+            $_SESSION['username'] = $user['username'];
+        }
+        else {
+            header("HTTP/1.1 404 Not Found");
+        }      
+    }
 }
diff --git a/app/controllers/Profile.php b/app/controllers/Profile.php
index 53c534708ffabdbc8de8fc1f9ca3206c4e3a9c8f..6e02e777bb645d0b94b5abf276a7da1c733b710c 100644
--- a/app/controllers/Profile.php
+++ b/app/controllers/Profile.php
@@ -3,13 +3,10 @@
 class Profile extends Controller
 {
     public function index()
-    {
-		if (!isset($_SESSION)) {
-            session_start();            
-		}
-		
+    {	
     	if (isset($_COOKIE['access_token'])) {
-            if ($this->model('Token')->validateToken($_COOKIE['access_token'])) {
+            $user_id = $this->model('Token')->validateToken($_COOKIE['access_token']);
+            if ($user_id) {
                 $access_valid = true;
             } else {
                 $access_valid = false;
@@ -22,7 +19,7 @@ class Profile extends Controller
             header('Location: /login');
         }
         else {
-            $data = $this->model("User")->readUserById($_COOKIE['id']);
+            $data = $this->model("User")->readUserById($user_id);
             $data["navigation"] = "Profile";
         	$this->view("profile",$data);
         }
@@ -30,10 +27,9 @@ class Profile extends Controller
 
      public function edit()
     {
-		session_start();
-
+        $id_user = $this->model('Token')->validateToken($_COOKIE['access_token']);
 		if (isset($_COOKIE['access_token'])) {
-            if ($this->model('Token')->validateToken($_COOKIE['access_token'])) {
+            if ($id_user) {
                 $access_valid = true;
             } else {
                 $access_valid = false;
@@ -46,7 +42,7 @@ class Profile extends Controller
             header('Location: /login');
             exit();
         } else {
-	        $data = $this->model("User")->readUserById($_COOKIE['id']);
+	        $data = $this->model("User")->readUserById($id_user);
 	        $data["navigation"] = "Profile";
 	        $this->view("editprofile", $data);
         }
@@ -67,7 +63,7 @@ class Profile extends Controller
         }
 
       	if ($access_valid){
-	    	$user['id'] = $_COOKIE['id'];
+	    	$user['id'] = $user_id;
 	        $user['name'] = $_POST['name'];
 	        $user['address'] = $_POST['address'];
 	        $user['phone'] = $_POST['phone'];
diff --git a/app/controllers/Register.php b/app/controllers/Register.php
index 9129cfd49585b1433c5b8b7872d07d81c9cb518f..a41e3719da1831fc32d6139a016963b497dd9601 100644
--- a/app/controllers/Register.php
+++ b/app/controllers/Register.php
@@ -45,7 +45,6 @@ class Register extends Controller
             }
 
             $id = $model->readUserIdByUsername($user['username'])['userID'];
-            setcookie('id', $id, time() + 3600,'/');
             $token = substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 1).substr(md5(time()),1);
 
             setcookie('access_token', $token , time() + 1800, '/');
diff --git a/app/models/SoapHelper.php b/app/models/SoapHelper.php
index e391860a458b5d2b5ae7e8b14eda5752e78db206..bac9da126db9435c86ad7c6c599a2559406d11e8 100644
--- a/app/models/SoapHelper.php
+++ b/app/models/SoapHelper.php
@@ -49,8 +49,8 @@ class SoapHelper {
         return $output;
     }
 
-    public function buyBook($id, $quantity, $no_rek) {
-        $data = $this->conn->buyBook($id, $quantity, $no_rek);
+    public function buyBook($id, $quantity, $no_rek, $token) {
+        $data = $this->conn->buyBook($id, $quantity, $no_rek, $token);
         $array = json_decode(json_encode($data), True);
         return $array;
     }
diff --git a/app/models/Token.php b/app/models/Token.php
index 5828e971c9381943ff8c3b36d274884983fbf89e..0bbed2b6d60999544a7d6228980c3cc29118fc9b 100644
--- a/app/models/Token.php
+++ b/app/models/Token.php
@@ -24,6 +24,8 @@ class Token extends Model
         $ip_address = $_SERVER['REMOTE_ADDR'];
         $result = $this->conn->query($sql)->fetch_assoc();
         if ($result['id'] && $result['browser'] == $browser && $result['ip_address'] == $ip_address && time() < (int)$result['time']) {
+            $sql = "UPDATE access_token SET time = " . (time() + (int)1200) . " WHERE token = '" . $token . "';" ;
+            $this->conn->query($sql);
             return $result['id'];
         } else {
             return null;
diff --git a/app/views/detail.php b/app/views/detail.php
index 6ca7e33b22fead9478d58c8054c58dc11511151b..7aa9695ac8c4891e3c375573f5fe8ce6077bdc9a 100644
--- a/app/views/detail.php
+++ b/app/views/detail.php
@@ -5,6 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <title>Detail</title>
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
     <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" />
 </head>
 <body>
@@ -52,6 +53,11 @@
                     <?php } ?>
                 </select>
             </div>
+                <label for="token" class="inp">
+                  <input type="text" id="token" placeholder="&nbsp;">
+                  <span class="label">Token</span>
+                  <span class="border"></span>
+                </label>
             <button onclick="order()" <?php if ($data['book']['price'] == -1) {echo "disabled";}?>>Order</button>
         </section>
         <section id="reviews">
@@ -98,12 +104,25 @@
         <div id="dialog-msg">
             <img id="check-notif" src="/public/images/check.png">
             <div id="msg-detail">
-                <p>Pemesanan Berhasil!</p>
+                <p id="text-msg-detail">Pemesanan Berhasil!</p>
                 <p>Nomor Transaksi : <span id="transaction-number">3<span></p>
             </div>
             <img id="close-notif" onclick="close_notif()" src="/public/images/close.png">
         </div>
     </div>
     <script src="/public/js/detail.js"></script>
+    <script>
+        function onLoad() {
+            gapi.load('auth2', function() {
+            gapi.auth2.init();
+            });
+        }        
+        function signOut() {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut().then(function () {
+                console.log('User signed out.');
+            });
+        }
+    </script>
 </body>
 </html>
diff --git a/app/views/editprofile.php b/app/views/editprofile.php
index ce3fb91f9833d68022259ad46b1ade4c2e5dd068..9f5f5ac92349ba1cd14d4e4b7e8b3969254b33c6 100644
--- a/app/views/editprofile.php
+++ b/app/views/editprofile.php
@@ -5,51 +5,66 @@
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<title>Profile</title>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
 	<link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" />
 </head>
 <body>
 	<?php require_once('navbar.php') ?>
-		<div id="edit-profile-container" class="container">
-			<h1 id="edit-profile-header"class="page-header">Edit Profile</h1>
-			<div id ="edit-profile-section">
-				<form action="/Profile/save" method="POST" id="edit-profile-form"  enctype="multipart/form-data">
-					<div class ="edit-profile-row">
-						<img id="edit-profile-image" src="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>">
-						<div class="edit-profile-label">
-							<label for="avatar"> Update Profile Picture </label>
-							<div id="image-input">
-								<div id="filebox"></div>
-								<input type="button" id="browse-button" onclick="document.getElementById('ava').click()" value="Browse ..." />
-								<input id = "avaHidden" class ="edit-profile-input" type="text" name="avaHidden" value="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>" hidden>
-								<input type="file" id="ava" name="avatar" onchange="FileSelected()" accept="image/*">
-							</div>
+	<div id="edit-profile-container" class="container">
+		<h1 id="edit-profile-header"class="page-header">Edit Profile</h1>
+		<div id ="edit-profile-section">
+			<form action="/Profile/save" method="POST" id="edit-profile-form"  enctype="multipart/form-data">
+				<div class ="edit-profile-row">
+					<img id="edit-profile-image" src="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>">
+					<div class="edit-profile-label">
+						<label for="avatar"> Update Profile Picture </label>
+						<div id="image-input">
+							<div id="filebox"></div>
+							<input type="button" id="browse-button" onclick="document.getElementById('ava').click()" value="Browse ..." />
+							<input id = "avaHidden" class ="edit-profile-input" type="text" name="avaHidden" value="<?php if(is_null($data['userPicture'])) { echo '../../public/images/profile/default.jpg'; } else { echo $data['userPicture']; }?>" hidden>
+							<input type="file" id="ava" name="avatar" onchange="FileSelected()" accept="image/*">
 						</div>
 					</div>
-					<div class ="edit-profile-row">
-						<label class = "edit-profile-label" for="name"> Name </label>
-						<input id = "nameinput" class ="edit-profile-input" type="text" name="name" value="<?php echo $data['name']?>">
-					</div>
-					<div class ="edit-profile-row">
-						<label id = "addresslabel" class = "edit-profile-label" for="address"> Address </label>
-						<textarea id = "addressinput" class ="edit-profile-input" form="edit-profile-form" name="address"><?php echo $data['address']?></textarea>
-					</div>
-					<div class ="edit-profile-row">
-						<label class = "edit-profile-label" for="phone"> Phone Number </label>
-						<input id = "phoneinput" class ="edit-profile-input" type="text" name="phone" value="<?php echo $data['phone']?>" >
-					</div>
-					<div class ="edit-profile-row">
-						<label class = "edit-profile-label" for="bank-account"> Bank Account </label>
-						<input id = "bankinput" class ="edit-profile-input" type="text" name="bank-account" value="<?php echo $data['no_kartu']?>" >
-					</div>
-					<div class ="edit-profile-row" id = "edit-profile-button-row">
-						<!-- <button onclick="location.href='/Profile'" id="cancel-button" type="button">Back</button> -->
-						<a href="/Profile" id = "cancel-button">Back</a>
-						<!-- <button id="save-button" type="button">Save</button> -->
-						<input type="submit" value="Save" id="save-button" class="save-button">
-					</div>
-				</form>
-			</div>
+				</div>
+				<div class ="edit-profile-row">
+					<label class = "edit-profile-label" for="name"> Name </label>
+					<input id = "nameinput" class ="edit-profile-input" type="text" name="name" value="<?php echo $data['name']?>">
+				</div>
+				<div class ="edit-profile-row">
+					<label id = "addresslabel" class = "edit-profile-label" for="address"> Address </label>
+					<textarea id = "addressinput" class ="edit-profile-input" form="edit-profile-form" name="address"><?php echo $data['address']?></textarea>
+				</div>
+				<div class ="edit-profile-row">
+					<label class = "edit-profile-label" for="phone"> Phone Number </label>
+					<input id = "phoneinput" class ="edit-profile-input" type="text" name="phone" value="<?php echo $data['phone']?>" >
+				</div>
+				<div class ="edit-profile-row">
+					<label class = "edit-profile-label" for="bank-account"> Bank Account </label>
+					<input id = "bankinput" class ="edit-profile-input" type="text" name="bank-account" value="<?php echo $data['no_kartu']?>" >
+				</div>
+				<div class ="edit-profile-row" id = "edit-profile-button-row">
+					<!-- <button onclick="location.href='/Profile'" id="cancel-button" type="button">Back</button> -->
+					<a href="/Profile" id = "cancel-button">Back</a>
+					<!-- <button id="save-button" type="button">Save</button> -->
+					<input type="submit" value="Save" id="save-button" class="save-button">
+				</div>
+			</form>
 		</div>
-		<script src="../../public/js/editprofile.js"></script>
+	</div>
+	<script src="../../public/js/editprofile.js"></script>
+	<script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
+    <script>
+        function onLoad() {
+            gapi.load('auth2', function() {
+            gapi.auth2.init();
+            });
+        }        
+        function signOut() {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut().then(function () {
+                console.log('User signed out.');
+            });
+        }
+    </script>
 </body>
 </html>
diff --git a/app/views/history.php b/app/views/history.php
index f2bb20b116e7084ea9726a008f4a62fdfc05c4e5..dea3911f216c266cfb6049d8afb6d13b6d6d41b1 100644
--- a/app/views/history.php
+++ b/app/views/history.php
@@ -5,6 +5,7 @@
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<title>Home</title>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
 	<link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" />
 </head>
 <body>
@@ -42,5 +43,19 @@
         </div>
         <?php } }?>
     </div>
+    <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
+    <script>
+        function onLoad() {
+            gapi.load('auth2', function() {
+            gapi.auth2.init();
+            });
+        }        
+        function signOut() {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut().then(function () {
+                console.log('User signed out.');
+            });
+        }
+    </script>
 </body>
 </html>
diff --git a/app/views/home.php b/app/views/home.php
index 65728654087d038c1371d7adfc7ce8a36c92330f..f1956131ffd07999b621274a78c3179187aac527 100644
--- a/app/views/home.php
+++ b/app/views/home.php
@@ -5,6 +5,7 @@
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<title>Home</title>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
 	<link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" />
 	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
     <link rel="stylesheet" href="https://loading.io/css/loading.css">
@@ -50,5 +51,19 @@
         </div>
 	</div>
 	<script src="/public/js/search.js"></script>
+    <script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
+    <script>
+        function onLoad() {
+            gapi.load('auth2', function() {
+            gapi.auth2.init();
+            });
+        }        
+        function signOut() {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut().then(function () {
+                console.log('User signed out.');
+            });
+        }
+    </script>
 </body>
 </html>
diff --git a/app/views/login.php b/app/views/login.php
index ce7a1a07f3cdd885d6e42b2c638ef81629bfdeee..dc52728fff8818ce2ac024298e21103b0ae7d7d1 100644
--- a/app/views/login.php
+++ b/app/views/login.php
@@ -5,6 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <title>Login</title>
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
     <link rel="stylesheet" type="text/css" media="screen" href="../../public/css/main.css" />
 </head>
 <body>
@@ -22,13 +23,17 @@
                             <label for="password">Password</label>
                             <input type="password" name="password">
                         </div>
-                        <a href="/register">Don't have an account?</a>
-                        <button id="submit-btn" class="disabled-btn" type="submit" disabled>LOGIN</button>
+                        <div class="register-google-login">
+                            <button id="submit-btn" class="disabled-btn" type="submit" disabled>LOGIN</button>
+                            <div class="g-signin2" data-onsuccess="onSignIn">Google</div>
+                        </div>
+                        <p><a href="/register">Don't have an account? register
                     </form>
                 </div>
             </div>
         </section>
     </div>
+    <script src="https://apis.google.com/js/platform.js" async defer></script>
     <script src="../../public/js/login.js"></script>
 </body>
 </html>
\ No newline at end of file
diff --git a/app/views/navbar.php b/app/views/navbar.php
index 715ef0d89a9725d5449f6840b7268afd64b6f0cc..671ff1a22234adf0a5f350d3f46a08955f660599 100644
--- a/app/views/navbar.php
+++ b/app/views/navbar.php
@@ -10,7 +10,7 @@
         </div>
 
         <div id="logout-button">
-            <a href="/Home/logout">
+            <a href="/Home/logout" onclick="signOut()">
                 <img src="../../public/images/logout.png">
             </a>
         </div>
@@ -29,4 +29,4 @@
         </li></a>
     </ul>
 </div>
-</nav>
+</nav>
\ No newline at end of file
diff --git a/app/views/profile.php b/app/views/profile.php
index 3cc038945d52d3b4bc9c9781311b5de287afba8a..582e17741d7c169b23b3e19f247a710efcf60785 100644
--- a/app/views/profile.php
+++ b/app/views/profile.php
@@ -5,6 +5,7 @@
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<title>Profile</title>
 	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
 	<link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" />
 </head>
 <body>
@@ -43,9 +44,30 @@
 				<div class ="profile-row">
 					<div class ="profile-icon"><img src="../../public/images/bank.png"></div>
 					<div class ="profile-label">Bank account</div>
-					<div class ="profile-value"><?php echo $data['no_kartu']?></div>
+					<div  class ="profile-value"><?php echo $data['no_kartu']?></div>
+					<input id="no-kartu" type="button" value="<?php echo $data['no_kartu']?>" hidden/>
+				</div>
+				<div class="qr-code">
+					<p>QR code</p>
+					<img id="qr-img" src="">
 				</div>
 			</div>
+
+			
 		</div>
+	<script src="../../public/js/profile.js"></script>
+	<script>
+        function onLoad() {
+            gapi.load('auth2', function() {
+            gapi.auth2.init();
+            });
+        }        
+        function signOut() {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut().then(function () {
+                console.log('User signed out.');
+            });
+        }
+    </script>
 </body>
 </html>
\ No newline at end of file
diff --git a/app/views/review.php b/app/views/review.php
index 5b2471262209df6c8762895221a05e41bee09211..9f7139613f0636eb506951e9d24e545131132707 100644
--- a/app/views/review.php
+++ b/app/views/review.php
@@ -5,43 +5,57 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <title>Review</title>
     <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta name="google-signin-client_id" content="1039450104464-p0bpievqv6nfcbhrcvbl2vrdkg7jgnnk.apps.googleusercontent.com">
     <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css" />
 </head>
 <body>
     <?php require_once('navbar.php') ?>
-        <div class="container">
-            <div id="review-detail">
-                <div id=review-desc>
-                    <h1 class="page-header"><?php echo $data['title']?></h1>
-                    <p id="author"><?php echo $data['author']?></p>
-                </div>
-                <img src=<?php echo $data['bookPicture']?> class="review-image">
+    <div class="container">
+        <div id="review-detail">
+            <div id=review-desc>
+                <h1 class="page-header"><?php echo $data['title']?></h1>
+                <p id="author"><?php echo $data['author']?></p>
             </div>
-            <form action="/review/submit" id ="review-form" method="POST">
-                <div class="review-rate">
-                    <p id="review-addrate">Add Rating</p>
-                    <div class="rating-star-wrapper">
-                      <img src="/public/images/star.png" class="filled-star star star1">
-                      <img src="/public/images/star-unfilled.png" class="unfilled-star star star1">
-                      <img src="/public/images/star.png" class="filled-star star star2">
-                      <img src="/public/images/star-unfilled.png" class="unfilled-star star star2">
-                      <img src="/public/images/star.png" class="filled-star star star3">
-                      <img src="/public/images/star-unfilled.png" class="unfilled-star star star3">
-                      <img src="/public/images/star.png" class="filled-star star star4">
-                      <img src="/public/images/star-unfilled.png" class="unfilled-star star star4">
-                      <img src="/public/images/star.png" class="filled-star star star5">
-                      <img src="/public/images/star-unfilled.png" class="unfilled-star star star5">
-                    </div>
-                    <input id="ratinginput" type="number" name="rating" value="">
-                    <p id="review-addrate">Add Comment</p>
-                    <textarea id = "commentinput" class ="comment-input" form ="review-form" name="comment"></textarea>
-                </div>
-                <div class ="comment-row">
-                    <a href="/History" id = "back-button">Back</a>
-                    <button id="save-button" class="disabled-save-button" type="submit" disabled>SAVE</button>
-                </div>
-            </form>
+            <img src=<?php echo $data['bookPicture']?> class="review-image">
         </div>
+        <form action="/review/submit" id ="review-form" method="POST">
+            <div class="review-rate">
+                <p id="review-addrate">Add Rating</p>
+                <div class="rating-star-wrapper">
+                    <img src="/public/images/star.png" class="filled-star star star1">
+                    <img src="/public/images/star-unfilled.png" class="unfilled-star star star1">
+                    <img src="/public/images/star.png" class="filled-star star star2">
+                    <img src="/public/images/star-unfilled.png" class="unfilled-star star star2">
+                    <img src="/public/images/star.png" class="filled-star star star3">
+                    <img src="/public/images/star-unfilled.png" class="unfilled-star star star3">
+                    <img src="/public/images/star.png" class="filled-star star star4">
+                    <img src="/public/images/star-unfilled.png" class="unfilled-star star star4">
+                    <img src="/public/images/star.png" class="filled-star star star5">
+                    <img src="/public/images/star-unfilled.png" class="unfilled-star star star5">
+                </div>
+                <input id="ratinginput" type="number" name="rating" value="">
+                <p id="review-addrate">Add Comment</p>
+                <textarea id = "commentinput" class ="comment-input" form ="review-form" name="comment"></textarea>
+            </div>
+            <div class ="comment-row">
+                <a href="/History" id = "back-button">Back</a>
+                <button id="save-button" class="disabled-save-button" type="submit" disabled>SAVE</button>
+            </div>
+        </form>
+    </div>
     <script src="/public/js/review.js"></script>
+    <script>
+        function onLoad() {
+            gapi.load('auth2', function() {
+            gapi.auth2.init();
+            });
+        }        
+        function signOut() {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut().then(function () {
+                console.log('User signed out.');
+            });
+        }
+    </script>
 </body>
 </html>
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..707632dcae8d2de97e4d6883184911888438f4f6
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,5 @@
+{
+    "require": {
+        "sonata-project/google-authenticator": "^2.2"
+    }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000000000000000000000000000000000000..f36ec219428a511a46ef23ef81b8d453f0d0bcde
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,75 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "e2df69fcfa963bd7ffe8e358d7e93d70",
+    "packages": [
+        {
+            "name": "sonata-project/google-authenticator",
+            "version": "2.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sonata-project/GoogleAuthenticator.git",
+                "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762",
+                "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Google\\Authenticator\\": "src/",
+                    "Sonata\\GoogleAuthenticator\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Stocker",
+                    "email": "me@chregu.tv"
+                },
+                {
+                    "name": "Andre DeMarre",
+                    "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989"
+                },
+                {
+                    "name": "Thomas Rabaix",
+                    "email": "thomas.rabaix@gmail.com"
+                }
+            ],
+            "description": "Library to integrate Google Authenticator into a PHP project",
+            "homepage": "https://github.com/sonata-project/GoogleAuthenticator",
+            "keywords": [
+                "google authenticator"
+            ],
+            "time": "2018-07-18T22:08:02+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}
diff --git a/example.php b/example.php
new file mode 100644
index 0000000000000000000000000000000000000000..50366d2fa9c470a34eb2474568393a4642a0ad78
--- /dev/null
+++ b/example.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+include_once __DIR__.'/../src/FixedBitNotation.php';
+include_once __DIR__.'/../src/GoogleAuthenticator.php';
+include_once __DIR__.'/../src/GoogleQrUrl.php';
+
+$secret = 'XVQ2UIGO75XRUKJO';
+$code = '846474';
+
+$g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
+
+echo 'Current Code is: ';
+echo $g->getCode($secret);
+
+echo "\n";
+
+echo "Check if $code is valid: ";
+
+if ($g->checkCode($secret, $code)) {
+    echo "YES \n";
+} else {
+    echo "NO \n";
+}
+
+$secret = $g->generateSecret();
+echo "Get a new Secret: $secret \n";
+echo "The QR Code for this secret (to scan with the Google Authenticator App: \n";
+
+echo \Sonata\GoogleAuthenticator\GoogleQrUrl::generate('chregu', $secret, 'GoogleAuthenticatorExample');
+echo "\n";
diff --git a/index.php b/index.php
index afa4a1aba47c70e2c90aea4438d7feb3d3673b83..996aa32a611deba4c23295a3f6db6b5f0bf47503 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,6 @@
 <?php
 
 ini_set('display_errors', 0);
+require_once 'vendor/autoload.php';
 require_once 'app/init.php';
 
diff --git a/public/css/main.css b/public/css/main.css
index 7bfd34b9d22487c2d17c8ba9e1c19c5793f86d2e..4d7b3da810e0365d2b1e65c0d32cb3cfdc3335d6 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -904,6 +904,13 @@ body {
     display: none;
 }
 
+.register-google-login {
+    display: flex;
+    flex-flow: column;
+    /* justify-content: center; */
+    align-items: center;
+}
+
 @keyframes lds-spinner {
     0% {
         opacity: 1;
@@ -1034,4 +1041,74 @@ body {
     height: 200px !important;
     -webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px);
     transform: translate(-100px, -100px) scale(1) translate(100px, 100px);
+}
+
+.qr-code{
+    display: block;
+    text-align: center; 
+    width: 100%;
+    height: 100%;
+    margin: auto;
+    border-radius: 3px;
+}
+
+* .inp {
+    position: relative;
+    margin: auto;
+    width: 100%;
+    max-width: 280px;
+}
+* .inp .label {
+    position: absolute;
+    top: 16px;
+    left: 0;
+    font-size: 16px;
+    color: #9098a9;
+    font-weight: 500;
+    transform-origin: 0 0;
+    transition: all 0.2s ease;
+}
+* .inp .border {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 2px;
+    width: 100%;
+    background: #07f;
+    transform: scaleX(0);
+    transform-origin: 0 0;
+    transition: all 0.15s ease;
+}
+* .inp input {
+    -webkit-appearance: none;
+    width: 100%;
+    border: 0;
+    font-family: inherit;
+    padding: 12px 0;
+    height: 48px;
+    font-size: 16px;
+    font-weight: 500;
+    border-bottom: 2px solid #c8ccd4;
+    background: none;
+    border-radius: 0;
+    color: #223254;
+    transition: all 0.15s ease;
+}
+* .inp input:hover {
+    background: rgba(34,50,84,0.03);
+}
+* .inp input:not(:placeholder-shown) + span {
+    color: #5a667f;
+    transform: translateY(-26px) scale(0.75);
+}
+* .inp input:focus {
+    background: none;
+    outline: none;
+}
+* .inp input:focus + span {
+    color: #07f;
+    transform: translateY(-26px) scale(0.75);
+}
+* .inp input:focus + span + .border {
+    transform: scaleX(1);
 }
\ No newline at end of file
diff --git a/public/images/profile/32 b/public/images/profile/32
new file mode 100644
index 0000000000000000000000000000000000000000..27cac80db760b8d73f20e7d0401ee5f3b1e5e530
Binary files /dev/null and b/public/images/profile/32 differ
diff --git a/public/js/detail.js b/public/js/detail.js
index f3d3a3cd777d45704ea1ef3b49b310fc7eaee87f..c0ddcce64d299c92f8c1ce866ed8c01267770417 100644
--- a/public/js/detail.js
+++ b/public/js/detail.js
@@ -1,5 +1,7 @@
 function order() {
     var total_order_element = document.getElementById("total-order");
+    var token = document.getElementById("token").value;
+    console.log("token: "+token);
     var sum_order = total_order_element.options[total_order_element.selectedIndex].value;
     var xhttp = new XMLHttpRequest();
     xhttp.open("POST", "/detail/order");
@@ -7,11 +9,21 @@ function order() {
     xhttp.onreadystatechange = function() {
         console.log(this.responseText);
         if (this.readyState == 4 && this.status == 200 && this.responseText != -1) {
+            document.getElementById("text-msg-detail").innerHTML = "Pemesanan Berhasil!";
             document.getElementById("transaction-number").innerHTML = this.responseText;
             document.getElementById("notif").setAttribute("style", "display: flex");
+            document.getElementById("check-notif").src = "/public/images/check.png";
+        } else {
+            document.getElementById("text-msg-detail").innerHTML = "Pemesanan Gagal!";
+            document.getElementById("transaction-number").innerHTML = -1;
+            document.getElementById("notif").setAttribute("style", "display: flex");
+            document.getElementById("check-notif").src = "/public/images/close.png";
+
         }
     }
-    xhttp.send(JSON.stringify({total : sum_order}));
+    xhttp.send(JSON.stringify({
+        total : sum_order,
+        token : token}));
 }
 
 function close_notif() {
diff --git a/public/js/login.js b/public/js/login.js
index 2e3952bcc60fa7ea215d9b62ca5adbbf9c7d2039..110a46387507322503045887d92848def2e2fe3a 100644
--- a/public/js/login.js
+++ b/public/js/login.js
@@ -33,4 +33,22 @@ document.querySelector("#password input").addEventListener("input", function() {
         pass_valid = true;
     }
     toggle_btn();
-})
\ No newline at end of file
+});
+
+function onSignIn(googleUser) {
+    var id_token = googleUser.getAuthResponse().id_token;
+    var xhr = new XMLHttpRequest();
+    xhr.onreadystatechange = function() {
+        if (this.readyState == 4 && this.status == 200) {
+            window.location="/home";
+        }
+        else {
+            var auth2 = gapi.auth2.getAuthInstance();
+            auth2.signOut();
+        }
+    }
+
+    xhr.open('POST', '/login/tokensignin');
+    xhr.setRequestHeader('Content-Type', 'application/json');
+    xhr.send(JSON.stringify({id_token: id_token}));
+}
\ No newline at end of file
diff --git a/public/js/profile.js b/public/js/profile.js
new file mode 100644
index 0000000000000000000000000000000000000000..d26153eb0e2a39618d3132309d02ee6a54f6d6b8
--- /dev/null
+++ b/public/js/profile.js
@@ -0,0 +1,11 @@
+// console.log("No kartu: " + document.getElementById("no-kartu").value);
+var xhttp = new XMLHttpRequest();
+xhttp.onreadystatechange = function() {
+    if (this.readyState == 4 && this.status == 200) {
+        var response = JSON.parse(this.responseText);
+        document.getElementById("qr-img").src = response.values
+    }
+}
+xhttp.open("POST", "http://localhost:3000/qrcode", true);
+xhttp.setRequestHeader("Content-type", "application/json");
+xhttp.send(JSON.stringify({ "no_kartu": document.getElementById("no-kartu").value }));
diff --git a/service/rest/api/controllers/key.js b/service/rest/api/controllers/key.js
new file mode 100644
index 0000000000000000000000000000000000000000..d9256a71a3de2f953eb98d4df55c2de73116de50
--- /dev/null
+++ b/service/rest/api/controllers/key.js
@@ -0,0 +1,58 @@
+var response = require('../controllers/res');
+var nasabah = require('../models/nasabah');
+var QRCode = require('qrcode');
+var speakeasy = require('speakeasy');
+
+exports.key = function(req, res) {
+    var no_kartu = req.body.no_kartu;
+    nasabah.getKeyByCard(no_kartu, function(error, rows) {
+        if (error) {
+           console.log(error)
+            return res.status(500).json({
+                message : 'Internal server error'
+            }); 
+        }
+        else {
+            if (rows.length == 0) {
+                console.log("TIDAK VALID: "+no_kartu);
+                response.ok(0, res);
+            } else {
+                console.log("KEY " + no_kartu + " IS " + rows[0].secret_key)
+                response.ok(rows[0].secret_key, res);
+            }
+        }
+    });
+};
+
+exports.qrcode = function(req, res) {
+    var no_kartu = req.body.no_kartu;
+    nasabah.getKeyByCard(no_kartu, function(error, rows) {
+        if (error) {
+           console.log(error)
+            return res.status(500).json({
+                message : 'Internal server error'
+            }); 
+        }
+        else {
+            if (rows.length == 0) {
+                console.log("TIDAK VALID: "+no_kartu);
+                response.ok(0, res);
+            } else {
+                var secret = rows[0].secret_key;
+                var label = 'token';
+                var issuer = 'probook' 
+                const otpAuthUrl = speakeasy.otpauthURL({
+                      secret,
+                      label,
+                      issuer,
+                      encoding: 'base32',
+                    });
+                QRCode.toDataURL(otpAuthUrl, function(err, image_data) {
+                    // console.log(image_data); // A data URI for the QR code image
+                    response.ok(image_data, res);
+                });
+                
+            }
+        }
+    });
+};
\ No newline at end of file
diff --git a/service/rest/api/controllers/transfer.js b/service/rest/api/controllers/transfer.js
index 6031da828d8b2d9e2bddeb158d2c7cda45e8b42d..6b06d8b05eb93467f9bb8afec335ab41a28db89b 100644
--- a/service/rest/api/controllers/transfer.js
+++ b/service/rest/api/controllers/transfer.js
@@ -1,67 +1,97 @@
 const response = require('../controllers/res');
 const nasabah = require('../models/nasabah');
 const transaksi = require('../models/transaksi');
+var speakeasy = require('speakeasy');
 
 exports.transfer = function(req, res) {
 	const pengirim = req.body.no_pengirim;
 	const penerima = req.body.no_penerima;
 	const jumlah = Number(req.body.jumlah);
+	const token = req.body.token;
+	console.log(token)
+	console.log(token[0])
+	console.log(token[1])
 
-	nasabah.getNasabahByCard(pengirim, function(error, rows) {
-		if (error) {
-			console.log(error);
-			return res.status(500).json({
-                message : 'Internal server error'
-            });
-		}
-		else {
-			if (rows.length == 0) {
-				response.notFound('No kartu pengirim tidak ditemukan', res);
-			}
-			else if (Number(rows[0].saldo) < jumlah) {
-				response.notAcceptable('Saldo pengirim tidak cukup', res);
-			}
-			else {
-				let saldo_pengirim = Number(rows[0].saldo);
-				nasabah.getNasabahByCard(penerima, function(error, rows) {
-					if (error) {
-						console.log(error);
-						return res.status(500).json({
-					        message : 'Internal server error'
-					    });
-					}
-					else {
-						if (rows.length == 0) {
-							response.notFound('No kartu penerima tidak ditemukan', res);
-						}
-						else {
-							let saldo_penerima = Number(rows[0].saldo);
-							console.log(saldo_penerima + jumlah);
-							nasabah.updateBalanceByCard(penerima, pengirim, (saldo_penerima + jumlah), (saldo_pengirim - jumlah), function(error) {
-								if (error) {
-									console.log(error);
-									return res.status(500).json({
-						                message : 'Internal server error'
-						            });
-								}
-								else {
-									transaksi.insertNewTransaction(penerima, pengirim, jumlah, function(error) {
-										if (error) {
-											console.log(error);
-											return res.status(500).json({
-								                message : error
-								            });
-										}
-										else {
-											response.ok('Transfer berhasil', res);
-										}
-									})
-								}
-							});
-						}
-					}
-				})
-			}
-		}
+	nasabah.getKeyByCard(pengirim, function(error, rows) {
+	    if (error) {
+	       console.log(error)
+	        return res.status(500).json({
+	            message : 'Internal server error'
+	        }); 
+	    }
+	    else {
+	        if (rows.length == 0) {
+	            console.log("TIDAK VALID: "+pengirim);
+	            response.notFound('No kartu pengirim tidak ditemukan', res);
+	        } else {
+	            console.log("VALIDASI TOKEN: " + token)
+            	if (speakeasy.totp.verify({	secret: rows[0].secret_key,
+                                            encoding: 'base32',
+                                            token: token })) {
+
+        			nasabah.getNasabahByCard(pengirim, function(error, rows) {
+        				if (error) {
+        					console.log(error);
+        					return res.status(500).json({
+        		                message : 'Internal server error'
+        		            });
+        				}
+        				else {
+        					if (rows.length == 0) {
+        						response.notFound('No kartu pengirim tidak ditemukan', res);
+        					}
+        					else if (Number(rows[0].saldo) < jumlah) {
+        						response.notAcceptable('Saldo pengirim tidak cukup', res);
+        					}
+        					else {
+        						let saldo_pengirim = Number(rows[0].saldo);
+        						nasabah.getNasabahByCard(penerima, function(error, rows) {
+        							if (error) {
+        								console.log(error);
+        								return res.status(500).json({
+        							        message : 'Internal server error'
+        							    });
+        							}
+        							else {
+        								if (rows.length == 0) {
+        									response.notFound('No kartu penerima tidak ditemukan', res);
+        								}
+        								else {
+        									let saldo_penerima = Number(rows[0].saldo);
+        									console.log(saldo_penerima + jumlah);
+        									nasabah.updateBalanceByCard(penerima, pengirim, (saldo_penerima + jumlah), (saldo_pengirim - jumlah), function(error) {
+        										if (error) {
+        											console.log(error);
+        											return res.status(500).json({
+        								                message : 'Internal server error'
+        								            });
+        										}
+        										else {
+        											transaksi.insertNewTransaction(penerima, pengirim, jumlah, function(error) {
+        												if (error) {
+        													console.log(error);
+        													return res.status(500).json({
+        										                message : error
+        										            });
+        												}
+        												else {
+        													response.ok('Transfer berhasil', res);
+        												}
+        											})
+        										}
+        									});
+        								}
+        							}
+        						})
+        					}
+        				}
+        			});
+
+            	} else {
+            		response.notAcceptable('Token salah', res);
+            	}
+	        }
+	    }
 	});
+	
 }
diff --git a/service/rest/api/models/nasabah.js b/service/rest/api/models/nasabah.js
index 841a4176fbf43726c112cdf15b6d4db2531dda1f..734982c1b16d27e443750e44d792d22d7b18942f 100644
--- a/service/rest/api/models/nasabah.js
+++ b/service/rest/api/models/nasabah.js
@@ -12,6 +12,17 @@ module.exports = {
         });
     },
 
+    getKeyByCard: function(card_num, callback) {
+        connection.query('SELECT secret_key FROM nasabah WHERE no_kartu = ? LIMIT 1', card_num, function (error, rows, fields) {
+            if (error) {
+                return callback(error);
+            }
+            else {
+                return callback(null, rows);
+            }
+        });
+    },
+
     updateBalanceByCard: function(card_receiver, card_sender, receiver_new_balance, sender_new_balance, callback) {
         connection.getConnection(function(error, conn) {
             if (error) {
diff --git a/service/rest/api/routes/route.js b/service/rest/api/routes/route.js
index 622afa25afbac0a2db02c268bba72606d9952788..f72ec8692b8f736c801d080646449bef83fe8e91 100644
--- a/service/rest/api/routes/route.js
+++ b/service/rest/api/routes/route.js
@@ -1,5 +1,6 @@
 module.exports = function(app) {
     const validasi = require('../controllers/validasi');
+    const key = require('../controllers/key');
     const nasabah = require('../controllers/transfer');
 
     app.use(function(req, res, next) {
@@ -20,4 +21,10 @@ module.exports = function(app) {
 
     app.route('/transfer')
     	.post(nasabah.transfer);
+
+    app.route('/key')
+        .post(key.key);
+
+    app.route('/qrcode')
+        .post(key.qrcode);
 };
\ No newline at end of file
diff --git a/service/rest/database/webservice_bank.sql b/service/rest/database/webservice_bank.sql
index 3706ce0682722ab7b94126d8048ca2fc3e7e32dc..0946a5598d5b6141aebf8f3c8ffe29495524150b 100644
--- a/service/rest/database/webservice_bank.sql
+++ b/service/rest/database/webservice_bank.sql
@@ -1,8 +1,8 @@
--- MySQL dump 10.13  Distrib 5.7.17, for macos10.12 (x86_64)
+-- MySQL dump 10.16  Distrib 10.3.10-MariaDB, for Win64 (AMD64)
 --
 -- Host: localhost    Database: webservice_bank
 -- ------------------------------------------------------
--- Server version	5.7.21
+-- Server version	10.3.10-MariaDB
 
 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
@@ -26,7 +26,8 @@ CREATE TABLE `nasabah` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `nama` varchar(255) NOT NULL,
   `no_kartu` varchar(16) NOT NULL,
-  `saldo` decimal(50,2) NOT NULL DEFAULT '0.00',
+  `saldo` decimal(50,2) NOT NULL DEFAULT 0.00,
+  `secret_key` varchar(32) DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `no_kartu_UNIQUE` (`no_kartu`)
 ) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
@@ -38,7 +39,7 @@ CREATE TABLE `nasabah` (
 
 LOCK TABLES `nasabah` WRITE;
 /*!40000 ALTER TABLE `nasabah` DISABLE KEYS */;
-INSERT INTO `nasabah` VALUES (1,'Charlotte Crane','5276132361916817',21394667.00),(2,'Ifeoma Dennis','5286723727626013',28139541.00),(3,'Keefe Stewart','5325129205703212',30465052.00),(4,'Rooney Warren','5227979660057598',10377602.00),(5,'Merrill Cook','5190360108515658',40538528.00),(6,'Holmes Cash','5177140223066341',12910688.00),(7,'Jorden Gutierrez','5285983471586166',7535644.00),(8,'Leroy Gay','5383964234862319',5244400.00),(9,'Frances Fuller','5527141327317884',34620348.00),(10,'Yeo Woodward','5103924062586793',40493665.00),(11,'Cole Ratliff','5379113508344093',34194939.00),(12,'Hammett Cline','5320135648090286',22486289.00),(13,'Priscilla Cross','5405128358909207',37758436.00),(14,'Fritz Mcintyre','5338433399779607',8039850.00),(15,'Kaden Page','5445962468685279',4612701.00),(16,'Autumn Davenport','5266297387437569',48670986.00),(17,'Rose Kaufman','5154262923084478',39986708.00),(18,'Mufutau Frazier','5350925176673724',30049129.00),(19,'Bryar Spence','5485026381319456',1499273.00),(20,'Rhonda Bentley','5397712060970290',35638064.00),(21,'Mara Joseph','5423839779395168',13923574.00),(22,'Daria Hale','5579185435634927',47652543.00),(23,'Candice Caldwell','5326739572440641',38261784.00),(24,'Gareth Dawson','5286083783472897',34018735.00),(25,'Azalia Camacho','5539663075005672',11962318.00),(26,'Grady Carlson','5448133600195182',16136156.00),(27,'Fredericka Brown','5272618031689226',36289974.00),(28,'Travis Curtis','5490283076847159',4512989.00),(29,'Kendall West','5538010661301886',1133173.00),(30,'Raphael Wallace','5416001472882112',37005267.00),(31,'Jeremy Riley','5304615022923772',37897128.00),(32,'Addison Hays','5199313088377376',8085189.00),(33,'Giacomo Gillespie','5390474676309531',32954873.00),(34,'Cleo Humphrey','5344531319507198',45993595.00),(35,'Ryan Nunez','5369734784023838',12806772.00),(36,'Sybil Mullen','5165912794190500',9735406.00),(37,'Sawyer Glover','5537167229140320',41045077.00),(38,'Hadassah Haynes','5268821798543794',5363287.00),(39,'Pearl Fitzpatrick','5559189881670850',23468519.00),(40,'Karyn Mcintyre','5591062763383894',47605383.00),(41,'Gavin Horn','5206803166426641',33293723.00),(42,'Rogan Bender','5410244319572407',8027515.00),(43,'Rigel House','5374615452371802',11362760.00),(44,'Roth Carey','5286312813966002',41241779.00),(45,'Bianca Conway','5544237251759510',19697493.00),(46,'Gray Olsen','5217134586897626',24166806.00),(47,'Ainsley Hatfield','5270443848826412',15670510.00),(48,'Aiko Calhoun','5534182104590724',37706349.00),(49,'Shelly Glass','5116273284860353',1669649.00),(50,'Regina Daniel','5417152860720403',40613394.00),(51,'Nerea Santana','5351672888845765',14199809.00),(52,'Ruth Miller','5459431609457710',17137056.00),(53,'Addison Mcclain','5480813762628380',45995104.00),(54,'Gail Shelton','5225789026415311',13914823.00),(55,'Kenneth Neal','5466848953504519',32152391.00),(56,'Ciaran Lewis','5549310920364809',16644423.00),(57,'Ocean Manning','5550067471358624',31729780.00),(58,'Erin Carson','5206305362172607',3800171.00),(59,'Samuel Irwin','5396926467981566',10050739.00),(60,'Herrod Howard','5133113883436198',28583062.00),(61,'Jescie Moon','5563590114328535',30836039.00),(62,'Claire Morin','5108326696983005',42664381.00),(63,'Lars Frank','5407906446397982',32697340.00),(64,'Summer Carroll','5499091893011854',4864930.00),(65,'Maite Santos','5477298142460180',28755025.00),(66,'Clark Fuentes','5349245581534385',49709793.00),(67,'Jordan Calhoun','5461689590615457',30631938.00),(68,'Audra Howell','5595960727394509',38964910.00),(69,'Carter Turner','5384289657823021',17501706.00),(70,'Kelsey Barry','5294109698613976',47103610.00),(71,'Chadwick Pace','5127999712180512',31375005.00),(72,'Shana Holland','5289830083014093',34205904.00),(73,'Eleanor Horn','5332808922265453',17735274.00),(74,'Jorden Farley','5559043425807397',22352248.00),(75,'Sebastian Johns','5174458385196463',24266219.00),(76,'Bevis Spence','5491952705015961',42504462.00),(77,'Hanae Rodriquez','5121081065598883',5348956.00),(78,'Malachi Moran','5334538526549352',1015059.00),(79,'Keely Moreno','5141292744575830',28593189.00),(80,'Xerxes Howe','5290282368400133',20999720.00),(81,'Dolan Howell','5185262453474290',4338581.00),(82,'Aretha Cooley','5346678478221588',19909990.00),(83,'Katelyn Ward','5532315662946555',44976212.00),(84,'Ahmed Kirkland','5519962750810297',40448438.00),(85,'Eve Clayton','5157084593586682',12252670.00),(86,'Baker Beck','5241166234480108',1543855.00),(87,'Carissa Gonzalez','5383436249380256',4001159.00),(88,'Darryl Cruz','5450352695891522',30342987.00),(89,'Lucius Terrell','5154786522417788',40738793.00),(90,'Xavier Smith','5122865966224170',20748740.00),(91,'Clarke Lancaster','5243967066072295',12564403.00),(92,'Adam Mcgee','5335172916188024',48521171.00),(93,'Colton Moore','5359008695947712',43491126.00),(94,'Kevin Glass','5354252342545025',7928903.00),(95,'Kiara Vaughan','5403970891646651',36640479.00),(96,'Harriet Cherry','5475997632056281',38104320.00),(97,'Armando Hardin','5548607767377427',20439149.00),(98,'Thor Dudley','5220642403737663',18897109.00),(99,'Chandler Alford','5534260752591994',44255207.00),(100,'Camden Head','5562991563901817',44065587.00);
+INSERT INTO `nasabah` VALUES (1,'Charlotte Crane','5276132361916817',66394667.00,'KJ3CYOJXJRFHKJSJHISV2OJRMNKWS5B2'),(2,'Ifeoma Dennis','5286723727626013',28139541.00,'MRGG6ZKUOVYSGZ2WNV2CS3BYNN3HERKP'),(3,'Keefe Stewart','5325129205703212',30465052.00,'M5UTGYZJMFXHIWBOEZ5XCWZGGZ4HQ6LB'),(4,'Rooney Warren','5227979660057598',10377602.00,'GFGUI2DLKRFTMNBIK5OU6UZKGF5TOJTC'),(5,'Merrill Cook','5190360108515658',40538528.00,'H52XSTTPOIYCSUT2GZUUCMTJEMRVO6LU'),(6,'Holmes Cash','5177140223066341',12910688.00,'FZYUI3SQMFBGQ4ZVORAGWLBPOQWEG5D3'),(7,'Jorden Gutierrez','5285983471586166',7535644.00,'ORZGW3S5JRXUQRZMJQ2WWZLWEMXEUP23'),(8,'Leroy Gay','5383964234862319',5244400.00,'IJ3SK5RWJESV4U2NPE5UUI3SM42DS52T'),(9,'Frances Fuller','5527141327317884',34620348.00,'LVRDUS3ZHFNXAOC3OFGSMJKONZLF442I'),(10,'Yeo Woodward','5103924062586793',40493665.00,'FJSCYI3DHR2FCT3OJVAVEVCVHQSEMUZJ'),(11,'Cole Ratliff','5379113508344093',34194939.00,'KVRGOKJXOJWDIILRMI7HUM3OI5YDKQCI'),(12,'Hammett Cline','5320135648090286',22486289.00,'FA2TS2BXIIVFO5B2LN2DCRBJPM4EEOBQ'),(13,'Priscilla Cross','5405128358909207',37758436.00,'PBJUSR2RPBSEC7JEHZ3XWKT3EEUTYNCG'),(14,'Fritz Mcintyre','5338433399779607',8039850.00,'IJRFANZMIFZF4NKHM42TM6SHNEWFKP2U'),(15,'Kaden Page','5445962468685279',4612701.00,'I5EXIILQHJICST3MPEZFCP3ZPJCW2IZ6'),(16,'Autumn Davenport','5266297387437569',48670986.00,'ORRWO3R7IQRVE6TTLJ4TOSJMFZDDWUCL'),(17,'Rose Kaufman','5154262923084478',39986708.00,'HNTDAUSYOFDW2XS5EQXDIMTOI4SSCKC5'),(18,'Mufutau Frazier','5350925176673724',30049129.00,'LV4FEKBXJM2DK5T5JBUWKS3WG5NXGODM'),(19,'Bryar Spence','5485026381319456',1499273.00,'ENZTAQDPIJ6UELCUKR2UGVKQKR3GYQCK'),(20,'Rhonda Bentley','5397712060970290',35638064.00,'ONUUQ4SSLNFEUWTXJQWDGY25NUVEELSI'),(21,'Mara Joseph','5423839779395168',13923574.00,'ORCCM7KILBAEO6LBLMUVQYRZMJFHQYTU'),(22,'Daria Hale','5579185435634927',47652543.00,'KRWCGMRMENTHW23QHA2FCUC5HJBU2KR4'),(23,'Candice Caldwell','5326739572440641',38261784.00,'KUUDK4DFMFFDMOCUOJ5FEVCNMJOTCT25'),(24,'Gareth Dawson','5286083783472897',34018735.00,'KZSFI3J4IV5DCPZUNURTIVBOJM3EARDG'),(25,'Azalia Camacho','5539663075005672',11962318.00,'OI7DU5C6F5PG6IKIEZLEKNCIJBNWMPZ2'),(26,'Grady Carlson','5448133600195182',16136156.00,'OJSCQ7L5LBETG42IJISD4ZTSFYZSGYSU'),(27,'Fredericka Brown','5272618031689226',36289974.00,'KRNFEXK5OEUCU5JWJ5ICGVDEHBHXSZKW'),(28,'Travis Curtis','5490283076847159',4512989.00,'JZLUYVTYNZPH2JCKGBWEQJJBOEUS6PSC'),(29,'Kendall West','5538010661301886',1133173.00,'NNTTSJLVIZJTCNS3HJVTY33MOJXUGNR6'),(30,'Raphael Wallace','5416001472882112',37005267.00,'OB3VAOLUM5IUA2CDLAUVCMBFHF3FMOST'),(31,'Jeremy Riley','5304615022923772',37897128.00,'LVCWEOJUINKGKMCNPI5EQ23INMZTG6K5'),(32,'Addison Hays','5199313088377376',8085189.00,'HRQT4NZRHZ5UE33LIQRUMZLMO4UVWM2Q'),(33,'Giacomo Gillespie','5390474676309531',32954873.00,'HE7VGWZEPBQTKR2RHBFHMW3FIJVX2OCM'),(34,'Cleo Humphrey','5344531319507198',45993595.00,'PNBXCJDYHZATGY2WF5RUOVLGHF2VOKK2'),(35,'Ryan Nunez','5369734784023838',12806772.00,'EZAGIZBBJAWH24RENEUGYVLYOEZWI2ZJ'),(36,'Sybil Mullen','5165912794190500',9735406.00,'PI3EETL2FA2DC6SNIMYWONK2F5XTM6JQ'),(37,'Sawyer Glover','5537167229140320',41045077.00,'KARUUJDIHZ5TUI2FGVHEK4KEKRRX22J2'),(38,'Hadassah Haynes','5268821798543794',5363287.00,'G52VOSJFORGW6ULQOYSCKNZGOUVDGWBE'),(39,'Pearl Fitzpatrick','5559189881670850',23468519.00,'JZJTITLGNYSFQ6TDMEYCMVBXHFTDUORR'),(40,'Karyn Mcintyre','5591062763383894',47605383.00,'HY7TQ6RXIR6UC5SWNR4DUOK6KREUYQTZ'),(41,'Gavin Horn','5206803166426641',33293723.00,'HBFGOLZ2OI2VI4CMFYXWMS2MHZZW6VDP'),(42,'Rogan Bender','5410244319572407',8027515.00,'KF2W4LBEHJLFI4KBLVEGM2CBERIG4TRS'),(43,'Rigel House','5374615452371802',11362760.00,'HBDG4R2KGFRVKOZVMRWVOL2GMJGE4QTP'),(44,'Roth Carey','5286312813966002',41241779.00,'PJ5TS626JFTV4TKGJJGFC4JVMFTCMJLW'),(45,'Bianca Conway','5544237251759510',19697493.00,'EY4GQZJXIEUWMSDIF5UGQ6DTKRUF46LT'),(46,'Gray Olsen','5217134586897626',24166806.00,'JNQWW5CKFQ7DKURMMN3UG5JSOQ2DIQTN'),(47,'Ainsley Hatfield','5270443848826412',15670510.00,'LJRDA3LMKQ4GIZSMIIXWCJD5ONZVEQLY'),(48,'Aiko Calhoun','5534182104590724',37706349.00,'PAXFUKJGIRNHUJKLJNEE6TS2FQ6EGNBM'),(49,'Shelly Glass','5116273284860353',1669649.00,'KVKVUUS2FBNWMILHOI5DAVSPNFQVIPRK'),(50,'Regina Daniel','5417152860720403',40613394.00,'EQSTOMCXGZEDC7K5GFATWSZIEZFCSJCJ'),(51,'Nerea Santana','5351672888845765',14199809.00,'KFFDCSDBEFNWMTCTJ5GEUSS6PFETYJKI'),(52,'Ruth Miller','5459431609457710',17137056.00,'MRYUY6JGLBBCGKKBNRDFCPCLMVBUM7KI'),(53,'Addison Mcclain','5480813762628380',45995104.00,'HRZXWL2LHFEVEIZYOJRG6W3NH4WEYIKV'),(54,'Gail Shelton','5225789026415311',13914823.00,'LV3WEMJKOJNS4YKUOYVHWRCEKRLUWURK'),(55,'Kenneth Neal','5466848953504519',32152391.00,'MFPEATTHMRJUGZ3JPA5DI2J6IRTCM6TI'),(56,'Ciaran Lewis','5549310920364809',16644423.00,'LYZSUMSEEQ7H2YZGNBRG6ZCPNRFX23DV'),(57,'Ocean Manning','5550067471358624',31729780.00,'KE7DE3LGMJLSSS2UER2TSVB3IJAHEZK3'),(58,'Erin Carson','5206305362172607',3800171.00,'JBZUOZLOJ47FIT2XMVBDYVCWM4XE2KJW'),(59,'Samuel Irwin','5396926467981566',10050739.00,'EZYGYYSWKU2UW4TJKZUGETJFONKDOLCY'),(60,'Herrod Howard','5133113883436198',28583062.00,'ORIWCJTBFFAHMXLUINLVGY3IIJDVQI2M'),(61,'Jescie Moon','5563590114328535',30836039.00,'OEZGOVDOG5PFOL3QFFUHIUDCMERTUVBO'),(62,'Claire Morin','5108326696983005',42664381.00,'GBVV2I3LJ5GEYVDMMN5EAOCVNZGWQY3U'),(63,'Lars Frank','5407906446397982',32697340.00,'HZDHKRJQKV4DE3S6GZLUEJJ6GBJXQRBY'),(64,'Summer Carroll','5499091893011854',4864930.00,'INBFEQLCFE7HUMBKLJTEWKTHJNWDAUJM'),(65,'Maite Santos','5477298142460180',28755025.00,'JRZF4NZXFJJFOULFMZ2FGWTLEQ4W2ZLD'),(66,'Clark Fuentes','5349245581534385',49709793.00,'H5IDQLDJFZWHOVKXGA2SI22XLB3WWPRE'),(67,'Jordan Calhoun','5461689590615457',30631938.00,'HEVHOVZRO5BXUUCRPAYES3S5O45VI7JY'),(68,'Audra Howell','5595960727394509',38964910.00,'EZNT62LLHJTFIW2IGUXWETLDPISTCYJP'),(69,'Carter Turner','5384289657823021',17501706.00,'IUVDOVCAOMTGI6SOKFKHCZREME7EUOC3'),(70,'Kelsey Barry','5294109698613976',47103610.00,'G54WCTTDGVDUS2LOHI3G6QBDGBFW2XS2'),(71,'Chadwick Pace','5127999712180512',31375005.00,'MNWCQ2BMMN3UCY3IGJVUEMB6IY7S4WZD'),(72,'Shana Holland','5289830083014093',34205904.00,'KBEUKJKLKVLFIR2LGI7SI4TUEQVHMKCC'),(73,'Eleanor Horn','5332808922265453',17735274.00,'J5YHENRMON5TKYSJERKDEP2BOUQTK43T'),(74,'Jorden Farley','5559043425807397',22352248.00,'OBFCQN2CJMXCU3KPGI4CYQ2THBUHSVZQ'),(75,'Sebastian Johns','5174458385196463',24266219.00,'GNXEC6BOOJZTQRKONMYTSP2UGJTHWW25'),(76,'Bevis Spence','5491952705015961',42504462.00,'HFJVUR2KKQQWE2L5F5RW4UR2HBQSIXLI'),(77,'Hanae Rodriquez','5121081065598883',5348956.00,'MJKEM3ZSEE3SUSZKNVQWOVCLONTXSNTB'),(78,'Malachi Moran','5334538526549352',1015059.00,'OE2U2ORFNB3WQ52FGQXVCTK6J43FKNSK'),(79,'Keely Moreno','5141292744575830',28593189.00,'FBVVCKTLHRRXEPBPERHWW3JOMRTWIRKN'),(80,'Xerxes Howe','5290282368400133',20999720.00,'HZCHS3DFHFGGWMTGMYXWCYROPAYG4PDC'),(81,'Dolan Howell','5185262453474290',4338581.00,'KN4U4XKKPE5GMVTBKY4TSMLENFLHINLT'),(82,'Aretha Cooley','5346678478221588',19909990.00,'NNTC4MRTOBGXC2DILJ4XKLTGENSXIVSD'),(83,'Katelyn Ward','5532315662946555',44976212.00,'EZFVWS3QKIYU6JREOMZUU3TFI5NGKWTL'),(84,'Ahmed Kirkland','5519962750810297',40448438.00,'J5BUSQREEVKFIXKEJA2FOMTMLJVTAQSP'),(85,'Eve Clayton','5157084593586682',12252670.00,'KIUXM6TSKI6GO7LSLJBS4V2MHNCV4RBW'),(86,'Baker Beck','5241166234480108',1543855.00,'PNQTE23DGZQSM6DVPBJHMQBVOZBX2LDT'),(87,'Carissa Gonzalez','5383436249380256',4001159.00,'ENTFAN3QKQWCKXRSJF4TE6BFFJNGQ3RE'),(88,'Darryl Cruz','5450352695891522',30342987.00,'NBSW4TRQLVOUAV25GF2FKOK5FZJF47JS'),(89,'Lucius Terrell','5154786522417788',40738793.00,'EYQWMI2HJJAEYJDRIR4XGUSKGRDTYLBY'),(90,'Xavier Smith','5122865966224170',20748740.00,'MZRVEZ2UMJ6UII3DOZJDKOCDKIZVM6RX'),(91,'Clarke Lancaster','5243967066072295',12564403.00,'HAYEIILHGEZT6T2YNM5F43LVIZSWMSJR'),(92,'Adam Mcgee','5335172916188024',48521171.00,'OQ7EIQ2JHQ3UKSBRMFNXGP3IOI2TCJCP'),(93,'Colton Moore','5359008695947712',43491126.00,'LIYFEKLPIVJSGLZYEETGW3LIIJCFKSKT'),(94,'Kevin Glass','5354252342545025',7928903.00,'LN5WEZDSGQ2VO632ER4CKYSUHY2TGVSC'),(95,'Kiara Vaughan','5403970891646651',36640479.00,'G5NC4JRXJ5IVK3S5HRIFIZTSNMTEKMRJ'),(96,'Harriet Cherry','5475997632056281',38104320.00,'MI2E4ZKOONFGIPTSNU4T6UTYEU7EQ5SD'),(97,'Armando Hardin','5548607767377427',20439149.00,'OQ7H25CCOAYUIOLFNMSGWY2GHRQTSMLQ'),(98,'Thor Dudley','5220642403737663',18897109.00,'MZ5DYZZFN4YGEZJWO5IWGZ2CEQYWGMZR'),(99,'Chandler Alford','5534260752591994',44255207.00,'GFVXITRRMEVCQ3RVPAXXOV35NZRGE52R'),(100,'Camden Head','5562991563901817',1000000.00,'HJEXOVRENA5DAUJKJQ3VORKAHBXCSSCW');
 /*!40000 ALTER TABLE `nasabah` ENABLE KEYS */;
 UNLOCK TABLES;
 
@@ -53,14 +54,14 @@ CREATE TABLE `transaksi` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `no_pengirim` varchar(16) NOT NULL,
   `no_penerima` varchar(16) NOT NULL,
-  `jumlah` decimal(50,2) NOT NULL DEFAULT '0.00',
-  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `jumlah` decimal(50,2) NOT NULL DEFAULT 0.00,
+  `timestamp` timestamp NOT NULL DEFAULT current_timestamp(),
   PRIMARY KEY (`id`),
   KEY `nomor kartu_idx` (`no_pengirim`,`no_penerima`),
   KEY `nomor kartu 2` (`no_penerima`),
   CONSTRAINT `nomor kartu` FOREIGN KEY (`no_pengirim`) REFERENCES `nasabah` (`no_kartu`),
   CONSTRAINT `nomor kartu 2` FOREIGN KEY (`no_penerima`) REFERENCES `nasabah` (`no_kartu`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -69,6 +70,7 @@ CREATE TABLE `transaksi` (
 
 LOCK TABLES `transaksi` WRITE;
 /*!40000 ALTER TABLE `transaksi` DISABLE KEYS */;
+INSERT INTO `transaksi` VALUES (1,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:33:42'),(2,'5562991563901817','5276132361916817',10000000.00,'2018-11-30 04:34:14'),(3,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:37:39'),(4,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:42:59'),(5,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:43:31'),(6,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:43:32'),(7,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:44:16'),(8,'5562991563901817','5276132361916817',25000000.00,'2018-11-30 04:44:36'),(9,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:46:19'),(10,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:53:26'),(11,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 04:53:47'),(12,'5562991563901817','5276132361916817',1000000.00,'2018-11-30 07:35:58');
 /*!40000 ALTER TABLE `transaksi` ENABLE KEYS */;
 UNLOCK TABLES;
 /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -81,4 +83,4 @@ UNLOCK TABLES;
 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
 /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 
--- Dump completed on 2018-11-21 18:07:41
+-- Dump completed on 2018-11-30 14:40:11
diff --git a/service/rest/package-lock.json b/service/rest/package-lock.json
index 76e3e835d51c1566251f9726f9127b8a6aa86d7d..e624a70c204ff0d8a13d33c10542b6ce137e9409 100644
--- a/service/rest/package-lock.json
+++ b/service/rest/package-lock.json
@@ -31,8 +31,7 @@
     "ansi-regex": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-      "dev": true
+      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
     },
     "ansi-styles": {
       "version": "3.2.1",
@@ -161,6 +160,11 @@
         }
       }
     },
+    "base32.js": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz",
+      "integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro="
+    },
     "basic-auth": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@@ -264,6 +268,11 @@
         }
       }
     },
+    "builtin-modules": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
+    },
     "bytes": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz",
@@ -289,8 +298,15 @@
     "camelcase": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-      "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
-      "dev": true
+      "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+    },
+    "can-promise": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/can-promise/-/can-promise-0.0.1.tgz",
+      "integrity": "sha512-gzVrHyyrvgt0YpDm7pn04MQt8gjh0ZAhN4ZDyCRtGl6YnuuK6b4aiUTD7G52r9l4YNmxfTtEscb92vxtAlL6XQ==",
+      "requires": {
+        "window-or-global": "1.0.1"
+      }
     },
     "capture-stack-trace": {
       "version": "1.0.1",
@@ -365,6 +381,54 @@
       "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
       "dev": true
     },
+    "cliui": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+      "requires": {
+        "string-width": "1.0.2",
+        "strip-ansi": "3.0.1",
+        "wrap-ansi": "2.1.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "requires": {
+            "number-is-nan": "1.0.1"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "requires": {
+            "code-point-at": "1.1.0",
+            "is-fullwidth-code-point": "1.0.0",
+            "strip-ansi": "3.0.1"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "requires": {
+            "ansi-regex": "2.1.1"
+          }
+        }
+      }
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+    },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -460,7 +524,6 @@
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
       "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
-      "dev": true,
       "requires": {
         "lru-cache": "4.1.4",
         "shebang-command": "1.2.0",
@@ -481,6 +544,11 @@
         "ms": "2.0.0"
       }
     },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+    },
     "decode-uri-component": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
@@ -544,6 +612,11 @@
       "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
     },
+    "dijkstrajs": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
+      "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
+    },
     "dot-prop": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -575,6 +648,14 @@
       "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
       "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
     },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "requires": {
+        "is-arrayish": "0.2.1"
+      }
+    },
     "escape-html": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -611,7 +692,6 @@
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
       "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
-      "dev": true,
       "requires": {
         "cross-spawn": "5.1.0",
         "get-stream": "3.0.0",
@@ -852,6 +932,14 @@
         "unpipe": "1.0.0"
       }
     },
+    "find-up": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+      "requires": {
+        "locate-path": "2.0.0"
+      }
+    },
     "flatmap-stream": {
       "version": "0.1.2",
       "resolved": "https://registry.npmjs.org/flatmap-stream/-/flatmap-stream-0.1.2.tgz",
@@ -1418,11 +1506,15 @@
         }
       }
     },
+    "get-caller-file": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w=="
+    },
     "get-stream": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
-      "dev": true
+      "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
     },
     "get-value": {
       "version": "2.0.6",
@@ -1482,8 +1574,7 @@
     "graceful-fs": {
       "version": "4.1.15",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-      "integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA=",
-      "dev": true
+      "integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA="
     },
     "has-flag": {
       "version": "3.0.0",
@@ -1523,6 +1614,11 @@
         }
       }
     },
+    "hosted-git-info": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w=="
+    },
     "http-errors": {
       "version": "1.6.3",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
@@ -1571,6 +1667,11 @@
       "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=",
       "dev": true
     },
+    "invert-kv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+    },
     "ipaddr.js": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
@@ -1596,6 +1697,11 @@
         }
       }
     },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+    },
     "is-binary-path": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@@ -1611,6 +1717,14 @@
       "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=",
       "dev": true
     },
+    "is-builtin-module": {
+      "version": "1.0.0",
+      "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+      "requires": {
+        "builtin-modules": "1.1.1"
+      }
+    },
     "is-ci": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
@@ -1674,8 +1788,7 @@
     "is-fullwidth-code-point": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-      "dev": true
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
     },
     "is-glob": {
       "version": "4.0.0",
@@ -1761,8 +1874,7 @@
     "is-stream": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-      "dev": true
+      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
     },
     "is-windows": {
       "version": "1.0.2",
@@ -1778,8 +1890,7 @@
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
     },
     "isobject": {
       "version": "3.0.1",
@@ -1802,6 +1913,41 @@
         "package-json": "4.0.1"
       }
     },
+    "lcid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "requires": {
+        "invert-kv": "1.0.0"
+      }
+    },
+    "load-json-file": {
+      "version": "2.0.0",
+      "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+      "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+      "requires": {
+        "graceful-fs": "4.1.15",
+        "parse-json": "2.2.0",
+        "pify": "2.3.0",
+        "strip-bom": "3.0.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
+      }
+    },
+    "locate-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+      "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+      "requires": {
+        "p-locate": "2.0.0",
+        "path-exists": "3.0.0"
+      }
+    },
     "lodash.debounce": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -1818,7 +1964,6 @@
       "version": "4.1.4",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.4.tgz",
       "integrity": "sha1-UcxG6ObZUwdxyFfiTMxyDs28wDE=",
-      "dev": true,
       "requires": {
         "pseudomap": "1.0.2",
         "yallist": "3.0.2"
@@ -1859,6 +2004,14 @@
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
     },
+    "mem": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
+      "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+      "requires": {
+        "mimic-fn": "1.2.0"
+      }
+    },
     "merge-descriptors": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
@@ -1908,6 +2061,11 @@
         "mime-db": "1.37.0"
       }
     },
+    "mimic-fn": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+      "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
+    },
     "minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -2047,6 +2205,17 @@
         "abbrev": "1.1.1"
       }
     },
+    "normalize-package-data": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+      "requires": {
+        "hosted-git-info": "2.7.1",
+        "is-builtin-module": "1.0.0",
+        "semver": "5.6.0",
+        "validate-npm-package-license": "3.0.4"
+      }
+    },
     "normalize-path": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
@@ -2060,11 +2229,15 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
       "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
-      "dev": true,
       "requires": {
         "path-key": "2.0.1"
       }
     },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
     "object-copy": {
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@@ -2127,11 +2300,41 @@
       "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
       "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
     },
+    "os-locale": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+      "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+      "requires": {
+        "execa": "0.7.0",
+        "lcid": "1.0.0",
+        "mem": "1.1.0"
+      }
+    },
     "p-finally": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
+      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+    },
+    "p-limit": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+      "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+      "requires": {
+        "p-try": "1.0.0"
+      }
+    },
+    "p-locate": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+      "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+      "requires": {
+        "p-limit": "1.3.0"
+      }
+    },
+    "p-try": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+      "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
     },
     "package-json": {
       "version": "4.0.1",
@@ -2145,6 +2348,14 @@
         "semver": "5.6.0"
       }
     },
+    "parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "requires": {
+        "error-ex": "1.3.2"
+      }
+    },
     "parseurl": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
@@ -2162,6 +2373,11 @@
       "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
       "dev": true
     },
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+    },
     "path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -2177,14 +2393,28 @@
     "path-key": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
     },
     "path-to-regexp": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
       "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
     },
+    "path-type": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+      "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+      "requires": {
+        "pify": "2.3.0"
+      },
+      "dependencies": {
+        "pify": {
+          "version": "2.3.0",
+          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+        }
+      }
+    },
     "pause-stream": {
       "version": "0.0.11",
       "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
@@ -2200,6 +2430,11 @@
       "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
       "dev": true
     },
+    "pngjs": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.3.3.tgz",
+      "integrity": "sha512-1n3Z4p3IOxArEs1VRXnZ/RXdfEniAUS9jb68g58FIXMNkPJeZd+Qh4Uq7/e0LVxAQGos1eIUrqrt4FpjdnEd+Q=="
+    },
     "posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -2238,8 +2473,7 @@
     "pseudomap": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
-      "dev": true
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
     },
     "pstree.remy": {
       "version": "1.1.0",
@@ -2250,6 +2484,25 @@
         "ps-tree": "1.1.0"
       }
     },
+    "qrcode": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.2.tgz",
+      "integrity": "sha512-sYo+Tf7nx14YZSNPqnyEWefVrjUEpM4/NxOrxOk+jcQ9NhM2LCNuzGmQctDhGTQdO2YJorKY55dypx+hvVo5jw==",
+      "requires": {
+        "can-promise": "0.0.1",
+        "dijkstrajs": "1.0.1",
+        "isarray": "2.0.4",
+        "pngjs": "3.3.3",
+        "yargs": "8.0.2"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "2.0.4",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
+          "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA=="
+        }
+      }
+    },
     "qs": {
       "version": "6.5.2",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
@@ -2288,6 +2541,25 @@
         "strip-json-comments": "2.0.1"
       }
     },
+    "read-pkg": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+      "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+      "requires": {
+        "load-json-file": "2.0.0",
+        "normalize-package-data": "2.4.0",
+        "path-type": "2.0.0"
+      }
+    },
+    "read-pkg-up": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+      "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+      "requires": {
+        "find-up": "2.1.0",
+        "read-pkg": "2.0.0"
+      }
+    },
     "readable-stream": {
       "version": "2.3.6",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
@@ -2360,6 +2632,16 @@
       "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
       "dev": true
     },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
+    },
     "resolve-url": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -2394,8 +2676,7 @@
     "semver": {
       "version": "5.6.0",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=",
-      "dev": true
+      "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ="
     },
     "semver-diff": {
       "version": "2.1.0",
@@ -2437,6 +2718,11 @@
         "send": "0.16.2"
       }
     },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+    },
     "set-value": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
@@ -2469,7 +2755,6 @@
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
       "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
-      "dev": true,
       "requires": {
         "shebang-regex": "1.0.0"
       }
@@ -2477,14 +2762,12 @@
     "shebang-regex": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
-      "dev": true
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
     },
     "signal-exit": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
-      "dev": true
+      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
     },
     "snapdragon": {
       "version": "0.8.2",
@@ -2618,6 +2901,42 @@
       "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
       "dev": true
     },
+    "spdx-correct": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
+      "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==",
+      "requires": {
+        "spdx-expression-parse": "3.0.0",
+        "spdx-license-ids": "3.0.2"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA=="
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+      "requires": {
+        "spdx-exceptions": "2.2.0",
+        "spdx-license-ids": "3.0.2"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz",
+      "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg=="
+    },
+    "speakeasy": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz",
+      "integrity": "sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=",
+      "requires": {
+        "base32.js": "0.0.1"
+      }
+    },
     "split": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
@@ -2681,7 +3000,6 @@
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
       "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=",
-      "dev": true,
       "requires": {
         "is-fullwidth-code-point": "2.0.0",
         "strip-ansi": "4.0.0"
@@ -2699,16 +3017,19 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
       "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-      "dev": true,
       "requires": {
         "ansi-regex": "3.0.0"
       }
     },
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
+    },
     "strip-eof": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
-      "dev": true
+      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
     },
     "strip-json-comments": {
       "version": "2.0.1",
@@ -2965,6 +3286,15 @@
       "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
       "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
     },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "requires": {
+        "spdx-correct": "3.0.2",
+        "spdx-expression-parse": "3.0.0"
+      }
+    },
     "vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -2974,11 +3304,15 @@
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
       "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=",
-      "dev": true,
       "requires": {
         "isexe": "2.0.0"
       }
     },
+    "which-module": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+    },
     "widest-line": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
@@ -2988,6 +3322,53 @@
         "string-width": "2.1.1"
       }
     },
+    "window-or-global": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/window-or-global/-/window-or-global-1.0.1.tgz",
+      "integrity": "sha1-2+RboqKRqrxW1iz2bEW3+jIpRt4="
+    },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "requires": {
+        "string-width": "1.0.2",
+        "strip-ansi": "3.0.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+          "requires": {
+            "number-is-nan": "1.0.1"
+          }
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+          "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+          "requires": {
+            "code-point-at": "1.1.0",
+            "is-fullwidth-code-point": "1.0.0",
+            "strip-ansi": "3.0.1"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+          "requires": {
+            "ansi-regex": "2.1.1"
+          }
+        }
+      }
+    },
     "write-file-atomic": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
@@ -3005,11 +3386,43 @@
       "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
       "dev": true
     },
+    "y18n": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+    },
     "yallist": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
-      "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
-      "dev": true
+      "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
+    },
+    "yargs": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
+      "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=",
+      "requires": {
+        "camelcase": "4.1.0",
+        "cliui": "3.2.0",
+        "decamelize": "1.2.0",
+        "get-caller-file": "1.0.3",
+        "os-locale": "2.1.0",
+        "read-pkg-up": "2.0.0",
+        "require-directory": "2.1.1",
+        "require-main-filename": "1.0.1",
+        "set-blocking": "2.0.0",
+        "string-width": "2.1.1",
+        "which-module": "2.0.0",
+        "y18n": "3.2.1",
+        "yargs-parser": "7.0.0"
+      }
+    },
+    "yargs-parser": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+      "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
+      "requires": {
+        "camelcase": "4.1.0"
+      }
     }
   }
 }
diff --git a/service/rest/package.json b/service/rest/package.json
index b8762f323dc82c544153945cdecffc03cb77c8b1..32a297186ea84ab03219ca70cf787772b536f1df 100644
--- a/service/rest/package.json
+++ b/service/rest/package.json
@@ -13,7 +13,9 @@
     "body-parser": "~1.0.1",
     "express": "^4.16.4",
     "morgan": "^1.9.1",
-    "mysql": "^2.16.0"
+    "mysql": "^2.16.0",
+    "qrcode": "^1.3.2",
+    "speakeasy": "^2.0.0"
   },
   "devDependencies": {
     "nodemon": "^1.18.6"
diff --git a/service/soap/src/example/HelloWorld.java b/service/soap/src/example/HelloWorld.java
index 677198d2de8147bb963711268077e67bc8010857..cb070dcba84796cecf8ac200e7fd3e4893b76df3 100644
--- a/service/soap/src/example/HelloWorld.java
+++ b/service/soap/src/example/HelloWorld.java
@@ -35,10 +35,10 @@ public class HelloWorld {
     public static void main(String[] argv) {
 
 //       //         test getBookByTitle
-        ArrayList<Book> testGetBookByTitle = getBookByTitle("kue");
-        ArrayList<Book> testGetBookByCategory = getBookByCategory("Cooking");
-        System.out.println("\n\n Test balikan1, Judul:" + testGetBookByTitle.get(0).getTitle());
-        System.out.println("\n\n Test balikan2, Judul:" + testGetBookByCategory.get(0).getTitle());
+//        ArrayList<Book> testGetBookByTitle = getBookByTitle("kue");
+//        ArrayList<Book> testGetBookByCategory = getBookByCategory("Cooking");
+//        System.out.println("\n\n Test balikan1, Judul:" + testGetBookByTitle.get(0).getTitle());
+//        System.out.println("\n\n Test balikan2, Judul:" + testGetBookByCategory.get(0).getTitle());
 //
 //        //       test getBookDetailByID
 //        Book testGetBookDetailByID = getBookDetailByID("Xl9nDwAAQBAJ");
@@ -46,31 +46,31 @@ public class HelloWorld {
 
 
         // test BookRepository
-        DaftarHarga dummyDaftarHarga1 = new DaftarHarga("napeDwAAQBAJ", 130000);
-        DaftarPenjualan dummyDaftarPejualan1 = new DaftarPenjualan("napeDwAAQBAJ", "Cooking", 3); // test daftar baru
-        DaftarPenjualan dummyDaftarPejualan2 = new DaftarPenjualan("Xl9nDwAAQBAJ", "Cooking", 1); // test daftar yang sudah ada
-        BookRepository testBookRepository = new BookRepository();
-        try {
-            testBookRepository.connect();
-            DaftarHarga test1 = testBookRepository.getDaftarHarga("Xl9nDwAAQBAJ");
-            System.out.println("\nTest\nHarga buku: " + test1.getHarga());
-
-            test1 = testBookRepository.getDaftarHarga("tubeswbdacac");
-            System.out.println("\nTest\nHarga buku: " + test1.getHarga());
-
-            DaftarPenjualan test2 = testBookRepository.getDaftarPenjualan("Xl9nDwAAQBAJ");
-            System.out.println("\nTest\nJumlah penjualan buku: " + test2.getJumlah());
-
-            testBookRepository.insertDaftarHarga(dummyDaftarHarga1);
-            testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan1);
-            testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan2);
-
-
-        } catch (SQLException e) {
-            e.printStackTrace();
-        } finally {
-            testBookRepository.disconnect();
-        }
+//        DaftarHarga dummyDaftarHarga1 = new DaftarHarga("napeDwAAQBAJ", 130000);
+//        DaftarPenjualan dummyDaftarPejualan1 = new DaftarPenjualan("napeDwAAQBAJ", "Cooking", 3); // test daftar baru
+//        DaftarPenjualan dummyDaftarPejualan2 = new DaftarPenjualan("Xl9nDwAAQBAJ", "Cooking", 1); // test daftar yang sudah ada
+//        BookRepository testBookRepository = new BookRepository();
+//        try {
+//            testBookRepository.connect();
+//            DaftarHarga test1 = testBookRepository.getDaftarHarga("Xl9nDwAAQBAJ");
+//            System.out.println("\nTest\nHarga buku: " + test1.getHarga());
+//
+//            test1 = testBookRepository.getDaftarHarga("tubeswbdacac");
+//            System.out.println("\nTest\nHarga buku: " + test1.getHarga());
+//
+//            DaftarPenjualan test2 = testBookRepository.getDaftarPenjualan("Xl9nDwAAQBAJ");
+//            System.out.println("\nTest\nJumlah penjualan buku: " + test2.getJumlah());
+//
+//            testBookRepository.insertDaftarHarga(dummyDaftarHarga1);
+//            testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan1);
+//            testBookRepository.insertDaftarPenjualan(dummyDaftarPejualan2);
+//
+//
+//        } catch (SQLException e) {
+//            e.printStackTrace();
+//        } finally {
+//            testBookRepository.disconnect();
+//        }
 
 
 //        System.out.println(WebServiceBankApi.transfer("5276132361916817", "5285983471586166", 7535644F));
@@ -82,6 +82,8 @@ public class HelloWorld {
         Object implementor2 = new BookServiceImpl();
         String address2 = "http://localhost:9000/BookService";
         Endpoint.publish(address2, implementor2);
+
+//        System.out.println(((BookServiceImpl) implementor2).buyBook("NP1wDwAAQBAJ", 25, "5562991563901817"));
     }
 
 }
diff --git a/service/soap/src/model/Book.java b/service/soap/src/model/Book.java
index 1177bb9206122e4a772e7ad93fee2fd327dd2e1d..6012076f8e5f5b4e3dc58e3a9f6b89fcb6a4f322 100644
--- a/service/soap/src/model/Book.java
+++ b/service/soap/src/model/Book.java
@@ -43,7 +43,7 @@ public class Book implements Serializable {
         this.title = STRING_UNDEFINED;
         this.author = STRING_UNDEFINED;
         this.description = STRING_UNDEFINED;
-        this.image = "https://almasoem.sch.id/wp-content/uploads/2016/02/IMG_9738-Large-1024x683.jpg";
+        this.image = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/No_picture_available.png/401px-No_picture_available.png";
         this.price = -1;
     }
 
diff --git a/service/soap/src/service/BookService.java b/service/soap/src/service/BookService.java
index 0feef7496cc76ac303e905ed7448e3b6bd8d0727..62fa029dd1785c2c64dea5e8f3964c150f387657 100644
--- a/service/soap/src/service/BookService.java
+++ b/service/soap/src/service/BookService.java
@@ -22,7 +22,7 @@ public interface BookService {
     Book getRecommendation(String... categories);
 
     @WebMethod
-    long buyBook(String idBuku, Integer jumlah, String norek);
+    long buyBook(String idBuku, Integer jumlah, String norek, String token);
 
     @WebMethod
     Book getBookDetailByID(String idBuku) throws SQLException;
diff --git a/service/soap/src/service/BookServiceImpl.java b/service/soap/src/service/BookServiceImpl.java
index b2d638ae2c0ed44e3908d22a8dc5bc9bce9bc13a..ce4c69c04cc12ed03951e23beadc6adbca1c1eac 100644
--- a/service/soap/src/service/BookServiceImpl.java
+++ b/service/soap/src/service/BookServiceImpl.java
@@ -76,7 +76,7 @@ public class BookServiceImpl implements BookService {
     }
 
     @Override
-    public long buyBook(String idBuku, Integer jumlah, String norek) {
+    public long buyBook(String idBuku, Integer jumlah, String norek, String token) {
 
         BookRepository bookRepository = new BookRepository();
         bookRepository.connect();
@@ -92,7 +92,7 @@ public class BookServiceImpl implements BookService {
             return -1;
         }
 
-        if (WebServiceBankApi.transfer(norek, BookServiceImpl.norek_juragan, (float) (jumlah*harga.getHarga()))) {
+        if (WebServiceBankApi.transfer(norek, BookServiceImpl.norek_juragan, (float) (jumlah*harga.getHarga()), token)) {
             Book book = GoogleBooksApi.getBookDetailByID(idBuku);
             DaftarPenjualan penjualan = new DaftarPenjualan(book.getId(), book.getCategory(), jumlah);
             try {
diff --git a/service/soap/src/service/WebServiceBankApi.java b/service/soap/src/service/WebServiceBankApi.java
index 15e40c7bb83d64c30cd943c097b719e2d23b73a3..d747c4743fba5aeabb39e4350153398f3014d494 100644
--- a/service/soap/src/service/WebServiceBankApi.java
+++ b/service/soap/src/service/WebServiceBankApi.java
@@ -12,7 +12,7 @@ import java.net.URL;
 public class WebServiceBankApi {
     private static final String url = "http://localhost:3000/transfer";
 
-    public static Boolean transfer(String pengirim, String penerima, Float jumlah) {
+    public static Boolean transfer(String pengirim, String penerima, Float jumlah, String token) {
         URL object = null;
         try {
             object = new URL(WebServiceBankApi.url);
@@ -39,6 +39,7 @@ public class WebServiceBankApi {
         request.put("no_pengirim", pengirim);
         request.put("no_penerima", penerima);
         request.put("jumlah", jumlah);
+        request.put("token", token);
 
         OutputStreamWriter wr = null;
         try {
@@ -57,8 +58,7 @@ public class WebServiceBankApi {
         }
         if (HttpResult == HttpURLConnection.HTTP_OK) {
             return true;
-        }
-        else {
+        } else {
             return false;
         }
 
diff --git a/vendor/autoload.php b/vendor/autoload.php
new file mode 100644
index 0000000000000000000000000000000000000000..46ffb4ce11adee005348f0eb20d6189078f6ac8f
--- /dev/null
+++ b/vendor/autoload.php
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c::getLoader();
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..fce8549f0781bafdc7da2301b84d048286757445
--- /dev/null
+++ b/vendor/composer/ClassLoader.php
@@ -0,0 +1,445 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ *     $loader = new \Composer\Autoload\ClassLoader();
+ *
+ *     // register classes with namespaces
+ *     $loader->add('Symfony\Component', __DIR__.'/component');
+ *     $loader->add('Symfony',           __DIR__.'/framework');
+ *
+ *     // activate the autoloader
+ *     $loader->register();
+ *
+ *     // to enable searching the include path (eg. for PEAR packages)
+ *     $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see    http://www.php-fig.org/psr/psr-0/
+ * @see    http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+    // PSR-4
+    private $prefixLengthsPsr4 = array();
+    private $prefixDirsPsr4 = array();
+    private $fallbackDirsPsr4 = array();
+
+    // PSR-0
+    private $prefixesPsr0 = array();
+    private $fallbackDirsPsr0 = array();
+
+    private $useIncludePath = false;
+    private $classMap = array();
+    private $classMapAuthoritative = false;
+    private $missingClasses = array();
+    private $apcuPrefix;
+
+    public function getPrefixes()
+    {
+        if (!empty($this->prefixesPsr0)) {
+            return call_user_func_array('array_merge', $this->prefixesPsr0);
+        }
+
+        return array();
+    }
+
+    public function getPrefixesPsr4()
+    {
+        return $this->prefixDirsPsr4;
+    }
+
+    public function getFallbackDirs()
+    {
+        return $this->fallbackDirsPsr0;
+    }
+
+    public function getFallbackDirsPsr4()
+    {
+        return $this->fallbackDirsPsr4;
+    }
+
+    public function getClassMap()
+    {
+        return $this->classMap;
+    }
+
+    /**
+     * @param array $classMap Class to filename map
+     */
+    public function addClassMap(array $classMap)
+    {
+        if ($this->classMap) {
+            $this->classMap = array_merge($this->classMap, $classMap);
+        } else {
+            $this->classMap = $classMap;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix, either
+     * appending or prepending to the ones previously set for this prefix.
+     *
+     * @param string       $prefix  The prefix
+     * @param array|string $paths   The PSR-0 root directories
+     * @param bool         $prepend Whether to prepend the directories
+     */
+    public function add($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            if ($prepend) {
+                $this->fallbackDirsPsr0 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr0
+                );
+            } else {
+                $this->fallbackDirsPsr0 = array_merge(
+                    $this->fallbackDirsPsr0,
+                    (array) $paths
+                );
+            }
+
+            return;
+        }
+
+        $first = $prefix[0];
+        if (!isset($this->prefixesPsr0[$first][$prefix])) {
+            $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+            return;
+        }
+        if ($prepend) {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixesPsr0[$first][$prefix]
+            );
+        } else {
+            $this->prefixesPsr0[$first][$prefix] = array_merge(
+                $this->prefixesPsr0[$first][$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace, either
+     * appending or prepending to the ones previously set for this namespace.
+     *
+     * @param string       $prefix  The prefix/namespace, with trailing '\\'
+     * @param array|string $paths   The PSR-4 base directories
+     * @param bool         $prepend Whether to prepend the directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function addPsr4($prefix, $paths, $prepend = false)
+    {
+        if (!$prefix) {
+            // Register directories for the root namespace.
+            if ($prepend) {
+                $this->fallbackDirsPsr4 = array_merge(
+                    (array) $paths,
+                    $this->fallbackDirsPsr4
+                );
+            } else {
+                $this->fallbackDirsPsr4 = array_merge(
+                    $this->fallbackDirsPsr4,
+                    (array) $paths
+                );
+            }
+        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+            // Register directories for a new namespace.
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        } elseif ($prepend) {
+            // Prepend directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                (array) $paths,
+                $this->prefixDirsPsr4[$prefix]
+            );
+        } else {
+            // Append directories for an already registered namespace.
+            $this->prefixDirsPsr4[$prefix] = array_merge(
+                $this->prefixDirsPsr4[$prefix],
+                (array) $paths
+            );
+        }
+    }
+
+    /**
+     * Registers a set of PSR-0 directories for a given prefix,
+     * replacing any others previously set for this prefix.
+     *
+     * @param string       $prefix The prefix
+     * @param array|string $paths  The PSR-0 base directories
+     */
+    public function set($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr0 = (array) $paths;
+        } else {
+            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Registers a set of PSR-4 directories for a given namespace,
+     * replacing any others previously set for this namespace.
+     *
+     * @param string       $prefix The prefix/namespace, with trailing '\\'
+     * @param array|string $paths  The PSR-4 base directories
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setPsr4($prefix, $paths)
+    {
+        if (!$prefix) {
+            $this->fallbackDirsPsr4 = (array) $paths;
+        } else {
+            $length = strlen($prefix);
+            if ('\\' !== $prefix[$length - 1]) {
+                throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+            }
+            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+            $this->prefixDirsPsr4[$prefix] = (array) $paths;
+        }
+    }
+
+    /**
+     * Turns on searching the include path for class files.
+     *
+     * @param bool $useIncludePath
+     */
+    public function setUseIncludePath($useIncludePath)
+    {
+        $this->useIncludePath = $useIncludePath;
+    }
+
+    /**
+     * Can be used to check if the autoloader uses the include path to check
+     * for classes.
+     *
+     * @return bool
+     */
+    public function getUseIncludePath()
+    {
+        return $this->useIncludePath;
+    }
+
+    /**
+     * Turns off searching the prefix and fallback directories for classes
+     * that have not been registered with the class map.
+     *
+     * @param bool $classMapAuthoritative
+     */
+    public function setClassMapAuthoritative($classMapAuthoritative)
+    {
+        $this->classMapAuthoritative = $classMapAuthoritative;
+    }
+
+    /**
+     * Should class lookup fail if not found in the current class map?
+     *
+     * @return bool
+     */
+    public function isClassMapAuthoritative()
+    {
+        return $this->classMapAuthoritative;
+    }
+
+    /**
+     * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+     *
+     * @param string|null $apcuPrefix
+     */
+    public function setApcuPrefix($apcuPrefix)
+    {
+        $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+    }
+
+    /**
+     * The APCu prefix in use, or null if APCu caching is not enabled.
+     *
+     * @return string|null
+     */
+    public function getApcuPrefix()
+    {
+        return $this->apcuPrefix;
+    }
+
+    /**
+     * Registers this instance as an autoloader.
+     *
+     * @param bool $prepend Whether to prepend the autoloader or not
+     */
+    public function register($prepend = false)
+    {
+        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+    }
+
+    /**
+     * Unregisters this instance as an autoloader.
+     */
+    public function unregister()
+    {
+        spl_autoload_unregister(array($this, 'loadClass'));
+    }
+
+    /**
+     * Loads the given class or interface.
+     *
+     * @param  string    $class The name of the class
+     * @return bool|null True if loaded, null otherwise
+     */
+    public function loadClass($class)
+    {
+        if ($file = $this->findFile($class)) {
+            includeFile($file);
+
+            return true;
+        }
+    }
+
+    /**
+     * Finds the path to the file where the class is defined.
+     *
+     * @param string $class The name of the class
+     *
+     * @return string|false The path if found, false otherwise
+     */
+    public function findFile($class)
+    {
+        // class map lookup
+        if (isset($this->classMap[$class])) {
+            return $this->classMap[$class];
+        }
+        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+            return false;
+        }
+        if (null !== $this->apcuPrefix) {
+            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+            if ($hit) {
+                return $file;
+            }
+        }
+
+        $file = $this->findFileWithExtension($class, '.php');
+
+        // Search for Hack files if we are running on HHVM
+        if (false === $file && defined('HHVM_VERSION')) {
+            $file = $this->findFileWithExtension($class, '.hh');
+        }
+
+        if (null !== $this->apcuPrefix) {
+            apcu_add($this->apcuPrefix.$class, $file);
+        }
+
+        if (false === $file) {
+            // Remember that this class does not exist.
+            $this->missingClasses[$class] = true;
+        }
+
+        return $file;
+    }
+
+    private function findFileWithExtension($class, $ext)
+    {
+        // PSR-4 lookup
+        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+        $first = $class[0];
+        if (isset($this->prefixLengthsPsr4[$first])) {
+            $subPath = $class;
+            while (false !== $lastPos = strrpos($subPath, '\\')) {
+                $subPath = substr($subPath, 0, $lastPos);
+                $search = $subPath . '\\';
+                if (isset($this->prefixDirsPsr4[$search])) {
+                    $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
+                        if (file_exists($file = $dir . $pathEnd)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-4 fallback dirs
+        foreach ($this->fallbackDirsPsr4 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 lookup
+        if (false !== $pos = strrpos($class, '\\')) {
+            // namespaced class name
+            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+                . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+        } else {
+            // PEAR-like class name
+            $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+        }
+
+        if (isset($this->prefixesPsr0[$first])) {
+            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+                if (0 === strpos($class, $prefix)) {
+                    foreach ($dirs as $dir) {
+                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                            return $file;
+                        }
+                    }
+                }
+            }
+        }
+
+        // PSR-0 fallback dirs
+        foreach ($this->fallbackDirsPsr0 as $dir) {
+            if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+                return $file;
+            }
+        }
+
+        // PSR-0 include paths.
+        if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+            return $file;
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+    include $file;
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..f27399a042d95c4708af3a8c74d35d338763cf8f
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000000000000000000000000000000000000..7a91153b0d8ea10bc693176a81d8a9eb96883a76
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7fc0125dbca56fd7565ad62097672a59473e64e
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
new file mode 100644
index 0000000000000000000000000000000000000000..c57cc2d8fb15c2c170b30ca1e7176b7b427ac591
--- /dev/null
+++ b/vendor/composer/autoload_psr4.php
@@ -0,0 +1,11 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+    'Sonata\\GoogleAuthenticator\\' => array($vendorDir . '/sonata-project/google-authenticator/src'),
+    'Google\\Authenticator\\' => array($vendorDir . '/sonata-project/google-authenticator/src'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000000000000000000000000000000000000..6cd5c1f9cc557f31c8346d95f0dfe9cea48f8d7a
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,52 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c
+{
+    private static $loader;
+
+    public static function loadClassLoader($class)
+    {
+        if ('Composer\Autoload\ClassLoader' === $class) {
+            require __DIR__ . '/ClassLoader.php';
+        }
+    }
+
+    public static function getLoader()
+    {
+        if (null !== self::$loader) {
+            return self::$loader;
+        }
+
+        spl_autoload_register(array('ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c', 'loadClassLoader'), true, true);
+        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+        spl_autoload_unregister(array('ComposerAutoloaderInitae14d3cd7eb3d58ce5dbc9a906ade70c', 'loadClassLoader'));
+
+        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+        if ($useStaticLoader) {
+            require_once __DIR__ . '/autoload_static.php';
+
+            call_user_func(\Composer\Autoload\ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::getInitializer($loader));
+        } else {
+            $map = require __DIR__ . '/autoload_namespaces.php';
+            foreach ($map as $namespace => $path) {
+                $loader->set($namespace, $path);
+            }
+
+            $map = require __DIR__ . '/autoload_psr4.php';
+            foreach ($map as $namespace => $path) {
+                $loader->setPsr4($namespace, $path);
+            }
+
+            $classMap = require __DIR__ . '/autoload_classmap.php';
+            if ($classMap) {
+                $loader->addClassMap($classMap);
+            }
+        }
+
+        $loader->register(true);
+
+        return $loader;
+    }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000000000000000000000000000000000000..20899b7474c2f76f372ce0ab6b481a0fb5ceb782
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,39 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c
+{
+    public static $prefixLengthsPsr4 = array (
+        'S' => 
+        array (
+            'Sonata\\GoogleAuthenticator\\' => 27,
+        ),
+        'G' => 
+        array (
+            'Google\\Authenticator\\' => 21,
+        ),
+    );
+
+    public static $prefixDirsPsr4 = array (
+        'Sonata\\GoogleAuthenticator\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/sonata-project/google-authenticator/src',
+        ),
+        'Google\\Authenticator\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/sonata-project/google-authenticator/src',
+        ),
+    );
+
+    public static function getInitializer(ClassLoader $loader)
+    {
+        return \Closure::bind(function () use ($loader) {
+            $loader->prefixLengthsPsr4 = ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInitae14d3cd7eb3d58ce5dbc9a906ade70c::$prefixDirsPsr4;
+
+        }, null, ClassLoader::class);
+    }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000000000000000000000000000000000000..b6b2711fd134f8028c4db588065cf44c51ab00ff
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,61 @@
+[
+    {
+        "name": "sonata-project/google-authenticator",
+        "version": "2.2.0",
+        "version_normalized": "2.2.0.0",
+        "source": {
+            "type": "git",
+            "url": "https://github.com/sonata-project/GoogleAuthenticator.git",
+            "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762"
+        },
+        "dist": {
+            "type": "zip",
+            "url": "https://api.github.com/repos/sonata-project/GoogleAuthenticator/zipball/feda53899b26af24e3db2fe7a3e5f053ca483762",
+            "reference": "feda53899b26af24e3db2fe7a3e5f053ca483762",
+            "shasum": ""
+        },
+        "require": {
+            "php": "^7.1"
+        },
+        "require-dev": {
+            "symfony/phpunit-bridge": "^4.0"
+        },
+        "time": "2018-07-18T22:08:02+00:00",
+        "type": "library",
+        "extra": {
+            "branch-alias": {
+                "dev-master": "2.x-dev"
+            }
+        },
+        "installation-source": "dist",
+        "autoload": {
+            "psr-4": {
+                "Google\\Authenticator\\": "src/",
+                "Sonata\\GoogleAuthenticator\\": "src/"
+            }
+        },
+        "notification-url": "https://packagist.org/downloads/",
+        "license": [
+            "MIT"
+        ],
+        "authors": [
+            {
+                "name": "Christian Stocker",
+                "email": "me@chregu.tv"
+            },
+            {
+                "name": "Andre DeMarre",
+                "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989"
+            },
+            {
+                "name": "Thomas Rabaix",
+                "email": "thomas.rabaix@gmail.com"
+            }
+        ],
+        "description": "Library to integrate Google Authenticator into a PHP project",
+        "homepage": "https://github.com/sonata-project/GoogleAuthenticator",
+        "keywords": [
+            "google authenticator"
+        ]
+    }
+]
diff --git a/vendor/sonata-project/google-authenticator/LICENSE b/vendor/sonata-project/google-authenticator/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..0135e0b6aebb4addef7246658ac1af1e56ba9b53
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2010 Thomas Rabaix
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/sonata-project/google-authenticator/Makefile b/vendor/sonata-project/google-authenticator/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4ae0c63d810bcc5615b7da192ff0145a5a7e72c8
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/Makefile
@@ -0,0 +1,60 @@
+# DO NOT EDIT THIS FILE!
+#
+# It's auto-generated by sonata-project/dev-kit package.
+
+all:
+	@echo "Please choose a task."
+.PHONY: all
+
+lint: lint-composer lint-yaml lint-composer lint-xml lint-php
+.PHONY: lint
+
+lint-composer:
+	composer validate
+.PHONY: lint-composer
+
+lint-yaml:
+	yaml-lint --ignore-non-yaml-files --quiet --exclude vendor .
+
+.PHONY: lint-yaml
+
+lint-xml:
+	find . \( -name '*.xml' -or -name '*.xliff' \) \
+		-not -path './vendor/*' \
+		-not -path './src/Resources/public/vendor/*' \
+		| while read xmlFile; \
+	do \
+		XMLLINT_INDENT='    ' xmllint --encode UTF-8 --format "$$xmlFile"|diff - "$$xmlFile"; \
+		if [ $$? -ne 0 ] ;then exit 1; fi; \
+	done
+
+.PHONY: lint-xml
+
+lint-php:
+	php-cs-fixer fix --ansi --verbose --diff --dry-run
+.PHONY: lint-php
+
+cs-fix: cs-fix-php cs-fix-xml
+.PHONY: cs-fix
+
+cs-fix-php:
+	php-cs-fixer fix --verbose
+.PHONY: cs-fix-php
+
+cs-fix-xml:
+	find . \( -name '*.xml' -or -name '*.xliff' \) \
+		-not -path './vendor/*' \
+		-not -path './src/Resources/public/vendor/*' \
+		| while read xmlFile; \
+	do \
+		XMLLINT_INDENT='    ' xmllint --encode UTF-8 --format "$$xmlFile" --output "$$xmlFile"; \
+	done
+.PHONY: cs-fix-xml
+
+test:
+	phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml
+.PHONY: test
+
+docs:
+	cd docs && sphinx-build -W -b html -d _build/doctrees . _build/html
+.PHONY: docs
diff --git a/vendor/sonata-project/google-authenticator/composer.json b/vendor/sonata-project/google-authenticator/composer.json
new file mode 100644
index 0000000000000000000000000000000000000000..4fbb160acfb53ae77238e3a9b848ea7c7ae8277c
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/composer.json
@@ -0,0 +1,50 @@
+{
+    "name": "sonata-project/google-authenticator",
+    "type": "library",
+    "description": "Library to integrate Google Authenticator into a PHP project",
+    "keywords": [
+        "google authenticator"
+    ],
+    "homepage": "https://github.com/sonata-project/GoogleAuthenticator",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Thomas Rabaix",
+            "email": "thomas.rabaix@gmail.com"
+        },
+        {
+            "name": "Christian Stocker",
+            "email": "me@chregu.tv"
+        },
+        {
+            "name": "Andre DeMarre",
+            "homepage": "http://www.devnetwork.net/viewtopic.php?f=50&t=94989"
+        }
+    ],
+    "require": {
+        "php": "^7.1"
+    },
+    "require-dev": {
+        "symfony/phpunit-bridge": "^4.0"
+    },
+    "config": {
+        "sort-packages": true
+    },
+    "extra": {
+        "branch-alias": {
+            "dev-master": "2.x-dev"
+        }
+    },
+    "autoload": {
+        "psr-4": {
+            "Google\\Authenticator\\": "src/",
+            "Sonata\\GoogleAuthenticator\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Google\\Authenticator\\Tests\\": "tests/",
+            "Sonata\\GoogleAuthenticator\\Tests\\": "tests/"
+        }
+    }
+}
diff --git a/vendor/sonata-project/google-authenticator/phpunit.xml.dist b/vendor/sonata-project/google-authenticator/phpunit.xml.dist
new file mode 100644
index 0000000000000000000000000000000000000000..492b036db0eb7cfd428de82bdf6e1303b56eefec
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/phpunit.xml.dist
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+DO NOT EDIT THIS FILE!
+
+It's auto-generated by sonata-project/dev-kit package.
+-->
+
+<phpunit backupGlobals="false"
+         backupStaticAttributes="false"
+         colors="true"
+         convertErrorsToExceptions="true"
+         convertNoticesToExceptions="true"
+         convertWarningsToExceptions="true"
+         processIsolation="false"
+         stopOnFailure="false"
+         syntaxCheck="false"
+         bootstrap="tests/bootstrap.php"
+>
+    <testsuites>
+        <testsuite name="Sonata Google Authenticator Test Suite">
+            <directory suffix="Test.php">./tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <listeners>
+       <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
+    </listeners>
+
+    <filter>
+        <whitelist>
+            <directory suffix=".php">./src/</directory>
+        </whitelist>
+    </filter>
+
+    <php>
+        <ini name="precision" value="8"/>
+    </php>
+
+</phpunit>
diff --git a/vendor/sonata-project/google-authenticator/sample/example.php b/vendor/sonata-project/google-authenticator/sample/example.php
new file mode 100644
index 0000000000000000000000000000000000000000..50366d2fa9c470a34eb2474568393a4642a0ad78
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/example.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+include_once __DIR__.'/../src/FixedBitNotation.php';
+include_once __DIR__.'/../src/GoogleAuthenticator.php';
+include_once __DIR__.'/../src/GoogleQrUrl.php';
+
+$secret = 'XVQ2UIGO75XRUKJO';
+$code = '846474';
+
+$g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
+
+echo 'Current Code is: ';
+echo $g->getCode($secret);
+
+echo "\n";
+
+echo "Check if $code is valid: ";
+
+if ($g->checkCode($secret, $code)) {
+    echo "YES \n";
+} else {
+    echo "NO \n";
+}
+
+$secret = $g->generateSecret();
+echo "Get a new Secret: $secret \n";
+echo "The QR Code for this secret (to scan with the Google Authenticator App: \n";
+
+echo \Sonata\GoogleAuthenticator\GoogleQrUrl::generate('chregu', $secret, 'GoogleAuthenticatorExample');
+echo "\n";
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php b/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php
new file mode 100644
index 0000000000000000000000000000000000000000..f3e06d477e22f323426f97ae46c67be8b7a2efba
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/ask-for-otp.php
@@ -0,0 +1,23 @@
+
+<h1>please otp</h1>
+<p>
+<form method="post" action="./">
+<?php if ($debug) {
+    ?>
+    <br/>
+    (Set $debug in index.php to false, if you don't want to have the OTP prefilled (for real life application, for example ;))<br/>
+<?php
+}
+?>
+
+otp: <input name="otp"
+value="<?php
+if ($debug) {
+    $g = new GoogleAuthenticator();
+    echo $g->getCode($user->getSecret());
+}
+?>"/><br/>
+<input type="checkbox" name="remember" id="remember" /><label for="remember"> Remember verification for this computer for 1 day.</label> <br/>
+<input type="submit"/>
+
+</form>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php b/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a19032ca4f1527b6e49c0128c4103799746e547
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/loggedin.php
@@ -0,0 +1,19 @@
+
+<p>
+Hello <?php echo $user->getUsername(); ?>
+</p>
+<?php
+if (!isset($_GET['showqr'])) {
+    ?>
+
+<p>
+<a href="?showqr=1">Show QR Code</a>
+</p>
+
+<?php
+}
+?>
+
+<p>
+<a href="?logout=1">Logout</a>
+</p>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php b/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d23fd33c7bd09a636c365d3c56e91fa70f08c59
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/login-error.php
@@ -0,0 +1,6 @@
+<p>
+Wrong username or password or token.
+</p>
+<p>
+<a href="./">try again</a>
+</p>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/login.php b/vendor/sonata-project/google-authenticator/sample/tmpl/login.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd81623009b4b8620b6d3c8e21111c93df2c182c
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/login.php
@@ -0,0 +1,8 @@
+
+<h1>please login</h1>
+<p>
+<form method="post" action="./">
+username: <input name="username"/><br/>
+password: <input name="password" type="password"/><br/>
+<input type="submit"/>
+</form>
diff --git a/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php b/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php
new file mode 100644
index 0000000000000000000000000000000000000000..774a298deed8458831ed231cb0680441223a4d0a
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/tmpl/show-qr.php
@@ -0,0 +1,11 @@
+<h1>Please scan this </h1>
+
+<p> with <a href="http://www.google.com/support/a/bin/answer.py?hl=en&answer=1037451">the Google Authenticator App</a></p>
+
+<p>
+<?php
+$link = \Sonata\GoogleAuthenticator\GoogleQrUrl::generate($user->getUsername(), $secret, 'GoogleAuthenticatorExample');
+?>
+
+<a  href="<?php echo $link; ?>"><img style="border: 0; padding:10px" src="<?php echo $link; ?>"/></a>
+</p>
diff --git a/vendor/sonata-project/google-authenticator/sample/users.dat b/vendor/sonata-project/google-authenticator/sample/users.dat
new file mode 100644
index 0000000000000000000000000000000000000000..fdcc130c43b31f2d7b337b977705a164826b9688
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/users.dat
@@ -0,0 +1 @@
+{"chregu":{"password":"foobar"}}
\ No newline at end of file
diff --git a/vendor/sonata-project/google-authenticator/sample/web/Users.php b/vendor/sonata-project/google-authenticator/sample/web/Users.php
new file mode 100644
index 0000000000000000000000000000000000000000..fb169def9245eae78279bf2c6a7357c6fc5e71a8
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/web/Users.php
@@ -0,0 +1,155 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+class Users
+{
+    public function __construct(string $file = '../users.dat')
+    {
+        $this->userFile = $file;
+
+        $this->users = json_decode(file_get_contents($file), true);
+    }
+
+    public function hasSession()
+    {
+        session_start();
+        if (isset($_SESSION['username'])) {
+            return $_SESSION['username'];
+        }
+
+        return false;
+    }
+
+    public function storeData(User $user): void
+    {
+        $this->users[$user->getUsername()] = $user->getData();
+        file_put_contents($this->userFile, json_encode($this->users));
+    }
+
+    public function loadUser($name)
+    {
+        if (isset($this->users[$name])) {
+            return new User($name, $this->users[$name]);
+        }
+
+        return false;
+    }
+}
+
+class User
+{
+    public function __construct($user, $data)
+    {
+        $this->data = $data;
+        $this->user = $user;
+    }
+
+    public function auth($pass)
+    {
+        if ($this->data['password'] === $pass) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public function startSession(): void
+    {
+        $_SESSION['username'] = $this->user;
+    }
+
+    public function doLogin(): void
+    {
+        session_regenerate_id();
+        $_SESSION['loggedin'] = true;
+        $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT'];
+    }
+
+    public function doOTP(): void
+    {
+        $_SESSION['OTP'] = true;
+    }
+
+    public function isOTP()
+    {
+        if (isset($_SESSION['OTP']) && true == $_SESSION['OTP']) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public function isLoggedIn()
+    {
+        if (isset($_SESSION['loggedin']) && true == $_SESSION['loggedin'] &&
+            isset($_SESSION['ua']) && $_SESSION['ua'] == $_SERVER['HTTP_USER_AGENT']
+        ) {
+            return $_SESSION['username'];
+        }
+
+        return false;
+    }
+
+    public function getUsername()
+    {
+        return $this->user;
+    }
+
+    public function getSecret()
+    {
+        if (isset($this->data['secret'])) {
+            return $this->data['secret'];
+        }
+
+        return false;
+    }
+
+    public function generateSecret()
+    {
+        $g = new \Sonata\GoogleAuthenticator\GoogleAuthenticator();
+        $secret = $g->generateSecret();
+        $this->data['secret'] = $secret;
+
+        return $secret;
+    }
+
+    public function getData()
+    {
+        return $this->data;
+    }
+
+    public function setOTPCookie(): void
+    {
+        $time = floor(time() / (3600 * 24)); // get day number
+        //about using the user agent: It's easy to fake it, but it increases the barrier for stealing and reusing cookies nevertheless
+        // and it doesn't do any harm (except that it's invalid after a browser upgrade, but that may be even intented)
+        $cookie = $time.':'.hash_hmac('sha1', $this->getUsername().':'.$time.':'.$_SERVER['HTTP_USER_AGENT'], $this->getSecret());
+        setcookie('otp', $cookie, time() + (30 * 24 * 3600), null, null, null, true);
+    }
+
+    public function hasValidOTPCookie()
+    {
+        // 0 = tomorrow it is invalid
+        $daysUntilInvalid = 0;
+        $time = (string) floor((time() / (3600 * 24))); // get day number
+        if (isset($_COOKIE['otp'])) {
+            list($otpday, $hash) = explode(':', $_COOKIE['otp']);
+
+            if ($otpday >= $time - $daysUntilInvalid && $hash == hash_hmac('sha1', $this->getUsername().':'.$otpday.':'.$_SERVER['HTTP_USER_AGENT'], $this->getSecret())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/vendor/sonata-project/google-authenticator/sample/web/index.php b/vendor/sonata-project/google-authenticator/sample/web/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..c039674c53f53691340e7c3a6f4bb1c236d94335
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/sample/web/index.php
@@ -0,0 +1,119 @@
+<?php declare(strict_types=1);
+ob_start(); //i'm too lazy to check when is sent what ;)
+//set session cookie to be read only via http and not by JavaScript
+ini_set('session.cookie_httponly', '1');
+
+include_once __DIR__.'/../../src/GoogleAuthenticator.php';
+include_once __DIR__.'/../../src/GoogleQrUrl.php';
+include_once __DIR__.'/../../src/FixedBitNotation.php';
+include_once 'Users.php';
+
+?>
+<!DOCTYPE HTML>
+<html>
+<head>
+    <title>Google Authenticator in PHP demo</title>
+</head>
+<body>
+<?php
+
+//set this to false, if you don't want the token prefilled
+$debug = true;
+
+$users = new Users();
+//check if the user has a session, if not, show the login screen
+if ($username = $users->hasSession()) {
+    //load the user data from the json storage.
+    $user = $users->loadUser($username);
+    //if he clicked logout, destroy the session and redirect to the startscreen.
+    if (isset($_GET['logout'])) {
+        session_destroy();
+        header('Location: ./');
+    }
+    // check if the user is logged in.
+    if ($user->isLoggedIn()) {
+        include __DIR__.'/../tmpl/loggedin.php';
+        //show the QR code if whished so
+        if (isset($_GET['showqr'])) {
+            $secret = $user->getSecret();
+            include __DIR__.'/../tmpl/show-qr.php';
+        }
+    }
+    //if the user is in the OTP phase and submit the OTP.
+    else {
+        if ($user->isOTP() && isset($_POST['otp'])) {
+            $g = new \Google\Authenticator\GoogleAuthenticator();
+            // check if the submitted token is the right one and log in
+            if ($g->checkCode($user->getSecret(), $_POST['otp'])) {
+                // do log-in the user
+                $user->doLogin();
+                //if the user clicked the "remember the token" checkbox, set the cookie
+                if (isset($_POST['remember']) && $_POST['remember']) {
+                    $user->setOTPCookie();
+                }
+                include __DIR__.'/../tmpl/loggedin.php';
+            }
+            //if the OTP is wrong, destroy the session and tell the user to try again
+            else {
+                session_destroy();
+                include __DIR__.'/../tmpl/login-error.php';
+            }
+        }
+        // if the user is neither logged in nor in the OTP phase, show the login form
+        else {
+            session_destroy();
+            include __DIR__.'/../tmpl/login.php';
+        }
+    }
+    die();
+}
+    //if the username is set in _POST, then we assume the user filled in the login form.
+
+    if (isset($_POST['username'])) {
+        // check if we can load the user (ie. the user exists in our db)
+        $user = $users->loadUser($_POST['username']);
+        if ($user) {
+            //try to authenticate the password and start the session if it's correct.
+            if ($user->auth($_POST['password'])) {
+                $user->startSession();
+                //check if the user has a valid OTP cookie, so we don't have to
+                // ask for the current token and can directly log in
+                if ($user->hasValidOTPCookie()) {
+                    include __DIR__.'/../tmpl/loggedin.php';
+                    $user->doLogin();
+                }
+                // try to get the users' secret from the db,
+                //  if he doesn't have one, generate one, store it and show it.
+                else {
+                    if (!$user->getSecret()) {
+                        include __DIR__.'/../tmpl/loggedin.php';
+
+                        $secret = $user->generateSecret();
+                        $users->storeData($user);
+                        $user->doLogin();
+                        include __DIR__.'/../tmpl/show-qr.php';
+                    }
+                    // if the user neither has a valid OTP cookie nor it's the first login
+                    //  ask for the OTP
+                    else {
+                        $user->doOTP();
+                        include __DIR__.'/../tmpl/ask-for-otp.php';
+                    }
+                }
+
+                die();
+            }
+        }
+        // if we're here, something went wrong, destroy the session and show a login error
+        session_destroy();
+
+        include __DIR__.'/../tmpl/login-error.php';
+        die();
+    }
+
+// if neither a session nor tried to submit the login credentials -> login screen
+include __DIR__.'/../tmpl/login.php';
+
+?>
+</body>
+</html>
diff --git a/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php
new file mode 100644
index 0000000000000000000000000000000000000000..3f5ff558b05e8a721dc4589796154bcd2d65d574
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/FixedBitNotation.php
@@ -0,0 +1,296 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\GoogleAuthenticator;
+
+/**
+ * FixedBitNotation.
+ *
+ * The FixedBitNotation class is for binary to text conversion. It
+ * can handle many encoding schemes, formally defined or not, that
+ * use a fixed number of bits to encode each character.
+ *
+ * @author Andre DeMarre
+ */
+final class FixedBitNotation
+{
+    /**
+     * @var string
+     */
+    private $chars;
+
+    /**
+     * @var int
+     */
+    private $bitsPerCharacter;
+
+    /**
+     * @var int
+     */
+    private $radix;
+
+    /**
+     * @var bool
+     */
+    private $rightPadFinalBits;
+
+    /**
+     * @var bool
+     */
+    private $padFinalGroup;
+
+    /**
+     * @var string
+     */
+    private $padCharacter;
+
+    /**
+     * @var string[]
+     */
+    private $charmap;
+
+    /**
+     * @param int    $bitsPerCharacter  Bits to use for each encoded character
+     * @param string $chars             Base character alphabet
+     * @param bool   $rightPadFinalBits How to encode last character
+     * @param bool   $padFinalGroup     Add padding to end of encoded output
+     * @param string $padCharacter      Character to use for padding
+     */
+    public function __construct(int $bitsPerCharacter, string $chars = null, bool $rightPadFinalBits = false, bool $padFinalGroup = false, string $padCharacter = '=')
+    {
+        // Ensure validity of $chars
+        if (!is_string($chars) || ($charLength = strlen($chars)) < 2) {
+            $chars =
+            '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,';
+            $charLength = 64;
+        }
+
+        // Ensure validity of $bitsPerCharacter
+        if ($bitsPerCharacter < 1) {
+            // $bitsPerCharacter must be at least 1
+            $bitsPerCharacter = 1;
+            $radix = 2;
+        } elseif ($charLength < 1 << $bitsPerCharacter) {
+            // Character length of $chars is too small for $bitsPerCharacter
+            // Set $bitsPerCharacter to greatest acceptable value
+            $bitsPerCharacter = 1;
+            $radix = 2;
+
+            while ($charLength >= ($radix <<= 1) && $bitsPerCharacter < 8) {
+                ++$bitsPerCharacter;
+            }
+
+            $radix >>= 1;
+        } elseif ($bitsPerCharacter > 8) {
+            // $bitsPerCharacter must not be greater than 8
+            $bitsPerCharacter = 8;
+            $radix = 256;
+        } else {
+            $radix = 1 << $bitsPerCharacter;
+        }
+
+        $this->chars = $chars;
+        $this->bitsPerCharacter = $bitsPerCharacter;
+        $this->radix = $radix;
+        $this->rightPadFinalBits = $rightPadFinalBits;
+        $this->padFinalGroup = $padFinalGroup;
+        $this->padCharacter = $padCharacter[0];
+    }
+
+    /**
+     * Encode a string.
+     *
+     * @param string $rawString Binary data to encode
+     *
+     * @return string
+     */
+    public function encode($rawString): string
+    {
+        // Unpack string into an array of bytes
+        $bytes = unpack('C*', $rawString);
+        $byteCount = count($bytes);
+
+        $encodedString = '';
+        $byte = array_shift($bytes);
+        $bitsRead = 0;
+
+        $chars = $this->chars;
+        $bitsPerCharacter = $this->bitsPerCharacter;
+        $rightPadFinalBits = $this->rightPadFinalBits;
+        $padFinalGroup = $this->padFinalGroup;
+        $padCharacter = $this->padCharacter;
+
+        // Generate encoded output;
+        // each loop produces one encoded character
+        for ($c = 0; $c < $byteCount * 8 / $bitsPerCharacter; ++$c) {
+            // Get the bits needed for this encoded character
+            if ($bitsRead + $bitsPerCharacter > 8) {
+                // Not enough bits remain in this byte for the current
+                // character
+                // Save the remaining bits before getting the next byte
+                $oldBitCount = 8 - $bitsRead;
+                $oldBits = $byte ^ ($byte >> $oldBitCount << $oldBitCount);
+                $newBitCount = $bitsPerCharacter - $oldBitCount;
+
+                if (!$bytes) {
+                    // Last bits; match final character and exit loop
+                    if ($rightPadFinalBits) {
+                        $oldBits <<= $newBitCount;
+                    }
+                    $encodedString .= $chars[$oldBits];
+
+                    if ($padFinalGroup) {
+                        // Array of the lowest common multiples of
+                        // $bitsPerCharacter and 8, divided by 8
+                        $lcmMap = [1 => 1, 2 => 1, 3 => 3, 4 => 1, 5 => 5, 6 => 3, 7 => 7, 8 => 1];
+                        $bytesPerGroup = $lcmMap[$bitsPerCharacter];
+                        $pads = $bytesPerGroup * 8 / $bitsPerCharacter
+                        - ceil((strlen($rawString) % $bytesPerGroup)
+                        * 8 / $bitsPerCharacter);
+                        $encodedString .= str_repeat($padCharacter[0], $pads);
+                    }
+
+                    break;
+                }
+
+                // Get next byte
+                $byte = array_shift($bytes);
+                $bitsRead = 0;
+            } else {
+                $oldBitCount = 0;
+                $newBitCount = $bitsPerCharacter;
+            }
+
+            // Read only the needed bits from this byte
+            $bits = $byte >> 8 - ($bitsRead + $newBitCount);
+            $bits ^= $bits >> $newBitCount << $newBitCount;
+            $bitsRead += $newBitCount;
+
+            if ($oldBitCount) {
+                // Bits come from seperate bytes, add $oldBits to $bits
+                $bits = ($oldBits << $newBitCount) | $bits;
+            }
+
+            $encodedString .= $chars[$bits];
+        }
+
+        return $encodedString;
+    }
+
+    /**
+     * Decode a string.
+     *
+     * @param string $encodedString Data to decode
+     * @param bool   $caseSensitive
+     * @param bool   $strict        Returns null if $encodedString contains
+     *                              an undecodable character
+     *
+     * @return string
+     */
+    public function decode($encodedString, $caseSensitive = true, $strict = false): string
+    {
+        if (!$encodedString || !is_string($encodedString)) {
+            // Empty string, nothing to decode
+            return '';
+        }
+
+        $chars = $this->chars;
+        $bitsPerCharacter = $this->bitsPerCharacter;
+        $radix = $this->radix;
+        $rightPadFinalBits = $this->rightPadFinalBits;
+        $padCharacter = $this->padCharacter;
+
+        // Get index of encoded characters
+        if ($this->charmap) {
+            $charmap = $this->charmap;
+        } else {
+            $charmap = [];
+
+            for ($i = 0; $i < $radix; ++$i) {
+                $charmap[$chars[$i]] = $i;
+            }
+
+            $this->charmap = $charmap;
+        }
+
+        // The last encoded character is $encodedString[$lastNotatedIndex]
+        $lastNotatedIndex = strlen($encodedString) - 1;
+
+        // Remove trailing padding characters
+        while ($encodedString[$lastNotatedIndex] == $padCharacter[0]) {
+            $encodedString = substr($encodedString, 0, $lastNotatedIndex);
+            --$lastNotatedIndex;
+        }
+
+        $rawString = '';
+        $byte = 0;
+        $bitsWritten = 0;
+
+        // Convert each encoded character to a series of unencoded bits
+        for ($c = 0; $c <= $lastNotatedIndex; ++$c) {
+            if (!isset($charmap[$encodedString[$c]]) && !$caseSensitive) {
+                // Encoded character was not found; try other case
+                if (isset($charmap[$cUpper = strtoupper($encodedString[$c])])) {
+                    $charmap[$encodedString[$c]] = $charmap[$cUpper];
+                } elseif (isset($charmap[$cLower = strtolower($encodedString[$c])])) {
+                    $charmap[$encodedString[$c]] = $charmap[$cLower];
+                }
+            }
+
+            if (isset($charmap[$encodedString[$c]])) {
+                $bitsNeeded = 8 - $bitsWritten;
+                $unusedBitCount = $bitsPerCharacter - $bitsNeeded;
+
+                // Get the new bits ready
+                if ($bitsNeeded > $bitsPerCharacter) {
+                    // New bits aren't enough to complete a byte; shift them
+                    // left into position
+                    $newBits = $charmap[$encodedString[$c]] << $bitsNeeded
+                    - $bitsPerCharacter;
+                    $bitsWritten += $bitsPerCharacter;
+                } elseif ($c != $lastNotatedIndex || $rightPadFinalBits) {
+                    // Zero or more too many bits to complete a byte;
+                    // shift right
+                    $newBits = $charmap[$encodedString[$c]] >> $unusedBitCount;
+                    $bitsWritten = 8; //$bitsWritten += $bitsNeeded;
+                } else {
+                    // Final bits don't need to be shifted
+                    $newBits = $charmap[$encodedString[$c]];
+                    $bitsWritten = 8;
+                }
+
+                $byte |= $newBits;
+
+                if (8 == $bitsWritten || $c == $lastNotatedIndex) {
+                    // Byte is ready to be written
+                    $rawString .= pack('C', $byte);
+
+                    if ($c != $lastNotatedIndex) {
+                        // Start the next byte
+                        $bitsWritten = $unusedBitCount;
+                        $byte = ($charmap[$encodedString[$c]]
+                        ^ ($newBits << $unusedBitCount)) << 8 - $bitsWritten;
+                    }
+                }
+            } elseif ($strict) {
+                // Unable to decode character; abort
+                return null;
+            }
+        }
+
+        return $rawString;
+    }
+}
+
+// NEXT_MAJOR: Remove class alias
+class_alias('Sonata\GoogleAuthenticator\FixedBitNotation', 'Google\Authenticator\FixedBitNotation', false);
diff --git a/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php
new file mode 100644
index 0000000000000000000000000000000000000000..145739c54bc369c59d8863649968cb5c980b3230
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticator.php
@@ -0,0 +1,173 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\GoogleAuthenticator;
+
+/**
+ * @see https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+ */
+final class GoogleAuthenticator implements GoogleAuthenticatorInterface
+{
+    /**
+     * @var int
+     */
+    private $passCodeLength;
+
+    /**
+     * @var int
+     */
+    private $secretLength;
+
+    /**
+     * @var int
+     */
+    private $pinModulo;
+
+    /**
+     * @var \DateTimeInterface
+     */
+    private $now;
+
+    /**
+     * @var int
+     */
+    private $codePeriod = 30;
+
+    /**
+     * @param int                     $passCodeLength
+     * @param int                     $secretLength
+     * @param \DateTimeInterface|null $now
+     */
+    public function __construct(int $passCodeLength = 6, int $secretLength = 10, \DateTimeInterface $now = null)
+    {
+        $this->passCodeLength = $passCodeLength;
+        $this->secretLength = $secretLength;
+        $this->pinModulo = 10 ** $passCodeLength;
+        $this->now = $now ?? new \DateTimeImmutable();
+    }
+
+    /**
+     * @param string $secret
+     * @param string $code
+     */
+    public function checkCode($secret, $code): bool
+    {
+        /**
+         * The result of each comparison is accumulated here instead of using a guard clause
+         * (https://refactoring.com/catalog/replaceNestedConditionalWithGuardClauses.html). This is to implement
+         * constant time comparison to make side-channel attacks harder. See
+         * https://cryptocoding.net/index.php/Coding_rules#Compare_secret_strings_in_constant_time for details.
+         * Each comparison uses hash_equals() instead of an operator to implement constant time equality comparison
+         * for each code.
+         */
+        $result = 0;
+
+        // current period
+        $result += hash_equals($this->getCode($secret, $this->now), $code);
+
+        // previous period, happens if the user was slow to enter or it just crossed over
+        $dateTime = new \DateTimeImmutable('@'.($this->now->getTimestamp() - $this->codePeriod));
+        $result += hash_equals($this->getCode($secret, $dateTime), $code);
+
+        // next period, happens if the user is not completely synced and possibly a few seconds ahead
+        $dateTime = new \DateTimeImmutable('@'.($this->now->getTimestamp() + $this->codePeriod));
+        $result += hash_equals($this->getCode($secret, $dateTime), $code);
+
+        return $result > 0;
+    }
+
+    /**
+     * NEXT_MAJOR: add the interface typehint to $time and remove deprecation.
+     *
+     * @param string                                   $secret
+     * @param float|string|int|null|\DateTimeInterface $time
+     */
+    public function getCode($secret, /* \DateTimeInterface */$time = null): string
+    {
+        if (null === $time) {
+            $time = $this->now;
+        }
+
+        if ($time instanceof \DateTimeInterface) {
+            $timeForCode = floor($time->getTimestamp() / $this->codePeriod);
+        } else {
+            @trigger_error(
+                'Passing anything other than null or a DateTimeInterface to $time is deprecated as of 2.0 '.
+                'and will not be possible as of 3.0.',
+                E_USER_DEPRECATED
+            );
+            $timeForCode = $time;
+        }
+
+        $base32 = new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', true, true);
+        $secret = $base32->decode($secret);
+
+        $timeForCode = str_pad(pack('N', $timeForCode), 8, chr(0), STR_PAD_LEFT);
+
+        $hash = hash_hmac('sha1', $timeForCode, $secret, true);
+        $offset = ord(substr($hash, -1));
+        $offset &= 0xF;
+
+        $truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF;
+
+        return str_pad((string) ($truncatedHash % $this->pinModulo), $this->passCodeLength, '0', STR_PAD_LEFT);
+    }
+
+    /**
+     * NEXT_MAJOR: Remove this method.
+     *
+     * @param string $user
+     * @param string $hostname
+     * @param string $secret
+     *
+     * @deprecated deprecated as of 2.1 and will be removed in 3.0. Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.
+     */
+    public function getUrl($user, $hostname, $secret): string
+    {
+        @trigger_error(sprintf(
+            'Using %s() is deprecated as of 2.1 and will be removed in 3.0. '.
+            'Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.',
+            __METHOD__
+        ), E_USER_DEPRECATED);
+
+        $issuer = func_get_args()[3] ?? null;
+        $accountName = sprintf('%s@%s', $user, $hostname);
+
+        // manually concat the issuer to avoid a change in URL
+        $url = GoogleQrUrl::generate($accountName, $secret);
+
+        if ($issuer) {
+            $url .= '%26issuer%3D'.$issuer;
+        }
+
+        return $url;
+    }
+
+    public function generateSecret(): string
+    {
+        return (new FixedBitNotation(5, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', true, true))
+            ->encode(random_bytes($this->secretLength));
+    }
+
+    /**
+     * @param string $bytes
+     * @param int    $start
+     */
+    private function hashToInt(string $bytes, int $start): int
+    {
+        return unpack('N', substr(substr($bytes, $start), 0, 4))[1];
+    }
+}
+
+// NEXT_MAJOR: Remove class alias
+class_alias('Sonata\GoogleAuthenticator\GoogleAuthenticator', 'Google\Authenticator\GoogleAuthenticator', false);
diff --git a/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..70219f2090524c8549410973634cc5d7c06fe05a
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/GoogleAuthenticatorInterface.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\GoogleAuthenticator;
+
+interface GoogleAuthenticatorInterface
+{
+    /**
+     * @param string $secret
+     * @param string $code
+     */
+    public function checkCode($secret, $code): bool;
+
+    /**
+     * NEXT_MAJOR: add the interface typehint to $time and remove deprecation.
+     *
+     * @param string                                   $secret
+     * @param float|string|int|null|\DateTimeInterface $time
+     */
+    public function getCode($secret, /* \DateTimeInterface */$time = null): string;
+
+    /**
+     * NEXT_MAJOR: Remove this method.
+     *
+     * @param string $user
+     * @param string $hostname
+     * @param string $secret
+     *
+     * @deprecated deprecated as of 2.1 and will be removed in 3.0. Use Sonata\GoogleAuthenticator\GoogleQrUrl::generate() instead.
+     */
+    public function getUrl($user, $hostname, $secret): string;
+
+    public function generateSecret(): string;
+}
diff --git a/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e9a537a4aa63f764233da690e2dd40e14851ef9
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/GoogleQrUrl.php
@@ -0,0 +1,94 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\GoogleAuthenticator;
+
+/**
+ * Responsible for QR image url generation.
+ *
+ * @see https://developers.google.com/chart/infographics/docs/qr_codes
+ * @see https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+ *
+ * @author Iltar van der Berg <kjarli@gmail.com>
+ */
+final class GoogleQrUrl
+{
+    /**
+     * Private by design.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * Generates a URL that is used to show a QR code.
+     *
+     * Account names may not contain a double colon (:). Valid account name
+     * examples:
+     *  - "John.Doe@gmail.com"
+     *  - "John Doe"
+     *  - "John_Doe_976"
+     *
+     * The Issuer may not contain a double colon (:). The issuer is recommended
+     * to pass along. If used, it will also be appended before the accountName.
+     *
+     * The previous examples with the issuer "Acme inc" would result in label:
+     *  - "Acme inc:John.Doe@gmail.com"
+     *  - "Acme inc:John Doe"
+     *  - "Acme inc:John_Doe_976"
+     *
+     * The contents of the label, issuer and secret will be encoded to generate
+     * a valid URL.
+     *
+     * @param string      $accountName The account name to show and identify
+     * @param string      $secret      The secret is the generated secret unique to that user
+     * @param string|null $issuer      Where you log in to
+     * @param int         $size        Image size in pixels, 200 will make it 200x200
+     *
+     * @return string
+     */
+    public static function generate(string $accountName, string $secret, string $issuer = null, int $size = 200): string
+    {
+        if ('' === $accountName || false !== strpos($accountName, ':')) {
+            throw RuntimeException::InvalidAccountName($accountName);
+        }
+
+        if ('' === $secret) {
+            throw RuntimeException::InvalidSecret();
+        }
+
+        $label = $accountName;
+        $otpauthString = 'otpauth://totp/%s?secret=%s';
+
+        if (null !== $issuer) {
+            if ('' === $issuer || false !== strpos($issuer, ':')) {
+                throw RuntimeException::InvalidIssuer($issuer);
+            }
+
+            // use both the issuer parameter and label prefix as recommended by Google for BC reasons
+            $label = $issuer.':'.$label;
+            $otpauthString .= '&issuer=%s';
+        }
+
+        $otpauthString = rawurlencode(sprintf($otpauthString, $label, $secret, $issuer));
+
+        return sprintf(
+            'https://chart.googleapis.com/chart?chs=%1$dx%1$d&chld=M|0&cht=qr&chl=%2$s',
+            $size,
+            $otpauthString
+        );
+    }
+}
+
+// NEXT_MAJOR: Remove class alias
+class_alias('Sonata\GoogleAuthenticator\GoogleQrUrl', 'Google\Authenticator\GoogleQrUrl', false);
diff --git a/vendor/sonata-project/google-authenticator/src/RuntimeException.php b/vendor/sonata-project/google-authenticator/src/RuntimeException.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d3cf5f63f2d3137dd8e9bc7095e8410ff49414d
--- /dev/null
+++ b/vendor/sonata-project/google-authenticator/src/RuntimeException.php
@@ -0,0 +1,46 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the Sonata Project package.
+ *
+ * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\GoogleAuthenticator;
+
+/**
+ * Contains runtime exception templates.
+ *
+ * @author Iltar van der Berg <kjarli@gmail.com>
+ */
+final class RuntimeException extends \RuntimeException
+{
+    public static function InvalidAccountName(string $accountName): self
+    {
+        return new self(sprintf(
+            'The account name may not contain a double colon (:) and may not be an empty string. Given "%s".',
+            $accountName
+        ));
+    }
+
+    public static function InvalidIssuer(string $issuer): self
+    {
+        return new self(sprintf(
+            'The issuer name may not contain a double colon (:) and may not be an empty string. Given "%s".',
+            $issuer
+        ));
+    }
+
+    public static function InvalidSecret(): self
+    {
+        return new self('The secret name may not be an empty string.');
+    }
+}
+
+// NEXT_MAJOR: Remove class alias
+class_alias('Sonata\GoogleAuthenticator\RuntimeException', 'Google\Authenticator\RuntimeException', false);