PHP WebShell
Текущая директория: /var/www/bitcardoApp/user/giftcards
Просмотр файла: submit_card.php
<?php
include '../common/header.php';
require_once '../../config/db_config.php';
if (session_status() === PHP_SESSION_NONE) { session_start(); }
// CSRF token
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Fetch all active card brands
$brands = [];
$brand_stmt = $conn->prepare("SELECT cbrand_id, card_brand FROM card_brands WHERE status = 1 ORDER BY card_brand ASC");
$brand_stmt->execute();
$brands_result = $brand_stmt->get_result();
while ($row = $brands_result->fetch_assoc()) {
$brands[] = $row;
}
$brand_stmt->close();
?>
<div class="container mt-4">
<div class="row">
<?php include '../common/nav.php'; ?>
<main class="col-md-9 col-lg-10 px-md-5 mb-5">
<?php include '../common/page-header.php'; ?>
<div class="container">
<div class="offset-md-3 col-md-6 mt-3">
<div class="card-header text-white">
<h5 class="mb-0">Submit Gift Card for Trade</h5>
</div>
<div class="card-body">
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-danger"><?= $_SESSION['error']; unset($_SESSION['error']); ?></div>
<?php elseif (isset($_SESSION['success'])): ?>
<div class="alert alert-success">
<?= $_SESSION['success']; unset($_SESSION['success']); ?>
</div>
<?php endif; ?>
<form action="../../models/giftcards/process_trade.php" method="POST" id="cardTradeForm" enctype="multipart/form-data" novalidate>
<!-- CSRF -->
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token']) ?>">
<div id="cardContainer">
<div class="card-entry border border-1 rounded-4 p-3 mb-3" style="border-style: dashed;">
<!-- Brand & Denom -->
<div class="row">
<div class="col-5 mb-3">
<label class="form-label ps-1">Card Brand</label>
<select class="form-select cbrand-select" name="cbrand_id[]" required>
<option value="">Select Brand</option>
<?php foreach ($brands as $brand): ?>
<option value="<?= (int)$brand['cbrand_id'] ?>"><?= htmlspecialchars($brand['card_brand']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-7 mb-1">
<div class="d-flex justify-content-between align-items-center">
<label class="form-label mb-0 ps-1">Gift Card</label>
<small class="text-muted price-info"></small>
</div>
<select class="form-select mt-1 gc-select" name="gc_id[]" required disabled>
<option value="">Select Gift Card</option>
</select>
</div>
</div>
<!-- Upload Images & Card Value -->
<div class="row align-items-end">
<div class="col-7 mb-3">
<label class="form-label ps-1">Upload Card Image</label>
<input type="file" name="card_images_0[]" class="form-control card-images" required
accept=".jpg,.jpeg,.png,.pdf">
</div>
<div class="col-4 mb-3">
<label class="form-label ps-1">Card Value</label>
<input type="number" name="card_value[]" class="form-control card-value" step="0.01" min="0" placeholder="e.g. 100" required>
</div>
<div class="col-1 mb-3 d-flex justify-content-end">
<button type="button" class="btn btn-danger btn-sm remove-card" style="display:none;" aria-label="Remove card">
<i class="bi bi-dash-circle"></i>
</button>
</div>
</div>
<!-- Per-entry payout -->
<div class="d-flex justify-content-between small mt-1">
<span class="text-muted">Estimated payout:</span>
<strong class="payout-info">—</strong>
</div>
</div>
</div>
<!-- Grand total + helper note -->
<div class="d-flex justify-content-between align-items-center my-3">
<span class="fw-semibold">Total Estimated Payout</span>
<span id="grandTotal" class="fs-5">₦0.00</span>
</div>
<small id="incompleteNote" class="text-danger d-none">
Complete all required fields in the current card to add another.
</small>
<!-- Add Button -->
<div class="d-flex justify-content-center my-2">
<button type="button" id="addCard" class="btn btn-success btn-sm" disabled>
<i class="bi bi-plus-circle"></i> Add More Card
</button>
</div>
<div class="mb-3 mt-3">
<label for="note" class="form-label ps-1">Note <small class="text-muted">(Optional)</small></label>
<textarea name="note" id="note" class="form-control" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary w-100">Sell My Cards</button>
</form>
</div>
</div>
</div>
</main>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const cardContainer = document.getElementById("cardContainer");
const addCardBtn = document.getElementById("addCard");
const grandTotalEl = document.getElementById("grandTotal");
const incompleteNote = document.getElementById("incompleteNote");
const form = document.getElementById("cardTradeForm");
const MAX_FILES = 6;
const MAX_SIZE = 8 * 1024 * 1024; // 8MB
function formatMoney(n) {
const val = isFinite(n) ? Number(n) : 0;
return '₦' + val.toFixed(2);
}
function perEntryComplete(entry) {
const brand = entry.querySelector('.cbrand-select');
const gc = entry.querySelector('.gc-select');
const value = entry.querySelector('.card-value');
const files = entry.querySelector('.card-images');
const brandOk = !!brand.value;
const gcOk = !!gc.value;
const valOk = value.value && parseFloat(value.value) > 0;
const fileOk = files && files.files && files.files.length > 0;
return brandOk && gcOk && valOk && fileOk;
}
function updateAddButtonState() {
const entries = cardContainer.querySelectorAll(".card-entry");
const allComplete = Array.from(entries).every(perEntryComplete);
addCardBtn.disabled = !allComplete;
incompleteNote.classList.toggle('d-none', allComplete);
}
function computeAndRenderPayout(entry) {
const gcSelect = entry.querySelector('.gc-select');
const valueInput = entry.querySelector('.card-value');
const payoutInfo = entry.querySelector('.payout-info');
const opt = gcSelect.options[gcSelect.selectedIndex] || {};
const price = parseFloat(opt.dataset?.price || '0'); // NGN per 1 unit of card currency
const val = parseFloat(valueInput.value || '0');
const payout = price * val; // NGN
payoutInfo.textContent = (price > 0 && val > 0) ? formatMoney(payout) : '—';
computeGrandTotal();
}
function computeGrandTotal() {
let total = 0;
const entries = cardContainer.querySelectorAll(".card-entry");
entries.forEach(entry => {
const gcSelect = entry.querySelector('.gc-select');
const valueInput = entry.querySelector('.card-value');
const opt = gcSelect.options[gcSelect.selectedIndex] || {};
const price = parseFloat(opt.dataset?.price || '0');
const val = parseFloat(valueInput.value || '0');
if (price > 0 && val > 0) total += price * val;
});
grandTotalEl.textContent = formatMoney(total);
}
function validateFiles(filesInput) {
const files = filesInput.files;
if (!files || files.length === 0) return true;
if (files.length > MAX_FILES) {
alert(`You can upload at most ${MAX_FILES} files per card.`);
filesInput.value = '';
updateAddButtonState();
return false;
}
for (const f of files) {
if (f.size > MAX_SIZE) {
alert(`Each file must be ≤ 8MB. "${f.name}" is too large.`);
filesInput.value = '';
updateAddButtonState();
return false;
}
}
return true;
}
function bindCardEvents(entry, index = 0) {
const cbrandSelect = entry.querySelector('.cbrand-select');
const gcSelect = entry.querySelector('.gc-select');
const priceInfo = entry.querySelector('.price-info');
const imageInput = entry.querySelector('.card-images');
const valueInput = entry.querySelector('.card-value');
const removeBtn = entry.querySelector('.remove-card');
// unique name for backend mapping
if (imageInput) imageInput.name = `card_images_${index}[]`;
cbrandSelect.addEventListener('change', function () {
const brandId = this.value;
gcSelect.innerHTML = '<option value="">Loading...</option>';
priceInfo.textContent = '';
gcSelect.disabled = true;
if (!brandId) {
gcSelect.innerHTML = '<option value="">Select Gift Card</option>';
gcSelect.disabled = true;
computeAndRenderPayout(entry);
updateAddButtonState();
return;
}
fetch('fetch_giftcards.php?cbrand_id=' + encodeURIComponent(brandId))
.then(res => res.json())
.then(data => {
gcSelect.innerHTML = '<option value="">Select Gift Card</option>';
data.forEach(gc => {
const option = document.createElement('option');
option.value = gc.gc_id;
option.dataset.price = gc.buy_price; // NGN per 1 unit of card currency
option.dataset.curr = gc.card_curr; // e.g., USD
option.textContent = `${gc.demon} (${gc.card_curr})`;
gcSelect.appendChild(option);
});
gcSelect.disabled = false;
updateAddButtonState();
})
.catch(() => {
gcSelect.innerHTML = '<option value="">Select Gift Card</option>';
gcSelect.disabled = true;
updateAddButtonState();
});
});
gcSelect.addEventListener('change', function () {
const selected = this.options[this.selectedIndex];
const price = selected?.dataset?.price;
const curr = selected?.dataset?.curr;
priceInfo.textContent = (price && curr) ? `${formatMoney(parseFloat(price))} / ${curr}` : '';
computeAndRenderPayout(entry);
updateAddButtonState();
});
if (imageInput) {
imageInput.addEventListener('change', function () {
validateFiles(imageInput);
updateAddButtonState();
});
}
if (valueInput) {
valueInput.addEventListener('input', function () {
computeAndRenderPayout(entry);
updateAddButtonState();
});
}
if (removeBtn) {
removeBtn.addEventListener('click', function () {
entry.remove();
computeGrandTotal();
updateAddButtonState();
});
}
}
// Bind initial card entry
bindCardEvents(cardContainer.querySelector(".card-entry"), 0);
// Evaluate initial state (no files selected yet, so "Add" disabled)
updateAddButtonState();
// Add new card entry
addCardBtn.addEventListener("click", function () {
const entries = cardContainer.querySelectorAll(".card-entry");
const clone = entries[0].cloneNode(true);
const newIndex = entries.length;
// Reset fields
clone.querySelectorAll("input").forEach(input => {
if (input.type === 'file') {
input.value = '';
input.name = `card_images_${newIndex}[]`;
} else {
input.value = '';
}
});
clone.querySelectorAll("select").forEach(select => {
select.selectedIndex = 0;
if (select.classList.contains('gc-select')) {
select.disabled = true;
select.innerHTML = '<option value="">Select Gift Card</option>';
}
});
const priceInfo = clone.querySelector('.price-info');
if (priceInfo) priceInfo.textContent = '';
const payoutInfo = clone.querySelector('.payout-info');
if (payoutInfo) payoutInfo.textContent = '—';
const removeBtn = clone.querySelector('.remove-card');
if (removeBtn) removeBtn.style.display = 'inline-block';
cardContainer.appendChild(clone);
bindCardEvents(clone, newIndex);
// New block is empty, so add button should disable until completed
updateAddButtonState();
});
// Guard: on submit, ensure each entry is complete
form.addEventListener('submit', function (e) {
const entries = cardContainer.querySelectorAll('.card-entry');
let ok = true;
entries.forEach((entry, idx) => {
if (!perEntryComplete(entry)) {
ok = false;
entry.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
if (!ok) {
e.preventDefault();
alert('Please complete all required fields (brand, gift card, images, and value) for each card.');
}
});
});
</script>
<?php include '../common/footer.php'; ?>
Выполнить команду
Для локальной разработки. Не используйте в интернете!