table = $wpdb->prefix . ‘geis_payments’;
register_activation_hook(__FILE__, [$this, ‘activate’]);
add_action(‘rest_api_init’, [$this, ‘register_routes’]);
add_action(‘admin_menu’, [$this, ‘admin_menu’]);
add_action(‘admin_enqueue_scripts’, [$this, ‘admin_assets’]);
add_action(‘wp_enqueue_scripts’, [$this, ‘frontend_assets’]);
add_action(‘admin_post_geis_export_csv’, [$this, ‘export_csv’]);
}
public function activate() {
global $wpdb;
require_once ABSPATH . ‘wp-admin/includes/upgrade.php’;
$charset = $wpdb->get_charset_collate();
$sql = “CREATE TABLE {$this->table} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
transaction_id varchar(40) NOT NULL,
created_at datetime NOT NULL,
updated_at datetime NOT NULL,
name varchar(190) NOT NULL,
whatsapp varchar(40) NOT NULL,
address text NULL,
join_status varchar(80) NULL,
package text NULL,
package_quantity varchar(50) NULL,
variant varchar(190) NULL,
notes text NULL,
referral_name varchar(190) NULL,
referral_whatsapp varchar(40) NULL,
referral_city varchar(190) NULL,
landing_page text NULL,
amount decimal(15,2) NOT NULL DEFAULT 0,
payment_method varchar(80) NULL,
payment_status varchar(40) NOT NULL DEFAULT ‘pending’,
proof_url text NULL,
admin_notes text NULL,
paid_at datetime NULL,
PRIMARY KEY (id),
UNIQUE KEY transaction_id (transaction_id),
KEY payment_status (payment_status),
KEY created_at (created_at),
KEY whatsapp (whatsapp)
) $charset;”;
dbDelta($sql);
update_option(‘geis_pd_version’, GEIS_PD_VERSION);
}
public function register_routes() {
register_rest_route(‘geis/v1’, ‘/register’, [
‘methods’ => ‘POST’,
‘callback’ => [$this, ‘create_registration’],
‘permission_callback’ => ‘__return_true’,
]);
register_rest_route(‘geis/v1’, ‘/payment/(?P
\d+)’, [
‘methods’ => ‘POST’,
‘callback’ => [$this, ‘update_payment’],
‘permission_callback’ => function() { return current_user_can(‘manage_options’); },
]);
}
private function clean($value) {
if (is_array($value)) return array_map([$this, ‘clean’], $value);
return sanitize_textarea_field((string)$value);
}
private function transaction_id() {
global $wpdb;
do {
$id = ‘GEIS-‘ . wp_date(‘Ymd’) . ‘-‘ . strtoupper(wp_generate_password(6, false, false));
$exists = $wpdb->get_var($wpdb->prepare(“SELECT id FROM {$this->table} WHERE transaction_id=%s”, $id));
} while ($exists);
return $id;
}
public function create_registration(WP_REST_Request $request) {
global $wpdb;
$data = $request->get_json_params();
if (!is_array($data)) $data = $request->get_params();
$name = $this->clean($data[‘name’] ?? ”);
$wa = $this->clean($data[‘whatsapp’] ?? ”);
if ($name === ” || $wa === ”) {
return new WP_REST_Response([‘success’=>false,’message’=>’Nama dan WhatsApp wajib diisi.’], 400);
}
// Basic anti-spam: same WA + same name within 30 seconds is treated as duplicate.
$recent = $wpdb->get_var($wpdb->prepare(
“SELECT transaction_id FROM {$this->table} WHERE whatsapp=%s AND name=%s AND created_at >= %s ORDER BY id DESC LIMIT 1″,
$wa, $name, gmdate(‘Y-m-d H:i:s’, time()-30)
));
if ($recent) {
return new WP_REST_Response([‘success’=>true,’duplicate’=>true,’transaction_id’=>$recent], 200);
}
$now = current_time(‘mysql’);
$transaction = $this->transaction_id();
$row = [
‘transaction_id’ => $transaction,
‘created_at’ => $now,
‘updated_at’ => $now,
‘name’ => $name,
‘whatsapp’ => $wa,
‘address’ => $this->clean($data[‘address’] ?? ”),
‘join_status’ => $this->clean($data[‘join_status’] ?? ‘Gabung Bisnis’),
‘package’ => $this->clean($data[‘package’] ?? ”),
‘package_quantity’ => $this->clean($data[‘package_quantity’] ?? ”),
‘variant’ => $this->clean($data[‘variant’] ?? ”),
‘notes’ => $this->clean($data[‘notes’] ?? ”),
‘referral_name’ => $this->clean($data[‘referral_name’] ?? ”),
‘referral_whatsapp’ => $this->clean($data[‘referral_whatsapp’] ?? ”),
‘referral_city’ => $this->clean($data[‘referral_city’] ?? ”),
‘landing_page’ => esc_url_raw($data[‘landing_page’] ?? ”),
‘amount’ => (float)($data[‘amount’] ?? 0),
‘payment_method’ => $this->clean($data[‘payment_method’] ?? ‘Transfer Manual’),
‘payment_status’ => ‘pending’,
];
$formats = [‘%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%s’,’%f’,’%s’,’%s’];
$ok = $wpdb->insert($this->table, $row, $formats);
if (!$ok) return new WP_REST_Response([‘success’=>false,’message’=>’Gagal menyimpan pendaftaran.’], 500);
return new WP_REST_Response([
‘success’=>true,
‘transaction_id’=>$transaction,
‘id’=>(int)$wpdb->insert_id,
‘message’=>’Pendaftaran tersimpan.’
], 201);
}
public function update_payment(WP_REST_Request $request) {
global $wpdb;
$id = absint($request[‘id’]);
$data = $request->get_json_params();
if (!is_array($data)) $data = $request->get_params();
$allowed_status = [‘pending’,’waiting_verification’,’paid’,’cancelled’];
$status = sanitize_key($data[‘payment_status’] ?? ‘pending’);
if (!in_array($status, $allowed_status, true)) $status = ‘pending’;
$amount = isset($data[‘amount’]) ? (float)$data[‘amount’] : 0;
$method = $this->clean($data[‘payment_method’] ?? ”);
$admin_notes = $this->clean($data[‘admin_notes’] ?? ”);
$proof = esc_url_raw($data[‘proof_url’] ?? ”);
$paid_at = ($status === ‘paid’) ? current_time(‘mysql’) : null;
$updated = $wpdb->update($this->table, [
‘payment_status’=>$status,
‘amount’=>$amount,
‘payment_method’=>$method,
‘proof_url’=>$proof,
‘admin_notes’=>$admin_notes,
‘paid_at’=>$paid_at,
‘updated_at’=>current_time(‘mysql’)
], [‘id’=>$id], [‘%s’,’%f’,’%s’,’%s’,’%s’,’%s’,’%s’], [‘%d’]);
if ($updated === false) return new WP_REST_Response([‘success’=>false,’message’=>’Gagal memperbarui.’], 500);
return [‘success’=>true];
}
public function admin_menu() {
add_menu_page(‘GEIS Payment Dashboard’,’GEIS Payments’,’manage_options’,’geis-payment-dashboard’,[$this,’dashboard_page’],’dashicons-chart-area’,26);
}
public function admin_assets($hook) {
if ($hook !== ‘toplevel_page_geis-payment-dashboard’) return;
wp_enqueue_style(‘geis-pd-admin’, GEIS_PD_URL.’assets/css/admin.css’, [], GEIS_PD_VERSION);
wp_enqueue_script(‘geis-pd-admin’, GEIS_PD_URL.’assets/js/admin.js’, [], GEIS_PD_VERSION, true);
wp_localize_script(‘geis-pd-admin’,’GEIS_PD’,[
‘rest’=>esc_url_raw(rest_url(‘geis/v1’)),
‘nonce’=>wp_create_nonce(‘wp_rest’),
‘ajax’=>admin_url(‘admin-post.php’),
]);
}
public function frontend_assets() {
wp_enqueue_script(‘geis-pd-front’, GEIS_PD_URL.’assets/js/frontend.js’, [], GEIS_PD_VERSION, true);
wp_localize_script(‘geis-pd-front’,’GEIS_PD_FRONT’,[
‘endpoint’=>esc_url_raw(rest_url(‘geis/v1/register’)),
]);
}
private function get_rows() {
global $wpdb;
$search = isset($_GET[‘s’]) ? sanitize_text_field(wp_unslash($_GET[‘s’])) : ”;
$status = isset($_GET[‘status’]) ? sanitize_key($_GET[‘status’]) : ”;
$from = isset($_GET[‘from’]) ? sanitize_text_field($_GET[‘from’]) : ”;
$to = isset($_GET[‘to’]) ? sanitize_text_field($_GET[‘to’]) : ”;
$where = [‘1=1′]; $args=[];
if ($search !== ”) {
$like=’%’.$wpdb->esc_like($search).’%’;
$where[]='(transaction_id LIKE %s OR name LIKE %s OR whatsapp LIKE %s OR referral_name LIKE %s)’;
array_push($args,$like,$like,$like,$like);
}
if ($status && in_array($status,[‘pending’,’waiting_verification’,’paid’,’cancelled’],true)) { $where[]=’payment_status=%s’; $args[]=$status; }
if ($from) { $where[]=’DATE(created_at) >= %s’; $args[]=$from; }
if ($to) { $where[]=’DATE(created_at) <= %s'; $args[]=$to; }
$sql='SELECT * FROM '.$this->table.’ WHERE ‘.implode(‘ AND ‘,$where).’ ORDER BY id DESC LIMIT 500′;
return $args ? $wpdb->get_results($wpdb->prepare($sql,$args)) : $wpdb->get_results($sql);
}
private function stats() {
global $wpdb;
return [
‘all’=>(int)$wpdb->get_var(“SELECT COUNT(*) FROM {$this->table}”),
‘pending’=>(int)$wpdb->get_var(“SELECT COUNT(*) FROM {$this->table} WHERE payment_status=’pending'”),
‘waiting’=>(int)$wpdb->get_var(“SELECT COUNT(*) FROM {$this->table} WHERE payment_status=’waiting_verification'”),
‘paid’=>(int)$wpdb->get_var(“SELECT COUNT(*) FROM {$this->table} WHERE payment_status=’paid'”),
‘revenue’=>(float)$wpdb->get_var(“SELECT COALESCE(SUM(amount),0) FROM {$this->table} WHERE payment_status=’paid'”),
];
}
public function dashboard_page() {
if (!current_user_can(‘manage_options’)) return;
$rows=$this->get_rows(); $stats=$this->stats();
$base=admin_url(‘admin.php?page=geis-payment-dashboard’);
?>
GEIS Payment Dashboard
Pendaftaran bisnis, verifikasi pembayaran manual, dan riwayat transaksi.
Total Pendaftar
Menunggu Bayar
Perlu Verifikasi
Sudah Lunas
Total Pembayaran LunasRp