├── .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 |
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 |
38 |
39 |
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 |
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 |
130 |
131 |
GetContact
132 |
133 |
134 |
135 |
136 |
137 |
138 |
142 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
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 |
165 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 | = $_SESSION["error"];
232 | unset($_SESSION["error"]); ?>
233 |
234 |
235 |
236 |
237 |
238 |
239 | + Add Credential
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 | ID
248 | Description
249 | Credential Data
250 | Header Data
251 | Created At/Updated At
252 | Action
253 |
254 |
255 |
256 |
257 |
258 | = htmlspecialchars($row["id"] ?? "-") ?>
259 | = htmlspecialchars($row["description"] ?? "-") ?>
260 |
261 |
262 | Final Key
263 | = htmlspecialchars($row["final_key"] ?? "-") ?>
264 | Token
265 | = htmlspecialchars($row["token"] ?? "-") ?>
266 |
267 |
268 |
269 |
270 | Client Device ID
271 | = htmlspecialchars($row["client_device_id"] ?? "-") ?>
272 |
273 |
274 |
275 | = htmlspecialchars($row["created_at"] ?? "-") ?>admin
276 | = htmlspecialchars($row["updated_at"] ?? "-") ?>admin
277 |
278 |
279 | Edit
280 | &updated_at== urlencode($row["updated_at"]) ?>" onclick="return confirm('Yakin ingin hapus data ini?')" class="btn btn-danger btn-sm">Delete
281 |
282 |
283 |
284 |
285 |
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 |
71 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | Please select a credential and click the generate or refresh captcha button to display the captcha code.
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
Credential *
148 |
149 |
150 | -- Choose GetContact Credential --
151 |
152 |
153 |
154 |
155 |
156 |
157 |
165 |
166 |
167 |
168 |
169 | Verify
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 | Click here to generate / refresh captcha
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
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 |
--------------------------------------------------------------------------------