├── .gitignore ├── public ├── images │ ├── .DS_Store │ ├── getcontact.webp │ ├── screenshot.png │ ├── screenshot2.png │ ├── screenshot3.png │ └── obtain_fk_and_token.png ├── js │ ├── custom.js │ └── qrcode.min.js └── css │ └── custom.css ├── src ├── configurations │ ├── database.php │ ├── site.php │ └── getcontact.php ├── pages │ └── dashboard │ │ ├── logout.php │ │ ├── login.php │ │ ├── manage_credentials.php │ │ └── verify_captcha.php ├── libraries │ ├── database.php │ ├── helper.php │ └── getcontact.php └── apis │ ├── credentials.php │ ├── subscription.php │ ├── refresh_captcha.php │ ├── verify_captcha.php │ └── generate_credentials.php ├── .htaccess ├── LICENSE ├── index.php ├── sql └── db.sql └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | *.log 3 | **/*.log -------------------------------------------------------------------------------- /public/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naufalist/getcontact-web/HEAD/public/images/.DS_Store -------------------------------------------------------------------------------- /public/images/getcontact.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naufalist/getcontact-web/HEAD/public/images/getcontact.webp -------------------------------------------------------------------------------- /public/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naufalist/getcontact-web/HEAD/public/images/screenshot.png -------------------------------------------------------------------------------- /public/images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naufalist/getcontact-web/HEAD/public/images/screenshot2.png -------------------------------------------------------------------------------- /public/images/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naufalist/getcontact-web/HEAD/public/images/screenshot3.png -------------------------------------------------------------------------------- /public/images/obtain_fk_and_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naufalist/getcontact-web/HEAD/public/images/obtain_fk_and_token.png -------------------------------------------------------------------------------- /src/configurations/database.php: -------------------------------------------------------------------------------- 1 | PDO::ERRMODE_EXCEPTION, 14 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, 15 | ]; 16 | 17 | $pdo = new PDO($dsn, DATABASE_USER, DATABASE_PASS, $options); 18 | } catch (\PDOException $e) { 19 | error_log($e->getMessage()); 20 | die("Database connection failed."); 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Muhammad Naufal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/configurations/site.php: -------------------------------------------------------------------------------- 1 | 5 minute 28 | * 3600 -> 1 hour 29 | */ 30 | define("CSRF_EXPIRY_DURATION", 60); 31 | 32 | /** 33 | * Maximum session (time) for inactive user 34 | * 35 | * Automatically log out the user after a specified number 36 | * of seconds of inactivity (sliding expiration is enabled). 37 | */ 38 | define("SESSION_MAX_INACTIVE", 3600); 39 | 40 | /** 41 | * Form Encryption/Decryption Secret Key 42 | * 43 | * To ensure data security during API calls. 44 | */ 45 | define("FORM_SECRET_KEY", "Y29udGFjdC5uYXVmYWxpc3RAZ21haWwuY29t"); // Change this value to your own configuration 46 | 47 | /** 48 | * Max json size (request body in form submission) 49 | * 50 | * i.e.: 51 | * 10240 KB => 10 KB 52 | * 32768 KB => 32 KB 53 | * 65536 KB => 64 KB 54 | */ 55 | define("MAX_JSON_SIZE", 65536); 56 | -------------------------------------------------------------------------------- /public/js/custom.js: -------------------------------------------------------------------------------- 1 | // set default theme to dark 2 | const defaultTheme = "dark"; 3 | 4 | // function to toggle between light and dark themes 5 | function toggleTheme() { 6 | 7 | let theme; 8 | 9 | if (localStorage.getItem("getcontact_web#theme") == null) { 10 | theme = document.documentElement.dataset.bsTheme; 11 | if (theme === null || theme === undefined || theme === "") { 12 | theme = defaultTheme; 13 | } 14 | document.documentElement.setAttribute("data-bs-theme", theme); 15 | localStorage.setItem("getcontact_web#theme", theme); 16 | } else { 17 | theme = localStorage.getItem("getcontact_web#theme"); 18 | } 19 | 20 | if (theme === "light") { 21 | document.querySelectorAll(".btn-outline-light").forEach(function (element) { 22 | element.classList.remove("btn-outline-light"); 23 | element.classList.add("btn-outline-dark"); 24 | }); 25 | } else { 26 | document.querySelectorAll(".btn-outline-dark").forEach(function (element) { 27 | element.classList.remove("btn-outline-dark"); 28 | element.classList.add("btn-outline-light"); 29 | }); 30 | } 31 | 32 | document.querySelectorAll("[data-bs-theme]").forEach(function (element) { 33 | element.setAttribute("data-bs-theme", theme); 34 | }); 35 | 36 | } 37 | 38 | // add event listener to call toggleTheme() after the entire page is loaded 39 | window.addEventListener("load", function () { 40 | 41 | toggleTheme(); 42 | 43 | }); 44 | 45 | // add event listener to toggle theme when the button is clicked 46 | document.querySelectorAll("[data-bs-theme-value]").forEach(value => { 47 | 48 | value.addEventListener("click", () => { 49 | const theme = value.getAttribute("data-bs-theme-value"); 50 | localStorage.setItem("getcontact_web#theme", theme); 51 | toggleTheme(); 52 | }); 53 | 54 | }); 55 | 56 | const notyfOption = { 57 | duration: 2500, 58 | position: { 59 | x: "center", 60 | y: "top", 61 | }, 62 | // types: [ 63 | // { 64 | // type: "warning", 65 | // background: "orange", 66 | // icon: { 67 | // className: "material-icons", 68 | // tagName: "i", 69 | // text: "warning", 70 | // }, 71 | // }, 72 | // { 73 | // type: "error", 74 | // background: "indianred", 75 | // duration: 2000, 76 | // dismissible: true, 77 | // }, 78 | // ], 79 | }; -------------------------------------------------------------------------------- /public/css/custom.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Quicksand, sans-serif; 3 | font-size: 15px; 4 | } 5 | 6 | html { 7 | position: relative; 8 | min-height: 100%; 9 | } 10 | 11 | body { 12 | padding-top: 10vh; 13 | margin-bottom: 60px; 14 | } 15 | 16 | nav { 17 | box-shadow: rgba(141, 150, 170, 0.4) 0 1px 4px 0; 18 | } 19 | 20 | /* Badge Colors - Light Theme */ 21 | .badge.bg-soft-light { 22 | color: #495057; 23 | } 24 | 25 | .badge.bg-soft-primary { 26 | color: #10335b; 27 | } 28 | 29 | .badge.bg-soft-success { 30 | color: #19490a; 31 | } 32 | 33 | .badge.bg-soft-danger { 34 | color: #660017; 35 | } 36 | 37 | /* Background Soft Colors - Light Theme */ 38 | .bg-soft-light { 39 | --bs-bg-opacity: 1; 40 | background-color: #fcfcfd; 41 | } 42 | 43 | .bg-soft-primary { 44 | --bs-bg-opacity: 1; 45 | background-color: #d4e6f9; 46 | } 47 | 48 | .bg-soft-success { 49 | --bs-bg-opacity: 1; 50 | background-color: #d9f0d1; 51 | } 52 | 53 | .bg-soft-danger { 54 | --bs-bg-opacity: 1; 55 | background-color: #ffccd7; 56 | } 57 | 58 | /* Badge Colors - Dark Theme */ 59 | [data-bs-theme="dark"] .badge.bg-soft-light { 60 | color: #f8f9fa; 61 | } 62 | 63 | [data-bs-theme="dark"] .badge.bg-soft-primary { 64 | color: #7db3ee; 65 | } 66 | 67 | [data-bs-theme="dark"] .badge.bg-soft-success { 68 | color: #7aff4d; 69 | } 70 | 71 | [data-bs-theme="dark"] .badge.bg-soft-danger { 72 | color: #f68; 73 | } 74 | 75 | /* Background Soft Colors - Dark Theme */ 76 | [data-bs-theme="dark"] .bg-soft-light { 77 | --bs-bg-opacity: 1; 78 | background-color: #373a3c; 79 | } 80 | 81 | [data-bs-theme="dark"] .bg-soft-primary { 82 | --bs-bg-opacity: 1; 83 | background-color: #081a2d; 84 | } 85 | 86 | [data-bs-theme="dark"] .bg-soft-success { 87 | --bs-bg-opacity: 1; 88 | background-color: #0d2405; 89 | } 90 | 91 | [data-bs-theme="dark"] .bg-soft-danger { 92 | --bs-bg-opacity: 1; 93 | background-color: #33000b; 94 | } 95 | 96 | /* Input Readonly States */ 97 | input[readonly] { 98 | opacity: 1 !important; 99 | } 100 | 101 | [data-bs-theme="dark"] input[readonly] { 102 | color: #858585 !important; 103 | } 104 | 105 | [data-bs-theme="light"] input[readonly] { 106 | background-color: #e9ecef !important; 107 | } 108 | 109 | /* Footer */ 110 | .footer { 111 | position: absolute; 112 | bottom: 0; 113 | width: 100%; 114 | height: 60px; 115 | line-height: 60px; 116 | text-align: center; 117 | } 118 | 119 | /* Custom style for Notyf */ 120 | .notyf__toast { 121 | max-width: 50em !important; 122 | 123 | } 124 | 125 | .notyf__ripple { 126 | height: 60em; 127 | width: 60em; 128 | } -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 1) { 34 | parse_str($request_uri_parts[1], $query_params); 35 | } else { 36 | $query_params = []; 37 | } 38 | } else { 39 | $endpoint = $request_uri; 40 | $query_params = []; 41 | } 42 | 43 | // d($query_params); 44 | // dd($endpoint); 45 | 46 | // Page and API routing 47 | $routes = array( 48 | "/" => "src/pages/index.php", 49 | "/api/getcontact/credentials" => "src/apis/credentials.php", 50 | "/api/getcontact/credentials/generate" => "src/apis/generate_credentials.php", 51 | "/api/getcontact/subscription" => "src/apis/subscription.php", 52 | "/api/getcontact/captcha/verify" => "src/apis/verify_captcha.php", 53 | "/api/getcontact/captcha/refresh" => "src/apis/refresh_captcha.php", 54 | "/dashboard" => "src/pages/dashboard/login.php", 55 | "/dashboard/credentials/manage" => "src/pages/dashboard/manage_credentials.php", 56 | "/dashboard/credentials/generate" => "src/pages/dashboard/generate_credentials.php", 57 | "/dashboard/captcha/verify" => "src/pages/dashboard/verify_captcha.php", 58 | "/dashboard/logout" => "src/pages/dashboard/logout.php", 59 | ); 60 | 61 | $normalized_endpoint = strtolower($endpoint); 62 | 63 | if (array_key_exists($normalized_endpoint, array_change_key_case($routes))) { 64 | $file = $routes[$normalized_endpoint] ?? $routes[array_keys(array_change_key_case($routes, CASE_LOWER))[$normalized_endpoint]]; 65 | page_file_exists($file); 66 | include($file); 67 | exit(); 68 | } 69 | 70 | if (strpos($normalized_endpoint, "/dashboard") === 0) { 71 | header("Location: " . URL_PREFIX . "/dashboard"); 72 | exit(); 73 | } 74 | 75 | header("HTTP/1.0 404 Not Found"); 76 | exit(); 77 | -------------------------------------------------------------------------------- /src/apis/credentials.php: -------------------------------------------------------------------------------- 1 | "Method not allowed" 19 | ]); 20 | exit(); 21 | } 22 | 23 | #endregion 24 | 25 | #region Fetch credentials 26 | 27 | if (USE_DATABASE) { 28 | $pdo_statement = $pdo->query( 29 | " 30 | SELECT 31 | id AS id, 32 | description AS description 33 | -- CONCAT(SUBSTRING(final_key, 1, CEIL(CHAR_LENGTH(final_key) / 2)), REPEAT('*', FLOOR(CHAR_LENGTH(final_key) / 2))) AS finalKey, 34 | -- CONCAT(SUBSTRING(token, 1, CEIL(CHAR_LENGTH(token) / 2)), REPEAT('*', FLOOR(CHAR_LENGTH(token) / 2))) AS token 35 | FROM credentials 36 | WHERE deleted_at IS NULL 37 | ORDER BY id ASC" 38 | ); 39 | 40 | $credentials = $pdo_statement->fetchAll(PDO::FETCH_ASSOC); 41 | 42 | if (empty($credentials)) { 43 | http_response_code(404); 44 | echo json_encode([ 45 | "message" => "Credential(s) not found" 46 | ]); 47 | exit(); 48 | } 49 | 50 | foreach ($credentials as &$credential) { 51 | $credential["id"] = encrypt_data($credential["id"]); 52 | } 53 | 54 | unset($credential); 55 | } else { 56 | 57 | if (!defined("GTC_CREDENTIALS")) { 58 | http_response_code(404); 59 | echo json_encode([ 60 | "message" => "Credential(s) not found" 61 | ]); 62 | exit(); 63 | } 64 | 65 | $credentials = json_decode(GTC_CREDENTIALS, true); 66 | 67 | if (json_last_error() !== JSON_ERROR_NONE) { 68 | error_log(json_last_error_msg()); 69 | error_log(GTC_CREDENTIALS); 70 | http_response_code(400); 71 | echo json_encode([ 72 | "message" => "Invalid JSON response. Please check error log." 73 | ]); 74 | exit(); 75 | } 76 | 77 | if (empty($credentials)) { 78 | http_response_code(404); 79 | echo json_encode([ 80 | "message" => "Credential(s) not found" 81 | ]); 82 | exit(); 83 | } 84 | 85 | foreach ($credentials as &$credential) { 86 | $credential["id"] = encrypt_data($credential["id"]); 87 | } 88 | 89 | unset($credential); 90 | } 91 | 92 | #endregion 93 | 94 | #region Return credentials 95 | 96 | http_response_code(200); 97 | echo json_encode([ 98 | "message" => "Credential(s) retrieved successfully", 99 | "data" => $credentials 100 | ]); 101 | exit(); 102 | 103 | #endregion 104 | 105 | } catch (\Exception $e) { 106 | error_log("Error Get Credentials: " . $e->getMessage()); 107 | http_response_code(500); 108 | echo json_encode([ 109 | "message" => "Internal server error" 110 | ]); 111 | exit(); 112 | } 113 | -------------------------------------------------------------------------------- /sql/db.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 5.2.1 3 | -- https://www.phpmyadmin.net/ 4 | -- 5 | -- Host: localhost:8889 6 | -- Generation Time: May 24, 2025 at 03:43 AM 7 | -- Server version: 5.7.44 8 | -- PHP Version: 8.2.20 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | START TRANSACTION; 12 | SET time_zone = "+00:00"; 13 | 14 | 15 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 16 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 17 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 18 | /*!40101 SET NAMES utf8mb4 */; 19 | 20 | -- 21 | -- Database: `getcontact_web` 22 | -- 23 | 24 | CREATE DATABASE IF NOT EXISTS getcontact_web 25 | CHARACTER SET utf8mb4 26 | COLLATE utf8mb4_unicode_ci; 27 | 28 | USE getcontact_web; 29 | 30 | -- -------------------------------------------------------- 31 | 32 | -- 33 | -- Table structure for table `administrators` 34 | -- 35 | 36 | CREATE TABLE `administrators` ( 37 | `id` int(11) NOT NULL, 38 | `username` varchar(100) NOT NULL, 39 | `password` varchar(255) NOT NULL 40 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 41 | 42 | -- 43 | -- Dumping data for table `administrators` 44 | -- 45 | -- Example php cli command to generate the password: 46 | -- php -r 'echo password_hash("admin", PASSWORD_ARGON2ID) . PHP_EOL;' 47 | -- 48 | 49 | INSERT INTO `administrators` (`id`, `username`, `password`) VALUES 50 | (1, 'admin', '$argon2id$v=19$m=65536,t=4,p=1$bEJMaWt3WklkQ3ZKejV6Vg$xMV7WToxfKsdy4ESqN3GF2FLbbVl6SmauyT7P0x2pr8'); 51 | 52 | -- -------------------------------------------------------- 53 | 54 | -- 55 | -- Table structure for table `credentials` 56 | -- 57 | 58 | CREATE TABLE `credentials` ( 59 | `id` int(11) NOT NULL, 60 | `description` varchar(255) NOT NULL, 61 | `final_key` varchar(255) NOT NULL, 62 | `token` varchar(255) NOT NULL, 63 | `client_device_id` varchar(255) DEFAULT NULL, 64 | `created_by` varchar(100) DEFAULT NULL, 65 | `created_at` datetime DEFAULT CURRENT_TIMESTAMP, 66 | `updated_by` varchar(100) DEFAULT NULL, 67 | `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 68 | `deleted_by` varchar(100) DEFAULT NULL, 69 | `deleted_at` datetime DEFAULT NULL 70 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 71 | 72 | -- 73 | -- Indexes for dumped tables 74 | -- 75 | 76 | -- 77 | -- Indexes for table `administrators` 78 | -- 79 | ALTER TABLE `administrators` 80 | ADD PRIMARY KEY (`id`), 81 | ADD UNIQUE KEY `username` (`username`); 82 | 83 | -- 84 | -- Indexes for table `credentials` 85 | -- 86 | ALTER TABLE `credentials` 87 | ADD PRIMARY KEY (`id`), 88 | ADD UNIQUE KEY `unique_final_key` (`final_key`); 89 | 90 | -- 91 | -- AUTO_INCREMENT for dumped tables 92 | -- 93 | 94 | -- 95 | -- AUTO_INCREMENT for table `administrators` 96 | -- 97 | ALTER TABLE `administrators` 98 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2; 99 | 100 | -- 101 | -- AUTO_INCREMENT for table `credentials` 102 | -- 103 | ALTER TABLE `credentials` 104 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT; 105 | COMMIT; 106 | 107 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 108 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 109 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 110 | -------------------------------------------------------------------------------- /src/configurations/getcontact.php: -------------------------------------------------------------------------------- 1 | Represent identifier like auto increment in sql table 7 | * Description -> Short description about your getcontact account 8 | * Final Key -> Key for aes-256-ecb encryption/decryption 9 | * Token -> getcontact token (retrieve from /register api) 10 | * Client Device Id -> Android id (random hex 16 digits) 11 | */ 12 | define("GTC_CREDENTIALS", json_encode([ 13 | [ 14 | "id" => 1, 15 | "description" => "Account #1", 16 | "finalKey" => "CHANGE_WITH_YOUR_FINAL_KEY_1", 17 | "token" => "CHANGE_WITH_YOUR_TOKEN_1", 18 | "clientDeviceId" => "", 19 | ], 20 | [ 21 | "id" => 2, 22 | "description" => "Account #2", 23 | "finalKey" => "CHANGE_WITH_YOUR_FINAL_KEY_2", 24 | "token" => "CHANGE_WITH_YOUR_TOKEN_2", 25 | "clientDeviceId" => "", 26 | ], 27 | [ 28 | "id" => 3, 29 | "description" => "Account #3", 30 | "finalKey" => "CHANGE_WITH_YOUR_FINAL_KEY_3", 31 | "token" => "CHANGE_WITH_YOUR_TOKEN_3", 32 | "clientDeviceId" => "", 33 | ], 34 | ])); 35 | 36 | /** 37 | * GetContact API Base URL 38 | */ 39 | define("GTC_API_BASE_URL", "https://pbssrv-centralevents.com"); 40 | 41 | /** 42 | * GetContact API Endpoint URLs 43 | */ 44 | define("GTC_API_EP_SUBSCRIPTION", "/v2.8/subscription"); // subscription 45 | define("GTC_API_EP_SEARCH", "/v2.8/search"); // view profile 46 | define("GTC_API_EP_NUMBER_DETAIL", "/v2.8/number-detail"); // view tag 47 | define("GTC_API_EP_VERIFY_CODE", "/v2.8/verify-code"); // verify captcha code 48 | define("GTC_API_EP_REFRESH_CODE", "/v2.8/refresh-code"); // change captcha code 49 | define("GTC_API_EP_PROFILE_SETTINGS", "/v2.8/profile/settings"); // update profile settings 50 | define("GTC_API_EP_REGISTER", "/v2.8/register"); 51 | define("GTC_API_EP_INIT_BASIC", "/v2.8/init-basic"); 52 | define("GTC_API_EP_AD_SETTINGS", "/v2.8/ad-settings"); 53 | define("GTC_API_EP_INIT_INTRO", "/v2.8/init-intro"); 54 | define("GTC_API_EP_EMAIL_CODE_VALIDATE_START", "/v2.8/email-code-validate/start"); 55 | define("GTC_API_EP_COUNTRY", "/v2.8/country"); 56 | define("GTC_API_EP_VALIDATION_START", "/v2.8/validation-start"); 57 | define("GTC_API_EP_VERIFYKIT_RESULT", "/v2.8/verifykit-result"); 58 | 59 | /** 60 | * GetContact HMAC Secret Key 61 | * 62 | * Note: 63 | * HMAC Secret Key for signing request signature, other mobile app version may have different hmac secret key 64 | * GTC_HMAC_SECRET_KEY "793167..." -> from mobile app version 5.6.2 65 | * GTC_HMAC_SECRET_KEY "314267..." -> from mobile app version 7.2.2+ (worked in 8.4.0) 66 | */ 67 | // define("GTC_HMAC_SECRET_KEY", "793167597c4a25263656206b5469243e5f416c69385d2f7843716d4d4d5031242a29493846774a2c2a725f59554d2034683f40372b40233c3e2b772d65335657"); 68 | define("GTC_HMAC_SECRET_KEY", "31426764382a642f3a6665497235466f3d236d5d785b722b4c657457442a495b494524324866782a2364292478587a78662d7a7b7578593f71703e2b7e365762"); 69 | 70 | /** 71 | * GetContact Header Values 72 | */ 73 | define("GTC_COUNTRY_CODE", "id"); 74 | define("GTC_ANDROID_OS", "android 9"); // i.e.: "android 5.1", "android 9" 75 | define("GTC_APP_VERSION", "8.4.0"); // i.e.: "5.6.2", "8.4.0" 76 | define("GTC_CLIENT_DEVICE_ID", "174680a6f1765b5f"); // 16 digit random hex value 77 | define("GTC_LANG", "en_US"); // i.e.: "in_ID", "en_US" 78 | define("GTC_BUNDLE_ID", "app.source.getcontact"); 79 | define("GTC_CARRIER_COUNTRY_CODE", "510"); 80 | define("GTC_CARRIER_NAME", "Indosat Ooredoo"); 81 | define("GTC_CARRIER_NETWORK_CODE", "01"); 82 | define("GTC_DEVICE_NAME", "SM-G977N"); 83 | define("GTC_DEVICE_TYPE", "Android"); 84 | define("GTC_TIME_ZONE", "Asia/Bangkok"); 85 | define("GTC_OUTSIDE_COUNTRY_CODE", "ID"); 86 | 87 | define("VFK_HMAC_SECRET_KEY", "3452235d713252604a35562d325f765238695738485863672a705e6841544d3c7e6e45463028266f372b544e596f3829236b392825262e534a7e774f37653932"); 88 | 89 | /** 90 | * VerifyKit API Base URL 91 | */ 92 | define("VFK_API_BASE_URL", "https://api.verifykit.com"); 93 | 94 | /** 95 | * VerifyKit API Endpoint URLs 96 | */ 97 | define("VFK_API_EP_INIT", "/v2.0/init"); 98 | define("VFK_API_EP_COUNTRY", "/v2.0/country"); 99 | define("VFK_API_EP_START", "/v2.0/start"); 100 | define("VFK_API_EP_CHECK", "/v2.0/check"); 101 | 102 | /** 103 | * VerifyKit Header Values 104 | */ 105 | define("VFK_CLIENT_DEVICE_ID", "174680a6f1765b5f"); 106 | define("VFK_CLIENT_KEY", "bhvbd7ced119dc6ad6a0b35bd3cf836555d6f71930d9e5a405f32105c790d"); 107 | define("VFK_FINAL_KEY", "bd48d8c25293cfb537619cc93ae3d6e372eb2ddfffff4ab0eb000777144c7bfa"); 108 | define("VFK_SDK_VERSION", "0.11.4"); 109 | define("VFK_OS", "android 9.0"); 110 | define("VFK_APP_VERSION", "8.4.0"); 111 | define("VFK_LANG", "in_ID"); 112 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 | Logo 7 | 8 | 9 |

