PHP WebShell
Текущая директория: /var/www/bitcardoApp/models/giftcards
Просмотр файла: process_trade.php
<?php
require_once '../../config/db_config.php';
if (session_status() === PHP_SESSION_NONE) { session_start(); }
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
// ===== Auth =====
if (empty($_SESSION['user_id'])) {
$_SESSION['error'] = 'Please sign in to submit a trade.';
header("Location: ../../user/giftcards/submit_card.php");
exit;
}
$user_id = (int)$_SESSION['user_id'];
// ===== CSRF =====
$csrf = $_POST['csrf_token'] ?? '';
if (empty($csrf) || !hash_equals($_SESSION['csrf_token'] ?? '', $csrf)) {
$_SESSION['error'] = 'Security check failed. Please try again.';
header("Location: ../../user/giftcards/submit_card.php");
exit;
}
// ===== Input =====
$note = trim($_POST['note'] ?? '');
$cbrand_ids = $_POST['cbrand_id'] ?? [];
$gc_ids = $_POST['gc_id'] ?? [];
$card_values = $_POST['card_value'] ?? [];
if (!is_array($cbrand_ids) || !is_array($gc_ids) || !is_array($card_values)) {
$_SESSION['error'] = "Invalid submission payload.";
header("Location: ../../user/giftcards/submit_card.php");
exit;
}
$entry_count = min(count($cbrand_ids), count($gc_ids), count($card_values));
if ($entry_count === 0) {
$_SESSION['error'] = "Incomplete submission. Please fill in all required fields.";
header("Location: ../../user/giftcards/submit_card.php");
exit;
}
// ===== Config (mirror frontend limits) =====
$MAX_FILES = 6;
$MAX_SIZE = 8 * 1024 * 1024; // 8MB
$ALLOWED_MIME = ['image/jpeg','image/png','image/webp','image/heic','application/pdf'];
$ALLOWED_EXT = ['jpg','jpeg','png','webp','heic','pdf'];
// ===== Helpers =====
function gen_batch_ref(): string {
// Pattern: 2 uppercase letters + 4 digits (e.g., AB2390)
$letters = strtoupper(bin2hex(random_bytes(1))); // 2 hex chars -> we’ll map to letters
// Map hex to letters A-P to ensure A-Z feel
$map = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P'];
$a = $map[hexdec($letters[0])];
$b = $map[hexdec($letters[1])];
$digits = str_pad((string)random_int(0, 9999), 4, '0', STR_PAD_LEFT);
return $a.$b.$digits;
}
function ensure_dir($path): bool { return is_dir($path) ?: mkdir($path, 0755, true); }
function safe_ext(string $name, array $allowed_ext): string {
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
return in_array($ext, $allowed_ext, true) ? $ext : 'bin';
}
function format_ngn($n): string { return '₦' . number_format((float)$n, 2, '.', ','); }
// ===== Create batch + legacy trade_ref for backward compat =====
$batch_ref = gen_batch_ref(); // NEW: authoritative batch ID (AB2390)
$trade_ref = $batch_ref; // keep old column aligned for now
// ===== Base upload dir: /year/month/batch_ref/ =====
$year = date('Y');
$month = date('m');
$base_upload_dir = "../../backyard/uploads/cards/{$year}/{$month}/{$batch_ref}/";
if (!ensure_dir($base_upload_dir)) {
$_SESSION['error'] = "Unable to prepare upload directory.";
header("Location: ../../user/giftcards/submit_card.php");
exit;
}
// ===== Prepare statements (once) =====
$gc_stmt = $conn->prepare("
SELECT gc.buy_price, gc.card_curr
FROM gift_cards gc
JOIN card_brands cb ON cb.cbrand_id = gc.cbrand_id
WHERE gc.gc_id = ? AND gc.cbrand_id = ? AND gc.status = 1 AND cb.status = 1
");
$trade_stmt = $conn->prepare("
INSERT INTO card_trade
(trade_ref, batch_ref, card_ref, user_id, cbrand_id, gc_id, card_value, card_curr,
buy_price_snapshot, est_payout_ngn, note, trade_status, trade_created)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', NOW())
");
/*
* Bind types (11 placeholders before 'pending', NOW()):
* s trade_ref
* s batch_ref
* s card_ref
* i user_id
* i cbrand_id
* i gc_id
* d card_value
* s card_curr
* d buy_price_snapshot
* d est_payout_ngn
* s note
* => "sssiiidsdds"
*/
$image_stmt = $conn->prepare("
INSERT INTO card_trade_images
(trade_id, path, original_name, mime, size, uploaded_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$finfo = new finfo(FILEINFO_MIME_TYPE);
// ===== Process entries =====
$cards_created = 0;
$cards_failed = 0;
$total_estimate = 0.0;
$error_messages = [];
// Sequential per-card reference inside the batch (…-001, …-002, …)
$seq = 0;
for ($i = 0; $i < $entry_count; $i++) {
try {
$cbrand_id = (int)$cbrand_ids[$i];
$gc_id = (int)$gc_ids[$i];
$card_value = (float)$card_values[$i];
// Basic validation
if ($cbrand_id <= 0 || $gc_id <= 0 || $card_value <= 0) {
$cards_failed++;
$error_messages[] = "Card #".($i+1).": invalid brand/card/value.";
continue;
}
// Files presence
$img_field = "card_images_{$i}";
if (empty($_FILES[$img_field]) || empty($_FILES[$img_field]['name'])) {
$cards_failed++;
$error_messages[] = "Card #".($i+1).": no image(s) uploaded.";
continue;
}
// Validate gift card & get snapshot price/curr
$gc_stmt->bind_param("ii", $gc_id, $cbrand_id);
$gc_stmt->execute();
$res = $gc_stmt->get_result();
if ($res->num_rows === 0) {
$cards_failed++;
$error_messages[] = "Card #".($i+1).": gift card not available or inactive.";
continue;
}
$gc_row = $res->fetch_assoc();
$buy_price = (float)$gc_row['buy_price']; // NGN per 1 unit
$card_curr = (string)$gc_row['card_curr'];
if ($buy_price <= 0) {
$cards_failed++;
$error_messages[] = "Card #".($i+1).": invalid buy price.";
continue;
}
$est_payout = $buy_price * $card_value;
// Build per-card reference
$seq++;
$card_ref = $batch_ref . '-' . str_pad((string)$seq, 3, '0', STR_PAD_LEFT); // e.g., AB2390-001
// Per-entry transaction
$conn->begin_transaction();
// Insert trade master row
$note_param = $note; // keep as string
$trade_stmt->bind_param(
"sssiiidsdds",
$trade_ref, $batch_ref, $card_ref, $user_id, $cbrand_id, $gc_id,
$card_value, $card_curr, $buy_price, $est_payout, $note_param
);
$trade_stmt->execute();
$trade_id = $conn->insert_id;
// Validate files (count/size/mime)
$names = $_FILES[$img_field]['name'];
$tmps = $_FILES[$img_field]['tmp_name'];
$errs = $_FILES[$img_field]['error'];
$sizes = $_FILES[$img_field]['size'];
// Count check
$file_count = 0;
foreach ($tmps as $tmpv) { if (!empty($tmpv)) $file_count++; }
if ($file_count === 0) {
throw new RuntimeException("no valid files uploaded.");
}
if ($file_count > $MAX_FILES) {
throw new RuntimeException("too many files (max {$MAX_FILES}).");
}
// First pass: validate all
for ($j = 0; $j < count($tmps); $j++) {
if (empty($tmps[$j])) continue;
if ($errs[$j] !== UPLOAD_ERR_OK) {
throw new RuntimeException("upload error on file #".($j+1).".");
}
if ($sizes[$j] > $MAX_SIZE) {
throw new RuntimeException("a file exceeds 8MB.");
}
$mime = $finfo->file($tmps[$j]) ?: 'application/octet-stream';
if (!in_array($mime, $ALLOWED_MIME, true)) {
throw new RuntimeException("unsupported file type.");
}
}
// Second pass: move + insert rows
for ($j = 0; $j < count($tmps); $j++) {
if (empty($tmps[$j])) continue;
$mime = $finfo->file($tmps[$j]) ?: 'application/octet-stream';
$orig = $names[$j] ?? 'file';
$ext = safe_ext($orig, $ALLOWED_EXT);
$fname = strtoupper(bin2hex(random_bytes(8))) . '.' . $ext;
$target_path = $base_upload_dir . $fname;
if (!move_uploaded_file($tmps[$j], $target_path)) {
throw new RuntimeException("failed to save an uploaded file.");
}
$rel_path = "{$year}/{$month}/{$batch_ref}/{$fname}";
$size_int = (int)$sizes[$j];
$image_stmt->bind_param("isssi", $trade_id, $rel_path, $orig, $mime, $size_int);
$image_stmt->execute();
}
$conn->commit();
$cards_created++;
$total_estimate += $est_payout;
} catch (Throwable $e) {
try { $conn->rollback(); } catch (Throwable $ignore) {}
$cards_failed++;
$error_messages[] = "Card #".($i+1).": " . $e->getMessage();
continue;
}
}
// ===== Cleanup =====
$gc_stmt->close();
$trade_stmt->close();
$image_stmt->close();
// ===== Feedback =====
if ($cards_created > 0 && $cards_failed === 0) {
$_SESSION['success'] =
"{$cards_created} card(s) submitted under batch <strong>{$batch_ref}</strong>. " .
"Estimated total payout: <strong>" . format_ngn($total_estimate) . "</strong>.<br>" .
"<a href=\"../../user/giftcards/card_transactions.php\" class=\"btn btn-sm btn-outline-light mt-2\">
View my transactions
</a>";
} elseif ($cards_created > 0 && $cards_failed > 0) {
$_SESSION['success'] =
"{$cards_created} card(s) submitted under batch <strong>{$batch_ref}</strong>. " .
"Estimated total payout: <strong>" . format_ngn($total_estimate) . "</strong>.<br>" .
"<a href=\"../../user/giftcards/card_transactions.php\" class=\"btn btn-sm btn-outline-light mt-2\">
View my transactions
</a>";
$_SESSION['error'] = "Some cards failed: " . htmlspecialchars(implode(' ', $error_messages));
} else {
$_SESSION['error'] = "No valid cards were processed. " . htmlspecialchars(implode(' ', $error_messages));
}
header("Location: ../../user/giftcards/submit_card.php");
exit;
Выполнить команду
Для локальной разработки. Не используйте в интернете!