├── .gitignore ├── Example.php ├── LICENSE.txt ├── README.md ├── composer.json └── src ├── Constant.php └── GojekApi.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ -------------------------------------------------------------------------------- /Example.php: -------------------------------------------------------------------------------- 1 | generateUuid(); 11 | $gojek->setUuid($uuid); 12 | 13 | /** LOGIN */ 14 | $phone = '[PHONE]'; 15 | $pin = '[PIN]'; 16 | 17 | $login = $gojek->login($phone, $pin); 18 | 19 | /** 20 | * VERIFY OTP 21 | */ 22 | $verifyOtp = $gojek->verifyOtp('[OTP]', $login->data->otp_token); 23 | 24 | if ($verifyOtp->access_token) { 25 | print_r($verifyOtp); 26 | } 27 | 28 | if ($verifyOtp->success) { 29 | print_r($verifyOtp); 30 | } 31 | 32 | /** 33 | * IF PIN AUTHENTICATION AFTER OTP 34 | */ 35 | if ($verifyOtp->errors[0]->code === 'mfa:customer_send_challenge:challenge_required') { 36 | $challengeToken = $verifyOtp->errors[0]->details->challenge_token; 37 | $challengeId = $verifyOtp->errors[0]->details->challenges[0]->gopay_challenge_id; 38 | 39 | $verifyMFA = $gojek->verifyMFA($challengeId, $pin); 40 | 41 | if ($verifyMFA->success) { 42 | $verifyMFAToken = $gojek->verifyMFAToken($challengeToken, $verifyMFA->data->token); 43 | print_r($verifyMFAToken); 44 | } 45 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2023 Scott Chacon and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MIT License][license-shield]][license-url] 2 | [![Contributors][contributors-shield]][contributors-url] 3 | [![Forks][forks-shield]][forks-url] 4 | [![Issues][issues-shield]][issues-url] 5 | 6 | [![Hits][hits-view]][hits-view-url] 7 | 8 | ## Gojek Api Unofficial (PHP NATIVE) 9 | AppVersion: 4.74.3 10 | 11 | Gimme Buff to Get More Power: https://trakteer.id/decoderid 12 | 13 | ## Api List 14 | - [x] Login 15 | - [x] Re-Login (No Need OTP) 16 | - [x] Resend OTP 17 | - [x] GoPay Pin 2FA 18 | - [x] Profile 19 | - [x] Balance 20 | - [x] Transaction List 21 | - [x] Transaction Detail 22 | - [x] Bank List 23 | - [x] Validate Bank 24 | - [x] Validate P2P 25 | - [x] Transfer Bank 26 | - [x] Transfer Gopay (P2P Sesama Akun) 27 | - [x] Validate QRCode 28 | - [x] Pay Static QR 29 | - [x] Pay Dynamic QR 30 | - [x] Update PIN 31 | - [x] Logout 32 | 33 | ## Composer 34 | 35 | ```bash 36 | $ composer require decoderid/gojek-api 37 | ``` 38 | 39 | ## Example: Login 40 | ```php 41 | login($phone, $pin); 51 | 52 | /** 53 | * VERIFY OTP 54 | */ 55 | $verifyOtp = $gojek->verifyOtp('[OTP]', $login->data->otp_token); 56 | 57 | if ($verifyOtp->access_token) { 58 | print_r($verifyOtp); 59 | } 60 | 61 | if ($verifyOtp->success) { 62 | print_r($verifyOtp); 63 | } 64 | 65 | /** 66 | * IF PIN AUTHENTICATION AFTER OTP 67 | */ 68 | if ($verifyOtp->errors[0]->code === 'mfa:customer_send_challenge:challenge_required') { 69 | $challengeToken = $verifyOtp->errors[0]->details->challenge_token; 70 | $challengeId = $verifyOtp->errors[0]->details->challenges[0]->gopay_challenge_id; 71 | 72 | $verifyMFA = $gojek->verifyMFA($challengeId, $pin); 73 | 74 | if ($verifyMFA->success) { 75 | $verifyMFAToken = $gojek->verifyMFAToken($challengeToken, $verifyMFA->data->token); 76 | print_r($verifyMFAToken); 77 | } 78 | } 79 | 80 | ?> 81 | ``` 82 | 83 | ## Demo 84 | https://php-demo.decoder.id/app/gojek/ 85 | 86 | ## Contact 87 | Email: im@decoder.id 88 | 89 | Telegram: [@decoderid](https://t.me/decoderid) 90 | 91 | ## Another Project 92 | 1. [Dana Unofficial Api](https://github.com/decoderid/Unofficial-Api-Dana) 93 | 2. [Cek Pajak Kendaraan](https://github.com/decoderid/Cek-Pajak-Kendaraan-BOT) 94 | 95 | 96 | 97 | [contributors-shield]: https://img.shields.io/github/contributors/decoderid/gojek-api-php-native.svg?style=for-the-badge 98 | [contributors-url]: https://github.com/decoderid/gojek-api-php-native/graphs/contributors 99 | [forks-shield]: https://img.shields.io/github/forks/decoderid/gojek-api-php-native.svg?style=for-the-badge 100 | [forks-url]: https://github.com/decoderid/gojek-api-php-native/network/members 101 | [stars-shield]: https://img.shields.io/github/stars/decoderid/gojek-api-php-native.svg?style=for-the-badge 102 | [stars-url]: https://github.com/decoderid/gojek-api-php-native/stargazers 103 | [issues-shield]: https://img.shields.io/github/issues/decoderid/gojek-api-php-native.svg?style=for-the-badge 104 | [issues-url]: https://github.com/decoderid/gojek-api-php-native/issues 105 | [license-shield]: https://img.shields.io/github/license/decoderid/gojek-api-php-native.svg?style=for-the-badge 106 | [license-url]: https://github.com/decoderid/gojek-api-php-native/blob/master/LICENSE.txt 107 | [hits-view]: https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdecoderid%2Fgojek-api-php-native&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=hits&edge_flat=true 108 | [hits-view-url]: https://hits.seeyoufarm.com -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decoderid/gojek-api", 3 | "description": "Gojek Api Unofficial", 4 | "license": "MIT", 5 | "type": "library", 6 | "required": { 7 | "php": ">=5.6.9" 8 | }, 9 | "authors": [ 10 | { 11 | "name": "decoderid", 12 | "email": "im@decoder.id" 13 | } 14 | ], 15 | "autoload": { 16 | "psr-4": { 17 | "Decoderid\\": "src/" 18 | }, 19 | "files": [ 20 | "src/Constant.php" 21 | ] 22 | }, 23 | "require": {} 24 | } -------------------------------------------------------------------------------- /src/Constant.php: -------------------------------------------------------------------------------- 1 | headers = array_merge($this->headers, [ 23 | 'Authorization: Bearer ' . $token 24 | ]); 25 | } 26 | } 27 | 28 | private function uuid() { 29 | return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', 30 | mt_rand(0, 0xffff), mt_rand(0, 0xffff), 31 | mt_rand(0, 0xffff), 32 | mt_rand(0, 0x0fff) | 0x4000, 33 | mt_rand(0, 0x3fff) | 0x8000, 34 | mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) 35 | ); 36 | } 37 | 38 | private function request($method, $url, $payload = [], $headers = []) { 39 | 40 | $method = strtoupper($method); 41 | 42 | $ch = curl_init(); 43 | curl_setopt($ch, CURLOPT_URL, $url); 44 | curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); 45 | curl_setopt($ch, CURLOPT_TIMEOUT, 10000); 46 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 47 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers ? array_merge($this->headers, $headers) : $this->headers); 48 | 49 | if ($method === 'POST' || $method === 'PATCH' || $method === 'PUT') { 50 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 51 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); 52 | } 53 | 54 | $exec = curl_exec($ch); 55 | curl_close($ch); 56 | 57 | return json_decode($exec); 58 | } 59 | 60 | public function setUuid($uuid) { 61 | if (($key = array_search('X-Uniqueid: ' . X_UNIQUEID, $this->headers)) !== false) { 62 | unset($this->headers[$key]); 63 | } 64 | 65 | $this->headers = array_merge($this->headers, [ 66 | 'X-Uniqueid: ' . $uuid 67 | ]); 68 | } 69 | 70 | public function generateUuid() { 71 | $splits = explode('-', $this->uuid()); 72 | $result = $splits[0] . $splits[1] . $splits[2]; 73 | return $result; 74 | } 75 | 76 | public function login($phone) { 77 | return $this->request('POST', EP_LOGIN_REQUEST, [ 78 | 'client_id' => CLIENT_ID, 79 | 'client_secret' => CLIENT_SECRET, 80 | 'country_code' => COUNTRY_CODE_PREFIX, 81 | 'login_type' => '', 82 | 'magic_link_ref' => '', 83 | 'phone_number' => $phone 84 | ]); 85 | } 86 | 87 | public function relogin($phone, $pin) { 88 | $challenge = $this->request('POST', EP_LOGIN_REQUEST, [ 89 | 'client_id' => CLIENT_ID, 90 | 'client_secret' => CLIENT_SECRET, 91 | 'country_code' => COUNTRY_CODE_PREFIX, 92 | 'login_type' => LOGIN_TYPE_PIN, 93 | 'phone_number' => $phone 94 | ]); 95 | 96 | if (!$challenge->success) { 97 | return 'Error Challenge'; 98 | } 99 | 100 | $challengeToken = $this->request('POST', EP_VERIFY_MFA, [ 101 | 'challenge_id' => $challenge->data->gopay_challenge_id, 102 | 'client_id' => CLIENT_ID_MFA, 103 | 'pin' => $pin 104 | ]); 105 | 106 | if (!$challengeToken->success) { 107 | return 'Error Challenge Token'; 108 | } 109 | 110 | $token = $this->request('POST', EP_VERIFY_OTP, [ 111 | 'client_id' => CLIENT_ID, 112 | 'client_secret' => CLIENT_SECRET, 113 | 'data' => [ 114 | 'gopay_challenge_id' => $challenge->data->gopay_challenge_id, 115 | 'gopay_jwt_value' => $challengeToken->data->token 116 | ], 117 | 'grant_type' => GRANT_TYPE_PIN, 118 | 'scopes' => [] 119 | ]); 120 | 121 | 122 | return $token; 123 | } 124 | 125 | public function verifyOtp($otp, $otp_token) { 126 | return $this->request('POST', EP_VERIFY_OTP, [ 127 | 'client_id' => CLIENT_ID, 128 | 'client_secret' => CLIENT_SECRET, 129 | 'data' => [ 130 | 'otp' => $otp, 131 | 'otp_token' => $otp_token 132 | ], 133 | 'grant_type' => GRANT_TYPE_OTP, 134 | 'scopes' => [] 135 | ]); 136 | } 137 | 138 | public function verifyMFA($challenge_id, $pin) { 139 | return $this->request('POST', EP_VERIFY_MFA, [ 140 | 'challenge_id' => $challenge_id, 141 | 'client_id' => CLIENT_ID_MFA, 142 | 'pin' => $pin 143 | ]); 144 | } 145 | 146 | public function verifyMFAToken($challenge_token, $token) { 147 | return $this->request('POST', EP_VERIFY_OTP, [ 148 | 'client_id' => CLIENT_ID, 149 | 'client_secret' => CLIENT_SECRET, 150 | 'data' => [ 151 | 'challenge_token' => $challenge_token, 152 | 'challenges' => [ 153 | [ 154 | 'name' => CHALLENGES_PIN_2FA, 155 | 'value' => $token 156 | ] 157 | ] 158 | ], 159 | 'grant_type' => 'challenge', 160 | 'scopes' => [] 161 | ]); 162 | } 163 | 164 | public function resendOtp($otp_token) { 165 | return $this->request('POST', EP_RESEND_OTP, [ 166 | 'channel_type' => CHANNEL_TYPE_SMS, 167 | 'otp_token' => $otp_token 168 | ]); 169 | } 170 | 171 | public function getProfile() { 172 | return $this->request('GET', EP_CUSTOMER); 173 | } 174 | 175 | public function getBalance() { 176 | return $this->request('GET', EP_PAYMENT_OPTIONS_BALANCES); 177 | } 178 | 179 | public function getTransactionList($page = 1, $limit = 10, $startDate = '', $endDate = '') { 180 | 181 | $query = http_build_query([ 182 | 'page' => $page, 183 | 'limit' => $limit, 184 | 'lower_bound' => $startDate ? $startDate . 'T00:00:00' : '', 185 | 'upper_bound' => $endDate ? $endDate . 'T00:00:00' : '', 186 | 'country_code' => COUNTRY_CODE_ID 187 | ]); 188 | 189 | return $this->request('GET', EP_USER_ORDER_HISTORY . '?' . $query); 190 | } 191 | 192 | public function getTransactionDetail($order_id) { 193 | $query = [ 194 | 'country_code' => COUNTRY_CODE_ID 195 | ]; 196 | 197 | return $this->request('GET', str_replace('{{ORDER_ID}}', $order_id, EP_USER_ORDER_HISTORY_DETAIL) . '?' . $query); 198 | } 199 | 200 | public function getBankList() { 201 | $query = http_build_query([ 202 | 'type' => 'transfer', 203 | 'show_withdrawal_block_status' => false 204 | ]); 205 | 206 | return $this->request('GET', EP_BANK_LIST . '?' . $query); 207 | } 208 | 209 | public function validateBank($bankCode, $accountNumber) { 210 | $query = http_build_query([ 211 | 'bank_code' => $bankCode, 212 | 'account_number' => $accountNumber 213 | ]); 214 | 215 | return $this->request('GET', EP_VALIDATE_BANK . '?' . $query); 216 | } 217 | 218 | public function validateP2P($phoneNumber) { 219 | 220 | $query = http_build_query([ 221 | 'phone_number' => $phoneNumber 222 | ]); 223 | 224 | return $this->request('GET', EP_VALIDATE_P2P . '?' . $query); 225 | } 226 | 227 | public function transferBank($bankCode, $accountNumber, $amount, $notes, $pin) { 228 | $validateBank = $this->validateBank($bankCode, $accountNumber); 229 | 230 | if (!$validateBank->success) { 231 | return 'Error ValidateBank'; 232 | } 233 | 234 | return $this->request('POST', EP_WITHDRAWALS, [ 235 | 'account_name' => $validateBank->data->account_name, 236 | 'account_number' => $accountNumber, 237 | 'amount' => $amount, 238 | 'bank_code' => $bankCode, 239 | 'currency' => 'IDR', 240 | 'notes' => $notes, 241 | 'pin' => $pin, 242 | 'type' => 'transfer' 243 | ], [ 244 | 'Idempotency-Key: ' . $this->uuid() 245 | ]); 246 | } 247 | 248 | public function transferP2P($phoneNumber, $amount, $pin) { 249 | $validateP2P = $this->validateP2P($phoneNumber); 250 | 251 | if (!$validateP2P->success) { 252 | return 'Error ValidateP2P'; 253 | } 254 | 255 | if ($validateP2P->data->is_blocked) { 256 | return 'Error ValidateP2P User Blocked'; 257 | } 258 | 259 | return $this->request('POST', EP_FUND_TRANSFER, [ 260 | 'amount' => [ 261 | 'currency' => 'IDR', 262 | 'value' => $amount 263 | ], 264 | 'description' => '', 265 | 'metadata' => [ 266 | 'post_visibility' => 'PRIVATE', 267 | 'theme_id' => 'THEME_CLASSIC' 268 | ], 269 | 'payee' => [ 270 | 'id' => $validateP2P->data->qr_id 271 | ] 272 | ], [ 273 | 'Pin: ' . $pin 274 | ]); 275 | } 276 | 277 | public function validateQRCode($data) { 278 | return $this->request('POST', EP_EXPLORE, [ 279 | 'data' => $data, 280 | 'type' => 'QR_CODE' 281 | ]); 282 | } 283 | 284 | public function paymentQR($validateQRCode, $amount) { 285 | return $this->request('POST', EP_PAYMENTS_V1, [ 286 | 'additional_data' => $validateQRCode->data->additional_data, 287 | 'amount' => [ 288 | 'currency' => 'IDR', 289 | 'value' => $amount 290 | ], 291 | 'channel_type' => 'STATIC_QR', 292 | 'checksum' => json_decode($validateQRCode->data->metadata->checksum), 293 | 'fetch_promotion_details' => false, 294 | 'metadata' => $validateQRCode->data->metadata, 295 | 'order_signature' => $validateQRCode->data->order_signature, 296 | 'payee' => $validateQRCode->data->payee, 297 | 'payment_intent' => $validateQRCode->data->metadata->payment_widget_intent 298 | ], [ 299 | 'Idempotency-Key: ' . $this->uuid() 300 | ]); 301 | } 302 | 303 | public function payStaticQR($validateQRCode, $amount, $pin) { 304 | 305 | $inquiry = $this->paymentQR($validateQRCode, $amount); 306 | 307 | if (!$inquiry->success) { 308 | return 'Error Inquiry'; 309 | } 310 | 311 | $query = http_build_query([ 312 | 'intent' => $inquiry->data->intent, 313 | 'merchant_id' => $inquiry->data->merchant_information->merchant_id, 314 | ]); 315 | 316 | $paymentOptions = $this->request('GET', EP_PAYMENT_OPTIONS . '?' . $query); 317 | 318 | if (!$paymentOptions->success) { 319 | return 'Error Payment Options'; 320 | } 321 | 322 | $paymentOptionsToken = $paymentOptions->data->payment_options[0]->token; 323 | 324 | return $this->request('PATCH', str_replace('{{PAYMENT_ID}}', $inquiry->data->payment_id, EP_PAYMENTS_V3), [ 325 | 'additional_data' => $validateQRCode->data->additional_data, 326 | 'applied_promo_code' => [ 327 | 'NO_PROMO_APPLIED' 328 | ], 329 | 'checksum' => json_decode($validateQRCode->data->metadata->checksum), 330 | 'metadata' => $validateQRCode->data->metadata, 331 | 'order_signature' => $validateQRCode->data->order_signature, 332 | 'payment_instructions' => [ 333 | [ 334 | 'amount' => [ 335 | 'currency' => 'IDR', 336 | 'display_value' => '', 337 | 'value' => $amount 338 | ], 339 | 'token' => $paymentOptionsToken 340 | ] 341 | ] 342 | ], [ 343 | 'Pin: ' . $pin, 344 | 'X-User-Locale: en_ID' 345 | ]); 346 | } 347 | 348 | public function payDynamicQR($validateQRCode, $amount, $pin) { 349 | 350 | $payment = $this->paymentQR($validateQRCode, $amount); 351 | 352 | $query = http_build_query([ 353 | 'fetch_promotion_details' => false 354 | ]); 355 | 356 | $inquiry = $this->request('GET', EP_PAYMENTS_V1 . '/' . $payment->data->payment_id . '?' . $query); 357 | 358 | if (!$inquiry->success) { 359 | return 'Error Inquiry'; 360 | } 361 | 362 | $query = http_build_query([ 363 | 'intent' => $inquiry->data->intent, 364 | 'merchant_id' => $inquiry->data->merchant_information->merchant_id, 365 | ]); 366 | 367 | $paymentOptions = $this->request('GET', EP_PAYMENT_OPTIONS . '?' . $query); 368 | 369 | if (!$paymentOptions->success) { 370 | return 'Error Payment Options'; 371 | } 372 | 373 | $paymentOptionsToken = $paymentOptions->data->payment_options[0]->token; 374 | 375 | return $this->request('PATCH', str_replace('{{PAYMENT_ID}}', $payment->data->payment_id, EP_PAYMENTS_V3), [ 376 | 'additional_data' => $validateQRCode->data->additional_data, 377 | 'applied_promo_code' => [ 378 | 'NO_PROMO_APPLIED' 379 | ], 380 | 'channel_type' => 'DYNAMIC_QR', 381 | 'checksum' => json_decode($validateQRCode->data->metadata->checksum), 382 | 'metadata' => $validateQRCode->data->metadata, 383 | 'order_signature' => $validateQRCode->data->order_signature, 384 | 'payment_instructions' => [ 385 | [ 386 | 'amount' => [ 387 | 'currency' => 'IDR', 388 | 'display_value' => '', 389 | 'value' => $inquiry->data->amount->value 390 | ], 391 | 'token' => $paymentOptionsToken 392 | ] 393 | ] 394 | ], [ 395 | 'Pin: ' . $pin, 396 | 'X-User-Locale: en_ID' 397 | ]); 398 | } 399 | 400 | public function updatePIN($oldPin, $newPin) { 401 | return $this->request('PUT', EP_PIN_UPDATE, [ 402 | 'new_pin' => $newPin 403 | ], [ 404 | 'Pin: ' . $oldPin 405 | ]); 406 | } 407 | 408 | public function logout() { 409 | return $this->request('DELETE', EP_VERIFY_OTP); 410 | } 411 | } --------------------------------------------------------------------------------