GetContact PHP (Web)

10 | 11 |

12 | A simple web app to view GetContact profile or tag(s) directly via the API without needing to access the mobile app, written in PHP. 13 |
14 | 17 | View Demo 18 | · 19 | Report Bug 20 | · 21 | Request Feature 22 |

23 |
24 | 25 | ## Note 26 | 27 | - __2025-03-31__ 28 | This script may not work perfectly yet, and currently it can only retrieve subscription data, view profiles, and search tags. Reverse engineering is necessary to further enhance the features of the script I have made. 29 | 30 |

(back to top)

31 | 32 | 33 | 34 | ## About 35 | 36 |

37 | Screenshot Form 38 | Screenshot Profile 39 | Screenshot Download Tags 40 |

41 | 42 | My friends often ask me to check the tags or profile of a phone number using GetContact because they don’t have the app. I used to do it manually — I would check the number, take a screenshot of the result, and send it to them. With this script, things are much easier. Now they can check it themselves by submitting the number through a form, and they can even download the result as an image. (***getcontact credentials (final key & token) must be stored in the config file (src/configurations/getcontact.php).***). 43 | 44 | ### Features 45 | 46 | * **Search** 47 | * You can search for both profiles and tags. 48 | * **Manage Credentials*** 49 | * Add, update, or delete credentials. Each credential includes: `client device id`, `final key`, `token`, and `description` field for notes or info about the credential. 50 | * **Verify Captcha*** 51 | * If a credential gets a 403 Forbidden error, you can validate the captcha through the web interface. 52 | * **Auto Generate Credential*** 53 | * You can generate the final key and token directly from the website without needing to log in manually through the mobile app. 54 | 55 | > :information_source: Features marked with an asterisk are available in the dashboard at `/dashboard`. 56 | > Default login (user/pass): `admin` / `admin`. 57 | 58 | ### Built With 59 | 60 | * [PHP](https://www.php.net/releases/8.2/en.php) `v8.2` 61 | * [Bootswatch (cosmo)](https://bootswatch.com/cosmo/) `v5.3.2` 62 | * [FontAwesome](https://fontawesome.com/v5/) `v5.15.4` 63 | * [SweetAlert2](https://sweetalert2.github.io/) `v2` 64 | * [JQuery](https://jquery.com/download/) `v3.5.1` 65 | * [Html2Canvas](https://html2canvas.hertzen.com/) `v1.4.1` 66 | * [Notyf](https://carlosroso.com/notyf/) `v3` 67 | 68 | ### Prerequisites 69 | 70 | To run this app, you need PHP and/or web server in your environment. You can use [XAMPP](https://www.apachefriends.org/download.html) or [Laragon]([Laragon](https://laragon.org/download/)) for Windows, or [MAMP](https://www.mamp.info/en/downloads/) for macOS. 71 | 72 |

(back to top)

73 | 74 | ## How to get GetContact Final Key & Token (Manual) 75 | 76 | To obtain the final key and token from the GetContact app, I used an Android smartphone with ROOT access enabled. You can use either a real device or an emulator (such as MEmu, LDPlayer, etc.). 77 | 78 | > Make sure you’re already logged in to GetContact on your smartphone. 79 | 80 | 1. Open file manager, find `GetContactSettingsPref.xml` in this directory: 81 | 82 | ```sh 83 | /data/data/app.source.getcontact/shared_prefs/GetContactSettingsPref.xml 84 | ``` 85 | 2. Find `FINAL_KEY` and `TOKEN` 86 | 87 | How to get GetContact Final Key & Token 88 | 3. Copy and paste both values into the configuration file: `src/configurations/getcontact.php`. 89 | 90 | ```php 91 | // just example 92 | define("GTC_CREDENTIALS", json_encode([ 93 | [ 94 | "account" => "FILL_THIS_WITH_SHORT_DESCRIPTION_ABOUT_YOUR_GTC_ACCOUNT", 95 | "finalKey" => "CHANGE_WITH_YOUR_FINAL_KEY", 96 | "token" => "CHANGE_WITH_YOUR_TOKEN", 97 | ], 98 | ])); 99 | ``` 100 | 101 |

(back to top)

102 | 103 | ## How to get GetContact Final Key & Token (Automatic) 104 | 105 | You can use an external tool here: [https://tools.naufalist.com/getcontact/credentials/generate](https://tools.naufalist.com/getcontact/credentials/generate), or you can do this by yourself from `/dashboard/credentials/generate`. 106 | 107 | > :exclamation: Due to some limitations, the generate process from the web might sometimes fail because of rate limiting. Therefore, I recommend that you generate the credentials yourself using the feature already available on your local setup. 108 | 109 |

(back to top)

110 | 111 | 112 | 113 | ## Installation 114 | 115 | 1. Clone the repository. If you’re using XAMPP/Laragon/MAMP, place it in the htdocs directory. 116 | 117 | ```sh 118 | git clone https://github.com/naufalist/getcontact-web.git 119 | ``` 120 | 2. In **src/configurations/getcontact.php**, you should update the credentials. 121 | 122 | > The number of credentials you can choose in the form depends on how many credential entries you’ve added. 123 | > 124 | 125 | ```php 126 | // just example 127 | define("GTC_CREDENTIALS", json_encode([ 128 | [ 129 | "account" => "Account #1", 130 | "finalKey" => "abc", 131 | "token" => "def", 132 | ], 133 | [ 134 | "account" => "Account #2", 135 | "finalKey" => "ghi", 136 | "token" => "jkl", 137 | ], 138 | ])); 139 | ``` 140 | 3. Start the web server 141 | 4. Now, this app can be accessed in `http://localhost/getcontact-web`. 142 | 143 |

(back to top)

144 | 145 | 146 | 147 | ## Usage (Search feature) 148 | 149 | 1. Access this site: `http://localhost/getcontact-web`. 150 | 2. Enter the phone number into the form, for example: `081234567890`. 151 | 3. Select the final key and token you want to use. 152 | 4. Select the type of search: whether you want to look up a profile or search for tags. 153 | 5. Click the submit button. 154 | 6. If success, information from GetContact will be displayed above the form. 155 | 7. Or, if it fails, the error message will appear in an alert and also above the form. 156 | 157 |

(back to top)

158 | 159 | 160 | 161 | ## Contributing 162 | 163 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". 164 | 165 |

(back to top)

166 | 167 | 168 | 169 | ## License 170 | 171 | This repo is under [MIT License](https://opensource.org/licenses/mit-license.php) 172 | 173 |

(back to top)

174 | 175 | 176 | 177 | ## Contact 178 | 179 | [@naufalist](https://twitter.com/naufalist) - contact.naufalist@gmail.com 180 | 181 |

(back to top)

