|
|
<?php |
|
|
ini_set('display_errors', 1); |
|
|
ini_set('display_startup_errors', 1); |
|
|
error_reporting(E_ALL); |
|
|
|
|
|
session_start(); |
|
|
if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) { |
|
|
header('Location: ../../index.php'); |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
require_once '../../config.php'; |
|
|
|
|
|
|
|
|
if (!isset($pdo)) { |
|
|
|
|
|
$db_host = '127.0.0.1'; |
|
|
$db_name = 'jmdb'; |
|
|
$db_user = 'root'; |
|
|
$db_pass = 'YourStrongPassword123'; |
|
|
|
|
|
try { |
|
|
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name;charset=utf8", $db_user, $db_pass); |
|
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
|
|
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); |
|
|
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); |
|
|
} catch(PDOException $e) { |
|
|
die("Database connection failed: " . $e->getMessage()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
$username = $_SESSION['username']; |
|
|
$email = $_SESSION['email']; |
|
|
$tier = $_SESSION['tier']; |
|
|
$package = $_SESSION['package']; |
|
|
$balance = $_SESSION['balance']; |
|
|
$total_deposits = $_SESSION['total_deposits']; |
|
|
$total_withdrawals = $_SESSION['total_withdrawals']; |
|
|
$rewards = $_SESSION['rewards']; |
|
|
$earnings = $total_deposits - $total_withdrawals; |
|
|
$user_id = $_SESSION['user_id']; |
|
|
|
|
|
|
|
|
$available_balance = $balance; |
|
|
$pending_withdrawals = 0; |
|
|
|
|
|
|
|
|
try { |
|
|
$stmt = $pdo->prepare("SELECT SUM(amount) as pending_amount FROM withdrawals WHERE user_id = ? AND status IN ('pending', 'processing')"); |
|
|
$stmt->execute([$user_id]); |
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC); |
|
|
$pending_withdrawals = $result['pending_amount'] ?? 0; |
|
|
$available_balance = $balance - $pending_withdrawals; |
|
|
} catch (Exception $e) { |
|
|
|
|
|
error_log("Error fetching pending withdrawals: " . $e->getMessage()); |
|
|
} |
|
|
|
|
|
|
|
|
$destinations = []; |
|
|
try { |
|
|
$stmt = $pdo->prepare("SELECT id, type, details, is_default FROM withdrawal_destinations WHERE user_id = ? AND is_active = 1 ORDER BY is_default DESC, created_at DESC"); |
|
|
$stmt->execute([$user_id]); |
|
|
$destinations = $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
|
} catch (Exception $e) { |
|
|
error_log("Error fetching withdrawal destinations: " . $e->getMessage()); |
|
|
} |
|
|
|
|
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') { |
|
|
$data = json_decode(file_get_contents('php://input'), true); |
|
|
$amount = floatval($data['amount']); |
|
|
$destination_id = intval($data['destination_id']); |
|
|
$idempotency_key = $data['idempotency_key'] ?? ''; |
|
|
|
|
|
|
|
|
if ($amount <= 0) { |
|
|
echo json_encode(['success' => false, 'message' => 'Invalid amount.']); |
|
|
exit; |
|
|
} |
|
|
|
|
|
if (empty($destination_id)) { |
|
|
echo json_encode(['success' => false, 'message' => 'Please select a withdrawal destination.']); |
|
|
exit; |
|
|
} |
|
|
|
|
|
if (empty($idempotency_key)) { |
|
|
echo json_encode(['success' => false, 'message' => 'Security error. Please refresh and try again.']); |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
if ($available_balance < $amount) { |
|
|
echo json_encode(['success' => false, 'message' => 'Insufficient available balance.']); |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
$min_withdrawal = 100; |
|
|
$max_withdrawal = 70000; |
|
|
|
|
|
if ($amount < $min_withdrawal) { |
|
|
echo json_encode(['success' => false, 'message' => "Minimum withdrawal amount is {$min_withdrawal} KES."]); |
|
|
exit; |
|
|
} |
|
|
|
|
|
if ($amount > $max_withdrawal) { |
|
|
echo json_encode(['success' => false, 'message' => "Maximum withdrawal amount is {$max_withdrawal} KES."]); |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
$fee = max(15, $amount * 0.015); |
|
|
$net_amount = $amount - $fee; |
|
|
|
|
|
|
|
|
$pdo->beginTransaction(); |
|
|
|
|
|
try { |
|
|
|
|
|
$stmt = $pdo->prepare("SELECT id, status FROM withdrawals WHERE user_id = ? AND idempotency_key = ?"); |
|
|
$stmt->execute([$user_id, $idempotency_key]); |
|
|
$existing_withdrawal = $stmt->fetch(PDO::FETCH_ASSOC); |
|
|
|
|
|
if ($existing_withdrawal) { |
|
|
|
|
|
$pdo->commit(); |
|
|
echo json_encode([ |
|
|
'success' => true, |
|
|
'idempotent' => true, |
|
|
'withdrawal_id' => $existing_withdrawal['id'], |
|
|
'status' => $existing_withdrawal['status'], |
|
|
'message' => 'Withdrawal request already processed.' |
|
|
]); |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
$stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ? FOR UPDATE"); |
|
|
$stmt->execute([$user_id]); |
|
|
$user = $stmt->fetch(PDO::FETCH_ASSOC); |
|
|
|
|
|
if (!$user || $user['balance'] < $amount) { |
|
|
throw new Exception('Insufficient balance.'); |
|
|
} |
|
|
|
|
|
|
|
|
$stmt = $pdo->prepare("UPDATE users SET balance = balance - ?, total_withdrawals = total_withdrawals + ? WHERE id = ?"); |
|
|
$stmt->execute([$amount, $amount, $user_id]); |
|
|
|
|
|
|
|
|
$reference = "WDL-" . date('Ymd') . "-" . mt_rand(1000, 9999); |
|
|
$stmt = $pdo->prepare("INSERT INTO withdrawals (user_id, destination_id, amount, fee, net_amount, status, idempotency_key, reference) VALUES (?, ?, ?, ?, ?, 'pending', ?, ?)"); |
|
|
$stmt->execute([$user_id, $destination_id, $amount, $fee, $net_amount, $idempotency_key, $reference]); |
|
|
$withdrawal_id = $pdo->lastInsertId(); |
|
|
|
|
|
|
|
|
$new_balance = $user['balance'] - $amount; |
|
|
$stmt = $pdo->prepare("INSERT INTO ledger (user_id, change_amount, balance_after, type, reference, description) VALUES (?, ?, ?, 'withdrawal', ?, ?)"); |
|
|
$stmt->execute([$user_id, -$amount, $new_balance, $withdrawal_id, "Withdrawal request #{$withdrawal_id}"]); |
|
|
|
|
|
|
|
|
$pdo->commit(); |
|
|
|
|
|
|
|
|
$_SESSION['balance'] -= $amount; |
|
|
$_SESSION['total_withdrawals'] += $amount; |
|
|
|
|
|
echo json_encode([ |
|
|
'success' => true, |
|
|
'withdrawal_id' => $withdrawal_id, |
|
|
'reference' => $reference, |
|
|
'message' => 'Withdrawal request submitted successfully! You will receive a confirmation message shortly.' |
|
|
]); |
|
|
|
|
|
} catch (Exception $e) { |
|
|
$pdo->rollBack(); |
|
|
error_log("Withdrawal error: " . $e->getMessage()); |
|
|
echo json_encode(['success' => false, 'message' => 'Withdrawal failed: ' . $e->getMessage()]); |
|
|
} |
|
|
exit; |
|
|
} |
|
|
|
|
|
|
|
|
$recent_withdrawals = []; |
|
|
try { |
|
|
$stmt = $pdo->prepare(" |
|
|
SELECT w.*, wd.details, wd.type |
|
|
FROM withdrawals w |
|
|
LEFT JOIN withdrawal_destinations wd ON w.destination_id = wd.id |
|
|
WHERE w.user_id = ? |
|
|
ORDER BY w.created_at DESC |
|
|
LIMIT 5 |
|
|
"); |
|
|
$stmt->execute([$user_id]); |
|
|
$recent_withdrawals = $stmt->fetchAll(PDO::FETCH_ASSOC); |
|
|
} catch (Exception $e) { |
|
|
error_log("Error fetching recent withdrawals: " . $e->getMessage()); |
|
|
} |
|
|
?> |
|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Japanese Motors — Withdraw Funds</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700;800&display=swap" rel="stylesheet"> |
|
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js"></script> |
|
|
<style> |
|
|
:root { |
|
|
--bg: |
|
|
--card: |
|
|
--card-2: |
|
|
--accent: |
|
|
--muted: rgba(255,255,255,0.6); |
|
|
--glass: rgba(255,255,255,0.04); |
|
|
--promo-gradient: linear-gradient(180deg,#a13df0 0%, #ff2a79 50%, #d70b1a 100%); |
|
|
font-family: 'Poppins', system-ui, Arial; |
|
|
--banner-gradient-start: |
|
|
--banner-gradient-end: |
|
|
--spacing-unit: 1rem; |
|
|
--accent-primary: |
|
|
--accent-secondary: |
|
|
--shadow-hover: 0 6px 18px rgba(0, 0, 0, 0.1); |
|
|
--premium-gold: |
|
|
} |
|
|
|
|
|
body { |
|
|
background: var(--bg); |
|
|
font-family: 'Poppins', sans-serif; |
|
|
transition: all 0.3s ease; |
|
|
min-height: 100vh; |
|
|
} |
|
|
|
|
|
.sidebar { |
|
|
width: 250px; |
|
|
height: 100vh; |
|
|
background: |
|
|
color: |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: -250px; |
|
|
transition: all 0.3s ease; |
|
|
z-index: 1000; |
|
|
overflow-y: auto; |
|
|
} |
|
|
|
|
|
.sidebar.active { |
|
|
left: 0; |
|
|
} |
|
|
|
|
|
|
|
|
margin-left: 0; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.sidebar.active ~ |
|
|
margin-left: 250px; |
|
|
} |
|
|
|
|
|
header { |
|
|
background: |
|
|
color: white; |
|
|
padding: 15px 20px; |
|
|
display: flex; |
|
|
justify-content: space-between; |
|
|
align-items: center; |
|
|
position: relative; |
|
|
z-index: 900; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.sidebar.active ~ |
|
|
margin-left: 250px; |
|
|
} |
|
|
|
|
|
.menu-toggle { |
|
|
background: transparent; |
|
|
border: none; |
|
|
color: white; |
|
|
font-size: 1.5rem; |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
.logo-section { |
|
|
padding: 15px; |
|
|
border-bottom: 1px solid |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 10px; |
|
|
} |
|
|
|
|
|
.brand { |
|
|
font-size: 1.2rem; |
|
|
font-weight: 700; |
|
|
color: |
|
|
} |
|
|
|
|
|
.subtitle { |
|
|
font-size: 0.75rem; |
|
|
color: |
|
|
} |
|
|
|
|
|
.menu { |
|
|
list-style: none; |
|
|
padding: 0; |
|
|
margin: 0; |
|
|
} |
|
|
|
|
|
.menu li a { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
padding: 12px 20px; |
|
|
color: white; |
|
|
text-decoration: none; |
|
|
transition: background 0.3s; |
|
|
} |
|
|
|
|
|
.menu li a:hover { |
|
|
background: |
|
|
} |
|
|
|
|
|
.menu li a i { |
|
|
margin-right: 12px; |
|
|
} |
|
|
|
|
|
.user-footer { |
|
|
padding: 15px; |
|
|
background: |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 10px; |
|
|
position: sticky; |
|
|
bottom: 0; |
|
|
} |
|
|
|
|
|
.avatar { |
|
|
width: 35px; |
|
|
height: 35px; |
|
|
background: |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-weight: bold; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.banner { |
|
|
max-width: 450px; |
|
|
margin: 0 auto calc(var(--spacing-unit) * 2); |
|
|
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary)); |
|
|
border-radius: 12px; |
|
|
padding: calc(var(--spacing-unit) * 1.5); |
|
|
text-align: center; |
|
|
box-shadow: var(--shadow-hover); |
|
|
animation: fadeIn 0.5s ease; |
|
|
} |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(20px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
|
|
|
@keyframes blink { |
|
|
0% { opacity: 1; } |
|
|
50% { opacity: 0.3; } |
|
|
100% { opacity: 1; } |
|
|
} |
|
|
|
|
|
.banner .title { |
|
|
font-size: 1.25rem; |
|
|
margin-bottom: calc(var(--spacing-unit) * 1); |
|
|
color: |
|
|
font-weight: 700; |
|
|
animation: blink 1.5s infinite; |
|
|
} |
|
|
|
|
|
.banner p { |
|
|
font-size: 0.95rem; |
|
|
line-height: 1.6; |
|
|
margin-bottom: calc(var(--spacing-unit) * 1); |
|
|
color: var(--premium-gold); |
|
|
animation: blink 1.5s infinite; |
|
|
} |
|
|
|
|
|
.banner .footer { |
|
|
font-size: 0.75rem; |
|
|
color: rgba(255, 255, 255, 0.9); |
|
|
font-style: italic; |
|
|
animation: blink 1.5s infinite; |
|
|
} |
|
|
|
|
|
.card { |
|
|
background: var(--card); |
|
|
border-radius: 12px; |
|
|
padding: 26px; |
|
|
color: white; |
|
|
box-shadow: 0 6px 0 rgba(0,0,0,0.08) inset; |
|
|
flex: 1; |
|
|
} |
|
|
|
|
|
.balance { |
|
|
background: rgba(255,255,255,0.03); |
|
|
padding: 14px; |
|
|
border-radius: 10px; |
|
|
margin: 18px 0; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: space-between; |
|
|
} |
|
|
|
|
|
.form-group { |
|
|
margin: 12px 0; |
|
|
} |
|
|
|
|
|
label { |
|
|
display: block; |
|
|
margin-bottom: 8px; |
|
|
color: rgba(255,255,255,0.85); |
|
|
} |
|
|
|
|
|
input, select, textarea { |
|
|
width: 100%; |
|
|
padding: 14px; |
|
|
border-radius: 10px; |
|
|
border: 1px solid rgba(255,255,255,0.05); |
|
|
background: transparent; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.btn { |
|
|
display: inline-block; |
|
|
padding: 14px 24px; |
|
|
border-radius: 10px; |
|
|
background: var(--accent); |
|
|
color: |
|
|
font-weight: 700; |
|
|
border: none; |
|
|
cursor: pointer; |
|
|
width: 100%; |
|
|
} |
|
|
|
|
|
.btn-outline { |
|
|
background: transparent; |
|
|
border: 2px solid var(--accent); |
|
|
color: var(--accent); |
|
|
} |
|
|
|
|
|
.btn-disabled { |
|
|
background: rgba(255,255,255,0.1); |
|
|
color: rgba(255,255,255,0.5); |
|
|
cursor: not-allowed; |
|
|
} |
|
|
|
|
|
.dashboard-card { |
|
|
background: rgba(0,0,0,0.2); |
|
|
border-radius: 12px; |
|
|
padding: 20px; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
|
|
|
.stat-card { |
|
|
background: rgba(0,0,0,0.15); |
|
|
border-radius: 10px; |
|
|
padding: 16px; |
|
|
margin-bottom: 16px; |
|
|
} |
|
|
|
|
|
.withdrawal-option { |
|
|
background: rgba(0,0,0,0.15); |
|
|
border-radius: 10px; |
|
|
padding: 16px; |
|
|
margin-bottom: 16px; |
|
|
transition: transform 0.3s ease; |
|
|
cursor: pointer; |
|
|
border: 2px solid transparent; |
|
|
} |
|
|
|
|
|
.withdrawal-option:hover { |
|
|
transform: translateY(-3px); |
|
|
} |
|
|
|
|
|
.withdrawal-option.selected { |
|
|
border-color: var(--accent); |
|
|
} |
|
|
|
|
|
.faq-item { |
|
|
background: rgba(0,0,0,0.15); |
|
|
border-radius: 10px; |
|
|
padding: 16px; |
|
|
margin-bottom: 16px; |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
.faq-answer { |
|
|
display: none; |
|
|
padding-top: 12px; |
|
|
color: var(--muted); |
|
|
} |
|
|
|
|
|
.active-page { |
|
|
background: |
|
|
border-right: 4px solid var(--accent); |
|
|
} |
|
|
|
|
|
.confirmation-modal { |
|
|
display: none; |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background: rgba(0,0,0,0.7); |
|
|
z-index: 1000; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.modal-content { |
|
|
background: var(--card); |
|
|
border-radius: 12px; |
|
|
padding: 30px; |
|
|
width: 90%; |
|
|
max-width: 500px; |
|
|
color: white; |
|
|
} |
|
|
|
|
|
.progress-bar { |
|
|
height: 6px; |
|
|
background: rgba(255,255,255,0.1); |
|
|
border-radius: 3px; |
|
|
overflow: hidden; |
|
|
margin: 15px 0; |
|
|
} |
|
|
|
|
|
.progress-fill { |
|
|
height: 100%; |
|
|
background: var(--accent); |
|
|
width: 0%; |
|
|
transition: width 0.5s ease; |
|
|
} |
|
|
|
|
|
.status-badge { |
|
|
display: inline-block; |
|
|
padding: 4px 8px; |
|
|
border-radius: 12px; |
|
|
font-size: 0.75rem; |
|
|
font-weight: 600; |
|
|
} |
|
|
|
|
|
.status-pending { |
|
|
background-color: |
|
|
color: |
|
|
} |
|
|
|
|
|
.status-processing { |
|
|
background-color: |
|
|
color: |
|
|
} |
|
|
|
|
|
.status-completed { |
|
|
background-color: |
|
|
color: |
|
|
} |
|
|
|
|
|
.status-failed { |
|
|
background-color: |
|
|
color: |
|
|
} |
|
|
|
|
|
@media (max-width: 768px) { |
|
|
.cards { |
|
|
flex-direction: column; |
|
|
} |
|
|
|
|
|
.promo { |
|
|
width: 92%; |
|
|
} |
|
|
|
|
|
.grid-cols-2 { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
|
|
|
.grid-cols-3 { |
|
|
grid-template-columns: 1fr; |
|
|
} |
|
|
} |
|
|
|
|
|
.destination-item { |
|
|
border: 1px solid rgba(255,255,255,0.1); |
|
|
border-radius: 8px; |
|
|
padding: 12px; |
|
|
margin-bottom: 10px; |
|
|
cursor: pointer; |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
|
|
|
.destination-item:hover { |
|
|
background: rgba(255,255,255,0.05); |
|
|
} |
|
|
|
|
|
.destination-item.selected { |
|
|
border-color: var(--accent); |
|
|
background: rgba(239, 223, 45, 0.1); |
|
|
} |
|
|
|
|
|
.destination-default-badge { |
|
|
background: var(--accent); |
|
|
color: |
|
|
font-size: 0.7rem; |
|
|
padding: 2px 6px; |
|
|
border-radius: 4px; |
|
|
margin-left: 8px; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<!-- Sidebar --> |
|
|
<aside class="sidebar" id="sidebar"> |
|
|
<div class="logo-section"> |
|
|
<i data-feather="zap" class="text-yellow-400"></i> |
|
|
<div> |
|
|
<h2 class="brand">JMOTORS</h2> |
|
|
<p class="subtitle">Marketing Platform</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<ul class="menu"> |
|
|
<li><a href="index.php"><i data-feather="home"></i> Dashboard</a></li> |
|
|
<li><a href="meta-uploads.php"><i data-feather="upload"></i> Meta Uploads</a></li> |
|
|
<li><a href="transactions.php"><i data-feather="repeat"></i> Transactions</a></li> |
|
|
<li><a href="transfer.php"><i data-feather="send"></i> Transfer</a></li> |
|
|
<li><a href="daily-product.php"><i data-feather="shopping-bag"></i> Daily Product</a></li> |
|
|
<li><a href="withdraw.php" class="active-page"><i data-feather="dollar-sign"></i> Withdraw</a></li> |
|
|
<li><a href="packages.php"><i data-feather="package"></i> Packages</a></li> |
|
|
<li><a href="loan.php"><i data-feather="credit-card"></i> Loan</a></li> |
|
|
<li><a href="recharge.php"><i data-feather="battery-charging"></i> Recharge</a></li> |
|
|
<li><a href="agent-approval.php"><i data-feather="user-check"></i> Agent Approval</a></li> |
|
|
<li><a href="access-token.php"><i data-feather="key"></i> Access Token</a></li> |
|
|
<li><a href="agent-claim.php"><i data-feather="tag"></i> Agent Claim</a></li> |
|
|
<li><a href="team.php"><i data-feather="users"></i> Team</a></li> |
|
|
</ul> |
|
|
|
|
|
<ul class="menu bottom"> |
|
|
<li><a href="profile.php"><i data-feather="user"></i> Profile</a></li> |
|
|
<li><a href="settings.php"><i data-feather="settings"></i> Settings</a></li> |
|
|
<li><a href="whatsapp-channel.php"><i data-feather="message-square"></i> Whatsapp Channel</a></li> |
|
|
<li><a href="customer-care.php"><i data-feather="headphones"></i> Customer Care</a></li> |
|
|
</ul> |
|
|
|
|
|
<div class="user-footer"> |
|
|
<div class="avatar"><?php echo substr($username, 0, 2); ?></div> |
|
|
<div> |
|
|
<h4><?php echo $username; ?></h4> |
|
|
<p><?php echo $tier; ?> - Marketer</p> |
|
|
</div> |
|
|
</div> |
|
|
</aside> |
|
|
|
|
|
<!-- Main Content --> |
|
|
<div id="content"> |
|
|
<header class="bg-gray-800 text-white p-4"> |
|
|
<div class="flex items-center"> |
|
|
<button class="menu-toggle" id="menu-toggle"> |
|
|
<i data-feather="menu"></i> |
|
|
</button> |
|
|
<div class="ml-4 font-bold text-xl">Jmotors</div> |
|
|
</div> |
|
|
<nav class="flex items-center space-x-6"> |
|
|
<a href="transfer.php" class="hover:text-yellow-300">Transfer</a> |
|
|
<a href="loan.php" class="hover:text-yellow-300">Loans</a> |
|
|
<a href="dailyproduct.php" class="hover:text-yellow-300">New Product</a> |
|
|
<div class="w-9 h-9 rounded-full bg-gradient-to-r from-yellow-300 to-orange-400 flex items-center justify-center font-bold"><?php echo substr($username, 0, 2); ?></div> |
|
|
</nav> |
|
|
</header> |
|
|
|
|
|
<main class="p-4"> |
|
|
<div class="banner"> |
|
|
<div class="title">💵 Withdraw Your Earnings</div> |
|
|
<p>Transfer your hard-earned money to your preferred payment method</p> |
|
|
<div class="footer">⚡ Instant processing • Low fees • Secure transactions</div> |
|
|
</div> |
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-6"> |
|
|
<div class="dashboard-card md:col-span-2"> |
|
|
<h3 class="text-lg font-bold mb-4 flex items-center gap-2"> |
|
|
<i data-feather="dollar-sign" class="text-green-400"></i> Withdrawal Details |
|
|
</h3> |
|
|
|
|
|
<div class="balance"> |
|
|
<div> |
|
|
<p class="text-sm">Available Balance</p> |
|
|
<h3 class="text-xl font-bold"><?php echo number_format($available_balance, 2); ?> KES</h3> |
|
|
</div> |
|
|
<div class="text-right"> |
|
|
<p class="text-sm">Pending Clearance</p> |
|
|
<p class="font-bold"><?php echo number_format($pending_withdrawals, 2); ?> KES</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<form id="withdrawal-form"> |
|
|
<input type="hidden" id="idempotency_key" value=""> |
|
|
|
|
|
<div class="form-group"> |
|
|
<label for="withdrawal-destination">Withdrawal Destination</label> |
|
|
<div id="destination-list"> |
|
|
<?php if (!empty($destinations)): ?> |
|
|
<?php foreach ($destinations as $destination): ?> |
|
|
<div class="destination-item" data-id="<?php echo $destination['id']; ?>"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<div> |
|
|
<strong><?php echo ucfirst($destination['type']); ?></strong> |
|
|
<?php if ($destination['is_default']): ?> |
|
|
<span class="destination-default-badge">Default</span> |
|
|
<?php endif; ?> |
|
|
</div> |
|
|
<i data-feather="check-circle" class="text-green-400 hidden"></i> |
|
|
</div> |
|
|
<p class="text-sm mt-1"><?php echo $destination['details']; ?></p> |
|
|
</div> |
|
|
<?php endforeach; ?> |
|
|
<?php else: ?> |
|
|
<p class="text-gray-400 py-4 text-center">No withdrawal destinations found. <a href="settings.php" class="text-yellow-400 underline">Add one in settings</a>.</p> |
|
|
<?php endif; ?> |
|
|
</div> |
|
|
<input type="hidden" id="destination_id" name="destination_id" required> |
|
|
</div> |
|
|
|
|
|
<div class="form-group"> |
|
|
<label for="amount">Amount to Withdraw (KES)</label> |
|
|
<input type="number" id="amount" name="amount" placeholder="Enter amount" min="100" max="70000" step="0.01" required> |
|
|
<p class="text-xs text-gray-400 mt-1">Minimum: 100 KES | Maximum: 70,000 KES per day | Fee: 15 KES or 1.5% (whichever is higher)</p> |
|
|
</div> |
|
|
|
|
|
<div class="form-group"> |
|
|
<label for="password">Security Password</label> |
|
|
<input type="password" id="password" name="password" placeholder="Enter your password" required> |
|
|
</div> |
|
|
|
|
|
<div class="form-group flex items-center"> |
|
|
<input type="checkbox" id="confirm-cashout" class="w-5 h-5 mr-2"> |
|
|
<label for="confirm-cashout" class="mb-0">I confirm that I want to cash out this amount</label> |
|
|
</div> |
|
|
|
|
|
<div class="bg-yellow-400 bg-opacity-20 p-3 rounded-lg mt-4 flex items-start"> |
|
|
<i data-feather="info" class="text-yellow-400 mr-2 mt-1"></i> |
|
|
<p class="text-sm">Withdrawals are typically processed within 24-72 hours. You'll receive a confirmation message when completed.</p> |
|
|
</div> |
|
|
|
|
|
<button type="submit" class="btn mt-4" id="withdraw-btn" disabled>Withdraw Now</button> |
|
|
</form> |
|
|
</div> |
|
|
|
|
|
<div class="dashboard-card"> |
|
|
<h3 class="text-lg font-bold mb-4 flex items-center gap-2"> |
|
|
<i data-feather="clock" class="text-blue-400"></i> Recent Withdrawals |
|
|
</h3> |
|
|
|
|
|
<?php if (empty($recent_withdrawals)): ?> |
|
|
<div class="text-center py-8" id="no-withdrawals"> |
|
|
<i data-feather="dollar-sign" class="mx-auto text-gray-400 text-4xl"></i> |
|
|
<p class="mt-2 text-gray-400">No recent withdrawals</p> |
|
|
</div> |
|
|
<?php else: ?> |
|
|
<div id="withdrawal-history"> |
|
|
<?php foreach ($recent_withdrawals as $withdrawal): ?> |
|
|
<div class="stat-card"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<div> |
|
|
<h4 class="font-bold"><?php echo ucfirst($withdrawal['type']); ?></h4> |
|
|
<p class="text-sm"><?php echo $withdrawal['details']; ?></p> |
|
|
</div> |
|
|
<div class="text-right"> |
|
|
<p class="font-bold"><?php echo number_format($withdrawal['amount'], 2); ?> KES</p> |
|
|
<span class="status-badge status-<?php echo $withdrawal['status']; ?>"> |
|
|
<?php echo ucfirst($withdrawal['status']); ?> |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
<p class="text-xs text-gray-400 mt-2"> |
|
|
<?php echo date('M j, Y H:i', strtotime($withdrawal['created_at'])); ?> |
|
|
| Ref: <?php echo $withdrawal['reference']; ?> |
|
|
</p> |
|
|
</div> |
|
|
<?php endforeach; ?> |
|
|
</div> |
|
|
<?php endif; ?> |
|
|
|
|
|
<a href="transactions.php" class="btn btn-outline mt-4 block text-center">View Full History</a> |
|
|
|
|
|
<div class="mt-6"> |
|
|
<h4 class="font-bold mb-3 flex items-center gap-2"> |
|
|
<i data-feather="zap" class="text-yellow-400"></i> Quick Cashout |
|
|
</h4> |
|
|
<div class="grid grid-cols-2 gap-2"> |
|
|
<button class="bg-gray-700 py-2 rounded-lg text-sm quick-amount" data-amount="500">500 KES</button> |
|
|
<button class="bg-gray-700 py-2 rounded-lg text-sm quick-amount" data-amount="1000">1,000 KES</button> |
|
|
<button class="bg-gray-700 py-2 rounded-lg text-sm quick-amount" data-amount="2000">2,000 KES</button> |
|
|
<button class="bg-gray-700 py-2 rounded-lg text-sm quick-amount" data-amount="5000">5,000 KES</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="dashboard-card mt-8"> |
|
|
<h3 class="text-lg font-bold mb-4 flex items-center gap-2"> |
|
|
<i data-feather="help-circle" class="text-purple-400"></i> Withdrawal Information |
|
|
</h3> |
|
|
|
|
|
<div class="faq-item"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<h4 class="font-bold">What are the withdrawal limits?</h4> |
|
|
<i data-feather="chevron-down" class="faq-toggle"></i> |
|
|
</div> |
|
|
<div class="faq-answer"> |
|
|
<p>Minimum withdrawal: 100 KES | Maximum per transaction: 70,000 KES | Daily limit: 70,000 KES | Weekly limit: 140,000 KES. Limits may vary based on your account level and verification status.</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="faq-item"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<h4 class="font-bold">How long do withdrawals take?</h4> |
|
|
<i data-feather="chevron-down" class="faq-toggle"></i> |
|
|
</div> |
|
|
<div class="faq-answer"> |
|
|
<p>M-Pesa withdrawals are typically processed within 24-72 hours. Bank transfers may take 1-3 business days. Delays can occur during weekends, holidays, or system maintenance.</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="faq-item"> |
|
|
<div class="flex justify-between items-center"> |
|
|
<h4 class="font-bold">Why was my withdrawal declined?</h4> |
|
|
<i data-feather="chevron-down" class="faq-toggle"></i> |
|
|
</div> |
|
|
<div class="faq-answer"> |
|
|
<p>Withdrawals may be declined due to: insufficient balance, incorrect recipient details, security checks, exceeded limits, or account restrictions. Contact support if you need assistance.</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
</div> |
|
|
|
|
|
<!-- Confirmation Modal --> |
|
|
<div class="confirmation-modal" id="confirmation-modal"> |
|
|
<div class="modal-content"> |
|
|
<h3 class="text-xl font-bold mb-4 flex items-center gap-2"> |
|
|
<i data-feather="check-circle" class="text-green-400"></i> Confirm Withdrawal |
|
|
</h3> |
|
|
|
|
|
<div class="bg-gray-700 p-4 rounded-lg mb-4"> |
|
|
<div class="flex justify-between mb-2"> |
|
|
<span>Amount:</span> |
|
|
<span class="font-bold" id="confirm-amount">0 KES</span> |
|
|
</div> |
|
|
<div class="flex justify-between mb-2"> |
|
|
<span>Fee:</span> |
|
|
<span class="font-bold" id="confirm-fee">0 KES</span> |
|
|
</div> |
|
|
<div class="flex justify-between mb-2"> |
|
|
<span>You'll Receive:</span> |
|
|
<span class="font-bold" id="confirm-net">0 KES</span> |
|
|
</div> |
|
|
<div class="flex justify-between"> |
|
|
<span>To:</span> |
|
|
<span class="font-bold" id="confirm-destination">Not selected</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<p class="text-sm mb-4">Your withdrawal will be processed within 24-72 hours. You'll receive a confirmation message when completed.</p> |
|
|
|
|
|
<div class="progress-bar"> |
|
|
<div class="progress-fill" id="progress-fill"></div> |
|
|
</div> |
|
|
|
|
|
<div class="flex gap-3"> |
|
|
<button class="btn btn-outline flex-1" id="cancel-withdrawal">Cancel</button> |
|
|
<button class="btn flex-1" id="confirm-withdrawal">Confirm</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
feather.replace(); |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
const toggleBtn = document.getElementById('menu-toggle'); |
|
|
const sidebar = document.getElementById('sidebar'); |
|
|
const content = document.getElementById('content'); |
|
|
|
|
|
toggleBtn.addEventListener('click', function() { |
|
|
sidebar.classList.toggle('active'); |
|
|
content.classList.toggle('active'); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('idempotency_key').value = uuid.v4(); |
|
|
|
|
|
|
|
|
const faqItems = document.querySelectorAll('.faq-item'); |
|
|
faqItems.forEach(item => { |
|
|
item.addEventListener('click', function() { |
|
|
const answer = this.querySelector('.faq-answer'); |
|
|
const icon = this.querySelector('.faq-toggle'); |
|
|
|
|
|
if (answer.style.display === 'block') { |
|
|
answer.style.display = 'none'; |
|
|
icon.setAttribute('data-feather', 'chevron-down'); |
|
|
} else { |
|
|
answer.style.display = 'block'; |
|
|
icon.setAttribute('data-feather', 'chevron-up'); |
|
|
} |
|
|
feather.replace(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const destinationItems = document.querySelectorAll('.destination-item'); |
|
|
const destinationIdInput = document.getElementById('destination_id'); |
|
|
|
|
|
destinationItems.forEach(item => { |
|
|
item.addEventListener('click', function() { |
|
|
// Remove selected class from all items |
|
|
destinationItems.forEach(i => { |
|
|
i.classList.remove('selected'); |
|
|
i.querySelector('i').classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
// Add selected class to clicked item |
|
|
this.classList.add('selected'); |
|
|
this.querySelector('i').classList.remove('hidden'); |
|
|
|
|
|
// Set the destination ID |
|
|
destinationIdInput.value = this.getAttribute('data-id'); |
|
|
|
|
|
checkFormValidity(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
if (destinationItems.length > 0) { |
|
|
destinationItems[0].click(); |
|
|
} |
|
|
|
|
|
|
|
|
const quickAmountButtons = document.querySelectorAll('.quick-amount'); |
|
|
const amountInput = document.getElementById('amount'); |
|
|
|
|
|
quickAmountButtons.forEach(button => { |
|
|
button.addEventListener('click', function() { |
|
|
amountInput.value = this.getAttribute('data-amount'); |
|
|
checkFormValidity(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
const withdrawalForm = document.getElementById('withdrawal-form'); |
|
|
const confirmCheckbox = document.getElementById('confirm-cashout'); |
|
|
const withdrawBtn = document.getElementById('withdraw-btn'); |
|
|
const confirmationModal = document.getElementById('confirmation-modal'); |
|
|
const confirmAmount = document.getElementById('confirm-amount'); |
|
|
const confirmFee = document.getElementById('confirm-fee'); |
|
|
const confirmNet = document.getElementById('confirm-net'); |
|
|
const confirmDestination = document.getElementById('confirm-destination'); |
|
|
const cancelWithdrawal = document.getElementById('cancel-withdrawal'); |
|
|
const confirmWithdrawal = document.getElementById('confirm-withdrawal'); |
|
|
const progressFill = document.getElementById('progress-fill'); |
|
|
|
|
|
function checkFormValidity() { |
|
|
const amount = parseFloat(amountInput.value) || 0; |
|
|
const hasDestination = destinationIdInput.value !== ''; |
|
|
const isAmountValid = amount >= 100 && amount <= 70000 && amount <= <?php echo $available_balance; ?>; |
|
|
|
|
|
if (withdrawalForm.checkValidity() && confirmCheckbox.checked && hasDestination && isAmountValid) { |
|
|
withdrawBtn.classList.remove('btn-disabled'); |
|
|
withdrawBtn.disabled = false; |
|
|
} else { |
|
|
withdrawBtn.classList.add('btn-disabled'); |
|
|
withdrawBtn.disabled = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function calculateFee(amount) { |
|
|
return Math.max(15, amount * 0.015); |
|
|
} |
|
|
|
|
|
|
|
|
withdrawalForm.addEventListener('input', checkFormValidity); |
|
|
confirmCheckbox.addEventListener('change', checkFormValidity); |
|
|
amountInput.addEventListener('input', checkFormValidity); |
|
|
|
|
|
|
|
|
withdrawalForm.addEventListener('submit', function(e) { |
|
|
e.preventDefault(); |
|
|
|
|
|
const amount = parseFloat(amountInput.value); |
|
|
const fee = calculateFee(amount); |
|
|
const netAmount = amount - fee; |
|
|
const selectedDestination = document.querySelector('.destination-item.selected'); |
|
|
const destinationText = selectedDestination ? selectedDestination.querySelector('strong').textContent + ' - ' + selectedDestination.querySelector('p').textContent : 'Not selected'; |
|
|
|
|
|
|
|
|
confirmAmount.textContent = amount.toFixed(2) + ' KES'; |
|
|
confirmFee.textContent = fee.toFixed(2) + ' KES'; |
|
|
confirmNet.textContent = netAmount.toFixed(2) + ' KES'; |
|
|
confirmDestination.textContent = destinationText; |
|
|
|
|
|
confirmationModal.style.display = 'flex'; |
|
|
}); |
|
|
|
|
|
|
|
|
cancelWithdrawal.addEventListener('click', function() { |
|
|
confirmationModal.style.display = 'none'; |
|
|
}); |
|
|
|
|
|
|
|
|
confirmWithdrawal.addEventListener('click', function() { |
|
|
const amount = parseFloat(amountInput.value); |
|
|
const destinationId = destinationIdInput.value; |
|
|
const idempotencyKey = document.getElementById('idempotency_key').value; |
|
|
|
|
|
|
|
|
confirmWithdrawal.disabled = true; |
|
|
cancelWithdrawal.disabled = true; |
|
|
confirmWithdrawal.textContent = 'Processing...'; |
|
|
|
|
|
|
|
|
let width = 0; |
|
|
const interval = setInterval(function() { |
|
|
if (width >= 100) { |
|
|
clearInterval(interval); |
|
|
|
|
|
|
|
|
fetch('withdraw.php', { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
amount: amount, |
|
|
destination_id: destinationId, |
|
|
idempotency_key: idempotencyKey |
|
|
}) |
|
|
}) |
|
|
.then(response => response.json()) |
|
|
.then(data => { |
|
|
if (data.success) { |
|
|
|
|
|
alert(data.message || 'Withdrawal successful! Your funds will be sent shortly.'); |
|
|
confirmationModal.style.display = 'none'; |
|
|
|
|
|
|
|
|
withdrawalForm.reset(); |
|
|
checkFormValidity(); |
|
|
|
|
|
|
|
|
document.getElementById('idempotency_key').value = uuid.v4(); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
window.location.reload(); |
|
|
}, 2000); |
|
|
} else { |
|
|
alert(data.message || 'Withdrawal failed. Please try again.'); |
|
|
confirmationModal.style.display = 'none'; |
|
|
confirmWithdrawal.disabled = false; |
|
|
cancelWithdrawal.disabled = false; |
|
|
confirmWithdrawal.textContent = 'Confirm'; |
|
|
} |
|
|
}) |
|
|
.catch(error => { |
|
|
console.error('Error:', error); |
|
|
alert('An error occurred. Please try again.'); |
|
|
confirmationModal.style.display = 'none'; |
|
|
confirmWithdrawal.disabled = false; |
|
|
cancelWithdrawal.disabled = false; |
|
|
confirmWithdrawal.textContent = 'Confirm'; |
|
|
}); |
|
|
} else { |
|
|
width += 5; |
|
|
progressFill.style.width = width + '%'; |
|
|
} |
|
|
}, 50); |
|
|
}); |
|
|
|
|
|
|
|
|
checkFormValidity(); |
|
|
}); |
|
|
</script> |
|
|
</body> |
|
|
</html> |