PHP WebShell
Текущая директория: /var/www/bitcardoApp/user/wallets
Просмотр файла: single-wallet.php
<?php
// Always use full PHP tags; short tags can be disabled on some servers.
include '../common/header.php';
include '../../includes/wallets/single-walllet.php'; // assumes this sets $wallet_id, $coin, $wallet_address, $wallet_balance, $wallet_qr, $coin_label, etc.
require_once '../../config/db_config.php'; // for site_settings
/**
* Assumptions / Hooks:
* - $userEmail is available (user email for Paystack).
* - $wallet_id and $coin are already set by single-walllet.php.
* - $paystack_public_key is available (from your gateways table or config).
*/
// ---------- site_settings helper ----------
function get_site_setting(mysqli $conn, string $key, $default = null) {
$stmt = $conn->prepare("SELECT setting_value FROM site_settings WHERE `setting_key` = ? LIMIT 1");
if ($stmt) {
$stmt->bind_param('s', $key);
$stmt->execute();
$stmt->bind_result($val);
if ($stmt->fetch()) {
$stmt->close();
return $val;
}
$stmt->close();
}
return $default;
}
// NEW: read how fees should be handled for NGN deposits
$add_naira_deposit_fee = (int) get_site_setting($conn, 'add_naira_deposit_fee', 1); // 1=add on top (user pays), 0=deduct from amount
// ---------- Amount formatting helpers ----------
if (!function_exists('coin_decimals_ui')) {
function coin_decimals_ui(string $coin): int {
$coin = strtoupper($coin);
return match ($coin) {
'BTC' => 8,
'ETH' => 10,
'SOL' => 9,
'TRX' => 6,
'USDT', 'USDC' => 6,
'USD', 'NGN' => 2,
default => 8,
};
}
}
if (!function_exists('fmt_coin_amount')) {
function fmt_coin_amount($amount, string $coin): string {
$scale = coin_decimals_ui($coin);
return number_format((float)$amount, $scale, '.', '');
}
}
// Allowed Paystack channels (prioritize bank transfer)
$paystack_channels = ['bank_transfer', 'card', 'ussd', 'mobile_money'];
// (Optional) fallback if not already set
if (!isset($paystack_public_key)) {
// TODO: replace with your actual pull-from-DB/config
$paystack_public_key = 'pk_live_6ae29fb1abed2559c70edc72d79001704e488772';
}
// Mask NGN account number (last 4 digits)
$masked_wallet_address = $wallet_address ?? '';
$coin = strtoupper($coin ?? '');
if ($coin === 'NGN' && !empty($masked_wallet_address)) {
// Keep original formatting; mask only last 4 digits
$masked_wallet_address = preg_replace_callback('/(\d)(?=(?:\D*\d){4}$)/', fn($m) => $m[1], $masked_wallet_address);
$masked_wallet_address = preg_replace('/(\d)(?=(?:\D*\d){0,3}$)/', '*', $masked_wallet_address);
}
// Build links / deposit button
if ($coin === 'NGN') {
$link = '../fiat/send_fiat.php?wallet_id=' . urlencode($wallet_id);
$deposit = '<div class="col-4 me-3"><button type="button" class="btn btn-outline-primary w-100" data-bs-toggle="modal" data-bs-target="#depositModal">Deposit</button></div>';
} else {
$link = '../crypto/send_crypto.php?coin=' . urlencode($coin);
$deposit = '';
}
// Compose display balance text with proper decimals
$display_balance = fmt_coin_amount($wallet_balance ?? 0, $coin) . ' ' . $coin;
$userFName = $userFName ?? '';
$userLName = $userLName ?? '';
$coin_label = $coin_label ?? $coin;
$wallet_qr = $wallet_qr ?? '';
$wallet_address = $wallet_address ?? '';
$userEmail = $userEmail ?? 'user@example.com';
?>
<!-- Main Container -->
<div class="container mt-3">
<div class="row">
<?php include '../common/nav.php'; ?>
<!-- Main Content -->
<main class="col-md-9 col-lg-10 px-md-5 mb-5">
<?php include '../common/page-header.php'; ?>
<div class="container my-5">
<div class="row g-4">
<!-- Center Column -->
<div class="offset-md-3 col-md-6 mt-2">
<div class="card card-body mt-5 text-center">
<h4 class="fw-bold mt-3 mb-0"><?= htmlspecialchars($userFName . ' ' . $userLName); ?></h4>
<h5 class="mb-2 mt-3"><?= htmlspecialchars($coin_label); ?></h5>
<center>
<img class="img img-fluid" style="width: 200px;"
src="<?= '../../assets/qr_codes/' . htmlspecialchars($wallet_qr); ?>"
alt="Wallet QR">
</center>
<div class="badge bg-light text-dark border mt-3 px-3 py-2">
<div id="walletid" class="text-break text-wrap" style="cursor: pointer;" onclick="copyWalletID(this)">
<?= $coin === 'NGN' ? htmlspecialchars($masked_wallet_address) : htmlspecialchars($wallet_address); ?>
</div>
</div>
<small style="font-size: 10px; color: #8f8e94;">Tap to copy</small>
<div id="copied-msg" class="mt-3" style="display: none; color: green;">
Copied!
</div>
<div class="open-business bg-light mt-3 mb-3">
<i class="bi bi-briefcase me-1"></i>
<?= htmlspecialchars($display_balance); ?>
</div>
</div>
<div class="mt-3 d-flex justify-content-center">
<?= $deposit; ?>
<div class="col-4">
<a href="<?= htmlspecialchars($link); ?>" class="btn btn-primary w-100">Transfer</a>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<?php if ($coin === 'NGN'): ?>
<!-- Deposit Amount Modal -->
<div class="modal fade" id="depositModal" tabindex="-1" aria-labelledby="depositModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<form id="depositForm" class="modal-content" onsubmit="startPaystack(event)">
<div class="modal-header">
<h5 class="modal-title" id="depositModalLabel">Deposit (NGN)</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="deposit_amount" class="form-label">Amount (NGN)</label>
<input type="number" min="100" step="50" class="form-control" id="deposit_amount" name="amount" placeholder="e.g. 5000" required>
<div class="form-text" id="fee_hint">
<?php if ($add_naira_deposit_fee): ?>
You’ll be charged Paystack fees on top, so your wallet receives the full amount.
<?php else: ?>
Paystack fees will be deducted from this amount before crediting your wallet.
<?php endif; ?>
</div>
</div>
<div class="small" id="preview_line" style="display:none;"></div>
<input type="hidden" id="wallet_id" value="<?= htmlspecialchars($wallet_id); ?>">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Continue</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
<script>
function copyWalletID(element) {
const text = element.textContent;
navigator.clipboard.writeText(text).then(() => {
const msg = document.getElementById("copied-msg");
msg.style.display = "inline";
setTimeout(() => msg.style.display = "none", 1500);
});
}
</script>
<?php if ($coin === 'NGN'): ?>
<!-- Paystack Inline Script -->
<script src="https://js.paystack.co/v1/inline.js"></script>
<script>
// configure endpoints
const verifyUrlBase = '../../models/fiat/verify_paystack.php';
const addFeeMode = <?= (int)$add_naira_deposit_fee ?>; // 1=add on top; 0=deduct from amount
// Simple Paystack fee estimator (fallback only; server will trust Paystack's fees if present)
// Nigeria local fee: 1.5% + ₦100 (for amounts > ₦2500), capped at ₦2000. (VAT not included here)
function estimatePaystackFee(amountNgn) {
const pct = 0.015;
const extra = amountNgn > 2500 ? 100 : 0;
const cap = 2000;
let fee = (amountNgn * pct) + extra;
if (fee > cap) fee = cap;
return Math.round(fee * 100) / 100; // 2dp
}
// For "add" mode, we need to gross-up so that (entered) arrives net
// amountGross = ceil((amountNet + extra) / (1 - pct)) but Paystack adds ₦100 only if gross>2500; we’ll do a simple iterate.
function grossUpForFee(netAmount) {
// simple iterative approach to hit target within a few Naira
let gross = netAmount;
for (let i=0;i<6;i++) {
const fee = estimatePaystackFee(gross);
const newGross = netAmount + fee;
if (Math.abs(newGross - gross) < 1) return Math.round(newGross);
gross = newGross;
}
return Math.round(gross);
}
// Update preview line
document.getElementById('deposit_amount').addEventListener('input', () => {
const amt = parseFloat(document.getElementById('deposit_amount').value || '0');
const line = document.getElementById('preview_line');
if (!(amt > 0)) { line.style.display='none'; return; }
if (addFeeMode === 1) {
const gross = grossUpForFee(amt);
const fee = gross - amt;
line.textContent = `You will pay ₦${gross.toLocaleString()} (includes ~₦${fee.toLocaleString()} Paystack fees). Wallet receives ₦${amt.toLocaleString()}.`;
} else {
const fee = estimatePaystackFee(amt);
const net = Math.max(0, amt - fee);
line.textContent = `You will pay ₦${amt.toLocaleString()}. Estimated Paystack fees: ~₦${fee.toLocaleString()}. Wallet receives ~₦${net.toLocaleString()}.`;
}
line.style.display='block';
});
function startPaystack(e){
e.preventDefault();
const amountField = document.getElementById('deposit_amount');
let amountNgn = parseInt(amountField.value, 10);
if (isNaN(amountNgn) || amountNgn <= 0) {
amountField.focus();
return;
}
let chargeAmount = amountNgn; // what Paystack will charge
if (addFeeMode === 1) {
// add-on-top: charge gross so wallet receives the entered amount
chargeAmount = grossUpForFee(amountNgn);
}
const amountKobo = chargeAmount * 100;
const ref = 'MWR-' + Date.now() + '-' + Math.floor(Math.random()*1000000);
const handler = PaystackPop.setup({
key: '<?= addslashes($paystack_public_key); ?>',
email: '<?= addslashes($userEmail); ?>',
amount: amountKobo,
currency: 'NGN',
ref: ref,
channels: <?= json_encode($paystack_channels); ?>,
metadata: {
custom_fields: [
{ display_name: "Wallet ID", variable_name: "wallet_id", value: document.getElementById('wallet_id').value }
],
// Pass the user's intended wallet credit (net) and the mode so server can finalize precisely
add_fee_mode: addFeeMode,
intended_wallet_credit: amountNgn
},
callback: function(response){
// Close modal
const modalEl = document.getElementById('depositModal');
if (modalEl) {
const modal = bootstrap.Modal.getInstance(modalEl);
if (modal) modal.hide();
}
// Verify on server (simple redirect)
const verifyUrl = verifyUrlBase
+ '?reference=' + encodeURIComponent(response.reference)
+ '&wallet_id=' + encodeURIComponent(document.getElementById('wallet_id').value)
+ '&amount=' + encodeURIComponent(amountNgn) // the user-entered amount (target credit in add-mode; pay amount in deduct-mode)
+ '&mode=' + encodeURIComponent(addFeeMode); // echo for server
window.location.href = verifyUrl;
},
onClose: function(){
// optional: no-op or go back
}
});
handler.openIframe();
}
</script>
<?php endif; ?>
<?php include '../common/footer.php'; ?>
Выполнить команду
Для локальной разработки. Не используйте в интернете!