PHP WebShell
Текущая директория: /var/www/bitcardoApp/backyard/models/dashboard
Просмотр файла: index.php
<?php
// backyard/models/dashboard/index.php
// Pure functions that read from $conn (procedural MySQLi). No side effects.
function dash_q_scalar(mysqli $conn, string $sql, string $field = 'c', int|float $default = 0) {
try {
$res = mysqli_query($conn, $sql);
if ($res) {
$row = mysqli_fetch_assoc($res);
mysqli_free_result($res);
if ($row && array_key_exists($field, $row)) {
return is_numeric($row[$field]) ? 0 + $row[$field] : $default;
}
}
} catch (\mysqli_sql_exception $e) {}
return $default;
}
function dash_col_exists(mysqli $conn, string $table, string $col): bool {
$table = mysqli_real_escape_string($conn, $table);
$col = mysqli_real_escape_string($conn, $col);
try {
$sql = "SHOW COLUMNS FROM `{$table}` LIKE '{$col}'";
$res = mysqli_query($conn, $sql);
if ($res) {
$ok = (mysqli_num_rows($res) > 0);
mysqli_free_result($res);
return $ok;
}
} catch (\mysqli_sql_exception $e) {}
return false;
}
function dash_users_pk(mysqli $conn): string {
// Prefer users.user_id if present, else users.id
return dash_col_exists($conn, 'users', 'user_id') ? 'user_id' : 'id';
}
function dash_detect_cwallet_balance_col(mysqli $conn): string {
// Try common balance column names on cwallet
$candidates = ['wallet_balance', 'balance', 'available_balance', 'cwallet_balance'];
foreach ($candidates as $c) {
if (dash_col_exists($conn, 'cwallet', $c)) return $c;
}
// fallback (will read as 0 if not present in row)
return 'wallet_balance';
}
/**
* Overview requirements:
* 1) Today's Transactions amount = SUM(transactions.amount today) + SUM(card_trade.est_payout_ngn today)
* 2) Users = distinct users who did transactions today (transactions + card_trade)
* 3) Transactions = total count overall (transactions + card_trade)
*/
function dash_get_overview(mysqli $conn): array {
$overview = [
'users_count' => 0, // users who transacted today
'transactions_count' => 0, // TODAY's transactions count (transactions + card_trade)
'today_tx_amount' => 0.0, // TODAY's amount (transactions.amount + card_trade.est_payout_ngn)
];
// Today's total amount (transactions.amount + giftcard est_payout_ngn)
$tx_today_amount = (float) dash_q_scalar(
$conn,
"SELECT COALESCE(SUM(`amount`),0) AS c
FROM `transactions`
WHERE DATE(`created_at`) = CURDATE()"
);
$gc_today_amount = (float) dash_q_scalar(
$conn,
"SELECT COALESCE(SUM(`est_payout_ngn`),0) AS c
FROM `card_trade`
WHERE DATE(`trade_created`) = CURDATE()"
);
$overview['today_tx_amount'] = $tx_today_amount + $gc_today_amount;
// Today's transaction count (transactions + giftcards)
$tx_today_count = (int) dash_q_scalar(
$conn,
"SELECT COUNT(*) AS c
FROM `transactions`
WHERE DATE(`created_at`) = CURDATE()"
);
$gc_today_count = (int) dash_q_scalar(
$conn,
"SELECT COUNT(*) AS c
FROM `card_trade`
WHERE DATE(`trade_created`) = CURDATE()"
);
$overview['transactions_count'] = $tx_today_count + $gc_today_count;
// Users who transacted today (distinct union)
$overview['users_count'] = (int) dash_q_scalar(
$conn,
"SELECT COUNT(*) AS c FROM (
SELECT DISTINCT t.user_id AS uid
FROM transactions t
WHERE t.user_id IS NOT NULL AND t.user_id > 0
AND DATE(t.created_at) = CURDATE()
UNION
SELECT DISTINCT ct.user_id AS uid
FROM card_trade ct
WHERE ct.user_id IS NOT NULL AND ct.user_id > 0
AND DATE(ct.trade_created) = CURDATE()
) x",
'c',
0
);
return $overview;
}
/**
* Platform wallets (FULL):
* - shows ALL wallets in cwallet
* - includes Platform Balance, All Users Balance, and growth %
* - $limit <= 0 => no LIMIT
*/
function dash_get_platform_wallets(mysqli $conn, int $limit = 0): array {
$wallets = [];
$limit = (int)$limit;
$balCol = dash_detect_cwallet_balance_col($conn);
$sql = "SELECT * FROM `cwallet` ORDER BY `cwallet_id` ASC";
if ($limit > 0) $sql .= " LIMIT {$limit}";
try {
$res = mysqli_query($conn, $sql);
if (!$res) return $wallets;
while ($w = mysqli_fetch_assoc($res)) {
$coin = trim((string)($w['coin'] ?? ''));
$icon = (string)($w['icon'] ?? '');
$status = (string)($w['status'] ?? '');
// platform balance (resilient)
$platform_balance = 0.0;
if (isset($w[$balCol]) && is_numeric($w[$balCol])) {
$platform_balance = (float)$w[$balCol];
} elseif (isset($w['wallet_balance']) && is_numeric($w['wallet_balance'])) {
$platform_balance = (float)$w['wallet_balance'];
}
// user balance sum for this coin
$user_balance = 0.0;
if ($coin !== '') {
$coin_safe = mysqli_real_escape_string($conn, $coin);
$sum_sql = "SELECT COALESCE(SUM(`balance`),0) AS total_user_balance
FROM `user_wallets`
WHERE `coin` = '{$coin_safe}'";
$user_balance = (float) dash_q_scalar($conn, $sum_sql, 'total_user_balance', 0.0);
}
// growth percent: (platform - users) / users * 100
$growth_percent = 0.0;
if ($user_balance > 0) {
$growth_percent = (($platform_balance - $user_balance) / $user_balance) * 100.0;
}
$wallets[] = [
'coin' => $coin,
'icon' => $icon,
'status' => $status,
'platform_balance' => $platform_balance,
'user_balance' => $user_balance,
'growth_percent' => $growth_percent,
];
}
mysqli_free_result($res);
} catch (\mysqli_sql_exception $e) {}
return $wallets;
}
function dash_get_recent_giftcard_batches(mysqli $conn, int $limit = 10): array {
$rows = [];
$limit = max(1, (int)$limit);
$usersPk = dash_users_pk($conn);
$sql = "
SELECT
ct.batch_ref,
MAX(u.first_name) AS first_name,
MAX(u.last_name) AS last_name,
SUM(ct.est_payout_ngn) AS total_payout,
COUNT(*) AS cards_count,
SUM(ct.trade_status IN ('SUCCESS','COMPLETED','APPROVED')) AS ok_count,
SUM(ct.trade_status IN ('PENDING','PROCESSING')) AS pending_count,
SUM(ct.trade_status IN ('DECLINED','FAILED','REJECTED')) AS bad_count,
MAX(ct.trade_created) AS last_time
FROM card_trade ct
LEFT JOIN users u ON u.`{$usersPk}` = ct.user_id
WHERE ct.batch_ref IS NOT NULL AND ct.batch_ref <> ''
GROUP BY ct.batch_ref
ORDER BY last_time DESC
LIMIT {$limit}
";
try {
if ($res = mysqli_query($conn, $sql)) {
while ($r = mysqli_fetch_assoc($res)) {
$rows[] = [
'batch_ref' => $r['batch_ref'] ?? '',
'first_name' => $r['first_name'] ?? '',
'last_name' => $r['last_name'] ?? '',
'total_payout' => (float)($r['total_payout'] ?? 0),
'cards_count' => (int)($r['cards_count'] ?? 0),
'ok_count' => (int)($r['ok_count'] ?? 0),
'pending_count' => (int)($r['pending_count'] ?? 0),
'bad_count' => (int)($r['bad_count'] ?? 0),
'last_time' => $r['last_time'] ?? null,
];
}
mysqli_free_result($res);
}
} catch (\mysqli_sql_exception $e) {}
return $rows;
}
function dash_batch_status(array $b): string {
$ok = (int)($b['ok_count'] ?? 0);
$pd = (int)($b['pending_count'] ?? 0);
$bd = (int)($b['bad_count'] ?? 0);
$total = (int)($b['cards_count'] ?? 0);
if ($total > 0 && $ok === $total) return 'Success';
if ($pd > 0 && $bd === 0) return 'Pending';
if ($bd > 0 && $ok === 0 && $pd === 0) return 'Declined';
if ($bd > 0 && ($ok > 0 || $pd > 0)) return 'Mixed';
return 'Unknown';
}
function dash_get_latest_transactions(mysqli $conn, int $limit = 5): array {
$rows = [];
$limit = max(1, (int)$limit);
$usersPk = dash_users_pk($conn);
$sql = "
SELECT
t.trans_id,
t.coin,
t.user_id,
t.wallet_id,
t.sender_address,
t.receiver_address,
t.amount,
t.type,
t.txid,
t.reference,
t.provider,
t.provider_meta,
t.confirmation,
t.status,
t.applied,
t.created_at,
u.first_name,
u.last_name,
u.email
FROM transactions t
LEFT JOIN users u ON u.`{$usersPk}` = t.user_id
ORDER BY t.created_at DESC, t.trans_id DESC
LIMIT {$limit}
";
try {
if ($res = mysqli_query($conn, $sql)) {
while ($r = mysqli_fetch_assoc($res)) $rows[] = $r;
mysqli_free_result($res);
}
} catch (\mysqli_sql_exception $e) {}
return $rows;
}
function dash_get_transaction_by_id(mysqli $conn, int $trans_id): ?array {
$trans_id = (int)$trans_id;
if ($trans_id <= 0) return null;
$usersPk = dash_users_pk($conn);
$sql = "
SELECT
t.*,
u.first_name,
u.last_name,
u.email,
u.phone
FROM transactions t
LEFT JOIN users u ON u.`{$usersPk}` = t.user_id
WHERE t.trans_id = {$trans_id}
LIMIT 1
";
try {
if ($res = mysqli_query($conn, $sql)) {
$row = mysqli_fetch_assoc($res) ?: null;
mysqli_free_result($res);
return $row;
}
} catch (\mysqli_sql_exception $e) {}
return null;
}
/**
* Approve/complete a pending transaction:
* - Locks tx row (FOR UPDATE)
* - If pending-like and applied=0, credits user_wallets.balance and completes tx
*
* Returns: ['ok'=>bool, 'msg'=>string]
*/
function dash_approve_transaction(mysqli $conn, int $trans_id): array {
$trans_id = (int)$trans_id;
if ($trans_id <= 0) return ['ok'=>false,'msg'=>'Invalid transaction ID.'];
mysqli_begin_transaction($conn);
try {
$txSql = "SELECT trans_id, user_id, wallet_id, coin, amount, status, applied
FROM transactions
WHERE trans_id = {$trans_id}
LIMIT 1
FOR UPDATE";
$txRes = mysqli_query($conn, $txSql);
if (!$txRes) { mysqli_rollback($conn); return ['ok'=>false,'msg'=>'Could not load transaction.']; }
$tx = mysqli_fetch_assoc($txRes);
mysqli_free_result($txRes);
if (!$tx) { mysqli_rollback($conn); return ['ok'=>false,'msg'=>'Transaction not found.']; }
$user_id = (int)($tx['user_id'] ?? 0);
$wallet_id = trim((string)($tx['wallet_id'] ?? ''));
$status = strtolower(trim((string)($tx['status'] ?? 'pending')));
$applied = (int)($tx['applied'] ?? 0);
$amount_str = trim((string)($tx['amount'] ?? '0'));
if ($user_id <= 0 || !is_numeric($amount_str) || (float)$amount_str <= 0) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'Transaction data incomplete (user/amount).'];
}
$pendingLike = in_array($status, ['pending','processing','initiated','queued'], true);
if (!$pendingLike) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'Only pending transactions can be approved.'];
}
if ($applied === 1) {
if (dash_col_exists($conn, 'transactions', 'updated_at')) {
mysqli_query($conn, "UPDATE transactions SET status='completed', updated_at=NOW() WHERE trans_id={$trans_id} LIMIT 1");
} else {
mysqli_query($conn, "UPDATE transactions SET status='completed' WHERE trans_id={$trans_id} LIMIT 1");
}
mysqli_commit($conn);
return ['ok'=>true,'msg'=>'Already applied earlier. Status normalized to completed.'];
}
$targetWalletId = null;
if ($wallet_id !== '' && ctype_digit($wallet_id)) {
$targetWalletId = (int)$wallet_id;
}
if ($targetWalletId === null || $targetWalletId <= 0) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>"Transaction wallet_id is missing/non-numeric. Ensure transactions.wallet_id stores user_wallets.wallet_id."];
}
$wSql = "SELECT wallet_id, user_id, coin, balance
FROM user_wallets
WHERE wallet_id = {$targetWalletId}
LIMIT 1
FOR UPDATE";
$wRes = mysqli_query($conn, $wSql);
if (!$wRes) { mysqli_rollback($conn); return ['ok'=>false,'msg'=>'Could not load user wallet.']; }
$w = mysqli_fetch_assoc($wRes);
mysqli_free_result($wRes);
if (!$w) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'Target wallet not found for this wallet_id.'];
}
if ((int)$w['user_id'] !== $user_id) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'Security check failed: wallet does not belong to this user.'];
}
$hasWUpdatedAt = dash_col_exists($conn, 'user_wallets', 'updated_at');
$creditSql = "UPDATE user_wallets
SET balance = balance + {$amount_str}" . ($hasWUpdatedAt ? ", updated_at = NOW()" : "") . "
WHERE wallet_id = {$targetWalletId}
LIMIT 1";
if (!mysqli_query($conn, $creditSql)) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'Failed to credit wallet.'];
}
$hasTUpdatedAt = dash_col_exists($conn, 'transactions', 'updated_at');
$updTx = "UPDATE transactions
SET status='completed',
applied=1" . ($hasTUpdatedAt ? ", updated_at=NOW()" : "") . "
WHERE trans_id={$trans_id}
LIMIT 1";
if (!mysqli_query($conn, $updTx)) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'Failed to update transaction status.'];
}
mysqli_commit($conn);
return ['ok'=>true,'msg'=>'Approved. Wallet credited and transaction completed.'];
} catch (\mysqli_sql_exception $e) {
mysqli_rollback($conn);
return ['ok'=>false,'msg'=>'DB error: '.$e->getMessage()];
}
}
function dash_get_batch_details(mysqli $conn, string $batch_ref): array {
$batch_ref_safe = mysqli_real_escape_string($conn, $batch_ref);
$out = ['batch' => null, 'cards' => []];
$usersPk = dash_users_pk($conn);
$sum_sql = "
SELECT
ct.batch_ref,
MAX(u.first_name) AS first_name,
MAX(u.last_name) AS last_name,
SUM(ct.est_payout_ngn) AS total_payout,
COUNT(*) AS cards_count,
SUM(ct.trade_status IN ('SUCCESS','COMPLETED','APPROVED')) AS ok_count,
SUM(ct.trade_status IN ('PENDING','PROCESSING')) AS pending_count,
SUM(ct.trade_status IN ('DECLINED','FAILED','REJECTED')) AS bad_count,
MAX(ct.trade_created) AS last_time
FROM card_trade ct
LEFT JOIN users u ON u.`{$usersPk}` = ct.user_id
WHERE ct.batch_ref = '{$batch_ref_safe}'
GROUP BY ct.batch_ref
LIMIT 1
";
if ($res = mysqli_query($conn, $sum_sql)) {
$out['batch'] = mysqli_fetch_assoc($res) ?: null;
mysqli_free_result($res);
}
$cards_sql = "
SELECT
ct.trade_id, ct.trade_ref, ct.trade_status, ct.trade_created,
ct.card_value, ct.card_curr, ct.buy_price_snapshot, ct.est_payout_ngn,
ct.note,
cb.card_brand, gc.demon
FROM card_trade ct
LEFT JOIN card_brands cb ON cb.cbrand_id = ct.cbrand_id
LEFT JOIN gift_cards gc ON gc.gc_id = ct.gc_id
WHERE ct.batch_ref = '{$batch_ref_safe}'
ORDER BY ct.trade_created ASC, ct.trade_id ASC
";
if ($cres = mysqli_query($conn, $cards_sql)) {
while ($row = mysqli_fetch_assoc($cres)) {
$out['cards'][] = $row;
}
mysqli_free_result($cres);
}
return $out;
}
function dash_get_images_for_trades(mysqli $conn, array $trade_ids): array {
$imgs = [];
if (empty($trade_ids)) return $imgs;
$ids = array_map('intval', $trade_ids);
$idlist = implode(',', $ids);
$sql = "SELECT trade_id, image_id, path, original_name, mime, size, uploaded_at
FROM card_trade_images
WHERE trade_id IN ({$idlist})
ORDER BY trade_id ASC, image_id ASC";
if ($res = mysqli_query($conn, $sql)) {
while ($r = mysqli_fetch_assoc($res)) {
$tid = (int)$r['trade_id'];
if (!isset($imgs[$tid])) $imgs[$tid] = [];
$imgs[$tid][] = $r;
}
mysqli_free_result($res);
}
return $imgs;
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!