add flutter

This commit is contained in:
Ariska
2026-03-11 15:29:37 +07:00
parent c253e1a370
commit 619d758027
9490 changed files with 135801 additions and 1353 deletions
View File
Vendored Regular → Executable
View File
View File
+13 -6
View File
@@ -22,6 +22,8 @@ defined('BASEPATH') or exit('No direct script access allowed');
| If you need to allow multiple domains, remember that this file is still
| a PHP script and you can easily do that on your own.
|
|
| Test backend API: apitest.semestaterpadu.my.id
*/
$config['base_url'] = 'https://apitest.semestaterpadu.my.id/';
@@ -30,11 +32,16 @@ $config['base_url'] = 'https://apitest.semestaterpadu.my.id/';
| API keys & external services (edit values here)
|--------------------------------------------------------------------------
*/
$config['google_maps_api_key'] = '';
$config['firebase_db_url'] = 'https://project-on-time-apps-default-rtdb.firebaseio.com/';
$config['fcm_project_id'] = '';
$config['google_maps_api_key'] = 'AIzaSyB-g7AdE_hmuDjv7GMMPbrfT0N36yR3N7o';
$config['firebase_db_url'] = 'https://ngojol-trial-default-rtdb.firebaseio.com/';
// Firebase / FCM HTTP v1 service account configuration.
// Project id from ngojol-trial-firebase-adminsdk JSON.
$config['fcm_project_id'] = 'ngojol-trial';
// Prefer using a credentials file path rather than inlining JSON here.
// The JSON file is stored at backendpanel/ngojol-trial-firebase-adminsdk-lc81n-00c9e935db.json
// and will be read by fcm_v1_helper via FCM_CREDENTIALS_PATH.
$config['fcm_credentials_json'] = '';
$config['fcm_credentials_path'] = '';
$config['fcm_credentials_path'] = FCPATH . 'ngojol-trial-firebase-adminsdk-lc81n-00c9e935db.json';
$config['fcm_limit_per_hour'] = 100;
$config['fcm_limit_per_day'] = 500;
$config['maps_limit_per_hour'] = 1000;
@@ -182,7 +189,7 @@ $config['composer_autoload'] = FALSE;
| Leave blank to allow all characters -- but only if you are insane.
|
| The configured value is actually a regular expression character group
| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
| and it will be executed as: ! preg_match('/^[permitted_uri_chars]+$/i
|
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
@@ -252,7 +259,7 @@ $config['allow_get_array'] = TRUE;
| your log files will fill up very fast.
|
*/
$config['log_threshold'] = 0;
$config['log_threshold'] = 2;
/*
|--------------------------------------------------------------------------
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+14 -2
View File
@@ -24,13 +24,25 @@ class appnotification extends CI_Controller
public function send()
{
$this->load->helper('fcm_v1_helper');
// Validate Firebase token before kirim. If no token, relogin (retry) is done in helper.
if (!fcm_v1_validate_token()) {
$this->session->set_flashdata('error', 'Firebase token tidak valid. Pastikan file ngojol-trial-firebase-adminsdk JSON ada dan dapat dibaca. Silakan coba lagi.');
redirect('appnotification/index');
return;
}
$topic = $this->input->post('topic');
$title = $this->input->post('title');
$message = $this->input->post('message');
$this->notif->send_notif($title, $message, $topic);
$this->session->set_flashdata('send', 'Notifikasi berhasil dikirim');
$ok = $this->notif->send_notif($title, $message, $topic);
if ($ok) {
$this->session->set_flashdata('send', 'Notifikasi berhasil dikirim');
} else {
$this->session->set_flashdata('error', 'Gagal mengirim notifikasi. Firebase token mungkin kedaluwarsa. Silakan coba lagi.');
}
redirect('appnotification/index');
}
}
View File
View File
View File
+3 -1
View File
@@ -59,6 +59,7 @@ class Driver extends CI_Controller
$this->form_validation->set_rules('tgl_lahir', 'tgl_lahir', 'trim|prep_for_form');
$this->form_validation->set_rules('gender', 'gender', 'trim|prep_for_form');
$this->form_validation->set_rules('alamat_driver', 'alamat_driver', 'trim|prep_for_form');
$this->form_validation->set_rules('reg_id', 'reg_id', 'trim|prep_for_form');
if ($this->form_validation->run() == TRUE) {
@@ -76,7 +77,8 @@ class Driver extends CI_Controller
'tempat_lahir' => html_escape($this->input->post('tempat_lahir', TRUE)),
'tgl_lahir' => html_escape($this->input->post('tgl_lahir', TRUE)),
'gender' => html_escape($this->input->post('gender', TRUE)),
'alamat_driver' => html_escape($this->input->post('alamat_driver', TRUE))
'alamat_driver' => html_escape($this->input->post('alamat_driver', TRUE)),
'reg_id' => html_escape($this->input->post('reg_id', TRUE))
];
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+45 -10
View File
@@ -10,6 +10,7 @@ class Driver extends REST_Controller
parent::__construct();
$this->load->helper("url");
$this->load->helper('fcm_v1_helper');
$this->load->database();
$this->load->model('Driver_model');
$this->load->model('Pelanggan_model');
@@ -75,16 +76,28 @@ class Driver extends REST_Controller
$data = file_get_contents("php://input");
$decoded_data = json_decode($data);
$reg_id = array(
'reg_id' => $decoded_data->token
);
// Only save reg_id (FCM token) when valid. Invalid/placeholder tokens are updated by relogin.
$token_from_regid = isset($decoded_data->reg_id) ? trim((string) $decoded_data->reg_id) : '';
$token_from_token = isset($decoded_data->token) ? trim((string) $decoded_data->token) : '';
$token = $token_from_regid !== '' ? $token_from_regid : $token_from_token;
$reg_id = array();
if ($token !== '' && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($token)) {
$reg_id['reg_id'] = $token;
}
$condition = array(
'password' => sha1($decoded_data->password),
'no_telepon' => $decoded_data->no_telepon,
//'token' => $decoded_data->token
'password' => sha1($decoded_data->password)
);
$check_banned = $this->Driver_model->check_banned($decoded_data->no_telepon);
$login_by_phone = isset($decoded_data->no_telepon) && $decoded_data->no_telepon !== '';
if ($login_by_phone) {
$condition['no_telepon'] = $decoded_data->no_telepon;
} else {
$condition['email'] = $decoded_data->email;
}
$check_banned = $login_by_phone
? $this->Driver_model->check_banned($decoded_data->no_telepon)
: $this->Driver_model->check_banned_by_email($decoded_data->email);
if ($check_banned) {
$message = array(
'message' => 'banned',
@@ -96,9 +109,12 @@ class Driver extends REST_Controller
$message = array();
if ($cek_login->num_rows() > 0) {
$upd_regid = $this->Driver_model->edit_profile($reg_id, $decoded_data->no_telepon);
$no_telepon = $cek_login->row()->no_telepon;
if (!empty($reg_id)) {
$this->Driver_model->edit_profile($reg_id, $no_telepon);
}
$get_pelanggan = $this->Driver_model->get_data_pelanggan($condition);
$this->Driver_model->edit_status_login($decoded_data->no_telepon);
$this->Driver_model->edit_status_login($no_telepon);
$message = array(
'code' => '200',
'message' => 'found',
@@ -133,6 +149,12 @@ class Driver extends REST_Controller
);
$ins = $this->Driver_model->my_location($data);
// When driver sends valid FCM token (reg_id) with location, update so they receive order requests. Invalid/placeholder tokens are updated by relogin.
$reg_id = isset($decoded_data->reg_id) ? trim((string) $decoded_data->reg_id) : '';
if ($reg_id !== '' && isset($decoded_data->id_driver) && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($reg_id)) {
$this->Driver_model->update_driver_reg_id($decoded_data->id_driver, $reg_id);
}
if ($ins) {
$message = array(
'message' => 'location updated',
@@ -360,6 +382,7 @@ class Driver extends REST_Controller
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'accept_post: payload=' . $data);
$data_req = array(
'id_driver' => $dec_data->id,
@@ -372,9 +395,11 @@ class Driver extends REST_Controller
);
$cek_login = $this->Driver_model->get_status_driver($condition);
log_message('debug', 'accept_post: get_status_driver rows=' . $cek_login->num_rows());
if ($cek_login->num_rows() > 0) {
$acc_req = $this->Driver_model->accept_request($data_req);
log_message('debug', 'accept_post: accept_request result=' . json_encode($acc_req));
if ($acc_req['status']) {
$message = array(
'message' => 'berhasil',
@@ -415,6 +440,7 @@ class Driver extends REST_Controller
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'start_post: payload=' . $data);
$data_req = array(
'id_driver' => $dec_data->id,
@@ -422,6 +448,7 @@ class Driver extends REST_Controller
);
$acc_req = $this->Driver_model->start_request($data_req);
log_message('debug', 'start_post: start_request result=' . json_encode($acc_req));
if ($acc_req['status']) {
$message = array(
'message' => 'berhasil',
@@ -455,6 +482,7 @@ class Driver extends REST_Controller
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'finish_post: payload=' . $data);
$data_req = array(
'id_driver' => $dec_data->id,
@@ -467,6 +495,7 @@ class Driver extends REST_Controller
);
$finish_transaksi = $this->Driver_model->finish_request($data_req, $data_tr);
log_message('debug', 'finish_post: finish_request result=' . json_encode($finish_transaksi));
if ($finish_transaksi['status']) {
$message = array(
'message' => 'berhasil',
@@ -863,6 +892,12 @@ class Driver extends REST_Controller
$namafoto = time() . '-' . rand(0, 99999) . ".jpg";
$path = "images/fotodriver/" . $namafoto;
file_put_contents($path, base64_decode($image));
// New driver registrations from older apps do not send an FCM token yet.
// Generate a deterministic placeholder based on email so reg_id is never empty.
// This stays unusable for FCM v1 because fcm_v1_is_valid_device_token() will reject it.
$regIdPlaceholder = 'R' . sprintf('%u', crc32(strtolower(trim((string) $dec_data->email))));
$data_signup = array(
'id' => 'D' . time(),
'nama_driver' => $dec_data->nama_driver,
@@ -878,7 +913,7 @@ class Driver extends REST_Controller
'countrycode' => $dec_data->countrycode,
'gender' => $dec_data->gender,
'alamat_driver' => $dec_data->alamat_driver,
'reg_id' => 12345,
'reg_id' => $regIdPlaceholder,
'status' => 0
);
View File
+17 -5
View File
@@ -10,6 +10,7 @@ class Merchant extends REST_Controller
parent::__construct();
$this->load->helper("url");
$this->load->helper('fcm_v1_helper');
$this->load->database();
$this->load->model('Merchantapi_model');
$this->load->model('wallet_model', 'wallet');
@@ -64,9 +65,13 @@ class Merchant extends REST_Controller
$data = file_get_contents("php://input");
$decoded_data = json_decode($data);
$reg_id = array(
'token_merchant' => $decoded_data->token
);
// Only save FCM token when valid (relogin overwrites invalid/placeholder tokens).
$token = isset($decoded_data->token) ? trim((string) $decoded_data->token) : '';
$token = (isset($decoded_data->reg_id) && trim((string) $decoded_data->reg_id) !== '') ? trim((string) $decoded_data->reg_id) : $token;
$reg_id = array();
if ($token !== '' && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($token)) {
$reg_id['token_merchant'] = $token;
}
$condition = array(
'password' => sha1($decoded_data->password),
@@ -84,7 +89,9 @@ class Merchant extends REST_Controller
$cek_login = $this->Merchantapi_model->get_data_merchant($condition);
$message = array();
if ($cek_login->num_rows() > 0) {
$this->Merchantapi_model->edit_profile_token($reg_id, $decoded_data->no_telepon);
if (!empty($reg_id)) {
$this->Merchantapi_model->edit_profile_token($reg_id, $decoded_data->no_telepon);
}
$get_pelanggan = $this->Merchantapi_model->get_data_merchant($condition);
$message = array(
'code' => '200',
@@ -183,6 +190,11 @@ class Merchant extends REST_Controller
$path = "images/merchant/" . $namafoto;
file_put_contents($path, base64_decode($image));
// Merchant apps prior to FCM v1 do not send a Firebase token on register.
// Generate a deterministic placeholder from email so token_merchant is never empty.
// This value is NOT a real FCM token; Notification_model will ignore it for push.
$tokenPlaceholder = 'R' . sprintf('%u', crc32(strtolower(trim((string) $dec_data->email))));
$data_merchant = array(
'id_fitur' => $dec_data->id_fitur,
'nama_merchant' => $dec_data->nama_merchant,
@@ -197,7 +209,7 @@ class Merchant extends REST_Controller
'phone_merchant' => $dec_data->phone,
'country_code_merchant' => $dec_data->countrycode,
'status_merchant' => '0',
'token_merchant' => time()
'token_merchant' => $tokenPlaceholder
);
$imagektp = $dec_data->foto_ktp;
View File
+11
View File
@@ -48,6 +48,17 @@ class Notification extends REST_Controller
return;
}
$this->load->helper('fcm_v1_helper');
// Always verify FCM token is ready before using Firebase service (as in test / panel send).
if (!fcm_v1_validate_token()) {
$this->response(array(
'code' => '503',
'message' => 'fcm_token_not_ready',
), 200);
return;
}
$target = isset($decoded['target']) ? trim($decoded['target']) : '';
$is_topic = !empty($decoded['is_topic']);
$data = isset($decoded['data']) && is_array($decoded['data']) ? $decoded['data'] : array();
+103 -25
View File
@@ -10,6 +10,7 @@ class Pelanggan extends REST_Controller
parent::__construct();
$this->load->helper("url");
$this->load->helper('fcm_v1_helper');
$this->load->database();
$this->load->model('Pelanggan_model');
$this->load->model('wallet_model', 'wallet');
@@ -115,16 +116,37 @@ class Pelanggan extends REST_Controller
$data = file_get_contents("php://input");
$decoded_data = json_decode($data);
$reg_id = array(
'token' => $decoded_data->token
);
if (!$decoded_data || !isset($decoded_data->password)) {
$this->response(array('code' => '400', 'message' => 'Invalid request', 'data' => []), 200);
return;
}
// Only save FCM token when valid (relogin overwrites invalid/placeholder tokens).
$token = isset($decoded_data->token) ? trim((string) $decoded_data->token) : '';
$token = (isset($decoded_data->reg_id) && trim((string) $decoded_data->reg_id) !== '') ? trim((string) $decoded_data->reg_id) : $token;
$reg_id = array();
if ($token !== '' && function_exists('fcm_v1_is_valid_device_token') && fcm_v1_is_valid_device_token($token)) {
$reg_id['token'] = $token;
}
$condition = array(
'password' => sha1($decoded_data->password),
'no_telepon' => $decoded_data->no_telepon,
//'token' => $decoded_data->token
'password' => sha1($decoded_data->password)
);
$check_banned = $this->Pelanggan_model->check_banned($decoded_data->no_telepon);
$no_telepon_val = isset($decoded_data->no_telepon) ? trim($decoded_data->no_telepon) : '';
$email_val = isset($decoded_data->email) ? trim($decoded_data->email) : '';
$login_by_phone = $no_telepon_val !== '';
if ($login_by_phone) {
$condition['no_telepon'] = $no_telepon_val;
} else {
if ($email_val === '') {
$this->response(array('code' => '404', 'message' => 'no hp atau password salah!', 'data' => []), 200);
return;
}
$condition['email'] = $email_val;
}
$check_banned = $login_by_phone
? $this->Pelanggan_model->check_banned($no_telepon_val)
: $this->Pelanggan_model->check_banned_user($email_val);
if ($check_banned) {
$message = array(
'message' => 'banned',
@@ -136,7 +158,10 @@ class Pelanggan extends REST_Controller
$message = array();
if ($cek_login->num_rows() > 0) {
$upd_regid = $this->Pelanggan_model->edit_profile($reg_id, $decoded_data->no_telepon);
$no_telepon = $cek_login->row()->no_telepon;
if (!empty($reg_id)) {
$this->Pelanggan_model->edit_profile($reg_id, $no_telepon);
}
$get_pelanggan = $this->Pelanggan_model->get_data_pelanggan($condition);
$message = array(
@@ -206,6 +231,21 @@ class Pelanggan extends REST_Controller
$namafoto = time() . '-' . rand(0, 99999) . ".jpg";
$path = "images/pelanggan/" . $namafoto;
file_put_contents($path, base64_decode($image));
// New users may register from older apps that do not send an FCM token.
// Generate a deterministic placeholder based on email so the column is never empty.
// This placeholder is intentionally SHORT / starting with "R" + digits so
// fcm_v1_is_valid_device_token() will treat it as invalid for push.
$incomingToken = isset($dec_data->token) ? trim((string) $dec_data->token) : '';
if ($incomingToken === '') {
$emailForToken = isset($dec_data->email) ? strtolower(trim((string) $dec_data->email)) : '';
if ($emailForToken !== '') {
$incomingToken = 'R' . sprintf('%u', crc32($emailForToken));
} else {
$incomingToken = 'R' . sprintf('%u', crc32('guest-' . time()));
}
}
$data_signup = array(
'id' => 'P' . time(),
'fullnama' => $dec_data->fullnama,
@@ -216,7 +256,7 @@ class Pelanggan extends REST_Controller
'tgl_lahir' => $dec_data->tgl_lahir,
'countrycode' => $dec_data->countrycode,
'fotopelanggan' => $namafoto,
'token' => $dec_data->token,
'token' => $incomingToken,
);
$signup = $this->Pelanggan_model->signup($data_signup);
if ($signup) {
@@ -831,24 +871,24 @@ class Pelanggan extends REST_Controller
$token = $this->wallet->gettoken($iduser);
$regid = $this->wallet->getregid($iduser);
$tokenmerchant = $this->wallet->gettokenmerchant($iduser);
if ($token == NULL and $tokenmerchant == NULL and $regid != NULL) {
$topic = null;
if ($token == NULL and $tokenmerchant == NULL and $regid != NULL && !empty(trim((string) $regid['reg_id']))) {
$topic = $regid['reg_id'];
} else if ($regid == NULL and $tokenmerchant == NULL and $token != NULL) {
} else if ($regid == NULL and $tokenmerchant == NULL and $token != NULL && !empty(trim((string) $token['token']))) {
$topic = $token['token'];
} else if ($regid == NULL and $token == NULL and $tokenmerchant != NULL) {
} else if ($regid == NULL and $token == NULL and $tokenmerchant != NULL && !empty(trim((string) $tokenmerchant['token_merchant']))) {
$topic = $tokenmerchant['token_merchant'];
}
$title = 'Sukses';
$message = 'Permintaan berhasil dikirim';
$saldo = $this->wallet->getsaldo($iduser);
$this->wallet->ubahsaldo($iduser, $amount, $saldo);
//$this->wallet->ubahstatuswithdrawbyid($id);
$this->wallet->send_notif($title, $message, $topic);
if ($topic !== null) {
$this->wallet->send_notif($title, $message, $topic);
}
/* END EDIT */
$message = array(
@@ -877,12 +917,26 @@ class Pelanggan extends REST_Controller
}
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'list_ride_post REQUEST: ' . $data);
$near = $this->Pelanggan_model->get_driver_ride($dec_data->latitude, $dec_data->longitude, $dec_data->fitur);
$dec_data = json_decode($data);
if (!$dec_data || !isset($dec_data->latitude, $dec_data->longitude, $dec_data->fitur)) {
log_message('error', 'list_ride_post: invalid request, missing latitude/longitude/fitur');
$message = array('data' => [], 'error' => 'Invalid request: latitude, longitude, fitur required');
$this->response($message, 200);
return;
}
$radius_km = null;
if (isset($dec_data->radius_km) && is_numeric($dec_data->radius_km)) {
$radius_km = max(1, min(100, (float) $dec_data->radius_km));
}
$near = $this->Pelanggan_model->get_driver_ride($dec_data->latitude, $dec_data->longitude, $dec_data->fitur, $radius_km);
$drivers = $near->result();
$message = array(
'data' => $near->result()
'data' => $drivers
);
log_message('debug', 'list_ride_post RESPONSE: fitur=' . $dec_data->fitur . ' lat=' . $dec_data->latitude . ' lng=' . $dec_data->longitude . ' radius_km=' . ($radius_km !== null ? $radius_km : 'default') . ' drivers_found=' . count($drivers));
$this->response($message, 200);
}
@@ -910,12 +964,26 @@ class Pelanggan extends REST_Controller
}
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'list_car_post REQUEST: ' . $data);
$near = $this->Pelanggan_model->get_driver_car($dec_data->latitude, $dec_data->longitude, $dec_data->fitur);
$dec_data = json_decode($data);
if (!$dec_data || !isset($dec_data->latitude, $dec_data->longitude, $dec_data->fitur)) {
log_message('error', 'list_car_post: invalid request, missing latitude/longitude/fitur');
$message = array('data' => [], 'error' => 'Invalid request: latitude, longitude, fitur required');
$this->response($message, 200);
return;
}
$radius_km = null;
if (isset($dec_data->radius_km) && is_numeric($dec_data->radius_km)) {
$radius_km = max(1, min(100, (float) $dec_data->radius_km));
}
$near = $this->Pelanggan_model->get_driver_car($dec_data->latitude, $dec_data->longitude, $dec_data->fitur, $radius_km);
$drivers = $near->result();
$message = array(
'data' => $near->result()
'data' => $drivers
);
log_message('debug', 'list_car_post RESPONSE: fitur=' . $dec_data->fitur . ' lat=' . $dec_data->latitude . ' lng=' . $dec_data->longitude . ' radius_km=' . ($radius_km !== null ? $radius_km : 'default') . ' drivers_found=' . count($drivers));
$this->response($message, 200);
}
@@ -946,6 +1014,7 @@ class Pelanggan extends REST_Controller
} else {
$cek = $this->Pelanggan_model->check_banned_user($_SERVER['PHP_AUTH_USER']);
if ($cek) {
log_message('debug', 'request_transaksi_post: banned user ' . $_SERVER['PHP_AUTH_USER']);
$message = array(
'message' => 'fail',
'data' => 'Status User Banned'
@@ -956,6 +1025,7 @@ class Pelanggan extends REST_Controller
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'request_transaksi_post: payload=' . $data);
$data_req = array(
'id_pelanggan' => $dec_data->id_pelanggan,
@@ -977,12 +1047,18 @@ class Pelanggan extends REST_Controller
$request = $this->Pelanggan_model->insert_transaksi($data_req);
if ($request['status']) {
if (isset($request['data'][0]->id)) {
log_message('debug', 'request_transaksi_post: success id_transaksi=' . $request['data'][0]->id . ' id_pelanggan=' . $dec_data->id_pelanggan . ' fitur=' . $dec_data->order_fitur);
} else {
log_message('debug', 'request_transaksi_post: success (no id in data) payload=' . json_encode($request['data']));
}
$message = array(
'message' => 'success',
'data' => $request['data']
);
$this->response($message, 200);
} else {
log_message('error', 'request_transaksi_post: insert_transaksi fail data=' . json_encode($request['data']));
$message = array(
'message' => 'fail',
'data' => $request['data']
@@ -1001,12 +1077,14 @@ class Pelanggan extends REST_Controller
$data = file_get_contents("php://input");
$dec_data = json_decode($data);
log_message('debug', 'check_status_transaksi_post: payload=' . $data);
$dataTrans = array(
'id_transaksi' => $dec_data->id_transaksi
);
$getStatus = $this->Pelanggan_model->check_status($dataTrans);
log_message('debug', 'check_status_transaksi_post: result=' . json_encode($getStatus));
$this->response($getStatus, 200);
}
View File
View File
View File
View File
View File
View File
+136 -11
View File
@@ -12,11 +12,17 @@ if (!function_exists('fcm_v1_get_credentials')) {
{
$json = (defined('FCM_CREDENTIALS_JSON') ? FCM_CREDENTIALS_JSON : '');
if ($json !== '') {
log_message('debug', 'FCM v1: loading credentials from FCM_CREDENTIALS_JSON');
return json_decode($json, true);
}
$path = (defined('FCM_CREDENTIALS_PATH') ? FCM_CREDENTIALS_PATH : '');
if ($path !== '' && is_readable($path)) {
return json_decode(file_get_contents($path), true);
if ($path !== '') {
if (is_readable($path)) {
log_message('debug', 'FCM v1: loading credentials from path=' . $path);
return json_decode(file_get_contents($path), true);
} else {
log_message('error', 'FCM v1: credentials path not readable: ' . $path);
}
}
return null;
}
@@ -25,47 +31,113 @@ if (!function_exists('fcm_v1_get_credentials')) {
if (!function_exists('fcm_v1_get_access_token')) {
/**
* Get OAuth2 access token for FCM using service account JWT.
* Uses ngojol-trial-firebase-adminsdk JSON credentials.
* Retries once on failure (relogin to Firebase).
* @return string|null Access token or null on failure
*/
function fcm_v1_get_access_token()
{
$token = fcm_v1_fetch_access_token();
if ($token) {
return $token;
}
log_message('debug', 'FCM v1: Retrying Firebase auth (relogin)');
return fcm_v1_fetch_access_token();
}
}
if (!function_exists('fcm_v1_fetch_access_token')) {
/**
* Single attempt to fetch OAuth2 access token from Firebase service account.
* @return string|null Access token or null on failure
*/
function fcm_v1_fetch_access_token()
{
$cred = fcm_v1_get_credentials();
if (!$cred || empty($cred['client_email']) || empty($cred['private_key'])) {
log_message('error', 'FCM v1: Missing FCM_CREDENTIALS_JSON or FCM_CREDENTIALS_PATH');
log_message('error', 'FCM v1: Missing credentials. Ensure ngojol-trial-firebase-adminsdk JSON is at FCM_CREDENTIALS_PATH');
return null;
}
$now = time();
$token_uri = !empty($cred['token_uri']) ? $cred['token_uri'] : 'https://oauth2.googleapis.com/token';
// Use cloud-platform scope. If you get invalid_scope, enable Firebase Cloud Messaging API
// in Google Cloud Console (APIs & Services → Library) for project ngojol-trial.
$scope = 'https://www.googleapis.com/auth/cloud-platform';
$jwt = array(
'iss' => $cred['client_email'],
'sub' => $cred['client_email'],
'aud' => 'https://oauth2.googleapis.com/token',
'iat' => $now,
'exp' => $now + 3600,
'scope' => $scope,
);
$jwt_b64 = fcm_v1_base64url_encode(json_encode(array('alg' => 'RS256', 'typ' => 'JWT')))
. '.' . fcm_v1_base64url_encode(json_encode($jwt));
$sig = '';
$key = openssl_pkey_get_private($cred['private_key']);
$pk = $cred['private_key'];
if (strpos($pk, '\\n') !== false) {
$pk = str_replace('\\n', "\n", $pk);
}
$key = openssl_pkey_get_private($pk);
if (!$key) {
log_message('error', 'FCM v1: Invalid private key');
log_message('error', 'FCM v1: Invalid private key in service account JSON');
return null;
}
openssl_sign($jwt_b64, $sig, $key, OPENSSL_ALGO_SHA256);
openssl_free_key($key);
$jwt_b64 .= '.' . fcm_v1_base64url_encode($sig);
$resp = @file_get_contents('https://oauth2.googleapis.com/token', false, stream_context_create(array(
$post_body = 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' . rawurlencode($jwt_b64);
$ctx = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded",
'content' => 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' . $jwt_b64,
'content' => $post_body,
'ignore_errors' => true,
),
)));
));
$resp = @file_get_contents($token_uri, false, $ctx);
if ($resp === false) {
log_message('error', 'FCM v1: Token request failed');
log_message('error', 'FCM v1: Token request failed (network or OAuth error)');
return null;
}
$data = json_decode($resp, true);
return isset($data['access_token']) ? $data['access_token'] : null;
if (isset($data['access_token'])) {
return $data['access_token'];
}
log_message('error', 'FCM v1: Token response invalid: ' . substr($resp, 0, 200));
return null;
}
}
if (!function_exists('fcm_v1_validate_token')) {
/**
* Validate that Firebase auth token can be obtained (for FCM send).
* Call before kirim/send. If false, backend should prompt relogin/retry.
* @return bool True if token is valid and ready for FCM
*/
function fcm_v1_validate_token()
{
$token = fcm_v1_get_access_token();
return !empty($token);
}
}
if (!function_exists('fcm_v1_is_valid_device_token')) {
/**
* Check if string looks like a valid FCM v1 device token (not a placeholder).
* Valid tokens from FirebaseMessaging.getToken() are typically 100+ chars.
* Placeholders like "12345", "R1234567890" are rejected. Use relogin to update.
*
* @param string|null $token FCM device token from client
* @return bool True if token looks valid and should be saved
*/
function fcm_v1_is_valid_device_token($token)
{
$s = $token === null ? '' : trim((string) $token);
if ($s === '') return false;
if (strlen($s) < 50) return false;
if (preg_match('/^(12345|R\d+)$/', $s)) return false;
return true;
}
}
@@ -124,6 +196,14 @@ if (!function_exists('fcm_v1_send')) {
$message['android'] = array('priority' => 'HIGH');
}
$body = json_encode(array('message' => $message));
// Log request (without auth token) for debugging
$logTarget = $is_topic ? ('topic=' . (string)$target) : ('token=' . substr((string)$target, 0, 20) . '...');
log_message(
'debug',
'FCM v1 request project_id=' . $project_id .
' ' . $logTarget .
' payload=' . $body
);
$ctx = stream_context_create(array(
'http' => array(
'method' => 'POST',
@@ -136,6 +216,51 @@ if (!function_exists('fcm_v1_send')) {
));
$url = 'https://fcm.googleapis.com/v1/projects/' . $project_id . '/messages:send';
$resp = @file_get_contents($url, false, $ctx);
return $resp !== false ? $resp : false;
if ($resp === false) {
log_message('error', 'FCM v1 response error for ' . $logTarget);
return false;
}
// Check for FCM error in response body (e.g. invalid topic, quota)
$decoded = json_decode($resp, true);
if (is_array($decoded) && isset($decoded['error'])) {
log_message('error', 'FCM v1 API error for ' . $logTarget . ': ' . $resp);
return false;
}
// On 401 (token expired), relogin and retry once
$http_response_header_local = isset($http_response_header) ? $http_response_header : array();
$is_401 = false;
foreach ($http_response_header_local as $h) {
if (preg_match('/^HTTP\/\d\.\d\s+401\b/', $h)) {
$is_401 = true;
break;
}
}
if ($is_401) {
log_message('debug', 'FCM v1: 401 received, relogin and retry');
$token = fcm_v1_get_access_token();
if ($token) {
$ctx = stream_context_create(array(
'http' => array(
'method' => 'POST',
'header' =>
"Content-Type: application/json\r\n" .
"Authorization: Bearer " . $token . "\r\n",
'content' => $body,
'ignore_errors' => true,
),
));
$resp = @file_get_contents($url, false, $ctx);
}
}
log_message('debug', 'FCM v1 response for ' . $logTarget . ' body=' . $resp);
// Treat FCM error in response body as failure
if (is_string($resp)) {
$decoded = json_decode($resp, true);
if (is_array($decoded) && isset($decoded['error'])) {
log_message('error', 'FCM v1 API error: ' . (isset($decoded['error']['message']) ? $decoded['error']['message'] : $resp));
return false;
}
}
return $resp;
}
}
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
View File
+26 -1
View File
@@ -124,6 +124,16 @@ class Driver_model extends CI_model
}
}
public function check_banned_by_email($email)
{
$stat = $this->db->query("SELECT id FROM driver WHERE status='3' AND email='" . $this->db->escape_str($email) . "'");
if ($stat->num_rows() == 1) {
return true;
} else {
return false;
}
}
public function check_exist_phone_edit($id, $phone)
{
$cek = $this->db->query("SELECT no_telepon FROM driver where no_telepon='$phone' AND id!='$id'");
@@ -242,6 +252,19 @@ class Driver_model extends CI_model
}
}
/**
* Update driver's FCM token (reg_id) so they receive order request notifications.
*/
public function update_driver_reg_id($id_driver, $reg_id)
{
if (empty($id_driver) || empty(trim((string) $reg_id))) {
return false;
}
$this->db->where('id', $id_driver);
$this->db->update('driver', array('reg_id' => trim((string) $reg_id)));
return $this->db->affected_rows() >= 0;
}
public function get_data_driver($condition)
{
$this->db->select('driver.*, saldo.saldo');
@@ -862,7 +885,9 @@ class Driver_model extends CI_model
$this->db->set('tempat_lahir', $data['tempat_lahir']);
$this->db->set('tgl_lahir', $data['tgl_lahir']);
$this->db->set('alamat_driver', $data['alamat_driver']);
if (isset($data['reg_id'])) {
$this->db->set('reg_id', $data['reg_id']);
}
$this->db->where('id', $data['id']);
$this->db->update('driver', $data);
View File
View File
View File
View File
View File
+162 -124
View File
@@ -4,155 +4,193 @@ class notification_model extends CI_model
{
public function notif_cancel_user($id_driver, $id_transaksi, $token_user)
{
$datanotif = array(
'id_driver' => $id_driver,
if ($this->is_token_empty($token_user) || !$this->is_valid_fcm_token($token_user)) {
log_message('debug', 'notif_cancel_user: skip, no/invalid Firebase token for user');
return true;
}
// Use FCM HTTP v1 via service account, not legacy keyfcm.
$payload = array(
'id_driver' => $id_driver,
'id_transaksi' => $id_transaksi,
'response' => '5',
'type' => 1
'response' => '5',
'type' => 1,
);
$senderdata = array(
'data' => $datanotif,
'to' => $token_user
);
$headers = array(
"Content-Type: application/json",
"Authorization: key=" . keyfcm
);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($senderdata),
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
return $response;
$this->send_generic_to_token($token_user, $payload, array());
return true;
}
public function notif_cancel_driver($id_transaksi, $token_driver)
{
$data = array(
if ($this->is_token_empty($token_driver) || !$this->is_valid_fcm_token($token_driver)) {
log_message('debug', 'notif_cancel_driver: skip, no/invalid Firebase token for driver');
return true;
}
$payload = array(
'id_transaksi' => $id_transaksi,
'response' => '0',
'type' => 1
'response' => '0',
'type' => 1,
);
$senderdata = array(
'data' => $data,
'to' => $token_driver
);
$headers = array(
"Content-Type: application/json",
'Authorization: key=' . keyfcm
);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($senderdata),
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
return $response;
$this->send_generic_to_token($token_driver, $payload, array());
return true;
}
public function send_notif($title, $message, $topic)
/**
* Check if token/target is empty (null, empty string, or whitespace).
* Use before sending FCM to avoid sending when device has no token.
*/
private function is_token_empty($token)
{
return $token === null || trim((string) $token) === '';
}
/**
* Check if string looks like a valid FCM v1 device token (not a placeholder).
* Uses shared fcm_v1_is_valid_device_token helper.
*/
private function is_valid_fcm_token($token)
{
if (!function_exists('fcm_v1_is_valid_device_token')) {
get_instance()->load->helper('fcm_v1_helper');
}
return fcm_v1_is_valid_device_token($token);
}
/**
* Send notification via FCM HTTP v1 to a device token (reg_id, token, token_merchant).
* Uses token API so reg_id can be used as FCM v1 target. Skips invalid/placeholder tokens.
* @param string $target FCM device token (from driver.reg_id, pelanggan.token, etc.)
* @return bool True on success or skip, false on failure
*/
public function send_notif($title, $message, $target)
{
if ($this->is_token_empty($target)) {
log_message('debug', 'send_notif: skip, no token');
return true;
}
if (!$this->is_valid_fcm_token($target)) {
log_message('debug', 'send_notif: skip, invalid/placeholder FCM token');
return true;
}
if (!function_exists('fcm_v1_validate_token')) {
get_instance()->load->helper('fcm_v1_helper');
}
if (!fcm_v1_validate_token()) {
log_message('error', 'send_notif: Firebase token not ready, skip send');
return false;
}
$data = array(
'title' => $title,
'title' => $title,
'message' => $message,
'type' => 4
'type' => 4,
);
$senderdata = array(
'data' => $data,
'to' => '/topics/' . $topic
$options = array(
'title' => $title,
'body' => $message,
);
$headers = array(
'Content-Type : application/json',
'Authorization: key=' . keyfcm
);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($senderdata),
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
return $this->send_generic_to_token($target, $data, $options);
}
public function send_notif_topup($title, $id, $message, $method, $token)
{
if ($this->is_token_empty($token)) {
log_message('debug', 'send_notif_topup: skip, no Firebase token for user id=' . $id);
return;
}
if (!function_exists('fcm_v1_validate_token')) {
get_instance()->load->helper('fcm_v1_helper');
}
if (!fcm_v1_validate_token()) {
log_message('error', 'send_notif_topup: Firebase token not ready, skip send for user id=' . $id);
return;
}
// Direct token notification for topup via HTTP v1.
$data = array(
'title' => $title,
'id' => $id,
'message' => $message,
'title' => $title,
'id' => $id,
'message'=> $message,
'method' => $method,
'type' => 3
'type' => 3,
);
$senderdata = array(
'data' => $data,
'to' => $token
$options = array(
'title' => $title,
'body' => $message,
);
$this->send_generic_to_token($token, $data, $options);
}
$headers = array(
'Content-Type : application/json',
'Authorization: key=' . keyfcm
);
$curl = curl_init();
/**
* Send FCM via HTTP v1 API to a single device token.
* Used by User and Driver apps (notification/send_generic).
* $data must be flat key-value; values are stringified for FCM.
*
* @param string $target FCM device token
* @param array $data Data payload (keys preserved; values cast to string)
* @param array $options Optional 'title', 'body' for notification
* @return bool True on success, false on failure
*/
public function send_generic_to_token($target, $data, $options = array())
{
if ($this->is_token_empty($target)) {
log_message('debug', 'FCM v1 send_generic_to_token: skip, target token empty');
return false;
}
if (!$this->is_valid_fcm_token($target)) {
log_message('debug', 'FCM v1 send_generic_to_token: skip, invalid/placeholder token');
return false;
}
if (!function_exists('fcm_v1_send')) {
get_instance()->load->helper('fcm_v1_helper');
}
if (!fcm_v1_validate_token()) {
log_message('error', 'FCM v1 send_generic_to_token: Firebase token not ready, skip send');
return false;
}
$flat = array();
foreach ($data as $k => $v) {
if (is_array($v) || is_object($v)) {
$flat[$k] = json_encode($v);
} else {
$flat[$k] = (string) $v;
}
}
log_message('debug', 'FCM v1 send_generic_to_token target=' . substr((string)$target, 0, 20) . '... payload=' . json_encode($flat));
$result = fcm_v1_send($target, $flat, false, $options);
if ($result === false) {
log_message('error', 'FCM v1 send_generic_to_token failed for target=' . substr((string)$target, 0, 20) . '...');
}
return $result !== false;
}
curl_setopt_array($curl, array(
CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($senderdata),
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
/**
* Send FCM via HTTP v1 API to a topic.
*
* @param string $target Topic name (without /topics/ prefix)
* @param array $data Data payload
* @param array $options Optional 'title', 'body'
* @return bool True on success, false on failure
*/
public function send_generic_to_topic($target, $data, $options = array())
{
if (!function_exists('fcm_v1_send')) {
get_instance()->load->helper('fcm_v1_helper');
}
if (!fcm_v1_validate_token()) {
log_message('error', 'FCM v1 send_generic_to_topic: Firebase token not ready, skip send');
return false;
}
$flat = array();
foreach ($data as $k => $v) {
if (is_array($v) || is_object($v)) {
$flat[$k] = json_encode($v);
} else {
$flat[$k] = (string) $v;
}
}
log_message('debug', 'FCM v1 send_generic_to_topic topic=' . (string)$target . ' payload=' . json_encode($flat));
$result = fcm_v1_send($target, $flat, true, $options);
if ($result === false) {
log_message('error', 'FCM v1 send_generic_to_topic failed for topic=' . (string)$target);
}
return $result !== false;
}
}
View File
View File
+104 -18
View File
@@ -269,19 +269,45 @@ class Pelanggan_model extends CI_model
return true;
}
public function get_driver_ride($lat, $lng, $fitur)
/**
* Get nearby drivers for ride: purely by GPS distance, no region/wilayah.
* Uses config_driver (driver's real-time lat/long from app) and user (lat, lng).
* Optional $radius_km (1100): when set, used as search radius; else fitur.jarak_minimum with min 10 km.
* Optimized: bounding-box pre-filter + Haversine + LIMIT 50.
*/
public function get_driver_ride($lat, $lng, $fitur, $radius_km = null)
{
$url_foto = base_url() . 'images/fotodriver/';
$lat = (float) $lat;
$lng = (float) $lng;
$fitur = (int) $fitur;
if ($radius_km !== null && $radius_km > 0) {
$radius_km = max(1, min(100, (float) $radius_km));
}
$box_km = isset($radius_km) ? $radius_km : 50;
$delta_lat = $box_km / 111.0;
$cos_lat = max(0.01, cos(deg2rad($lat)));
$delta_lng = $box_km / (111.0 * $cos_lat);
$lat_min = $lat - $delta_lat;
$lat_max = $lat + $delta_lat;
$lng_min = $lng - $delta_lng;
$lng_max = $lng + $delta_lng;
$having_radius = isset($radius_km)
? (float) $radius_km
: 'GREATEST(COALESCE(f.jarak_minimum, 10), 10)';
$result = $this->db->query("
SELECT f.jarak_minimum, f.wallet_minimum, d.id as id, d.nama_driver, ld.latitude, ld.longitude, ld.bearing, ld.update_at,
k.merek, k.nomor_kendaraan, k.warna, k.tipe, s.saldo,
d.no_telepon, CONCAT('$url_foto', d.foto, '') as foto, d.reg_id, dj.driver_job,
(6371 * acos(cos(radians($lat)) * cos(radians( ld.latitude ))"
. " * cos(radians(ld.longitude) - radians($lng))"
. " + sin(radians($lat)) * sin( radians(ld.latitude)))) AS distance
FROM config_driver ld, driver d, driver_job dj, kendaraan k, saldo s,fitur f
WHERE ld.id_driver = d.id
(6371 * acos(cos(radians($lat)) * cos(radians(ld.latitude))
* cos(radians(ld.longitude) - radians($lng))
+ sin(radians($lat)) * sin(radians(ld.latitude)))) AS distance
FROM config_driver ld, driver d, driver_job dj, kendaraan k, saldo s, fitur f
WHERE ld.id_driver = d.id
AND f.id_fitur = $fitur
AND ld.status = '1'
AND dj.id = d.job
@@ -290,35 +316,88 @@ class Pelanggan_model extends CI_model
AND k.id_k = d.kendaraan
AND s.id_user = d.id
AND s.saldo > f.wallet_minimum
HAVING distance <= f.jarak_minimum
ORDER BY distance ASC");
AND ld.latitude BETWEEN $lat_min AND $lat_max
AND ld.longitude BETWEEN $lng_min AND $lng_max
HAVING distance <= $having_radius
ORDER BY distance ASC
LIMIT 50");
$count = $result->num_rows();
log_message('debug', 'get_driver_ride: fitur=' . $fitur . ' lat=' . $lat . ' lng=' . $lng . ' drivers_found=' . $count);
if ($count === 0) {
$this->log_driver_diagnostic('ride', $lat, $lng, $fitur, $lat_min, $lat_max, $lng_min, $lng_max, $radius_km);
}
return $result;
}
public function get_driver_car($lat, $lng, $fitur)
/**
* Diagnostic logging when 0 drivers found: counts online drivers, in-box, by job, etc.
*/
private function log_driver_diagnostic($type, $lat, $lng, $fitur, $lat_min, $lat_max, $lng_min, $lng_max, $radius_km)
{
$r_online = $this->db->query("SELECT COUNT(*) as c FROM config_driver WHERE status='1'")->row();
$cnt_online = $r_online ? (int) $r_online->c : 0;
$r_box = $this->db->query("SELECT COUNT(*) as c FROM config_driver ld JOIN driver d ON ld.id_driver=d.id WHERE ld.status='1' AND d.status='1' AND ld.latitude BETWEEN " . (float)$lat_min . " AND " . (float)$lat_max . " AND ld.longitude BETWEEN " . (float)$lng_min . " AND " . (float)$lng_max)->row();
$cnt_in_box = $r_box ? (int) $r_box->c : 0;
$fitur_row = $this->db->query("SELECT driver_job, jarak_minimum, wallet_minimum FROM fitur WHERE id_fitur=" . (int)$fitur)->row();
$driver_job = $fitur_row ? $fitur_row->driver_job : null;
$cnt_job = 0;
if ($driver_job !== null) {
$r_job = $this->db->query("SELECT COUNT(*) as c FROM driver WHERE status='1' AND job=" . (int)$driver_job)->row();
$cnt_job = $r_job ? (int) $r_job->c : 0;
}
$fitur_exists = $fitur_row ? 'yes' : 'NO_FITUR_NOT_FOUND';
log_message('debug', 'get_driver_' . $type . '_diagnostic: fitur=' . $fitur . ' fitur_exists=' . $fitur_exists . ' driver_job=' . ($driver_job !== null ? $driver_job : 'n/a') . ' radius_km=' . ($radius_km !== null ? $radius_km : 'default') . ' online_drivers=' . $cnt_online . ' in_box=' . $cnt_in_box . ' with_job=' . $cnt_job);
}
/**
* Get nearby drivers for car: purely by GPS distance, no region/wilayah.
* Optional $radius_km (1100); else 10 km. Optimized: bounding-box + LIMIT 50.
*/
public function get_driver_car($lat, $lng, $fitur, $radius_km = null)
{
$range = 10;
$url_foto = base_url() . 'images/fotodriver/';
$lat = (float) $lat;
$lng = (float) $lng;
$fitur = (int) $fitur;
$range = ($radius_km !== null && $radius_km > 0)
? max(1, min(100, (float) $radius_km))
: 10;
$delta_lat = $range / 111.0;
$cos_lat = max(0.01, cos(deg2rad($lat)));
$delta_lng = $range / (111.0 * $cos_lat);
$lat_min = $lat - $delta_lat;
$lat_max = $lat + $delta_lat;
$lng_min = $lng - $delta_lng;
$lng_max = $lng + $delta_lng;
$result = $this->db->query("
SELECT f.jarak_minimum, f.wallet_minimum, d.id as id, d.nama_driver, ld.latitude, ld.longitude, ld.bearing, ld.update_at,
k.merek, k.nomor_kendaraan, k.warna, k.tipe, s.saldo,
d.no_telepon, CONCAT('$url_foto', d.foto, '') as foto, d.reg_id, dj.driver_job,
(6371 * acos(cos(radians($lat)) * cos(radians( ld.latitude ))"
. " * cos(radians(ld.longitude) - radians($lng))"
. " + sin(radians($lat)) * sin( radians(ld.latitude)))) AS distance
FROM config_driver ld, driver d, driver_job dj, kendaraan k, saldo s,fitur f
WHERE ld.id_driver = d.id
(6371 * acos(cos(radians($lat)) * cos(radians(ld.latitude))
* cos(radians(ld.longitude) - radians($lng))
+ sin(radians($lat)) * sin(radians(ld.latitude)))) AS distance
FROM config_driver ld, driver d, driver_job dj, kendaraan k, saldo s, fitur f
WHERE ld.id_driver = d.id
AND f.id_fitur = $fitur
AND ld.status = '1'
AND dj.id = d.job
AND d.job = '2'
AND d.job = f.driver_job
AND d.status = '1'
AND k.id_k = d.kendaraan
AND s.id_user = d.id
AND s.saldo > 500
AND s.saldo > GREATEST(COALESCE(f.wallet_minimum, 0), 500)
AND ld.latitude BETWEEN $lat_min AND $lat_max
AND ld.longitude BETWEEN $lng_min AND $lng_max
HAVING distance <= $range
ORDER BY distance ASC");
ORDER BY distance ASC
LIMIT 50");
$count = $result->num_rows();
log_message('debug', 'get_driver_car: fitur=' . $fitur . ' lat=' . $lat . ' lng=' . $lng . ' drivers_found=' . $count);
if ($count === 0) {
$this->log_driver_diagnostic('car', $lat, $lng, $fitur, $lat_min, $lat_max, $lng_min, $lng_max, $radius_km);
}
return $result;
}
@@ -467,6 +546,7 @@ class Pelanggan_model extends CI_model
$this->delete_transaksi($dataTrans['id_transaksi']);
$stat = FALSE;
}
log_message('debug', 'check_status: id_transaksi=' . $dataTrans['id_transaksi'] . ' raw_status=' . $cek->row('status') . ' stat=' . ($stat ? 'TRUE' : 'FALSE'));
$dataCheck = array(
'message' => 'check status',
'status' => $stat,
@@ -1273,6 +1353,10 @@ class Pelanggan_model extends CI_model
return $this->db->get('category_merchant');
}
/**
* Merchant nearby: purely by GPS (merchant lat/long vs user lat/long), no region.
* Distance <= fitur.jarak_minimum.
*/
public function merchantnearby($long, $lat)
{
$this->db->select("merchant.id_merchant , merchant.nama_merchant , merchant.alamat_merchant , merchant.rate_merchant, merchant.latitude_merchant , merchant.longitude_merchant , merchant.jam_buka , merchant.jam_tutup ,
@@ -1329,6 +1413,7 @@ class Pelanggan_model extends CI_model
return $data;
}
/** Merchant promo nearby: purely GPS, no region. */
public function merchantpromo($long, $lat)
{
$this->db->select("merchant.id_merchant , merchant.nama_merchant , merchant.alamat_merchant , merchant.rate_merchant, merchant.latitude_merchant , merchant.longitude_merchant , merchant.jam_buka , merchant.jam_tutup ,
@@ -1411,6 +1496,7 @@ class Pelanggan_model extends CI_model
return $this->db->get('merchant');
}
/** All merchant nearby by fitur: purely GPS (merchant lat/long vs user), no region. */
public function allmerchantnearby($long, $lat, $fitur)
{
$this->db->select("merchant.id_merchant , merchant.nama_merchant , merchant.alamat_merchant , merchant.rate_merchant, merchant.latitude_merchant , merchant.longitude_merchant , merchant.jam_buka , merchant.jam_tutup ,
View File
View File
View File
View File
View File
View File
+7 -34
View File
@@ -146,39 +146,10 @@ class wallet_model extends CI_model
public function send_notif($title, $message, $topic)
{
$data = array(
'title' => $title,
'message' => $message,
'type' => 3
);
$senderdata = array(
'data' => $data,
'to' => $topic
);
$headers = array(
'Content-Type : application/json',
'Authorization: key=' . keyfcm
);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://fcm.googleapis.com/fcm/send",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode($senderdata),
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
// Delegate to Notification_model, which uses FCM HTTP v1 + service account.
$CI = &get_instance();
$CI->load->model('notification_model', 'notif');
$CI->notif->send_notif($title, $message, $topic);
}
public function updatesaldowallet($data)
@@ -319,7 +290,9 @@ class wallet_model extends CI_model
$title = 'Dibatalkan';
$desc = 'Pembayaran Kamu telah Dibatalkan';
$this->Notification_model->send_notif_topup($title, $wl['id_user'], $desc, $wl['invoice'], $token);
if ($token !== null && trim((string) $token) !== '') {
$this->Notification_model->send_notif_topup($title, $wl['id_user'], $desc, $wl['invoice'], $token);
}
}
}
}
View File
View File
View File
+6 -1
View File
@@ -5,11 +5,16 @@
<div class="card">
<div class="card-body">
<h4 class="card-title">Kirim Notifikasi</h4>
<?php if ($this->session->flashdata()) : ?>
<?php if ($this->session->flashdata('send')) : ?>
<div class="alert alert-success" role="alert">
<?php echo $this->session->flashdata('send'); ?>
</div>
<?php endif; ?>
<?php if ($this->session->flashdata('error')) : ?>
<div class="alert alert-danger" role="alert">
<?php echo $this->session->flashdata('error'); ?>
</div>
<?php endif; ?>
<?= form_open_multipart('appnotification/send'); ?>
<div class="form-group">
<label for="newscategory">Kirim Ke</label>
View File
View File
View File
View File

Some files were not shown because too many files have changed in this diff Show More