182 | -------------------------------------------------------------------------------- /src/libraries/helper.php: -------------------------------------------------------------------------------- 1 | "; 7 | print_r($variable); 8 | echo ""; 9 | } 10 | } 11 | 12 | if (!function_exists("dd")) { 13 | function dd($variable) 14 | { 15 | var_dump($variable); 16 | die(); 17 | } 18 | } 19 | 20 | if (!function_exists("base_url")) { 21 | function base_url($suffix = "") 22 | { 23 | $protocol = isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "https" : "http"; 24 | $domain = $_SERVER["HTTP_HOST"]; 25 | $base_url = $protocol . "://" . $domain . URL_PREFIX . "/" . ltrim($suffix, "/"); 26 | return $base_url; 27 | } 28 | } 29 | 30 | if (!function_exists("page_file_exists")) { 31 | function page_file_exists(string $filePath) 32 | { 33 | if (!file_exists($filePath)) { 34 | header("HTTP/1.0 404 Not Found"); 35 | exit(); 36 | } 37 | } 38 | } 39 | 40 | if (!function_exists("is_logged_in")) { 41 | function is_logged_in() 42 | { 43 | $max_inactive = SESSION_MAX_INACTIVE ?? 3600; 44 | 45 | if (!isset($_SESSION["admin_id"])) { 46 | return false; 47 | } 48 | 49 | if (!isset($_SESSION["last_activity"])) { 50 | return false; 51 | } 52 | 53 | if (time() - $_SESSION["last_activity"] > $max_inactive) { 54 | session_unset(); 55 | session_destroy(); 56 | return false; 57 | } 58 | 59 | // auto slide expiry time 60 | $_SESSION["last_activity"] = time(); 61 | 62 | return true; 63 | } 64 | } 65 | 66 | if (!function_exists("require_login")) { 67 | function require_login() 68 | { 69 | if (!is_logged_in()) { 70 | header("Location: " . URL_PREFIX . "/dashboard"); 71 | exit(); 72 | } 73 | } 74 | } 75 | 76 | if (!function_exists("csrf_token_generate")) { 77 | function csrf_token_generate() 78 | { 79 | if (empty($_SESSION["csrf_token"])) { 80 | $_SESSION["csrf_token"] = bin2hex(random_bytes(32)); 81 | $_SESSION["csrf_token_expire"] = time() + CSRF_EXPIRY_DURATION; 82 | } 83 | 84 | return $_SESSION["csrf_token"]; 85 | } 86 | } 87 | 88 | if (!function_exists("csrf_token_validate")) { 89 | function csrf_token_validate(string $csrfToken) 90 | { 91 | if (!isset($_SESSION["csrf_token"])) { 92 | return false; 93 | } 94 | 95 | if (!isset($_SESSION["csrf_token_expire"])) { 96 | return false; 97 | } 98 | 99 | if (!is_string($csrfToken)) { 100 | return false; 101 | } 102 | 103 | $is_csrf_token_valid = hash_equals($_SESSION["csrf_token"], $csrfToken); 104 | 105 | if (!$is_csrf_token_valid) { 106 | return false; 107 | } 108 | 109 | // auto renew 110 | if ($_SESSION["csrf_token_expire"] < time()) { 111 | unset($_SESSION["csrf_token"]); 112 | unset($_SESSION["csrf_token_expire"]); 113 | csrf_token_generate(); 114 | } 115 | 116 | return true; 117 | } 118 | } 119 | 120 | if (!function_exists("curl_execute_request")) { 121 | function curl_execute_request($curl) 122 | { 123 | try { 124 | $response = curl_exec($curl); 125 | $header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE); 126 | $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); 127 | 128 | $response_header = null; 129 | $response_body = null; 130 | 131 | if ($response !== false) { 132 | $response_header = substr($response, 0, $header_size); 133 | $response_body = substr($response, $header_size); 134 | } 135 | 136 | if ($response === false || curl_errno($curl)) { 137 | $error_message = curl_error($curl); 138 | error_log("cURL Error: $error_message"); 139 | error_log("Status Code: $http_code"); 140 | error_log("Header: " . ($response_header ?? "[null]")); 141 | error_log("Body: " . ($response_body ?? "[null]")); 142 | throw new Exception("cURL failed: $error_message"); 143 | } 144 | 145 | return (object)[ 146 | "httpCode" => $http_code, 147 | "header" => $response_header, 148 | "body" => $response_body 149 | ]; 150 | } finally { 151 | curl_close($curl); 152 | } 153 | } 154 | } 155 | 156 | if (!function_exists("log_api_transaction")) { 157 | function log_api_transaction($request, $response, $label = "API") 158 | { 159 | error_log("==== [REQUEST ($label)] ===="); 160 | error_log(json_encode($request, JSON_PRETTY_PRINT)); 161 | 162 | error_log("==== [RESPONSE ($label)] ===="); 163 | error_log(json_encode($response, JSON_PRETTY_PRINT)); 164 | 165 | error_log("=========================="); 166 | } 167 | } 168 | 169 | if (!function_exists("has_nested_property")) { 170 | function has_nested_property($data, $path) 171 | { 172 | $keys = explode(".", $path); 173 | 174 | foreach ($keys as $key) { 175 | if (is_object($data)) { 176 | if (!isset($data->$key)) { 177 | return false; 178 | } 179 | $data = $data->$key; 180 | } elseif (is_array($data)) { 181 | if (!array_key_exists($key, $data)) { 182 | return false; 183 | } 184 | $data = $data[$key]; 185 | } else { 186 | return false; 187 | } 188 | } 189 | 190 | return true; 191 | } 192 | } 193 | 194 | if (!function_exists("get_nested_value")) { 195 | 196 | /** 197 | * Example of usage: 198 | * 199 | * $token = get_nested_value($data, "result.token"); 200 | * echo "Token: " . ($token ?? "-") . PHP_EOL; 201 | * 202 | * $providers = get_nested_value($data, "result.preEmailValidate.providerList"); 203 | * $first_provider = is_array($providers) && isset($providers[0]) ? $providers[0] : null; 204 | * echo "First Provider: " . ($first_provider ?? "-") . PHP_EOL; 205 | */ 206 | function get_nested_value($data, $path, $default = null) 207 | { 208 | $keys = explode(".", $path); 209 | 210 | foreach ($keys as $key) { 211 | if (is_object($data)) { 212 | if (!isset($data->$key)) { 213 | return $default; 214 | } 215 | $data = $data->$key; 216 | } elseif (is_array($data)) { 217 | if (!array_key_exists($key, $data)) { 218 | return $default; 219 | } 220 | $data = $data[$key]; 221 | } else { 222 | return $default; 223 | } 224 | } 225 | 226 | return $data; 227 | } 228 | } 229 | 230 | if (!function_exists("encrypt_data")) { 231 | function encrypt_data($input) 232 | { 233 | if (!is_string($input) && !is_int($input) && !is_array($input)) { 234 | return false; 235 | } 236 | 237 | $data = json_encode($input); 238 | if ($data === false) { 239 | return false; 240 | } 241 | 242 | $key = hash("sha256", FORM_SECRET_KEY, true); 243 | 244 | $iv = openssl_random_pseudo_bytes(16); // random IV, 16 byte, for AES-256-CBC 245 | 246 | $encrypted = openssl_encrypt($data, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv); 247 | if ($encrypted === false) { 248 | return false; 249 | } 250 | 251 | $hmac = hash_hmac("sha256", $encrypted, $key, true); // hmac for integrity 252 | 253 | return base64_encode($iv . $hmac . $encrypted); 254 | } 255 | } 256 | 257 | if (!function_exists("decrypt_data")) { 258 | function decrypt_data($encryptedData) 259 | { 260 | $key = hash("sha256", FORM_SECRET_KEY, true); 261 | 262 | $decoded = base64_decode($encryptedData, true); 263 | if ($decoded === false || strlen($decoded) < 48) { 264 | return false; // 16 (IV) + 32 (HMAC) 265 | } 266 | 267 | // separate 268 | $iv = substr($decoded, 0, 16); 269 | $hmac = substr($decoded, 16, 32); 270 | $ciphertext = substr($decoded, 48); 271 | 272 | $calculated_hmac = hash_hmac("sha256", $ciphertext, $key, true); 273 | if (!hash_equals($hmac, $calculated_hmac)) { 274 | return false; 275 | } 276 | 277 | $decrypted = openssl_decrypt($ciphertext, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv); 278 | if ($decrypted === false) return false; 279 | 280 | return json_decode($decrypted, true); // true: decode to array/primitive 281 | } 282 | } 283 | 284 | if (!function_exists("censor_phone_number")) { 285 | function censor_phone_number($phoneNumber) 286 | { 287 | if (strlen($phoneNumber) < 7) { 288 | return "xxxxx"; 289 | } 290 | 291 | $censored_after_position = 6; 292 | $count_censored_digit = 5; 293 | 294 | return substr_replace($phoneNumber, "xxxxx", $censored_after_position, $count_censored_digit); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/apis/subscription.php: -------------------------------------------------------------------------------- 1 | "Method not allowed" 19 | ]); 20 | exit(); 21 | } 22 | 23 | #endregion 24 | 25 | #region Test 26 | 27 | // test 28 | // $result = [ 29 | // "info" => [ 30 | // "search" => [ 31 | // "limit" => 999, 32 | // "remainingCount" => 999, 33 | // ], 34 | // "numberDetail" => [ 35 | // "limit" => 999, 36 | // "remainingCount" => 999, 37 | // ], 38 | // "receiptEndDate" => "" 39 | // ] 40 | // ]; 41 | 42 | // http_response_code(200); 43 | // echo json_encode([ 44 | // "message" => "Data received successfully", 45 | // "data" => $result 46 | // ]); 47 | // exit(); 48 | // test 49 | 50 | #endregion 51 | 52 | #region Parse request body 53 | 54 | $raw_json = file_get_contents("php://input"); 55 | 56 | if (strlen($raw_json) > MAX_JSON_SIZE) { 57 | http_response_code(413); 58 | echo json_encode([ 59 | "message" => "Payload too large" 60 | ]); 61 | exit(); 62 | } 63 | 64 | $parsed_json = json_decode($raw_json, true); 65 | 66 | if (json_last_error() !== JSON_ERROR_NONE) { 67 | error_log(json_last_error_msg()); 68 | error_log($raw_json); 69 | http_response_code(400); 70 | echo json_encode([ 71 | "message" => "Invalid JSON response. Please check error log." 72 | ]); 73 | exit(); 74 | } 75 | 76 | #endregion 77 | 78 | #region Parse and validate all request input 79 | 80 | $id = $parsed_json["id"] ?? null; 81 | $decrypted_id = decrypt_data($id); 82 | $decrypted_id = (int)$decrypted_id; 83 | 84 | if (!isset($decrypted_id) || !is_int($decrypted_id) || $decrypted_id <= 0) { 85 | error_log("Decryption failed for ID: $id"); 86 | http_response_code(400); 87 | echo json_encode([ 88 | "message" => "Invalid or malformed credential ID" 89 | ]); 90 | exit(); 91 | } 92 | 93 | $id = $decrypted_id; 94 | 95 | #endregion 96 | 97 | #region Fetch and validate credentials from database/config 98 | 99 | if (USE_DATABASE) { 100 | 101 | $pdo_statement = $pdo->prepare(" 102 | SELECT final_key AS finalKey, token AS token, client_device_id AS clientDeviceId 103 | FROM credentials 104 | WHERE deleted_at IS NULL AND id = ? 105 | ORDER BY id ASC 106 | "); 107 | 108 | $pdo_statement->execute([$id]); 109 | 110 | $credential = $pdo_statement->fetch(PDO::FETCH_ASSOC); 111 | } else { 112 | $credentials = json_decode(GTC_CREDENTIALS, true); 113 | 114 | if (json_last_error() !== JSON_ERROR_NONE) { 115 | error_log(json_last_error()); 116 | http_response_code(400); 117 | echo json_encode([ 118 | "message" => "Invalid credentials" 119 | ]); 120 | exit(); 121 | } 122 | 123 | $credential = array_filter($credentials, function ($credential) use ($id) { 124 | return $credential["id"] === $id; 125 | }); 126 | 127 | $credential = reset($credential); 128 | } 129 | 130 | if (!$credential || !isset($credential["finalKey"], $credential["token"])) { 131 | http_response_code(400); 132 | echo json_encode([ 133 | "message" => "Credential not found or incomplete" 134 | ]); 135 | exit(); 136 | } 137 | 138 | $final_key = $credential["finalKey"] ?? null; 139 | $token = $credential["token"] ?? null; 140 | 141 | if (empty($final_key) || empty($token)) { 142 | http_response_code(400); 143 | echo json_encode([ 144 | "message" => "Invalid final key or token" 145 | ]); 146 | exit(); 147 | } 148 | 149 | #endregion 150 | 151 | #region Call GetContact API Subscription 152 | 153 | $api_request = (object)[ 154 | "clientDeviceId" => $credential["clientDeviceId"], 155 | "finalKey" => $final_key, 156 | "token" => $token 157 | ]; 158 | 159 | $api_response = getcontact_call_api_subscription($api_request); 160 | 161 | #endregion 162 | 163 | #region Validate http status code 164 | 165 | if ($api_response->httpCode !== 200) { 166 | log_api_transaction($api_request, $api_response, "subscription: call"); 167 | http_response_code(400); 168 | echo json_encode([ 169 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 170 | ]); 171 | exit(); 172 | } 173 | 174 | #endregion 175 | 176 | #region Parse, decrypt, and decode API response 177 | 178 | $api_response_body = json_decode($api_response->body, false); 179 | 180 | if (json_last_error() !== JSON_ERROR_NONE) { 181 | log_api_transaction($api_request, $api_response, "subscription: decode body"); 182 | error_log(json_last_error_msg()); 183 | error_log($api_response->body); 184 | http_response_code(400); 185 | echo json_encode([ 186 | "message" => "Invalid JSON response. Please check error log." 187 | ]); 188 | exit(); 189 | } 190 | 191 | $decrypted_data_raw = getcontact_decrypt($api_response_body->data, $final_key); 192 | $decrypted_body = json_decode($decrypted_data_raw, false); 193 | 194 | if (json_last_error() !== JSON_ERROR_NONE) { 195 | log_api_transaction($api_request, $api_response, "subscription: decode body data"); 196 | error_log(json_last_error_msg()); 197 | error_log($decrypted_data_raw); 198 | http_response_code(400); 199 | echo json_encode([ 200 | "message" => "Invalid JSON response. Please check error log." 201 | ]); 202 | exit(); 203 | } 204 | 205 | if ($decrypted_body == null) { 206 | log_api_transaction($api_request, $api_response, "subscription: body null"); 207 | http_response_code(500); 208 | echo json_encode([ 209 | "message" => "Could not get subscription info (invalid response body)" 210 | ]); 211 | exit(); 212 | } 213 | 214 | #endregion 215 | 216 | #region Validate and return result 217 | 218 | $search_limit_path = "result.subscriptionInfo.usage.search.limit"; 219 | $search_remaining_count_path = "result.subscriptionInfo.usage.search.remainingCount"; 220 | $number_detail_limit_path = "result.subscriptionInfo.usage.numberDetail.limit"; 221 | $number_detail_remaining_count_path = "result.subscriptionInfo.usage.numberDetail.remainingCount"; 222 | // $receipt_end_date_path = "result.subscriptionInfo.receiptEndDate"; 223 | $renew_date_path = "result.subscriptionInfo.renewDate"; 224 | 225 | $required_keys = [ 226 | $search_limit_path, 227 | $search_remaining_count_path, 228 | $number_detail_limit_path, 229 | $number_detail_remaining_count_path, 230 | // $receipt_end_date_path, 231 | $renew_date_path, 232 | ]; 233 | 234 | foreach ($required_keys as $path) { 235 | if (!has_nested_property($decrypted_body, $path)) { 236 | log_api_transaction($api_request, $api_response, "subscription: missing $path"); 237 | http_response_code(500); 238 | echo json_encode([ 239 | "message" => "Unexpected API response structure" 240 | ]); 241 | exit(); 242 | } 243 | } 244 | 245 | $result = [ 246 | "info" => [ 247 | "search" => [ 248 | "limit" => 0, 249 | "remainingCount" => 0, 250 | ], 251 | "numberDetail" => [ 252 | "limit" => 0, 253 | "remainingCount" => 0, 254 | ], 255 | "receiptEndDate" => "" 256 | ] 257 | ]; 258 | 259 | $result["info"]["search"]["limit"] = get_nested_value($decrypted_body, $search_limit_path); 260 | $result["info"]["search"]["remainingCount"] = get_nested_value($decrypted_body, $search_remaining_count_path); 261 | $result["info"]["numberDetail"]["limit"] = get_nested_value($decrypted_body, $number_detail_limit_path); 262 | $result["info"]["numberDetail"]["remainingCount"] = get_nested_value($decrypted_body, $number_detail_remaining_count_path); 263 | // $result["info"]["receiptEndDate"] = get_nested_value($decrypted_body, $receipt_end_date_path); 264 | $result["info"]["receiptEndDate"] = get_nested_value($decrypted_body, $renew_date_path); 265 | 266 | http_response_code(200); 267 | echo json_encode([ 268 | "message" => "Data received successfully", 269 | "data" => $result 270 | ]); 271 | exit(); 272 | 273 | #endregion 274 | 275 | } catch (\Exception $e) { 276 | error_log("Error GetContact API Subscription: " . $e->getMessage()); 277 | http_response_code(500); 278 | echo json_encode([ 279 | "message" => "Internal server error" 280 | ]); 281 | exit(); 282 | } 283 | -------------------------------------------------------------------------------- /src/pages/dashboard/login.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT id, username, password FROM administrators WHERE username = :username"); 58 | $pdo_statement->execute(["username" => $username]); 59 | $admin = $pdo_statement->fetch(); 60 | 61 | if (!$admin || !password_verify($password, $admin["password"])) { 62 | $error = "Username or password is invalid"; 63 | break; 64 | } 65 | 66 | $_SESSION["admin_id"] = $admin["id"]; 67 | $_SESSION["last_activity"] = time(); 68 | header("Location: " . URL_PREFIX . "/dashboard/credentials/manage"); 69 | exit(); 70 | 71 | #endregion 72 | 73 | break; 74 | } 75 | 76 | $csrf_token = csrf_token_generate(); 77 | 78 | ?> 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | GetContact PHP Web App | Admin Dashboard 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | "> 124 | 125 | 126 | 127 | 128 | 129 | 161 | 162 | 163 |
164 | 165 | 166 |
167 |
168 | 169 |
170 |
171 | Login 172 |
173 |
174 |
" method="POST"> 175 | 176 | 177 | 178 | 179 | 180 |
181 | 182 | 183 |
184 | Username is required. 185 |
186 |
187 | 188 | 189 |
190 |
191 | 194 | 197 |
198 | 199 |
200 | Password is required. 201 |
202 |
203 | 204 | 205 |
206 | 207 |
208 | 209 | 210 | 211 | 216 | 217 | 218 |
219 |
220 |
221 |
222 |
223 | 224 |
225 | 226 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 254 | 255 | 256 | 257 | -------------------------------------------------------------------------------- /src/apis/refresh_captcha.php: -------------------------------------------------------------------------------- 1 | "Method not allowed" 19 | ]); 20 | exit(); 21 | } 22 | 23 | #endregion 24 | 25 | #region Test 26 | 27 | // // test 28 | // sleep(2); 29 | // $result = [ 30 | // "captcha" => [ 31 | // "image" => base64_encode("\/9j\/4AAQSkZJRgABAQEAYABgAAD\/\/gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBxdWFsaXR5ID0gOTAK\/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwKDAwLCgsLDQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU\/9sAQwEDBAQFBAUJBQUJFA0LDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU\/8AAEQgAKAB4AwERAAIRAQMRAf\/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC\/\/EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29\/j5+v\/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC\/\/EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29\/j5+v\/aAAwDAQACEQMRAD8A\/T2u0+aCgAoAKACgAoAKACgAoAKAOT+JHxX8I\/CHRE1fxhrtroVhJIIUkuCSXcgnaqqCzHAJ4HauSvi6OGt7WVm+mrf3K7+ZjUrQoq83\/XyJPhh8RtI+LfgPSPF2gmc6TqkbSQfaY\/LkAV2QhlycHKnvVYbERxNJVYppO+\/k7efYKVRVoKcf66HUV0mwUAFABQAUAFAHgX7Ufxb8YeENS8EeCfh8bODxd4uvWt4r++j8yOzhQAvJswQTz3BwAePT5rNcbWpVYYag7Sl6dXZJN6LXdnl4zETpyjSp\/FIwtX8Z\/HL9n3SpPEHjMaV8UvCFuQ+pT6Jbmz1HT4ON8wixtmRRkkDBxySACRg6uY5elOv78PVfnZP77rzW5DnisMuepaUevdfl\/W9lqfRXh7X9P8VaFp+s6VdJe6ZqECXNtcR\/dkjdQysPqCK+mo1oYimqsNmepGSnFSjsybVNVstD0641DUryDT7C2Qyz3V1KscUSDkszMQAB6mqqVYUY81SSS8xykormk7I+cPGH\/BRX4J+EPEsejjXbvXMOyT3+j2hntICD3kyPMB7GIOPevKnmtGLtGLkvJW\/No454yEJWSb9Lfq1+Bztp\/wAFMvh3q6wf2T4Q8darLKrFYrXS4nOU5lTiY5ZExIcZAUjJFcs86px0UPvaXz6\/Lv5GTx9JNJp\/h\/mQxftHfG\/4+3K2fwq+Gc\/g3w1cHypPF3i6LDpBLwlxbwlkVnTDsVUzrnYDjPOc8ficVG2Gh7rurr13vsvO1322BVq1b+FH3e9\/x6fhc7P4bfsO+CfD2r3Xibx27\/FPxpfqRfap4liSeB2OAClu25VwqqoyWIAwCBxXRhcrUPfxDvLte6\/JN\/Pfe3bWlhIwfNJ8z8\/6\/Nnvs\/iLSbO4trabU7KCe5laCCKS4RWlkX7yICcsw7gcivU+tYaD5PaRXS11v2Op1IXSclrp8ybSNYsPEGm2+o6XfW2pafcrvhu7SVZYpV9VdSQR7g10QnGpHmg7ruioyjNc0XdFurKCgAoAKACgD5r\/AGvNH1DwvrXw6+LNhYXWq2vgrUzJq9tZrvmGnygLNKi5G4oBnGe+TgAkfH5thnTrU8Xq0rX12s7rXpfVX72PJx0FGdPE2+F6+n9fmj0e\/wDjN8O\/Fnwr1TxBb+K9LvvDk9hL5kpuVXKNGcoyNhlYjjawB7YrrxOLwtTC1OSrrZq19dVtZ\/5fM6amJozoyfMrWf8AVtz8\/wC3+PHxAX9nr4ffB\/wZo2v6d4l16YHTNcgmNsk1oZ3KxxScEEHCscgBQcnmvlKNSo6XsJN25m7d3tZLte\/W1zwaVap7BUJb3fXV67Lyv12Jf+EQ8W698JPiRP8AFvxjqniO4+GEy6dD4Pjumitrl3YeXLdSIFkmiZiCG3B9qnDLwKHGCi5RVnBpba6t3t2s\/wDLsaScvZz5npC2m+7\/AK\/z2L+u\/CXS\/wBntvhRrl3428Pay9pexWmqaTbaZawyWUN7bsrGWVB5kkYVzgzk5BBHXFZVqShTjJTUnNXaT1Wz187eS8kc1SCoqMuZNy3Xbrd99PTyLf7POn\/C+x+CmjXem\/Gi6+GHxHllvpJ\/s2vGOylnindYDeQOTFsaLysA7d4BxuKtj0qSpRoqftHGrZ2Vn5O10rre29rnXRjCNGP7xxm1\/wAHt+v36o9E8WftDeOvj\/8ACX4TeGvBE+reHPHXjFpje6pZRNZwoLeGTzCksm0mMvsdmiLFQpUbnwh6Z5hXxKp4em3zpu\/S9nZap9rt9L+jNZ4mpXUKUH7zvfpe3n+On\/AO08DfFC71r9kzUPiX4u1jUNcvtG0ya3vfDV00cNrHf2xMQEhSNZmdyEZhJIy5kJCjChdLrE4B1a0nPkdrPRa2SbtZvRp773NYT9phXUm3K2lnpr0v17Pfr9258JvidY69rmi+GfHXwWg+H954v03\/AIl00y213BqqRxCWSGTau6MhcsI5RnAOcHg7YSrh3KnSq4eKU1o7b9r3V9fV7q+9y6M4NxhOklzLfv63X53PplVCjAAAznivr4xUVaKsevsLTGFABQAUAFAARkUmk1ZgeQeJ\/wBkT4OeLdb\/ALY1L4faS+oclntka3WQk5JdI2VXJPdgTXkVcrwsk5KLXXRtfJK9jjlg6Endx\/FpfcmfN3xbuPiF8TP2lvC+nfCTRf8AhGIPAGjv5N1runeXa2z3IMZ\/cgM+3YqqmIycgttEf7yvm4w+t4pQw0eTkSsmrONtbtNPdvz7nkVZc9WPsotci7arztZ73XTz6E1l+y18TdS+L10vjrVpvFWi+PNHmtvFOoafDHaWkE0KD7Kdi7XbaVixhYySDnK7gW8txEq8fbK\/M3dq+nXXRLezWny7wsPiJ1PeXxbv+rbWVk0tfmdUv7B\/ie\/8Gaf4Q1r416pf+GNK+fTNPtdGitfs8obdHIzrIWlKtyNx69MV6iyOH2p6eXn63\/BX8zrnl9SUI01Usl5P\/wCS\/ruH7PP7HOj2fwxl0j4kfD7RNY1+z1e7jGoXh2zXVuZNyTeYik4OeFz0HUdBhl+XQqRl7eDTT81sltZ2a8zOhhZqL5qave27Wy8k7+RB4x8L+PvEn7TB0r4S\/wDCP+E7TwJ4bi0zztSgMkFsbtvMxDGqkb9kadRjGc\/erBUnVx\/ssM+XkVumlr33ve7kyZRrSruFBKLgkv60e9+3TUyNT\/ZS+KUXhf4s+BF1\/wD4SDTfF1xZ6uNYmWCzhku5LkPe7oVVmU4QMApC4Kj1FYTwePhKpRhBPms21otNVu0t915ehFTCYpOcYu97Pok3e\/r9z7Ht3xO8A6z4g\/aA+Dt5ax3zeHtDGp3d1cxsDHBL9nWOEE43Zbe3ByCAfevUxVCax1CEI+6vw6+itZHbVw83XpqLfKr+dv6st9z2I2t9FCiQ3yyMPvSXUAdm\/wC+Cg\/Svp9btnbyVoq0Z39Vf8nFfgPbTUndJLhnlcKVKh2WJs9cx52n8c0uXSz1H7FS1qO7+dvuvb77liGGO3hSKJFiijUKiIMKoHAAA6CrN4xUUoxVkh9IoKACgAoAKACgAoAKACgAxSsk7gFMAoAKACgAoA\/\/2Q=="), 32 | // ], 33 | // ]; 34 | 35 | // http_response_code(200); 36 | // echo json_encode([ 37 | // "message" => "Your code has been refreshed successfully.", 38 | // "data" => $result 39 | // ]); 40 | // exit(); 41 | // // test 42 | 43 | #endregion 44 | 45 | #region Parse request body 46 | 47 | $raw_json = file_get_contents("php://input"); 48 | 49 | if (strlen($raw_json) > MAX_JSON_SIZE) { 50 | http_response_code(413); 51 | echo json_encode([ 52 | "message" => "Payload too large" 53 | ]); 54 | exit(); 55 | } 56 | 57 | $parsed_json = json_decode($raw_json, true); 58 | 59 | if (json_last_error() !== JSON_ERROR_NONE) { 60 | error_log(json_last_error_msg()); 61 | error_log($raw_json); 62 | http_response_code(400); 63 | echo json_encode([ 64 | "message" => "Invalid JSON response. Please check error log." 65 | ]); 66 | exit(); 67 | } 68 | 69 | #endregion 70 | 71 | #region Parse and validate all request input 72 | 73 | $id = $parsed_json["id"] ?? null; 74 | $decrypted_id = decrypt_data($id); 75 | $decrypted_id = (int)$decrypted_id; 76 | 77 | if (!isset($decrypted_id) || !is_int($decrypted_id) || $decrypted_id <= 0) { 78 | error_log("Decryption failed for ID: $id"); 79 | http_response_code(400); 80 | echo json_encode([ 81 | "message" => "Invalid or malformed credential ID" 82 | ]); 83 | exit(); 84 | } 85 | 86 | $id = $decrypted_id; 87 | 88 | #endregion 89 | 90 | #region Fetch and validate credentials from database/config 91 | 92 | if (USE_DATABASE) { 93 | 94 | $pdo_statement = $pdo->prepare(" 95 | SELECT final_key AS finalKey, token AS token, client_device_id AS clientDeviceId 96 | FROM credentials 97 | WHERE deleted_at IS NULL AND id = ? 98 | ORDER BY id ASC 99 | "); 100 | 101 | $pdo_statement->execute([$id]); 102 | 103 | $credential = $pdo_statement->fetch(PDO::FETCH_ASSOC); 104 | } else { 105 | $credentials = json_decode(GTC_CREDENTIALS, true); 106 | 107 | if (json_last_error() !== JSON_ERROR_NONE) { 108 | error_log(json_last_error()); 109 | http_response_code(400); 110 | echo json_encode([ 111 | "message" => "Invalid credentials" 112 | ]); 113 | exit(); 114 | } 115 | 116 | $credential = array_filter($credentials, function ($credential) use ($id) { 117 | return $credential["id"] === $id; 118 | }); 119 | 120 | $credential = reset($credential); 121 | } 122 | 123 | if (!$credential || !isset($credential["finalKey"], $credential["token"])) { 124 | http_response_code(400); 125 | echo json_encode([ 126 | "message" => "Credential not found or incomplete" 127 | ]); 128 | exit(); 129 | } 130 | 131 | $final_key = $credential["finalKey"] ?? null; 132 | $token = $credential["token"] ?? null; 133 | 134 | if (empty($final_key) || empty($token)) { 135 | http_response_code(400); 136 | echo json_encode([ 137 | "message" => "Invalid final key or token" 138 | ]); 139 | exit(); 140 | } 141 | 142 | #endregion 143 | 144 | #region Call GetContact API Refresh Code 145 | 146 | $api_request = (object)[ 147 | "clientDeviceId" => $credential["clientDeviceId"], 148 | "finalKey" => $final_key, 149 | "token" => $token 150 | ]; 151 | 152 | $api_response = getcontact_call_api_refresh_code($api_request); 153 | 154 | #endregion 155 | 156 | #region Parse, decrypt, and decode API response 157 | 158 | $api_response_body = json_decode($api_response->body, false); 159 | 160 | if (json_last_error() !== JSON_ERROR_NONE) { 161 | log_api_transaction($api_request, $api_response, "refresh code: decode body"); 162 | error_log(json_last_error_msg()); 163 | error_log($api_response->body); 164 | http_response_code(400); 165 | echo json_encode([ 166 | "message" => "Invalid JSON response. Please check error log." 167 | ]); 168 | exit(); 169 | } 170 | 171 | $decrypted_data_raw = getcontact_decrypt($api_response_body->data, $final_key); 172 | $decrypted_body = json_decode($decrypted_data_raw, false); 173 | 174 | if (json_last_error() !== JSON_ERROR_NONE) { 175 | log_api_transaction($api_request, $api_response, "refresh code: decode body data"); 176 | error_log(json_last_error_msg()); 177 | error_log($decrypted_data_raw); 178 | http_response_code(400); 179 | echo json_encode([ 180 | "message" => "Invalid JSON response. Please check error log." 181 | ]); 182 | exit(); 183 | } 184 | 185 | if ($decrypted_body == null) { 186 | log_api_transaction($api_request, $api_response, "refresh code: body null"); 187 | http_response_code(500); 188 | echo json_encode([ 189 | "message" => "Could not refresh captcha (invalid response body)" 190 | ]); 191 | exit(); 192 | } 193 | 194 | #endregion 195 | 196 | #region Validate http status code 197 | 198 | $meta_http_status_code_path = "meta.httpStatusCode"; 199 | 200 | if (!has_nested_property($decrypted_body, $meta_http_status_code_path)) { 201 | log_api_transaction($api_request, $api_response, "$source_type: missing $meta_http_status_code_path"); 202 | http_response_code(500); 203 | echo json_encode([ 204 | "message" => "Unexpected API response structure" 205 | ]); 206 | exit(); 207 | } 208 | 209 | $http_code = get_nested_value($decrypted_body, $meta_http_status_code_path); 210 | $api_response_http_code = $api_response->httpCode ?? null; 211 | 212 | /** 213 | * 200 -> success 214 | * 4xx -> failed 215 | */ 216 | 217 | if ( 218 | !in_array($http_code, [200]) || 219 | !in_array($api_response_http_code, [200]) 220 | ) { 221 | log_api_transaction( 222 | $api_request, 223 | $api_response, 224 | "$source_type: invalid http status" 225 | ); 226 | 227 | $error_message_path = "meta.errorMessage"; 228 | $error_message = "An error occurred. Please contact administrator."; 229 | 230 | if (has_nested_property($decrypted_body, $error_message_path)) { 231 | $error_message = get_nested_value($decrypted_body, $error_message_path); 232 | error_log(json_encode($decrypted_body, JSON_PRETTY_PRINT)); 233 | } else { 234 | error_log("$source_type: missing $error_message_path"); 235 | } 236 | 237 | // if (!has_nested_property($decrypted_body, $error_message_path)) { 238 | // error_log("$source_type: missing $error_message_path"); 239 | // } 240 | 241 | http_response_code(500); 242 | echo json_encode([ 243 | "message" => "Invalid response ($http_code: $error_message)" 244 | ]); 245 | exit(); 246 | } 247 | 248 | #endregion 249 | 250 | #region Validate and return result 251 | 252 | $image_path = "result.image"; 253 | 254 | if (!has_nested_property($decrypted_body, $image_path)) { 255 | log_api_transaction($api_request, $api_response, "verify code: missing $image_path"); 256 | http_response_code(500); 257 | echo json_encode([ 258 | "message" => "Unexpected API response structure" 259 | ]); 260 | exit(); 261 | } 262 | 263 | $result = [ 264 | "captcha" => [ 265 | "image" => "", 266 | ], 267 | ]; 268 | 269 | $result["captcha"]["image"] = get_nested_value($decrypted_body, $image_path); 270 | 271 | http_response_code(200); 272 | echo json_encode([ 273 | "message" => "Your code has been refreshed successfully.", 274 | "data" => $result 275 | ]); 276 | exit(); 277 | 278 | #endregion 279 | 280 | } catch (\Exception $e) { 281 | error_log("Error GetContact API Refresh Captcha: " . $e->getMessage()); 282 | http_response_code(500); 283 | echo json_encode([ 284 | "message" => "Internal server error" 285 | ]); 286 | exit(); 287 | } 288 | -------------------------------------------------------------------------------- /src/apis/verify_captcha.php: -------------------------------------------------------------------------------- 1 | "Method not allowed" 19 | ]); 20 | exit(); 21 | } 22 | 23 | #endregion 24 | 25 | #region Test 26 | 27 | // test 28 | // $result = [ 29 | // "captcha" => [ 30 | // "image" => base64_encode("\/9j\/4AAQSkZJRgABAQEAYABgAAD\/\/gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBkZWZhdWx0IHF1YWxpdHkK\/9sAQwAIBgYHBgUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy\/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy\/8AAEQgAKAB4AwEiAAIRAQMRAf\/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC\/\/EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29\/j5+v\/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC\/\/EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29\/j5+v\/aAAwDAQACEQMRAD8A9K1y4httHv7vTNN\/s51kunk1KGGOQqY8PI4ET\/MztEBiQqCUxJ\/Cj8\/4u1W7s77+zf7Pub0atDsmtIhI8pUGXzgrDZK8cbhWRSVV1mkAO0rsi1fU5dIvWttSjv5NMSCCK3ksSfIV4lYyxkxoUCo8LM0ipHOhB2LtA2+fXUOreJ9dtbbTri71BLqO8Nv5tlGgYszu+1m2K6FycSEKynkIpCrXbTp9XsYykd\/e+MooPHFvA1jc6vPaInl6lp2nQyzzI6CVIweeSm8l0KghpAIxw65+jeJF1bUf7Gl0h9RvIlkWC11Rnikuo42lOy4Z5Csjg7SuUYK4kAVBhxStrie08deK7jT9IcabHarFqm77PcTQxCL96cNIVeRmQk5dhncXV24DPiLd61d2EMet21kk9rdRlYHug12Y3hjBxsRA0ZfeGZQfnOFwE5pQV1FdhX6noHii1fxBrkFrbmzeVI5Vs5UVvMjIZRIyz4ZYXDJtyInKFAN8bSoKj0XSZ7jTNOv9HvkutPt4L23ghe4aFhHJLGBC0w80kR+W4EiEZ2R7TjJPPJd6lqPiltO1ufTJdNnjm1Kxlvybm2jjy28YZE80Dkq2V2oAVYoSr8Nq9hqC22qX39n6Vb6bZGTTUSKzgB8wBMglmLlwAW3q0hDK2DtJNTGm2uW43LqdHL440HSvE1ncaPeXd9FbFFkmMcdtmEqoYRhWSNuEjXa8O4heHG1Cs+leLfCN3pjRSXI0r+zEBgQxu0csRjKhY0VgysJmWVhuZtyK3mMEDI\/S9TsIPDOkNrGuQaHb2Qh\/s5IdOxcyqcNI8sTrJuVigZZEOwvhiNyhV57UYI\/EPxCnhjuPEFk93alL9tR0xZZmIUYXyIlGRtEZBxkHDcYBrRRi9H0\/rsTdnYXnj7wxruhXL6nJcXWnLA8arqFn5ji6KYTiOMIGIV2BEynLuMKApXJsPEsNxc380kly2j2kAeK6s7OSeCW4kiiSW2kEqFmjkky2GZNzHczFirLzXxD8MW\/hPV7S\/wBO1q+nuLrF2ouo3S5jzyJC+BliwYnIVlOMjnNa+r3EGkeAv7EsbPVksr0JNLNdxp9ilIt2fMEpKNlnWNwpIOVxszujYUIWXL1C7vqbVn471hrPSRbaN4mnnnngurvdC8iXUSqPPkhwQygyOuEU+XhV4AdlPTeEdciNpaWb6Ld6fYXJt5IZVmjjWNyDjcsbjakkkR2sqIkhkxs5Jfzq+1LXPCOgS6XeafYWVzCsSGfbP9t3MjeXIlxGAmFBkRRu+6jpyM52bKePWNKhg17xfdxu9\/HfE3E8EDNC6lo5rZpE\/dsj4VlRyF2SgZ3AGJ01bRaFJ6nc\/wBv3VpdWNxrGpWcdrqMjXdoyghLOBQoxK6v5bqyuo3FsCWRdokGNuPY2kVnbfuLq\/hvpVif7HaQAXFuvmI7OxKNtYW8duHgUCMYVAv7yOpz4nie0u9b03WrCS20qAXV3C6ASXMpXygkrxsI\/MJhZVYblO9SEIWNmfdQ3XhaCDT7b\/Q7K3jgWEJcGQTLFOu3CRqJBLK0gSXEbLiRTuJXY+SVtCh+s366V4M0N7LVbD7Lazxst7BcmG1eOEk+TkSu7FkUpgCbJU5UHBUq9fxW2q3q6Z4ss9KePd5MFxLbMqzM6kNHE7n5JCfKcFSfvMi7jEz0Uk6dvfTC0vsmXpsVxq93HZGF9OuAz\/2pNaWrxMty7W87x+YqoVDLHKm5XYFREzMzMmaep6VdXXjKx1JdWmS0SKWJpPOV5bOW4lZFicI5Zh5pePKkBQuAysm+m3vh2ebTv7Y1WzsLNrhjFeM9\/PavHENsfkz3DMfNVhHGpYKQT90HzPNTh5r66s9Lt7GM2dlZfYkvW8m8vY1gYskm2BZLgRyTIskU231OOSOdYRvs\/wCupLdty\/dWGqWfjrUxFot60d7NbhLKDU4beSQoUcByGlZpAVV3dWDgMXYqHrcn0Hxd4v1fSINfVNPsNOki8zTY7pmluNuzfKrFsuBvQF95K5OCz5DZGpeGvGU3iCH+y9V1iyN5H9+6vpsyIsjAOqrukSIebF8knzoZTnIV3GpbaNaPY3+n+JPEWsXF3BJGr2OsXUkcUxUeYw\/dSuCmySJmYFjEUDt8p2mm9E09RJGN46srSWB9fsNV\/ty8s1WK\/vkgEcUu5VRkLxhY2BDgAKxkUEglhgxUvC+oabf2i6XqmoTZlRrJW0wgvKJv3shlScfMdyhQYV3kpt5zFu1774ZeGrbVoZxfTrospkheeUtFHbvCVhkDylSA7OXdSQqkxlADvBW3feGbbTJYWutY1vQvDTzoLa2F20QDeb8rKrlgg+eOUl2EgMUp8sfwUpx5VG4rO9znPCvjzU9P8P6noOkaabuMLLcfa4gttNDAqjfI20Eb9oOGJJDEfe4UwaK9rPqj6h4rvdQh\/sK3gTT7nTFjuIY\/KI2I0iK6E5ZOvGSQSOBXQaj4XuNV006vfX8N\/aWPmR+RdS3dy8LImW84iUGPIyz8Iw8qICMM5iqFdF0jR5tQt530FdVjxKLLU7WOBkAQKIkczyRBvLcMC6uGZgWLMjbHzQ1tuwsxNKs9SW6i8S+KZJ4rfViyLJcSxMk9u8EjeSZPtEfl71LYGwKrhPuj5ax08PXun30+gQWdvdw7lvLS+dXuYgJYsjYBE3mk7cZRPmCOxX5FaGw6S6ToF7Bf+BknVGmhi1Ce2NsYbXCAEu0CK0hCnZI3z7z93na13UPCieH47S41DRtE+y3KyzRQQGaS4UC1zMWjmaNmjARmClg8buuGJ+Vi9nr\/AF+IWKun3994ht5rnQ9ItEhh08C7hcwi2gmiyRMN+PJ+WWQgLsG8M2WAet\/TrTR9F0i1g8Q2cdtbRTRmB44TazTyxvcrDI53pLFI4+YGTMaiMncpY7INM8Ox3ugW103h+20pWgt5zczWsVypMgkVGi6lhtEY8phI7s6DCuTOx\/wjJ1DxnA9p4d0SJSwlinMbw2t0ECq8RRg6rIQJ90Plq8ZTdubaSZk07rb+u40mP0DUL+TR2vL271K108x38xv7VYRPdl8Ts0bMWWL5VIMUe0h1Jy22Qxa97pus6HpWsx6ZoSWgup\/PXU7u+c\/ZfNVEMpHmSsJlEs2+UFV2x7ssOKz7vwRodvNf2sOizSRNZTXdmLe2c3AYoMIxcv5asi7V81C3mCYrsIUGAa3r\/hnwvp2nXeiw3NvLc2rWl\/DCJWnAceQrJFIoeUJArArI4wsat97dUuzd4j23N7+wdD0G9jin8R2F1qMTQ+fZXCxBrgFWRk8lBuYeVNIIo1GRuVSXUIqlZ2mtN4i0uKfR4ftGkaHcwG3WOWR1DRsJSqLIrSvgMsZbCsqx5jDCRoaKzkpfP7ilYnjSG3j1ptbkmgSC5WS1VLuOW5E1mryvcok+WG9RGAGMjbGUE+XtIgv5PDmn+NLbzG1KBpY1u4pUhkW9lLutr5TPNunfILkKnllVGVDNgqUVpBcxLdjqdZ1+00a3PltDfajodswuvLeRrmIGNPnEJbdLEd6M26QAbQSxZeMdtWHiBGFl4V1vWJpp5JXOrwJHDZrny2iAYqkgVo0byc8kbiwdcqUVnypQ5uo73djB8MWusaj4gslt76z1ImygkXW3uftUls0cjSMq5EUm1t+x4+WVZk3NjCm7L4U1LU9UjTUpdmpWm1pfslpGZbhCsgH7xg+9CkbwjzpFDAqZFBXExRTnNqTSBK6JDp2irp13d6FoKa1cPOJZpRK0yXwi5mkSK4Ztx35j3rvKifKs7bkojgsvCVrcWaTaU15b6fFqFmdStJVgt4kuZGDsrZkSTNy6IgLNmMEnLbaKKpXcuVi6XOlfTUn17UZLKeG8tr+QWTJBtMdhLGss26QRgOHWbLbvMVt0\/YqpOI2kPaa5dWtk01lcad515ONHsGiaS0dmeKKLI8p5S0UQb5AXChNx2PkorKLd36FNE1umqaYv9mwyabcJZ2zi1GmXbTXUJjlkUSiOfzNiBS8TBFlcH5RuwFWS4m0nVNOsGuNDtkTV2n8nUF0USwxXEvliOYl8HBZlTc6DzSob5VxkoqlrqIx9fS98P+HGL2um6joMkbNfLOH8\/f5rGSKSUGQHEsrKmH3q7AlmCSM2ibaLVfEdk84cpet\/Z9lqFlci7truzgdpmim3cguo2MdzFmjfouQ5RTv7vN11DrYnltdJfTtZuzZPaGzgfyLy7tg95PbLmW42QzxrgETvEXO4sWy5J5JRRW1GCle5E5WP\/9k="), 31 | // ], 32 | // ]; 33 | 34 | // sleep(2); 35 | // http_response_code(200); 36 | // echo json_encode([ 37 | // "message" => "Captcha has been successfully verified.", 38 | // "data" => null 39 | // ]); 40 | 41 | // sleep(2); 42 | // http_response_code(400); 43 | // echo json_encode([ 44 | // "message" => "Your code is invalid, please try again.", 45 | // "data" => $result 46 | // ]); 47 | 48 | // exit(); 49 | // test 50 | 51 | #endregion 52 | 53 | #region Parse request body 54 | 55 | $raw_json = file_get_contents("php://input"); 56 | 57 | if (strlen($raw_json) > MAX_JSON_SIZE) { 58 | http_response_code(413); 59 | echo json_encode([ 60 | "message" => "Payload too large" 61 | ]); 62 | exit(); 63 | } 64 | 65 | $parsed_json = json_decode($raw_json, true); 66 | 67 | if (json_last_error() !== JSON_ERROR_NONE) { 68 | error_log(json_last_error_msg()); 69 | error_log($raw_json); 70 | http_response_code(400); 71 | echo json_encode([ 72 | "message" => "Invalid JSON response. Please check error log." 73 | ]); 74 | exit(); 75 | } 76 | 77 | #endregion 78 | 79 | #region Parse and validate all request input 80 | 81 | $id = $parsed_json["id"] ?? null; 82 | $decrypted_id = decrypt_data($id); 83 | $decrypted_id = (int)$decrypted_id; 84 | 85 | if (!isset($decrypted_id) || !is_int($decrypted_id) || $decrypted_id <= 0) { 86 | error_log("Decryption failed for ID: $id"); 87 | http_response_code(400); 88 | echo json_encode([ 89 | "message" => "Invalid or malformed credential ID" 90 | ]); 91 | exit(); 92 | } 93 | 94 | $id = $decrypted_id; 95 | 96 | $captcha_code = $parsed_json["captchaCode"] ?? null; 97 | 98 | if (!$captcha_code) { 99 | error_log("Invalid captcha code: $captcha_code"); 100 | http_response_code(400); 101 | echo json_encode([ 102 | "message" => "Invalid captcha code" 103 | ]); 104 | exit(); 105 | } 106 | 107 | #endregion 108 | 109 | #region Fetch and validate credentials from database/config 110 | 111 | if (USE_DATABASE) { 112 | 113 | $pdo_statement = $pdo->prepare(" 114 | SELECT final_key AS finalKey, token AS token, client_device_id AS clientDeviceId 115 | FROM credentials 116 | WHERE deleted_at IS NULL AND id = ? 117 | ORDER BY id ASC 118 | "); 119 | 120 | $pdo_statement->execute([$id]); 121 | 122 | $credential = $pdo_statement->fetch(PDO::FETCH_ASSOC); 123 | } else { 124 | $credentials = json_decode(GTC_CREDENTIALS, true); 125 | 126 | if (json_last_error() !== JSON_ERROR_NONE) { 127 | error_log(json_last_error()); 128 | http_response_code(400); 129 | echo json_encode([ 130 | "message" => "Invalid credentials" 131 | ]); 132 | exit(); 133 | } 134 | 135 | $credential = array_filter($credentials, function ($credential) use ($id) { 136 | return $credential["id"] === $id; 137 | }); 138 | 139 | $credential = reset($credential); 140 | } 141 | 142 | if (!$credential || !isset($credential["finalKey"], $credential["token"])) { 143 | http_response_code(400); 144 | echo json_encode([ 145 | "message" => "Credential not found or incomplete" 146 | ]); 147 | exit(); 148 | } 149 | 150 | $final_key = $credential["finalKey"] ?? null; 151 | $token = $credential["token"] ?? null; 152 | 153 | if (empty($final_key) || empty($token)) { 154 | http_response_code(400); 155 | echo json_encode([ 156 | "message" => "Invalid final key or token" 157 | ]); 158 | exit(); 159 | } 160 | 161 | #endregion 162 | 163 | #region Call GetContact API Verify Code 164 | 165 | $api_request = (object)[ 166 | "clientDeviceId" => $credential["clientDeviceId"], 167 | "validationCode" => $captcha_code, 168 | "finalKey" => $final_key, 169 | "token" => $token 170 | ]; 171 | 172 | $api_response = getcontact_call_api_verify_code($api_request); 173 | 174 | #endregion 175 | 176 | #region Parse, decrypt, and decode API response 177 | 178 | $api_response_body = json_decode($api_response->body, false); 179 | 180 | if (json_last_error() !== JSON_ERROR_NONE) { 181 | log_api_transaction($api_request, $api_response, "verify code: decode body"); 182 | error_log(json_last_error_msg()); 183 | error_log($api_response->body); 184 | http_response_code(400); 185 | echo json_encode([ 186 | "message" => "Invalid JSON response. Please check error log." 187 | ]); 188 | exit(); 189 | } 190 | 191 | $decrypted_data_raw = getcontact_decrypt($api_response_body->data, $final_key); 192 | $decrypted_body = json_decode($decrypted_data_raw, false); 193 | 194 | if (json_last_error() !== JSON_ERROR_NONE) { 195 | log_api_transaction($api_request, $api_response, "verify code: decode body data"); 196 | error_log(json_last_error_msg()); 197 | error_log($decrypted_data_raw); 198 | http_response_code(400); 199 | echo json_encode([ 200 | "message" => "Invalid JSON response. Please check error log." 201 | ]); 202 | exit(); 203 | } 204 | 205 | if ($decrypted_body == null) { 206 | log_api_transaction($api_request, $api_response, "verify code: body null"); 207 | http_response_code(500); 208 | echo json_encode([ 209 | "message" => "Could not verify captcha (invalid response body)" 210 | ]); 211 | exit(); 212 | } 213 | 214 | #endregion 215 | 216 | #region Validate http status code 217 | 218 | $meta_http_status_code_path = "meta.httpStatusCode"; 219 | 220 | if (!has_nested_property($decrypted_body, $meta_http_status_code_path)) { 221 | log_api_transaction($api_request, $api_response, "$source_type: missing $meta_http_status_code_path"); 222 | http_response_code(500); 223 | echo json_encode([ 224 | "message" => "Unexpected API response structure" 225 | ]); 226 | exit(); 227 | } 228 | 229 | $http_code = get_nested_value($decrypted_body, $meta_http_status_code_path); 230 | $api_response_http_code = $api_response->httpCode ?? null; 231 | 232 | /** 233 | * 200 -> success 234 | * 403 -> failed but return a new image 235 | * 236 | * so, we have to permit those http status codes 237 | */ 238 | 239 | if ( 240 | !in_array($http_code, [200, 403]) || 241 | !in_array($api_response_http_code, [200, 403]) // permit 200 & 403 242 | ) { 243 | log_api_transaction($api_request, $api_response, "$source_type: invalid http status"); 244 | 245 | $error_message_path = "meta.errorMessage"; 246 | $error_message = "An error occurred. Please contact administrator."; 247 | 248 | if (has_nested_property($decrypted_body, $error_message_path)) { 249 | $error_message = get_nested_value($decrypted_body, $error_message_path); 250 | error_log(json_encode($decrypted_body, JSON_PRETTY_PRINT)); 251 | } else { 252 | error_log("$source_type: missing $error_message_path"); 253 | } 254 | 255 | // if (!has_nested_property($decrypted_body, $error_message_path)) { 256 | // error_log("$source_type: missing $error_message_path"); 257 | // } 258 | 259 | http_response_code(500); 260 | echo json_encode([ 261 | "message" => "Invalid response ($http_code: $error_message)" 262 | ]); 263 | exit(); 264 | } 265 | 266 | #endregion 267 | 268 | #region Validate and return result 269 | 270 | if ($api_response_http_code !== 200) { 271 | $image_path = "result.image"; 272 | 273 | if (!has_nested_property($decrypted_body, $image_path)) { 274 | log_api_transaction($api_request, $api_response, "verify code: missing $image_path"); 275 | http_response_code(500); 276 | echo json_encode([ 277 | "message" => "Unexpected API response structure" 278 | ]); 279 | exit(); 280 | } 281 | 282 | $result = [ 283 | "captcha" => [ 284 | "image" => "", 285 | ], 286 | ]; 287 | 288 | $result["captcha"]["image"] = get_nested_value($decrypted_body, $image_path); 289 | 290 | http_response_code(403); 291 | echo json_encode([ 292 | "message" => "Failed to verify captcha. Please try again with new captcha.", 293 | "data" => $result 294 | ]); 295 | exit(); 296 | } else { 297 | http_response_code(200); 298 | echo json_encode([ 299 | "message" => "Captcha has been successfully verified." 300 | ]); 301 | exit(); 302 | } 303 | 304 | #endregion 305 | 306 | } catch (\Exception $e) { 307 | error_log("Error GetContact API Verify Captcha: " . $e->getMessage()); 308 | http_response_code(500); 309 | echo json_encode([ 310 | "message" => "Internal server error" 311 | ]); 312 | exit(); 313 | } 314 | -------------------------------------------------------------------------------- /src/pages/dashboard/manage_credentials.php: -------------------------------------------------------------------------------- 1 | prepare("SELECT updated_at FROM credentials WHERE id = ? AND deleted_at IS NULL"); 39 | $pdo_statement->execute([$id]); 40 | $current = $pdo_statement->fetch(PDO::FETCH_ASSOC); 41 | 42 | if (!$current || $current["updated_at"] !== $_POST["updated_at"]) { 43 | // data has changed 44 | $_SESSION["error"] = "The data has been updated elsewhere. Please reload the page."; 45 | header("Location: " . URL_PREFIX . "/dashboard/credentials/manage"); 46 | exit(); 47 | } 48 | 49 | $pdo_statement = $pdo->prepare("UPDATE credentials SET description = ?, final_key = ?, token = ?, client_device_id = ?, updated_by = ?, updated_at = ? WHERE id = ?"); 50 | 51 | $pdo_statement->bindValue(1, $description); 52 | $pdo_statement->bindValue(2, $final_key); 53 | $pdo_statement->bindValue(3, $token); 54 | $pdo_statement->bindValue(4, $client_device_id, is_null($client_device_id) ? PDO::PARAM_NULL : PDO::PARAM_STR); 55 | $pdo_statement->bindValue(5, $user); 56 | $pdo_statement->bindValue(6, $now); 57 | $pdo_statement->bindValue(7, $id); 58 | 59 | $pdo_statement->execute(); 60 | } else { 61 | 62 | // CREATE 63 | $pdo_statement = $pdo->prepare("INSERT INTO credentials (description, final_key, token, client_device_id, created_by, created_at) VALUES (?, ?, ?, ?, ?, ?)"); 64 | 65 | $pdo_statement->bindValue(1, $description); 66 | $pdo_statement->bindValue(2, $final_key); 67 | $pdo_statement->bindValue(3, $token); 68 | $pdo_statement->bindValue(4, $client_device_id, is_null($client_device_id) ? PDO::PARAM_NULL : PDO::PARAM_STR); 69 | $pdo_statement->bindValue(5, $user); 70 | $pdo_statement->bindValue(6, $now); 71 | 72 | $pdo_statement->execute(); 73 | } 74 | 75 | header("Location: " . URL_PREFIX . "/dashboard/credentials/manage"); 76 | exit(); 77 | 78 | break; 79 | } 80 | 81 | // DELETE (Soft Delete) 82 | if (isset($_GET["delete"])) { 83 | $id = $_GET["delete"]; 84 | $updated_at = $_GET["updated_at"]; 85 | $user = "admin"; 86 | $now = date("Y-m-d H:i:s"); 87 | 88 | $pdo_statement = $pdo->prepare("SELECT updated_at FROM credentials WHERE id=? AND deleted_at IS NULL"); 89 | $pdo_statement->execute([$id]); 90 | $current = $pdo_statement->fetch(PDO::FETCH_ASSOC); 91 | 92 | if (!$current || $current["updated_at"] !== $updated_at) { 93 | $_SESSION["error"] = "The data has been updated or deleted elsewhere."; 94 | header("Location: " . URL_PREFIX . "/dashboard/credentials/manage"); 95 | exit(); 96 | } 97 | 98 | $pdo_statement = $pdo->prepare("UPDATE credentials SET deleted_by=?, deleted_at=?, updated_at=? WHERE id=?"); 99 | $pdo_statement->execute([$user, $now, $now, $id]); 100 | 101 | header("Location: " . URL_PREFIX . "/dashboard/credentials/manage"); 102 | exit(); 103 | } 104 | 105 | // Fetch Data 106 | $pdo_statement = $pdo->query("SELECT * FROM credentials WHERE deleted_at IS NULL ORDER BY id DESC"); 107 | $data = $pdo_statement->fetchAll(PDO::FETCH_ASSOC); 108 | 109 | $csrf_token = csrf_token_generate(); 110 | ?> 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | GetContact PHP Web App | Admin Dashboard 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | "> 159 | 160 | 161 | 162 | 163 | 164 | 221 | 222 | 223 |
224 | 225 | 226 |
227 |
228 | 229 | 230 | 235 | 236 | 237 |
238 | 241 |
242 | 243 |
244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 268 | 274 | 278 | 282 | 283 | 284 | 285 |
IDDescriptionCredential DataHeader DataCreated At/Updated AtAction
261 |
262 |
Final Key
263 |
264 |
Token
265 |
266 |
267 |
269 |
270 |
Client Device ID
271 |
272 |
273 |
275 |
admin

