mirror of
https://github.com/myronblair/tomtomgames
synced 2026-06-30 17:51:08 -05:00
Initial commit
This commit is contained in:
+971
@@ -0,0 +1,971 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
requireAdmin();
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ─── STATS ────────────────────────────────────────────────
|
||||
case 'stats':
|
||||
echo json_encode(['success' => true, 'stats' => [
|
||||
'total_users' => db()->query("SELECT COUNT(*) FROM users")->fetchColumn(),
|
||||
'active_users' => db()->query("SELECT COUNT(*) FROM users WHERE status='active'")->fetchColumn(),
|
||||
'pending_purchases' => db()->query("SELECT COUNT(*) FROM token_purchases WHERE status='pending'")->fetchColumn(),
|
||||
'pending_cashouts' => db()->query("SELECT COUNT(*) FROM cashout_requests WHERE status='pending'")->fetchColumn(),
|
||||
'pending_signups' => db()->query("SELECT COUNT(*) FROM pending_registrations WHERE expires_at > NOW()")->fetchColumn(),
|
||||
'total_tokens_sold' => db()->query("SELECT COALESCE(SUM(tokens),0) FROM token_purchases WHERE status='completed'")->fetchColumn(),
|
||||
'total_revenue' => db()->query("SELECT COALESCE(SUM(amount_cents),0)/100 FROM token_purchases WHERE status='completed'")->fetchColumn(),
|
||||
]]);
|
||||
break;
|
||||
|
||||
// ─── PENDING SIGNUPS ──────────────────────────────────────
|
||||
case 'pending_signups':
|
||||
$rows = db()->query("SELECT id,username,alias,email,expires_at,created_at FROM pending_registrations WHERE expires_at > NOW() ORDER BY created_at DESC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'pending'=>$rows]);
|
||||
break;
|
||||
|
||||
case 'delete_pending':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
db()->prepare("DELETE FROM pending_registrations WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'approve_pending':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
// Fetch the pending record
|
||||
$stmt = db()->prepare("SELECT * FROM pending_registrations WHERE id=?");
|
||||
$stmt->execute([$id]);
|
||||
$pending = $stmt->fetch();
|
||||
if (!$pending) { echo json_encode(['success'=>false,'error'=>'Pending signup not found']); exit; }
|
||||
// Check username/email not already taken
|
||||
$chkUser = db()->prepare("SELECT id FROM users WHERE username=?");
|
||||
$chkUser->execute([$pending['username']]);
|
||||
if ($chkUser->fetch()) { echo json_encode(['success'=>false,'error'=>'Username already taken']); exit; }
|
||||
if (!empty($pending['email'])) {
|
||||
$chkEmail = db()->prepare("SELECT id FROM users WHERE email=?");
|
||||
$chkEmail->execute([$pending['email']]);
|
||||
if ($chkEmail->fetch()) { echo json_encode(['success'=>false,'error'=>'Email already registered']); exit; }
|
||||
}
|
||||
// Create the user account — bypass email verification
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
db()->prepare("INSERT INTO users (username,password,alias,email,email_verified,tokens,is_admin,status)
|
||||
VALUES (?,?,?,?,1,0,0,'active')")
|
||||
->execute([$pending['username'],$pending['password'],$pending['alias'],$pending['email']]);
|
||||
db()->prepare("DELETE FROM pending_registrations WHERE id=?")->execute([$id]);
|
||||
db()->commit();
|
||||
logActivity('account_approved', (int)db()->lastInsertId(), (int)$_SESSION['user_id'], 'user', 0, 'Account approved for '.$pending['username']);
|
||||
echo json_encode(['success'=>true,'username'=>$pending['username']]);
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'Could not create account']);
|
||||
}
|
||||
break;
|
||||
|
||||
// ─── PURCHASES ────────────────────────────────────────────
|
||||
case 'purchases':
|
||||
$status = $_GET['status'] ?? 'pending';
|
||||
if ($status === 'all') {
|
||||
$stmt = db()->query("SELECT tp.*, u.username, u.alias FROM token_purchases tp JOIN users u ON tp.user_id=u.id ORDER BY tp.created_at DESC LIMIT 200");
|
||||
} else {
|
||||
$stmt = db()->prepare("SELECT tp.*, u.username, u.alias FROM token_purchases tp JOIN users u ON tp.user_id=u.id WHERE tp.status=? ORDER BY tp.created_at DESC LIMIT 200");
|
||||
$stmt->execute([$status]);
|
||||
}
|
||||
echo json_encode(['success' => true, 'purchases' => $stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
// ─── RESOLVE PURCHASE (approve manual / reject) ──────────
|
||||
case 'resolve_purchase':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
$status = $data['status'] ?? '';
|
||||
$note = trim($data['note'] ?? '');
|
||||
|
||||
if (!in_array($status, ['completed','failed'])) {
|
||||
echo json_encode(['success'=>false,'error'=>'Invalid status']); exit;
|
||||
}
|
||||
|
||||
// Fetch purchase
|
||||
$row = db()->prepare("SELECT * FROM token_purchases WHERE id=? AND status='pending'");
|
||||
$row->execute([$id]);
|
||||
$purchase = $row->fetch();
|
||||
|
||||
if (!$purchase) {
|
||||
echo json_encode(['success'=>false,'error'=>'Purchase not found or already resolved']); exit;
|
||||
}
|
||||
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
if ($status === 'completed') {
|
||||
// Credit tokens to user
|
||||
logAdminAction('TOKENS_ADJUSTED', $adminId, 'user', isset($targetId)?(int)$targetId:0, 'Manual token adjustment: '.($data['tokens']??0).' tokens', '', ($data['tokens']??''), 'critical');
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$purchase['tokens'], $purchase['user_id']]);
|
||||
}
|
||||
db()->prepare("UPDATE token_purchases SET status=?,admin_note=? WHERE id=?")->execute([$status, $note, $id]);
|
||||
db()->commit();
|
||||
echo json_encode(['success'=>true]);
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'DB error']);
|
||||
}
|
||||
break;
|
||||
|
||||
// ─── CASHOUTS ─────────────────────────────────────────────
|
||||
case 'cashouts':
|
||||
$status = $_GET['status'] ?? 'pending';
|
||||
$valid = ['pending','sent','approved','rejected','deleted'];
|
||||
if (!in_array($status, $valid)) $status = 'pending';
|
||||
if ($status === 'pending') {
|
||||
// Show both pending (player editing) and locked (submitted to admin)
|
||||
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias AS user_alias FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.status IN ('pending','locked') ORDER BY cr.status DESC, cr.created_at DESC");
|
||||
$stmt->execute();
|
||||
} elseif ($status === 'sent') {
|
||||
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias AS user_alias FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.status IN ('sent','approved') ORDER BY cr.created_at DESC");
|
||||
$stmt->execute();
|
||||
} else {
|
||||
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias AS user_alias FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.status=? ORDER BY cr.created_at DESC");
|
||||
$stmt->execute([$status]);
|
||||
}
|
||||
echo json_encode(['success'=>true,'cashouts'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
case 'resolve_cashout':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
$status = $data['status'] ?? '';
|
||||
$note = trim($data['note'] ?? '');
|
||||
// 'approved' is treated as 'sent' (payment sent to player)
|
||||
if ($status === 'approved') $status = 'sent';
|
||||
if (!in_array($status, ['sent','rejected','deleted'])) { echo json_encode(['success'=>false,'error'=>'Invalid status']); exit; }
|
||||
|
||||
$r = db()->prepare("SELECT user_id,tokens FROM cashout_requests WHERE id=? AND status IN ('pending','locked')");
|
||||
$r->execute([$id]);
|
||||
$req = $r->fetch();
|
||||
if (!$req) { echo json_encode(['success'=>false,'error'=>'Not found or already resolved']); exit; }
|
||||
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
// Return tokens to player if denied or deleted
|
||||
if (in_array($status, ['rejected','deleted'])) {
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$req['tokens'],$req['user_id']]);
|
||||
}
|
||||
db()->prepare("UPDATE cashout_requests SET status=?,admin_note=?,resolved_at=NOW() WHERE id=?")->execute([$status,$note,$id]);
|
||||
db()->commit();
|
||||
logActivity('cashout_'.$status, $req['user_id'], (int)$_SESSION['user_id'], 'cashout', $id,
|
||||
'Cashout '.$status.' by admin. Tokens: '.$req['tokens'].($note?' Note: '.$note:''));
|
||||
echo json_encode(['success'=>true,'status'=>$status]);
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'DB error']);
|
||||
}
|
||||
break;
|
||||
if (!in_array($status, ['approved','rejected'])) { echo json_encode(['success'=>false,'error'=>'Invalid status']); exit; }
|
||||
|
||||
if ($status === 'rejected') {
|
||||
$r = db()->prepare("SELECT user_id,tokens FROM cashout_requests WHERE id=? AND status='pending'");
|
||||
$r->execute([$id]);
|
||||
$req = $r->fetch();
|
||||
if ($req) db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$req['tokens'],$req['user_id']]);
|
||||
}
|
||||
db()->prepare("UPDATE cashout_requests SET status=?,admin_note=?,resolved_at=NOW() WHERE id=?")->execute([$status,$note,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── USERS LIST ───────────────────────────────────────────
|
||||
case 'users':
|
||||
$users = db()->query("SELECT id,username,alias,email,email_verified,tokens,is_admin,status,created_at,last_login FROM users ORDER BY created_at DESC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'users'=>$users]);
|
||||
break;
|
||||
|
||||
// ─── SINGLE USER DETAIL ───────────────────────────────────
|
||||
case 'user_detail':
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$stmt = db()->prepare("SELECT id,username,alias,email,email_verified,tokens,is_admin,status,created_at,last_login FROM users WHERE id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$user = $stmt->fetch();
|
||||
if (!$user) { echo json_encode(['success'=>false,'error'=>'User not found']); exit; }
|
||||
// Rich stats
|
||||
$s1 = db()->prepare("SELECT COALESCE(SUM(amount_cents),0)/100 FROM token_purchases WHERE user_id=? AND status='completed'");
|
||||
$s1->execute([$uid]);
|
||||
$s2 = db()->prepare("SELECT COUNT(*) FROM token_purchases WHERE user_id=? AND status='completed'"); $s2->execute([$uid]);
|
||||
$s3 = db()->prepare("SELECT COUNT(*) FROM token_purchases WHERE user_id=? AND status='pending'"); $s3->execute([$uid]);
|
||||
$s4 = db()->prepare("SELECT COUNT(*) FROM token_purchases WHERE user_id=? AND status='failed'"); $s4->execute([$uid]);
|
||||
$s5 = db()->prepare("SELECT COUNT(*) FROM cashout_requests WHERE user_id=?"); $s5->execute([$uid]);
|
||||
$s6 = db()->prepare("SELECT COALESCE(SUM(tokens),0) FROM token_purchases WHERE user_id=? AND status='completed'"); $s6->execute([$uid]);
|
||||
$stats = [
|
||||
'total_spent' => $s1->fetchColumn(),
|
||||
'completed_purchases'=> $s2->fetchColumn(),
|
||||
'pending_purchases' => $s3->fetchColumn(),
|
||||
'failed_purchases' => $s4->fetchColumn(),
|
||||
'total_cashouts' => $s5->fetchColumn(),
|
||||
'total_tokens_bought'=> $s6->fetchColumn(),
|
||||
];
|
||||
echo json_encode(['success'=>true,'user'=>$user,'stats'=>$stats]);
|
||||
break;
|
||||
|
||||
// ─── USER PURCHASES ───────────────────────────────────────
|
||||
case 'user_purchases':
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
$stmt = db()->prepare("SELECT id,tokens,amount_cents,payment_method,square_payment_id,platform_id,game_alias,player_name,billing_name,billing_address,billing_city,billing_state,billing_zip,billing_email,is_custom,failure_reason,card_brand,card_last4,receipt_url,status,admin_note,created_at FROM token_purchases WHERE user_id=? ORDER BY created_at DESC LIMIT 100");
|
||||
$stmt->execute([$uid]);
|
||||
echo json_encode(['success'=>true,'purchases'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
// ─── USER CASHOUTS ────────────────────────────────────────
|
||||
case 'user_cashouts':
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
$stmt = db()->prepare("SELECT * FROM cashout_requests WHERE user_id=? ORDER BY created_at DESC LIMIT 100");
|
||||
$stmt->execute([$uid]);
|
||||
echo json_encode(['success'=>true,'cashouts'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
// ─── ADJUST TOKENS ────────────────────────────────────────
|
||||
case 'adjust_tokens':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
$amount = (float)($data['amount'] ?? 0);
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$amount,$uid]);
|
||||
$bal = db()->prepare("SELECT tokens FROM users WHERE id=?"); $bal->execute([$uid]);
|
||||
$newBal = $bal->fetchColumn();
|
||||
echo json_encode(['success'=>true,'new_balance'=>$newBal]);
|
||||
break;
|
||||
|
||||
// ─── SET EXACT TOKEN BALANCE ──────────────────────────────
|
||||
case 'set_tokens':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
$bal = (float)($data['balance'] ?? 0);
|
||||
if ($bal < 0) { echo json_encode(['success'=>false,'error'=>'Balance cannot be negative']); exit; }
|
||||
db()->prepare("UPDATE users SET tokens=? WHERE id=?")->execute([$bal,$uid]);
|
||||
echo json_encode(['success'=>true,'new_balance'=>$bal]);
|
||||
break;
|
||||
|
||||
// ─── EDIT USER ────────────────────────────────────────────
|
||||
case 'edit_user':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
$username = strtolower(trim($data['username'] ?? ''));
|
||||
$alias = trim($data['alias'] ?? '');
|
||||
$email = strtolower(trim($data['email'] ?? ''));
|
||||
$password = trim($data['password'] ?? '');
|
||||
|
||||
if (!$uid || empty($username) || empty($alias))
|
||||
{ echo json_encode(['success'=>false,'error'=>'Username and alias required']); exit; }
|
||||
if (!preg_match('/^[a-z0-9_]{3,50}$/', $username))
|
||||
{ echo json_encode(['success'=>false,'error'=>'Invalid username format']); exit; }
|
||||
if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL))
|
||||
{ echo json_encode(['success'=>false,'error'=>'Invalid email address']); exit; }
|
||||
|
||||
// Check username not taken by another user
|
||||
$chk = db()->prepare("SELECT id FROM users WHERE username=? AND id!=?");
|
||||
$chk->execute([$username,$uid]);
|
||||
if ($chk->fetch()) { echo json_encode(['success'=>false,'error'=>'Username already taken']); exit; }
|
||||
|
||||
if (!empty($email)) {
|
||||
$chk2 = db()->prepare("SELECT id FROM users WHERE email=? AND id!=?");
|
||||
$chk2->execute([$email,$uid]);
|
||||
if ($chk2->fetch()) { echo json_encode(['success'=>false,'error'=>'Email already in use']); exit; }
|
||||
}
|
||||
|
||||
if (!empty($password)) {
|
||||
if (strlen($password) < 6) { echo json_encode(['success'=>false,'error'=>'Password must be 6+ characters']); exit; }
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
db()->prepare("UPDATE users SET username=?,alias=?,email=?,password=? WHERE id=?")->execute([$username,$alias,$email,$hash,$uid]);
|
||||
} else {
|
||||
db()->prepare("UPDATE users SET username=?,alias=?,email=? WHERE id=?")->execute([$username,$alias,$email,$uid]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── TOGGLE ADMIN ROLE ───────────────────────────────────
|
||||
case 'toggle_admin':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
// Master admin (ID=1) can NEVER lose admin status
|
||||
if ($uid === MASTER_ADMIN_ID) {
|
||||
echo json_encode(['success'=>false,'error'=>'Master admin cannot be modified.']); exit;
|
||||
}
|
||||
// Cannot remove your own admin
|
||||
if ($uid === (int)$_SESSION['user_id']) {
|
||||
echo json_encode(['success'=>false,'error'=>'You cannot change your own admin status.']); exit;
|
||||
}
|
||||
// Only master admin can grant/revoke admin
|
||||
if ((int)$_SESSION['user_id'] !== MASTER_ADMIN_ID) {
|
||||
echo json_encode(['success'=>false,'error'=>'Only the master admin can change admin roles.']); exit;
|
||||
}
|
||||
$stmt = db()->prepare("SELECT is_admin FROM users WHERE id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$current = $stmt->fetchColumn();
|
||||
$new_val = $current ? 0 : 1;
|
||||
if ($new_val) {
|
||||
db()->prepare("UPDATE users SET is_admin=1, email_verified=1 WHERE id=?")->execute([$uid]);
|
||||
} else {
|
||||
db()->prepare("UPDATE users SET is_admin=0 WHERE id=?")->execute([$uid]);
|
||||
}
|
||||
// Force the affected user to re-login by invalidating their sessions
|
||||
// Store a flag in DB that forces re-auth on next request
|
||||
db()->prepare("UPDATE users SET last_login=last_login WHERE id=?")->execute([$uid]);
|
||||
logActivity($new_val?'admin_granted':'admin_revoked', $uid, (int)$_SESSION['user_id'], 'user', $uid, 'Admin status changed to '.($new_val?'admin':'player'), '', 'admin', '', '', 'warning');
|
||||
echo json_encode(['success'=>true, 'is_admin'=>$new_val, 'needs_relogin'=>true, 'message'=>$new_val ? 'Admin access granted. User must log out and back in.' : 'Admin access removed. User must log out and back in.']);
|
||||
break;
|
||||
|
||||
// ─── TOGGLE SUSPEND ───────────────────────────────────────
|
||||
case 'toggle_user':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
if ($uid === MASTER_ADMIN_ID) { echo json_encode(['success'=>false,'error'=>'Cannot suspend the master admin.']); exit; }
|
||||
logAdminAction('USER_STATUS_CHANGE', $adminId, 'user', isset($userId)?(int)$userId:0, 'Changed user status to: '.($data['status']??'unknown'), '', ($data['status']??''), 'warning');
|
||||
db()->prepare("UPDATE users SET status=IF(status='active','suspended','active') WHERE id=?")->execute([$uid]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── DELETE USER ──────────────────────────────────────────
|
||||
case 'delete_user':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'Invalid user']); exit; }
|
||||
// Prevent deleting own account
|
||||
if ($uid === MASTER_ADMIN_ID) { echo json_encode(['success'=>false,'error'=>'Cannot delete the master admin account.']); exit; }
|
||||
if ($uid === (int)$_SESSION['user_id']) { echo json_encode(['success'=>false,'error'=>'Cannot delete your own account']); exit; }
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
db()->prepare("DELETE FROM chat_messages WHERE user_id=?")->execute([$uid]);
|
||||
db()->prepare("DELETE FROM cashout_requests WHERE user_id=?")->execute([$uid]);
|
||||
db()->prepare("DELETE FROM token_purchases WHERE user_id=?")->execute([$uid]);
|
||||
db()->prepare("DELETE FROM users WHERE id=?")->execute([$uid]);
|
||||
db()->commit();
|
||||
echo json_encode(['success'=>true]);
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'Delete failed']);
|
||||
}
|
||||
break;
|
||||
|
||||
// ─── SEND PASSWORD RESET ──────────────────────────────────
|
||||
case 'send_password_reset':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
$stmt = db()->prepare("SELECT email,alias FROM users WHERE id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$user = $stmt->fetch();
|
||||
if (!$user || empty($user['email'])) { echo json_encode(['success'=>false,'error'=>'No email on file']); exit; }
|
||||
// Generate reset token — reuse pending_registrations pattern
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$exp = date('Y-m-d H:i:s', time() + 3600); // 1 hour
|
||||
db()->prepare("INSERT INTO pending_registrations (username,password,alias,email,token,expires_at) VALUES ('__reset__','',''.?,?,'__reset__',?) ON DUPLICATE KEY UPDATE token=VALUES(token),expires_at=VALUES(expires_at)")->execute([$user['alias'],$user['email'],$token,$exp]);
|
||||
// Simple reset email
|
||||
$resetUrl = rtrim(SITE_URL,'/') . '/reset_password.php?token=' . urlencode($token);
|
||||
$subject = SITE_NAME . ' — Password Reset Request';
|
||||
$body = "Hi {$user['alias']},\n\nA password reset was requested for your account.\n\nClick here to reset: {$resetUrl}\n\nExpires in 1 hour. If you didn't request this, ignore this email.\n\n— " . SITE_NAME;
|
||||
$headers = "From: " . MAIL_FROM_NAME . " <" . MAIL_FROM . ">\r\nReply-To: " . MAIL_REPLY_TO;
|
||||
mail($user['email'], $subject, $body, $headers, '-f' . MAIL_FROM);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── PLATFORM ACCOUNTS ────────────────────────────────
|
||||
case 'platform_accounts_list':
|
||||
$status = $_GET['status'] ?? 'pending';
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
if ($uid) {
|
||||
$stmt = db()->prepare("SELECT pa.*, COALESCE(p.name,pa.platform_slug) AS platform_name, u.username, u.alias AS user_alias FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug JOIN users u ON pa.user_id=u.id WHERE pa.user_id=? ORDER BY pa.requested_at DESC");
|
||||
$stmt->execute([$uid]);
|
||||
} else {
|
||||
$valid = ['pending','approved','denied','deleted'];
|
||||
if (!in_array($status,$valid)) $status='pending';
|
||||
$stmt = db()->prepare("SELECT pa.*, COALESCE(p.name,pa.platform_slug) AS platform_name, u.username, u.alias AS user_alias FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug JOIN users u ON pa.user_id=u.id WHERE pa.status=? ORDER BY pa.requested_at DESC");
|
||||
$stmt->execute([$status]);
|
||||
}
|
||||
echo json_encode(['success'=>true,'accounts'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
case 'platform_account_resolve':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id=$d['id']??0; $status=$d['status']??'';
|
||||
$uname=substr(trim($d['platform_username']??''),0,100);
|
||||
$pass=substr(trim($d['platform_password']??''),0,200);
|
||||
$note=substr(trim($d['admin_note']??''),0,300);
|
||||
if (!in_array($status,['approved','denied','deleted'])){echo json_encode(['success'=>false,'error'=>'Invalid status']);exit;}
|
||||
$chk=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$chk->execute([$id]);$row=$chk->fetch();
|
||||
if (!$row){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
||||
db()->prepare("UPDATE platform_accounts SET status=?,platform_username=?,platform_password=?,admin_note=?,resolved_at=NOW(),admin_id=? WHERE id=?")
|
||||
->execute([$status,$uname,$pass,$note,(int)$_SESSION['user_id'],$id]);
|
||||
if ($status==='approved'&&$uname) {
|
||||
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
||||
->execute([$row['user_id'],$row['platform_slug'],$uname]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'platform_account_update':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d=json_decode(file_get_contents('php://input'),true);
|
||||
$id=$d['id']??0;
|
||||
$uname=substr(trim($d['platform_username']??''),0,100);
|
||||
$pass=substr(trim($d['platform_password']??''),0,200);
|
||||
$note=substr(trim($d['admin_note']??''),0,300);
|
||||
$chk=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$chk->execute([$id]);$row=$chk->fetch();
|
||||
if (!$row){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
||||
db()->prepare("UPDATE platform_accounts SET platform_username=?,platform_password=?,admin_note=? WHERE id=?")
|
||||
->execute([$uname,$pass,$note,$id]);
|
||||
if ($uname){
|
||||
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
||||
->execute([$row['user_id'],$row['platform_slug'],$uname]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
$rows = db()->query("
|
||||
SELECT b.*, u.username AS sender_name,
|
||||
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id) AS read_count,
|
||||
(SELECT COUNT(*) FROM broadcast_replies WHERE broadcast_id=b.id) AS reply_count,
|
||||
(SELECT COUNT(*) FROM users WHERE is_admin=0 AND status='active') AS total_players
|
||||
FROM broadcasts b JOIN users u ON b.admin_id=u.id
|
||||
ORDER BY b.sent_at DESC LIMIT 50
|
||||
")->fetchAll();
|
||||
echo json_encode(['success'=>true,'broadcasts'=>$rows]);
|
||||
break;
|
||||
|
||||
case 'broadcast_list':
|
||||
try {
|
||||
$sql = "SELECT b.id, b.subject, b.message, b.target, b.sent_at,
|
||||
u.username AS sender_name,
|
||||
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id) AS read_count,
|
||||
(SELECT COUNT(*) FROM broadcast_replies WHERE broadcast_id=b.id) AS reply_count,
|
||||
(SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0) AS total_players
|
||||
FROM broadcasts b
|
||||
JOIN users u ON b.admin_id=u.id
|
||||
ORDER BY b.sent_at DESC
|
||||
LIMIT 100";
|
||||
$stmt = db()->query($sql);
|
||||
echo json_encode(['success'=>true,'broadcasts'=>$stmt->fetchAll()]);
|
||||
} catch(Exception $e) {
|
||||
echo json_encode(['success'=>false,'error'=>$e->getMessage()]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'broadcast_send':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$subject = substr(trim($d['subject']??''),0,200);
|
||||
$message = substr(trim($d['message']??''),0,5000);
|
||||
$target = in_array($d['target']??'',['all','verified','unverified','admins']) ? $d['target'] : 'all';
|
||||
if (!$subject||!$message) { echo json_encode(['success'=>false,'error'=>'Subject and message required']); exit; }
|
||||
db()->prepare("INSERT INTO broadcasts (admin_id,subject,message,target) VALUES (?,?,?,?)")
|
||||
->execute([$_SESSION['user_id'],$subject,$message,$target]);
|
||||
$bid = db()->lastInsertId();
|
||||
// Count recipients
|
||||
$countQ = [
|
||||
'all' => "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0",
|
||||
'verified' => "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0 AND email_verified=1",
|
||||
'unverified' => "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0 AND email_verified=0",
|
||||
'admins' => "SELECT COUNT(*) FROM users WHERE is_admin=1",
|
||||
];
|
||||
$count = db()->query($countQ[$target])->fetchColumn();
|
||||
echo json_encode(['success'=>true,'id'=>$bid,'recipient_count'=>(int)$count]);
|
||||
break;
|
||||
|
||||
case 'broadcast_delete':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id']??0);
|
||||
db()->prepare("DELETE FROM broadcasts WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'broadcast_edit':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$subject = substr(trim($d['subject'] ?? ''), 0, 200);
|
||||
$message = trim($d['message'] ?? '');
|
||||
$target = in_array($d['target']??'', ['all','verified','unverified','admins']) ? $d['target'] : 'all';
|
||||
if (!$id || !$subject || !$message) { echo json_encode(['success'=>false,'error'=>'Missing fields']); exit; }
|
||||
db()->prepare("UPDATE broadcasts SET subject=?, message=?, target=? WHERE id=?")->execute([$subject, $message, $target, $id]);
|
||||
logAdminAction('BROADCAST_EDITED', $adminId, 'broadcast', $id, 'Edited broadcast #'.$id, '', '', 'info');
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'broadcast_resend':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'Missing ID']); exit; }
|
||||
$bc = db()->prepare("SELECT * FROM broadcasts WHERE id=?");
|
||||
$bc->execute([$id]);
|
||||
$orig = $bc->fetch();
|
||||
if (!$orig) { echo json_encode(['success'=>false,'error'=>'Broadcast not found']); exit; }
|
||||
// Count recipients
|
||||
$target = $orig['target'];
|
||||
$countSql = "SELECT COUNT(*) FROM users WHERE status='active' AND is_admin=0";
|
||||
if ($target === 'verified') $countSql .= " AND email_verified=1";
|
||||
if ($target === 'unverified') $countSql .= " AND email_verified=0";
|
||||
$recipientCount = (int)db()->query($countSql)->fetchColumn();
|
||||
// Delete old reads so everyone sees it again
|
||||
db()->prepare("DELETE FROM broadcast_reads WHERE broadcast_id=?")->execute([$id]);
|
||||
// Update sent_at to now
|
||||
db()->prepare("UPDATE broadcasts SET sent_at=NOW() WHERE id=?")->execute([$id]);
|
||||
logAdminAction('BROADCAST_RESENT', $adminId, 'broadcast', $id, 'Resent broadcast #'.$id.' to '.$recipientCount.' players', '', '', 'info');
|
||||
echo json_encode(['success'=>true,'recipient_count'=>$recipientCount]);
|
||||
break;
|
||||
|
||||
case 'broadcast_reads':
|
||||
$bid = (int)($_GET['broadcast_id']??0);
|
||||
$rows = db()->prepare("SELECT br.read_at, u.username, u.alias FROM broadcast_reads br JOIN users u ON br.user_id=u.id WHERE br.broadcast_id=? ORDER BY br.read_at ASC");
|
||||
$rows->execute([$bid]);
|
||||
echo json_encode(['success'=>true,'reads'=>$rows->fetchAll()]);
|
||||
break;
|
||||
|
||||
case 'broadcast_replies':
|
||||
$bid = (int)($_GET['broadcast_id']??0);
|
||||
$rows = db()->prepare("SELECT br.*, u.username, u.alias, u.is_admin FROM broadcast_replies br JOIN users u ON br.user_id=u.id WHERE br.broadcast_id=? ORDER BY br.created_at ASC");
|
||||
$rows->execute([$bid]);
|
||||
echo json_encode(['success'=>true,'replies'=>$rows->fetchAll()]);
|
||||
break;
|
||||
|
||||
case 'broadcast_reply':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$bid = (int)($d['broadcast_id']??0);
|
||||
$msg = substr(trim($d['message']??''),0,1000);
|
||||
if (!$bid||!$msg) { echo json_encode(['success'=>false,'error'=>'Required fields missing']); exit; }
|
||||
db()->prepare("INSERT INTO broadcast_replies (broadcast_id,user_id,message) VALUES (?,?,?)")
|
||||
->execute([$bid,$_SESSION['user_id'],$msg]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
break;
|
||||
$rows = db()->query("SELECT * FROM cashout_method_types ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'types'=>$rows]);
|
||||
break;
|
||||
|
||||
case 'cashout_methods_create':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$slug = preg_replace('/[^a-z0-9_]/','',strtolower(trim($d['slug']??'')));
|
||||
$label= substr(trim($d['label']??''),0,100);
|
||||
$icon = substr(trim($d['icon']??'💰'),0,10);
|
||||
$desc = substr(trim($d['description']??''),0,200);
|
||||
$sort = (int)($d['sort_order']??99);
|
||||
$active=(int)(bool)($d['is_active']??1);
|
||||
if (!$slug||!$label){echo json_encode(['success'=>false,'error'=>'Slug and label required']);exit;}
|
||||
try {
|
||||
db()->prepare("INSERT INTO cashout_method_types (slug,label,icon,description,is_active,sort_order) VALUES (?,?,?,?,?,?)")
|
||||
->execute([$slug,$label,$icon,$desc,$active,$sort]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
} catch(Exception $e){ echo json_encode(['success'=>false,'error'=>'Slug already exists']); }
|
||||
break;
|
||||
|
||||
case 'cashout_methods_update':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id']??0);
|
||||
$label= substr(trim($d['label']??''),0,100);
|
||||
$icon = substr(trim($d['icon']??'💰'),0,10);
|
||||
$desc = substr(trim($d['description']??''),0,200);
|
||||
$sort = (int)($d['sort_order']??0);
|
||||
$active=(int)(bool)($d['is_active']??1);
|
||||
if (!$id||!$label){echo json_encode(['success'=>false,'error'=>'ID and label required']);exit;}
|
||||
db()->prepare("UPDATE cashout_method_types SET label=?,icon=?,description=?,is_active=?,sort_order=? WHERE id=?")
|
||||
->execute([$label,$icon,$desc,$active,$sort,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'cashout_methods_delete':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d=(json_decode(file_get_contents('php://input'),true));
|
||||
$id=(int)($d['id']??0);
|
||||
if (!$id){echo json_encode(['success'=>false,'error'=>'ID required']);exit;}
|
||||
db()->prepare("DELETE FROM cashout_method_types WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ──
|
||||
case 'platform_account_deny':
|
||||
if ($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['success'=>false]);exit;}
|
||||
$d=json_decode(file_get_contents('php://input'),true);
|
||||
$id=(int)($d['id']??0);$nt=substr(trim($d['admin_note']??''),0,500);
|
||||
db()->prepare("UPDATE platform_accounts SET status='denied',admin_note=?,admin_id=? WHERE id=?")->execute([$nt,$_SESSION['user_id'],$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'platform_account_delete':
|
||||
if ($_SERVER['REQUEST_METHOD']!=='POST'){echo json_encode(['success'=>false]);exit;}
|
||||
$d=json_decode(file_get_contents('php://input'),true);
|
||||
$id=(int)($d['id']??0);
|
||||
db()->prepare("DELETE FROM platform_accounts WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'platform_accounts_user':
|
||||
$uid=(int)($_GET['user_id']??0);
|
||||
$stmt=db()->prepare("SELECT pa.*,COALESCE(p.name,pa.platform_name,pa.platform_slug) AS display_name,p.color,p.player_url FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug WHERE pa.user_id=? ORDER BY pa.requested_at DESC");
|
||||
$stmt->execute([$uid]);
|
||||
echo json_encode(['success'=>true,'accounts'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
case 'activity_log':
|
||||
case 'activity_log_v2':
|
||||
$page = max(1, (int)($_GET['page']??1));
|
||||
$limit = 20;
|
||||
$offset = ($page - 1) * $limit;
|
||||
$category = trim($_GET['category'] ?? '');
|
||||
$severity = trim($_GET['severity'] ?? '');
|
||||
$search = trim($_GET['search'] ?? '');
|
||||
$date = trim($_GET['date'] ?? '');
|
||||
|
||||
$where = ["al.created_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)"];
|
||||
$params = [];
|
||||
|
||||
if ($category) { $where[] = "al.category = ?"; $params[] = $category; }
|
||||
if ($severity) { $where[] = "al.severity = ?"; $params[] = $severity; }
|
||||
if ($date) { $where[] = "DATE(al.created_at) = ?"; $params[] = $date; }
|
||||
if ($search) {
|
||||
$where[] = "(al.action LIKE ? OR al.detail LIKE ? OR u.username LIKE ? OR u.alias LIKE ? OR al.ip LIKE ?)";
|
||||
$s = '%'.$search.'%';
|
||||
$params = array_merge($params, [$s,$s,$s,$s,$s]);
|
||||
}
|
||||
|
||||
$whereStr = implode(' AND ', $where);
|
||||
$baseQuery = "FROM activity_log al
|
||||
LEFT JOIN users u ON al.user_id = u.id
|
||||
LEFT JOIN users a ON al.admin_id = a.id
|
||||
WHERE $whereStr";
|
||||
|
||||
$countStmt = db()->prepare("SELECT COUNT(*) $baseQuery");
|
||||
$countStmt->execute($params);
|
||||
$total = (int)$countStmt->fetchColumn();
|
||||
|
||||
$dataStmt = db()->prepare("SELECT al.*, u.username, u.alias,
|
||||
a.username AS admin_username
|
||||
$baseQuery
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT $limit OFFSET $offset");
|
||||
$dataStmt->execute($params);
|
||||
$events = $dataStmt->fetchAll();
|
||||
|
||||
// Stats for the current filter set
|
||||
$statsParams = $params;
|
||||
$statsStmt = db()->prepare("SELECT
|
||||
SUM(al.severity='critical') AS critical,
|
||||
SUM(al.severity='warning') AS warning,
|
||||
COUNT(DISTINCT al.ip) AS unique_ips
|
||||
$baseQuery");
|
||||
$statsStmt->execute($statsParams);
|
||||
$stats = $statsStmt->fetch();
|
||||
|
||||
echo json_encode(['success'=>true,'events'=>$events,'total'=>$total,'page'=>$page,'stats'=>$stats]);
|
||||
break;
|
||||
|
||||
case 'activity_log_csv':
|
||||
$category = trim($_GET['category'] ?? '');
|
||||
$severity = trim($_GET['severity'] ?? '');
|
||||
$search = trim($_GET['search'] ?? '');
|
||||
$date = trim($_GET['date'] ?? '');
|
||||
|
||||
$where = ["al.created_at >= DATE_SUB(NOW(), INTERVAL 90 DAY)"];
|
||||
$params = [];
|
||||
if ($category) { $where[] = "al.category = ?"; $params[] = $category; }
|
||||
if ($severity) { $where[] = "al.severity = ?"; $params[] = $severity; }
|
||||
if ($date) { $where[] = "DATE(al.created_at) = ?"; $params[] = $date; }
|
||||
if ($search) {
|
||||
$where[] = "(al.action LIKE ? OR al.detail LIKE ? OR u.username LIKE ?)";
|
||||
$s = '%'.$search.'%'; $params = array_merge($params, [$s,$s,$s]);
|
||||
}
|
||||
|
||||
$whereStr = implode(' AND ', $where);
|
||||
$stmt = db()->prepare("SELECT al.*, u.username, u.alias, a.username AS admin_username
|
||||
FROM activity_log al
|
||||
LEFT JOIN users u ON al.user_id=u.id
|
||||
LEFT JOIN users a ON al.admin_id=a.id
|
||||
WHERE $whereStr ORDER BY al.created_at DESC LIMIT 5000");
|
||||
$stmt->execute($params);
|
||||
$rows = $stmt->fetchAll();
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="tomtomgames_audit_' . date('Y-m-d') . '.csv"');
|
||||
$out = fopen('php://output', 'w');
|
||||
fputcsv($out, ['ID','Timestamp','Category','Severity','Action','Username','Alias','Admin','Detail','Old Value','New Value','IP','User Agent','Page','Session ID']);
|
||||
foreach ($rows as $r) {
|
||||
fputcsv($out, [$r['id'],$r['created_at'],$r['category'],$r['severity'],$r['action'],
|
||||
$r['username']??'',$r['alias']??'',$r['admin_username']??'',
|
||||
$r['detail']??'',$r['old_value']??'',$r['new_value']??'',
|
||||
$r['ip']??'',$r['user_agent']??'',$r['page']??'',$r['session_id']??'']);
|
||||
}
|
||||
fclose($out);
|
||||
exit;
|
||||
break;
|
||||
|
||||
// ─── CASHOUT METHODS: list (admin) ────────────────────
|
||||
case 'cashout_methods_list':
|
||||
$rows = db()->query("SELECT * FROM cashout_method_types ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'types'=>$rows]);
|
||||
break;
|
||||
|
||||
// ─── PAYMENT SETTINGS: list (admin) ───────────────────
|
||||
case 'payment_settings_list':
|
||||
$rows = db()->query("SELECT * FROM payment_settings ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'methods'=>$rows]);
|
||||
break;
|
||||
|
||||
case 'payment_settings_update':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$label= substr(trim($d['label']??''), 0, 100);
|
||||
$handle = substr(trim($d['handle']??''), 0, 200);
|
||||
$inst = substr(trim($d['instructions']??''), 0, 500);
|
||||
$enabled = (int)(bool)($d['is_enabled'] ?? 1);
|
||||
$sort = (int)($d['sort_order'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
||||
db()->prepare("UPDATE payment_settings SET label=?,handle=?,instructions=?,is_enabled=?,sort_order=? WHERE id=?")
|
||||
->execute([$label,$handle,$inst,$enabled,$sort,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
|
||||
// ─── PAYOUT METHODS: get for user ────────────────────────
|
||||
case 'payout_methods_get':
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$rows = db()->prepare("SELECT * FROM payout_methods WHERE user_id=? ORDER BY is_default DESC, id ASC");
|
||||
$rows->execute([$uid]);
|
||||
echo json_encode(['success'=>true,'methods'=>$rows->fetchAll()]);
|
||||
break;
|
||||
|
||||
// ─── GAME ALIASES: get ────────────────────────────────
|
||||
case 'game_aliases_get':
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$stmt = db()->prepare("SELECT platform_slug, alias FROM game_aliases WHERE user_id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$rows = $stmt->fetchAll();
|
||||
$map = [];
|
||||
foreach ($rows as $r) $map[$r['platform_slug']] = $r['alias'];
|
||||
echo json_encode(['success'=>true,'aliases'=>$map]);
|
||||
break;
|
||||
|
||||
// ─── GAME ALIASES: save all ───────────────────────────
|
||||
case 'game_aliases_save_all':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
$aliases = $data['aliases'] ?? [];
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$stmt = db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?)
|
||||
ON DUPLICATE KEY UPDATE alias=VALUES(alias)");
|
||||
$del = db()->prepare("DELETE FROM game_aliases WHERE user_id=? AND platform_slug=?");
|
||||
foreach ($aliases as $slug => $alias) {
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($slug)));
|
||||
$alias = substr(trim($alias), 0, 100);
|
||||
if (!$slug) continue;
|
||||
if ($alias === '') $del->execute([$uid, $slug]);
|
||||
else $stmt->execute([$uid, $slug, $alias]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── PLATFORMS: admin list ────────────────────────────
|
||||
case 'platforms_admin':
|
||||
$rows = db()->query("SELECT * FROM platforms ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'platforms'=>$rows]);
|
||||
break;
|
||||
|
||||
// ─── PLATFORMS: create ────────────────────────────────
|
||||
case 'platforms_create':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($d['slug'] ?? '')));
|
||||
$name = substr(trim($d['name'] ?? ''), 0, 100);
|
||||
$purl = substr(trim($d['player_url'] ?? ''), 0, 500);
|
||||
$curl = substr(trim($d['console_url'] ?? ''), 0, 500);
|
||||
$color= preg_match('/^#[0-9a-fA-F]{3,8}$/', $d['color']??'') ? $d['color'] : '#f0c040';
|
||||
$sort = (int)($d['sort_order'] ?? 99);
|
||||
$active=(int)(bool)($d['is_active'] ?? 1);
|
||||
if (!$slug||!$name||!$purl) { echo json_encode(['success'=>false,'error'=>'Slug, name, and player URL required']); exit; }
|
||||
try {
|
||||
db()->prepare("INSERT INTO platforms (slug,name,player_url,console_url,color,sort_order,is_active) VALUES (?,?,?,?,?,?,?)")
|
||||
->execute([$slug,$name,$purl,$curl,$color,$sort,$active]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
} catch (Exception $e) { echo json_encode(['success'=>false,'error'=>'Slug already exists']); }
|
||||
break;
|
||||
|
||||
// ─── PLATFORMS: update ────────────────────────────────
|
||||
case 'platforms_update':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$name = substr(trim($d['name'] ?? ''), 0, 100);
|
||||
$purl = substr(trim($d['player_url'] ?? ''), 0, 500);
|
||||
$curl = substr(trim($d['console_url'] ?? ''), 0, 500);
|
||||
$color= preg_match('/^#[0-9a-fA-F]{3,8}$/', $d['color']??'') ? $d['color'] : '#f0c040';
|
||||
$sort = (int)($d['sort_order'] ?? 99);
|
||||
$active=(int)(bool)($d['is_active'] ?? 1);
|
||||
if (!$id||!$name||!$purl) { echo json_encode(['success'=>false,'error'=>'ID, name, and URL required']); exit; }
|
||||
db()->prepare("UPDATE platforms SET name=?,player_url=?,console_url=?,color=?,sort_order=?,is_active=? WHERE id=?")
|
||||
->execute([$name,$purl,$curl,$color,$sort,$active,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── PLATFORMS: delete ────────────────────────────────
|
||||
case 'platforms_delete':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
||||
db()->prepare("DELETE FROM platforms WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
case 'billing_get':
|
||||
$uid = (int)($_GET['user_id'] ?? 0);
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$stmt = db()->prepare("SELECT * FROM saved_billing WHERE user_id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$row = $stmt->fetch();
|
||||
// Admin sees masked card info
|
||||
if ($row) {
|
||||
$row['card_display'] = $row['card_brand'] && $row['card_last4']
|
||||
? $row['card_brand'] . ' ····' . $row['card_last4'] : null;
|
||||
// Don't expose raw sq_card_id
|
||||
unset($row['sq_card_id']);
|
||||
}
|
||||
echo json_encode(['success'=>true,'billing'=>$row ?: null]);
|
||||
break;
|
||||
|
||||
// ─── BILLING: save/update ────────────────────────────────
|
||||
case 'billing_save':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
if (!$uid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$stmt = db()->prepare("
|
||||
INSERT INTO saved_billing (user_id,first_name,last_name,email,address,city,state,zip)
|
||||
VALUES (?,?,?,?,?,?,?,?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
first_name=VALUES(first_name), last_name=VALUES(last_name),
|
||||
email=VALUES(email), address=VALUES(address),
|
||||
city=VALUES(city), state=VALUES(state), zip=VALUES(zip)
|
||||
");
|
||||
$stmt->execute([
|
||||
$uid,
|
||||
substr(trim($data['first_name']??''),0,80),
|
||||
substr(trim($data['last_name'] ??''),0,80),
|
||||
substr(strtolower(trim($data['email']??'')),0,150),
|
||||
substr(trim($data['address'] ??''),0,200),
|
||||
substr(trim($data['city'] ??''),0,80),
|
||||
strtoupper(substr(trim($data['state']??''),0,2)),
|
||||
substr(trim($data['zip'] ??''),0,10),
|
||||
]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── BILLING: clear card ─────────────────────────────────
|
||||
case 'billing_clear_card':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
db()->prepare("UPDATE saved_billing SET card_brand=NULL,card_last4=NULL,card_exp_month=NULL,card_exp_year=NULL,sq_card_id=NULL WHERE user_id=?")->execute([$uid]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── BILLING: clear all ──────────────────────────────────
|
||||
case 'billing_clear_all':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
db()->prepare("DELETE FROM saved_billing WHERE user_id=?")->execute([$uid]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── RESEND VERIFICATION (from admin) ─────────────────────
|
||||
case 'resend_verification':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = (int)($data['user_id'] ?? 0);
|
||||
$stmt = db()->prepare("SELECT email,alias FROM users WHERE id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$user = $stmt->fetch();
|
||||
if (!$user) { echo json_encode(['success'=>false,'error'=>'User not found']); exit; }
|
||||
$result = resendVerification($user['email']);
|
||||
echo json_encode($result);
|
||||
break;
|
||||
|
||||
// ─── CHAT: inbox list ──────────────────────────────────
|
||||
case 'chat_inbox':
|
||||
$rows = db()->query("
|
||||
SELECT u.id AS user_id, u.username, u.alias,
|
||||
cm.message AS last_message, cm.sender AS last_sender,
|
||||
cm.created_at AS last_time,
|
||||
(SELECT COUNT(*) FROM chat_messages
|
||||
WHERE user_id=u.id AND sender='user' AND is_read=0) AS unread_count
|
||||
FROM users u
|
||||
INNER JOIN chat_messages cm ON cm.id=(
|
||||
SELECT id FROM chat_messages WHERE user_id=u.id ORDER BY id DESC LIMIT 1
|
||||
)
|
||||
ORDER BY cm.created_at DESC
|
||||
")->fetchAll();
|
||||
echo json_encode(['success'=>true,'inbox'=>$rows]);
|
||||
break;
|
||||
|
||||
// ─── CHAT: full thread for one user ───────────────────
|
||||
case 'chat_thread':
|
||||
$tid = (int)($_GET['user_id'] ?? 0);
|
||||
$since = (int)($_GET['since'] ?? 0);
|
||||
if (!$tid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
$stmt = db()->prepare("SELECT id,sender,message,is_read,created_at FROM chat_messages WHERE user_id=? AND id>? ORDER BY id ASC LIMIT 300");
|
||||
$stmt->execute([$tid, $since]);
|
||||
// Mark user messages read
|
||||
db()->prepare("UPDATE chat_messages SET is_read=1 WHERE user_id=? AND sender='user' AND is_read=0")->execute([$tid]);
|
||||
$uStmt = db()->prepare("SELECT id,username,alias,tokens FROM users WHERE id=?");
|
||||
$uStmt->execute([$tid]);
|
||||
echo json_encode(['success'=>true,'messages'=>$stmt->fetchAll(),'user'=>$uStmt->fetch()]);
|
||||
break;
|
||||
|
||||
// ─── CHAT: admin reply ────────────────────────────────
|
||||
case 'chat_admin_send':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$tid = (int)($data['user_id'] ?? 0);
|
||||
$msg = trim($data['message'] ?? '');
|
||||
if (!$tid || empty($msg)) { echo json_encode(['success'=>false,'error'=>'Invalid']); exit; }
|
||||
$stmt = db()->prepare("INSERT INTO chat_messages (user_id,sender,message) VALUES (?,'admin',?)");
|
||||
$stmt->execute([$tid, $msg]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
break;
|
||||
|
||||
// ─── CHAT: clear single user thread ───────────────────
|
||||
case 'chat_clear_thread':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$tid = (int)($data['user_id'] ?? 0);
|
||||
if (!$tid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
db()->prepare("DELETE FROM chat_messages WHERE user_id=?")->execute([$tid]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ─── CHAT: clear ALL chats ────────────────────────────
|
||||
case 'chat_clear_all':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
db()->exec("DELETE FROM chat_messages");
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
case 'chat_unread':
|
||||
$count = db()->query("SELECT COUNT(*) FROM chat_messages WHERE sender='user' AND is_read=0")->fetchColumn();
|
||||
echo json_encode(['success'=>true,'count'=>(int)$count]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$userId = $_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ── Get saved billing (user sees own; admin passes user_id param) ──
|
||||
case 'get':
|
||||
$uid = $isAdmin ? (int)($_GET['user_id'] ?? $userId) : $userId;
|
||||
$stmt = db()->prepare("SELECT * FROM saved_billing WHERE user_id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$row = $stmt->fetch();
|
||||
if ($row && !$isAdmin) {
|
||||
// Mask card number for non-admin
|
||||
$row['card_display'] = $row['card_brand'] && $row['card_last4']
|
||||
? $row['card_brand'] . ' ····' . $row['card_last4']
|
||||
: null;
|
||||
unset($row['sq_card_id']);
|
||||
}
|
||||
echo json_encode(['success'=>true, 'billing'=>$row ?: null]);
|
||||
break;
|
||||
|
||||
// ── Save / update billing info ─────────────────────────────
|
||||
case 'save':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = $isAdmin && isset($data['user_id']) ? (int)$data['user_id'] : $userId;
|
||||
|
||||
$firstName = substr(trim($data['first_name'] ?? ''), 0, 80);
|
||||
$lastName = substr(trim($data['last_name'] ?? ''), 0, 80);
|
||||
$email = substr(strtolower(trim($data['email'] ?? '')), 0, 150);
|
||||
$address = substr(trim($data['address'] ?? ''), 0, 200);
|
||||
$city = substr(trim($data['city'] ?? ''), 0, 80);
|
||||
$state = strtoupper(substr(trim($data['state'] ?? ''), 0, 2));
|
||||
$zip = substr(trim($data['zip'] ?? ''), 0, 10);
|
||||
|
||||
// Card info — only update if provided
|
||||
$cardBrand = isset($data['card_brand']) ? substr(trim($data['card_brand']), 0, 30) : null;
|
||||
$cardLast4 = isset($data['card_last4']) ? substr(trim($data['card_last4']), 0, 4) : null;
|
||||
$cardExpMonth = isset($data['card_exp_month'])? substr(trim($data['card_exp_month']),0, 2) : null;
|
||||
$cardExpYear = isset($data['card_exp_year']) ? substr(trim($data['card_exp_year']), 0, 4) : null;
|
||||
$sqCardId = isset($data['sq_card_id']) ? substr(trim($data['sq_card_id']), 0, 255) : null;
|
||||
|
||||
$stmt = db()->prepare("
|
||||
INSERT INTO saved_billing
|
||||
(user_id, first_name, last_name, email, address, city, state, zip,
|
||||
card_brand, card_last4, card_exp_month, card_exp_year, sq_card_id)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
first_name=VALUES(first_name), last_name=VALUES(last_name),
|
||||
email=VALUES(email), address=VALUES(address), city=VALUES(city),
|
||||
state=VALUES(state), zip=VALUES(zip),
|
||||
card_brand=COALESCE(VALUES(card_brand), card_brand),
|
||||
card_last4=COALESCE(VALUES(card_last4), card_last4),
|
||||
card_exp_month=COALESCE(VALUES(card_exp_month), card_exp_month),
|
||||
card_exp_year=COALESCE(VALUES(card_exp_year), card_exp_year),
|
||||
sq_card_id=COALESCE(VALUES(sq_card_id), sq_card_id)
|
||||
");
|
||||
$stmt->execute([$uid,$firstName,$lastName,$email,$address,$city,$state,$zip,
|
||||
$cardBrand,$cardLast4,$cardExpMonth,$cardExpYear,$sqCardId]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ── Clear card info only ───────────────────────────────────
|
||||
case 'clear_card':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = $isAdmin && isset($data['user_id']) ? (int)$data['user_id'] : $userId;
|
||||
db()->prepare("UPDATE saved_billing SET card_brand=NULL, card_last4=NULL, card_exp_month=NULL, card_exp_year=NULL, sq_card_id=NULL WHERE user_id=?")
|
||||
->execute([$uid]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ── Clear all billing info ────────────────────────────────
|
||||
case 'clear_all':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = $isAdmin && isset($data['user_id']) ? (int)$data['user_id'] : $userId;
|
||||
db()->prepare("DELETE FROM saved_billing WHERE user_id=?")->execute([$uid]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); header('Content-Type: application/json'); echo json_encode(['success'=>false,'error'=>'Server error']); exit; }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ── List broadcasts for this user ─────────────────────
|
||||
case 'list':
|
||||
// Get broadcasts targeting this user
|
||||
$stmt = db()->prepare("
|
||||
SELECT b.*,
|
||||
u.username AS sender_name,
|
||||
u.alias AS sender_alias,
|
||||
(SELECT COUNT(*) FROM broadcast_replies WHERE broadcast_id=b.id) AS reply_count,
|
||||
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id AND user_id=?) AS is_read,
|
||||
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id) AS read_count
|
||||
FROM broadcasts b
|
||||
JOIN users u ON b.admin_id = u.id
|
||||
WHERE b.target = 'all'
|
||||
OR (b.target = 'verified' AND EXISTS(SELECT 1 FROM users WHERE id=? AND email_verified=1 AND is_admin=0))
|
||||
OR (b.target = 'unverified' AND EXISTS(SELECT 1 FROM users WHERE id=? AND email_verified=0))
|
||||
OR (b.target = 'admins' AND ?)
|
||||
ORDER BY b.sent_at DESC
|
||||
");
|
||||
$stmt->execute([$userId, $userId, $userId, $isAdmin ? 1 : 0]);
|
||||
echo json_encode(['success'=>true, 'broadcasts'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
// ── Mark as read ──────────────────────────────────────
|
||||
case 'mark_read':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$bid= (int)($d['broadcast_id'] ?? 0);
|
||||
if (!$bid) { echo json_encode(['success'=>false]); exit; }
|
||||
db()->prepare("INSERT IGNORE INTO broadcast_reads (broadcast_id,user_id) VALUES (?,?)")->execute([$bid,$userId]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ── Get replies for a broadcast ───────────────────────
|
||||
case 'replies':
|
||||
$bid = (int)($_GET['broadcast_id'] ?? 0);
|
||||
if (!$bid) { echo json_encode(['success'=>false]); exit; }
|
||||
$stmt = db()->prepare("
|
||||
SELECT br.*, u.username, u.alias, u.is_admin
|
||||
FROM broadcast_replies br
|
||||
JOIN users u ON br.user_id = u.id
|
||||
WHERE br.broadcast_id = ?
|
||||
ORDER BY br.created_at ASC
|
||||
");
|
||||
$stmt->execute([$bid]);
|
||||
echo json_encode(['success'=>true,'replies'=>$stmt->fetchAll()]);
|
||||
break;
|
||||
|
||||
// ── Post a reply ──────────────────────────────────────
|
||||
case 'reply':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$bid = (int)($d['broadcast_id'] ?? 0);
|
||||
$msg = substr(trim($d['message'] ?? ''), 0, 1000);
|
||||
if (!$bid || !$msg) { echo json_encode(['success'=>false,'error'=>'broadcast_id and message required']); exit; }
|
||||
db()->prepare("INSERT INTO broadcast_replies (broadcast_id,user_id,message) VALUES (?,?,?)")->execute([$bid,$userId,$msg]);
|
||||
db()->prepare("INSERT IGNORE INTO broadcast_reads (broadcast_id,user_id) VALUES (?,?)")->execute([$bid,$userId]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
break;
|
||||
|
||||
// ── Unread count ──────────────────────────────────────
|
||||
case 'unread_count':
|
||||
$stmt = db()->prepare("
|
||||
SELECT COUNT(*) FROM broadcasts b
|
||||
WHERE (b.target='all' OR (b.target='verified' AND EXISTS(SELECT 1 FROM users WHERE id=? AND email_verified=1 AND is_admin=0))
|
||||
OR (b.target='unverified' AND EXISTS(SELECT 1 FROM users WHERE id=? AND email_verified=0))
|
||||
OR (b.target='admins' AND ?))
|
||||
AND NOT EXISTS (SELECT 1 FROM broadcast_reads WHERE broadcast_id=b.id AND user_id=?)
|
||||
");
|
||||
$stmt->execute([$userId,$userId,$isAdmin?1:0,$userId]);
|
||||
echo json_encode(['success'=>true,'count'=>(int)$stmt->fetchColumn()]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
ob_end_clean();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// GET — player's own requests (list, delete, update, lock)
|
||||
// ══════════════════════════════════════════════════════════
|
||||
if ($method === 'GET') {
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
|
||||
if ($action === 'list') {
|
||||
$stmt = db()->prepare("
|
||||
SELECT cr.*,
|
||||
COALESCE(p.name, cr.platform_id) AS platform_name
|
||||
FROM cashout_requests cr
|
||||
LEFT JOIN platforms p ON cr.platform_id = p.slug
|
||||
WHERE cr.user_id = ?
|
||||
ORDER BY cr.created_at DESC
|
||||
LIMIT 50
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
echo json_encode(['success'=>true, 'requests'=>$stmt->fetchAll()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'delete') {
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$chk = db()->prepare("SELECT id,tokens FROM cashout_requests WHERE id=? AND user_id=? AND status='pending'");
|
||||
$chk->execute([$id, $userId]);
|
||||
$row = $chk->fetch();
|
||||
if (!$row) { echo json_encode(['success'=>false,'error'=>'Request not found or already locked']); exit; }
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$row['tokens'], $userId]);
|
||||
db()->prepare("DELETE FROM cashout_requests WHERE id=?")->execute([$id]);
|
||||
$nb = db()->prepare("SELECT tokens FROM users WHERE id=?");
|
||||
$nb->execute([$userId]);
|
||||
echo json_encode(['success'=>true,'new_balance'=>(float)$nb->fetchColumn()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'update') {
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$tokens = (float)($_GET['tokens'] ?? 0);
|
||||
$alias = substr(trim($_GET['alias'] ?? ''), 0, 100);
|
||||
$chk = db()->prepare("SELECT id,tokens AS old_tokens FROM cashout_requests WHERE id=? AND user_id=? AND status='pending'");
|
||||
$chk->execute([$id, $userId]);
|
||||
$row = $chk->fetch();
|
||||
if (!$row) { echo json_encode(['success'=>false,'error'=>'Request not found or already locked']); exit; }
|
||||
if ($tokens < 1) { echo json_encode(['success'=>false,'error'=>'Minimum 1 token']); exit; }
|
||||
$diff = $tokens - $row['old_tokens'];
|
||||
if ($diff > 0) {
|
||||
$balChk = db()->prepare("SELECT tokens FROM users WHERE id=?");
|
||||
$balChk->execute([$userId]);
|
||||
if ($diff > (float)$balChk->fetchColumn()) { echo json_encode(['success'=>false,'error'=>'Insufficient balance']); exit; }
|
||||
}
|
||||
db()->beginTransaction();
|
||||
db()->prepare("UPDATE users SET tokens=tokens-? WHERE id=?")->execute([$diff, $userId]);
|
||||
db()->prepare("UPDATE cashout_requests SET tokens=?,alias=? WHERE id=?")->execute([$tokens, $alias, $id]);
|
||||
db()->commit();
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'lock') {
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$chk = db()->prepare("SELECT id FROM cashout_requests WHERE id=? AND user_id=? AND status='pending'");
|
||||
$chk->execute([$id, $userId]);
|
||||
if (!$chk->fetch()) { echo json_encode(['success'=>false,'error'=>'Request not found']); exit; }
|
||||
try {
|
||||
db()->exec("ALTER TABLE cashout_requests MODIFY COLUMN status ENUM('pending','locked','sent','approved','rejected','deleted') DEFAULT 'pending'");
|
||||
} catch (Exception $e) {}
|
||||
db()->prepare("UPDATE cashout_requests SET status='locked' WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// POST — submit new cashout request
|
||||
// ══════════════════════════════════════════════════════════
|
||||
if ($method !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; }
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$platformId = trim($data['platform_id'] ?? '');
|
||||
$alias = trim($data['alias'] ?? '');
|
||||
$tokens = (float)($data['tokens'] ?? 0);
|
||||
$payoutMethodId = (int)($data['payout_method_id'] ?? 0);
|
||||
$payoutMethodType = trim($data['payout_method_type'] ?? '');
|
||||
$payoutHandle = trim($data['payout_handle'] ?? '');
|
||||
|
||||
// Validate platform
|
||||
$platStmt = db()->prepare("SELECT slug FROM platforms WHERE slug=? AND is_active=1 LIMIT 1");
|
||||
$platStmt->execute([$platformId]);
|
||||
if (!$platStmt->fetch()) {
|
||||
$platforms = json_decode(PLATFORMS, true);
|
||||
if (empty(array_filter($platforms, fn($p) => $p['id'] === $platformId))) {
|
||||
echo json_encode(['success'=>false,'error'=>'Invalid platform.']); exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($alias)) { echo json_encode(['success'=>false,'error'=>'Platform alias required.']); exit; }
|
||||
if ($tokens < 1) { echo json_encode(['success'=>false,'error'=>'Minimum cashout is 1 token.']); exit; }
|
||||
|
||||
// Validate payout method
|
||||
if ($payoutMethodId) {
|
||||
$chk = db()->prepare("SELECT method_type,account_handle FROM payout_methods WHERE id=? AND user_id=?");
|
||||
$chk->execute([$payoutMethodId, $userId]);
|
||||
if ($pm = $chk->fetch()) {
|
||||
$payoutMethodType = $pm['method_type'];
|
||||
$payoutHandle = $pm['account_handle'];
|
||||
}
|
||||
}
|
||||
|
||||
// Check balance
|
||||
$balStmt = db()->prepare("SELECT tokens FROM users WHERE id=?");
|
||||
$balStmt->execute([$userId]);
|
||||
$balance = (float)$balStmt->fetchColumn();
|
||||
if ($tokens > $balance) { echo json_encode(['success'=>false,'error'=>'Insufficient token balance.']); exit; }
|
||||
|
||||
// Deduct & create
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
db()->prepare("UPDATE users SET tokens=tokens-? WHERE id=?")->execute([$tokens, $userId]);
|
||||
db()->prepare("INSERT INTO cashout_requests (user_id,platform_id,alias,tokens,payout_method_type,payout_handle) VALUES (?,?,?,?,?,?)")
|
||||
->execute([$userId, $platformId, $alias, $tokens, $payoutMethodType, $payoutHandle]);
|
||||
db()->commit();
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'Request failed. Try again.']); exit;
|
||||
}
|
||||
|
||||
$newBalStmt = db()->prepare("SELECT tokens FROM users WHERE id=?");
|
||||
$newBalStmt->execute([$userId]);
|
||||
$nb = (float)$newBalStmt->fetchColumn();
|
||||
|
||||
try { logActivity('cashout_request', $userId, null, 'cashout', 0, "Cashout: {$tokens} tokens via {$payoutMethodType}"); } catch(Exception $e){}
|
||||
|
||||
echo json_encode(['success'=>true, 'new_balance'=>$nb]);
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); header('Content-Type: application/json'); echo json_encode(['success'=>false,'error'=>'Server error']); exit; }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$isAdmin = isLoggedIn() && !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// Public: active types for player dropdown
|
||||
case 'list':
|
||||
$rows = db()->query("SELECT slug,label,icon,description FROM cashout_method_types WHERE is_active=1 ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'types'=>$rows]);
|
||||
break;
|
||||
|
||||
// Admin: all types
|
||||
case 'admin_list':
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$rows = db()->query("SELECT * FROM cashout_method_types ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'types'=>$rows]);
|
||||
break;
|
||||
|
||||
// Admin: create
|
||||
case 'create':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($d['slug'] ?? '')));
|
||||
$label= substr(trim($d['label'] ?? ''), 0, 100);
|
||||
$icon = substr(trim($d['icon'] ?? '💰'), 0, 10);
|
||||
$desc = substr(trim($d['description'] ?? ''), 0, 200);
|
||||
$sort = (int)($d['sort_order'] ?? 99);
|
||||
$active = (int)(bool)($d['is_active'] ?? 1);
|
||||
if (!$slug || !$label) { echo json_encode(['success'=>false,'error'=>'Slug and label required']); exit; }
|
||||
try {
|
||||
db()->prepare("INSERT INTO cashout_method_types (slug,label,icon,description,is_active,sort_order) VALUES (?,?,?,?,?,?)")
|
||||
->execute([$slug,$label,$icon,$desc,$active,$sort]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success'=>false,'error'=>'Slug already exists']);
|
||||
}
|
||||
break;
|
||||
|
||||
// Admin: update
|
||||
case 'update':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$label= substr(trim($d['label'] ?? ''), 0, 100);
|
||||
$icon = substr(trim($d['icon'] ?? '💰'), 0, 10);
|
||||
$desc = substr(trim($d['description'] ?? ''), 0, 200);
|
||||
$sort = (int)($d['sort_order'] ?? 0);
|
||||
$active = (int)(bool)($d['is_active'] ?? 1);
|
||||
if (!$id || !$label) { echo json_encode(['success'=>false,'error'=>'ID and label required']); exit; }
|
||||
db()->prepare("UPDATE cashout_method_types SET label=?,icon=?,description=?,is_active=?,sort_order=? WHERE id=?")
|
||||
->execute([$label,$icon,$desc,$active,$sort,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// Admin: delete
|
||||
case 'delete':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
||||
db()->prepare("DELETE FROM cashout_method_types WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$userId = $_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ── User: get own messages ─────────────────────────────
|
||||
case 'messages':
|
||||
$since = (int)($_GET['since'] ?? 0); // last message id for polling
|
||||
$stmt = db()->prepare("
|
||||
SELECT id, sender, message, is_read, created_at
|
||||
FROM chat_messages
|
||||
WHERE user_id = ? AND id > ?
|
||||
ORDER BY id ASC
|
||||
LIMIT 100
|
||||
");
|
||||
$stmt->execute([$userId, $since]);
|
||||
$msgs = $stmt->fetchAll();
|
||||
|
||||
// Mark admin messages as read
|
||||
db()->prepare("UPDATE chat_messages SET is_read=1 WHERE user_id=? AND sender='admin' AND is_read=0")->execute([$userId]);
|
||||
|
||||
echo json_encode(['success'=>true, 'messages'=>$msgs]);
|
||||
break;
|
||||
|
||||
// ── User: send message to admin ───────────────────────
|
||||
case 'send':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$msg = trim($data['message'] ?? '');
|
||||
if (empty($msg) || mb_strlen($msg) > 2000) { echo json_encode(['success'=>false,'error'=>'Invalid message']); exit; }
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO chat_messages (user_id, sender, message) VALUES (?, 'user', ?)");
|
||||
$stmt->execute([$userId, $msg]);
|
||||
echo json_encode(['success'=>true, 'id'=>db()->lastInsertId()]);
|
||||
break;
|
||||
|
||||
// ── Admin: get inbox list (one row per user, latest msg) ──
|
||||
case 'admin_inbox':
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$rows = db()->query("
|
||||
SELECT
|
||||
u.id AS user_id,
|
||||
u.username,
|
||||
u.alias,
|
||||
cm.message AS last_message,
|
||||
cm.sender AS last_sender,
|
||||
cm.created_at AS last_time,
|
||||
SUM(CASE WHEN cm2.sender='user' AND cm2.is_read=0 THEN 1 ELSE 0 END) AS unread_count
|
||||
FROM users u
|
||||
JOIN chat_messages cm ON cm.id = (
|
||||
SELECT id FROM chat_messages
|
||||
WHERE user_id = u.id
|
||||
ORDER BY id DESC LIMIT 1
|
||||
)
|
||||
LEFT JOIN chat_messages cm2 ON cm2.user_id = u.id
|
||||
GROUP BY u.id, u.username, u.alias, cm.message, cm.sender, cm.created_at
|
||||
ORDER BY cm.created_at DESC
|
||||
")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'inbox'=>$rows]);
|
||||
break;
|
||||
|
||||
// ── Admin: get all messages for a specific user ────────
|
||||
case 'admin_thread':
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$tid = (int)($_GET['user_id'] ?? 0);
|
||||
$since = (int)($_GET['since'] ?? 0);
|
||||
if (!$tid) { echo json_encode(['success'=>false,'error'=>'user_id required']); exit; }
|
||||
|
||||
$stmt = db()->prepare("
|
||||
SELECT id, sender, message, is_read, created_at
|
||||
FROM chat_messages
|
||||
WHERE user_id = ? AND id > ?
|
||||
ORDER BY id ASC LIMIT 200
|
||||
");
|
||||
$stmt->execute([$tid, $since]);
|
||||
$msgs = $stmt->fetchAll();
|
||||
|
||||
// Mark user messages as read
|
||||
db()->prepare("UPDATE chat_messages SET is_read=1 WHERE user_id=? AND sender='user' AND is_read=0")->execute([$tid]);
|
||||
|
||||
// Get user info
|
||||
$uStmt = db()->prepare("SELECT id, username, alias, tokens FROM users WHERE id=?");
|
||||
$uStmt->execute([$tid]);
|
||||
$user = $uStmt->fetch();
|
||||
|
||||
echo json_encode(['success'=>true, 'messages'=>$msgs, 'user'=>$user]);
|
||||
break;
|
||||
|
||||
// ── Admin: reply to a user ────────────────────────────
|
||||
case 'admin_send':
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$tid = (int)($data['user_id'] ?? 0);
|
||||
$msg = trim($data['message'] ?? '');
|
||||
if (!$tid || empty($msg) || mb_strlen($msg) > 2000) { echo json_encode(['success'=>false,'error'=>'Invalid']); exit; }
|
||||
|
||||
$stmt = db()->prepare("INSERT INTO chat_messages (user_id, sender, message) VALUES (?, 'admin', ?)");
|
||||
$stmt->execute([$tid, $msg]);
|
||||
echo json_encode(['success'=>true, 'id'=>db()->lastInsertId()]);
|
||||
break;
|
||||
|
||||
// ── Unread count (for badge) ──────────────────────────
|
||||
case 'unread':
|
||||
if ($isAdmin) {
|
||||
$count = db()->query("SELECT COUNT(*) FROM chat_messages WHERE sender='user' AND is_read=0")->fetchColumn();
|
||||
} else {
|
||||
$stmt = db()->prepare("SELECT COUNT(*) FROM chat_messages WHERE user_id=? AND sender='admin' AND is_read=0");
|
||||
$stmt->execute([$userId]);
|
||||
$count = $stmt->fetchColumn();
|
||||
}
|
||||
echo json_encode(['success'=>true, 'count'=>(int)$count]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); header('Content-Type: application/json'); echo json_encode(['success'=>false,'error'=>'Server error']); exit; }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$userId = $_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ── Get all aliases for a user ────────────────────────
|
||||
case 'get':
|
||||
$uid = $isAdmin ? (int)($_GET['user_id'] ?? $userId) : $userId;
|
||||
$stmt = db()->prepare("SELECT platform_slug, alias FROM game_aliases WHERE user_id=?");
|
||||
$stmt->execute([$uid]);
|
||||
$rows = $stmt->fetchAll();
|
||||
$map = [];
|
||||
foreach ($rows as $r) $map[$r['platform_slug']] = $r['alias'];
|
||||
echo json_encode(['success'=>true, 'aliases'=>$map]);
|
||||
break;
|
||||
|
||||
// ── Save a single alias ───────────────────────────────
|
||||
case 'save':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = $isAdmin && isset($data['user_id']) ? (int)$data['user_id'] : $userId;
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($data['platform_slug'] ?? '')));
|
||||
$alias = substr(trim($data['alias'] ?? ''), 0, 100);
|
||||
if (!$slug) { echo json_encode(['success'=>false,'error'=>'Platform slug required']); exit; }
|
||||
if ($alias === '') {
|
||||
// Empty alias = delete it
|
||||
db()->prepare("DELETE FROM game_aliases WHERE user_id=? AND platform_slug=?")->execute([$uid,$slug]);
|
||||
} else {
|
||||
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?)
|
||||
ON DUPLICATE KEY UPDATE alias=VALUES(alias)")->execute([$uid,$slug,$alias]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ── Save all aliases at once (bulk) ───────────────────
|
||||
case 'save_all':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = $isAdmin && isset($data['user_id']) ? (int)$data['user_id'] : $userId;
|
||||
$aliases = $data['aliases'] ?? [];
|
||||
$stmt = db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?)
|
||||
ON DUPLICATE KEY UPDATE alias=VALUES(alias)");
|
||||
$del = db()->prepare("DELETE FROM game_aliases WHERE user_id=? AND platform_slug=?");
|
||||
foreach ($aliases as $slug => $alias) {
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($slug)));
|
||||
$alias = substr(trim($alias), 0, 100);
|
||||
if (!$slug) continue;
|
||||
if ($alias === '') $del->execute([$uid, $slug]);
|
||||
else $stmt->execute([$uid, $slug, $alias]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try {
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
} catch (Throwable $e) {
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success'=>false,'error'=>'Server error']);
|
||||
exit;
|
||||
}
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$username = trim($data['username'] ?? '');
|
||||
$password = trim($data['password'] ?? '');
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
echo json_encode(['success'=>false,'error'=>'Username and password required']); exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$result = loginUser($username, $password);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success'=>false,'error'=>'Login error. Please try again.']); exit;
|
||||
}
|
||||
|
||||
if ($result['success'] && isset($result['user'])) {
|
||||
logPlayerAction('LOGIN_SUCCESS', $result['user']['id'], 'User logged in', 'auth', 'info');
|
||||
unset($result['user']['password']);
|
||||
}
|
||||
if (!$result['success']) { logSecurityEvent('LOGIN_FAILED', null, 'Failed login attempt for: ' . $username, 'warning'); }
|
||||
echo json_encode($result);
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
logoutUser();
|
||||
echo json_encode(['success' => true]);
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try {
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
} catch (Throwable $e) {
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['success' => false, 'error' => 'Server error']);
|
||||
exit;
|
||||
}
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) {
|
||||
echo json_encode(['success' => false, 'error' => 'Not authenticated']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$user = currentUser();
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'error' => 'DB error']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
echo json_encode(['success' => false, 'error' => 'User not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sync session is_admin with DB value — catches admin elevation/demotion
|
||||
$_SESSION['is_admin'] = (int)$user['is_admin'];
|
||||
|
||||
unset($user['password']);
|
||||
echo json_encode(['success' => true, 'user' => $user]);
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$action = $_GET['action'] ?? 'all';
|
||||
|
||||
// ── Purchases ──────────────────────────────────────────────
|
||||
if ($action === 'all' || $action === 'purchases') {
|
||||
$stmt = db()->prepare("
|
||||
SELECT id, tokens, amount_cents, payment_method, platform_id, game_alias,
|
||||
card_brand, card_last4, status, admin_note, created_at
|
||||
FROM token_purchases
|
||||
WHERE user_id=?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 50
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$purchases = $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// ── Cashouts ───────────────────────────────────────────────
|
||||
if ($action === 'all' || $action === 'cashouts') {
|
||||
$stmt = db()->prepare("
|
||||
SELECT cr.*,
|
||||
COALESCE(p.name, cr.platform_id) AS platform_name
|
||||
FROM cashout_requests cr
|
||||
LEFT JOIN platforms p ON cr.platform_id = p.slug
|
||||
WHERE cr.user_id=?
|
||||
ORDER BY cr.created_at DESC
|
||||
LIMIT 50
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$cashouts = $stmt->fetchAll();
|
||||
}
|
||||
|
||||
// ── Broadcasts/Invites (use broadcasts as announcements) ───
|
||||
if ($action === 'all' || $action === 'broadcasts') {
|
||||
$stmt = db()->prepare("
|
||||
SELECT b.id, b.subject, b.message, b.sent_at,
|
||||
u.username AS sender,
|
||||
(SELECT COUNT(*) FROM broadcast_reads WHERE broadcast_id=b.id AND user_id=?) AS is_read,
|
||||
(SELECT COUNT(*) FROM broadcast_replies WHERE broadcast_id=b.id AND user_id=?) AS replied
|
||||
FROM broadcasts b
|
||||
JOIN users u ON b.admin_id=u.id
|
||||
WHERE b.target='all'
|
||||
OR (b.target='verified' AND EXISTS(SELECT 1 FROM users WHERE id=? AND email_verified=1))
|
||||
OR (b.target='unverified' AND EXISTS(SELECT 1 FROM users WHERE id=? AND email_verified=0))
|
||||
OR (b.target='admins' AND 0)
|
||||
ORDER BY b.sent_at DESC
|
||||
LIMIT 20
|
||||
");
|
||||
$stmt->execute([$userId,$userId,$userId,$userId]);
|
||||
$broadcasts = $stmt->fetchAll();
|
||||
}
|
||||
|
||||
if ($action === 'all') {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'purchases' => $purchases,
|
||||
'cashouts' => $cashouts,
|
||||
'broadcasts' => $broadcasts,
|
||||
]);
|
||||
} elseif ($action === 'purchases') {
|
||||
echo json_encode(['success'=>true,'purchases'=>$purchases]);
|
||||
} elseif ($action === 'cashouts') {
|
||||
echo json_encode(['success'=>true,'cashouts'=>$cashouts]);
|
||||
} elseif ($action === 'broadcasts') {
|
||||
echo json_encode(['success'=>true,'broadcasts'=>$broadcasts]);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$stmt = db()->prepare("
|
||||
SELECT id, tokens, amount_cents, payment_method, platform_id, game_alias,
|
||||
card_brand, card_last4, status, created_at
|
||||
FROM token_purchases
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
echo json_encode(['success'=>true, 'purchases'=>$stmt->fetchAll()]);
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); header('Content-Type: application/json'); echo json_encode(['success'=>false,'error'=>'Server error']); exit; }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$isAdmin = isLoggedIn() && !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// Public: get all enabled payment methods including card status
|
||||
case 'list':
|
||||
// Include card row (is_enabled controls whether card appears at checkout)
|
||||
$rows = db()->query("SELECT method_key, label, handle, instructions, is_enabled FROM payment_settings ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'methods'=>$rows]);
|
||||
break;
|
||||
|
||||
// Admin: get all methods including disabled
|
||||
case 'admin_list':
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$rows = db()->query("SELECT * FROM payment_settings ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'methods'=>$rows]);
|
||||
break;
|
||||
|
||||
// Admin: update a single method
|
||||
case 'update':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$label= substr(trim($d['label']??''), 0, 100);
|
||||
$handle = substr(trim($d['handle']??''), 0, 200);
|
||||
$instructions = substr(trim($d['instructions']??''), 0, 500);
|
||||
$enabled = (int)(bool)($d['is_enabled'] ?? 1);
|
||||
$sort = (int)($d['sort_order'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
||||
db()->prepare("UPDATE payment_settings SET label=?,handle=?,instructions=?,is_enabled=?,sort_order=? WHERE id=?")
|
||||
->execute([$label,$handle,$instructions,$enabled,$sort,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); header('Content-Type: application/json'); echo json_encode(['success'=>false,'error'=>'Server error']); exit; }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
case 'list':
|
||||
$uid = $isAdmin ? (int)($_GET['user_id'] ?? $userId) : $userId;
|
||||
$rows = db()->prepare("SELECT * FROM payout_methods WHERE user_id=? ORDER BY is_default DESC, id ASC");
|
||||
$rows->execute([$uid]);
|
||||
echo json_encode(['success'=>true, 'methods'=>$rows->fetchAll()]);
|
||||
break;
|
||||
|
||||
case 'add':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$uid = $isAdmin && isset($d['user_id']) ? (int)$d['user_id'] : $userId;
|
||||
$type = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($d['method_type'] ?? '')));
|
||||
$label = substr(trim($d['label'] ?? ''), 0, 100);
|
||||
$handle= substr(trim($d['account_handle'] ?? ''), 0, 200);
|
||||
$def = (int)(bool)($d['is_default'] ?? 0);
|
||||
if (!$type || !$label || !$handle) { echo json_encode(['success'=>false,'error'=>'All fields required']); exit; }
|
||||
db()->beginTransaction();
|
||||
if ($def) db()->prepare("UPDATE payout_methods SET is_default=0 WHERE user_id=?")->execute([$uid]);
|
||||
// If first method, auto-set as default
|
||||
$count = db()->prepare("SELECT COUNT(*) FROM payout_methods WHERE user_id=?"); $count->execute([$uid]);
|
||||
if ((int)$count->fetchColumn() === 0) $def = 1;
|
||||
db()->prepare("INSERT INTO payout_methods (user_id,method_type,label,account_handle,is_default) VALUES (?,?,?,?,?)")
|
||||
->execute([$uid,$type,$label,$handle,$def]);
|
||||
$newId = db()->lastInsertId();
|
||||
db()->commit();
|
||||
echo json_encode(['success'=>true,'id'=>$newId]);
|
||||
break;
|
||||
|
||||
case 'set_default':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
// Verify ownership
|
||||
$chk = db()->prepare("SELECT user_id FROM payout_methods WHERE id=?"); $chk->execute([$id]);
|
||||
$row = $chk->fetch();
|
||||
if (!$row || ($row['user_id'] != $userId && !$isAdmin)) { echo json_encode(['success'=>false,'error'=>'Not found']); exit; }
|
||||
$uid = $row['user_id'];
|
||||
db()->prepare("UPDATE payout_methods SET is_default=0 WHERE user_id=?")->execute([$uid]);
|
||||
db()->prepare("UPDATE payout_methods SET is_default=1 WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false]); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$chk = db()->prepare("SELECT user_id,is_default FROM payout_methods WHERE id=?"); $chk->execute([$id]);
|
||||
$row = $chk->fetch();
|
||||
if (!$row || ($row['user_id'] != $userId && !$isAdmin)) { echo json_encode(['success'=>false,'error'=>'Not found']); exit; }
|
||||
db()->prepare("DELETE FROM payout_methods WHERE id=?")->execute([$id]);
|
||||
// If deleted default, set next one as default
|
||||
if ($row['is_default']) {
|
||||
$next = db()->prepare("SELECT id FROM payout_methods WHERE user_id=? LIMIT 1"); $next->execute([$row['user_id']]);
|
||||
if ($n = $next->fetch()) db()->prepare("UPDATE payout_methods SET is_default=1 WHERE id=?")->execute([$n['id']]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
require_once __DIR__ . '/../../includes/square.php';
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn() || empty($_SESSION['is_admin'])) {
|
||||
echo json_encode(['success'=>false,'error'=>'Forbidden']); exit;
|
||||
}
|
||||
|
||||
$adminId = (int)$_SESSION['user_id'];
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$action = $_GET['action'] ?? 'list_settings';
|
||||
|
||||
// ── GET actions ──────────────────────────────────────────
|
||||
if ($method === 'GET') {
|
||||
if ($action === 'list_settings') {
|
||||
$rows = db()->query("SELECT * FROM admin_payout_settings ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'settings'=>$rows]);
|
||||
exit;
|
||||
}
|
||||
if ($action === 'cashout_detail') {
|
||||
$id = (int)($_GET['id'] ?? 0);
|
||||
$stmt = db()->prepare("
|
||||
SELECT cr.*, u.username, u.alias AS user_alias, u.email,
|
||||
pm.method_type AS saved_payout_type, pm.account_handle AS saved_payout_handle, pm.label AS saved_payout_label
|
||||
FROM cashout_requests cr
|
||||
JOIN users u ON cr.user_id = u.id
|
||||
LEFT JOIN payout_methods pm ON pm.user_id = cr.user_id AND pm.is_default = 1
|
||||
WHERE cr.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch();
|
||||
echo json_encode(['success'=>true, 'cashout'=>$row]);
|
||||
exit;
|
||||
}
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']); exit;
|
||||
}
|
||||
|
||||
if ($method !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// ── Update payout settings ────────────────────────────────
|
||||
if ($action === 'update_setting') {
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$handle = substr(trim($d['handle']??''), 0, 200);
|
||||
$instr = substr(trim($d['instructions']??''), 0, 500);
|
||||
$enabled = (int)(bool)($d['is_enabled']??1);
|
||||
$label = substr(trim($d['label']??''), 0, 100);
|
||||
$sort = (int)($d['sort_order']??0);
|
||||
db()->prepare("UPDATE admin_payout_settings SET label=?,handle=?,instructions=?,is_enabled=?,sort_order=? WHERE id=?")
|
||||
->execute([$label,$handle,$instr,$enabled,$sort,$id]);
|
||||
echo json_encode(['success'=>true]); exit;
|
||||
}
|
||||
|
||||
// ── Process cashout payout ────────────────────────────────
|
||||
if ($action === 'process_payout') {
|
||||
$cashoutId = (int)($d['cashout_id'] ?? 0);
|
||||
$payoutKey = trim($d['payout_method_key'] ?? '');
|
||||
$payoutType = trim($d['payout_type'] ?? 'manual'); // 'manual' or 'square_gift_card'
|
||||
$note = substr(trim($d['note'] ?? ''), 0, 500);
|
||||
|
||||
// Load cashout
|
||||
$stmt = db()->prepare("SELECT cr.*, u.username, u.alias, u.email FROM cashout_requests cr JOIN users u ON cr.user_id=u.id WHERE cr.id=? AND cr.status IN ('pending','locked')");
|
||||
$stmt->execute([$cashoutId]);
|
||||
$cashout = $stmt->fetch();
|
||||
if (!$cashout) { echo json_encode(['success'=>false,'error'=>'Cashout not found or already processed']); exit; }
|
||||
|
||||
// Amount in cents (1 token = $1)
|
||||
$amountCents = (int)round($cashout['tokens'] * 100);
|
||||
|
||||
// Load payout setting
|
||||
$pset = db()->prepare("SELECT * FROM admin_payout_settings WHERE method_key=? AND is_enabled=1");
|
||||
$pset->execute([$payoutKey]);
|
||||
$setting = $pset->fetch();
|
||||
|
||||
$squareTxnId = null;
|
||||
$giftCardGan = null;
|
||||
$giftCardBal = null;
|
||||
$txnStatus = 'completed';
|
||||
|
||||
if ($payoutType === 'square_gift_card') {
|
||||
// ── Square Gift Card Flow ─────────────────────────
|
||||
try {
|
||||
// 1. Create gift card
|
||||
$gcRes = SquareClient::post('/v2/gift-cards', [
|
||||
'idempotency_key' => 'gc-create-' . $cashoutId . '-' . time(),
|
||||
'location_id' => SQUARE_LOCATION_ID,
|
||||
'gift_card' => ['type' => 'DIGITAL'],
|
||||
]);
|
||||
|
||||
if (empty($gcRes['gift_card']['id'])) {
|
||||
throw new Exception('Failed to create gift card: ' . json_encode($gcRes));
|
||||
}
|
||||
|
||||
$gcId = $gcRes['gift_card']['id'];
|
||||
$gcGan = $gcRes['gift_card']['gan'] ?? '';
|
||||
|
||||
// 2. Activate gift card with balance
|
||||
$actRes = SquareClient::post('/v2/gift-card-activities', [
|
||||
'idempotency_key' => 'gc-activate-' . $cashoutId . '-' . time(),
|
||||
'gift_card_activity' => [
|
||||
'type' => 'ACTIVATE',
|
||||
'location_id' => SQUARE_LOCATION_ID,
|
||||
'gift_card_id' => $gcId,
|
||||
'activate_activity_details' => [
|
||||
'amount_money' => [
|
||||
'amount' => $amountCents,
|
||||
'currency' => 'USD',
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
if (empty($actRes['gift_card_activity']['id'])) {
|
||||
throw new Exception('Failed to activate gift card: ' . json_encode($actRes));
|
||||
}
|
||||
|
||||
$giftCardGan = $gcGan;
|
||||
$giftCardBal = $amountCents;
|
||||
$squareTxnId = $actRes['gift_card_activity']['id'];
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success'=>false,'error'=>'Square error: ' . $e->getMessage()]); exit;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mark cashout as sent ──────────────────────────────
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
db()->prepare("UPDATE cashout_requests SET status='sent', admin_note=?, resolved_at=NOW() WHERE id=?")
|
||||
->execute([$note, $cashoutId]);
|
||||
|
||||
db()->prepare("INSERT INTO cashout_transactions (cashout_id,admin_id,payout_method,payout_type,amount_cents,square_txn_id,gift_card_gan,gift_card_balance,note,status)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,'completed')")
|
||||
->execute([$cashoutId, $adminId, $payoutKey, $payoutType, $amountCents, $squareTxnId, $giftCardGan, $giftCardBal, $note]);
|
||||
|
||||
db()->commit();
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'DB error: '.$e->getMessage()]); exit;
|
||||
}
|
||||
|
||||
$response = ['success'=>true, 'status'=>'sent', 'amount_cents'=>$amountCents];
|
||||
if ($giftCardGan) {
|
||||
$response['gift_card_gan'] = $giftCardGan;
|
||||
$response['gift_card_balance'] = $giftCardBal;
|
||||
}
|
||||
echo json_encode($response); exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
|
||||
if ($method === 'GET') {
|
||||
if ($action === 'list') {
|
||||
$uid = $isAdmin ? (int)($_GET['user_id'] ?? $userId) : $userId;
|
||||
$stmt = db()->prepare("SELECT pa.*, COALESCE(p.name, pa.platform_slug) AS platform_name, p.color
|
||||
FROM platform_accounts pa LEFT JOIN platforms p ON pa.platform_slug=p.slug
|
||||
WHERE pa.user_id=? ORDER BY pa.requested_at DESC");
|
||||
$stmt->execute([$uid]);
|
||||
$rows = $stmt->fetchAll();
|
||||
foreach ($rows as &$row) {
|
||||
if (!$isAdmin && $row['status'] !== 'approved') $row['platform_password'] = null;
|
||||
}
|
||||
echo json_encode(['success'=>true,'accounts'=>$rows]);
|
||||
} elseif ($action === 'check_onboarding') {
|
||||
$cnt = db()->prepare("SELECT COUNT(*) FROM platform_accounts WHERE user_id=?");
|
||||
$cnt->execute([$userId]);
|
||||
$hasAny = (int)$cnt->fetchColumn() > 0;
|
||||
// Check flag — graceful fallback if column doesn't exist
|
||||
$done = false;
|
||||
try {
|
||||
$s = db()->prepare("SELECT platform_onboarding_done FROM users WHERE id=?");
|
||||
$s->execute([$userId]);
|
||||
$r = $s->fetch(); $done = !empty($r['platform_onboarding_done']);
|
||||
} catch(Exception $e){}
|
||||
echo json_encode(['success'=>true,'needs_onboarding'=>(!$done && !$hasAny),'has_accounts'=>$hasAny]);
|
||||
} else {
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($method !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if ($action === 'request') {
|
||||
$slug = preg_replace('/[^a-z0-9_]/','',strtolower(trim($d['platform_slug']??'')));
|
||||
if (!$slug) { echo json_encode(['success'=>false,'error'=>'Platform required']); exit; }
|
||||
try {
|
||||
db()->prepare("INSERT INTO platform_accounts (user_id,platform_slug) VALUES (?,?)")->execute([$userId,$slug]);
|
||||
try { db()->prepare("UPDATE users SET platform_onboarding_done=1 WHERE id=?")->execute([$userId]); } catch(Exception $e){}
|
||||
echo json_encode(['success'=>true]);
|
||||
} catch(Exception $e) { echo json_encode(['success'=>false,'error'=>'Already requested for this platform']); }
|
||||
exit;
|
||||
}
|
||||
if ($action === 'dismiss_onboarding') {
|
||||
try { db()->prepare("UPDATE users SET platform_onboarding_done=1 WHERE id=?")->execute([$userId]); } catch(Exception $e){}
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
}
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
if ($action === 'resolve') {
|
||||
$id=$d['id']??0; $status=$d['status']??'';
|
||||
$uname=substr(trim($d['platform_username']??''),0,100);
|
||||
$pass=substr(trim($d['platform_password']??''),0,200);
|
||||
$note=substr(trim($d['admin_note']??''),0,300);
|
||||
if (!in_array($status,['approved','denied','deleted'])){echo json_encode(['success'=>false,'error'=>'Invalid status']);exit;}
|
||||
$chk=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$chk->execute([$id]);$row=$chk->fetch();
|
||||
if (!$row){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
||||
db()->prepare("UPDATE platform_accounts SET status=?,platform_username=?,platform_password=?,admin_note=?,resolved_at=NOW(),admin_id=? WHERE id=?")
|
||||
->execute([$status,$uname,$pass,$note,(int)$_SESSION['user_id'],$id]);
|
||||
if ($status==='approved'&&$uname) {
|
||||
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
||||
->execute([$row['user_id'],$row['platform_slug'],$uname]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);exit;
|
||||
}
|
||||
if ($action === 'update_credentials') {
|
||||
$id=$d['id']??0;
|
||||
$uname=substr(trim($d['platform_username']??''),0,100);
|
||||
$pass=substr(trim($d['platform_password']??''),0,200);
|
||||
$note=substr(trim($d['admin_note']??''),0,300);
|
||||
$chk=db()->prepare("SELECT user_id,platform_slug FROM platform_accounts WHERE id=?");$chk->execute([$id]);$row=$chk->fetch();
|
||||
if (!$row){echo json_encode(['success'=>false,'error'=>'Not found']);exit;}
|
||||
db()->prepare("UPDATE platform_accounts SET platform_username=?,platform_password=?,admin_note=? WHERE id=?")
|
||||
->execute([$uname,$pass,$note,$id]);
|
||||
if ($uname) {
|
||||
db()->prepare("INSERT INTO game_aliases (user_id,platform_slug,alias) VALUES (?,?,?) ON DUPLICATE KEY UPDATE alias=VALUES(alias)")
|
||||
->execute([$row['user_id'],$row['platform_slug'],$uname]);
|
||||
}
|
||||
echo json_encode(['success'=>true]);exit;
|
||||
}
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
ob_start();
|
||||
try { require_once __DIR__ . '/../../includes/auth.php'; } catch(Throwable $e) { ob_end_clean(); header('Content-Type: application/json'); echo json_encode(['success'=>false,'error'=>'Server error']); exit; }
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$isAdmin = isLoggedIn() && !empty($_SESSION['is_admin']);
|
||||
|
||||
switch ($action) {
|
||||
|
||||
// ── Public: active platforms for player app ───────────
|
||||
case 'list':
|
||||
$stmt = db()->query("SELECT slug,name,player_url,color,icon_path FROM platforms WHERE is_active=1 ORDER BY sort_order ASC, id ASC");
|
||||
$rows = $stmt->fetchAll();
|
||||
// Normalize to match old CFG format
|
||||
$out = array_map(fn($r) => [
|
||||
'id' => $r['slug'],
|
||||
'name' => $r['name'],
|
||||
'url' => $r['player_url'],
|
||||
'color' => $r['color'],
|
||||
], $rows);
|
||||
echo json_encode(['success'=>true, 'platforms'=>$out]);
|
||||
break;
|
||||
|
||||
// ── Admin: full list including console_url and inactive ─
|
||||
case 'admin_list':
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$rows = db()->query("SELECT * FROM platforms ORDER BY sort_order ASC, id ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true, 'platforms'=>$rows]);
|
||||
break;
|
||||
|
||||
// ── Admin: create platform ────────────────────────────
|
||||
case 'create':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$slug = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($d['slug'] ?? '')));
|
||||
$name = substr(trim($d['name'] ?? ''), 0, 100);
|
||||
$player_url = substr(trim($d['player_url'] ?? ''), 0, 500);
|
||||
$console_url = substr(trim($d['console_url'] ?? ''), 0, 500);
|
||||
$color = preg_match('/^#[0-9a-fA-F]{3,8}$/', $d['color'] ?? '') ? $d['color'] : '#f0c040';
|
||||
$sort_order = (int)($d['sort_order'] ?? 99);
|
||||
$is_active = isset($d['is_active']) ? (int)(bool)$d['is_active'] : 1;
|
||||
if (!$slug || !$name || !$player_url) { echo json_encode(['success'=>false,'error'=>'Slug, name, and player URL are required']); exit; }
|
||||
try {
|
||||
$stmt = db()->prepare("INSERT INTO platforms (slug,name,player_url,console_url,color,sort_order,is_active) VALUES (?,?,?,?,?,?,?)");
|
||||
$stmt->execute([$slug,$name,$player_url,$console_url,$color,$sort_order,$is_active]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success'=>false,'error'=>'Slug already exists or DB error']);
|
||||
}
|
||||
break;
|
||||
|
||||
// ── Admin: update platform ────────────────────────────
|
||||
case 'update':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$name = substr(trim($d['name'] ?? ''), 0, 100);
|
||||
$player_url = substr(trim($d['player_url'] ?? ''), 0, 500);
|
||||
$console_url = substr(trim($d['console_url'] ?? ''), 0, 500);
|
||||
$color = preg_match('/^#[0-9a-fA-F]{3,8}$/', $d['color'] ?? '') ? $d['color'] : '#f0c040';
|
||||
$sort_order = (int)($d['sort_order'] ?? 99);
|
||||
$is_active = (int)(bool)($d['is_active'] ?? 1);
|
||||
if (!$id || !$name || !$player_url) { echo json_encode(['success'=>false,'error'=>'ID, name, and player URL required']); exit; }
|
||||
db()->prepare("UPDATE platforms SET name=?,player_url=?,console_url=?,color=?,sort_order=?,is_active=? WHERE id=?")
|
||||
->execute([$name,$player_url,$console_url,$color,$sort_order,$is_active,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ── Admin: delete platform ────────────────────────────
|
||||
case 'delete':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
||||
db()->prepare("DELETE FROM platforms WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
// ── Admin: reorder platforms ──────────────────────────
|
||||
case 'reorder':
|
||||
if (!$isAdmin || $_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
$order = $d['order'] ?? []; // array of IDs in desired order
|
||||
$stmt = db()->prepare("UPDATE platforms SET sort_order=? WHERE id=?");
|
||||
foreach ($order as $i => $pid) { $stmt->execute([$i, (int)$pid]); }
|
||||
echo json_encode(['success'=>true]);
|
||||
break;
|
||||
|
||||
default:
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
require_once __DIR__ . '/../../includes/square.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; }
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$sourceId = trim($data['source_id'] ?? '');
|
||||
$tokens = (int)($data['tokens'] ?? 0);
|
||||
$priceCents = (int)($data['price_cents'] ?? 0);
|
||||
$method = trim($data['method'] ?? 'card');
|
||||
$platformId = trim($data['platform_id'] ?? '');
|
||||
$gameAlias = trim($data['game_alias'] ?? '');
|
||||
$playerName = trim($data['player_name'] ?? '');
|
||||
$isCustom = (bool)($data['is_custom'] ?? false);
|
||||
$billing = $data['billing'] ?? [];
|
||||
$userId = $_SESSION['user_id'];
|
||||
|
||||
// ─── Validate ─────────────────────────────────────────────
|
||||
if ($tokens < 1 || $priceCents < 100) {
|
||||
echo json_encode(['success'=>false,'error'=>'Minimum purchase is $1 (1 token).']); exit;
|
||||
}
|
||||
|
||||
// Validate preset packages (custom bypasses preset check)
|
||||
if (!$isCustom) {
|
||||
$packages = json_decode(TOKEN_PACKAGES, true);
|
||||
$validPkg = false;
|
||||
foreach ($packages as $pkg) {
|
||||
if ($pkg['tokens'] === $tokens && ($pkg['price'] * 100) === $priceCents) { $validPkg = true; break; }
|
||||
}
|
||||
if (!$validPkg) {
|
||||
echo json_encode(['success'=>false,'error'=>'Invalid token package.']); exit;
|
||||
}
|
||||
} else {
|
||||
// Custom: tokens must equal dollars (1:1 ratio), cap at $500
|
||||
if ($tokens !== ($priceCents / 100) || $tokens > 500) {
|
||||
echo json_encode(['success'=>false,'error'=>'Invalid custom amount.']); exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize billing fields
|
||||
$billingFirst = substr(trim($billing['first_name'] ?? ''), 0, 80);
|
||||
$billingLast = substr(trim($billing['last_name'] ?? ''), 0, 80);
|
||||
$billingAddress = substr(trim($billing['address'] ?? ''), 0, 200);
|
||||
$billingCity = substr(trim($billing['city'] ?? ''), 0, 80);
|
||||
$billingState = strtoupper(substr(trim($billing['state'] ?? ''), 0, 2));
|
||||
$billingZip = substr(trim($billing['zip'] ?? ''), 0, 10);
|
||||
$billingEmail = substr(strtolower(trim($billing['email'] ?? '')), 0, 150);
|
||||
$cardholderName = trim("$billingFirst $billingLast");
|
||||
|
||||
$isManual = in_array($method, ['venmo','chime','cashapp','zelle']);
|
||||
|
||||
// ─── Manual payment ────────────────────────────────────────
|
||||
if ($isManual) {
|
||||
$stmt = db()->prepare("
|
||||
INSERT INTO token_purchases
|
||||
(user_id, tokens, amount_cents, payment_method, platform_id, game_alias,
|
||||
player_name, billing_name, billing_email, is_custom, status)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,'pending')
|
||||
");
|
||||
$stmt->execute([
|
||||
$userId, $tokens, $priceCents, $method, $platformId, $gameAlias,
|
||||
$playerName, $cardholderName, $billingEmail, $isCustom ? 1 : 0
|
||||
]);
|
||||
$pid = db()->lastInsertId();
|
||||
logActivity('manual_payment_pending', $userId, null, 'purchase', 0, "Manual payment pending: {$paymentMethod} \${$amountDollars}");
|
||||
echo json_encode(['success'=>true,'manual'=>true,'purchase_id'=>$pid,
|
||||
'message'=>"Request #{$pid} submitted! Tokens credited after payment verification."]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ─── Card payment via Square ───────────────────────────────
|
||||
if (empty($sourceId)) { echo json_encode(['success'=>false,'error'=>'Card payment token required.']); exit; }
|
||||
|
||||
$square = new SquarePayment();
|
||||
$note = "TomGames {$tokens}tok | {$platformId} | {$gameAlias} | user#{$userId}" . ($isCustom ? ' [CUSTOM]' : '');
|
||||
|
||||
// Build buyer info for Square
|
||||
$buyerInfo = [];
|
||||
if ($cardholderName) $buyerInfo['buyer_email_address'] = $billingEmail ?: null;
|
||||
$billingAddr = [];
|
||||
if ($billingAddress) $billingAddr['address_line_1'] = $billingAddress;
|
||||
if ($billingCity) $billingAddr['locality'] = $billingCity;
|
||||
if ($billingState) $billingAddr['administrative_district_level_1'] = $billingState;
|
||||
if ($billingZip) $billingAddr['postal_code'] = $billingZip;
|
||||
$billingAddr['country'] = 'US';
|
||||
|
||||
$result = $square->charge($sourceId, $priceCents, $note, $cardholderName, $billingAddr, $billingEmail);
|
||||
|
||||
if (!$result['success']) {
|
||||
// Log failed attempt
|
||||
db()->prepare("INSERT INTO token_purchases (user_id,tokens,amount_cents,payment_method,platform_id,game_alias,player_name,billing_name,billing_email,is_custom,status,failure_reason) VALUES (?,?,?,'card',?,?,?,?,?,?,'failed',?)")
|
||||
->execute([$userId,$tokens,$priceCents,$platformId,$gameAlias,$playerName,$cardholderName,$billingEmail,$isCustom?1:0,$result['error']]);
|
||||
echo json_encode(['success'=>false,'error'=>$result['error']]); exit;
|
||||
}
|
||||
|
||||
// ─── Credit tokens ─────────────────────────────────────────
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$tokens,$userId]);
|
||||
$cardBrand = $result['card_brand'] ?? null;
|
||||
$cardLast4 = $result['last_4'] ?? null;
|
||||
$receiptUrl= $result['receipt_url'] ?? null;
|
||||
|
||||
db()->prepare("
|
||||
INSERT INTO token_purchases
|
||||
(user_id, tokens, amount_cents, payment_method, square_payment_id,
|
||||
platform_id, game_alias, player_name, billing_name, billing_address,
|
||||
billing_city, billing_state, billing_zip, billing_email,
|
||||
is_custom, card_brand, card_last4, receipt_url, status)
|
||||
VALUES (?,?,?,'card',?,?,?,?,?,?,?,?,?,?,?,?,?,?,'completed')
|
||||
")->execute([
|
||||
$userId, $tokens, $priceCents, $result['payment_id'],
|
||||
$platformId, $gameAlias, $playerName,
|
||||
$cardholderName, $billingAddress, $billingCity, $billingState, $billingZip, $billingEmail,
|
||||
$isCustom ? 1 : 0, $cardBrand, $cardLast4, $receiptUrl
|
||||
]);
|
||||
|
||||
db()->commit();
|
||||
} catch (Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'Token credit failed. Payment ID: '.$result['payment_id']]); exit;
|
||||
}
|
||||
|
||||
// ─── Update saved billing (separate try — must NOT roll back token credit) ──
|
||||
try {
|
||||
db()->prepare("
|
||||
INSERT INTO saved_billing (user_id,first_name,last_name,email,address,city,state,zip,card_brand,card_last4)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
card_brand=VALUES(card_brand), card_last4=VALUES(card_last4),
|
||||
first_name=COALESCE(NULLIF(VALUES(first_name),''),first_name),
|
||||
last_name=COALESCE(NULLIF(VALUES(last_name),''),last_name),
|
||||
email=COALESCE(NULLIF(VALUES(email),''),email),
|
||||
address=COALESCE(NULLIF(VALUES(address),''),address),
|
||||
city=COALESCE(NULLIF(VALUES(city),''),city),
|
||||
state=COALESCE(NULLIF(VALUES(state),''),state),
|
||||
zip=COALESCE(NULLIF(VALUES(zip),''),zip)
|
||||
")->execute([
|
||||
$userId, $billingFirst, $billingLast, $billingEmail,
|
||||
$billingAddress, $billingCity, $billingState, $billingZip,
|
||||
$cardBrand, $cardLast4
|
||||
]);
|
||||
} catch (Exception $e) { /* non-critical — tokens already credited */ }
|
||||
|
||||
$bal = db()->prepare("SELECT tokens FROM users WHERE id=?");
|
||||
$bal->execute([$userId]);
|
||||
$newBal = (float)$bal->fetchColumn();
|
||||
logActivity('token_purchase', $userId, null, 'purchase', (int)db()->lastInsertId(),
|
||||
"Bought {$tokens} tokens via {$paymentMethod} for \${$amountDollars}");
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'manual' => false,
|
||||
'tokens_added' => (int)$tokens,
|
||||
'new_balance' => $newBal,
|
||||
'payment_id' => $result['payment_id'],
|
||||
'card_brand' => $cardBrand,
|
||||
'card_last4' => $cardLast4,
|
||||
'receipt_url' => $receiptUrl,
|
||||
]);
|
||||
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
ob_start();
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isLoggedIn()) { echo json_encode(['success'=>false,'error'=>'Not authenticated']); exit; }
|
||||
|
||||
$userId = (int)$_SESSION['user_id'];
|
||||
$isAdmin = !empty($_SESSION['is_admin']);
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$action = $_GET['action'] ?? 'status';
|
||||
|
||||
// ── GET actions ───────────────────────────────────────────
|
||||
if ($method === 'GET') {
|
||||
|
||||
if ($action === 'status') {
|
||||
// Player's referral dashboard data
|
||||
$user = db()->prepare("SELECT referral_code, referred_by FROM users WHERE id=?");
|
||||
$user->execute([$userId]);
|
||||
$u = $user->fetch();
|
||||
|
||||
// Auto-generate code if missing
|
||||
if (empty($u['referral_code'])) {
|
||||
$code = strtoupper(substr(md5($userId.uniqid()),0,8));
|
||||
db()->prepare("UPDATE users SET referral_code=? WHERE id=?")->execute([$code, $userId]);
|
||||
$u['referral_code'] = $code;
|
||||
}
|
||||
|
||||
// Count verified referrals
|
||||
$countStmt = db()->prepare("SELECT COUNT(*) FROM referrals WHERE referrer_id=? AND status='verified'");
|
||||
$countStmt->execute([$userId]);
|
||||
$verified = (int)$countStmt->fetchColumn();
|
||||
|
||||
// All referrals
|
||||
$refs = db()->prepare("
|
||||
SELECT r.*, u.username, u.alias, u.email_verified, u.created_at AS joined_at
|
||||
FROM referrals r
|
||||
JOIN users u ON r.referred_id = u.id
|
||||
WHERE r.referrer_id = ?
|
||||
ORDER BY r.created_at DESC
|
||||
");
|
||||
$refs->execute([$userId]);
|
||||
|
||||
// Current tier
|
||||
$tier = db()->query("
|
||||
SELECT * FROM referral_tiers
|
||||
WHERE is_active=1 AND min_referrals <= $verified
|
||||
ORDER BY min_referrals DESC LIMIT 1
|
||||
")->fetch();
|
||||
|
||||
// Next tier
|
||||
$nextTier = db()->query("
|
||||
SELECT * FROM referral_tiers
|
||||
WHERE is_active=1 AND min_referrals > $verified
|
||||
ORDER BY min_referrals ASC LIMIT 1
|
||||
")->fetch();
|
||||
|
||||
// Total tokens earned from referrals
|
||||
$earned = db()->prepare("SELECT COALESCE(SUM(tokens_awarded),0) FROM referrals WHERE referrer_id=? AND status='verified'");
|
||||
$earned->execute([$userId]);
|
||||
|
||||
// Social shares
|
||||
$shares = db()->prepare("SELECT * FROM referral_social_shares WHERE user_id=? ORDER BY created_at DESC");
|
||||
$shares->execute([$userId]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'referral_code' => $u['referral_code'] ?? '',
|
||||
'referral_url' => (defined('SITE_URL')?SITE_URL:'https://tomtomgames.com') . '/?ref=' . ($u['referral_code'] ?? ''),
|
||||
'verified_count' => $verified,
|
||||
'total_earned' => (float)$earned->fetchColumn(),
|
||||
'current_tier' => $tier,
|
||||
'next_tier' => $nextTier,
|
||||
'referrals' => $refs->fetchAll(),
|
||||
'social_shares' => $shares->fetchAll(),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'tiers') {
|
||||
$rows = db()->query("SELECT * FROM referral_tiers WHERE is_active=1 ORDER BY min_referrals ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'tiers'=>$rows]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'all_tiers' && $isAdmin) {
|
||||
$rows = db()->query("SELECT * FROM referral_tiers ORDER BY sort_order ASC, min_referrals ASC")->fetchAll();
|
||||
echo json_encode(['success'=>true,'tiers'=>$rows]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'admin_list' && $isAdmin) {
|
||||
$status = $_GET['status'] ?? 'pending';
|
||||
$stmt = db()->prepare("
|
||||
SELECT r.*,
|
||||
ru.username AS referrer_name, ru.alias AS referrer_alias,
|
||||
rd.username AS referred_name, rd.alias AS referred_alias, rd.email_verified,
|
||||
t.name AS tier_name
|
||||
FROM referrals r
|
||||
JOIN users ru ON r.referrer_id = ru.id
|
||||
JOIN users rd ON r.referred_id = rd.id
|
||||
LEFT JOIN referral_tiers t ON r.tier_id = t.id
|
||||
WHERE r.status = ?
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT 100
|
||||
");
|
||||
$stmt->execute([$status]);
|
||||
echo json_encode(['success'=>true,'referrals'=>$stmt->fetchAll()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'admin_shares' && $isAdmin) {
|
||||
$status = $_GET['status'] ?? 'pending';
|
||||
$stmt = db()->prepare("
|
||||
SELECT rs.*, u.username, u.alias
|
||||
FROM referral_social_shares rs
|
||||
JOIN users u ON rs.user_id = u.id
|
||||
WHERE rs.status = ?
|
||||
ORDER BY rs.created_at DESC
|
||||
");
|
||||
$stmt->execute([$status]);
|
||||
echo json_encode(['success'=>true,'shares'=>$stmt->fetchAll()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']); exit;
|
||||
}
|
||||
|
||||
// ── POST actions ──────────────────────────────────────────
|
||||
if ($method !== 'POST') { echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit; }
|
||||
$d = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
if ($action === 'submit_share') {
|
||||
$platform = preg_replace('/[^a-z0-9_]/', '', strtolower(trim($d['platform'] ?? '')));
|
||||
if (!$platform) { echo json_encode(['success'=>false,'error'=>'Platform required']); exit; }
|
||||
// Get bonus tokens for this platform from tiers config
|
||||
$bonus = 5; // default
|
||||
try {
|
||||
db()->prepare("INSERT INTO referral_social_shares (user_id,platform,bonus_tokens) VALUES (?,?,?)")
|
||||
->execute([$userId, $platform, $bonus]);
|
||||
echo json_encode(['success'=>true]);
|
||||
} catch(Exception $e) {
|
||||
echo json_encode(['success'=>false,'error'=>'Already submitted for this platform']);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Admin only below ──────────────────────────────────────
|
||||
if (!$isAdmin) { echo json_encode(['success'=>false,'error'=>'Forbidden']); exit; }
|
||||
|
||||
if ($action === 'resolve_referral') {
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$status = $d['status'] ?? '';
|
||||
$note = substr(trim($d['note'] ?? ''), 0, 300);
|
||||
if (!in_array($status, ['verified','denied','deleted'])) { echo json_encode(['success'=>false,'error'=>'Invalid status']); exit; }
|
||||
|
||||
$chk = db()->prepare("SELECT r.*, t.tokens_per_ref, t.min_referrals, t.bonus_tokens FROM referrals r LEFT JOIN referral_tiers t ON r.tier_id=t.id WHERE r.id=?");
|
||||
$chk->execute([$id]);
|
||||
$ref = $chk->fetch();
|
||||
if (!$ref) { echo json_encode(['success'=>false,'error'=>'Not found']); exit; }
|
||||
|
||||
if ($status === 'verified') {
|
||||
// Determine best tier for referrer
|
||||
$countStmt = db()->prepare("SELECT COUNT(*) FROM referrals WHERE referrer_id=? AND status='verified'");
|
||||
$countStmt->execute([$ref['referrer_id']]);
|
||||
$verifiedCount = (int)$countStmt->fetchColumn() + 1; // +1 for this one
|
||||
|
||||
$tier = db()->query("SELECT * FROM referral_tiers WHERE is_active=1 AND min_referrals <= $verifiedCount ORDER BY min_referrals DESC LIMIT 1")->fetch();
|
||||
$tokensToAward = $tier ? (float)$tier['tokens_per_ref'] : 5;
|
||||
|
||||
// Check if this hits a bonus milestone
|
||||
$bonusTokens = 0;
|
||||
if ($tier && $verifiedCount == (int)$tier['min_referrals']) {
|
||||
$bonusTokens = (float)$tier['bonus_tokens'];
|
||||
}
|
||||
|
||||
$totalAward = $tokensToAward + $bonusTokens;
|
||||
|
||||
db()->beginTransaction();
|
||||
try {
|
||||
db()->prepare("UPDATE referrals SET status='verified', tier_id=?, tokens_awarded=?, admin_id=?, admin_note=?, resolved_at=NOW() WHERE id=?")
|
||||
->execute([$tier['id'] ?? null, $totalAward, (int)$_SESSION['user_id'], $note, $id]);
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$totalAward, $ref['referrer_id']]);
|
||||
logAdminAction('REFERRAL_VERIFIED', (int)$_SESSION['user_id'], 'referral', $id, 'Verified referral #'.$id.' — awarded '.$totalAward.' tokens to user #'.$ref['referrer_id'], 'pending', 'verified', 'info');
|
||||
db()->commit();
|
||||
echo json_encode(['success'=>true,'tokens_awarded'=>$totalAward,'bonus'=>$bonusTokens,'tier'=>$tier['name']??'']);
|
||||
} catch(Exception $e) {
|
||||
db()->rollBack();
|
||||
echo json_encode(['success'=>false,'error'=>'DB error']);
|
||||
}
|
||||
} else {
|
||||
db()->prepare("UPDATE referrals SET status=?, admin_id=?, admin_note=?, resolved_at=NOW() WHERE id=?")
|
||||
->execute([$status, (int)$_SESSION['user_id'], $note, $id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'resolve_share') {
|
||||
$id = (int)($d['id'] ?? 0);
|
||||
$status = $d['status'] ?? '';
|
||||
if (!in_array($status, ['approved','denied'])) { echo json_encode(['success'=>false,'error'=>'Invalid']); exit; }
|
||||
$chk = db()->prepare("SELECT * FROM referral_social_shares WHERE id=?"); $chk->execute([$id]); $share = $chk->fetch();
|
||||
if (!$share) { echo json_encode(['success'=>false,'error'=>'Not found']); exit; }
|
||||
db()->prepare("UPDATE referral_social_shares SET status=?,admin_id=?,resolved_at=NOW() WHERE id=?")->execute([$status,(int)$_SESSION['user_id'],$id]);
|
||||
if ($status === 'approved') {
|
||||
db()->prepare("UPDATE users SET tokens=tokens+? WHERE id=?")->execute([$share['bonus_tokens'],$share['user_id']]);
|
||||
logAdminAction('SOCIAL_SHARE_APPROVED', (int)$_SESSION['user_id'], 'referral_share', $id, 'Approved social share #'.$id.' — awarded '.$share['bonus_tokens'].' bonus tokens to user #'.$share['user_id'], '', 'approved', 'info');
|
||||
}
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Tier CRUD ─────────────────────────────────────────────
|
||||
if ($action === 'tier_create') {
|
||||
$name = substr(trim($d['name']??''),0,100);
|
||||
$minRefs = (int)($d['min_referrals']??1);
|
||||
$tokPer = (float)($d['tokens_per_ref']??5);
|
||||
$bonus = (float)($d['bonus_tokens']??0);
|
||||
$desc = substr(trim($d['description']??''),0,300);
|
||||
$sort = (int)($d['sort_order']??99);
|
||||
$active = (int)(bool)($d['is_active']??1);
|
||||
if (!$name) { echo json_encode(['success'=>false,'error'=>'Name required']); exit; }
|
||||
db()->prepare("INSERT INTO referral_tiers (name,min_referrals,tokens_per_ref,bonus_tokens,description,is_active,sort_order) VALUES (?,?,?,?,?,?,?)")
|
||||
->execute([$name,$minRefs,$tokPer,$bonus,$desc,$active,$sort]);
|
||||
echo json_encode(['success'=>true,'id'=>db()->lastInsertId()]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'tier_update') {
|
||||
$id = (int)($d['id']??0);
|
||||
$name = substr(trim($d['name']??''),0,100);
|
||||
$minRefs = (int)($d['min_referrals']??1);
|
||||
$tokPer = (float)($d['tokens_per_ref']??5);
|
||||
$bonus = (float)($d['bonus_tokens']??0);
|
||||
$desc = substr(trim($d['description']??''),0,300);
|
||||
$sort = (int)($d['sort_order']??0);
|
||||
$active = (int)(bool)($d['is_active']??1);
|
||||
if (!$id||!$name) { echo json_encode(['success'=>false,'error'=>'ID and name required']); exit; }
|
||||
db()->prepare("UPDATE referral_tiers SET name=?,min_referrals=?,tokens_per_ref=?,bonus_tokens=?,description=?,is_active=?,sort_order=? WHERE id=?")
|
||||
->execute([$name,$minRefs,$tokPer,$bonus,$desc,$active,$sort,$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'tier_delete') {
|
||||
$id = (int)($d['id']??0);
|
||||
if (!$id) { echo json_encode(['success'=>false,'error'=>'ID required']); exit; }
|
||||
db()->prepare("DELETE FROM referral_tiers WHERE id=?")->execute([$id]);
|
||||
echo json_encode(['success'=>true]);
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['success'=>false,'error'=>'Unknown action']);
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$username = trim($data['username'] ?? '');
|
||||
$password = trim($data['password'] ?? '');
|
||||
$alias = trim($data['alias'] ?? '');
|
||||
$email = trim($data['email'] ?? '');
|
||||
$referralCode= trim($data['referral_code']?? '');
|
||||
|
||||
logSecurityEvent('REGISTER_ATTEMPT', null, 'Registration attempt for: ' . $email, 'info');
|
||||
$result = initiateRegistration($username, $password, $alias, $email, $referralCode);
|
||||
echo json_encode($result);
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../includes/auth.php';
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success'=>false,'error'=>'Method not allowed']); exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$email = trim($data['email'] ?? '');
|
||||
|
||||
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
echo json_encode(['success'=>false,'error'=>'Valid email required']); exit;
|
||||
}
|
||||
|
||||
echo json_encode(resendVerification($email));
|
||||
Reference in New Issue
Block a user