276 |
admin 277 |
279 | 280 | &updated_at=" onclick="return confirm('Yakin ingin hapus data ini?')" class="btn btn-danger btn-sm">Delete 281 |
286 |
287 | 288 |
289 |
290 | 291 |
292 | 293 | 294 | 331 | 332 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 392 | 393 | 394 | -------------------------------------------------------------------------------- /public/js/qrcode.min.js: -------------------------------------------------------------------------------- 1 | var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push('');g.push("")}g.push("
"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); -------------------------------------------------------------------------------- /src/pages/dashboard/verify_captcha.php: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | GetContact PHP Web App | Admin Dashboard 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | "> 65 | 66 | 67 | 68 | 69 | 70 | 128 | 129 | 130 |
131 | 132 | 133 |
134 |
135 | 136 | 140 | 141 |
142 |
143 |
144 | 145 | 146 |
147 | 148 |
149 | 152 |
153 |
154 |
155 | 156 | 157 |
158 | 159 |
160 | 161 |
162 |
163 |
164 |
165 | 166 | 167 |
168 |
169 | 170 |
171 |
172 | 173 |
174 |
175 |
176 |
177 |
178 | 179 |
180 | 181 | 182 |
183 |
184 | 185 |
186 |
187 | 188 | 189 |
190 |
191 | 192 |
193 |
194 | 195 | 196 |
197 |
198 | 199 |
200 |
201 | 202 |
203 |
204 | 205 |
206 |
207 | 208 |
209 | 210 |
211 |
212 | Made with ❤️ naufalist 213 |
214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 609 | 610 | 611 | -------------------------------------------------------------------------------- /src/apis/generate_credentials.php: -------------------------------------------------------------------------------- 1 | "Method not allowed"]); 16 | exit(); 17 | } 18 | 19 | $phase = $_GET['phase'] ?? null; 20 | 21 | if (!is_numeric($phase) || $phase <= 0) { 22 | http_response_code(400); 23 | echo json_encode([ 24 | "message" => "Invalid phase number" 25 | ]); 26 | exit(); 27 | } 28 | 29 | switch ($phase) { 30 | case 1: 31 | 32 | #region Parse request body 33 | 34 | $raw_json = file_get_contents("php://input"); 35 | 36 | if (strlen($raw_json) > MAX_JSON_SIZE) { 37 | http_response_code(413); 38 | echo json_encode([ 39 | "message" => "Payload too large" 40 | ]); 41 | exit(); 42 | } 43 | 44 | $parsed_json = json_decode($raw_json, true); 45 | 46 | if (json_last_error() !== JSON_ERROR_NONE) { 47 | error_log(json_last_error_msg()); 48 | error_log($raw_json); 49 | http_response_code(400); 50 | echo json_encode([ 51 | "message" => "Invalid JSON response. Please check error log." 52 | ]); 53 | exit(); 54 | } 55 | 56 | #endregion 57 | 58 | #region Parse and validate all request input 59 | 60 | $phone_number = trim(htmlspecialchars($parsed_json['phoneNumber'])) ?? null; 61 | 62 | if (!$phone_number) { 63 | http_response_code(400); 64 | echo json_encode([ 65 | "message" => "Invalid phone number" 66 | ]); 67 | exit(); 68 | } 69 | 70 | if (strpos($phone_number, "0") === 0) { 71 | $phone_number = "+62" . substr($phone_number, 1); 72 | } else if ( 73 | strpos($phone_number, "62") === 0 74 | ) { 75 | $phone_number = "+" . $phone_number; 76 | } else if (strpos($phone_number, "-") !== false || strpos($phone_number, " ") !== false) { 77 | $phone_number = str_replace(["-", " "], "", $phone_number); 78 | } else { 79 | http_response_code(400); 80 | echo json_encode([ 81 | "message" => "Invalid phone number" 82 | ]); 83 | exit(); 84 | } 85 | 86 | #endregion 87 | 88 | #region Generate client device id, client private key, client public key 89 | 90 | // generate client device id 91 | $client_device_id = strtolower(bin2hex(random_bytes(8))); 92 | 93 | // generate client private key 94 | $client_private_key = getcontact_generate_client_private_key(); 95 | 96 | // generate client public key 97 | $client_public_key = getcontact_generate_client_public_key($client_private_key); 98 | 99 | #endregion 100 | 101 | #region Call v2.8/register 102 | 103 | $api_request = (object)[ 104 | "clientPublicKey" => $client_public_key, 105 | "clientDeviceId" => $client_device_id, 106 | ]; 107 | 108 | $api_response = getcontact_call_api_register($api_request); 109 | 110 | #endregion 111 | 112 | #region Validate http status code (201) 113 | 114 | if ($api_response->httpCode !== 201) { 115 | log_api_transaction($api_request, $api_response, "register: call"); 116 | http_response_code(400); 117 | echo json_encode([ 118 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 119 | ]); 120 | exit(); 121 | } 122 | 123 | #endregion 124 | 125 | #region Parse and decode API response 126 | 127 | $api_response_body = json_decode($api_response->body, false); 128 | 129 | if (json_last_error() !== JSON_ERROR_NONE) { 130 | log_api_transaction($api_request, $api_response, "register: decode body"); 131 | error_log(json_last_error_msg()); 132 | error_log($api_response->body); 133 | http_response_code(400); 134 | echo json_encode([ 135 | "message" => "Invalid JSON response. Please check error log." 136 | ]); 137 | exit(); 138 | } 139 | 140 | #endregion 141 | 142 | #region Parse and validate token, server public key 143 | 144 | // parsing token 145 | $token = get_nested_value($api_response_body, 'result.token'); 146 | if (empty($token)) { 147 | http_response_code(500); 148 | echo json_encode([ 149 | "message" => "Invalid token" 150 | ]); 151 | exit(); 152 | } 153 | 154 | // parsing server public key 155 | $server_public_key = get_nested_value($api_response_body, 'result.serverKey'); 156 | // permit 0 => !isset($server_public_key) || $server_public_key === null || $server_public_key === '' 157 | if (empty($server_public_key)) { 158 | http_response_code(500); 159 | echo json_encode([ 160 | "message" => "Invalid server key" 161 | ]); 162 | exit(); 163 | } 164 | 165 | #endregion 166 | 167 | #region Generate final key 168 | 169 | // generate final key 170 | $final_key = getcontact_generate_final_key($client_private_key, $server_public_key); 171 | 172 | if (empty($final_key)) { 173 | http_response_code(500); 174 | echo json_encode([ 175 | "message" => "Invalid final key" 176 | ]); 177 | exit(); 178 | } 179 | 180 | #endregion 181 | 182 | #region Log client device id, fk and token for debugging 183 | 184 | error_log($client_device_id); 185 | error_log($final_key); 186 | error_log($token); 187 | 188 | #endregion 189 | 190 | #region Call v2.8/init-basic 191 | 192 | $api_request = (object)[ 193 | "clientDeviceId" => $client_device_id, 194 | "finalKey" => $final_key, 195 | "token" => $token 196 | ]; 197 | 198 | $api_response = getcontact_call_api_init_basic($api_request); 199 | 200 | #endregion 201 | 202 | #region Validate http status code (201) 203 | 204 | if ($api_response->httpCode !== 201) { 205 | log_api_transaction($api_request, $api_response, "init basic: call"); 206 | http_response_code(400); 207 | echo json_encode([ 208 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 209 | ]); 210 | exit(); 211 | } 212 | 213 | #endregion 214 | 215 | #region Decode API response 216 | 217 | $api_response_body = json_decode($api_response->body, false); 218 | 219 | if (json_last_error() !== JSON_ERROR_NONE) { 220 | log_api_transaction($api_request, $api_response, "init basic: decode body"); 221 | error_log(json_last_error_msg()); 222 | error_log($api_response->body); 223 | http_response_code(400); 224 | echo json_encode([ 225 | "message" => "Invalid JSON response. Please check error log." 226 | ]); 227 | exit(); 228 | } 229 | 230 | // no need to parse response body, nothing important 231 | 232 | #endregion 233 | 234 | #region Call v2.8/ad-settings 235 | 236 | $api_request = (object)[ 237 | "clientDeviceId" => $client_device_id, 238 | "finalKey" => $final_key, 239 | "token" => $token 240 | ]; 241 | 242 | $api_response = getcontact_call_api_ad_settings($api_request); 243 | 244 | #endregion 245 | 246 | #region Validate http status code (200) 247 | 248 | if ($api_response->httpCode !== 200) { 249 | log_api_transaction($api_request, $api_response, "ad settings: call"); 250 | http_response_code(400); 251 | echo json_encode([ 252 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 253 | ]); 254 | exit(); 255 | } 256 | 257 | #endregion 258 | 259 | #region Decode API response 260 | 261 | $api_response_body = json_decode($api_response->body, false); 262 | 263 | if (json_last_error() !== JSON_ERROR_NONE) { 264 | log_api_transaction($api_request, $api_response, "ad settings: decode body"); 265 | error_log(json_last_error_msg()); 266 | error_log($api_response->body); 267 | http_response_code(400); 268 | echo json_encode([ 269 | "message" => "Invalid JSON response. Please check error log." 270 | ]); 271 | exit(); 272 | } 273 | 274 | // no need to parse response body, nothing important 275 | 276 | #endregion 277 | 278 | #region Call v2.8/init-intro 279 | 280 | $api_request = (object)[ 281 | "clientDeviceId" => $client_device_id, 282 | "finalKey" => $final_key, 283 | "token" => $token 284 | ]; 285 | 286 | $api_response = getcontact_call_api_init_intro($api_request); 287 | 288 | #endregion 289 | 290 | #region Validate http status code (201) 291 | 292 | if ($api_response->httpCode !== 201) { 293 | log_api_transaction($api_request, $api_response, "init intro: call"); 294 | http_response_code(400); 295 | echo json_encode([ 296 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 297 | ]); 298 | exit(); 299 | } 300 | 301 | #endregion 302 | 303 | #region Decode API response 304 | 305 | $api_response_body = json_decode($api_response->body, false); 306 | 307 | if (json_last_error() !== JSON_ERROR_NONE) { 308 | log_api_transaction($api_request, $api_response, "init intro: decode body"); 309 | error_log(json_last_error_msg()); 310 | error_log($api_response->body); 311 | http_response_code(400); 312 | echo json_encode([ 313 | "message" => "Invalid JSON response. Please check error log." 314 | ]); 315 | exit(); 316 | } 317 | 318 | // no need to parse response body, nothing important 319 | 320 | #endregion 321 | 322 | #region Generate (random) fullname and email 323 | 324 | // generate random fullname and email 325 | $fullname = "User" . random_int(1000, 999999); 326 | $email = "user" . random_int(10000000, 99999999) . "@gmail.com"; 327 | 328 | #endregion 329 | 330 | #region Call v2.8/email-code-validate/start 331 | 332 | $api_request = (object)[ 333 | "email" => $email, 334 | "fullname" => $fullname, 335 | "clientDeviceId" => $client_device_id, 336 | "finalKey" => $final_key, 337 | "token" => $token 338 | ]; 339 | 340 | $api_response = getcontact_call_api_email_code_validate_start($api_request); 341 | 342 | #endregion 343 | 344 | #region Validate http status code (200) 345 | 346 | if ($api_response->httpCode !== 200) { 347 | log_api_transaction($api_request, $api_response, "email code validate/start: call"); 348 | http_response_code(400); 349 | echo json_encode([ 350 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 351 | ]); 352 | exit(); 353 | } 354 | 355 | #endregion 356 | 357 | #region Decode API response 358 | 359 | $api_response_body = json_decode($api_response->body, false); 360 | 361 | if (json_last_error() !== JSON_ERROR_NONE) { 362 | log_api_transaction($api_request, $api_response, "email code validate/start: decode body"); 363 | error_log(json_last_error_msg()); 364 | error_log($api_response->body); 365 | http_response_code(400); 366 | echo json_encode([ 367 | "message" => "Invalid JSON response. Please check error log." 368 | ]); 369 | exit(); 370 | } 371 | 372 | // no need to parse response body, nothing important 373 | 374 | #endregion 375 | 376 | #region Call v2.8/country 377 | 378 | $api_request = (object)[ 379 | "clientDeviceId" => $client_device_id, 380 | "finalKey" => $final_key, 381 | "token" => $token 382 | ]; 383 | 384 | $api_response = getcontact_call_api_country($api_request); 385 | 386 | #endregion 387 | 388 | #region Validate http status code (200) 389 | 390 | if ($api_response->httpCode !== 200) { 391 | log_api_transaction($api_request, $api_response, "country: call"); 392 | http_response_code(400); 393 | echo json_encode([ 394 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 395 | ]); 396 | exit(); 397 | } 398 | 399 | #endregion 400 | 401 | #region Decode API response (decoded response data field already in decrypted) 402 | 403 | $api_response_body = json_decode($api_response->body, false); 404 | 405 | if (json_last_error() !== JSON_ERROR_NONE) { 406 | log_api_transaction($api_request, $api_response, "country: decode body"); 407 | error_log(json_last_error_msg()); 408 | error_log($api_response->body); 409 | http_response_code(400); 410 | echo json_encode([ 411 | "message" => "Invalid JSON response. Please check error log." 412 | ]); 413 | exit(); 414 | } 415 | 416 | // no need to parse response body, nothing important 417 | 418 | #endregion 419 | 420 | #region Call v2.8/validation-start 421 | 422 | $api_request = (object)[ 423 | "clientDeviceId" => $client_device_id, 424 | "finalKey" => $final_key, 425 | "token" => $token 426 | ]; 427 | 428 | $api_response = getcontact_call_api_validation_start($api_request); 429 | 430 | #endregion 431 | 432 | #region Validate http status code (200) 433 | 434 | if ($api_response->httpCode !== 200) { 435 | log_api_transaction($api_request, $api_response, "validation start: call"); 436 | http_response_code(400); 437 | echo json_encode([ 438 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 439 | ]); 440 | exit(); 441 | } 442 | 443 | #endregion 444 | 445 | #region Decode API response 446 | 447 | $api_response_body = json_decode($api_response->body, false); 448 | 449 | if (json_last_error() !== JSON_ERROR_NONE) { 450 | log_api_transaction($api_request, $api_response, "validation start: decode body"); 451 | error_log(json_last_error_msg()); 452 | error_log($api_response->body); 453 | http_response_code(400); 454 | echo json_encode([ 455 | "message" => "Invalid JSON response. Please check error log." 456 | ]); 457 | exit(); 458 | } 459 | 460 | // no need to parse response body, nothing important 461 | 462 | #endregion 463 | 464 | #region Generate outside phone number 465 | 466 | $outside_phone_number = $phone_number; 467 | if (strpos($phone_number, '+62') === 0) { 468 | $outside_phone_number = substr($phone_number, 3); // cut first 3 char (+62) 469 | } 470 | 471 | #endregion 472 | 473 | #region Call v2.0/init (VFK) 474 | 475 | $api_request = (object)[ 476 | "outsidePhoneNumber" => $outside_phone_number, 477 | "clientDeviceId" => $client_device_id, 478 | "finalKey" => VFK_FINAL_KEY, 479 | ]; 480 | 481 | $api_response = verifykit_call_api_init($api_request); 482 | 483 | #endregion 484 | 485 | #region Validate http status code (200) 486 | 487 | if ($api_response->httpCode !== 200) { 488 | log_api_transaction($api_request, $api_response, "vfk init: call"); 489 | http_response_code(400); 490 | echo json_encode([ 491 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 492 | ]); 493 | exit(); 494 | } 495 | 496 | #endregion 497 | 498 | #region Decode API response 499 | 500 | $api_response_body = json_decode($api_response->body, false); 501 | 502 | if (json_last_error() !== JSON_ERROR_NONE) { 503 | log_api_transaction($api_request, $api_response, "vfk init: decode body"); 504 | error_log(json_last_error_msg()); 505 | error_log($api_response->body); 506 | http_response_code(400); 507 | echo json_encode([ 508 | "message" => "Invalid JSON response. Please check error log." 509 | ]); 510 | exit(); 511 | } 512 | 513 | // no need to parse response body, nothing important 514 | 515 | #endregion 516 | 517 | #region Call v2.0/country (VFK) 518 | 519 | $api_request = (object)[ 520 | "clientDeviceId" => $client_device_id, 521 | "finalKey" => VFK_FINAL_KEY, 522 | ]; 523 | 524 | $api_response = verifykit_call_api_country($api_request); 525 | 526 | #endregion 527 | 528 | #region Validate http status code (200) 529 | 530 | if ($api_response->httpCode !== 200) { 531 | log_api_transaction($api_request, $api_response, "vfk country: call"); 532 | http_response_code(400); 533 | echo json_encode([ 534 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 535 | ]); 536 | exit(); 537 | } 538 | 539 | #endregion 540 | 541 | #region Decode API response (decoded response data field already in decrypted) 542 | 543 | $api_response_body = json_decode($api_response->body, false); 544 | 545 | if (json_last_error() !== JSON_ERROR_NONE) { 546 | log_api_transaction($api_request, $api_response, "vfk country: decode body"); 547 | error_log(json_last_error_msg()); 548 | error_log($api_response->body); 549 | http_response_code(400); 550 | echo json_encode([ 551 | "message" => "Invalid JSON response. Please check error log." 552 | ]); 553 | exit(); 554 | } 555 | 556 | // no need to parse response body, nothing important 557 | 558 | #endregion 559 | 560 | #region Return response 561 | 562 | http_response_code(200); 563 | echo json_encode([ 564 | "message" => "Data processed successfully", 565 | "data" => encrypt_data([ 566 | "phoneNumber" => $phone_number, 567 | "clientDeviceId" => $client_device_id, 568 | "token" => $token, 569 | "finalKey" => $final_key, 570 | ]) 571 | ]); 572 | exit(); 573 | 574 | #endregion 575 | 576 | break; 577 | 578 | case 2: 579 | 580 | #region Parse request body 581 | 582 | $raw_json = file_get_contents("php://input"); 583 | 584 | if (strlen($raw_json) > MAX_JSON_SIZE) { 585 | http_response_code(413); 586 | echo json_encode([ 587 | "message" => "Payload too large" 588 | ]); 589 | exit(); 590 | } 591 | 592 | $parsed_json = json_decode($raw_json, true); 593 | 594 | if (json_last_error() !== JSON_ERROR_NONE) { 595 | error_log(json_last_error_msg()); 596 | error_log($raw_json); 597 | http_response_code(400); 598 | echo json_encode([ 599 | "message" => "Invalid JSON response. Please check error log." 600 | ]); 601 | exit(); 602 | } 603 | 604 | #endregion 605 | 606 | #region Parse and validate all request input 607 | 608 | $parsed_json = $parsed_json['data'] ?? null; 609 | 610 | $parsed_json = decrypt_data($parsed_json); 611 | 612 | if (empty($parsed_json)) { 613 | http_response_code(400); 614 | echo json_encode([ 615 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (1)" 616 | ]); 617 | exit(); 618 | } 619 | 620 | $phone_number = trim(htmlspecialchars($parsed_json['phoneNumber'])) ?? null; 621 | 622 | if (!$phone_number) { 623 | http_response_code(400); 624 | echo json_encode([ 625 | // "message" => "Invalid phone number" 626 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (2)" 627 | ]); 628 | exit(); 629 | } 630 | 631 | $client_device_id = trim(htmlspecialchars($parsed_json['clientDeviceId'])) ?? null; 632 | 633 | if (!$client_device_id) { 634 | http_response_code(400); 635 | echo json_encode([ 636 | // "message" => "Invalid client device id" 637 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (3)" 638 | ]); 639 | exit(); 640 | } 641 | 642 | $final_key = trim(htmlspecialchars($parsed_json['finalKey'])) ?? null; 643 | 644 | if (!$final_key) { 645 | http_response_code(400); 646 | echo json_encode([ 647 | // "message" => "Invalid final key" 648 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (4)" 649 | ]); 650 | exit(); 651 | } 652 | 653 | $token = trim(htmlspecialchars($parsed_json['token'])) ?? null; 654 | 655 | if (!$token) { 656 | http_response_code(400); 657 | echo json_encode([ 658 | // "message" => "Invalid token" 659 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (5)" 660 | ]); 661 | exit(); 662 | } 663 | 664 | #endregion 665 | 666 | #region Call v2.0/start (VFK) 667 | 668 | $api_request = (object)[ 669 | "phoneNumber" => $phone_number, 670 | "clientDeviceId" => $client_device_id, 671 | "finalKey" => VFK_FINAL_KEY, 672 | ]; 673 | 674 | $api_response = verifykit_call_api_start($api_request); 675 | 676 | #endregion 677 | 678 | #region Validate http status code (200) 679 | 680 | if ($api_response->httpCode !== 200) { 681 | log_api_transaction($api_request, $api_response, "vfk start: call"); 682 | http_response_code(400); 683 | echo json_encode([ 684 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 685 | ]); 686 | exit(); 687 | } 688 | 689 | #endregion 690 | 691 | #region Decode API response 692 | 693 | $api_response_body = json_decode($api_response->body, false); 694 | 695 | if (json_last_error() !== JSON_ERROR_NONE) { 696 | log_api_transaction($api_request, $api_response, "vfk start: decode body"); 697 | error_log(json_last_error_msg()); 698 | error_log($api_response->body); 699 | http_response_code(400); 700 | echo json_encode([ 701 | "message" => "Invalid JSON response. Please check error log." 702 | ]); 703 | exit(); 704 | } 705 | 706 | #endregion 707 | 708 | #region Decrypt API response 709 | 710 | $decrypted_data_raw = getcontact_decrypt($api_response_body->data, VFK_FINAL_KEY); 711 | $decrypted_body = json_decode($decrypted_data_raw, false); 712 | 713 | if (json_last_error() !== JSON_ERROR_NONE) { 714 | log_api_transaction($api_request, $api_response, "vfk start: decode body data"); 715 | error_log(json_last_error_msg()); 716 | error_log($decrypted_data_raw); 717 | http_response_code(400); 718 | echo json_encode([ 719 | "message" => "Invalid JSON response. Please check error log." 720 | ]); 721 | exit(); 722 | } 723 | 724 | if ($decrypted_body == null) { 725 | log_api_transaction($api_request, $api_response, "vfk start: body null"); 726 | http_response_code(500); 727 | echo json_encode([ 728 | "message" => "Invalid JSON response. Please check error log." 729 | ]); 730 | exit(); 731 | } 732 | 733 | #endregion 734 | 735 | #region Parse deeplink (this link contain verification code) 736 | 737 | $deeplink = get_nested_value($decrypted_body, 'result.deeplink'); 738 | if (empty($deeplink)) { 739 | http_response_code(500); 740 | echo json_encode([ 741 | "message" => "Invalid deeplink" 742 | ]); 743 | exit(); 744 | } 745 | 746 | #endregion 747 | 748 | #region Parse and validate verification code from deeplink url 749 | 750 | // parse verification code 751 | preg_match_all('/\*(.*?)\*/', urldecode($deeplink), $matches); 752 | 753 | if (empty($matches[1])) { 754 | http_response_code(404); 755 | echo json_encode([ 756 | "message" => "Verification code not found in deeplink" 757 | ]); 758 | exit(); 759 | } 760 | 761 | $verification_code = null; 762 | 763 | foreach ($matches[1] as $candidate) { 764 | if (preg_match('/^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)+$/', $candidate)) { 765 | // example format: eipvB-seNwn-CpnN0-B2nkU 766 | $verification_code = $candidate; 767 | break; 768 | } 769 | } 770 | 771 | if (!$verification_code) { 772 | http_response_code(404); 773 | echo json_encode([ 774 | "message" => "Verification code not found in deeplink" 775 | ]); 776 | exit(); 777 | } 778 | 779 | #endregion 780 | 781 | #region Parse and validate reference (id) 782 | 783 | // parse reference 784 | $reference = get_nested_value($decrypted_body, 'result.reference'); 785 | if (empty($reference)) { 786 | http_response_code(500); 787 | echo json_encode([ 788 | "message" => "Invalid reference" 789 | ]); 790 | exit(); 791 | } 792 | 793 | #endregion 794 | 795 | #region Call v2.8/validation-start 796 | 797 | $api_request = (object)[ 798 | "clientDeviceId" => $client_device_id, 799 | "finalKey" => $final_key, 800 | "token" => $token 801 | ]; 802 | 803 | $api_response = getcontact_call_api_validation_start($api_request); 804 | 805 | #endregion 806 | 807 | #region Validate http status code (200) 808 | 809 | if ($api_response->httpCode !== 200) { 810 | log_api_transaction($api_request, $api_response, "validation start: call"); 811 | http_response_code(400); 812 | echo json_encode([ 813 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 814 | ]); 815 | exit(); 816 | } 817 | 818 | #endregion 819 | 820 | #region Decode API response 821 | 822 | $api_response_body = json_decode($api_response->body, false); 823 | 824 | if (json_last_error() !== JSON_ERROR_NONE) { 825 | log_api_transaction($api_request, $api_response, "validation start: decode body"); 826 | error_log(json_last_error_msg()); 827 | error_log($api_response->body); 828 | http_response_code(400); 829 | echo json_encode([ 830 | "message" => "Invalid JSON response. Please check error log." 831 | ]); 832 | exit(); 833 | } 834 | 835 | // no need to parse response body, nothing important 836 | 837 | #endregion 838 | 839 | #region Return response 840 | 841 | http_response_code(200); 842 | echo json_encode([ 843 | "message" => "Data processed successfully", 844 | "data" => encrypt_data([ 845 | "phoneNumber" => $phone_number, 846 | "clientDeviceId" => $client_device_id, 847 | "token" => $token, 848 | "finalKey" => $final_key, 849 | "deeplink" => $deeplink, 850 | "reference" => $reference, 851 | ]), 852 | "verificationCode" => $verification_code, // for public view 853 | ]); 854 | exit(); 855 | 856 | #endregion 857 | 858 | break; 859 | 860 | case 3: 861 | 862 | #region Parse request body 863 | 864 | $raw_json = file_get_contents("php://input"); 865 | 866 | if (strlen($raw_json) > MAX_JSON_SIZE) { 867 | http_response_code(413); 868 | echo json_encode([ 869 | "message" => "Payload too large" 870 | ]); 871 | exit(); 872 | } 873 | 874 | $parsed_json = json_decode($raw_json, true); 875 | 876 | if (json_last_error() !== JSON_ERROR_NONE) { 877 | error_log(json_last_error_msg()); 878 | error_log($raw_json); 879 | http_response_code(400); 880 | echo json_encode([ 881 | "message" => "Invalid JSON response. Please check error log." 882 | ]); 883 | exit(); 884 | } 885 | 886 | #endregion 887 | 888 | #region Parse and validate all request input 889 | 890 | $parsed_json = $parsed_json['data'] ?? null; 891 | 892 | $parsed_json = decrypt_data($parsed_json); 893 | 894 | if (empty($parsed_json)) { 895 | http_response_code(400); 896 | echo json_encode([ 897 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (1)" 898 | ]); 899 | exit(); 900 | } 901 | 902 | $phone_number = trim(htmlspecialchars($parsed_json['phoneNumber'])) ?? null; 903 | 904 | if (!$phone_number) { 905 | http_response_code(400); 906 | echo json_encode([ 907 | // "message" => "Invalid phone number" 908 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (1)" 909 | ]); 910 | exit(); 911 | } 912 | 913 | $client_device_id = trim(htmlspecialchars($parsed_json['clientDeviceId'])) ?? null; 914 | 915 | if (!$client_device_id) { 916 | http_response_code(400); 917 | echo json_encode([ 918 | // "message" => "Invalid client device id" 919 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (2)" 920 | ]); 921 | exit(); 922 | } 923 | 924 | $reference = trim(htmlspecialchars($parsed_json['reference'])) ?? null; 925 | 926 | if (!$reference) { 927 | http_response_code(400); 928 | echo json_encode([ 929 | // "message" => "Invalid reference" 930 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (3)" 931 | ]); 932 | exit(); 933 | } 934 | 935 | $final_key = trim(htmlspecialchars($parsed_json['finalKey'])) ?? null; 936 | 937 | if (!$final_key) { 938 | http_response_code(400); 939 | echo json_encode([ 940 | // "message" => "Invalid final key" 941 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (4)" 942 | ]); 943 | exit(); 944 | } 945 | 946 | $token = trim(htmlspecialchars($parsed_json['token'])) ?? null; 947 | 948 | if (!$token) { 949 | http_response_code(400); 950 | echo json_encode([ 951 | // "message" => "Invalid token" 952 | "message" => "The form data is invalid. Please fill out and submit the form again from the start. (5)" 953 | ]); 954 | exit(); 955 | } 956 | 957 | #endregion 958 | 959 | #region Call v2.0/check (VFK) 960 | 961 | $api_request = (object)[ 962 | "reference" => $reference, 963 | "clientDeviceId" => $client_device_id, 964 | "finalKey" => VFK_FINAL_KEY, 965 | ]; 966 | 967 | $api_response = verifykit_call_api_check($api_request); 968 | 969 | #endregion 970 | 971 | #region Validate http status code (200) 972 | 973 | if ($api_response->httpCode !== 200) { 974 | log_api_transaction($api_request, $api_response, "vfk check: call"); 975 | http_response_code(400); 976 | echo json_encode([ 977 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 978 | ]); 979 | exit(); 980 | } 981 | 982 | #endregion 983 | 984 | #region Decode API response 985 | 986 | $api_response_body = json_decode($api_response->body, false); 987 | 988 | if (json_last_error() !== JSON_ERROR_NONE) { 989 | log_api_transaction($api_request, $api_response, "vfk check: decode body"); 990 | error_log(json_last_error_msg()); 991 | error_log($api_response->body); 992 | http_response_code(400); 993 | echo json_encode([ 994 | "message" => "Invalid JSON response. Please check error log." 995 | ]); 996 | exit(); 997 | } 998 | 999 | #endregion 1000 | 1001 | #region Decrypt API response 1002 | 1003 | $decrypted_data_raw = getcontact_decrypt($api_response_body->data, VFK_FINAL_KEY); 1004 | $decrypted_body = json_decode($decrypted_data_raw, false); 1005 | 1006 | if (json_last_error() !== JSON_ERROR_NONE) { 1007 | log_api_transaction($api_request, $api_response, "vfk check: decode body data"); 1008 | error_log(json_last_error_msg()); 1009 | error_log($decrypted_data_raw); 1010 | http_response_code(400); 1011 | echo json_encode([ 1012 | "message" => "Invalid JSON response. Please check error log." 1013 | ]); 1014 | exit(); 1015 | } 1016 | 1017 | if ($decrypted_body == null) { 1018 | log_api_transaction($api_request, $api_response, "vfk check: body null"); 1019 | http_response_code(500); 1020 | echo json_encode([ 1021 | "message" => "Invalid JSON response. Please check error log." 1022 | ]); 1023 | exit(); 1024 | } 1025 | 1026 | #endregion 1027 | 1028 | #region Parse and validate session (id) 1029 | 1030 | // parse sessionId (use to confirm to gtc api that i have been verified, ex: "mnu9Ymt9VLZeeb5") 1031 | $session_id = get_nested_value($decrypted_body, 'result.sessionId'); 1032 | if (empty($session_id)) { 1033 | http_response_code(500); 1034 | echo json_encode([ 1035 | "message" => "Invalid session id" 1036 | ]); 1037 | exit(); 1038 | } 1039 | 1040 | #endregion 1041 | 1042 | #region Call v2.8/verifykit-result 1043 | 1044 | $api_request = (object)[ 1045 | "sessionId" => $session_id, 1046 | "clientDeviceId" => $client_device_id, 1047 | "finalKey" => $final_key, 1048 | "token" => $token, 1049 | ]; 1050 | 1051 | $api_response = getcontact_call_api_verifykit_result($api_request); 1052 | 1053 | #endregion 1054 | 1055 | #region Validate http status code (200) 1056 | 1057 | if ($api_response->httpCode !== 200) { 1058 | log_api_transaction($api_request, $api_response, "gtc verifykit result: call"); 1059 | http_response_code(400); 1060 | echo json_encode([ 1061 | "message" => "Invalid HTTP status code: " . $api_response->httpCode 1062 | ]); 1063 | exit(); 1064 | } 1065 | 1066 | #endregion 1067 | 1068 | #region Decode API response 1069 | 1070 | $api_response_body = json_decode($api_response->body, false); 1071 | 1072 | if (json_last_error() !== JSON_ERROR_NONE) { 1073 | log_api_transaction($api_request, $api_response, "gtc verifykit result: decode body"); 1074 | error_log(json_last_error_msg()); 1075 | error_log($api_response->body); 1076 | http_response_code(400); 1077 | echo json_encode([ 1078 | "message" => "Invalid JSON response. Please check error log." 1079 | ]); 1080 | exit(); 1081 | } 1082 | 1083 | #endregion 1084 | 1085 | #region Decrypt API response 1086 | 1087 | $decrypted_data_raw = getcontact_decrypt($api_response_body->data, $final_key); 1088 | $decrypted_body = json_decode($decrypted_data_raw, false); 1089 | 1090 | if (json_last_error() !== JSON_ERROR_NONE) { 1091 | log_api_transaction($api_request, $api_response, "gtc verifykit result: decode body data"); 1092 | error_log(json_last_error_msg()); 1093 | error_log($decrypted_data_raw); 1094 | http_response_code(400); 1095 | echo json_encode([ 1096 | "message" => "Invalid JSON response. Please check error log." 1097 | ]); 1098 | exit(); 1099 | } 1100 | 1101 | if ($decrypted_body == null) { 1102 | log_api_transaction($api_request, $api_response, "gtc verifykit result: body null"); 1103 | http_response_code(500); 1104 | echo json_encode([ 1105 | "message" => "Invalid JSON response. Please check error log." 1106 | ]); 1107 | exit(); 1108 | } 1109 | 1110 | #endregion 1111 | 1112 | #region Parse and validate validation date 1113 | 1114 | // parse validationDate (ex: "2025-04-27 11:44:45" ---> server time (UTC)) 1115 | $validation_date = get_nested_value($decrypted_body, 'result.validationDate'); 1116 | if (empty($validation_date)) { 1117 | http_response_code(500); 1118 | echo json_encode([ 1119 | "message" => "Invalid validation date" 1120 | ]); 1121 | exit(); 1122 | } 1123 | 1124 | #endregion 1125 | 1126 | #region Return response 1127 | 1128 | error_log(json_encode([ 1129 | "phoneNumber" => $phone_number, 1130 | "clientDeviceId" => $client_device_id, 1131 | "token" => $token, 1132 | "finalKey" => $final_key, 1133 | "validationDate" => $validation_date, 1134 | ], JSON_PRETTY_PRINT)); 1135 | 1136 | http_response_code(200); 1137 | echo json_encode([ 1138 | "message" => "Data processed successfully", 1139 | "data" => [ 1140 | "clientDeviceId" => $client_device_id, 1141 | "token" => $token, 1142 | "finalKey" => $final_key, 1143 | "validationDate" => $validation_date, 1144 | ] 1145 | ]); 1146 | exit(); 1147 | 1148 | #endregion 1149 | 1150 | break; 1151 | 1152 | default: 1153 | http_response_code(400); 1154 | echo json_encode([ 1155 | "message" => "Invalid phase number", 1156 | "data" => $result 1157 | ]); 1158 | exit(); 1159 | break; 1160 | } 1161 | } catch (\Exception $e) { 1162 | error_log("Error GetContact API Generate Credentials: " . $ex->getMessage()); 1163 | http_response_code(500); 1164 | echo json_encode([ 1165 | "message" => "Internal server error" 1166 | ]); 1167 | exit(); 1168 | } 1169 | -------------------------------------------------------------------------------- /src/libraries/getcontact.php: -------------------------------------------------------------------------------- 1 | getMessage()); 52 | } 53 | } 54 | } 55 | 56 | if (!function_exists("getcontact_generate_client_public_key")) { 57 | function getcontact_generate_client_public_key($client_private_key) 58 | { 59 | try { 60 | 61 | $ch = curl_init(); 62 | curl_setopt($ch, CURLOPT_URL, 'https://tools.naufalist.com/getcontact/api/credentials/public-key?privateKey=' . $client_private_key); 63 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 64 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 65 | curl_setopt($ch, CURLOPT_TIMEOUT, 10); 66 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 67 | 68 | $response = curl_exec($ch); 69 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 70 | curl_close($ch); 71 | 72 | if ($http_code === 200) { 73 | $json = json_decode($response, true); 74 | if (isset($json['data'])) { 75 | return (int)$json['data']; 76 | } else { 77 | throw new Exception("Invalid response data field"); 78 | } 79 | } else { 80 | throw new Exception("Invalid http code: $http_code"); 81 | } 82 | } catch (Exception $e) { 83 | throw new Exception("Failed to generate client public key: " . $e->getMessage()); 84 | } 85 | } 86 | } 87 | 88 | if (!function_exists("getcontact_generate_final_key")) { 89 | function getcontact_generate_final_key($client_private_key, $server_public_key) 90 | { 91 | try { 92 | 93 | $ch = curl_init(); 94 | curl_setopt($ch, CURLOPT_URL, 'https://tools.naufalist.com/getcontact/api/credentials/final-key?privateKey=' . $client_private_key . '&publicKey=' . $server_public_key); 95 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 96 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 97 | curl_setopt($ch, CURLOPT_TIMEOUT, 10); 98 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 99 | 100 | $response = curl_exec($ch); 101 | $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 102 | curl_close($ch); 103 | 104 | if ($http_code === 200) { 105 | $json = json_decode($response, true); 106 | if (isset($json['data'])) { 107 | return $json['data']; 108 | } else { 109 | throw new Exception("Invalid response data field"); 110 | } 111 | } else { 112 | throw new Exception("Invalid http code: $http_code"); 113 | } 114 | } catch (Exception $e) { 115 | throw new Exception("Failed to generate final key: " . $e->getMessage()); 116 | } 117 | } 118 | } 119 | 120 | if (!function_exists("getcontact_call_api_subscription")) { 121 | function getcontact_call_api_subscription($request) 122 | { 123 | $request_body = (object)[ 124 | "token" => $request->token 125 | ]; 126 | 127 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 128 | 129 | $request_timestamp = date_create()->format("Uv"); 130 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 131 | 132 | $curl = curl_init(); 133 | 134 | curl_setopt_array($curl, array( 135 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_SUBSCRIPTION, 136 | CURLOPT_RETURNTRANSFER => true, 137 | CURLOPT_ENCODING => "", 138 | CURLOPT_MAXREDIRS => 10, 139 | CURLOPT_TIMEOUT => 20, 140 | CURLOPT_CONNECTTIMEOUT => 10, 141 | CURLOPT_FOLLOWLOCATION => true, 142 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 143 | CURLOPT_CUSTOMREQUEST => "POST", 144 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 145 | CURLOPT_HTTPHEADER => [ 146 | "Content-Type: application/json", 147 | "x-os: " . GTC_ANDROID_OS, 148 | "x-app-version: " . GTC_APP_VERSION, 149 | "x-client-device-id: " . $request_client_device_id, 150 | "x-lang: " . GTC_LANG, 151 | "x-token: " . $request->token, 152 | "x-req-timestamp: $request_timestamp", 153 | "x-country-code: id", 154 | "x-encrypted: 1", 155 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 156 | ], 157 | CURLOPT_HEADER => true 158 | )); 159 | 160 | return curl_execute_request($curl); 161 | } 162 | } 163 | 164 | if (!function_exists("getcontact_call_api_search")) { 165 | function getcontact_call_api_search($request) 166 | { 167 | $request_body = (object)[ 168 | "countryCode" => GTC_COUNTRY_CODE, 169 | "phoneNumber" => $request->phoneNumber, 170 | "source" => "search", 171 | "token" => $request->token 172 | ]; 173 | 174 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 175 | 176 | $request_timestamp = date_create()->format("Uv"); 177 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 178 | 179 | $curl = curl_init(); 180 | 181 | curl_setopt_array($curl, array( 182 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_SEARCH, 183 | CURLOPT_RETURNTRANSFER => true, 184 | CURLOPT_ENCODING => "", 185 | CURLOPT_MAXREDIRS => 10, 186 | CURLOPT_TIMEOUT => 20, 187 | CURLOPT_CONNECTTIMEOUT => 10, 188 | CURLOPT_FOLLOWLOCATION => true, 189 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 190 | CURLOPT_CUSTOMREQUEST => "POST", 191 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 192 | CURLOPT_HTTPHEADER => [ 193 | "Content-Type: application/json", 194 | "x-os: " . GTC_ANDROID_OS, 195 | "x-app-version: " . GTC_APP_VERSION, 196 | "x-client-device-id: " . $request_client_device_id, 197 | "x-lang: " . GTC_LANG, 198 | "x-token: " . $request->token, 199 | "x-req-timestamp: $request_timestamp", 200 | "x-country-code: id", 201 | "x-encrypted: 1", 202 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 203 | ], 204 | CURLOPT_HEADER => true 205 | )); 206 | 207 | return curl_execute_request($curl); 208 | } 209 | } 210 | 211 | if (!function_exists("getcontact_call_api_number_detail")) { 212 | function getcontact_call_api_number_detail($request) 213 | { 214 | $request_body = (object)[ 215 | "countryCode" => GTC_COUNTRY_CODE, 216 | "phoneNumber" => $request->phoneNumber, 217 | "source" => "profile", 218 | "token" => $request->token 219 | ]; 220 | 221 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 222 | 223 | $request_timestamp = date_create()->format("Uv"); 224 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 225 | 226 | $curl = curl_init(); 227 | 228 | curl_setopt_array($curl, array( 229 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_NUMBER_DETAIL, 230 | CURLOPT_RETURNTRANSFER => true, 231 | CURLOPT_ENCODING => "", 232 | CURLOPT_MAXREDIRS => 10, 233 | CURLOPT_TIMEOUT => 20, 234 | CURLOPT_CONNECTTIMEOUT => 10, 235 | CURLOPT_FOLLOWLOCATION => true, 236 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 237 | CURLOPT_CUSTOMREQUEST => "POST", 238 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 239 | CURLOPT_HTTPHEADER => [ 240 | "Content-Type: application/json", 241 | "x-os: " . GTC_ANDROID_OS, 242 | "x-app-version: " . GTC_APP_VERSION, 243 | "x-client-device-id: " . $request_client_device_id, 244 | "x-lang: " . GTC_LANG, 245 | "x-token: " . $request->token, 246 | "x-req-timestamp: $request_timestamp", 247 | "x-country-code: id", 248 | "x-encrypted: 1", 249 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 250 | ], 251 | CURLOPT_HEADER => true 252 | )); 253 | 254 | return curl_execute_request($curl); 255 | } 256 | } 257 | 258 | if (!function_exists("getcontact_call_api_verify_code")) { 259 | function getcontact_call_api_verify_code($request) 260 | { 261 | $request_body = (object)[ 262 | "validationCode" => $request->validationCode, 263 | "token" => $request->token, 264 | ]; 265 | 266 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 267 | 268 | $request_timestamp = date_create()->format("Uv"); 269 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 270 | 271 | $curl = curl_init(); 272 | 273 | curl_setopt_array($curl, array( 274 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_VERIFY_CODE, 275 | CURLOPT_RETURNTRANSFER => true, 276 | CURLOPT_ENCODING => "", 277 | CURLOPT_MAXREDIRS => 10, 278 | CURLOPT_TIMEOUT => 20, 279 | CURLOPT_CONNECTTIMEOUT => 10, 280 | CURLOPT_FOLLOWLOCATION => true, 281 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 282 | CURLOPT_CUSTOMREQUEST => "POST", 283 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 284 | CURLOPT_HTTPHEADER => [ 285 | "Content-Type: application/json", 286 | "x-os: " . GTC_ANDROID_OS, 287 | "x-app-version: " . GTC_APP_VERSION, 288 | "x-client-device-id: " . $request_client_device_id, 289 | "x-lang: " . GTC_LANG, 290 | "x-token: " . $request->token, 291 | "x-req-timestamp: $request_timestamp", 292 | "x-country-code: id", 293 | "x-encrypted: 1", 294 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 295 | ], 296 | CURLOPT_HEADER => true 297 | )); 298 | 299 | return curl_execute_request($curl); 300 | } 301 | } 302 | 303 | if (!function_exists("getcontact_call_api_refresh_code")) { 304 | function getcontact_call_api_refresh_code($request) 305 | { 306 | $request_body = (object)[ 307 | "token" => $request->token, 308 | ]; 309 | 310 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 311 | 312 | $request_timestamp = date_create()->format("Uv"); 313 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 314 | 315 | $curl = curl_init(); 316 | 317 | curl_setopt_array($curl, array( 318 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_REFRESH_CODE, 319 | CURLOPT_RETURNTRANSFER => true, 320 | CURLOPT_ENCODING => "", 321 | CURLOPT_MAXREDIRS => 10, 322 | CURLOPT_TIMEOUT => 20, 323 | CURLOPT_CONNECTTIMEOUT => 10, 324 | CURLOPT_FOLLOWLOCATION => true, 325 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 326 | CURLOPT_CUSTOMREQUEST => "POST", 327 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 328 | CURLOPT_HTTPHEADER => [ 329 | "Content-Type: application/json", 330 | "x-os: " . GTC_ANDROID_OS, 331 | "x-app-version: " . GTC_APP_VERSION, 332 | "x-client-device-id: " . $request_client_device_id, 333 | "x-lang: " . GTC_LANG, 334 | "x-token: " . $request->token, 335 | "x-req-timestamp: $request_timestamp", 336 | "x-country-code: id", 337 | "x-encrypted: 1", 338 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 339 | ], 340 | CURLOPT_HEADER => true 341 | )); 342 | 343 | return curl_execute_request($curl); 344 | } 345 | } 346 | 347 | if (!function_exists("getcontact_call_api_register")) { 348 | function getcontact_call_api_register($request) 349 | { 350 | $request_body = (object)[ 351 | "carrierCountryCode" => GTC_CARRIER_COUNTRY_CODE, 352 | "carrierName" => GTC_CARRIER_NAME, 353 | "carrierNetworkCode" => GTC_CARRIER_NETWORK_CODE, 354 | "countryCode" => GTC_COUNTRY_CODE, 355 | "deepLink" => null, 356 | "deviceName" => GTC_DEVICE_NAME, 357 | "deviceType" => GTC_DEVICE_TYPE, 358 | "email" => null, 359 | "notificationToken" => "", 360 | "oldToken" => null, 361 | "peerKey" => $request->clientPublicKey, 362 | "timeZone" => GTC_TIME_ZONE, 363 | "token" => "" 364 | ]; 365 | 366 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 367 | 368 | $request_timestamp = date_create()->format('Uv'); 369 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 370 | 371 | $curl = curl_init(); 372 | 373 | curl_setopt_array($curl, array( 374 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_REGISTER, 375 | CURLOPT_RETURNTRANSFER => true, 376 | CURLOPT_ENCODING => '', 377 | CURLOPT_MAXREDIRS => 10, 378 | CURLOPT_TIMEOUT => 20, 379 | CURLOPT_CONNECTTIMEOUT => 10, 380 | CURLOPT_FOLLOWLOCATION => true, 381 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 382 | CURLOPT_CUSTOMREQUEST => "POST", 383 | CURLOPT_POSTFIELDS => $request_body_data_json, 384 | CURLOPT_HTTPHEADER => [ 385 | "Content-Type: application/json", 386 | "x-os: " . GTC_ANDROID_OS, 387 | "x-app-version: " . GTC_APP_VERSION, 388 | "x-client-device-id: " . $request_client_device_id, 389 | "x-lang: " . GTC_LANG, 390 | "x-req-timestamp: $request_timestamp", 391 | "x-country-code: id", 392 | "x-encrypted: 0", 393 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 394 | ], 395 | CURLOPT_HEADER => true 396 | )); 397 | 398 | return curl_execute_request($curl); 399 | } 400 | } 401 | 402 | if (!function_exists("getcontact_call_api_init_basic")) { 403 | function getcontact_call_api_init_basic($request) 404 | { 405 | $request_body = (object)[ 406 | "carrierCountryCode" => GTC_CARRIER_COUNTRY_CODE, 407 | "carrierName" => GTC_CARRIER_NAME, 408 | "carrierNetworkCode" => GTC_CARRIER_NETWORK_CODE, 409 | "countryCode" => GTC_COUNTRY_CODE, 410 | "deviceName" => GTC_DEVICE_NAME, 411 | "notificationToken" => "", 412 | "timeZone" => GTC_TIME_ZONE, 413 | "token" => $request->token 414 | ]; 415 | 416 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 417 | 418 | $request_timestamp = date_create()->format('Uv'); 419 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 420 | 421 | $curl = curl_init(); 422 | 423 | curl_setopt_array($curl, array( 424 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_INIT_BASIC, 425 | CURLOPT_RETURNTRANSFER => true, 426 | CURLOPT_ENCODING => '', 427 | CURLOPT_MAXREDIRS => 10, 428 | CURLOPT_TIMEOUT => 20, 429 | CURLOPT_CONNECTTIMEOUT => 10, 430 | CURLOPT_FOLLOWLOCATION => true, 431 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 432 | CURLOPT_CUSTOMREQUEST => "POST", 433 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 434 | CURLOPT_HTTPHEADER => [ 435 | "Content-Type: application/json", 436 | "x-os: " . GTC_ANDROID_OS, 437 | "x-app-version: " . GTC_APP_VERSION, 438 | "x-client-device-id: " . $request_client_device_id, 439 | "x-lang: " . GTC_LANG, 440 | "x-token: " . $request->token, 441 | "x-req-timestamp: $request_timestamp", 442 | "x-country-code: id", 443 | "x-encrypted: 1", 444 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 445 | ], 446 | CURLOPT_HEADER => true 447 | )); 448 | 449 | return curl_execute_request($curl); 450 | } 451 | } 452 | 453 | if (!function_exists("getcontact_call_api_ad_settings")) { 454 | function getcontact_call_api_ad_settings($request) 455 | { 456 | $request_body = (object)[ 457 | "source" => "init", 458 | "token" => $request->token 459 | ]; 460 | 461 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 462 | 463 | $request_timestamp = date_create()->format('Uv'); 464 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 465 | 466 | $curl = curl_init(); 467 | 468 | curl_setopt_array($curl, array( 469 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_AD_SETTINGS, 470 | CURLOPT_RETURNTRANSFER => true, 471 | CURLOPT_ENCODING => '', 472 | CURLOPT_MAXREDIRS => 10, 473 | CURLOPT_TIMEOUT => 20, 474 | CURLOPT_CONNECTTIMEOUT => 10, 475 | CURLOPT_FOLLOWLOCATION => true, 476 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 477 | CURLOPT_CUSTOMREQUEST => "POST", 478 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 479 | CURLOPT_HTTPHEADER => [ 480 | "Content-Type: application/json", 481 | "x-os: " . GTC_ANDROID_OS, 482 | "x-app-version: " . GTC_APP_VERSION, 483 | "x-client-device-id: " . $request_client_device_id, 484 | "x-lang: " . GTC_LANG, 485 | "x-token: " . $request->token, 486 | "x-req-timestamp: $request_timestamp", 487 | "x-country-code: id", 488 | "x-encrypted: 1", 489 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 490 | ], 491 | CURLOPT_HEADER => true 492 | )); 493 | 494 | return curl_execute_request($curl); 495 | } 496 | } 497 | 498 | if (!function_exists("getcontact_call_api_init_intro")) { 499 | function getcontact_call_api_init_intro($request) 500 | { 501 | $request_body = (object)[ 502 | "carrierCountryCode" => GTC_CARRIER_COUNTRY_CODE, 503 | "carrierName" => GTC_CARRIER_NAME, 504 | "carrierNetworkCode" => GTC_CARRIER_NETWORK_CODE, 505 | "countryCode" => GTC_COUNTRY_CODE, 506 | "deviceName" => GTC_DEVICE_NAME, 507 | "hasRouting" => false, 508 | "notificationToken" => "", 509 | "timeZone" => GTC_TIME_ZONE, 510 | "token" => $request->token 511 | ]; 512 | 513 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 514 | 515 | $request_timestamp = date_create()->format('Uv'); 516 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 517 | 518 | $curl = curl_init(); 519 | 520 | curl_setopt_array($curl, array( 521 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_INIT_INTRO, 522 | CURLOPT_RETURNTRANSFER => true, 523 | CURLOPT_ENCODING => '', 524 | CURLOPT_MAXREDIRS => 10, 525 | CURLOPT_TIMEOUT => 20, 526 | CURLOPT_CONNECTTIMEOUT => 10, 527 | CURLOPT_FOLLOWLOCATION => true, 528 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 529 | CURLOPT_CUSTOMREQUEST => "POST", 530 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 531 | CURLOPT_HTTPHEADER => [ 532 | "Content-Type: application/json", 533 | "x-os: " . GTC_ANDROID_OS, 534 | "x-app-version: " . GTC_APP_VERSION, 535 | "x-client-device-id: " . $request_client_device_id, 536 | "x-lang: " . GTC_LANG, 537 | "x-token: " . $request->token, 538 | "x-req-timestamp: $request_timestamp", 539 | "x-country-code: id", 540 | "x-encrypted: 1", 541 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 542 | ], 543 | CURLOPT_HEADER => true 544 | )); 545 | 546 | return curl_execute_request($curl); 547 | } 548 | } 549 | 550 | if (!function_exists("getcontact_call_api_email_code_validate_start")) { 551 | function getcontact_call_api_email_code_validate_start($request) 552 | { 553 | $request_body = (object)[ 554 | "email" => $request->email, 555 | "fullName" => $request->fullname, 556 | "token" => $request->token 557 | ]; 558 | 559 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 560 | 561 | $request_timestamp = date_create()->format('Uv'); 562 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 563 | 564 | $curl = curl_init(); 565 | 566 | curl_setopt_array($curl, array( 567 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_EMAIL_CODE_VALIDATE_START, 568 | CURLOPT_RETURNTRANSFER => true, 569 | CURLOPT_ENCODING => '', 570 | CURLOPT_MAXREDIRS => 10, 571 | CURLOPT_TIMEOUT => 20, 572 | CURLOPT_CONNECTTIMEOUT => 10, 573 | CURLOPT_FOLLOWLOCATION => true, 574 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 575 | CURLOPT_CUSTOMREQUEST => "POST", 576 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 577 | CURLOPT_HTTPHEADER => [ 578 | "Content-Type: application/json", 579 | "x-os: " . GTC_ANDROID_OS, 580 | "x-app-version: " . GTC_APP_VERSION, 581 | "x-client-device-id: " . $request_client_device_id, 582 | "x-lang: " . GTC_LANG, 583 | "x-token: " . $request->token, 584 | "x-req-timestamp: $request_timestamp", 585 | "x-country-code: id", 586 | "x-encrypted: 1", 587 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 588 | ], 589 | CURLOPT_HEADER => true 590 | )); 591 | 592 | return curl_execute_request($curl); 593 | } 594 | } 595 | 596 | if (!function_exists("getcontact_call_api_country")) { 597 | function getcontact_call_api_country($request) 598 | { 599 | $request_body = (object)[ 600 | "countryCode" => strtoupper(GTC_COUNTRY_CODE), 601 | "token" => $request->token 602 | ]; 603 | 604 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 605 | 606 | $request_timestamp = date_create()->format('Uv'); 607 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 608 | 609 | $curl = curl_init(); 610 | 611 | curl_setopt_array($curl, array( 612 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_COUNTRY, 613 | CURLOPT_RETURNTRANSFER => true, 614 | CURLOPT_ENCODING => '', 615 | CURLOPT_MAXREDIRS => 10, 616 | CURLOPT_TIMEOUT => 20, 617 | CURLOPT_CONNECTTIMEOUT => 10, 618 | CURLOPT_FOLLOWLOCATION => true, 619 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 620 | CURLOPT_CUSTOMREQUEST => "POST", 621 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 622 | CURLOPT_HTTPHEADER => [ 623 | "Content-Type: application/json", 624 | "x-os: " . GTC_ANDROID_OS, 625 | "x-app-version: " . GTC_APP_VERSION, 626 | "x-client-device-id: " . $request_client_device_id, 627 | "x-lang: " . GTC_LANG, 628 | "x-token: " . $request->token, 629 | "x-req-timestamp: $request_timestamp", 630 | "x-country-code: id", 631 | "x-encrypted: 1", 632 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 633 | ], 634 | CURLOPT_HEADER => true 635 | )); 636 | 637 | return curl_execute_request($curl); 638 | } 639 | } 640 | 641 | if (!function_exists("getcontact_call_api_validation_start")) { 642 | function getcontact_call_api_validation_start($request) 643 | { 644 | $request_body = (object)[ 645 | "app" => "verifykit", 646 | "countryCode" => GTC_COUNTRY_CODE, 647 | "notificationToken" => "", 648 | "token" => $request->token 649 | ]; 650 | 651 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 652 | 653 | $request_timestamp = date_create()->format('Uv'); 654 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 655 | 656 | $curl = curl_init(); 657 | 658 | curl_setopt_array($curl, array( 659 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_VALIDATION_START, 660 | CURLOPT_RETURNTRANSFER => true, 661 | CURLOPT_ENCODING => '', 662 | CURLOPT_MAXREDIRS => 10, 663 | CURLOPT_TIMEOUT => 20, 664 | CURLOPT_CONNECTTIMEOUT => 10, 665 | CURLOPT_FOLLOWLOCATION => true, 666 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 667 | CURLOPT_CUSTOMREQUEST => "POST", 668 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 669 | CURLOPT_HTTPHEADER => [ 670 | "Content-Type: application/json", 671 | "x-os: " . GTC_ANDROID_OS, 672 | "x-app-version: " . GTC_APP_VERSION, 673 | "x-client-device-id: " . $request_client_device_id, 674 | "x-lang: " . GTC_LANG, 675 | "x-token: " . $request->token, 676 | "x-req-timestamp: $request_timestamp", 677 | "x-country-code: id", 678 | "x-encrypted: 1", 679 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 680 | ], 681 | CURLOPT_HEADER => true 682 | )); 683 | 684 | return curl_execute_request($curl); 685 | } 686 | } 687 | 688 | if (!function_exists("getcontact_call_api_verifykit_result")) { 689 | function getcontact_call_api_verifykit_result($request) 690 | { 691 | $request_body = (object)[ 692 | "sessionId" => $request->sessionId, 693 | "token" => $request->token 694 | ]; 695 | 696 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 697 | 698 | $request_timestamp = date_create()->format('Uv'); 699 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 700 | 701 | $curl = curl_init(); 702 | 703 | curl_setopt_array($curl, array( 704 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_VERIFYKIT_RESULT, 705 | CURLOPT_RETURNTRANSFER => true, 706 | CURLOPT_ENCODING => '', 707 | CURLOPT_MAXREDIRS => 10, 708 | CURLOPT_TIMEOUT => 20, 709 | CURLOPT_CONNECTTIMEOUT => 10, 710 | CURLOPT_FOLLOWLOCATION => true, 711 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 712 | CURLOPT_CUSTOMREQUEST => "POST", 713 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 714 | CURLOPT_HTTPHEADER => [ 715 | "Content-Type: application/json", 716 | "x-os: " . GTC_ANDROID_OS, 717 | "x-app-version: " . GTC_APP_VERSION, 718 | "x-client-device-id: " . $request_client_device_id, 719 | "x-lang: " . GTC_LANG, 720 | "x-token: " . $request->token, 721 | "x-req-timestamp: $request_timestamp", 722 | "x-country-code: id", 723 | "x-encrypted: 1", 724 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 725 | ], 726 | CURLOPT_HEADER => true 727 | )); 728 | 729 | return curl_execute_request($curl); 730 | } 731 | } 732 | 733 | if (!function_exists("verifykit_call_api_init")) { 734 | function verifykit_call_api_init($request) 735 | { 736 | $request_body = (object)[ 737 | "isCallPermissionGranted" => false, 738 | "countryCode" => GTC_COUNTRY_CODE, 739 | "deviceName" => 'marlin', // GTC_DEVICE_NAME, // marlin? 740 | "installedApps" => "{\"whatsapp\":1,\"telegram\":0,\"viber\":0}", 741 | "outsideCountryCode" => GTC_OUTSIDE_COUNTRY_CODE, 742 | "outsidePhoneNumber" => $request->outsidePhoneNumber, 743 | "timezone" => GTC_TIME_ZONE, 744 | "bundleId" => GTC_BUNDLE_ID, 745 | ]; 746 | 747 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 748 | 749 | $request_timestamp = date_create()->format('Uv'); 750 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : VFK_CLIENT_DEVICE_ID; 751 | 752 | $curl = curl_init(); 753 | 754 | curl_setopt_array($curl, array( 755 | CURLOPT_URL => VFK_API_BASE_URL . VFK_API_EP_INIT, 756 | CURLOPT_RETURNTRANSFER => true, 757 | CURLOPT_ENCODING => '', 758 | CURLOPT_MAXREDIRS => 10, 759 | CURLOPT_TIMEOUT => 20, 760 | CURLOPT_CONNECTTIMEOUT => 10, 761 | CURLOPT_FOLLOWLOCATION => true, 762 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 763 | CURLOPT_CUSTOMREQUEST => "POST", 764 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 765 | CURLOPT_HTTPHEADER => [ 766 | "Content-Type: application/json", 767 | "X-VFK-Client-Device-Id: " . $request_client_device_id, 768 | "X-VFK-Client-Key: " . VFK_CLIENT_KEY, 769 | "X-VFK-Sdk-Version: " . VFK_SDK_VERSION, 770 | "X-VFK-Os: " . VFK_OS, 771 | "X-VFK-App-Version: " . VFK_APP_VERSION, 772 | "X-VFK-Encrypted: 1", 773 | "X-VFK-Lang: " . VFK_LANG, 774 | "X-VFK-Req-Timestamp: $request_timestamp", 775 | "X-VFK-Req-Signature: " . getcontact_signature($request_timestamp, $request_body_data_json, VFK_HMAC_SECRET_KEY), 776 | ], 777 | CURLOPT_HEADER => true 778 | )); 779 | 780 | return curl_execute_request($curl); 781 | } 782 | } 783 | 784 | if (!function_exists("verifykit_call_api_country")) { 785 | function verifykit_call_api_country($request) 786 | { 787 | $request_body = (object)[ 788 | "countryCode" => GTC_COUNTRY_CODE, 789 | "bundleId" => GTC_BUNDLE_ID, 790 | ]; 791 | 792 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 793 | 794 | $request_timestamp = date_create()->format('Uv'); 795 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : VFK_CLIENT_DEVICE_ID; 796 | 797 | $curl = curl_init(); 798 | 799 | curl_setopt_array($curl, array( 800 | CURLOPT_URL => VFK_API_BASE_URL . VFK_API_EP_COUNTRY, 801 | CURLOPT_RETURNTRANSFER => true, 802 | CURLOPT_ENCODING => '', 803 | CURLOPT_MAXREDIRS => 10, 804 | CURLOPT_TIMEOUT => 20, 805 | CURLOPT_CONNECTTIMEOUT => 10, 806 | CURLOPT_FOLLOWLOCATION => true, 807 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 808 | CURLOPT_CUSTOMREQUEST => "POST", 809 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 810 | CURLOPT_HTTPHEADER => [ 811 | "Content-Type: application/json", 812 | "X-VFK-Client-Device-Id: " . $request_client_device_id, 813 | "X-VFK-Client-Key: " . VFK_CLIENT_KEY, 814 | "X-VFK-Sdk-Version: " . VFK_SDK_VERSION, 815 | "X-VFK-Os: " . VFK_OS, 816 | "X-VFK-App-Version: " . VFK_APP_VERSION, 817 | "X-VFK-Encrypted: 1", 818 | "X-VFK-Lang: " . VFK_LANG, 819 | "X-VFK-Req-Timestamp: $request_timestamp", 820 | "X-VFK-Req-Signature: " . getcontact_signature($request_timestamp, $request_body_data_json, VFK_HMAC_SECRET_KEY), 821 | ], 822 | CURLOPT_HEADER => true 823 | )); 824 | 825 | return curl_execute_request($curl); 826 | } 827 | } 828 | 829 | if (!function_exists("verifykit_call_api_start")) { 830 | function verifykit_call_api_start($request) 831 | { 832 | $request_body = (object)[ 833 | "countryCode" => GTC_COUNTRY_CODE, 834 | "phoneNumber" => $request->phoneNumber, 835 | "app" => "whatsapp", 836 | "bundleId" => GTC_BUNDLE_ID, 837 | ]; 838 | 839 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 840 | 841 | $request_timestamp = date_create()->format('Uv'); 842 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : VFK_CLIENT_DEVICE_ID; 843 | 844 | $curl = curl_init(); 845 | 846 | curl_setopt_array($curl, array( 847 | CURLOPT_URL => VFK_API_BASE_URL . VFK_API_EP_START, 848 | CURLOPT_RETURNTRANSFER => true, 849 | CURLOPT_ENCODING => '', 850 | CURLOPT_MAXREDIRS => 10, 851 | CURLOPT_TIMEOUT => 20, 852 | CURLOPT_CONNECTTIMEOUT => 10, 853 | CURLOPT_FOLLOWLOCATION => true, 854 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 855 | CURLOPT_CUSTOMREQUEST => "POST", 856 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 857 | CURLOPT_HTTPHEADER => [ 858 | "Content-Type: application/json", 859 | "X-VFK-Client-Device-Id: " . $request_client_device_id, 860 | "X-VFK-Client-Key: " . VFK_CLIENT_KEY, 861 | "X-VFK-Sdk-Version: " . VFK_SDK_VERSION, 862 | "X-VFK-Os: " . VFK_OS, 863 | "X-VFK-App-Version: " . VFK_APP_VERSION, 864 | "X-VFK-Encrypted: 1", 865 | "X-VFK-Lang: " . VFK_LANG, 866 | "X-VFK-Req-Timestamp: $request_timestamp", 867 | "X-VFK-Req-Signature: " . getcontact_signature($request_timestamp, $request_body_data_json, VFK_HMAC_SECRET_KEY), 868 | ], 869 | CURLOPT_HEADER => true 870 | )); 871 | 872 | return curl_execute_request($curl); 873 | } 874 | } 875 | 876 | if (!function_exists("verifykit_call_api_check")) { 877 | function verifykit_call_api_check($request) 878 | { 879 | $request_body = (object)[ 880 | "reference" => $request->reference, 881 | "bundleId" => GTC_BUNDLE_ID, 882 | ]; 883 | 884 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 885 | 886 | $request_timestamp = date_create()->format('Uv'); 887 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : VFK_CLIENT_DEVICE_ID; 888 | 889 | $curl = curl_init(); 890 | 891 | curl_setopt_array($curl, array( 892 | CURLOPT_URL => VFK_API_BASE_URL . VFK_API_EP_CHECK, 893 | CURLOPT_RETURNTRANSFER => true, 894 | CURLOPT_ENCODING => '', 895 | CURLOPT_MAXREDIRS => 10, 896 | CURLOPT_TIMEOUT => 20, 897 | CURLOPT_CONNECTTIMEOUT => 10, 898 | CURLOPT_FOLLOWLOCATION => true, 899 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 900 | CURLOPT_CUSTOMREQUEST => "POST", 901 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 902 | CURLOPT_HTTPHEADER => [ 903 | "Content-Type: application/json", 904 | "X-VFK-Client-Device-Id: " . $request_client_device_id, 905 | "X-VFK-Client-Key: " . VFK_CLIENT_KEY, 906 | "X-VFK-Sdk-Version: " . VFK_SDK_VERSION, 907 | "X-VFK-Os: " . VFK_OS, 908 | "X-VFK-App-Version: " . VFK_APP_VERSION, 909 | "X-VFK-Encrypted: 1", 910 | "X-VFK-Lang: " . VFK_LANG, 911 | "X-VFK-Req-Timestamp: $request_timestamp", 912 | "X-VFK-Req-Signature: " . getcontact_signature($request_timestamp, $request_body_data_json, VFK_HMAC_SECRET_KEY), 913 | ], 914 | CURLOPT_HEADER => true 915 | )); 916 | 917 | return curl_execute_request($curl); 918 | } 919 | } 920 | 921 | if (!function_exists("getcontact_call_api_profile_settings")) { 922 | function getcontact_call_api_profile_settings($request) 923 | { 924 | $request_body = (object)[ 925 | "blockCountrySpam" => null, 926 | "communicationSettings" => null, 927 | "howDoILook" => null, 928 | "landing" => null, 929 | "notificationSettings" => null, 930 | "privateMode" => $request->privateMode, 931 | "serviceNumber" => null, 932 | "showCommunication" => null, 933 | "showPrivatePopup" => null, 934 | "telegramUsed" => null, 935 | "whatsappUsed" => null, 936 | "whoIsHere" => null, 937 | "token" => $request->token, 938 | ]; 939 | 940 | $request_body_data_json = json_encode($request_body, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); 941 | 942 | $request_timestamp = date_create()->format("Uv"); 943 | $request_client_device_id = isset($request->clientDeviceId) && $request->clientDeviceId ? $request->clientDeviceId : GTC_CLIENT_DEVICE_ID; 944 | 945 | $curl = curl_init(); 946 | 947 | curl_setopt_array($curl, array( 948 | CURLOPT_URL => GTC_API_BASE_URL . GTC_API_EP_PROFILE_SETTINGS, 949 | CURLOPT_RETURNTRANSFER => true, 950 | CURLOPT_ENCODING => "", 951 | CURLOPT_MAXREDIRS => 10, 952 | CURLOPT_TIMEOUT => 20, 953 | CURLOPT_CONNECTTIMEOUT => 10, 954 | CURLOPT_FOLLOWLOCATION => true, 955 | CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 956 | CURLOPT_CUSTOMREQUEST => "POST", 957 | CURLOPT_POSTFIELDS => "{\"data\": \"" . getcontact_encrypt($request_body_data_json, $request->finalKey) . "\"}", 958 | CURLOPT_HTTPHEADER => [ 959 | "Content-Type: application/json", 960 | "x-os: " . GTC_ANDROID_OS, 961 | "x-app-version: " . GTC_APP_VERSION, 962 | "x-client-device-id: " . $request_client_device_id, 963 | "x-lang: " . GTC_LANG, 964 | "x-token: " . $request->token, 965 | "x-req-timestamp: $request_timestamp", 966 | "x-country-code: id", 967 | "x-encrypted: 1", 968 | "x-req-signature: " . getcontact_signature($request_timestamp, $request_body_data_json, GTC_HMAC_SECRET_KEY), 969 | ], 970 | CURLOPT_HEADER => true 971 | )); 972 | 973 | return curl_execute_request($curl); 974 | } 975 | } 976 | --------------------------------------------------------------------------------