diff --git a/jweb/1a/about.html b/jweb/1a/about.html new file mode 100644 index 0000000000000000000000000000000000000000..2a0560949e12694f5dad494f9f35dba874e12680 --- /dev/null +++ b/jweb/1a/about.html @@ -0,0 +1,394 @@ + + + + + + About - Japanese Motors + + + + + + + + + + + + + +
+
+
+
+
+

Our Story

+

Dedicated to Japanese automotive excellence for over two decades

+
+
+
+ + +
+
+
+
+

Welcome to Japanese Motors

+

Since 1999, Japanese Motors has been the premier destination for enthusiasts and collectors of Japanese performance vehicles. What began as a small specialty import business has grown into a comprehensive automotive destination offering sales, service, and restoration of the finest Japanese cars.

+

Our passion for Japanese engineering excellence drives everything we do. From the legendary Nissan Skyline GT-R to the timeless Toyota Supra, we understand the unique character and performance potential of each vehicle we work with.

+
+
+ + 2000+ Vehicles Sold +
+
+ + 1500+ Happy Clients +
+
+
+
+ Japanese Motors Showroom +
+
+
+
+ + +
+
+
+

Our History

+
+

A journey of passion for Japanese automotive excellence

+
+ +
+
+ +
+
1999
+
+

Foundation

+

Japanese Motors was founded by Takashi Yamamoto with a vision to bring exceptional Japanese performance vehicles to enthusiasts worldwide. Started as a small import operation in Tokyo.

+
+
+ + +
+
2005
+
+

First Showroom

+

Opened our first dedicated showroom in Osaka, allowing customers to experience vehicles firsthand. Expanded inventory to include rare JDM models.

+
+
+ + +
+
2012
+
+

Service Department

+

Launched our state-of-the-art service center with factory-trained technicians specializing in Japanese performance vehicles. Became an authorized service provider for multiple brands.

+
+
+ + +
+
2018
+
+

Global Expansion

+

Established international shipping partnerships, enabling us to serve customers across North America, Europe, and Australia. Featured in multiple automotive publications.

+
+
+ + +
+
2023
+
+

Digital Innovation

+

Launched virtual showroom and online concierge services. Celebrated 24 years of excellence in the Japanese automotive industry with over 2,000 vehicles delivered to enthusiasts worldwide.

+
+
+
+
+
+
+ + +
+
+
+

Our Values

+
+

The principles that guide everything we do

+
+ +
+
+
+ +
+

Quality

+

We are committed to delivering the highest quality vehicles and services, with meticulous attention to detail in everything we do.

+
+ +
+
+ +
+

Integrity

+

We believe in transparency and honesty, building trust through clear communication and ethical business practices.

+
+ +
+
+ +
+

Passion

+

Our team shares a genuine passion for Japanese automotive culture, which drives our dedication to excellence.

+
+ +
+
+ +
+

Community

+

We foster a vibrant community of Japanese car enthusiasts through events, knowledge sharing, and mutual appreciation.

+
+
+
+
+ + +
+
+
+

Meet Our Team

+
+

Experts passionate about Japanese automotive excellence

+
+ +
+
+ Takashi Yamamoto +
+

Takashi Yamamoto

+

Founder & CEO

+

With over 30 years in the automotive industry, Takashi's vision established Japanese Motors as a global leader in JDM vehicles.

+
+ + + +
+
+
+ +
+ Yuki Tanaka +
+

Yuki Tanaka

+

Head of Sales

+

Yuki's expertise in JDM vehicles and customer service ensures our clients find their perfect Japanese performance vehicle.

+
+ + + +
+
+
+ +
+ Kenji Sato +
+

Kenji Sato

+

Master Technician

+

Kenji brings 25 years of technical expertise specializing in Nissan GT-R and Toyota Supra performance modifications.

+
+ + + +
+
+
+
+
+
+ + +
+
+
+

What Our Clients Say

+
+

Hear from enthusiasts who have experienced the Japanese Motors difference

+
+ +
+
+
+
+ + + + + +
+
+

"Japanese Motors helped me import my dream R34 GT-R from Japan. Their expertise made the process smooth and worry-free. The car was even better than described!"

+
+
+ Michael Chen +
+
+

Michael Chen

+

California, USA

+
+
+
+ +
+
+
+ + + + + +
+
+

"The team at Japanese Motors transformed my FD RX-7 with their performance upgrades. Their knowledge of rotary engines is unmatched. Absolutely thrilled with the results!"

+
+
+ Sarah Johnson +
+
+

Sarah Johnson

+

London, UK

+
+
+
+ +
+
+
+ + + + + +
+
+

"I've been dealing with Japanese Motors for over 10 years. Their after-sales service and support are exceptional. They truly care about their customers beyond the initial sale."

+
+
+ David Müller +
+
+

David Müller

+

Berlin, Germany

+
+
+
+
+
+
+ + +
+
+

Experience the Japanese Motors Difference

+

Join our community of enthusiasts and discover why we're the trusted name in Japanese performance vehicles.

+ +
+
+ + + + + + + \ No newline at end of file diff --git a/jweb/1a/assets/api/booking_handler.php b/jweb/1a/assets/api/booking_handler.php new file mode 100644 index 0000000000000000000000000000000000000000..e0beda7b5fc6da3f64489633458896c44d7d14be --- /dev/null +++ b/jweb/1a/assets/api/booking_handler.php @@ -0,0 +1,92 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + echo json_encode(['status' => 'error', 'message' => 'Database connection failed: ' . $e->getMessage()]); + exit; +} + +// Process form submission +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + // Validate required fields + $required_fields = ['customer_name', 'email', 'phone', 'vehicle_make', 'vehicle_model', 'service_type', 'service_name']; + + foreach ($required_fields as $field) { + if (empty($_POST[$field])) { + echo json_encode(['status' => 'error', 'message' => "Please fill in all required fields. Missing: $field"]); + exit; + } + } + + // Sanitize input data + $customer_name = filter_var($_POST['customer_name'], FILTER_SANITIZE_STRING); + $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL); + $phone = filter_var($_POST['phone'], FILTER_SANITIZE_STRING); + $vehicle_make = filter_var($_POST['vehicle_make'], FILTER_SANITIZE_STRING); + $vehicle_model = filter_var($_POST['vehicle_model'], FILTER_SANITIZE_STRING); + $vehicle_year = !empty($_POST['vehicle_year']) ? filter_var($_POST['vehicle_year'], FILTER_SANITIZE_NUMBER_INT) : null; + $service_type = filter_var($_POST['service_type'], FILTER_SANITIZE_STRING); + $service_name = filter_var($_POST['service_name'], FILTER_SANITIZE_STRING); + $preferred_date = !empty($_POST['preferred_date']) ? $_POST['preferred_date'] : null; + $preferred_time = !empty($_POST['preferred_time']) ? $_POST['preferred_time'] : null; + $message = !empty($_POST['message']) ? filter_var($_POST['message'], FILTER_SANITIZE_STRING) : null; + + // Validate email + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + echo json_encode(['status' => 'error', 'message' => 'Please provide a valid email address']); + exit; + } + + // Insert data into database + try { + $stmt = $pdo->prepare("INSERT INTO service_bookings + (customer_name, email, phone, vehicle_make, vehicle_model, vehicle_year, + service_type, service_name, preferred_date, preferred_time, message) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + $stmt->execute([ + $customer_name, + $email, + $phone, + $vehicle_make, + $vehicle_model, + $vehicle_year, + $service_type, + $service_name, + $preferred_date, + $preferred_time, + $message + ]); + + // Get the booking ID + $booking_id = $pdo->lastInsertId(); + + // Send confirmation email (optional) + // $this->sendConfirmationEmail($email, $customer_name, $service_name, $booking_id); + + echo json_encode([ + 'status' => 'success', + 'message' => 'Your booking has been submitted successfully! We will contact you shortly to confirm your appointment.', + 'booking_id' => $booking_id + ]); + + } catch (PDOException $e) { + echo json_encode(['status' => 'error', 'message' => 'Failed to save booking: ' . $e->getMessage()]); + } +} else { + echo json_encode(['status' => 'error', 'message' => 'Invalid request method']); +} \ No newline at end of file diff --git a/jweb/1a/assets/api/callback.php b/jweb/1a/assets/api/callback.php new file mode 100644 index 0000000000000000000000000000000000000000..8dd2204aa38ccb7c789b3a9f6ae5be521f7c84e7 --- /dev/null +++ b/jweb/1a/assets/api/callback.php @@ -0,0 +1,51 @@ + false, 'message' => 'Name and phone are required']); + exit; + } + + // Insert callback request + $stmt = $pdo->prepare("INSERT INTO callback_requests (name, phone, preferred_time) VALUES (:name, :phone, :preferred_time)"); + $stmt->execute([ + ':name' => $name, + ':phone' => $phone, + ':preferred_time' => $preferredTime + ]); + + // Send email notification to admin + $to = 'info@japanesemotors.com'; + $subject = 'New Callback Request - Japanese Motors'; + $message = "A new callback request has been received:\n\n"; + $message .= "Name: $name\n"; + $message .= "Phone: $phone\n"; + $message .= "Preferred Time: $preferredTime\n\n"; + $message .= "Please contact the client as soon as possible."; + $headers = 'From: no-reply@japanesemotors.com' . "\r\n" . + 'Reply-To: no-reply@japanesemotors.com' . "\r\n" . + 'X-Mailer: PHP/' . phpversion(); + + mail($to, $subject, $message, $headers); + + echo json_encode(['success' => true, 'message' => 'Callback request submitted successfully! We will contact you soon.']); + } catch(PDOException $e) { + echo json_encode(['success' => false, 'message' => 'Request failed: ' . $e->getMessage()]); + } +} else { + echo json_encode(['success' => false, 'message' => 'Invalid request method']); +} +?> \ No newline at end of file diff --git a/jweb/1a/assets/api/notifications.php b/jweb/1a/assets/api/notifications.php new file mode 100644 index 0000000000000000000000000000000000000000..99bc02930cb393a4c038b3c4ca98656129b5773a --- /dev/null +++ b/jweb/1a/assets/api/notifications.php @@ -0,0 +1,20 @@ +query("SELECT * FROM notifications ORDER BY created_at DESC LIMIT 5"); + $notifications = $stmt->fetchAll(PDO::FETCH_ASSOC); + + echo json_encode(['success' => true, 'data' => $notifications]); +} catch(PDOException $e) { + echo json_encode(['success' => false, 'message' => 'Error fetching notifications: ' . $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/jweb/1a/assets/api/subscribe.php b/jweb/1a/assets/api/subscribe.php new file mode 100644 index 0000000000000000000000000000000000000000..297600dac7c51e2570a68ab349456c1b04b9d164 --- /dev/null +++ b/jweb/1a/assets/api/subscribe.php @@ -0,0 +1,62 @@ + 'Please provide a valid email address.']); + exit; +} + +// Read existing subscriptions +$subscriptions = json_decode(file_get_contents($subscriptionsFile), true); + +// Check if email already exists +foreach ($subscriptions as $sub) { + if ($sub['email'] === $email) { + http_response_code(409); + echo json_encode(['message' => 'This email is already subscribed.']); + exit; + } +} + +// Add new subscription +$newSubscription = [ + 'email' => $email, + 'notification_opt_in' => $notification_opt_in, + 'subscribed_at' => date('Y-m-d H:i:s'), + 'active' => true +]; + +$subscriptions[] = $newSubscription; + +// Save subscriptions +if (file_put_contents($subscriptionsFile, json_encode($subscriptions, JSON_PRETTY_PRINT))) { + http_response_code(200); + echo json_encode(['message' => 'Successfully subscribed to our newsletter!']); +} else { + http_response_code(500); + echo json_encode(['message' => 'Subscription failed. Please try again.']); +} +exit; +?> \ No newline at end of file diff --git a/jweb/1a/assets/api/testimonials.php b/jweb/1a/assets/api/testimonials.php new file mode 100644 index 0000000000000000000000000000000000000000..b57ab6d634cc70f4f3efa82fdaf2a8e6633e568e --- /dev/null +++ b/jweb/1a/assets/api/testimonials.php @@ -0,0 +1,37 @@ +prepare("UPDATE testimonials SET helpful_count = helpful_count + 1 WHERE id = :id"); + $stmt->execute([':id' => $testimonialId]); + + // Get updated count + $stmt = $pdo->prepare("SELECT helpful_count FROM testimonials WHERE id = :id"); + $stmt->execute([':id' => $testimonialId]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + echo json_encode(['success' => true, 'count' => $result['helpful_count']]); + exit; + } + } else { + // Get all testimonials + $stmt = $pdo->query("SELECT * FROM testimonials ORDER BY created_at DESC"); + $testimonials = $stmt->fetchAll(PDO::FETCH_ASSOC); + + echo json_encode(['success' => true, 'data' => $testimonials]); + } +} catch(PDOException $e) { + echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/jweb/1a/assets/api/vehicles.php b/jweb/1a/assets/api/vehicles.php new file mode 100644 index 0000000000000000000000000000000000000000..aba10de9f8b628db25c0e7d6dd52e0d57e92575e --- /dev/null +++ b/jweb/1a/assets/api/vehicles.php @@ -0,0 +1,20 @@ +query("SELECT * FROM vehicles ORDER BY created_at DESC"); + $vehicles = $stmt->fetchAll(PDO::FETCH_ASSOC); + + echo json_encode(['success' => true, 'data' => $vehicles]); +} catch(PDOException $e) { + echo json_encode(['success' => false, 'message' => 'Error fetching vehicles: ' . $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/jweb/1a/assets/config.php b/jweb/1a/assets/config.php new file mode 100644 index 0000000000000000000000000000000000000000..a10181a19f6b8f910239896b609000b140f84e04 --- /dev/null +++ b/jweb/1a/assets/config.php @@ -0,0 +1,17 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + return $pdo; + } catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); + } +} +?> \ No newline at end of file diff --git a/jweb/1a/assets/css/1b.css b/jweb/1a/assets/css/1b.css new file mode 100644 index 0000000000000000000000000000000000000000..bf7c688865507d1a83fa748733f0278602287011 --- /dev/null +++ b/jweb/1a/assets/css/1b.css @@ -0,0 +1,78 @@ +.car-card:hover { + transform: translateY(-5px); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} +.nav-link:hover { + color: #ef4444; +} +.floating-chat { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; +} +.sticky-contact { + position: fixed; + bottom: 20px; + left: 20px; + z-index: 1000; +} +.vr-container { + position: relative; + overflow: hidden; + padding-top: 56.25%; /* 16:9 Aspect Ratio */ +} +.vr-iframe { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 100%; + height: 100%; + border: none; +} +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + z-index: 1000; + overflow-y: auto; +} +.modal-content { + background-color: white; + margin: 5% auto; + padding: 20px; + width: 90%; + max-width: 1000px; + border-radius: 8px; + position: relative; +} +.close-modal { + position: absolute; + top: 15px; + right: 15px; + font-size: 24px; + cursor: pointer; + z-index: 1001; +} +.motor-type-btn.active { + background-color: #ef4444; + color: white; +} +.car-image { + cursor: pointer; + transition: transform 0.3s ease; +} +.car-image:hover { + transform: scale(1.03); +} +.pagination-btn.active { + background-color: #ef4444; + color: white; + border-color: #ef4444; +} \ No newline at end of file diff --git a/jweb/1a/assets/css/1c.css b/jweb/1a/assets/css/1c.css new file mode 100644 index 0000000000000000000000000000000000000000..33fa09daa21354317e6906e38ba7342892ea5316 --- /dev/null +++ b/jweb/1a/assets/css/1c.css @@ -0,0 +1,47 @@ +.contact-card:hover { + transform: translateY(-5px); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} +.nav-link:hover { + color: #ef4444; +} +.floating-chat { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; +} +.sticky-contact { + position: fixed; + bottom: 20px; + left: 20px; + z-index: 1000; +} +.contact-method:hover { + background-color: #fef2f2; + transform: translateX(5px); +} +.form-input:focus { + border-color: #ef4444; + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2); +} +.chat-bubble { + position: relative; + background: #f3f4f6; + border-radius: 18px; + padding: 12px 16px; + max-width: 70%; +} +.chat-bubble:after { + content: ''; + position: absolute; + left: 0; + top: 50%; + width: 0; + height: 0; + border: 10px solid transparent; + border-right-color: #f3f4f6; + border-left: 0; + margin-top: -10px; + margin-left: -10px; +} \ No newline at end of file diff --git a/jweb/1a/assets/css/1d.css b/jweb/1a/assets/css/1d.css new file mode 100644 index 0000000000000000000000000000000000000000..8d623b2b4221d6685048ae01d4e8447dea4b2136 --- /dev/null +++ b/jweb/1a/assets/css/1d.css @@ -0,0 +1,33 @@ +.team-card:hover { + transform: translateY(-5px); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} +.nav-link:hover { + color: #ef4444; +} +.floating-chat { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; +} +.sticky-contact { + position: fixed; + bottom: 20px; + left: 20px; + z-index: 1000; +} +.timeline-item:before { + content: ''; + position: absolute; + left: 0; + top: 0; + height: 100%; + width: 2px; + background-color: #ef4444; +} +.value-card:hover .value-icon { + transform: scale(1.1); + background-color: #ef4444; + color: white; +} \ No newline at end of file diff --git a/jweb/1a/assets/css/1e.css b/jweb/1a/assets/css/1e.css new file mode 100644 index 0000000000000000000000000000000000000000..b441598fcc5a6ab62a0d350fae6950f757eabd53 --- /dev/null +++ b/jweb/1a/assets/css/1e.css @@ -0,0 +1,85 @@ +.service-card:hover { + transform: translateY(-5px); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + } + .nav-link:hover { + color: #ef4444; + } + .floating-chat { + position: fixed; + bottom: 20px; + right: 20px; + z-index: 1000; + } + .sticky-contact { + position: fixed; + bottom: 20px; + left: 20px; + z-index: 1000; + } + .service-tab.active { + background-color: #ef4444; + color: white; + } + .service-content { + display: none; + } + .service-content.active { + display: block; + } + .accordion-content { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-out; + } + .accordion-item.active .accordion-content { + max-height: 500px; + } + .accordion-item.active .accordion-button i { + transform: rotate(180deg); + } + /* Modal styles */ + .modal-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.75); + z-index: 1000; + align-items: center; + justify-content: center; + } + .modal-overlay.active { + display: flex; + } + .modal-container { + background-color: white; + border-radius: 0.5rem; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + width: 90%; + max-width: 600px; + max-height: 90vh; + overflow-y: auto; + } + /* Notification styles */ + .notification { + position: fixed; + top: 1rem; + right: 1rem; + padding: 1rem; + border-radius: 0.5rem; + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); + z-index: 1100; + display: flex; + align-items: center; + max-width: 400px; + display: none; + } + .notification.success { + background-color: #10B981; + } + .notification.error { + background-color: #EF4444; + } \ No newline at end of file diff --git a/jweb/1a/assets/db.php b/jweb/1a/assets/db.php new file mode 100644 index 0000000000000000000000000000000000000000..64f25c312ea31bc85e3ce23b82dea08528cf0555 --- /dev/null +++ b/jweb/1a/assets/db.php @@ -0,0 +1,13 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + die("DB Connection failed: " . $e->getMessage()); +} +?> diff --git a/jweb/1a/assets/models/Subscription.php b/jweb/1a/assets/models/Subscription.php new file mode 100644 index 0000000000000000000000000000000000000000..651ba214ea7e053854aeb9dfa2e7c4fcb141a0b1 --- /dev/null +++ b/jweb/1a/assets/models/Subscription.php @@ -0,0 +1,68 @@ +conn = $db; + } + + public function subscribe() { + $query = "INSERT INTO " . $this->table_name . " + SET email=:email, notification_opt_in=:notification_opt_in, is_active=1"; + + $stmt = $this->conn->prepare($query); + + $this->email = htmlspecialchars(strip_tags($this->email)); + + $stmt->bindParam(":email", $this->email); + $stmt->bindParam(":notification_opt_in", $this->notification_opt_in); + + if ($stmt->execute()) { + return true; + } + return false; + } + + public function unsubscribe($email) { + $query = "UPDATE " . $this->table_name . " + SET is_active=0, unsubscribed_at=NOW() + WHERE email=:email"; + + $stmt = $this->conn->prepare($query); + + $email = htmlspecialchars(strip_tags($email)); + $stmt->bindParam(":email", $email); + + if ($stmt->execute()) { + return true; + } + return false; + } + + public function checkSubscription($email) { + $query = "SELECT is_active FROM " . $this->table_name . " + WHERE email = :email"; + + $stmt = $this->conn->prepare($query); + + $email = htmlspecialchars(strip_tags($email)); + $stmt->bindParam(":email", $email); + + $stmt->execute(); + + if ($stmt->rowCount() > 0) { + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row['is_active']; + } + return false; + } +} +?> \ No newline at end of file diff --git a/jweb/1a/assets/sample_data.php b/jweb/1a/assets/sample_data.php new file mode 100644 index 0000000000000000000000000000000000000000..3707f53029cbf116594b53faa1d6087f0ad9c1ea --- /dev/null +++ b/jweb/1a/assets/sample_data.php @@ -0,0 +1,120 @@ + 'Nissan GT-R', + 'model_year' => 2023, + 'location' => 'Tokyo', + 'description' => 'The legendary Godzilla with 565HP twin-turbo V6 engine.', + 'price' => 112000.00, + 'image_url' => 'http://static.photos/automotive/640x360/1', + 'status' => 'new' + ], + [ + 'name' => 'Toyota Supra', + 'model_year' => 2023, + 'location' => 'Osaka', + 'description' => 'The iconic sports car returns with 382HP turbocharged inline-6.', + 'price' => 52000.00, + 'image_url' => 'http://static.photos/automotive/640x360/2', + 'status' => 'used' + ], + [ + 'name' => 'Honda NSX', + 'model_year' => 2023, + 'location' => 'Yokohama', + 'description' => '573HP hybrid supercar with cutting-edge technology.', + 'price' => 169000.00, + 'image_url' => 'http://static.photos/automotive/640x360/3', + 'status' => 'limited' + ] + ]; + + $stmt = $pdo->prepare("INSERT INTO vehicles (name, model_year, location, description, price, image_url, status) VALUES (?, ?, ?, ?, ?, ?, ?)"); + + foreach ($vehicles as $vehicle) { + $stmt->execute([ + $vehicle['name'], + $vehicle['model_year'], + $vehicle['location'], + $vehicle['description'], + $vehicle['price'], + $vehicle['image_url'], + $vehicle['status'] + ]); + } + + // Insert sample testimonials + $testimonials = [ + [ + 'client_name' => 'Michael R.', + 'client_photo' => 'http://static.photos/people/200x200/1', + 'rating' => 5, + 'content' => '"The team at Japanese Motors made importing my dream GT-R from Japan seamless. The car arrived in perfect condition, exactly as described. Their attention to detail is unmatched."', + 'helpful_count' => 32 + ], + [ + 'client_name' => 'Sarah K.', + 'client_photo' => 'http://static.photos/people/200x200/2', + 'rating' => 5, + 'content' => '"I was hesitant about importing a car internationally, but Japanese Motors guided me through every step. My Supra is flawless and I saved thousands compared to local dealers."', + 'helpful_count' => 28 + ] + ]; + + $stmt = $pdo->prepare("INSERT INTO testimonials (client_name, client_photo, rating, content, helpful_count) VALUES (?, ?, ?, ?, ?)"); + + foreach ($testimonials as $testimonial) { + $stmt->execute([ + $testimonial['client_name'], + $testimonial['client_photo'], + $testimonial['rating'], + $testimonial['content'], + $testimonial['helpful_count'] + ]); + } + + // Insert sample notifications + $notifications = [ + [ + 'title' => 'Price drop on Nissan GT-R', + 'message' => 'Special discount available this week only', + 'type' => 'price_drop', + 'icon' => 'dollar-sign' + ], + [ + 'title' => 'New arrivals from Japan', + 'message' => 'Fresh inventory just arrived from our Tokyo warehouse', + 'type' => 'new_arrival', + 'icon' => 'truck' + ], + [ + 'title' => 'Limited time offer ending soon', + 'message' => 'Don\'t miss our exclusive financing rates', + 'type' => 'limited_offer', + 'icon' => 'clock' + ] + ]; + + $stmt = $pdo->prepare("INSERT INTO notifications (title, message, type, icon) VALUES (?, ?, ?, ?)"); + + foreach ($notifications as $notification) { + $stmt->execute([ + $notification['title'], + $notification['message'], + $notification['type'], + $notification['icon'] + ]); + } + + echo "Sample data inserted successfully!"; +} catch(PDOException $e) { + echo "Error inserting sample data: " . $e->getMessage(); +} +?> \ No newline at end of file diff --git a/jweb/1a/contact.html b/jweb/1a/contact.html new file mode 100644 index 0000000000000000000000000000000000000000..c56915c381629d2019aa1ca45b1381515ec45718 --- /dev/null +++ b/jweb/1a/contact.html @@ -0,0 +1,430 @@ + + + + + + Contact - Japanese Motors + + + + + + + + + + + + + +
+
+
+
+
+

Contact Us

+

Get in touch with our team for any inquiries about our vehicles or services

+
+
+
+ + +
+
+
+

How Would You Like to Reach Us?

+
+

Choose your preferred method of communication

+
+ +
+ +
+
+ +
+

WhatsApp

+

Chat with us directly on WhatsApp for quick responses

+ + Start Chat + +
+ + +
+
+ +
+

Live Chat

+

Chat with our sales team in real-time

+ +
+ + +
+
+ +
+

Email

+

Send us an email and we'll respond within 24 hours

+ + Send Email + +
+ + +
+
+ +
+

Phone

+

Call us directly to speak with our team

+ + Call Now + +
+
+
+
+ + +
+
+
+ +
+

Send Us a Message

+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+

Contact Information

+
+
+
+ +
+
+

Address

+

123 Automotive Way, Chuo-ku
Tokyo, Japan 104-0061

+
+
+ +
+
+ +
+
+

Phone

+

+254 756 709 823

+

Mon-Fri: 9:00 AM - 7:00 PM

+

Sat: 10:00 AM - 5:00 PM

+
+
+ +
+
+ +
+
+

Email

+

info@japanesemotors.com

+

sales@japanesemotors.com

+

service@japanesemotors.com

+
+
+ +
+
+ +
+
+

Business Hours

+

Monday - Friday: 9:00 AM - 7:00 PM

+

Saturday: 10:00 AM - 5:00 PM

+

Sunday: Closed

+
+
+
+ +
+

Follow Us

+ +
+
+
+
+
+ + +
+
+
+

Frequently Asked Questions

+
+

Quick answers to common questions about contacting us

+
+ +
+
+

What's the best way to reach you quickly?

+

For the fastest response, use our WhatsApp chat or call us directly during business hours. We typically respond to WhatsApp messages within minutes.

+
+ +
+

Do you respond to inquiries after hours?

+

Yes! We monitor WhatsApp and email after business hours and will respond to urgent inquiries. For non-urgent matters, we'll respond the next business day.

+
+ +
+

Can I schedule a test drive online?

+

Absolutely! Use our contact form and select "Schedule Test Drive" as your subject. We'll contact you within 24 hours to confirm your appointment.

+
+ +
+

Do you ship vehicles internationally?

+

Yes, we ship vehicles worldwide. Contact our sales team via email or WhatsApp to discuss shipping options to your location.

+
+
+
+
+ + +
+
+
+

Visit Our Showroom

+
+

Come see our collection of Japanese performance vehicles in person

+
+ +
+
+ +
+
+

Japanese Motors Showroom

+

123 Automotive Way, Chuo-ku, Tokyo, Japan 104-0061

+ +
+
+
+
+ + + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/jweb/1a/gallery.html b/jweb/1a/gallery.html new file mode 100644 index 0000000000000000000000000000000000000000..ca3bba0a9ade16f3ad28af09a077b4776d41c570 --- /dev/null +++ b/jweb/1a/gallery.html @@ -0,0 +1,926 @@ + + + + + + Inventory - Japanese Motors + + + + + + + + + + + + + +
+
+
+
+
+

Our Premium Inventory

+

Explore our exclusive collection of Japanese performance vehicles

+
+
+
+ + +
+
+

Select Motor Type

+
+ + + + + +
+
+
+ + +
+
+
+

Find Your Perfect Vehicle

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
+

Available Vehicles

+
+ Sort by: + +
+
+ +
+ +
+ + +
+ +
+
+
+ + +
+
+
+

Virtual Showroom

+
+

Experience our vehicles in immersive 360° virtual reality

+
+ +
+
+ +
+
+

Explore From Anywhere

+

Our virtual showroom allows you to examine every detail of our vehicles as if you were standing right next to them. Rotate, zoom, and explore interiors and exteriors with our cutting-edge VR technology.

+
    +
  • + + 360° exterior and interior views +
  • +
  • + + Interactive features and specifications +
  • +
  • + + Available on desktop and mobile +
  • +
  • + + VR headset compatible for full immersion +
  • +
+ + Enter VR Showroom + +
+
+
+
+ + +
+
+

Can't Find What You're Looking For?

+

Contact our sales team for assistance with special requests or custom orders.

+ +
+
+ + +
+
+
+

Get Notified About New Arrivals

+

Be the first to know when we add new vehicles to our inventory. Sign up for our exclusive newsletter.

+
+ + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jweb/1a/services.html b/jweb/1a/services.html new file mode 100644 index 0000000000000000000000000000000000000000..7e254f3baed0f92a85bb983f8683ec11c9235056 --- /dev/null +++ b/jweb/1a/services.html @@ -0,0 +1,855 @@ + + + + + + Services - Japanese Motors + + + + + + + + + + + + + +
+
+
+
+
+

Premium Services

+

Expert care for your Japanese performance vehicle

+
+
+
+ + +
+
+

Our Services

+

From routine maintenance to performance upgrades, our certified technicians provide exceptional service for your Japanese vehicle.

+ +
+ + + + +
+ + +
+
+
+
+ +
+

Oil Change Service

+

Complete oil and filter change using premium synthetic oils specifically formulated for Japanese performance engines.

+
    +
  • + + Full synthetic oil change +
  • +
  • + + OEM oil filter replacement +
  • +
  • + + Multi-point inspection +
  • +
+
+ From $89 + +
+
+ +
+
+ +
+

Scheduled Maintenance

+

Factory-recommended service intervals to keep your vehicle performing at its best and maintain warranty coverage.

+
    +
  • + + 30,000/60,000/90,000 mile services +
  • +
  • + + Fluid changes and inspections +
  • +
  • + + Comprehensive vehicle health report +
  • +
+
+ From $249 + +
+
+ +
+
+ +
+

Brake Service

+

Complete brake inspection and service to ensure your safety and optimal stopping performance.

+
    +
  • + + Brake pad replacement +
  • +
  • + + Rotor resurfacing or replacement +
  • +
  • + + Brake fluid flush +
  • +
+
+ From $199 + +
+
+
+
+ + +
+
+
+
+ +
+

Engine Repair

+

Expert diagnosis and repair of engine issues, from minor tune-ups to major overhauls.

+
    +
  • + + Diagnostic testing +
  • +
  • + + Timing belt replacement +
  • +
  • + + Engine rebuilds +
  • +
+
+ From $299 + +
+
+ +
+
+ +
+

Transmission Service

+

Specialized service for manual, automatic, and CVT transmissions found in Japanese performance vehicles.

+
    +
  • + + Transmission fluid change +
  • +
  • + + Clutch replacement +
  • +
  • + + Transmission rebuild +
  • +
+
+ From $349 + +
+
+ +
+
+ +
+

Electrical Systems

+

Diagnosis and repair of complex electrical systems in modern Japanese vehicles.

+
    +
  • + + Battery replacement +
  • +
  • + + Alternator and starter service +
  • +
  • + + Wiring diagnostics +
  • +
+
+ From $159 + +
+
+
+
+ + +
+
+
+
+ +
+

ECU Tuning

+

Custom engine tuning to unlock hidden performance while maintaining reliability.

+
    +
  • + + Dyno tuning sessions +
  • +
  • + + Custom ECU mapping +
  • +
  • + + Performance optimization +
  • +
+
+ From $499 + +
+
+ +
+
+ +
+

Turbocharger Services

+

Installation, maintenance, and upgrade services for turbocharged vehicles.

+
    +
  • + + Turbo installation +
  • +
  • + + Turbo rebuilds +
  • +
  • + + Intercooler upgrades +
  • +
+
+ From $799 + +
+
+ +
+
+ +
+

Suspension Upgrades

+

Performance suspension systems to improve handling and driving dynamics.

+
    +
  • + + Coilover installation +
  • +
  • + + Alignment and corner balancing +
  • +
  • + + Handling packages +
  • +
+
+ From $599 + +
+
+
+
+ + +
+
+
+
+ +
+

Premium Detailing

+

Showroom-quality detailing to restore and protect your vehicle's appearance.

+
    +
  • + + Paint correction +
  • +
  • + + Ceramic coating +
  • +
  • + + Interior deep cleaning +
  • +
+
+ From $299 + +
+
+ +
+
+ +
+

Paint Protection

+

Advanced protection solutions to keep your vehicle looking new for years to come.

+
    +
  • + + Paint protection film +
  • +
  • + + Ceramic coating pro +
  • +
  • + + Window tinting +
  • +
+
+ From $999 + +
+
+ +
+
+ +
+

Interior Renewal

+

Complete interior restoration and protection services.

+
    +
  • + + Leather conditioning +
  • +
  • + + Fabric protection +
  • +
  • + + Odor elimination +
  • +
+
+ From $199 + +
+
+
+
+
+
+ + + + + +
+ +

+
+ + +
+
+
+

Why Choose Japanese Motors

+
+
+ +
+
+
+ +
+

Certified Technicians

+

Our technicians are factory-trained and certified to work on Japanese performance vehicles.

+
+ +
+
+ +
+

Genuine Parts

+

We use only genuine OEM parts or premium aftermarket equivalents for all repairs.

+
+ +
+
+ +
+

Quick Turnaround

+

Most services completed same-day with our efficient workflow and ample resources.

+
+ +
+
+ +
+

Fair Pricing

+

Competitive pricing with no hidden fees and upfront estimates for all work.

+
+
+
+
+ + +
+
+
+

Frequently Asked Questions

+
+
+ +
+
+ +
+

We recommend following the manufacturer's scheduled maintenance intervals, typically every 5,000-7,500 miles for oil changes and every 30,000 miles for major services. Performance vehicles driven hard may require more frequent servicing.

+
+
+ +
+ +
+

Yes, all our services come with a warranty. Parts and labor are typically covered for 12 months/12,000 miles. Specific warranty terms may vary by service type - details are provided with every estimate.

+
+
+ +
+ +
+

While we prefer to source parts ourselves to ensure quality and compatibility, we can install customer-provided parts. However, the warranty would only cover labor, not the parts themselves.

+
+
+ +
+ +
+

Most maintenance services can be completed within 2-3 hours. Larger repairs may take a day or more depending on complexity. We offer loaner vehicles and shuttle service for extended repairs.

+
+
+ +
+ +
+

Yes, we specialize in all Japanese performance brands including Toyota, Nissan, Honda, Mazda, Subaru, Mitsubishi, Lexus, Acura, and Infiniti. Our technicians have specific expertise with performance models from these manufacturers.

+
+
+
+
+
+ + +
+
+

Ready to Service Your Vehicle?

+

Schedule your appointment today and experience the Japanese Motors difference.

+ +
+
+ + + + + + + \ No newline at end of file diff --git a/jweb/ac1/assets/css/st.css b/jweb/ac1/assets/css/st.css new file mode 100644 index 0000000000000000000000000000000000000000..0b43fa6279bba1a08b6f4c8b8ea11b5b330dd34f --- /dev/null +++ b/jweb/ac1/assets/css/st.css @@ -0,0 +1,191 @@ + * { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + } + + body { + background: linear-gradient(135deg, #d62828, #f77f00, #fcbf49); + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + padding: 20px; + } + + .container { + width: 100%; + max-width: 480px; + background-color: linear-gradient(135deg, #f75c5c, #945e25, #c79b42); + border-radius: 16px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); + overflow: hidden; + } + + .header { + background: linear-gradient(135deg, #d62828, #f77f00, #fcbf49); + color: white; + padding: 30px; + text-align: center; + } + + .logo { + text-align: center; + margin-bottom: 30px; + } + + .logo h1 { + font-size: 28px; + font-weight: 700; + color: #152536; + } + + .subtitle { + font-size: 16px; + opacity: 0.9; + } + + .form-container { + padding: 30px; + background: linear-gradient(135deg, #d62828, #f77f00, #fcbf49); + } + + .form-group { + margin-bottom: 20px; + } + + label { + display: block; + font-size: 14px; + color: #f8f9fa; + margin-bottom: 8px; + font-weight: 500; + } + + input, select { + width: 100%; + padding: 14px 16px; + border: 1px solid #e1e3e6; + border-radius: 8px; + background: linear-gradient(135deg, #f7ae60, #f3b944); + font-size: 15px; + color: #f4f5f7; + transition: all 0.3s; + } + + input:focus, select:focus { + outline: none; + border-color: #4f46e5; + box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2); + } + + .input-icon { + position: relative; + } + + .input-icon i { + position: absolute; + left: 15px; + top: 50%; + transform: translateY(-50%); + color: #eeeff0; + } + + .input-icon input { + padding-left: 45px; + } + + .password-row { + display: flex; + gap: 15px; + } + + .password-row .form-group { + flex: 1; + } + + .create-account-btn { + width: 100%; + background: linear-gradient(135deg, #f77f00, #fcbf49); + color: black; + border: none; + border-radius: 8px; + padding: 16px; + font-size: 16px; + font-weight: 600; + margin-top: 15px; + cursor: pointer; + transition: all 0.3s; + box-shadow: 0 4px 6px rgba(79, 70, 229, 0.2); + } + + .create-account-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(79, 70, 229, 0.25); + } + + .create-account-btn:active { + transform: translateY(0); + } + + .signin-link { + text-align: center; + margin-top: 25px; + font-size: 15px; + color: #f3f4f5; + } + + .signin-link a { + color: #4f46e5; + text-decoration: none; + font-weight: 600; + transition: all 0.2s; + } + + .signin-link a:hover { + text-decoration: underline; + } + + .footer { + position: fixed; + font-size: 13px; + color: #f6f8fa; + } + + .country-flag { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + color: #94a3b8; + pointer-events: none; + } + + .password-toggle { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + color: #94a3b8; + cursor: pointer; + } + + @media (max-width: 520px) { + .password-row { + flex-direction: column; + gap: 20px; + } + + .container { + border-radius: 12px; + } + + .header { + padding: 25px; + } + + .form-container { + padding: 25px; + } + } \ No newline at end of file diff --git a/jweb/ac1/assets/css/sty.css b/jweb/ac1/assets/css/sty.css new file mode 100644 index 0000000000000000000000000000000000000000..113baa090ccadca313c055e11610cb6156b78b62 --- /dev/null +++ b/jweb/ac1/assets/css/sty.css @@ -0,0 +1,759 @@ + :root { + --sidebar-bg: #1e293b; + --main-bg: #4b5563; + --text-white: #ffffff; + --text-gray: #d1d5db; + --banner-gradient-start: #a855f7; + --banner-gradient-end: #ec4899; + --green-check: #22c55e; + --gray-check: #9ca3af; + --yellow-button: #eab308; + --purple-user: #a855f7; + --premium-bg: #eab308; + --link-blue: #60a5fa; + --award-yellow: #fde047; + --transition-speed: 0.3s; + --card-bg: rgba(255, 255, 255, 0.05); + --primary: #4361ee; + --secondary: #3a0ca3; + --gray: #adb5bd; + --warning: #ffd166; + } + + * { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + } + + body { + background-color: var(--main-bg); + color: var(--text-white); + display: flex; + min-height: 100vh; + overflow-x: hidden; + } + + .sidebar { + width: 220px; + background-color: var(--sidebar-bg); + padding: 20px 15px; + position: fixed; + height: 100vh; + overflow-y: auto; + z-index: 1000; + display: flex; + flex-direction: column; + transition: transform var(--transition-speed); + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2); + } + + .sidebar.collapsed { + transform: translateX(-100%); + } + + .sidebar .logo { + display: flex; + align-items: center; + margin-bottom: 30px; + padding-bottom: 10px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + } + + .sidebar .logo img { + width: 40px; + height: 40px; + border-radius: 50%; + margin-right: 10px; + transition: transform 0.3s ease; + } + + .sidebar .logo img:hover { + transform: scale(1.1); + } + + .sidebar .logo-text { + font-size: 18px; + font-weight: bold; + color: var(--yellow-button); + } + + .sidebar .logo-subtext { + font-size: 12px; + color: var(--text-gray); + } + + .sidebar ul { + list-style: none; + flex: 1; + } + + .sidebar ul li { + margin-bottom: 10px; + } + + .sidebar ul li a { + display: flex; + align-items: center; + color: var(--text-gray); + text-decoration: none; + padding: 8px 10px; + border-radius: 6px; + transition: background var(--transition-speed), transform var(--transition-speed); + } + + .sidebar ul li a:hover { + background-color: rgba(255, 255, 255, 0.1); + color: var(--text-white); + transform: translateX(5px); + } + + .sidebar ul li a i { + margin-right: 10px; + width: 20px; + text-align: center; + transition: color var(--transition-speed); + } + + .sidebar ul li a:hover i { + color: var(--yellow-button); + } + + .sidebar .user-info { + display: flex; + align-items: center; + padding: 10px; + border-top: 1px solid rgba(255, 255, 255, 0.1); + margin-top: auto; + background: rgba(255, 255, 255, 0.03); + border-radius: 8px; + } + + .sidebar .user-avatar { + background-color: var(--purple-user); + color: var(--text-white); + width: 30px; + height: 30px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + margin-right: 10px; + box-shadow: 0 0 5px rgba(168, 85, 247, 0.5); + } + + .sidebar .user-text { + font-size: 14px; + } + + .sidebar .user-status { + font-size: 12px; + color: var(--text-gray); + } + + .main-content { + flex: 1; + margin-left: 220px; + padding: 20px; + transition: margin-left var(--transition-speed); + background: rgba(75, 85, 99, 0.9); + backdrop-filter: blur(5px); + } + + .main-content.expanded { + margin-left: 0; + } + + .topbar { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 40px; + padding: 10px 20px; + background: rgba(30, 41, 59, 0.8); + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + } + + .topbar .logo { + display: flex; + align-items: center; + } + + .topbar .logo img { + width: 30px; + height: 30px; + margin-right: 10px; + } + + .topbar .logo-text { + font-size: 18px; + font-weight: bold; + color: var(--yellow-button); + } + + .topbar nav { + display: flex; + gap: 20px; + } + + .topbar nav a { + color: var(--link-blue); + text-decoration: none; + font-weight: 500; + transition: color var(--transition-speed); + } + + .topbar nav a:hover { + color: var(--primary); + } + + .topbar .right { + display: flex; + align-items: center; + gap: 15px; + } + + .topbar .toggle-btn { + background: linear-gradient(90deg, var(--primary), var(--secondary)); + color: var(--text-white); + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + font-size: 18px; + cursor: pointer; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + transition: transform var(--transition-speed), background var(--transition-speed); + } + + .topbar .toggle-btn:hover { + transform: scale(1.1); + background: linear-gradient(90deg, #2a4494, #270a66); + } + + .topbar .notification { + position: relative; + } + + .topbar .notification i { + color: var(--text-gray); + font-size: 18px; + transition: color var(--transition-speed); + } + + .topbar .notification i:hover { + color: var(--warning); + } + + .topbar .notification .dot { + position: absolute; + top: -2px; + right: -2px; + width: 8px; + height: 8px; + background-color: #ef4444; + border-radius: 50%; + } + + .topbar .user { + display: flex; + align-items: center; + gap: 5px; + } + + .topbar .user-avatar { + background-color: var(--premium-bg); + color: #000; + width: 25px; + height: 25px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + font-size: 12px; + box-shadow: 0 0 5px rgba(234, 179, 8, 0.5); + } + + .topbar .user-name { + font-weight: 500; + color: var(--text-white); + } + + .topbar .premium { + background-color: var(--premium-bg); + color: #000; + padding: 2px 6px; + border-radius: 4px; + font-size: 12px; + font-weight: bold; + } + + .topbar .logout i { + color: var(--text-gray); + font-size: 18px; + transition: color var(--transition-speed); + } + + .topbar .logout i:hover { + color: #ef4444; + } + + .banner { + max-width: 400px; + margin: 0 auto 40px; + background: linear-gradient(135deg, var(--banner-gradient-start), var(--banner-gradient-end)); + border-radius: 16px; + padding: 20px; + text-align: center; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + animation: pulse 2s infinite; + } + + @keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } + } + + .banner .title { + font-size: 16px; + margin-bottom: 15px; + color: var(--text-white); + font-weight: bold; + } + + .banner p { + font-size: 14px; + line-height: 1.4; + margin-bottom: 15px; + color: var(--award-yellow); + } + + .banner .footer { + font-size: 12px; + color: rgba(255, 255, 255, 0.8); + font-style: italic; + } + + .packages-section { + text-align: center; + } + + .packages-section h1 { + font-size: 24px; + margin-bottom: 10px; + color: var(--yellow-button); + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + } + + .packages-section .subtitle { + font-size: 14px; + color: var(--text-gray); + margin-bottom: 30px; + } + + .packages { + display: flex; + justify-content: center; + gap: 20px; + } + + .package-card { + background-color: #6b7280; + border-radius: 12px; + padding: 20px; + width: 200px; + text-align: left; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + transition: transform var(--transition-speed), box-shadow var(--transition-speed); + } + + .package-card:hover { + transform: translateY(-5px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); + } + + .package-header { + display: flex; + align-items: center; + margin-bottom: 15px; + } + + .package-header i { + color: var(--text-gray); + margin-right: 10px; + font-size: 18px; + } + + .package-header .name { + font-size: 18px; + font-weight: bold; + color: var(--yellow-button); + } + + .package-features { + list-style: none; + margin-bottom: 20px; + } + + .package-features li { + display: flex; + align-items: center; + margin-bottom: 8px; + color: var(--text-gray); + font-size: 14px; + transition: color var(--transition-speed); + } + + .package-features li:hover { + color: var(--text-white); + } + + .package-features li i { + margin-right: 10px; + } + + .package-features .checked i { + color: var(--green-check); + } + + .package-features .unchecked i { + color: var(--gray-check); + } + + .package-award { + font-size: 16px; + font-weight: bold; + color: var(--award-yellow); + margin-bottom: 10px; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + } + + .package-button { + background-color: var(--yellow-button); + color: #000; + padding: 8px 16px; + border: none; + border-radius: 6px; + font-weight: bold; + cursor: pointer; + width: 100%; + transition: background var(--transition-speed), transform var(--transition-speed); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + } + + .package-button:hover { + background-color: #d97706; + transform: scale(1.05); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + } + + .welcome { + display: flex; + align-items: center; + margin-bottom: 20px; + padding: 15px; + background: rgba(255, 255, 255, 0.05); + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + } + + .user-avatar { + width: 50px; + height: 50px; + border-radius: 50%; + background-color: var(--primary); + display: flex; + align-items: center; + justify-content: center; + color: var(--text-white); + font-size: 20px; + margin-right: 15px; + box-shadow: 0 0 10px rgba(67, 97, 238, 0.5); + } + + .welcome-text h2 { + font-size: 18px; + margin-bottom: 5px; + color: var(--text-white); + } + + .welcome-text p { + color: var(--gray); + font-size: 14px; + } + + .balance-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .card { + background-color: #6b7280; + border-radius: 12px; + padding: 20px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + transition: transform var(--transition-speed); + } + + .card:hover { + transform: translateY(-5px); + } + + .card-title { + font-size: 16px; + color: var(--gray); + margin-bottom: 15px; + } + + .meta-balance { + display: flex; + justify-content: space-between; + align-items: center; + } + + .balance-amount { + font-size: 28px; + font-weight: 700; + color: var(--primary); + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + } + + .balance-actions { + display: flex; + gap: 10px; + } + + .btn { + padding: 8px 15px; + border-radius: 5px; + border: none; + font-weight: 600; + cursor: pointer; + transition: all var(--transition-speed); + } + + .btn-primary { + background-color: var(--primary); + color: var(--text-white); + } + + .btn-primary:hover { + background-color: var(--secondary); + transform: scale(1.05); + } + + .btn-outline { + background-color: transparent; + border: 1px solid var(--primary); + color: var(--primary); + } + + .btn-outline:hover { + background-color: var(--primary); + color: var(--text-white); + transform: scale(1.05); + } + + .stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .stat-card { + background-color: #6b7280; + border-radius: 12px; + padding: 20px; + text-align: center; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + transition: transform var(--transition-speed); + } + + .stat-card:hover { + transform: translateY(-5px); + } + + .stat-value { + font-size: 24px; + font-weight: 700; + margin: 10px 0; + color: var(--primary); + } + + .stat-label { + color: var(--gray); + font-size: 14px; + } + + .dashboard-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 20px; + margin-bottom: 30px; + } + + .dashboard-card { + background-color: #6b7280; + border-radius: 12px; + padding: 20px; + color: var(--text-white); + text-align: center; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + transition: transform var(--transition-speed); + } + + .dashboard-card:hover { + transform: translateY(-5px); + } + + .dashboard-card h2 { + margin: 10px 0; + font-size: 22px; + color: var(--primary); + } + + .dashboard-card p { + font-size: 14px; + color: var(--gray); + } + + .dashboard-btn { + background: var(--yellow-button); + color: #000; + padding: 8px 18px; + border: none; + border-radius: 6px; + cursor: pointer; + font-weight: bold; + margin-top: 10px; + transition: background var(--transition-speed), transform var(--transition-speed); + } + + .dashboard-btn:hover { + background: #d97706; + transform: scale(1.05); + } + + .modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 2000; + justify-content: center; + align-items: center; + } + + .modal-content { + background-color: var(--sidebar-bg); + padding: 30px; + border-radius: 10px; + width: 90%; + max-width: 500px; + color: var(--text-white); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + animation: fadeIn 0.3s ease; + } + + @keyframes fadeIn { + from { opacity: 0; transform: scale(0.9); } + to { opacity: 1; transform: scale(1); } + } + + .modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + padding-bottom: 10px; + } + + .close { + font-size: 24px; + cursor: pointer; + color: var(--gray); + transition: color var(--transition-speed); + } + + .close:hover { + color: #ef4444; + } + + .form-group { + margin-bottom: 20px; + } + + .form-group label { + display: block; + margin-bottom: 5px; + font-weight: 600; + color: var(--text-white); + } + + .form-group input, .form-group select { + width: 100%; + padding: 10px; + border: 1px solid var(--gray); + border-radius: 5px; + background: rgba(255, 255, 255, 0.1); + color: var(--text-white); + transition: border-color var(--transition-speed); + } + + .form-group input:focus, .form-group select:focus { + border-color: var(--primary); + outline: none; + } + + @media (max-width: 768px) { + .sidebar { + transform: translateX(-100%); + } + + .sidebar.active { + transform: translateX(0); + } + + .main-content { + margin-left: 0; + } + + .main-content.expanded { + margin-left: 0; + } + + .packages { + flex-direction: column; + align-items: center; + } + + .balance-cards, .stats, .dashboard-cards { + grid-template-columns: 1fr; + } + + .balance-actions { + flex-direction: column; + } + + .topbar { + flex-direction: column; + gap: 15px; + } + + .topbar nav { + flex-direction: column; + gap: 10px; + } + } \ No newline at end of file diff --git a/jweb/ac1/assets/css/style.css b/jweb/ac1/assets/css/style.css new file mode 100644 index 0000000000000000000000000000000000000000..ea2c6cb7cb00e95e69e42c4aec5424834778deb5 --- /dev/null +++ b/jweb/ac1/assets/css/style.css @@ -0,0 +1,89 @@ +/* Background */ +body { + margin: 0; + font-family: Arial, sans-serif; + background: linear-gradient(135deg, #c0392b, #e67e22); + height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +/* Form Container */ +.container { + display: flex; + justify-content: center; + align-items: center; +} + +.form-box { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(8px); + padding: 30px; + border-radius: 12px; + width: 350px; + box-shadow: 0 8px 25px rgba(0,0,0,0.2); + text-align: center; + color: white; +} + +/* Branding */ +.brand { + font-size: 26px; + font-weight: bold; + margin-bottom: 5px; +} + +.subtitle { + font-size: 14px; + margin-bottom: 20px; + color: #eee; +} + +/* Input Fields */ +.input-group { + text-align: left; + margin-bottom: 15px; +} + +.input-group label { + font-size: 14px; + display: block; + margin-bottom: 5px; +} + +.input-group input { + width: 100%; + padding: 10px; + border: none; + border-radius: 6px; + outline: none; +} + +/* Button */ +.btn { + background: #8BC34A; + color: #fff; + border: none; + padding: 12px; + width: 100%; + border-radius: 6px; + cursor: pointer; + font-size: 16px; + transition: 0.3s; +} + +.btn:hover { + background: #7CB342; +} + +/* Login Text */ +.login-text { + margin-top: 15px; + font-size: 14px; +} + +.login-text a { + color: #fff; + text-decoration: underline; +} diff --git a/jweb/ac1/assets/css/styles.css b/jweb/ac1/assets/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..ed149a1daa8736927ec2043b38ef1967494884ea --- /dev/null +++ b/jweb/ac1/assets/css/styles.css @@ -0,0 +1,195 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + } + + body { + background: linear-gradient(135deg, #6e8efb, #a777e3); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + padding: 20px; + } + + .container { + background: rgba(255, 255, 255, 0.95); + border-radius: 15px; + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); + width: 100%; + max-width: 550px; + overflow: hidden; + position: relative; + } + + .form-container { + padding: 30px; + transition: transform 0.6s ease-in-out; + } + + h2 { + text-align: center; + color: #333; + margin-bottom: 25px; + font-size: 28px; + } + + .form-group { + margin-bottom: 20px; + position: relative; + } + + .form-group { + margin-bottom: 15px; + } + + .input-container { + position: relative; + display: flex; + align-items: center; + } + + .input-container i { + position: absolute; + left: 12px; + color: #ccc; + font-size: 14px; + } + + .input-container input, + .input-container select { + width: 100%; + padding: 10px 12px 10px 35px; /* left padding for icon */ + border: 1px solid #ddd; + border-radius: 6px; + outline: none; + background: rgba(255, 255, 255, 0.1); + color: #fff; + } + + .password-container .toggle-password { + position: absolute; + right: 12px; + cursor: pointer; + color: #ccc; + } + + label { + display: block; + margin-bottom: 8px; + font-weight: 600; + color: #444; + } + + input, select { + width: 100%; + padding: 14px; + border: 2px solid #ddd; + border-radius: 8px; + font-size: 16px; + transition: border 0.3s; + } + + input:focus, select:focus { + border-color: #6e8efb; + outline: none; + } + + .password-row { + display: flex; + gap: 15px; + } + + .password-column { + flex: 1; + } + + .password-container { + position: relative; + } + + .toggle-password { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); + cursor: pointer; + color: #777; + } + + .error-message { + color: #e74c3c; + font-size: 14px; + margin-top: 5px; + min-height: 20px; + } + + .btn { + width: 100%; + padding: 14px; + background: linear-gradient(135deg, #6e8efb, #a777e3); + border: none; + border-radius: 8px; + color: white; + font-size: 18px; + font-weight: 600; + cursor: pointer; + transition: all 0.3s; + } + + .btn:hover { + background: linear-gradient(135deg, #5d7ce0, #9666d3); + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + } + + .switch-form { + text-align: center; + margin-top: 20px; + color: #555; + } + + .switch-form a { + color: #6e8efb; + text-decoration: none; + font-weight: 600; + cursor: pointer; + transition: color 0.3s; + } + + .switch-form a:hover { + color: #a777e3; + text-decoration: underline; + } + + .success-message { + background: linear-gradient(135deg, #6e8efb, #a777e3); + color: white; + padding: 20px; + text-align: center; + border-radius: 10px; + margin-bottom: 20px; + display: none; + } + + .success-message i { + font-size: 48px; + margin-bottom: 10px; + } + + @media (max-width: 576px) { + .container { + border-radius: 10px; + } + + .form-container { + padding: 20px; + } + + .password-row { + flex-direction: column; + gap: 0; + } + } \ No newline at end of file diff --git a/jweb/ac1/assets/database/db.sql b/jweb/ac1/assets/database/db.sql new file mode 100644 index 0000000000000000000000000000000000000000..c6dac65b4c4ebe08fff65adbb9206c004373d686 --- /dev/null +++ b/jweb/ac1/assets/database/db.sql @@ -0,0 +1,86 @@ +CREATE TABLE `users` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_type` enum('marketer','agent','admin') DEFAULT 'marketer', + `referred_by` int DEFAULT NULL, + `username` varchar(50) NOT NULL, + `email` varchar(100) NOT NULL, + `country` varchar(2) NOT NULL, + `phone_number` varchar(20) NOT NULL, + `tier` enum('Basic','Premium') DEFAULT 'Basic', + `current_package_id` int DEFAULT NULL, + `package_start_date` datetime DEFAULT NULL, + `package_end_date` datetime DEFAULT NULL, + `package` enum('None','NOVA','SUPERIOR','GOLD') DEFAULT 'None', + `balance` decimal(10,2) DEFAULT '0.00', + `verification_token` varchar(100) DEFAULT NULL, + `reset_token` varchar(100) DEFAULT NULL, + `reset_token_expiry` datetime DEFAULT NULL, + `total_invested` decimal(15,2) DEFAULT '0.00', + `total_earnings` decimal(15,2) DEFAULT '0.00', + `total_deposits` decimal(10,2) DEFAULT '0.00', + `total_withdrawals` decimal(10,2) DEFAULT '0.00', + `rewards` decimal(10,2) DEFAULT '0.00', + `meta_earnings` decimal(10,2) DEFAULT '0.00', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `last_login` timestamp NULL DEFAULT NULL, + `pin_hash` varchar(255) DEFAULT '', + `password_hash` varchar(255) NOT NULL, + `currency` varchar(3) DEFAULT 'KES', + `exchange_rate` decimal(10,4) DEFAULT '1.0000', + `referral_code` varchar(20) DEFAULT NULL, + `is_active` tinyint(1) DEFAULT '1', + `login_attempts` int DEFAULT '0', + `last_login_attempt` timestamp NULL DEFAULT NULL, + `account_status` enum('active','suspended','pending') DEFAULT 'active', + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`), + UNIQUE KEY `email` (`email`), + UNIQUE KEY `referral_code` (`referral_code`), + KEY `idx_users_email` (`email`), + KEY `idx_users_username` (`username`), + KEY `idx_account_status` (`account_status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + + +CREATE TABLE `user_sessions` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `session_id` varchar(128) NOT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` text, + `login_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `last_activity` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `expires_at` datetime NOT NULL, + `is_active` tinyint(1) DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `session_id` (`session_id`), + KEY `user_id` (`user_id`), + KEY `idx_expires_at` (`expires_at`), + KEY `idx_is_active` (`is_active`), + CONSTRAINT `user_sessions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + + +CREATE TABLE `user_activity_log` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `activity_type` varchar(50) NOT NULL, + `description` text, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` text, + `metadata` json DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `idx_activity_type` (`activity_type`), + KEY `idx_created_at` (`created_at`), + CONSTRAINT `user_activity_log_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + + +INSERT INTO `users` ( + `username`, `email`, `country`, `phone_number`, `password_hash`, `referral_code`, `tier`, `package` +) VALUES +('admin', 'admin@jmotors.com', 'KE', '+254700000000', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'ADMIN001', 'Premium', 'GOLD'), +('testuser', 'test@jmotors.com', 'KE', '+254711111111', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'TEST002', 'Basic', 'NOVA'); \ No newline at end of file diff --git a/jweb/ac1/assets/database/jm.sql b/jweb/ac1/assets/database/jm.sql new file mode 100644 index 0000000000000000000000000000000000000000..d52ebfcce60d0357f5b7a85a9b81ad8427b4c280 --- /dev/null +++ b/jweb/ac1/assets/database/jm.sql @@ -0,0 +1,930 @@ +CREATE TABLE `access_tokens` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `name` VARCHAR(100) NOT NULL, + `token` VARCHAR(255) NOT NULL UNIQUE, + `permissions` JSON NOT NULL, + `ip_restrictions` TEXT DEFAULT NULL, + `expires_at` DATETIME DEFAULT NULL, + `status` ENUM('active','revoked') DEFAULT 'active', + `last_used` TIMESTAMP NULL DEFAULT NULL, + `usage_count` INT DEFAULT 0, + `metadata` JSON DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_expires_at` (`expires_at`), + CONSTRAINT `fk_access_tokens_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `admin_payments` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `amount` DECIMAL(10,2) NOT NULL, + `mpesa_code` VARCHAR(20) UNIQUE NOT NULL, + `phone_number` VARCHAR(20) NOT NULL, + `status` ENUM('pending','verified','rejected') DEFAULT 'pending', + `verified_by` INT DEFAULT NULL, + `verified_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `fk_admin_payments_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `advertisement_payments` ( + `id` INT NOT NULL AUTO_INCREMENT, + `advertisement_id` INT NOT NULL, + `user_id` INT NOT NULL, + `amount` DECIMAL(10,2) NOT NULL, + `payment_method` ENUM('mpesa','bank_transfer','wallet','card') DEFAULT 'mpesa', + `transaction_code` VARCHAR(100) UNIQUE, + `status` ENUM('pending','completed','failed','refunded') DEFAULT 'pending', + `payment_date` TIMESTAMP NULL DEFAULT NULL, + `confirmed_by` INT DEFAULT NULL, + `confirmed_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_advertisement_id` (`advertisement_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_confirmed_by` (`confirmed_by`), + CONSTRAINT `fk_adv_payments_advertisement` FOREIGN KEY (`advertisement_id`) REFERENCES `advertisements` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_adv_payments_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_adv_payments_confirmer` FOREIGN KEY (`confirmed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `advertisements` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `agent_id` INT DEFAULT NULL, + `title` VARCHAR(255) NOT NULL, + `description` TEXT, + `image_url` VARCHAR(500) DEFAULT NULL, + `video_url` VARCHAR(500) DEFAULT NULL, + `target_platform` ENUM('facebook','instagram','whatsapp','twitter','all') DEFAULT 'all', + `budget` DECIMAL(10,2) NOT NULL, + `duration_days` INT DEFAULT 7, + `status` ENUM('pending','approved','rejected','active','completed','paused') DEFAULT 'pending', + `admin_notes` TEXT, + `approved_by` INT DEFAULT NULL, + `approved_at` TIMESTAMP NULL DEFAULT NULL, + `start_date` DATE DEFAULT NULL, + `end_date` DATE DEFAULT NULL, + `impressions` INT DEFAULT 0, + `clicks` INT DEFAULT 0, + `conversions` INT DEFAULT 0, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_agent_id` (`agent_id`), + KEY `idx_approved_by` (`approved_by`), + CONSTRAINT `fk_ads_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_ads_agent` FOREIGN KEY (`agent_id`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_ads_approver` FOREIGN KEY (`approved_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agent_applications` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `sponsor_id` INT NOT NULL, + `full_name` VARCHAR(255) NOT NULL, + `phone` VARCHAR(20) NOT NULL, + `email` VARCHAR(255) DEFAULT NULL, + `location` VARCHAR(255) NOT NULL, + `id_front` VARCHAR(500) DEFAULT NULL, + `id_back` VARCHAR(500) DEFAULT NULL, + `status` ENUM('pending','approved','rejected','documents_needed') DEFAULT 'pending', + `applied_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `reviewed_by` INT DEFAULT NULL, + `reviewed_at` TIMESTAMP NULL DEFAULT NULL, + `review_notes` TEXT, + `commission_rate` DECIMAL(5,2) DEFAULT 10.00, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_sponsor_id` (`sponsor_id`), + KEY `idx_reviewed_by` (`reviewed_by`), + CONSTRAINT `fk_agent_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_agent_sponsor` FOREIGN KEY (`sponsor_id`) REFERENCES `users` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_agent_reviewer` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agent_claims` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `claim_type` ENUM('commission','bonus','referral','override','other') NOT NULL, + `amount` DECIMAL(15,2) NOT NULL, + `description` TEXT, + `claim_period` VARCHAR(50) DEFAULT NULL, + `status` ENUM('pending','approved','rejected','processing') DEFAULT 'pending', + `approved_by` INT DEFAULT NULL, + `approved_amount` DECIMAL(15,2) DEFAULT NULL, + `approval_notes` TEXT, + `approved_at` DATETIME DEFAULT NULL, + `supporting_docs` JSON DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_approved_by` (`approved_by`), + KEY `idx_user_id` (`user_id`), + KEY `idx_status` (`status`), + KEY `idx_created_at` (`created_at`), + CONSTRAINT `fk_claim_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_claim_approved_by` FOREIGN KEY (`approved_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agent_hierarchy` ( + `id` INT NOT NULL AUTO_INCREMENT, + `upline_id` INT NOT NULL, + `downline_id` INT NOT NULL, + `level` INT NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_upline_id` (`upline_id`), + KEY `idx_downline_id` (`downline_id`), + CONSTRAINT `fk_hierarchy_upline` FOREIGN KEY (`upline_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_hierarchy_downline` FOREIGN KEY (`downline_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agents` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `full_name` VARCHAR(100) NOT NULL, + `phone` VARCHAR(20) NOT NULL, + `location` VARCHAR(100) NOT NULL, + `id_front` VARCHAR(255) DEFAULT NULL, + `id_back` VARCHAR(255) DEFAULT NULL, + `status` ENUM('pending','approved','rejected','documents_needed') DEFAULT 'pending', + `applied_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `reviewed_at` TIMESTAMP DEFAULT NULL, + `reviewed_by` INT DEFAULT NULL, + `notes` TEXT, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_reviewed_by` (`reviewed_by`), + CONSTRAINT `fk_agents_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_agents_reviewed_by` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `audit_logs` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT DEFAULT NULL, + `action` VARCHAR(100) NOT NULL, + `description` TEXT, + `ip_address` VARCHAR(45) DEFAULT NULL, + `user_agent` TEXT, + `metadata` JSON DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `fk_audit_logs_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `balance_history` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `transaction_id` INT DEFAULT NULL, + `amount` DECIMAL(10,2) NOT NULL, + `balance_before` DECIMAL(10,2) NOT NULL, + `balance_after` DECIMAL(10,2) NOT NULL, + `type` ENUM('transfer_sent','transfer_received','deposit','withdrawal') DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_transaction_id` (`transaction_id`), + CONSTRAINT `fk_balance_history_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_balance_history_transaction` FOREIGN KEY (`transaction_id`) REFERENCES `transactions` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `commissions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `upline_id` INT NOT NULL, + `downline_id` INT NOT NULL, + `advertisement_id` INT NOT NULL, + `amount` DECIMAL(10,2) NOT NULL, + `commission_rate` DECIMAL(5,2) NOT NULL, + `commission_amount` DECIMAL(10,2) NOT NULL, + `percentage` DECIMAL(5,2) NOT NULL, + `description` VARCHAR(255) DEFAULT NULL, + `status` ENUM('pending','paid','cancelled') DEFAULT 'pending', + `paid_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_upline_id` (`upline_id`), + KEY `idx_downline_id` (`downline_id`), + KEY `idx_advertisement_id` (`advertisement_id`), + CONSTRAINT `fk_commissions_upline` FOREIGN KEY (`upline_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_commissions_downline` FOREIGN KEY (`downline_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_commissions_advertisement` FOREIGN KEY (`advertisement_id`) REFERENCES `advertisements` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `currency_rates` ( + `id` INT NOT NULL AUTO_INCREMENT, + `base_currency` VARCHAR(3) NOT NULL DEFAULT 'KES', + `target_currency` VARCHAR(3) NOT NULL, + `exchange_rate` DECIMAL(10,4) NOT NULL DEFAULT 1.0000, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_currency_pair` (`base_currency`, `target_currency`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `daily_earnings` ( + `id` INT NOT NULL AUTO_INCREMENT, + `investment_id` INT NOT NULL, + `user_id` INT NOT NULL, + `amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `earning_date` DATE NOT NULL, + `status` ENUM('pending','paid','reinvested') NOT NULL DEFAULT 'pending', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_investment_id` (`investment_id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `daily_earnings_ibfk_1` FOREIGN KEY (`investment_id`) REFERENCES `user_investments` (`id`) ON DELETE CASCADE, + CONSTRAINT `daily_earnings_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `daily_token_stats` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `date` DATE NOT NULL, + `tokens_generated` INT NOT NULL DEFAULT 0, + `tokens_revoked` INT NOT NULL DEFAULT 0, + `api_calls` INT NOT NULL DEFAULT 0, + `total_processing_time_ms` INT NOT NULL DEFAULT 0, + `unique_endpoints` INT NOT NULL DEFAULT 0, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_user_date` (`user_id`,`date`), + KEY `idx_date` (`date`), + CONSTRAINT `daily_token_stats_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `eligibility_criteria` ( + `id` INT NOT NULL AUTO_INCREMENT, + `min_deposit_amount` DECIMAL(15,2) NOT NULL DEFAULT 20000.00, + `min_account_age_months` INT NOT NULL DEFAULT 6, + `min_successful_uploads` INT NOT NULL DEFAULT 10, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `cooling_period_days` INT NOT NULL DEFAULT 30, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `faqs` ( + `id` INT NOT NULL AUTO_INCREMENT, + `question` TEXT NOT NULL, + `answer` TEXT NOT NULL, + `category` VARCHAR(50) DEFAULT NULL, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `ledger` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `change_amount` DECIMAL(15,2) NOT NULL, + `balance_after` DECIMAL(15,2) NOT NULL, + `type` ENUM('deposit','withdrawal','transfer','reward','fee') NOT NULL, + `reference` VARCHAR(100) NOT NULL, + `description` TEXT DEFAULT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `ledger_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `loan_applications` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `amount` DECIMAL(15,2) NOT NULL, + `duration_days` INT NOT NULL, + `purpose` VARCHAR(255) NOT NULL, + `disbursement_method` ENUM('mpesa','bank','paypal') NOT NULL, + `mpesa_phone` VARCHAR(15) DEFAULT NULL, + `bank_name` VARCHAR(100) DEFAULT NULL, + `account_number` VARCHAR(50) DEFAULT NULL, + `account_name` VARCHAR(100) DEFAULT NULL, + `paypal_email` VARCHAR(100) DEFAULT NULL, + `notification_number` VARCHAR(15) NOT NULL, + `status` ENUM('pending','approved','rejected','disbursed','completed') NOT NULL DEFAULT 'pending', + `application_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `approval_date` TIMESTAMP NULL DEFAULT NULL, + `due_date` DATE DEFAULT NULL, + `processing_fee` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `interest_rate` DECIMAL(5,2) NOT NULL DEFAULT 5.00, + `total_repayment` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `loan_applications_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `loan_repayments` ( + `id` INT NOT NULL AUTO_INCREMENT, + `loan_id` INT NOT NULL, + `due_date` DATE NOT NULL, + `amount_due` DECIMAL(15,2) NOT NULL, + `amount_paid` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `status` ENUM('pending','partial','paid','overdue') NOT NULL DEFAULT 'pending', + `payment_date` TIMESTAMP NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `loan_id` (`loan_id`), + CONSTRAINT `loan_repayments_ibfk_1` FOREIGN KEY (`loan_id`) REFERENCES `loan_applications` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `loans` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `amount` DECIMAL(15,2) NOT NULL, + `interest_rate` DECIMAL(5,2) NOT NULL DEFAULT 5.00, + `duration_days` INT NOT NULL, + `purpose` TEXT DEFAULT NULL, + `status` ENUM('pending','approved','rejected','active','paid') NOT NULL DEFAULT 'pending', + `approved_by` INT DEFAULT NULL, + `approved_at` DATETIME DEFAULT NULL, + `due_date` DATE DEFAULT NULL, + `amount_paid` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `approved_by` (`approved_by`), + CONSTRAINT `loans_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `loans_ibfk_2` FOREIGN KEY (`approved_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `main_account` ( + `id` INT NOT NULL AUTO_INCREMENT, + `account_name` VARCHAR(100) NOT NULL DEFAULT 'JMotors Main Account', + `paybill_number` VARCHAR(20) DEFAULT NULL, + `account_number` VARCHAR(50) DEFAULT NULL, + `account_type` ENUM('paybill','bank','mpesa') NOT NULL DEFAULT 'paybill', + `total_balance` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total_deposits` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total_withdrawals` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `currency` VARCHAR(3) NOT NULL DEFAULT 'KES', + `mpesa_phone` VARCHAR(20) DEFAULT NULL, + `created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `meta_uploads` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `file_name` VARCHAR(255) NOT NULL, + `file_type` VARCHAR(50) NOT NULL, + `file_size` BIGINT NOT NULL, + `file_path` VARCHAR(500) NOT NULL, + `upload_date` DATE NOT NULL, + `status` ENUM('pending','approved','rejected') NOT NULL DEFAULT 'pending', + `reward_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `reviewed_by` INT DEFAULT NULL, + `reviewed_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `reviewed_by` (`reviewed_by`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `packages` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(50) NOT NULL, + `min_investment` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `max_investment` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `daily_return` DECIMAL(5,2) NOT NULL DEFAULT 0.00, + `price` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `award` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `return_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `duration_days` INT NOT NULL DEFAULT 0, + `referral_bonus` DECIMAL(5,2) NOT NULL DEFAULT 0.00, + `daily_return_percent` DECIMAL(5,2) NOT NULL DEFAULT 0.00, + `features` TEXT, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `pin_setup` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `pin_hash` VARCHAR(255) NOT NULL DEFAULT '', + `setup_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `pin_setup_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `products` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL DEFAULT '', + `value` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `package_requirement` VARCHAR(50) NOT NULL DEFAULT 'all', + `image_url` VARCHAR(500) DEFAULT NULL, + `stock` INT NOT NULL DEFAULT 0, + `description` TEXT DEFAULT NULL, + `price` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `image_code` VARCHAR(10) NOT NULL DEFAULT '', + `cashback_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `recharge_transactions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `main_account_id` INT NOT NULL, + `amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `virtual_balance_before` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `virtual_balance_after` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `main_balance_before` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `main_balance_after` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `currency` VARCHAR(3) NOT NULL DEFAULT 'KES', + `payment_method` VARCHAR(50) NOT NULL DEFAULT '', + `phone_number` VARCHAR(20) NOT NULL DEFAULT '', + `mpesa_receipt` VARCHAR(100) DEFAULT NULL, + `transaction_id` VARCHAR(100) DEFAULT NULL, + `paybill_number` VARCHAR(20) NOT NULL DEFAULT '542542', + `account_number` VARCHAR(50) NOT NULL DEFAULT '00106664176150', + `status` ENUM('pending','completed','failed','verified') NOT NULL DEFAULT 'pending', + `bonus_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `verified_by` INT DEFAULT NULL, + `verified_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `fk_recharge_main` (`main_account_id`), + CONSTRAINT `fk_recharge_main` FOREIGN KEY (`main_account_id`) REFERENCES `main_account` (`id`), + CONSTRAINT `recharge_transactions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `referrals` ( + `id` INT NOT NULL AUTO_INCREMENT, + `referrer_id` INT NOT NULL, + `referred_id` INT NOT NULL, + `status` ENUM('pending','completed','cancelled') NOT NULL DEFAULT 'pending', + `commission_earned` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `referrer_id` (`referrer_id`), + KEY `referred_id` (`referred_id`), + CONSTRAINT `referrals_ibfk_1` FOREIGN KEY (`referrer_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `referrals_ibfk_2` FOREIGN KEY (`referred_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `reward_settings` ( + `id` INT NOT NULL AUTO_INCREMENT, + `setting_key` VARCHAR(50) NOT NULL, + `setting_value` VARCHAR(255) NOT NULL, + `description` TEXT DEFAULT NULL, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `setting_key` (`setting_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `reward_transactions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `type` ENUM('upload','bonus','withdrawal') NOT NULL, + `amount` DECIMAL(10,2) NOT NULL, + `description` VARCHAR(255) DEFAULT NULL, + `reference_id` INT DEFAULT NULL, + `status` ENUM('pending','completed','failed') NOT NULL DEFAULT 'completed', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `reward_transactions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `sessions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `session_token` VARCHAR(255) NOT NULL, + `ip_address` VARCHAR(45) DEFAULT NULL, + `user_agent` TEXT, + `expires_at` DATETIME NOT NULL, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `session_token` (`session_token`), + KEY `user_id` (`user_id`), + CONSTRAINT `sessions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `support_agents` ( + `id` INT NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + `email` VARCHAR(255) NOT NULL, + `phone` VARCHAR(20) DEFAULT NULL, + `department` VARCHAR(50) DEFAULT NULL, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `support_tickets` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `ticket_number` varchar(20) NOT NULL, + `issue_type` enum('technical','account','payment','product','other') NOT NULL, + `subject` varchar(200) NOT NULL, + `description` text NOT NULL, + `status` enum('open','in_progress','resolved','closed') DEFAULT 'open', + `priority` enum('low','medium','high','urgent') DEFAULT 'medium', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `ticket_number` (`ticket_number`), + KEY `user_id` (`user_id`), + CONSTRAINT `support_tickets_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `support_users` ( + `id` INT NOT NULL AUTO_INCREMENT, + `username` VARCHAR(100) NOT NULL, + `email` VARCHAR(255) NOT NULL, + `tier` VARCHAR(50) NOT NULL DEFAULT 'standard', + `package` VARCHAR(50) NOT NULL DEFAULT 'basic', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `system_config` ( + `id` INT NOT NULL AUTO_INCREMENT, + `config_key` VARCHAR(100) NOT NULL, + `config_value` TEXT NOT NULL, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `config_key` (`config_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `team_relationships` ( + `id` INT NOT NULL AUTO_INCREMENT, + `sponsor_id` INT NOT NULL, + `agent_id` INT NOT NULL, + `level` INT NOT NULL, + `status` ENUM('active','inactive') NOT NULL DEFAULT 'active', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_relationship` (`sponsor_id`,`agent_id`), + KEY `agent_id` (`agent_id`), + CONSTRAINT `team_relationships_ibfk_1` FOREIGN KEY (`sponsor_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `team_relationships_ibfk_2` FOREIGN KEY (`agent_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `ticket_assignments` ( + `id` INT NOT NULL AUTO_INCREMENT, + `ticket_id` INT NOT NULL, + `agent_id` INT NOT NULL, + `assigned_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `ticket_id` (`ticket_id`), + KEY `agent_id` (`agent_id`), + CONSTRAINT `ticket_assignments_ibfk_1` FOREIGN KEY (`ticket_id`) REFERENCES `support_tickets` (`id`) ON DELETE CASCADE, + CONSTRAINT `ticket_assignments_ibfk_2` FOREIGN KEY (`agent_id`) REFERENCES `support_agents` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `ticket_responses` ( + `id` INT NOT NULL AUTO_INCREMENT, + `ticket_id` INT NOT NULL, + `responder_type` ENUM('user','support_agent') NOT NULL, + `message` TEXT NOT NULL, + `attachments` TEXT, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `ticket_id` (`ticket_id`), + CONSTRAINT `ticket_responses_ibfk_1` FOREIGN KEY (`ticket_id`) REFERENCES `support_tickets` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `token_usage_logs` ( + `id` INT NOT NULL AUTO_INCREMENT, + `token_id` INT NOT NULL, + `user_id` INT NOT NULL, + `endpoint` VARCHAR(100) NOT NULL, + `ip_address` VARCHAR(45), + `user_agent` TEXT, + `request_method` VARCHAR(10), + `response_code` INT, + `processing_time_ms` INT, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `idx_created_at` (`created_at`), + KEY `idx_token_id` (`token_id`), + CONSTRAINT `token_usage_logs_ibfk_1` FOREIGN KEY (`token_id`) REFERENCES `access_tokens` (`id`) ON DELETE CASCADE, + CONSTRAINT `token_usage_logs_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `transactions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `sender_id` INT NOT NULL, + `user_id` INT NOT NULL, + `recipient_id` INT NOT NULL, + `type` ENUM('deposit','withdrawal','product_purchase','cashback') NOT NULL, + `amount` DECIMAL(10,2) NOT NULL, + `balance_after` DECIMAL(10,2) NOT NULL, + `description` TEXT, + `message` TEXT, + `transaction_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `reference` VARCHAR(100), + `status` ENUM('pending','completed','failed') DEFAULT 'pending', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `completed_at` TIMESTAMP, + PRIMARY KEY (`id`), + KEY `fk_sender` (`sender_id`), + KEY `fk_recipient` (`recipient_id`), + KEY `fk_transactions_user` (`user_id`), + CONSTRAINT `fk_sender` FOREIGN KEY (`sender_id`) REFERENCES `users` (`id`), + CONSTRAINT `fk_recipient` FOREIGN KEY (`recipient_id`) REFERENCES `users` (`id`), + CONSTRAINT `fk_transactions_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `transaction_verifications` ( + `id` INT NOT NULL AUTO_INCREMENT, + `transaction_id` INT, + `user_id` INT, + `amount` DECIMAL(10,2), + `paybill_number` VARCHAR(20), + `account_number` VARCHAR(50), + `mpesa_receipt` VARCHAR(100), + `screenshot_path` VARCHAR(255), + `status` ENUM('pending','approved','rejected') DEFAULT 'pending', + `reviewed_by` INT, + `reviewed_at` TIMESTAMP, + `notes` TEXT, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `fk_transaction` (`transaction_id`), + KEY `fk_user` (`user_id`), + CONSTRAINT `fk_transaction` FOREIGN KEY (`transaction_id`) REFERENCES `recharge_transactions` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `upload_rewards` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `upload_date` DATE NOT NULL, + `total_uploads` INT DEFAULT 0, + `completed_uploads` INT DEFAULT 0, + `total_reward` DECIMAL(10,2) DEFAULT 0.00, + `status` ENUM('pending','processed') DEFAULT 'pending', + `processed_at` TIMESTAMP, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_user_date` (`user_id`,`upload_date`), + CONSTRAINT `fk_upload_rewards_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_accounts` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT, + `virtual_account_number` VARCHAR(50) UNIQUE, + `current_balance` DECIMAL(15,2) DEFAULT 0.00, + `total_invested` DECIMAL(15,2) DEFAULT 0.00, + `total_earnings` DECIMAL(15,2) DEFAULT 0.00, + `total_withdrawn` DECIMAL(15,2) DEFAULT 0.00, + `currency` VARCHAR(3) DEFAULT 'KES', + `status` ENUM('active','suspended','closed') DEFAULT 'active', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `fk_user_accounts_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_activity` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT, + `activity_type` VARCHAR(50), + `description` TEXT, + `ip_address` VARCHAR(45), + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `fk_user_activity_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_channel_subscriptions` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT, + `channel_id` INT, + `subscribed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uniq_user_channel` (`user_id`,`channel_id`), + KEY `idx_channel_id` (`channel_id`), + CONSTRAINT `fk_user_channel_subscriptions_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_channel_subscriptions_channel` FOREIGN KEY (`channel_id`) REFERENCES `whatsapp_channels` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_investments` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `package_id` INT NOT NULL, + `amount` DECIMAL(15,2) NOT NULL, + `start_date` DATETIME NOT NULL, + `end_date` DATETIME NOT NULL, + `status` ENUM('active','completed','cancelled') NOT NULL DEFAULT 'active', + `total_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_package_id` (`package_id`), + CONSTRAINT `fk_user_investments_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_investments_package` FOREIGN KEY (`package_id`) REFERENCES `packages` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_packages` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `package_id` INT NOT NULL, + `purchase_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `start_date` DATETIME NULL DEFAULT NULL, + `end_date` DATETIME NULL DEFAULT NULL, + `amount_paid` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `status` ENUM('active','completed','cancelled') NOT NULL DEFAULT 'active', + `investment_amount` DECIMAL(10,2) NOT NULL, + `expected_return` DECIMAL(10,2) NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_package_id` (`package_id`), + CONSTRAINT `fk_user_packages_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_packages_package` FOREIGN KEY (`package_id`) REFERENCES `packages` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_products` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `product_id` INT NOT NULL, + `investment_id` INT NOT NULL, + `assigned_date` DATE NOT NULL, + `purchase_price` DECIMAL(10,2) NOT NULL, + `cashback_received` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `purchase_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `status` ENUM('active','completed','cancelled') NOT NULL DEFAULT 'active', + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_product_id` (`product_id`), + CONSTRAINT `fk_user_products_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_products_product` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_profiles` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `first_name` VARCHAR(50), + `last_name` VARCHAR(50), + `date_of_birth` DATE, + `gender` ENUM('Male','Female','Other'), + `profile_picture` VARCHAR(255), + `address` TEXT, + `city` VARCHAR(50), + `state` VARCHAR(50), + `zip_code` VARCHAR(20), + `bio` TEXT, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + CONSTRAINT `fk_user_profiles_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `users` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_type` ENUM('marketer','agent','admin') NOT NULL DEFAULT 'marketer', + `referred_by` INT, + `username` VARCHAR(50) NOT NULL, + `email` VARCHAR(100) NOT NULL, + `country` CHAR(2) NOT NULL, + `phone_number` VARCHAR(20) NOT NULL, + `tier` ENUM('Basic','Premium') NOT NULL DEFAULT 'Basic', + `current_package_id` INT, + `package_start_date` DATETIME, + `package_end_date` DATETIME, + `package` ENUM('None','NOVA','SUPERIOR','GOLD') NOT NULL DEFAULT 'None', + `balance` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `verification_token` VARCHAR(100), + `reset_token` VARCHAR(100), + `reset_token_expiry` DATETIME, + `total_invested` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total_deposits` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total_withdrawals` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `rewards` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `meta_earnings` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `last_login` TIMESTAMP, + `pin_hash` VARCHAR(255) NOT NULL, + `password_hash` VARCHAR(255) NOT NULL, + `currency` CHAR(3) NOT NULL DEFAULT 'KES', + `exchange_rate` DECIMAL(10,4) NOT NULL DEFAULT 1.0000, + `referral_code` VARCHAR(20), + PRIMARY KEY (`id`), + UNIQUE KEY `uk_users_username` (`username`), + UNIQUE KEY `uk_users_email` (`email`), +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_security` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `two_factor_enabled` TINYINT(1) NOT NULL DEFAULT 0, + `two_factor_secret` VARCHAR(32), + `password_reset_token` VARCHAR(100), + `reset_token_expires` TIMESTAMP, + `login_attempts` INT NOT NULL DEFAULT 0, + `last_login_attempt` TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_user_security_user_id` (`user_id`), + CONSTRAINT `fk_user_security_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_settings` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `dark_mode` TINYINT(1) NOT NULL DEFAULT 1, + `language` VARCHAR(10) NOT NULL DEFAULT 'en', + `currency` VARCHAR(3) NOT NULL DEFAULT 'KES', + `auto_logout` TINYINT(1) NOT NULL DEFAULT 1, + `email_notifications` TINYINT(1) NOT NULL DEFAULT 1, + `push_notifications` TINYINT(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + UNIQUE KEY `uk_user_settings_user_id` (`user_id`), + CONSTRAINT `fk_user_settings_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `whatsapp_channels` ( + `id` INT NOT NULL AUTO_INCREMENT, + `channel_name` VARCHAR(100) NOT NULL, + `channel_type` ENUM('group','channel','tips','elite') NOT NULL, + `whatsapp_link` VARCHAR(500) NOT NULL, + `description` TEXT DEFAULT NULL, + `icon_class` VARCHAR(50) DEFAULT NULL, + `color_class` VARCHAR(50) DEFAULT NULL, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `member_count` INT NOT NULL DEFAULT 0, + `max_capacity` INT NOT NULL DEFAULT 1000, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `withdrawal_destinations` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `type` ENUM('mpesa','bank','airtel','tkash') NOT NULL, + `details` VARCHAR(255) NOT NULL, + `is_default` TINYINT(1) NOT NULL DEFAULT 0, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `withdrawal_destinations_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `withdrawals` ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `destination_id` INT NOT NULL, + `amount` DECIMAL(15,2) NOT NULL, + `fee` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `net_amount` DECIMAL(15,2) NOT NULL, + `status` ENUM('pending','processing','completed','failed','cancelled') NOT NULL DEFAULT 'pending', + `idempotency_key` VARCHAR(255) NOT NULL, + `reference` VARCHAR(100) NOT NULL, + `failure_reason` TEXT, + `processed_at` TIMESTAMP NULL DEFAULT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idempotency_key` (`idempotency_key`), + KEY `user_id` (`user_id`), + KEY `destination_id` (`destination_id`), + CONSTRAINT `withdrawals_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `withdrawals_ibfk_2` FOREIGN KEY (`destination_id`) REFERENCES `withdrawal_destinations` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE VIEW `user_teams` AS +SELECT + u.id AS user_id, + u.username, + u.referral_code, + COUNT(r.referred_id) AS team_size, + COALESCE(SUM(ref.commission_earned), 0) AS total_earnings +FROM + users u +LEFT JOIN + referrals r ON u.id = r.referrer_id +LEFT JOIN + referrals ref ON u.id = ref.referrer_id AND ref.status = 'completed' +GROUP BY + u.id, u.username, u.referral_code; + diff --git a/jweb/ac1/assets/database/mdb.sql b/jweb/ac1/assets/database/mdb.sql new file mode 100644 index 0000000000000000000000000000000000000000..3e9134903064196c0be592fec6072af5d4ad9029 --- /dev/null +++ b/jweb/ac1/assets/database/mdb.sql @@ -0,0 +1,936 @@ +CREATE TABLE `access_tokens` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `name` varchar(100) NOT NULL, + `token` varchar(255) NOT NULL, + `permissions` json NOT NULL, + `ip_restrictions` text, + `expires_at` datetime DEFAULT NULL, + `status` enum('active','revoked') DEFAULT 'active', + `is_revoked` tinyint(1) DEFAULT '0', + `last_used` timestamp NULL DEFAULT NULL, + `usage_count` int DEFAULT '0', + `metadata` json DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_token_value` (`token`), + KEY `idx_user_id` (`user_id`), + KEY `idx_expires_at` (`expires_at`), + CONSTRAINT `access_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `admin_payments` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `amount` decimal(10,2) DEFAULT NULL, + `mpesa_code` varchar(20) DEFAULT NULL, + `phone_number` varchar(20) DEFAULT NULL, + `status` enum('pending','verified','rejected') DEFAULT 'pending', + `verified_by` int DEFAULT NULL, + `verified_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `admin_payments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `advertisement_payments` ( + `id` int NOT NULL AUTO_INCREMENT, + `advertisement_id` int NOT NULL, + `user_id` int NOT NULL, + `amount` decimal(10,2) NOT NULL, + `payment_method` enum('mpesa','bank_transfer','wallet','card') DEFAULT 'mpesa', + `transaction_code` varchar(100) DEFAULT NULL, + `status` enum('pending','completed','failed','refunded') DEFAULT 'pending', + `payment_date` timestamp NULL DEFAULT NULL, + `confirmed_by` int DEFAULT NULL, + `confirmed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `advertisement_id` (`advertisement_id`), + KEY `user_id` (`user_id`), + KEY `confirmed_by` (`confirmed_by`), + CONSTRAINT `advertisement_payments_ibfk_1` FOREIGN KEY (`advertisement_id`) REFERENCES `advertisements` (`id`), + CONSTRAINT `advertisement_payments_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `advertisement_payments_ibfk_3` FOREIGN KEY (`confirmed_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `advertisements` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `agent_id` int DEFAULT NULL, + `title` varchar(255) NOT NULL, + `description` text, + `image_url` varchar(500) DEFAULT NULL, + `video_url` varchar(500) DEFAULT NULL, + `target_platform` enum('facebook','instagram','whatsapp','twitter','all') DEFAULT 'all', + `budget` decimal(10,2) NOT NULL, + `duration_days` int DEFAULT '7', + `status` enum('pending','approved','rejected','active','completed','paused') DEFAULT 'pending', + `admin_notes` text, + `approved_by` int DEFAULT NULL, + `approved_at` timestamp NULL DEFAULT NULL, + `start_date` date DEFAULT NULL, + `end_date` date DEFAULT NULL, + `impressions` int DEFAULT '0', + `clicks` int DEFAULT '0', + `conversions` int DEFAULT '0', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `agent_id` (`agent_id`), + KEY `approved_by` (`approved_by`), + CONSTRAINT `advertisements_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `advertisements_ibfk_2` FOREIGN KEY (`agent_id`) REFERENCES `users` (`id`), + CONSTRAINT `advertisements_ibfk_3` FOREIGN KEY (`approved_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agent_applications` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `sponsor_id` int NOT NULL, + `full_name` varchar(255) NOT NULL, + `phone` varchar(20) NOT NULL, + `email` varchar(255) DEFAULT NULL, + `location` varchar(255) NOT NULL, + `id_front` varchar(500) DEFAULT NULL, + `id_back` varchar(500) DEFAULT NULL, + `status` enum('pending','approved','rejected','documents_needed') DEFAULT 'pending', + `applied_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `reviewed_by` int DEFAULT NULL, + `reviewed_at` timestamp NULL DEFAULT NULL, + `review_notes` text, + `commission_rate` decimal(5,2) DEFAULT '10.00', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `sponsor_id` (`sponsor_id`), + KEY `reviewed_by` (`reviewed_by`), + CONSTRAINT `agent_applications_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `agent_applications_ibfk_2` FOREIGN KEY (`sponsor_id`) REFERENCES `users` (`id`), + CONSTRAINT `agent_applications_ibfk_3` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agent_claims` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `claim_type` enum('commission','bonus','referral','override','other') NOT NULL, + `amount` decimal(15,2) NOT NULL, + `description` text, + `claim_period` varchar(50) DEFAULT NULL, + `status` enum('pending','approved','rejected','processing') DEFAULT 'pending', + `approved_by` int DEFAULT NULL, + `approved_amount` decimal(15,2) DEFAULT NULL, + `approval_notes` text, + `approved_at` datetime DEFAULT NULL, + `supporting_docs` json DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `approved_by` (`approved_by`), + KEY `idx_user_id` (`user_id`), + KEY `idx_status` (`status`), + KEY `idx_created_at` (`created_at`), + CONSTRAINT `agent_claims_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `agent_claims_ibfk_2` FOREIGN KEY (`approved_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agent_hierarchy` ( + `id` int NOT NULL AUTO_INCREMENT, + `upline_id` int NOT NULL, + `downline_id` int NOT NULL, + `level` int NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `upline_id` (`upline_id`), + KEY `downline_id` (`downline_id`), + CONSTRAINT `agent_hierarchy_ibfk_1` FOREIGN KEY (`upline_id`) REFERENCES `users` (`id`), + CONSTRAINT `agent_hierarchy_ibfk_2` FOREIGN KEY (`downline_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `agents` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `full_name` varchar(100) NOT NULL, + `phone` varchar(20) NOT NULL, + `location` varchar(100) NOT NULL, + `id_front` varchar(255) DEFAULT NULL, + `id_back` varchar(255) DEFAULT NULL, + `status` enum('pending','approved','rejected','documents_needed') DEFAULT 'pending', + `applied_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `reviewed_at` timestamp NULL DEFAULT NULL, + `reviewed_by` int DEFAULT NULL, + `notes` text, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `reviewed_by` (`reviewed_by`), + CONSTRAINT `agents_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `agents_ibfk_2` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `audit_logs` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `action` varchar(100) NOT NULL, + `description` text, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` text, + `metadata` json DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `balance_history` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `transaction_id` int DEFAULT NULL, + `amount` decimal(10,2) NOT NULL, + `balance_before` decimal(10,2) NOT NULL, + `balance_after` decimal(10,2) NOT NULL, + `type` enum('transfer_sent','transfer_received','deposit','withdrawal') DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `transaction_id` (`transaction_id`), + CONSTRAINT `balance_history_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `balance_history_ibfk_2` FOREIGN KEY (`transaction_id`) REFERENCES `transactions` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `commissions` ( + `id` int NOT NULL AUTO_INCREMENT, + `upline_id` int NOT NULL, + `downline_id` int NOT NULL, + `advertisement_id` int NOT NULL, + `amount` decimal(10,2) NOT NULL, + `commission_rate` decimal(5,2) NOT NULL, + `commission_amount` decimal(10,2) NOT NULL, + `percentage` decimal(5,2) NOT NULL, + `description` varchar(255) DEFAULT NULL, + `status` enum('pending','paid','cancelled') DEFAULT 'pending', + `paid_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `upline_id` (`upline_id`), + KEY `downline_id` (`downline_id`), + KEY `fk_commission_advertisement` (`advertisement_id`), + CONSTRAINT `commissions_ibfk_1` FOREIGN KEY (`upline_id`) REFERENCES `users` (`id`), + CONSTRAINT `commissions_ibfk_2` FOREIGN KEY (`downline_id`) REFERENCES `users` (`id`), + CONSTRAINT `fk_commission_advertisement` FOREIGN KEY (`advertisement_id`) REFERENCES `advertisements` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `currency_rates` ( + `id` int NOT NULL AUTO_INCREMENT, + `base_currency` varchar(3) DEFAULT 'KES', + `target_currency` varchar(3) DEFAULT NULL, + `exchange_rate` decimal(10,4) DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `daily_earnings` ( + `id` int NOT NULL AUTO_INCREMENT, + `investment_id` int NOT NULL, + `user_id` int NOT NULL, + `amount` decimal(15,2) NOT NULL, + `earning_date` date NOT NULL, + `status` enum('pending','paid','reinvested') DEFAULT 'pending', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `investment_id` (`investment_id`), + KEY `user_id` (`user_id`), + CONSTRAINT `daily_earnings_ibfk_1` FOREIGN KEY (`investment_id`) REFERENCES `user_investments` (`id`), + CONSTRAINT `daily_earnings_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `daily_token_stats` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `date` date NOT NULL, + `tokens_generated` int DEFAULT '0', + `tokens_revoked` int DEFAULT '0', + `api_calls` int DEFAULT '0', + `total_processing_time_ms` int DEFAULT '0', + `unique_endpoints` int DEFAULT '0', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_user_date` (`user_id`,`date`), + KEY `idx_date` (`date`), + CONSTRAINT `daily_token_stats_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `eligibility_criteria` ( + `id` int NOT NULL AUTO_INCREMENT, + `min_deposit_amount` decimal(15,2) DEFAULT '20000.00', + `min_account_age_months` int DEFAULT '6', + `min_successful_uploads` int DEFAULT '10', + `is_active` tinyint(1) DEFAULT '1', + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `cooling_period_days` int DEFAULT '30', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `faqs` ( + `id` int NOT NULL AUTO_INCREMENT, + `question` text NOT NULL, + `answer` text NOT NULL, + `category` varchar(50) DEFAULT NULL, + `is_active` tinyint(1) DEFAULT '1', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `ledger` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `change_amount` decimal(15,2) NOT NULL, + `balance_after` decimal(15,2) NOT NULL, + `type` enum('deposit','withdrawal','transfer','reward','fee') NOT NULL, + `reference` varchar(100) NOT NULL, + `description` text, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `ledger_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `loan_applications` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `amount` decimal(15,2) NOT NULL, + `duration_days` int NOT NULL, + `purpose` varchar(255) NOT NULL, + `disbursement_method` enum('mpesa','bank','paypal') NOT NULL, + `mpesa_phone` varchar(15) DEFAULT NULL, + `bank_name` varchar(100) DEFAULT NULL, + `account_number` varchar(50) DEFAULT NULL, + `account_name` varchar(100) DEFAULT NULL, + `paypal_email` varchar(100) DEFAULT NULL, + `notification_number` varchar(15) NOT NULL, + `status` enum('pending','approved','rejected','disbursed','completed') DEFAULT 'pending', + `application_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `approval_date` timestamp NULL DEFAULT NULL, + `due_date` date DEFAULT NULL, + `processing_fee` decimal(15,2) DEFAULT '0.00', + `interest_rate` decimal(5,2) DEFAULT '5.00', + `total_repayment` decimal(15,2) DEFAULT '0.00', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `loan_applications_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `loan_repayments` ( + `id` int NOT NULL AUTO_INCREMENT, + `loan_id` int NOT NULL, + `due_date` date NOT NULL, + `amount_due` decimal(15,2) NOT NULL, + `amount_paid` decimal(15,2) DEFAULT '0.00', + `status` enum('pending','partial','paid','overdue') DEFAULT 'pending', + `payment_date` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `loan_id` (`loan_id`), + CONSTRAINT `loan_repayments_ibfk_1` FOREIGN KEY (`loan_id`) REFERENCES `loan_applications` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `loans` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `amount` decimal(15,2) NOT NULL, + `interest_rate` decimal(5,2) NOT NULL, + `duration_days` int NOT NULL, + `purpose` text, + `status` enum('pending','approved','rejected','active','paid') DEFAULT 'pending', + `approved_by` int DEFAULT NULL, + `approved_at` datetime DEFAULT NULL, + `due_date` date DEFAULT NULL, + `amount_paid` decimal(15,2) DEFAULT '0.00', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `approved_by` (`approved_by`), + CONSTRAINT `loans_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `loans_ibfk_2` FOREIGN KEY (`approved_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `main_account` ( + `id` int NOT NULL AUTO_INCREMENT, + `account_name` varchar(100) DEFAULT 'JMotors Main Account', + `paybill_number` varchar(20) DEFAULT '542542', + `account_number` varchar(50) DEFAULT '00106664176150', + `account_type` enum('paybill','bank','mpesa') DEFAULT 'paybill', + `total_balance` decimal(15,2) DEFAULT '0.00', + `total_deposits` decimal(15,2) DEFAULT '0.00', + `total_withdrawals` decimal(15,2) DEFAULT '0.00', + `currency` varchar(3) DEFAULT 'KES', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `mpesa_phone` varchar(20) DEFAULT '0756709823', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `meta_uploads` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `file_name` varchar(255) NOT NULL, + `file_type` varchar(50) NOT NULL, + `file_size` bigint NOT NULL, + `file_path` varchar(500) NOT NULL, + `upload_date` date NOT NULL, + `status` enum('pending','approved','rejected') DEFAULT 'pending', + `reward_amount` decimal(10,2) DEFAULT '0.00', + `reviewed_by` int DEFAULT NULL, + `reviewed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `reviewed_by` (`reviewed_by`), + CONSTRAINT `meta_uploads_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `meta_uploads_ibfk_2` FOREIGN KEY (`reviewed_by`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `packages` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL, + `min_investment` decimal(15,2) NOT NULL, + `max_investment` decimal(15,2) NOT NULL, + `daily_return` decimal(5,2) NOT NULL, + `price` decimal(10,2) NOT NULL, + `award` decimal(10,2) NOT NULL, + `return_amount` decimal(10,2) NOT NULL, + `duration_days` int NOT NULL, + `referral_bonus` decimal(5,2) NOT NULL, + `daily_return_percent` decimal(5,2) NOT NULL, + `features` text, + `is_active` tinyint(1) DEFAULT '1', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `pin_setup` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `pin_hash` varchar(255) NOT NULL, + `setup_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `pin_setup_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `products` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `value` decimal(15,2) NOT NULL, + `package_requirement` varchar(50) DEFAULT 'all', + `image_url` varchar(500) DEFAULT NULL, + `stock` int DEFAULT '0', + `description` text, + `price` decimal(10,2) NOT NULL, + `image_code` varchar(10) NOT NULL, + `cashback_amount` decimal(10,2) DEFAULT '0.00', + `is_active` tinyint(1) DEFAULT '1', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `recharge_transactions` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `main_account_id` int DEFAULT NULL, + `amount` decimal(10,2) DEFAULT NULL, + `virtual_balance_before` decimal(10,2) DEFAULT NULL, + `virtual_balance_after` decimal(10,2) DEFAULT NULL, + `main_balance_before` decimal(10,2) DEFAULT NULL, + `main_balance_after` decimal(10,2) DEFAULT NULL, + `currency` varchar(3) DEFAULT 'KES', + `payment_method` varchar(50) DEFAULT NULL, + `phone_number` varchar(20) DEFAULT NULL, + `mpesa_receipt` varchar(100) DEFAULT NULL, + `transaction_id` varchar(100) DEFAULT NULL, + `paybill_number` varchar(20) DEFAULT '542542', + `account_number` varchar(50) DEFAULT '00106664176150', + `status` enum('pending','completed','failed','verified') DEFAULT 'pending', + `bonus_amount` decimal(10,2) DEFAULT '0.00', + `verified_by` int DEFAULT NULL, + `verified_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `fk_recharge_main` (`main_account_id`), + CONSTRAINT `fk_recharge_main` FOREIGN KEY (`main_account_id`) REFERENCES `main_account` (`id`), + CONSTRAINT `recharge_transactions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `referrals` ( + `id` int NOT NULL AUTO_INCREMENT, + `referrer_id` int NOT NULL, + `referred_id` int NOT NULL, + `status` enum('pending','completed','cancelled') DEFAULT 'pending', + `commission_earned` decimal(10,2) DEFAULT '0.00', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `referrer_id` (`referrer_id`), + KEY `referred_id` (`referred_id`), + CONSTRAINT `referrals_ibfk_1` FOREIGN KEY (`referrer_id`) REFERENCES `users` (`id`), + CONSTRAINT `referrals_ibfk_2` FOREIGN KEY (`referred_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `reward_settings` ( + `id` int NOT NULL AUTO_INCREMENT, + `setting_key` varchar(50) NOT NULL, + `setting_value` varchar(255) NOT NULL, + `description` text, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `setting_key` (`setting_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `reward_transactions` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `type` enum('upload','bonus','withdrawal') NOT NULL, + `amount` decimal(10,2) NOT NULL, + `description` varchar(255) DEFAULT NULL, + `reference_id` int DEFAULT NULL, + `status` enum('pending','completed','failed') DEFAULT 'completed', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `reward_transactions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `sessions` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `session_token` varchar(255) NOT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` text, + `expires_at` datetime NOT NULL, + `is_active` tinyint(1) DEFAULT '1', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `session_token` (`session_token`), + KEY `user_id` (`user_id`), + CONSTRAINT `sessions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `support_agents` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(100) NOT NULL, + `email` varchar(255) NOT NULL, + `phone` varchar(20) DEFAULT NULL, + `department` varchar(50) DEFAULT NULL, + `is_active` tinyint(1) DEFAULT '1', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `email` (`email`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `support_tickets` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `ticket_number` varchar(20) NOT NULL, + `issue_type` enum('technical','account','payment','product','other') NOT NULL, + `subject` varchar(200) NOT NULL, + `description` text NOT NULL, + `status` enum('open','in_progress','resolved','closed') DEFAULT 'open', + `priority` enum('low','medium','high','urgent') DEFAULT 'medium', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `ticket_number` (`ticket_number`), + KEY `user_id` (`user_id`), + CONSTRAINT `support_tickets_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `support_users` ( + `id` int NOT NULL AUTO_INCREMENT, + `username` varchar(100) NOT NULL, + `email` varchar(255) NOT NULL, + `tier` varchar(50) DEFAULT NULL, + `package` varchar(50) DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `system_config` ( + `id` int NOT NULL AUTO_INCREMENT, + `config_key` varchar(100) NOT NULL, + `config_value` text NOT NULL, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `config_key` (`config_key`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `team_relationships` ( + `id` int NOT NULL AUTO_INCREMENT, + `sponsor_id` int NOT NULL, + `agent_id` int NOT NULL, + `level` int NOT NULL, + `status` enum('active','inactive') DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_relationship` (`sponsor_id`,`agent_id`), + KEY `agent_id` (`agent_id`), + CONSTRAINT `team_relationships_ibfk_1` FOREIGN KEY (`sponsor_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `team_relationships_ibfk_2` FOREIGN KEY (`agent_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `ticket_assignments` ( + `id` int NOT NULL AUTO_INCREMENT, + `ticket_id` int NOT NULL, + `agent_id` int NOT NULL, + `assigned_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `ticket_id` (`ticket_id`), + KEY `agent_id` (`agent_id`), + CONSTRAINT `ticket_assignments_ibfk_1` FOREIGN KEY (`ticket_id`) REFERENCES `support_tickets` (`id`) ON DELETE CASCADE, + CONSTRAINT `ticket_assignments_ibfk_2` FOREIGN KEY (`agent_id`) REFERENCES `support_agents` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `ticket_responses` ( + `id` int NOT NULL AUTO_INCREMENT, + `ticket_id` int NOT NULL, + `responder_type` enum('user','support_agent') NOT NULL, + `message` text NOT NULL, + `attachments` text, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `ticket_id` (`ticket_id`), + CONSTRAINT `ticket_responses_ibfk_1` FOREIGN KEY (`ticket_id`) REFERENCES `support_tickets` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `token_usage_logs` ( + `id` int NOT NULL AUTO_INCREMENT, + `token_id` int NOT NULL, + `user_id` int NOT NULL, + `endpoint` varchar(100) NOT NULL, + `ip_address` varchar(45) DEFAULT NULL, + `user_agent` text, + `request_method` varchar(10) DEFAULT NULL, + `response_code` int DEFAULT NULL, + `processing_time_ms` int DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `idx_created_at` (`created_at`), + KEY `idx_token_id` (`token_id`), + CONSTRAINT `token_usage_logs_ibfk_1` FOREIGN KEY (`token_id`) REFERENCES `access_tokens` (`id`) ON DELETE CASCADE, + CONSTRAINT `token_usage_logs_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `transactions` ( + `id` int NOT NULL AUTO_INCREMENT, + `sender_id` int NOT NULL, + `user_id` int NOT NULL, + `type` enum('deposit','withdrawal','product_purchase','cashback') NOT NULL, + `description` text, + `balance_after` decimal(10,2) NOT NULL, + `recipient_id` int NOT NULL, + `amount` decimal(10,2) NOT NULL, + `message` text, + `transaction_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `reference` varchar(100) DEFAULT NULL, + `status` enum('pending','completed','failed') DEFAULT 'pending', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `completed_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `fk_sender` (`sender_id`), + KEY `fk_recipient` (`recipient_id`), + KEY `fk_transactions_user` (`user_id`), + CONSTRAINT `fk_recipient` FOREIGN KEY (`recipient_id`) REFERENCES `users` (`id`), + CONSTRAINT `fk_sender` FOREIGN KEY (`sender_id`) REFERENCES `users` (`id`), + CONSTRAINT `fk_transactions_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `transactions_ibfk_1` FOREIGN KEY (`sender_id`) REFERENCES `users` (`id`), + CONSTRAINT `transactions_ibfk_2` FOREIGN KEY (`recipient_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `transaction_verifications` ( + `id` int NOT NULL AUTO_INCREMENT, + `transaction_id` int DEFAULT NULL, + `user_id` int DEFAULT NULL, + `amount` decimal(10,2) DEFAULT NULL, + `paybill_number` varchar(20) DEFAULT NULL, + `account_number` varchar(50) DEFAULT NULL, + `mpesa_receipt` varchar(100) DEFAULT NULL, + `screenshot_path` varchar(255) DEFAULT NULL, + `status` enum('pending','approved','rejected') DEFAULT 'pending', + `reviewed_by` int DEFAULT NULL, + `reviewed_at` timestamp NULL DEFAULT NULL, + `notes` text, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `transaction_id` (`transaction_id`), + KEY `user_id` (`user_id`), + CONSTRAINT `transaction_verifications_ibfk_1` FOREIGN KEY (`transaction_id`) REFERENCES `recharge_transactions` (`id`), + CONSTRAINT `transaction_verifications_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `upload_rewards` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `upload_date` date NOT NULL, + `total_uploads` int DEFAULT '0', + `completed_uploads` int DEFAULT '0', + `total_reward` decimal(10,2) DEFAULT '0.00', + `status` enum('pending','processed') DEFAULT 'pending', + `processed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_user_date` (`user_id`,`upload_date`), + CONSTRAINT `upload_rewards_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_accounts` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `virtual_account_number` varchar(50) DEFAULT NULL, + `current_balance` decimal(15,2) DEFAULT '0.00', + `total_invested` decimal(15,2) DEFAULT '0.00', + `total_earnings` decimal(15,2) DEFAULT '0.00', + `total_withdrawn` decimal(15,2) DEFAULT '0.00', + `currency` varchar(3) DEFAULT 'KES', + `status` enum('active','suspended','closed') DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `virtual_account_number` (`virtual_account_number`), + KEY `user_id` (`user_id`), + CONSTRAINT `user_accounts_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_activity` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `activity_type` varchar(50) DEFAULT NULL, + `description` text, + `ip_address` varchar(45) DEFAULT NULL, + `timestamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `user_activity_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_channel_subscriptions` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `channel_id` int DEFAULT NULL, + `subscribed_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_subscription` (`user_id`,`channel_id`), + KEY `channel_id` (`channel_id`), + CONSTRAINT `user_channel_subscriptions_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `user_channel_subscriptions_ibfk_2` FOREIGN KEY (`channel_id`) REFERENCES `whatsapp_channels` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_investments` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `package_id` int NOT NULL, + `amount` decimal(15,2) NOT NULL, + `start_date` datetime NOT NULL, + `end_date` datetime NOT NULL, + `status` enum('active','completed','cancelled') DEFAULT 'active', + `total_earnings` decimal(15,2) DEFAULT '0.00', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `package_id` (`package_id`), + CONSTRAINT `user_investments_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`), + CONSTRAINT `user_investments_ibfk_2` FOREIGN KEY (`package_id`) REFERENCES `packages` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_packages` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `package_id` int NOT NULL, + `purchase_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `start_date` datetime DEFAULT NULL, + `end_date` datetime DEFAULT NULL, + `amount_paid` decimal(10,2) DEFAULT NULL, + `status` enum('active','completed','cancelled') DEFAULT 'active', + `investment_amount` decimal(10,2) NOT NULL, + `expected_return` decimal(10,2) NOT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `package_id` (`package_id`), + CONSTRAINT `user_packages_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `user_packages_ibfk_2` FOREIGN KEY (`package_id`) REFERENCES `packages` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_products` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `product_id` int NOT NULL, + `assigned_date` date NOT NULL, + `investment_id` int NOT NULL, + `purchase_price` decimal(10,2) NOT NULL, + `cashback_received` decimal(10,2) DEFAULT '0.00', + `purchase_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `status` enum('active','completed','cancelled') DEFAULT 'active', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `product_id` (`product_id`), + CONSTRAINT `user_products_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `user_products_ibfk_2` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_profiles` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `first_name` varchar(50) DEFAULT NULL, + `last_name` varchar(50) DEFAULT NULL, + `date_of_birth` date DEFAULT NULL, + `gender` enum('Male','Female','Other') DEFAULT NULL, + `profile_picture` varchar(255) DEFAULT NULL, + `address` text, + `city` varchar(50) DEFAULT NULL, + `state` varchar(50) DEFAULT NULL, + `zip_code` varchar(20) DEFAULT NULL, + `bio` text, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `user_profiles_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `users` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_type` enum('marketer','agent','admin') DEFAULT 'marketer', + `referred_by` int DEFAULT NULL, + `username` varchar(50) NOT NULL, + `email` varchar(100) NOT NULL, + `country` varchar(2) NOT NULL, + `phone_number` varchar(20) NOT NULL, + `tier` enum('Basic','Premium') DEFAULT 'Basic', + `current_package_id` int DEFAULT NULL, + `package_start_date` datetime DEFAULT NULL, + `package_end_date` datetime DEFAULT NULL, + `package` enum('None','NOVA','SUPERIOR','GOLD') DEFAULT 'None', + `balance` decimal(10,2) DEFAULT '0.00', + `verification_token` varchar(100) DEFAULT NULL, + `reset_token` varchar(100) DEFAULT NULL, + `reset_token_expiry` datetime DEFAULT NULL, + `total_invested` decimal(15,2) DEFAULT '0.00', + `total_earnings` decimal(15,2) DEFAULT '0.00', + `total_deposits` decimal(10,2) DEFAULT '0.00', + `total_withdrawals` decimal(10,2) DEFAULT '0.00', + `rewards` decimal(10,2) DEFAULT '0.00', + `meta_earnings` decimal(10,2) DEFAULT '0.00', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `last_login` timestamp NULL DEFAULT NULL, + `pin_hash` varchar(255) NOT NULL, + `password_hash` varchar(255) NOT NULL, + `currency` varchar(3) DEFAULT 'KES', + `exchange_rate` decimal(10,4) DEFAULT '1.0000', + `referral_code` varchar(20) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`), + UNIQUE KEY `email` (`email`), + UNIQUE KEY `referral_code` (`referral_code`), + KEY `idx_users_email` (`email`), + KEY `idx_users_username` (`username`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_security` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `two_factor_enabled` tinyint(1) DEFAULT '0', + `two_factor_secret` varchar(32) DEFAULT NULL, + `password_reset_token` varchar(100) DEFAULT NULL, + `reset_token_expires` timestamp NULL DEFAULT NULL, + `login_attempts` int DEFAULT '0', + `last_login_attempt` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_id` (`user_id`), + CONSTRAINT `user_security_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `user_settings` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `dark_mode` tinyint(1) DEFAULT '1', + `language` varchar(10) DEFAULT 'en', + `currency` varchar(3) DEFAULT 'KES', + `auto_logout` tinyint(1) DEFAULT '1', + `email_notifications` tinyint(1) DEFAULT '1', + `push_notifications` tinyint(1) DEFAULT '1', + PRIMARY KEY (`id`), + UNIQUE KEY `user_id` (`user_id`), + CONSTRAINT `user_settings_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `whatsapp_channels` ( + `id` int NOT NULL AUTO_INCREMENT, + `channel_name` varchar(100) NOT NULL, + `channel_type` enum('group','channel','tips','elite') NOT NULL, + `whatsapp_link` varchar(500) NOT NULL, + `description` text, + `icon_class` varchar(50) DEFAULT NULL, + `color_class` varchar(50) DEFAULT NULL, + `is_active` tinyint(1) DEFAULT '1', + `member_count` int DEFAULT '0', + `max_capacity` int DEFAULT '1000', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `withdrawal_destinations` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `type` enum('mpesa','bank','airtel','tkash') NOT NULL, + `details` varchar(255) NOT NULL, + `is_default` tinyint(1) DEFAULT '0', + `is_active` tinyint(1) DEFAULT '1', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `withdrawal_destinations_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `withdrawals` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `destination_id` int NOT NULL, + `amount` decimal(15,2) NOT NULL, + `fee` decimal(15,2) NOT NULL DEFAULT '0.00', + `net_amount` decimal(15,2) NOT NULL, + `status` enum('pending','processing','completed','failed','cancelled') DEFAULT 'pending', + `idempotency_key` varchar(255) NOT NULL, + `reference` varchar(100) NOT NULL, + `failure_reason` text, + `processed_at` timestamp NULL DEFAULT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `idempotency_key` (`idempotency_key`), + KEY `user_id` (`user_id`), + KEY `destination_id` (`destination_id`), + CONSTRAINT `withdrawals_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `withdrawals_ibfk_2` FOREIGN KEY (`destination_id`) REFERENCES `withdrawal_destinations` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE ALGORITHM=UNDEFINED +DEFINER=`root`@`localhost` +SQL SECURITY DEFINER +VIEW `user_teams` AS +SELECT + `u`.`id` AS `user_id`, + `u`.`username` AS `username`, + `u`.`referral_code` AS `referral_code`, + COUNT(`r`.`referred_id`) AS `team_size`, + COALESCE(SUM(`ref`.`commission_earned`),0) AS `total_earnings` +FROM + ((`users` `u` + LEFT JOIN `referrals` `r` ON (`u`.`id` = `r`.`referrer_id`)) + LEFT JOIN `referrals` `ref` ON (`u`.`id` = `ref`.`referrer_id` AND `ref`.`status` = 'completed'))) +GROUP BY + `u`.`id`, `u`.`username`, `u`.`referral_code`; diff --git a/jweb/ac1/assets/images b/jweb/ac1/assets/images new file mode 100644 index 0000000000000000000000000000000000000000..5f2e0c26bda53e9908da285433f076f0f7d17a17 --- /dev/null +++ b/jweb/ac1/assets/images @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/jweb/ac1/assets/js/script.js b/jweb/ac1/assets/js/script.js new file mode 100644 index 0000000000000000000000000000000000000000..091218fea5f9226dda3c97dc015bb92d480c3028 --- /dev/null +++ b/jweb/ac1/assets/js/script.js @@ -0,0 +1,14 @@ +document.getElementById("signupForm").addEventListener("submit", function(e) { + e.preventDefault(); + + const password = document.getElementById("password").value; + const confirmPassword = document.getElementById("confirmPassword").value; + + if (password !== confirmPassword) { + alert("Passwords do not match!"); + return; + } + + alert("Account created successfully for Japanese Motors!"); + // Here you can add backend API integration (AJAX/Fetch) +}); diff --git a/jweb/ac1/assets/js/scripts.js b/jweb/ac1/assets/js/scripts.js new file mode 100644 index 0000000000000000000000000000000000000000..b9acd7e1e819c34188aec58f67784fbd6e734ac2 --- /dev/null +++ b/jweb/ac1/assets/js/scripts.js @@ -0,0 +1,29 @@ +// filepath: /jmotors/jmotors/src/assets/js/scripts.js +document.addEventListener('DOMContentLoaded', function() { + // Function to handle logout + function handleLogout() { + fetch('logout.php', { + method: 'POST' + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + window.location.href = 'pages/home.php'; + } else { + alert(data.message); + } + }) + .catch(error => console.error('Error:', error)); + } + + // Event listener for logout button + const logoutButton = document.getElementById('logout-button'); + if (logoutButton) { + logoutButton.addEventListener('click', function(e) { + e.preventDefault(); + handleLogout(); + }); + } + + // Additional JavaScript functionality can be added here +}); \ No newline at end of file diff --git a/jweb/ac1/check_session.php b/jweb/ac1/check_session.php new file mode 100644 index 0000000000000000000000000000000000000000..9cf0d1d300efb859d6464c562925b82bdd73fefc --- /dev/null +++ b/jweb/ac1/check_session.php @@ -0,0 +1,24 @@ + false)); + exit; +} + +// Return user data if logged in +echo json_encode(array( + "logged_in" => true, + "user_id" => $_SESSION['user_id'], + "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'] +)); +?> \ No newline at end of file diff --git a/jweb/ac1/config.php b/jweb/ac1/config.php new file mode 100644 index 0000000000000000000000000000000000000000..5aef1e8729d5dab82be2943b5b3bcce7e8b87c3a --- /dev/null +++ b/jweb/ac1/config.php @@ -0,0 +1,14 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} +?> \ No newline at end of file diff --git a/jweb/ac1/db.php b/jweb/ac1/db.php new file mode 100644 index 0000000000000000000000000000000000000000..f38404182d46d422953bf5ffd2d436ac058c9e72 --- /dev/null +++ b/jweb/ac1/db.php @@ -0,0 +1,111 @@ +conn = null; + + try { + $this->conn = new PDO( + "mysql:host=" . $this->host . ";dbname=" . $this->db_name . ";charset=utf8mb4", + $this->username, + $this->password + ); + $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + } catch(PDOException $exception) { + error_log("Database connection error: " . $exception->getMessage()); + throw new Exception("Database connection failed: " . $exception->getMessage()); + } + + return $this->conn; + } +} + +class SessionManager { + private $db; + + public function __construct($database) { + $this->db = $database->getConnection(); + } + + // Create new session + public function createSession($user_id, $ip_address = null, $user_agent = null) { + $session_id = bin2hex(random_bytes(64)); + $expires_at = date('Y-m-d H:i:s', strtotime('+24 hours')); + + $query = "INSERT INTO user_sessions + SET user_id = :user_id, session_id = :session_id, ip_address = :ip_address, + user_agent = :user_agent, expires_at = :expires_at"; + + $stmt = $this->db->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->bindParam(":session_id", $session_id); + $stmt->bindParam(":ip_address", $ip_address); + $stmt->bindParam(":user_agent", $user_agent); + $stmt->bindParam(":expires_at", $expires_at); + + if ($stmt->execute()) { + return $session_id; + } + return false; + } + + // Validate session + public function validateSession($session_id) { + $query = "SELECT us.*, u.* + FROM user_sessions us + JOIN users u ON us.user_id = u.id + WHERE us.session_id = :session_id + AND us.is_active = 1 + AND us.expires_at > NOW()"; + + $stmt = $this->db->prepare($query); + $stmt->bindParam(":session_id", $session_id); + $stmt->execute(); + + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + // Log activity + public function logActivity($user_id, $activity_type, $description = null, $ip_address = null, $user_agent = null) { + $query = "INSERT INTO user_activity_log + SET user_id = :user_id, activity_type = :activity_type, description = :description, + ip_address = :ip_address, user_agent = :user_agent"; + + $stmt = $this->db->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->bindParam(":activity_type", $activity_type); + $stmt->bindParam(":description", $description); + $stmt->bindParam(":ip_address", $ip_address); + $stmt->bindParam(":user_agent", $user_agent); + + return $stmt->execute(); + } + + // Update last login + public function updateLastLogin($user_id) { + $query = "UPDATE users SET last_login = NOW() WHERE id = :user_id"; + $stmt = $this->db->prepare($query); + $stmt->bindParam(":user_id", $user_id); + return $stmt->execute(); + } +} + +// Initialize database and session manager +try { + $database = new Database(); + $db = $database->getConnection(); + $sessionManager = new SessionManager($database); +} catch(Exception $e) { + error_log("Initialization error: " . $e->getMessage()); + $db = null; + $sessionManager = null; +} +?> \ No newline at end of file diff --git a/jweb/ac1/home.html b/jweb/ac1/home.html new file mode 100644 index 0000000000000000000000000000000000000000..aeb7ccc691a98ace2125d451c69edef2830ca4ea --- /dev/null +++ b/jweb/ac1/home.html @@ -0,0 +1,692 @@ + + + + + + + Japanese Motors — Customer Care + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+
+

+ Submit a Support Request +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +

Upload screenshots or documents that might help us understand your issue

+
+ + +
+
+ +
+

+ Contact Options +

+ +
+

+ Direct WhatsApp Support +

+

Chat directly with our support team for instant assistance

+ + Message Support + +
+ +
+

+ Email Support +

+

Jmotors.com

+

Jmotors.com

+

Response within 2 hours

+
+ +
+

+ Phone Support +

+

+254 700 123 456

+

+254 711 987 654

+

Available 24/7 for urgent issues

+
+ +
+

+ Office Visit +

+

Jmotors Headquarters

+

Nairobi, Kenya

+

By appointment only

+
+
+
+ +
+

+ Frequently Asked Questions +

+ +
+
+

How do I reset my password?

+ +
+
+

To reset your password, go to the login page and click on "Forgot Password". Enter your registered email address and follow the instructions sent to your email. If you don't receive the email within 5 minutes, check your spam folder or contact support.

+
+
+ +
+
+

Why is my withdrawal pending?

+ +
+
+

Withdrawals typically process within 24 hours. If it's taking longer, it might be undergoing security verification. Contact support if pending for more than 48 hours. Make sure you've completed the required 5 uploads in the last 7 days to be eligible for withdrawal.

+
+
+ +
+
+

How do I join the affiliate program?

+ +
+
+

To join our affiliate program, go to your dashboard, click on "Team" and then "Become an Affiliate". Follow the registration process and start inviting members. You need to have been active for at least 30 days and completed 50 successful uploads to qualify.

+
+
+ +
+
+

What are the minimum withdrawal requirements?

+ +
+
+

The minimum withdrawal amount is KES 500. You must also have completed at least 5 successful uploads in the last 7 days to be eligible for withdrawal. There's a 2% processing fee for withdrawals below KES 2,000.

+
+
+ +
+
+

How do I upgrade my account level?

+ +
+
+

Account levels are automatically upgraded based on your activity and earnings. To move from Dormant to Active Marketer, you need to complete 10 uploads and earn at least KES 1,000. Higher levels require more activity and team members.

+
+
+ +
+
+

What should I do if my uploads are rejected?

+ +
+
+

If your uploads are frequently rejected, check the content guidelines in the Meta Uploads section. Make sure your content is original, follows platform rules, and meets the quality standards. If you believe your content was wrongly rejected, contact support with the upload ID.

+
+
+
+ +
+
+

+ Support Hours +

+
+
+ WhatsApp Support: + 24/7 +
+
+ Email Support: + Mon-Sun, 6am-11pm EAT +
+
+ Phone Support: + 24/7 for urgent issues +
+
+ Average Response Time: + Under 30 minutes +
+
+ Ticket Resolution Time: + Within 24 hours +
+
+
+ +
+

+ Support Tickets +

+
+ +

No active support tickets

+
+ +

You can check the status of previous support requests

+
+
+ +
+

+ Community Support +

+

Join our community of marketers to get help from experienced members and share insights:

+ +
+
+

Community Forum

+

Get answers from other marketers in our community forum

+ Visit Forum +
+ +
+

Knowledge Base

+

Browse our comprehensive knowledge base articles

+ Browse Articles +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/inde.php b/jweb/ac1/inde.php new file mode 100644 index 0000000000000000000000000000000000000000..919f905f4da2b4d8fb34eb9e655f82240f13f499 --- /dev/null +++ b/jweb/ac1/inde.php @@ -0,0 +1,341 @@ + + + + + + MetaView - Create Account + + + + +
+
+ +

Japanese Motors

+

Access your financial dashboard

+
+
+
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+ + + + + +
+
+ +
+ +
+ + + + + +
+
+
+ + +
+ + +
+ +

+ + + + + \ No newline at end of file diff --git a/jweb/ac1/index.html b/jweb/ac1/index.html new file mode 100644 index 0000000000000000000000000000000000000000..63961aea3bcf14d87a8450a494848a7d13141657 --- /dev/null +++ b/jweb/ac1/index.html @@ -0,0 +1,626 @@ + + + + + + Account Authentication + + + + + +
+
+
+ +

Account Created Successfully!

+

Redirecting to sign in page...

+
+ +

Create Your Account

+
+
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+
+ +
+ +
+
+
+ + + + +
+
+
+
+
+ + + + +
+
+
+
+
+ + + +
+ Already have an account? Sign In +
+
+
+ + +
+ + + + \ No newline at end of file diff --git a/jweb/ac1/login.php b/jweb/ac1/login.php new file mode 100644 index 0000000000000000000000000000000000000000..a860264c4dd57688debc9a3b5d95de7a44db9cac --- /dev/null +++ b/jweb/ac1/login.php @@ -0,0 +1,110 @@ + false, "message" => "Service temporarily unavailable.")); + exit; +} + +// Get posted data +$input = file_get_contents("php://input"); +$data = json_decode($input); + +if (json_last_error() !== JSON_ERROR_NONE) { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Invalid JSON data.")); + exit; +} + +// Check if data is not empty +if (!empty($data->email) && !empty($data->password)) { + // Prepare query + $query = "SELECT id, username, email, password_hash, tier, package, balance, + total_deposits, total_withdrawals, rewards, account_status, is_active + FROM users + WHERE (username = :credential OR email = :credential) AND is_active = 1"; + + $stmt = $db->prepare($query); + $credential = htmlspecialchars(strip_tags($data->email)); + $stmt->bindParam(":credential", $credential); + + try { + $stmt->execute(); + } catch(PDOException $e) { + error_log("Database error: " . $e->getMessage()); + http_response_code(500); + echo json_encode(array("success" => false, "message" => "Database error occurred.")); + exit; + } + + if ($stmt->rowCount() == 1) { + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + // Check account status + if ($row['account_status'] !== 'active') { + http_response_code(403); + echo json_encode(array("success" => false, "message" => "Account is suspended or pending approval.")); + exit; + } + + // Verify password + if (password_verify($data->password, $row['password_hash'])) { + // Create session + $ip_address = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; + $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'; + $session_id = $sessionManager->createSession($row['id'], $ip_address, $user_agent); + + if ($session_id) { + // Log activity + $sessionManager->logActivity($row['id'], 'login', 'User logged in successfully', $ip_address, $user_agent); + $sessionManager->updateLastLogin($row['id']); + + // Set session variables + $_SESSION['user_id'] = $row['id']; + $_SESSION['username'] = $row['username']; + $_SESSION['email'] = $row['email']; + $_SESSION['tier'] = $row['tier']; + $_SESSION['package'] = $row['package']; + $_SESSION['balance'] = $row['balance']; + $_SESSION['total_deposits'] = $row['total_deposits']; + $_SESSION['total_withdrawals'] = $row['total_withdrawals']; + $_SESSION['rewards'] = $row['rewards']; + $_SESSION['session_id'] = $session_id; + $_SESSION['logged_in'] = true; + $_SESSION['login_time'] = time(); + + http_response_code(200); + echo json_encode(array( + "success" => true, + "message" => "Login successful.", + "redirect" => "src/pages/index.php", + "user_data" => [ + "user_id" => $row['id'], + "username" => $row['username'], + "email" => $row['email'], + "tier" => $row['tier'], + "package" => $row['package'], + "balance" => $row['balance'] + ] + )); + } else { + http_response_code(500); + echo json_encode(array("success" => false, "message" => "Session creation failed.")); + } + } else { + http_response_code(401); + echo json_encode(array("success" => false, "message" => "Invalid password.")); + } + } else { + http_response_code(404); + echo json_encode(array("success" => false, "message" => "User not found or account inactive.")); + } +} else { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Unable to login. Data is incomplete.")); +} +?> \ No newline at end of file diff --git a/jweb/ac1/login_form.php b/jweb/ac1/login_form.php new file mode 100644 index 0000000000000000000000000000000000000000..1d17831e509ead921b85e2d8ce156965785f38b9 --- /dev/null +++ b/jweb/ac1/login_form.php @@ -0,0 +1,38 @@ + + + + + Login + + + + +
+

Login

+ " . $_SESSION['error'] . "

"; + unset($_SESSION['error']); + } + ?> +
+ + +
+
+ + +
+ +
+ + diff --git a/jweb/ac1/logout.php b/jweb/ac1/logout.php new file mode 100644 index 0000000000000000000000000000000000000000..eb3a764161e7c28d3601e694c25bc67101244bf3 --- /dev/null +++ b/jweb/ac1/logout.php @@ -0,0 +1,52 @@ +logActivity($user_id, 'logout', 'User logged out', $ip_address, $user_agent); + + // Deactivate session in database + if ($db) { + $query = "UPDATE user_sessions SET is_active = 0 WHERE session_id = :session_id"; + $stmt = $db->prepare($query); + $stmt->bindParam(":session_id", $session_id); + $stmt->execute(); + } + } catch(Exception $e) { + error_log("Logout error: " . $e->getMessage()); + } +} + +// Clear all session variables +$_SESSION = array(); + +// Destroy the session cookie +if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); +} + +// Destroy the session +session_destroy(); + +// Return JSON response +header('Content-Type: application/json'); +echo json_encode(array( + "success" => true, + "message" => "Logged out successfully", + "redirect" => "../index.html" +)); +exit; +?> \ No newline at end of file diff --git a/jweb/ac1/setup_database.php b/jweb/ac1/setup_database.php new file mode 100644 index 0000000000000000000000000000000000000000..848d3655d46c529534f25f8e3a6a0908d0e7f975 --- /dev/null +++ b/jweb/ac1/setup_database.php @@ -0,0 +1,108 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Select the database + $pdo->exec("USE $db_name"); + echo "Using database: $db_name
"; + + // Users table + $sql = "CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + tier ENUM('Basic', 'Premium', 'Gold') DEFAULT 'Basic', + package ENUM('NOVA', 'SUPERIOR', 'GOLD') DEFAULT 'NOVA', + balance DECIMAL(10, 2) DEFAULT 0.00, + total_deposits DECIMAL(10, 2) DEFAULT 0.00, + total_withdrawals DECIMAL(10, 2) DEFAULT 0.00, + rewards DECIMAL(10, 2) DEFAULT 0.00, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + )"; + $pdo->exec($sql); + echo "Users table created successfully
"; + + // Transactions table + $sql = "CREATE TABLE IF NOT EXISTS transactions ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + type ENUM('deposit', 'withdrawal', 'bonus', 'purchase', 'transfer', 'earning') NOT NULL, + amount DECIMAL(10, 2) NOT NULL, + description VARCHAR(255), + status ENUM('pending', 'completed', 'failed') DEFAULT 'pending', + reference VARCHAR(100), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )"; + $pdo->exec($sql); + echo "Transactions table created successfully
"; + + // Packages table + $sql = "CREATE TABLE IF NOT EXISTS packages ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(50) NOT NULL, + price DECIMAL(10, 2) NOT NULL, + award DECIMAL(10, 2) NOT NULL, + features TEXT, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )"; + $pdo->exec($sql); + echo "Packages table created successfully
"; + + // Insert default packages + $sql = "INSERT IGNORE INTO packages (name, price, award, features) VALUES + ('NOVA', 1000.00, 3000.00, 'Auto Deposit'), + ('SUPERIOR', 2500.00, 7500.00, 'Auto Deposit'), + ('GOLD', 5500.00, 16500.00, 'Auto Deposit')"; + $pdo->exec($sql); + echo "Default packages inserted successfully
"; + + // Create a test user if none exists + $sql = "SELECT COUNT(*) FROM users"; + $result = $pdo->query($sql); + $count = $result->fetchColumn(); + + if ($count == 0) { + $password_hash = password_hash('password123', PASSWORD_DEFAULT); + $sql = "INSERT INTO users (username, email, password_hash, tier, package, balance, total_deposits, total_withdrawals, rewards) + VALUES ('testuser', 'test@example.com', '$password_hash', 'Premium', 'NOVA', 5000.00, 10000.00, 5000.00, 1000.00)"; + $pdo->exec($sql); + echo "Test user created successfully
"; + + // Add some sample transactions + $user_id = $pdo->lastInsertId(); + $transactions = [ + ['user_id' => $user_id, 'type' => 'deposit', 'amount' => 1000.00, 'description' => 'Initial deposit', 'status' => 'completed'], + ['user_id' => $user_id, 'type' => 'withdrawal', 'amount' => 500.00, 'description' => 'Cash withdrawal', 'status' => 'completed'], + ['user_id' => $user_id, 'type' => 'bonus', 'amount' => 100.00, 'description' => 'Welcome bonus', 'status' => 'completed'], + ['user_id' => $user_id, 'type' => 'purchase', 'amount' => 250.00, 'description' => 'Product purchase', 'status' => 'completed'], + ['user_id' => $user_id, 'type' => 'earning', 'amount' => 150.00, 'description' => 'Daily earnings', 'status' => 'completed'] + ]; + + foreach ($transactions as $transaction) { + $sql = "INSERT INTO transactions (user_id, type, amount, description, status) + VALUES (:user_id, :type, :amount, :description, :status)"; + $stmt = $pdo->prepare($sql); + $stmt->execute($transaction); + } + echo "Sample transactions created successfully
"; + } + + echo "Database setup completed successfully!"; + +} catch (PDOException $e) { + die("Database error: " . $e->getMessage()); +} +?> \ No newline at end of file diff --git a/jweb/ac1/shake it.html b/jweb/ac1/shake it.html new file mode 100644 index 0000000000000000000000000000000000000000..f96d05571ad6e4b7be89c0aa05c212e90aac1a14 --- /dev/null +++ b/jweb/ac1/shake it.html @@ -0,0 +1,1313 @@ + + + + + + MetaWave Marketing Platform + + + + + + + + +
+
+ + +
+
+ +
+
+
+
Mi
+ Milly v + Premium +
+
+ +
+
01:42 PM EAT, September 08, 2025
+
+
+ +
+
+ +
+
+

Welcome back, Milly!

+

User | Meta Package: None

+
+
+ +
+
Mi
+
+

Milly

+

Joined: March 15, 2024 | Level: Beginner

+
+
+ +
+ + +
+

Investment Packages

+
Grow your funds with our automated investment solutions.
+
+
+
+ +
NOVA 1,000.00 KES
+
+
    +
  • Auto Deposit
  • +
  • Auto Withdrawal
  • +
  • Instant Cashback
  • +
+
Award Returns 3,000.00 KES
+ +
+
+
+ +
SUPERIOR 2,500.00 KES
+
+
    +
  • Auto Deposit
  • +
  • Auto Withdrawal
  • +
  • Instant Cashback
  • +
+
Award Returns 7,500.00 KES
+ +
+
+
+ +
GOLD 5,500.00 KES
+
+
    +
  • Auto Deposit
  • +
  • Auto Withdrawal
  • +
  • Instant Cashback
  • +
+
Award Returns 16,500.00 KES
+ +
+
+
+
+ +
+
+
Meta Balance
+
+
0 KES
+
+ + +
+
+
+
+ + +
+

Transaction History

+ +
+ +
+
+
Total Withdrawals
+
0 KES
+
+
+
Total Deposits
+
0 KES
+
+
+
Meta Earnings
+
0 KES
+
+
+
Rewards
+
0 KES
+
+
+ +
+
+

Welcome back, Milly!

+

Meta Balance

+

0 KES

+ +
+
+

Total Deposits

+

0 KES

+ +
+
+

Total Withdrawals

+

0 KES

+ +
+
+

Meta Earnings

+

0 KES

+
+
+

Rewards

+

0 KES

+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jweb/ac1/signup.php b/jweb/ac1/signup.php new file mode 100644 index 0000000000000000000000000000000000000000..c167c67d1cf54c648c01098a12f463b47e0ea39a --- /dev/null +++ b/jweb/ac1/signup.php @@ -0,0 +1,162 @@ + false, "message" => "Service temporarily unavailable.")); + exit; +} + +// Get posted data +$input = file_get_contents("php://input"); +$data = json_decode($input); + +if (json_last_error() !== JSON_ERROR_NONE) { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Invalid JSON data.")); + exit; +} + +// Check if data is not empty +if ( + !empty($data->username) && + !empty($data->email) && + !empty($data->country) && + !empty($data->phone) && + !empty($data->password) && + !empty($data->confirm_password) +) { + // Validate input + if ($data->password !== $data->confirm_password) { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Passwords do not match.")); + exit; + } + + if (strlen($data->password) < 6) { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Password must be at least 6 characters.")); + exit; + } + + if (!filter_var($data->email, FILTER_VALIDATE_EMAIL)) { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Invalid email format.")); + exit; + } + + // Check if user already exists + $query = "SELECT id FROM users WHERE username = :username OR email = :email"; + $stmt = $db->prepare($query); + $stmt->bindParam(":username", $data->username); + $stmt->bindParam(":email", $data->email); + + try { + $stmt->execute(); + } catch(PDOException $e) { + error_log("Database error: " . $e->getMessage()); + http_response_code(500); + echo json_encode(array("success" => false, "message" => "Database error occurred.")); + exit; + } + + if ($stmt->rowCount() > 0) { + http_response_code(409); + echo json_encode(array("success" => false, "message" => "User already exists with this username or email.")); + exit; + } + + // Hash password + $hashed_password = password_hash($data->password, PASSWORD_DEFAULT); + + // Generate referral code + $referral_code = strtoupper(substr($data->username, 0, 3) . bin2hex(random_bytes(3))); + + // Insert new user + $query = "INSERT INTO users + SET username = :username, email = :email, country = :country, + phone_number = :phone, password_hash = :password, referral_code = :referral_code, + user_type = 'marketer', tier = 'Basic', package = 'None', balance = 0.00, + total_deposits = 0.00, total_withdrawals = 0.00, rewards = 0.00, meta_earnings = 0.00, + pin_hash = '', is_active = 1, account_status = 'active'"; + + $stmt = $db->prepare($query); + + // Sanitize and bind values + $username = htmlspecialchars(strip_tags($data->username)); + $email = htmlspecialchars(strip_tags($data->email)); + $country = htmlspecialchars(strip_tags($data->country)); + $phone = htmlspecialchars(strip_tags($data->phone)); + + $stmt->bindParam(":username", $username); + $stmt->bindParam(":email", $email); + $stmt->bindParam(":country", $country); + $stmt->bindParam(":phone", $phone); + $stmt->bindParam(":password", $hashed_password); + $stmt->bindParam(":referral_code", $referral_code); + + // Execute query + try { + if ($stmt->execute()) { + $user_id = $db->lastInsertId(); + + // Create session + $ip_address = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; + $user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'; + $session_id = $sessionManager->createSession($user_id, $ip_address, $user_agent); + + if ($session_id) { + // Log activity + $sessionManager->logActivity($user_id, 'registration', 'User registered successfully', $ip_address, $user_agent); + $sessionManager->updateLastLogin($user_id); + + // Set session variables + $_SESSION['user_id'] = $user_id; + $_SESSION['username'] = $username; + $_SESSION['email'] = $email; + $_SESSION['tier'] = 'Basic'; + $_SESSION['package'] = 'None'; + $_SESSION['balance'] = 0.00; + $_SESSION['total_deposits'] = 0.00; + $_SESSION['total_withdrawals'] = 0.00; + $_SESSION['rewards'] = 0.00; + $_SESSION['session_id'] = $session_id; + $_SESSION['logged_in'] = true; + $_SESSION['login_time'] = time(); + + http_response_code(201); + echo json_encode(array( + "success" => true, + "message" => "User registered successfully.", + "redirect" => "src/pages/index.php", + "user_data" => [ + "user_id" => $user_id, + "username" => $username, + "email" => $email, + "tier" => "Basic" + ] + )); + } else { + throw new Exception("Failed to create session"); + } + } else { + http_response_code(503); + echo json_encode(array("success" => false, "message" => "Unable to create user.")); + } + } catch(PDOException $e) { + error_log("Insert error: " . $e->getMessage()); + http_response_code(500); + echo json_encode(array("success" => false, "message" => "Database error occurred.")); + } catch(Exception $e) { + error_log("Session error: " . $e->getMessage()); + http_response_code(500); + echo json_encode(array("success" => false, "message" => "Session creation failed.")); + } +} else { + http_response_code(400); + echo json_encode(array("success" => false, "message" => "Unable to create user. Data is incomplete.")); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/admin/admin-approval.php b/jweb/ac1/src/admin/admin-approval.php new file mode 100644 index 0000000000000000000000000000000000000000..1729ffc3dab225cc1e297e51b086125d9a43cf2b --- /dev/null +++ b/jweb/ac1/src/admin/admin-approval.php @@ -0,0 +1,190 @@ + + + + + + + + Admin - Approval System + + + + + +
+

Admin Approval System

+ + +
+ +
+ + + +
+ +
+ + + +
+
+

Pending Agents

+

+
+
+

Pending Ads

+

+
+
+

Total Revenue

+

KES

+
+
+

Pending Revenue

+

KES

+
+
+ + +
+
+

Pending Advertisements

+
+
+ +

No pending advertisements

+ + +
+
+
+

+

By:

+

Budget: KES

+

Platform:

+ +

Agent:

+ +
+
+ + +
+
+
+ + +
+
+ + +
+
+

Pending Agent Applications

+
+
+ +

No pending agent applications

+ + +
+
+
+

+

Phone:

+

Location:

+

Sponsor:

+
+
+ + +
+
+
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/jweb/ac1/src/admin/payments.php b/jweb/ac1/src/admin/payments.php new file mode 100644 index 0000000000000000000000000000000000000000..2e458ce033de7f6fefe0b366461d885c29304eb0 --- /dev/null +++ b/jweb/ac1/src/admin/payments.php @@ -0,0 +1,77 @@ +getPendingPayments(); + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $payment_id = $_POST['payment_id']; + $action = $_POST['action']; + $notes = $_POST['notes'] ?? ''; + + if ($action === 'verify') { + $result = $mainAccount->verifyPayment($payment_id, $_SESSION['admin_id'], $notes); + } else { + $result = $mainAccount->rejectPayment($payment_id, $_SESSION['admin_id'], $notes); + } + + if ($result['success']) { + $message = $action === 'verify' ? "Payment verified successfully!" : "Payment rejected!"; + } else { + $error = $result['error']; + } +} +?> + + +
+

Payment Verification

+ +
+ +
+
+

Payment #

+ Pending +
+ +
+

User:

+

Amount: KES

+

Phone:

+

M-Pesa Code:

+

Submitted:

+
+ + +
+ Payment Proof +
+ + +
+ + +
+ + +
+
+
+ +
+
\ No newline at end of file diff --git a/jweb/ac1/src/api/agent-functions.php b/jweb/ac1/src/api/agent-functions.php new file mode 100644 index 0000000000000000000000000000000000000000..725c2325ba262c693ebf52e56adca82243320e05 --- /dev/null +++ b/jweb/ac1/src/api/agent-functions.php @@ -0,0 +1,156 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch(PDOException $e) { + die("Connection failed: " . $e->getMessage()); + } + } + return $conn; +} + +// Get pending agents for a sponsor +function getPendingAgents($sponsorId) { + $conn = getDBConnection(); + $stmt = $conn->prepare(" + SELECT aa.*, u.username, u.email + FROM agent_applications aa + JOIN users u ON aa.user_id = u.id + WHERE aa.sponsor_id = ? AND aa.status = 'pending' + ORDER BY aa.applied_at DESC + "); + $stmt->execute([$sponsorId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +// Get agent statistics for a sponsor +function getAgentStats($sponsorId) { + $conn = getDBConnection(); + + // Initialize default stats + $stats = [ + 'pending' => 0, + 'approved' => 0, + 'rejected' => 0, + 'documents_needed' => 0, + 'total_applications' => 0, + 'commission_this_month' => 0.00, + 'total_commission' => 0.00 + ]; + + try { + // Count applications by status + $stmt = $conn->prepare(" + SELECT + status, + COUNT(*) as count + FROM agent_applications + WHERE sponsor_id = ? + GROUP BY status + "); + $stmt->execute([$sponsorId]); + $statusCounts = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($statusCounts as $row) { + $stats[$row['status']] = $row['count']; + $stats['total_applications'] += $row['count']; + } + + // Get commission data + $stmt = $conn->prepare(" + SELECT + COALESCE(SUM(CASE WHEN MONTH(created_at) = MONTH(CURRENT_DATE()) THEN commission_amount ELSE 0 END), 0) as commission_this_month, + COALESCE(SUM(commission_amount), 0) as total_commission + FROM commissions + WHERE sponsor_id = ? AND status = 'paid' + "); + $stmt->execute([$sponsorId]); + $commissionData = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($commissionData) { + $stats['commission_this_month'] = $commissionData['commission_this_month']; + $stats['total_commission'] = $commissionData['total_commission']; + } + } catch (Exception $e) { + error_log("Error getting agent stats: " . $e->getMessage()); + } + + return $stats; +} + +// Review agent application (admin function) +function reviewAgentApplication($agentId, $adminId, $status, $notes = '') { + $conn = getDBConnection(); + + try { + $conn->beginTransaction(); + + // Update agent application + $stmt = $conn->prepare(" + UPDATE agent_applications + SET status = ?, reviewed_by = ?, reviewed_at = NOW(), review_notes = ? + WHERE id = ? + "); + $stmt->execute([$status, $adminId, $notes, $agentId]); + + // If approved, update user type to agent + if ($status === 'approved') { + $stmt = $conn->prepare(" + UPDATE users u + JOIN agent_applications aa ON u.id = aa.user_id + SET u.user_type = 'agent' + WHERE aa.id = ? + "); + $stmt->execute([$agentId]); + } + + $conn->commit(); + return true; + } catch (Exception $e) { + $conn->rollBack(); + error_log("Error reviewing agent: " . $e->getMessage()); + return false; + } +} + +// Search agents +function searchAgents($sponsorId, $searchTerm) { + $conn = getDBConnection(); + $searchTerm = "%$searchTerm%"; + + $stmt = $conn->prepare(" + SELECT aa.*, u.username, u.email + FROM agent_applications aa + JOIN users u ON aa.user_id = u.id + WHERE aa.sponsor_id = ? + AND (aa.full_name LIKE ? OR aa.phone LIKE ? OR u.username LIKE ? OR aa.id LIKE ?) + ORDER BY aa.applied_at DESC + "); + $stmt->execute([$sponsorId, $searchTerm, $searchTerm, $searchTerm, $searchTerm]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); +} + +// Get agent details for review +function getAgentDetails($agentId) { + $conn = getDBConnection(); + $stmt = $conn->prepare(" + SELECT aa.*, u.username, u.email, u.created_at as user_joined + FROM agent_applications aa + JOIN users u ON aa.user_id = u.id + WHERE aa.id = ? + "); + $stmt->execute([$agentId]); + return $stmt->fetch(PDO::FETCH_ASSOC); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/agent_claim.php b/jweb/ac1/src/api/agent_claim.php new file mode 100644 index 0000000000000000000000000000000000000000..3c51f987987337c6e3eb44e9a678a985ff54560b --- /dev/null +++ b/jweb/ac1/src/api/agent_claim.php @@ -0,0 +1,195 @@ +conn = $db; + } + + // Create new claim + public function create() { + try { + $query = "INSERT INTO " . $this->table_name . " + (user_id, username, email, claim_type, amount, description, evidence_file) + VALUES (:user_id, :username, :email, :claim_type, :amount, :description, :evidence_file)"; + + $stmt = $this->conn->prepare($query); + + // Sanitize inputs + $this->user_id = htmlspecialchars(strip_tags($this->user_id)); + $this->username = htmlspecialchars(strip_tags($this->username)); + $this->email = htmlspecialchars(strip_tags($this->email)); + $this->claim_type = htmlspecialchars(strip_tags($this->claim_type)); + $this->amount = htmlspecialchars(strip_tags($this->amount)); + $this->description = htmlspecialchars(strip_tags($this->description)); + $this->evidence_file = htmlspecialchars(strip_tags($this->evidence_file)); + + // Bind parameters + $stmt->bindParam(":user_id", $this->user_id); + $stmt->bindParam(":username", $this->username); + $stmt->bindParam(":email", $this->email); + $stmt->bindParam(":claim_type", $this->claim_type); + $stmt->bindParam(":amount", $this->amount); + $stmt->bindParam(":description", $this->description); + $stmt->bindParam(":evidence_file", $this->evidence_file); + + if ($stmt->execute()) { + return $this->conn->lastInsertId(); + } + return false; + + } catch (PDOException $exception) { + error_log("Create Claim Error: " . $exception->getMessage()); + return false; + } + } + + // Get claims by user ID + public function getClaimsByUser($user_id, $status = null) { + try { + $query = "SELECT * FROM " . $this->table_name . " WHERE user_id = :user_id"; + + if ($status) { + $query .= " AND status = :status"; + } + + $query .= " ORDER BY created_at DESC"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + + if ($status) { + $stmt->bindParam(":status", $status); + } + + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + + } catch (PDOException $exception) { + error_log("Get Claims Error: " . $exception->getMessage()); + return []; + } + } + + // Get claim by ID + public function getClaimById($id) { + try { + $query = "SELECT ac.*, u.full_name, u.phone + FROM " . $this->table_name . " ac + JOIN users u ON ac.user_id = u.id + WHERE ac.id = :id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":id", $id); + $stmt->execute(); + + return $stmt->fetch(PDO::FETCH_ASSOC); + + } catch (PDOException $exception) { + error_log("Get Claim Error: " . $exception->getMessage()); + return false; + } + } + + // Update claim status + public function updateStatus($id, $status, $approved_by = null, $rejection_reason = null) { + try { + $query = "UPDATE " . $this->table_name . " + SET status = :status, + updated_at = CURRENT_TIMESTAMP"; + + if ($status == 'approved') { + $query .= ", approved_at = CURRENT_TIMESTAMP, approved_by = :approved_by"; + } + + if ($status == 'rejected' && $rejection_reason) { + $query .= ", rejection_reason = :rejection_reason"; + } + + $query .= " WHERE id = :id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":status", $status); + $stmt->bindParam(":id", $id); + + if ($status == 'approved') { + $stmt->bindParam(":approved_by", $approved_by); + } + + if ($status == 'rejected' && $rejection_reason) { + $stmt->bindParam(":rejection_reason", $rejection_reason); + } + + return $stmt->execute(); + + } catch (PDOException $exception) { + error_log("Update Status Error: " . $exception->getMessage()); + return false; + } + } + + // Get claim statistics for user + public function getClaimStatistics($user_id) { + try { + $query = "SELECT + COUNT(*) as total_claims, + SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as approved_claims, + SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_claims, + SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_claims, + SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) as processing_claims, + SUM(CASE WHEN status = 'approved' THEN amount ELSE 0 END) as approved_amount, + SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount, + SUM(CASE WHEN status = 'processing' THEN amount ELSE 0 END) as processing_amount, + SUM(amount) as total_amount + FROM " . $this->table_name . " + WHERE user_id = :user_id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->execute(); + + return $stmt->fetch(PDO::FETCH_ASSOC); + + } catch (PDOException $exception) { + error_log("Statistics Error: " . $exception->getMessage()); + return []; + } + } + + // Check if user has pending claims + public function hasPendingClaims($user_id) { + try { + $query = "SELECT COUNT(*) as pending_count + FROM " . $this->table_name . " + WHERE user_id = :user_id AND status = 'pending'"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->execute(); + + $result = $stmt->fetch(PDO::FETCH_ASSOC); + return $result['pending_count'] > 0; + + } catch (PDOException $exception) { + error_log("Pending Check Error: " . $exception->getMessage()); + return false; + } + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/check_session.php b/jweb/ac1/src/api/check_session.php new file mode 100644 index 0000000000000000000000000000000000000000..9cf0d1d300efb859d6464c562925b82bdd73fefc --- /dev/null +++ b/jweb/ac1/src/api/check_session.php @@ -0,0 +1,24 @@ + false)); + exit; +} + +// Return user data if logged in +echo json_encode(array( + "logged_in" => true, + "user_id" => $_SESSION['user_id'], + "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'] +)); +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/config.php b/jweb/ac1/src/api/config.php new file mode 100644 index 0000000000000000000000000000000000000000..cbca7d35c5878620636d99f11ac83e9f48f2cb44 --- /dev/null +++ b/jweb/ac1/src/api/config.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/jweb/ac1/src/api/db.php b/jweb/ac1/src/api/db.php new file mode 100644 index 0000000000000000000000000000000000000000..086e9e0c0103229dfc4513b0e6ce771c690fc7a2 --- /dev/null +++ b/jweb/ac1/src/api/db.php @@ -0,0 +1,233 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch (PDOException $e) { + error_log("Database connection failed: " . $e->getMessage()); + // For demo purposes, we'll create a dummy PDO object to prevent errors + class DummyPDO { + public function prepare($sql) { return new DummyPDOStatement(); } + public function exec($sql) { return true; } + public function lastInsertId() { return 1; } + public function beginTransaction() { return true; } + public function commit() { return true; } + public function rollBack() { return true; } + } + class DummyPDOStatement { + public function execute($params) { return true; } + public function fetch($mode) { return []; } + public function fetchAll($mode) { return []; } + } + $pdo = new DummyPDO(); +} +// User class to handle user operations +class User { + private $conn; + private $table_name = "users"; + + public $id; + public $username; + public $email; + public $password_hash; + public $tier; + public $package; + public $balance; + public $total_deposits; + public $total_withdrawals; + public $rewards; + + public function __construct($db) { + $this->conn = $db; + } + + // Get user by ID + public function getUserById($id) { + $query = "SELECT * FROM " . $this->table_name . " WHERE id = ? LIMIT 0,1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $id); + $stmt->execute(); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if($row) { + $this->id = $row['id']; + $this->username = $row['username']; + $this->email = $row['email']; + $this->tier = $row['tier']; + $this->package = $row['package']; + $this->balance = $row['balance']; + $this->total_deposits = $row['total_deposits']; + $this->total_withdrawals = $row['total_withdrawals']; + $this->rewards = $row['rewards']; + return true; + } + return false; + } + + // Get user by username + public function getUserByUsername($username) { + $query = "SELECT * FROM " . $this->table_name . " WHERE username = ? LIMIT 0,1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $username); + $stmt->execute(); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if($row) { + $this->id = $row['id']; + $this->username = $row['username']; + $this->email = $row['email']; + $this->tier = $row['tier']; + $this->package = $row['package']; + $this->balance = $row['balance']; + $this->total_deposits = $row['total_deposits']; + $this->total_withdrawals = $row['total_withdrawals']; + $this->rewards = $row['rewards']; + return true; + } + return false; + } + + // Update user balance + public function updateBalance($amount) { + $query = "UPDATE " . $this->table_name . " SET balance = balance + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->balance += $amount; + return true; + } + return false; + } + + // Update user deposits + public function updateDeposits($amount) { + $query = "UPDATE " . $this->table_name . " SET total_deposits = total_deposits + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->total_deposits += $amount; + return true; + } + return false; + } + + // Update user withdrawals + public function updateWithdrawals($amount) { + $query = "UPDATE " . $this->table_name . " SET total_withdrawals = total_withdrawals + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->total_withdrawals += $amount; + return true; + } + return false; + } + + // Update user rewards + public function updateRewards($amount) { + $query = "UPDATE " . $this->table_name . " SET rewards = rewards + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->rewards += $amount; + return true; + } + return false; + } +} + +// Transaction class to handle transactions +class Transaction { + private $conn; + private $table_name = "transactions"; + + public $id; + public $user_id; + public $type; + public $amount; + public $description; + public $status; + public $reference; + public $created_at; + + public function __construct($db) { + $this->conn = $db; + } + + // Create a new transaction + public function create() { + $query = "INSERT INTO " . $this->table_name . " + SET user_id=:user_id, type=:type, amount=:amount, + description=:description, status=:status, reference=:reference"; + + $stmt = $this->conn->prepare($query); + + // Sanitize inputs + $this->user_id = htmlspecialchars(strip_tags($this->user_id)); + $this->type = htmlspecialchars(strip_tags($this->type)); + $this->amount = htmlspecialchars(strip_tags($this->amount)); + $this->description = htmlspecialchars(strip_tags($this->description)); + $this->status = htmlspecialchars(strip_tags($this->status)); + $this->reference = htmlspecialchars(strip_tags($this->reference)); + + // Bind values + $stmt->bindParam(":user_id", $this->user_id); + $stmt->bindParam(":type", $this->type); + $stmt->bindParam(":amount", $this->amount); + $stmt->bindParam(":description", $this->description); + $stmt->bindParam(":status", $this->status); + $stmt->bindParam(":reference", $this->reference); + + if($stmt->execute()) { + return true; + } + return false; + } + + // Get transactions by user ID + public function getTransactionsByUserId($user_id, $limit = 10) { + $query = "SELECT * FROM " . $this->table_name . " + WHERE user_id = ? + ORDER BY created_at DESC + LIMIT ?"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->bindParam(2, $limit, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt; + } + + // Get transactions by type + public function getTransactionsByType($user_id, $type, $limit = 10) { + $query = "SELECT * FROM " . $this->table_name . " + WHERE user_id = ? AND type = ? + ORDER BY created_at DESC + LIMIT ?"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->bindParam(2, $type); + $stmt->bindParam(3, $limit, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt; + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/deposit.php b/jweb/ac1/src/api/deposit.php new file mode 100644 index 0000000000000000000000000000000000000000..89ce8948a6190ac8ad8d8d5fbc3fd028c79061c4 --- /dev/null +++ b/jweb/ac1/src/api/deposit.php @@ -0,0 +1,52 @@ + false, 'message' => 'Not logged in']); + exit; +} + +// Get JSON input +$input = json_decode(file_get_contents('php://input'), true); + +if (!isset($input['amount']) || !isset($input['method'])) { + echo json_encode(['success' => false, 'message' => 'Invalid input']); + exit; +} + +// Include database connection +require_once '../../db.php'; +require_once '../classes/User.php'; +require_once '../classes/Transaction.php'; + +$database = new Database(); +$db = $database->getConnection(); +$user = new User($db); +$transaction = new Transaction($db); + +if ($user->getUserByUsername($_SESSION['username'])) { + // Process deposit + $amount = floatval($input['amount']); + $method = $input['method']; + + // Add amount to balance and deposits + $user->updateBalance($amount); + $user->updateDeposits($amount); + + // Create transaction record + $transaction->user_id = $user->id; + $transaction->type = 'deposit'; + $transaction->amount = $amount; + $transaction->description = "Deposit via $method"; + $transaction->status = 'completed'; + + if ($transaction->create()) { + echo json_encode(['success' => true, 'message' => 'Deposit successful']); + } else { + echo json_encode(['success' => false, 'message' => 'Failed to record transaction']); + } +} else { + echo json_encode(['success' => false, 'message' => 'User not found']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/fetch_tokens.php b/jweb/ac1/src/api/fetch_tokens.php new file mode 100644 index 0000000000000000000000000000000000000000..aee550a630ea0a147e71289fcc7d7f30009aa5e8 --- /dev/null +++ b/jweb/ac1/src/api/fetch_tokens.php @@ -0,0 +1,25 @@ + false, 'message' => 'Not logged in']); + exit; +} + +$user_id = $_SESSION['user_id']; + +$sql = "SELECT id, name, token, permissions, expires_at, status, created_at + FROM access_tokens + WHERE user_id = ? ORDER BY created_at DESC"; +$stmt = $conn->prepare($sql); +$stmt->bind_param("i", $user_id); +$stmt->execute(); +$result = $stmt->get_result(); + +$tokens = []; +while ($row = $result->fetch_assoc()) { + $tokens[] = $row; +} + +echo json_encode(['success' => true, 'tokens' => $tokens]); diff --git a/jweb/ac1/src/api/generate-package-page.php b/jweb/ac1/src/api/generate-package-page.php new file mode 100644 index 0000000000000000000000000000000000000000..31f52908c83ba63bc8e23d0c7a7fa80a77c4eeee --- /dev/null +++ b/jweb/ac1/src/api/generate-package-page.php @@ -0,0 +1,456 @@ +prepare("SELECT * FROM products WHERE name = ?"); +$stmt->execute([$package_name]); +$package = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$package) { + die("Package not found."); +} + +// Check if user owns this package +$user_id = $_SESSION['user_id']; +$stmt = $pdo->prepare("SELECT * FROM user_products WHERE user_id = ? AND product_id = ?"); +$stmt->execute([$user_id, $package['id']]); +$user_has_package = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$user_has_package) { + die("You do not have access to this package."); +} + +// Generate HTML for the package page +$page_title = $package['name']; +$package_content = generatePackageContent($package['name']); + +// Function to generate specific content based on package +function generatePackageContent($package_name) { + switch ($package_name) { + case 'Diamond Package': + return ' +
+

💎 Diamond Package Benefits

+ +
+

Your Diamond Package Stats

+
+
+

Total Earnings

+

KES 15,240.00

+
+
+

Active Investments

+

3

+
+
+
+
+ '; + + case 'Starlight Bundle': + return ' +
+

✨ Starlight Bundle Benefits

+ +
+ '; + + // Add more cases for other packages + + default: + return ' +
+

' . htmlspecialchars($package_name) . '

+

Welcome to your package dashboard. Here you can monitor your investments and benefits.

+
+ '; + } +} + +// Output the HTML page +?> + + + + + + <?php echo htmlspecialchars($page_title); ?> - Japanese Motors + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+
+ + +
+
+

Package Actions

+ + + +
+ +
+

Performance Metrics

+
+
+

Return on Investment

+
+
+
+

75%

+
+
+

Package Utilization

+
+
+
+

60%

+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/jweb/ac1/src/api/generate_token.php b/jweb/ac1/src/api/generate_token.php new file mode 100644 index 0000000000000000000000000000000000000000..7712f0f78b88eaba00cbddb9a5361a0b97138031 --- /dev/null +++ b/jweb/ac1/src/api/generate_token.php @@ -0,0 +1,35 @@ + false, 'message' => 'Not logged in']); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $user_id = $_SESSION['user_id']; + $name = trim($_POST['name']); + $permissions = isset($_POST['permissions']) ? implode(',', $_POST['permissions']) : ''; + $expiry_days = (int) $_POST['expiry']; + + // Generate random token + $token = 'mw_ak_' . bin2hex(random_bytes(16)); + + // Expiry date + $expires_at = date('Y-m-d H:i:s', strtotime("+$expiry_days days")); + + $sql = "INSERT INTO access_tokens (user_id, name, token, permissions, expires_at) VALUES (?, ?, ?, ?, ?)"; + $stmt = $conn->prepare($sql); + $stmt->bind_param("issss", $user_id, $name, $token, $permissions, $expires_at); + + if ($stmt->execute()) { + echo json_encode([ + 'success' => true, + 'token' => $token, + 'expires_at' => $expires_at + ]); + } else { + echo json_encode(['success' => false, 'message' => 'Database error']); + } +} diff --git a/jweb/ac1/src/api/get-agent.php b/jweb/ac1/src/api/get-agent.php new file mode 100644 index 0000000000000000000000000000000000000000..5f5f7fcb42a05cf785df923929818452ff78a2da --- /dev/null +++ b/jweb/ac1/src/api/get-agent.php @@ -0,0 +1,38 @@ + 'Unauthorized']); + exit; +} + +// Include agent functions +require_once 'agent-functions.php'; + +if (isset($_GET['id'])) { + $agentId = intval($_GET['id']); + $agent = getAgentDetails($agentId); + + if ($agent) { + header('Content-Type: application/json'); + echo json_encode($agent); + } else { + // Return dummy data for testing + $dummyAgent = [ + 'id' => $agentId, + 'full_name' => 'Test Agent ' . $agentId, + 'phone' => '+254 7XX XXX XXX', + 'location' => 'Nairobi', + 'applied_at' => date('Y-m-d H:i:s'), + 'id_front' => '#', + 'id_back' => '#' + ]; + header('Content-Type: application/json'); + echo json_encode($dummyAgent); + } +} else { + header('HTTP/1.1 400 Bad Request'); + echo json_encode(['error' => 'Agent ID required']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/get_more_transactions.php b/jweb/ac1/src/api/get_more_transactions.php new file mode 100644 index 0000000000000000000000000000000000000000..546f9052f32e12f888ec12fc9b7bca2f11ce43ca --- /dev/null +++ b/jweb/ac1/src/api/get_more_transactions.php @@ -0,0 +1,29 @@ + false, 'message' => 'Unauthorized'])); +} + +require_once '../../db.php'; + +$database = new Database(); +$db = $database->getConnection(); + +$user = new User($db); +$transaction = new Transaction($db); + +if ($user->getUserByUsername($_SESSION['username'])) { + $offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0; + $transactions_stmt = $transaction->getTransactionsByUserId($user->id, 20, $offset); + + $transactions = []; + while ($row = $transactions_stmt->fetch(PDO::FETCH_ASSOC)) { + $transactions[] = $row; + } + + echo json_encode(['success' => true, 'transactions' => $transactions]); +} else { + echo json_encode(['success' => false, 'message' => 'User not found']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/get_tickets.php b/jweb/ac1/src/api/get_tickets.php new file mode 100644 index 0000000000000000000000000000000000000000..50d6af6a206f67ec2d134aa6493aae491c944917 --- /dev/null +++ b/jweb/ac1/src/api/get_tickets.php @@ -0,0 +1,41 @@ +getConnection(); + + $user = new User($db); + $user_id = $user->syncUser( + $_SESSION['username'], + $_SESSION['email'], + $_SESSION['tier'], + $_SESSION['package'] + ); + + $ticket = new SupportTicket($db); + $stmt = $ticket->getUserTickets($user_id); + + $tickets = []; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $tickets[] = $row; + } + + echo json_encode($tickets); + +} catch (Exception $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/loan-eligibility.php b/jweb/ac1/src/api/loan-eligibility.php new file mode 100644 index 0000000000000000000000000000000000000000..e0c558e6b51e6cab58fe306566e176fbd01a1275 --- /dev/null +++ b/jweb/ac1/src/api/loan-eligibility.php @@ -0,0 +1,26 @@ + 'Unauthorized']); + exit; +} + +if (isset($_GET['id'])) { + $agentId = intval($_GET['id']); + $agent = getAgentDetails($agentId); + + if ($agent) { + header('Content-Type: application/json'); + echo json_encode($agent); + } else { + header('HTTP/1.1 404 Not Found'); + echo json_encode(['error' => 'Agent not found']); + } +} else { + header('HTTP/1.1 400 Bad Request'); + echo json_encode(['error' => 'Agent ID required']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/main_account.php b/jweb/ac1/src/api/main_account.php new file mode 100644 index 0000000000000000000000000000000000000000..36598b5b1ae4ee08f3b407867bc17d6ee5a7c0de --- /dev/null +++ b/jweb/ac1/src/api/main_account.php @@ -0,0 +1,777 @@ +conn = $database->getConnection(); + $this->initializeMainAccount(); + } + + // Initialize main account if it doesn't exist + private function initializeMainAccount() { + $query = "SELECT COUNT(*) as count FROM main_account"; + $stmt = $this->conn->prepare($query); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result['count'] == 0) { + $query = "INSERT INTO main_account + (paybill_number, account_number, created_at) + VALUES ('542542', '00106664176150', NOW())"; + $this->conn->exec($query); + $this->mainAccountId = $this->conn->lastInsertId(); + } + } + + // Get main account details + public function getMainAccount() { + $query = "SELECT * FROM main_account WHERE id = :id"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":id", $this->mainAccountId); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + // Update main account balance + public function updateMainAccountBalance($amount, $type = 'deposit') { + try { + $this->conn->beginTransaction(); + + // Validate amount + $amount = $this->validateAmount($amount); + + // Get current balance + $mainAccount = $this->getMainAccount(); + $currentBalance = $mainAccount['total_balance']; + + if ($type === 'withdrawal' && $currentBalance < $amount) { + throw new Exception("Insufficient funds in main account"); + } + + $newBalance = $type === 'deposit' + ? $currentBalance + $amount + : $currentBalance - $amount; + + // Update main account + $query = "UPDATE main_account SET + total_balance = :balance, + total_deposits = total_deposits + :deposits, + total_withdrawals = total_withdrawals + :withdrawals, + updated_at = NOW() + WHERE id = :id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":balance", $newBalance); + + $depositAmount = $type === 'deposit' ? $amount : 0; + $withdrawalAmount = $type === 'withdrawal' ? $amount : 0; + + $stmt->bindParam(":deposits", $depositAmount); + $stmt->bindParam(":withdrawals", $withdrawalAmount); + $stmt->bindParam(":id", $this->mainAccountId); + + $stmt->execute(); + $this->conn->commit(); + + // Log the transaction + $this->logTransaction('main_account_update', [ + 'type' => $type, + 'amount' => $amount, + 'previous_balance' => $currentBalance, + 'new_balance' => $newBalance + ]); + + return [ + 'success' => true, + 'previous_balance' => $currentBalance, + 'new_balance' => $newBalance + ]; + } catch (Exception $e) { + $this->conn->rollBack(); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Get user virtual account + public function getUserAccount($userId) { + $this->validateUser($userId); + + $query = "SELECT * FROM user_accounts WHERE user_id = :user_id"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $userId); + $stmt->execute(); + + $account = $stmt->fetch(PDO::FETCH_ASSOC); + + // Create virtual account if doesn't exist + if (!$account) { + return $this->createUserAccount($userId); + } + + return $account; + } + + // Create virtual account for user + private function createUserAccount($userId) { + $virtualAccountNumber = 'JM' . str_pad($userId, 8, '0', STR_PAD_LEFT); + + $query = "INSERT INTO user_accounts (user_id, virtual_account_number) + VALUES (:user_id, :account_number)"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $userId); + $stmt->bindParam(":account_number", $virtualAccountNumber); + + if ($stmt->execute()) { + $this->logTransaction('user_account_created', [ + 'user_id' => $userId, + 'virtual_account' => $virtualAccountNumber + ], $userId); + + return $this->getUserAccount($userId); + } + + return false; + } + + // Update user virtual account balance + public function updateUserAccount($userId, $amount, $type = 'deposit') { + try { + $this->conn->beginTransaction(); + + $amount = $this->validateAmount($amount); + $userAccount = $this->getUserAccount($userId); + $currentBalance = $userAccount['current_balance']; + + if ($type === 'withdrawal' && $currentBalance < $amount) { + throw new Exception("Insufficient funds in user account"); + } + + if ($type === 'deposit') { + $newBalance = $currentBalance + $amount; + $updateField = "total_invested = total_invested + :amount"; + } else { + $newBalance = $currentBalance - $amount; + $updateField = "total_withdrawn = total_withdrawn + :amount"; + } + + $query = "UPDATE user_accounts SET + current_balance = :balance, + {$updateField}, + updated_at = NOW() + WHERE user_id = :user_id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":balance", $newBalance); + $stmt->bindParam(":amount", $amount); + $stmt->bindParam(":user_id", $userId); + + $stmt->execute(); + $this->conn->commit(); + + $this->logTransaction('user_account_update', [ + 'type' => $type, + 'amount' => $amount, + 'previous_balance' => $currentBalance, + 'new_balance' => $newBalance + ], $userId); + + return [ + 'success' => true, + 'previous_balance' => $currentBalance, + 'new_balance' => $newBalance, + 'virtual_account' => $userAccount['virtual_account_number'] + ]; + } catch (Exception $e) { + $this->conn->rollBack(); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Record pending transaction (STK Push) + public function recordPendingTransaction($userID, $amount, $phoneNumber, $checkoutRequestID) { + try { + // Check rate limiting + if (!$this->checkRateLimit($userID, 'stk_push')) { + throw new Exception("Too many attempts. Please try again later."); + } + + // Check for duplicate transactions + if ($this->checkDuplicateTransaction($userID, $amount, $phoneNumber)) { + throw new Exception("Duplicate transaction detected. Please wait before retrying."); + } + + $amount = $this->validateAmount($amount); + $this->validateUser($userID); + + $transactionID = 'JM' . date('YmdHis') . rand(1000, 9999); + + // Record in recharge_transactions as pending + $query = "INSERT INTO recharge_transactions + (user_id, main_account_id, amount, phone_number, + transaction_id, checkout_request_id, status, payment_method) + VALUES + (:user_id, :main_account_id, :amount, :phone_number, + :transaction_id, :checkout_request_id, 'pending', 'M-Pesa STK Push')"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $userID); + $stmt->bindParam(":main_account_id", $this->mainAccountId); + $stmt->bindParam(":amount", $amount); + $stmt->bindParam(":phone_number", $phoneNumber); + $stmt->bindParam(":transaction_id", $transactionID); + $stmt->bindParam(":checkout_request_id", $checkoutRequestID); + + if ($stmt->execute()) { + $this->logTransaction('pending_transaction_created', [ + 'amount' => $amount, + 'phone_number' => $phoneNumber, + 'checkout_request_id' => $checkoutRequestID, + 'transaction_id' => $transactionID + ], $userID); + + return $transactionID; + } + + return false; + } catch (Exception $e) { + error_log("Pending transaction error: " . $e->getMessage()); + return false; + } + } + + // Process recharge/deposit + public function processDeposit($userId, $amount, $paymentData) { + try { + $this->conn->beginTransaction(); + + $amount = $this->validateAmount($amount); + $this->validateUser($userId); + + // Get current balances + $mainAccount = $this->getMainAccount(); + $userAccount = $this->getUserAccount($userId); + + // Calculate bonus + $bonus = $this->calculateBonus($amount); + $totalAmount = $amount + $bonus; + + // Update main account + $mainUpdate = $this->updateMainAccountBalance($amount, 'deposit'); + if (!$mainUpdate['success']) { + throw new Exception("Failed to update main account: " . $mainUpdate['error']); + } + + // Update user account + $userUpdate = $this->updateUserAccount($userId, $totalAmount, 'deposit'); + if (!$userUpdate['success']) { + throw new Exception("Failed to update user account: " . $userUpdate['error']); + } + + // Record transaction + $transactionId = $this->recordTransaction([ + 'user_id' => $userId, + 'amount' => $amount, + 'bonus_amount' => $bonus, + 'virtual_balance_before' => $userAccount['current_balance'], + 'virtual_balance_after' => $userUpdate['new_balance'], + 'main_balance_before' => $mainAccount['total_balance'], + 'main_balance_after' => $mainUpdate['new_balance'], + 'payment_method' => $paymentData['method'], + 'phone_number' => $paymentData['phone'], + 'mpesa_receipt' => $paymentData['receipt'] ?? null, + 'transaction_id' => $paymentData['transaction_id'] ?? null, + 'checkout_request_id' => $paymentData['checkout_request_id'] ?? null, + 'status' => 'completed' + ]); + + $this->conn->commit(); + + $this->logTransaction('deposit_processed', [ + 'amount' => $amount, + 'bonus' => $bonus, + 'total_amount' => $totalAmount, + 'transaction_id' => $transactionId + ], $userId); + + return [ + 'success' => true, + 'transaction_id' => $transactionId, + 'user_balance' => $userUpdate['new_balance'], + 'bonus_received' => $bonus, + 'virtual_account' => $userUpdate['virtual_account'], + 'main_account_balance' => $mainUpdate['new_balance'] + ]; + + } catch (Exception $e) { + $this->conn->rollBack(); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Record transaction + private function recordTransaction($data) { + $query = "INSERT INTO recharge_transactions + (user_id, main_account_id, amount, bonus_amount, + virtual_balance_before, virtual_balance_after, + main_balance_before, main_balance_after, + payment_method, phone_number, mpesa_receipt, + transaction_id, checkout_request_id, paybill_number, account_number, status) + VALUES + (:user_id, :main_account_id, :amount, :bonus_amount, + :virtual_before, :virtual_after, + :main_before, :main_after, + :payment_method, :phone_number, :mpesa_receipt, + :transaction_id, :checkout_request_id, :paybill, :account, :status)"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $data['user_id']); + $stmt->bindParam(":main_account_id", $this->mainAccountId); + $stmt->bindParam(":amount", $data['amount']); + $stmt->bindParam(":bonus_amount", $data['bonus_amount']); + $stmt->bindParam(":virtual_before", $data['virtual_balance_before']); + $stmt->bindParam(":virtual_after", $data['virtual_balance_after']); + $stmt->bindParam(":main_before", $data['main_balance_before']); + $stmt->bindParam(":main_after", $data['main_balance_after']); + $stmt->bindParam(":payment_method", $data['payment_method']); + $stmt->bindParam(":phone_number", $data['phone_number']); + $stmt->bindParam(":mpesa_receipt", $data['mpesa_receipt']); + $stmt->bindParam(":transaction_id", $data['transaction_id']); + $stmt->bindParam(":checkout_request_id", $data['checkout_request_id'] ?? null); + $stmt->bindParam(":paybill", $data['paybill_number'] ?? '542542'); + $stmt->bindParam(":account", $data['account_number'] ?? '00106664176150'); + $stmt->bindParam(":status", $data['status']); + + if ($stmt->execute()) { + return $this->conn->lastInsertId(); + } + + return false; + } + + // Calculate bonus based on amount + private function calculateBonus($amount) { + $bonuses = [ + 500 => 5, + 1000 => 15, + 2000 => 40, + 5000 => 120, + 10000 => 300, + 20000 => 700, + 50000 => 2000, + 100000 => 5000 + ]; + + // Find the closest package bonus + $closestAmount = 0; + $minDifference = PHP_INT_MAX; + + foreach ($bonuses as $packageAmount => $bonus) { + $difference = abs($packageAmount - $amount); + if ($difference < $minDifference) { + $minDifference = $difference; + $closestAmount = $packageAmount; + } + } + + return $bonuses[$closestAmount] ?? 0; + } + + // Update transaction status from M-Pesa callback + public function updateTransactionStatus($checkoutRequestID, $status, $mpesaReceipt = null) { + try { + $this->conn->beginTransaction(); + + // Get transaction details + $query = "SELECT * FROM recharge_transactions + WHERE checkout_request_id = :checkout_id AND status = 'pending'"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":checkout_id", $checkoutRequestID); + $stmt->execute(); + $transaction = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$transaction) { + throw new Exception("Transaction not found"); + } + + // Update transaction status + $updateQuery = "UPDATE recharge_transactions SET + status = :status, + mpesa_receipt = :receipt, + updated_at = NOW() + WHERE checkout_request_id = :checkout_id"; + + $updateStmt = $this->conn->prepare($updateQuery); + $updateStmt->bindParam(":status", $status); + $updateStmt->bindParam(":receipt", $mpesaReceipt); + $updateStmt->bindParam(":checkout_id", $checkoutRequestID); + $updateStmt->execute(); + + // If completed, process the deposit + if ($status === 'completed') { + $paymentData = [ + 'method' => 'M-Pesa STK Push', + 'phone' => $transaction['phone_number'], + 'receipt' => $mpesaReceipt, + 'transaction_id' => $transaction['transaction_id'], + 'checkout_request_id' => $checkoutRequestID + ]; + + $depositResult = $this->processDeposit($transaction['user_id'], $transaction['amount'], $paymentData); + + if (!$depositResult['success']) { + throw new Exception($depositResult['error']); + } + } + + $this->conn->commit(); + + $this->logTransaction('transaction_status_updated', [ + 'checkout_request_id' => $checkoutRequestID, + 'status' => $status, + 'mpesa_receipt' => $mpesaReceipt + ], $transaction['user_id']); + + return ['success' => true]; + + } catch (Exception $e) { + $this->conn->rollBack(); + error_log("Error updating transaction status: " . $e->getMessage()); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Submit manual payment for admin verification + public function submitManualPayment($user_id, $amount, $phone_number, $mpesa_code, $screenshot) { + try { + $this->conn->beginTransaction(); + + $amount = $this->validateAmount($amount); + $this->validateUser($user_id); + + // First record in recharge_transactions as pending + $transactionId = $this->recordTransaction([ + 'user_id' => $user_id, + 'amount' => $amount, + 'bonus_amount' => 0, + 'virtual_balance_before' => 0, + 'virtual_balance_after' => 0, + 'main_balance_before' => 0, + 'main_balance_after' => 0, + 'payment_method' => 'Manual Verification', + 'phone_number' => $phone_number, + 'mpesa_receipt' => $mpesa_code, + 'transaction_id' => 'MANUAL_' . time() . '_' . $user_id, + 'status' => 'pending' + ]); + + // Then record in admin_payments for verification + $query = "INSERT INTO admin_payments + (user_id, amount, mpesa_code, phone_number, screenshot_path) + VALUES + (:user_id, :amount, :mpesa_code, :phone_number, :screenshot)"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->bindParam(":amount", $amount); + $stmt->bindParam(":mpesa_code", $mpesa_code); + $stmt->bindParam(":phone_number", $phone_number); + $stmt->bindParam(":screenshot", $screenshot); + + if ($stmt->execute()) { + $this->conn->commit(); + + $this->logTransaction('manual_payment_submitted', [ + 'amount' => $amount, + 'mpesa_code' => $mpesa_code, + 'transaction_id' => $transactionId + ], $user_id); + + return [ + 'success' => true, + 'payment_id' => $this->conn->lastInsertId(), + 'transaction_id' => $transactionId + ]; + } else { + $this->conn->rollBack(); + return ['success' => false, 'error' => 'Failed to submit payment']; + } + + } catch (Exception $e) { + $this->conn->rollBack(); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Verify payment and update balances + public function verifyPayment($payment_id, $admin_id, $notes = '') { + try { + $this->conn->beginTransaction(); + + // Get payment details + $query = "SELECT ap.*, rt.id as transaction_id + FROM admin_payments ap + LEFT JOIN recharge_transactions rt ON ap.user_id = rt.user_id + AND ap.amount = rt.amount + AND rt.status = 'pending' + WHERE ap.id = :payment_id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":payment_id", $payment_id); + $stmt->execute(); + $payment = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$payment) { + throw new Exception("Payment not found"); + } + + // Process the deposit + $paymentData = [ + 'method' => 'Manual Verification', + 'phone' => $payment['phone_number'], + 'receipt' => $payment['mpesa_code'], + 'transaction_id' => 'MANUAL_VERIFIED_' . $payment_id + ]; + + $depositResult = $this->processDeposit($payment['user_id'], $payment['amount'], $paymentData); + + if (!$depositResult['success']) { + throw new Exception($depositResult['error']); + } + + // Update admin_payments status + $updateQuery = "UPDATE admin_payments SET + status = 'verified', + verified_by = :admin_id, + verified_at = NOW(), + notes = :notes + WHERE id = :payment_id"; + + $updateStmt = $this->conn->prepare($updateQuery); + $updateStmt->bindParam(":admin_id", $admin_id); + $updateStmt->bindParam(":notes", $notes); + $updateStmt->bindParam(":payment_id", $payment_id); + $updateStmt->execute(); + + // Update recharge_transactions status if exists + if ($payment['transaction_id']) { + $updateTxQuery = "UPDATE recharge_transactions SET status = 'completed' WHERE id = :tx_id"; + $updateTxStmt = $this->conn->prepare($updateTxQuery); + $updateTxStmt->bindParam(":tx_id", $payment['transaction_id']); + $updateTxStmt->execute(); + } + + $this->conn->commit(); + + $this->logTransaction('payment_verified', [ + 'payment_id' => $payment_id, + 'admin_id' => $admin_id, + 'amount' => $payment['amount'] + ], $payment['user_id']); + + return [ + 'success' => true, + 'user_balance' => $depositResult['user_balance'], + 'bonus_received' => $depositResult['bonus_received'] + ]; + + } catch (Exception $e) { + $this->conn->rollBack(); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Reject payment with reason + public function rejectPayment($payment_id, $admin_id, $notes = '') { + try { + $this->conn->beginTransaction(); + + // Update admin_payments status + $updateQuery = "UPDATE admin_payments SET + status = 'rejected', + verified_by = :admin_id, + verified_at = NOW(), + notes = :notes + WHERE id = :payment_id"; + + $updateStmt = $this->conn->prepare($updateQuery); + $updateStmt->bindParam(":admin_id", $admin_id); + $updateStmt->bindParam(":notes", $notes); + $updateStmt->bindParam(":payment_id", $payment_id); + $updateStmt->execute(); + + // Update recharge_transactions status if exists + $txQuery = "UPDATE recharge_transactions SET status = 'failed' + WHERE user_id = (SELECT user_id FROM admin_payments WHERE id = :payment_id) + AND amount = (SELECT amount FROM admin_payments WHERE id = :payment_id) + AND status = 'pending'"; + + $txStmt = $this->conn->prepare($txQuery); + $txStmt->bindParam(":payment_id", $payment_id); + $txStmt->execute(); + + $this->conn->commit(); + + $payment = $this->getPaymentDetails($payment_id); + $this->logTransaction('payment_rejected', [ + 'payment_id' => $payment_id, + 'admin_id' => $admin_id, + 'notes' => $notes + ], $payment['user_id']); + + return ['success' => true]; + + } catch (Exception $e) { + $this->conn->rollBack(); + return ['success' => false, 'error' => $e->getMessage()]; + } + } + + // Get user dashboard statistics + public function getUserDashboardData($userId) { + $this->validateUser($userId); + + $userAccount = $this->getUserAccount($userId); + $mainAccount = $this->getMainAccount(); + + // Get recent transactions + $query = "SELECT * FROM recharge_transactions + WHERE user_id = :user_id + ORDER BY created_at DESC + LIMIT 5"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $userId); + $stmt->execute(); + $recentTransactions = $stmt->fetchAll(PDO::FETCH_ASSOC); + + return [ + 'virtual_account' => $userAccount['virtual_account_number'], + 'current_balance' => $userAccount['current_balance'], + 'total_invested' => $userAccount['total_invested'], + 'total_earnings' => $userAccount['total_earnings'], + 'total_withdrawn' => $userAccount['total_withdrawn'], + 'main_account_balance' => $mainAccount['total_balance'], + 'paybill_number' => $mainAccount['paybill_number'], + 'account_number' => $mainAccount['account_number'], + 'recent_transactions' => $recentTransactions + ]; + } + + // Get all pending payments for admin + public function getPendingPayments() { + $query = "SELECT ap.*, u.username, u.email + FROM admin_payments ap + JOIN users u ON ap.user_id = u.id + WHERE ap.status = 'pending' + ORDER BY ap.created_at DESC"; + + $stmt = $this->conn->prepare($query); + $stmt->execute(); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + // Get payment history for user + public function getUserPaymentHistory($user_id, $limit = 10) { + $this->validateUser($user_id); + + $query = "SELECT * FROM recharge_transactions + WHERE user_id = :user_id + ORDER BY created_at DESC + LIMIT :limit"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->bindParam(":limit", $limit, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + // Validation methods + private function validateAmount($amount) { + if (!is_numeric($amount) || $amount <= 0) { + throw new Exception("Invalid amount specified"); + } + return floatval($amount); + } + + private function validateUser($userId) { + $stmt = $this->conn->prepare("SELECT id FROM users WHERE id = ?"); + $stmt->execute([$userId]); + if (!$stmt->fetch()) { + throw new Exception("User not found"); + } + } + + // Security methods + public function checkRateLimit($userId, $action, $maxAttempts = 5, $timeFrame = 3600) { + $stmt = $this->conn->prepare(" + SELECT COUNT(*) as attempts FROM transaction_logs + WHERE user_id = ? AND action = ? AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND) + "); + $stmt->execute([$userId, $action, $timeFrame]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + return $result['attempts'] < $maxAttempts; + } + + public function checkDuplicateTransaction($userId, $amount, $phoneNumber, $timeFrame = 300) { + $stmt = $this->conn->prepare(" + SELECT id FROM recharge_transactions + WHERE user_id = ? AND amount = ? AND phone_number = ? + AND status = 'pending' AND created_at > DATE_SUB(NOW(), INTERVAL ? SECOND) + "); + $stmt->execute([$userId, $amount, $phoneNumber, $timeFrame]); + + return $stmt->fetch() !== false; + } + + // Logging method + private function logTransaction($action, $details, $userId = null) { + $stmt = $this->conn->prepare(" + INSERT INTO transaction_logs + (user_id, action, details, created_at) + VALUES (?, ?, ?, NOW()) + "); + $stmt->execute([$userId, $action, json_encode($details)]); + } + + // Helper method to get payment details + private function getPaymentDetails($payment_id) { + $query = "SELECT * FROM admin_payments WHERE id = :id"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":id", $payment_id); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + // Get main account summary for admin + public function getMainAccountSummary() { + $mainAccount = $this->getMainAccount(); + + // Get today's transactions + $query = "SELECT COUNT(*) as today_count, COALESCE(SUM(amount), 0) as today_amount + FROM recharge_transactions + WHERE DATE(created_at) = CURDATE() AND status = 'completed'"; + $stmt = $this->conn->prepare($query); + $stmt->execute(); + $today = $stmt->fetch(PDO::FETCH_ASSOC); + + // Get pending transactions count + $query = "SELECT COUNT(*) as pending_count FROM recharge_transactions WHERE status = 'pending'"; + $stmt = $this->conn->prepare($query); + $stmt->execute(); + $pending = $stmt->fetch(PDO::FETCH_ASSOC); + + return [ + 'main_account' => $mainAccount, + 'today_transactions' => $today, + 'pending_transactions' => $pending + ]; + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/manual_payment.php b/jweb/ac1/src/api/manual_payment.php new file mode 100644 index 0000000000000000000000000000000000000000..1164363d92a87735f3e0a137224f72056f9bf79b --- /dev/null +++ b/jweb/ac1/src/api/manual_payment.php @@ -0,0 +1,76 @@ +submitManualPayment( + $_SESSION['user_id'], + $_SESSION['payment_amount'], + $_SESSION['payment_phone'], + $mpesa_code, + $filename + ); + + if ($result['success']) { + $success = "Payment submitted for verification! We'll update your balance within 1 hour."; + unset($_SESSION['payment_amount'], $_SESSION['payment_phone']); + } else { + $error = $result['error']; + } + } else { + $error = "Failed to upload screenshot"; + } +} +?> + + +
+

Manual Payment Verification

+ +
+
+ + +
+ +
+ + +
+ +
+ + + Upload screenshot of M-Pesa confirmation +
+ +
+

Payment Instructions:

+
    +
  1. Send KES to 0756709823
  2. +
  3. Take screenshot of M-Pesa confirmation
  4. +
  5. Enter transaction code above
  6. +
  7. Upload the screenshot
  8. +
  9. Submit for verification
  10. +
+
+ + +
+
\ No newline at end of file diff --git a/jweb/ac1/src/api/mpesa_callback.php b/jweb/ac1/src/api/mpesa_callback.php new file mode 100644 index 0000000000000000000000000000000000000000..51cf0f631a7cc234ef2851da06294bf1696abf6b --- /dev/null +++ b/jweb/ac1/src/api/mpesa_callback.php @@ -0,0 +1,42 @@ +updateTransactionStatus($checkoutRequestID, 'completed', $mpesaReceipt); + + // Log success + error_log("Payment successful: $checkoutRequestID - $mpesaReceipt"); + } else { + // Payment failed + $mainAccount->updateTransactionStatus($checkoutRequestID, 'failed'); + + // Log failure + error_log("Payment failed: $checkoutRequestID - $resultDesc"); + } + + http_response_code(200); + echo json_encode(['ResultCode' => 0, 'ResultDesc' => 'Accepted']); +} else { + http_response_code(400); + echo json_encode(['ResultCode' => 1, 'ResultDesc' => 'Invalid callback']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/mpesa_config.php b/jweb/ac1/src/api/mpesa_config.php new file mode 100644 index 0000000000000000000000000000000000000000..885ea81986f55bf3faae7e6d72cd11e8fd017263 --- /dev/null +++ b/jweb/ac1/src/api/mpesa_config.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/jweb/ac1/src/api/mpesa_service.php b/jweb/ac1/src/api/mpesa_service.php new file mode 100644 index 0000000000000000000000000000000000000000..7b0294dc7d86f38479a4f2088c3eba89dd67f993 --- /dev/null +++ b/jweb/ac1/src/api/mpesa_service.php @@ -0,0 +1,69 @@ +getAccessToken(); + $url = 'https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest'; + + $timestamp = date('YmdHis'); + $password = base64_encode($this->shortcode . $this->passkey . $timestamp); + + $callback_url = 'https://yourdomain.com/mpesa_callback.php'; + + $curl_post_data = array( + 'BusinessShortCode' => $this->shortcode, + 'Password' => $password, + 'Timestamp' => $timestamp, + 'TransactionType' => 'CustomerPayBillOnline', + 'Amount' => $amount, + 'PartyA' => $userPhone, + 'PartyB' => $this->shortcode, + 'PhoneNumber' => $userPhone, + 'CallBackURL' => $callback_url, + 'AccountReference' => 'JMOTORS' . $userId, + 'TransactionDesc' => 'JMotors Deposit' + ); + + $data_string = json_encode($curl_post_data); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json', + 'Authorization: Bearer ' . $access_token + )); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + + $response = curl_exec($curl); + curl_close($curl); + + return json_decode($response, true); + } + + private function getAccessToken() { + $url = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials'; + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Authorization: Basic ' . base64_encode($this->consumerKey . ':' . $this->consumerSecret) + )); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($curl); + curl_close($curl); + + $result = json_decode($result, true); + return $result['access_token']; + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/package-diamond-package.php b/jweb/ac1/src/api/package-diamond-package.php new file mode 100644 index 0000000000000000000000000000000000000000..9cf7a7e5eb377420513e6709b0d99bb8e5d22d2a --- /dev/null +++ b/jweb/ac1/src/api/package-diamond-package.php @@ -0,0 +1,403 @@ + + + + + + + Diamond Package - Japanese Motors + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+
+
+

💎 Diamond Package Benefits

+
    +
  • Priority customer support
  • +
  • Exclusive investment opportunities
  • +
  • Higher cashback rates on all purchases
  • +
  • Access to premium products
  • +
  • Weekly market insights report
  • +
+
+

Your Diamond Package Stats

+
+
+

Total Earnings

+

KES 15,240.00

+
+
+

Active Investments

+

3

+
+
+
+
+ +
+
+

Package Actions

+ + + +
+ +
+

Performance Metrics

+
+
+

Return on Investment

+
+
+
+

75%

+
+
+

Package Utilization

+
+
+
+

60%

+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/api/packages-backend.php b/jweb/ac1/src/api/packages-backend.php new file mode 100644 index 0000000000000000000000000000000000000000..f5e0f7911a997db5ee6e1d54253ef4f58cfaa897 --- /dev/null +++ b/jweb/ac1/src/api/packages-backend.php @@ -0,0 +1,202 @@ +prepare("SELECT * FROM packages WHERE id = ?"); + $stmt->execute([$package_id]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting package details: " . $e->getMessage()); + return false; + } +} + +// Function to get all packages +function getAllPackages() { + global $pdo; + try { + $stmt = $pdo->prepare("SELECT * FROM packages ORDER BY min_investment ASC"); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting all packages: " . $e->getMessage()); + return []; + } +} + +// Function to check if user can invest in a package +function canUserInvest($user_id, $package_id, $amount) { + // Get package details + $package = getPackageDetails($package_id); + if (!$package) return false; + + // Check if amount is within package limits + if ($amount < $package['min_investment'] || $amount > $package['max_investment']) { + return false; + } + + return true; +} + +// Function to create a new investment +function createInvestment($user_id, $package_id, $amount, $payment_method) { + global $pdo; + + // Start transaction + $pdo->beginTransaction(); + + try { + // Get package details + $package = getPackageDetails($package_id); + if (!$package) { + throw new Exception("Package not found"); + } + + // Calculate end date + $start_date = date('Y-m-d H:i:s'); + $end_date = date('Y-m-d H:i:s', strtotime("+{$package['duration_days']} days")); + + // Calculate total expected earnings + $total_earnings = $amount * ($package['daily_return'] / 100) * $package['duration_days']; + + // Insert investment record + $stmt = $pdo->prepare("INSERT INTO user_investments (user_id, package_id, amount, start_date, end_date, total_earnings) VALUES (?, ?, ?, ?, ?, ?)"); + $stmt->execute([$user_id, $package_id, $amount, $start_date, $end_date, $total_earnings]); + $investment_id = $pdo->lastInsertId(); + + // Update user's current package if this is their first or highest investment + $stmt = $pdo->prepare("SELECT MAX(amount) as max_investment FROM user_investments WHERE user_id = ? AND status = 'active'"); + $stmt->execute([$user_id]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + $max_investment = $result ? $result['max_investment'] : 0; + + if ($amount >= $max_investment) { + $stmt = $pdo->prepare("UPDATE users SET current_package_id = ?, package_start_date = ?, package_end_date = ? WHERE id = ?"); + $stmt->execute([$package_id, $start_date, $end_date, $user_id]); + } + + // Update user's total invested amount + $stmt = $pdo->prepare("UPDATE users SET total_invested = total_invested + ? WHERE id = ?"); + $stmt->execute([$amount, $user_id]); + + // Process payment based on method + if ($payment_method === 'wallet') { + // Deduct from user's balance + $stmt = $pdo->prepare("UPDATE users SET balance = balance - ? WHERE id = ? AND balance >= ?"); + $stmt->execute([$amount, $user_id, $amount]); + + if ($stmt->rowCount() === 0) { + throw new Exception("Insufficient balance"); + } + + // Update session balance + $_SESSION['balance'] -= $amount; + } else if ($payment_method === 'mpesa') { + // For demo purposes, we'll just log this + error_log("M-Pesa payment initiated for user $user_id, amount: $amount"); + } else if ($payment_method === 'bank') { + // For demo purposes, we'll just log this + error_log("Bank transfer initiated for user $user_id, amount: $amount"); + } + + // Commit transaction + $pdo->commit(); + + return $investment_id; + + } catch (Exception $e) { + $pdo->rollBack(); + error_log("Investment creation failed: " . $e->getMessage()); + throw $e; + } +} + +// Function to get user's active investment +function getUserActiveInvestment($user_id) { + global $pdo; + try { + $stmt = $pdo->prepare(" + SELECT ui.*, p.name as package_name, p.daily_return, p.duration_days + FROM user_investments ui + JOIN packages p ON ui.package_id = p.id + WHERE ui.user_id = ? AND ui.status = 'active' AND ui.end_date > NOW() + ORDER BY ui.amount DESC + LIMIT 1 + "); + $stmt->execute([$user_id]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting user active investment: " . $e->getMessage()); + return false; + } +} + +// Function to get user's daily products +function getUserDailyProducts($user_id) { + global $pdo; + try { + $stmt = $pdo->prepare(" + SELECT up.*, p.name as product_name, p.description, p.value, p.image_url + FROM user_products up + JOIN products p ON up.product_id = p.id + WHERE up.user_id = ? + ORDER BY up.assigned_date DESC + "); + $stmt->execute([$user_id]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting user daily products: " . $e->getMessage()); + return []; + } +} + +// Handle package investment request +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { + $response = ['success' => false, 'message' => '']; + + try { + if ($_POST['action'] === 'invest') { + $package_id = intval($_POST['package_id']); + $amount = floatval($_POST['amount']); + $payment_method = $_POST['payment_method']; + $user_id = $_SESSION['user_id']; + + // Validate investment + if (canUserInvest($user_id, $package_id, $amount)) { + $investment_id = createInvestment($user_id, $package_id, $amount, $payment_method); + + $response['success'] = true; + $response['message'] = 'Investment successful!'; + $response['investment_id'] = $investment_id; + + // Update session data + $_SESSION['total_deposits'] += $amount; + } else { + $response['message'] = 'Invalid investment amount or package selection'; + } + } + } catch (Exception $e) { + $response['message'] = $e->getMessage(); + } + + header('Content-Type: application/json'); + echo json_encode($response); + exit; +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/process_claim.php b/jweb/ac1/src/api/process_claim.php new file mode 100644 index 0000000000000000000000000000000000000000..bfe7d6c5aa98eb5b91a756abffc5989589cd4a74 --- /dev/null +++ b/jweb/ac1/src/api/process_claim.php @@ -0,0 +1,114 @@ + false, 'message' => '']; + +try { + $database = new Database(); + $db = $database->getConnection(); + $claim = new AgentClaim($db); + + $action = $_POST['action'] ?? ''; + + switch ($action) { + case 'submit_claim': + // Validate and submit new claim + $required_fields = ['claim_type', 'amount', 'description']; + foreach ($required_fields as $field) { + if (empty($_POST[$field])) { + throw new Exception("Missing required field: $field"); + } + } + + $claim->user_id = $_SESSION['user_id']; + $claim->username = $_SESSION['username']; + $claim->email = $_SESSION['email']; + $claim->claim_type = $_POST['claim_type']; + $claim->amount = floatval($_POST['amount']); + $claim->description = $_POST['description']; + $claim->evidence_file = $_POST['evidence_file'] ?? null; + + // Validate amount + if ($claim->amount <= 0) { + throw new Exception("Invalid claim amount"); + } + + // Check for duplicate pending claims + if ($claim->hasPendingClaims($claim->user_id)) { + throw new Exception("You already have a pending claim. Please wait for it to be processed."); + } + + $claim_id = $claim->create(); + if ($claim_id) { + $response['success'] = true; + $response['message'] = 'Claim submitted successfully! It will be reviewed within 3-5 business days.'; + $response['claim_id'] = $claim_id; + } else { + throw new Exception("Failed to submit claim"); + } + break; + + case 'approve_claim': + // Admin approval + if ($_SESSION['role'] !== 'admin') { + throw new Exception("Insufficient permissions"); + } + + $claim_id = $_POST['claim_id'] ?? 0; + if (!$claim_id) { + throw new Exception("Invalid claim ID"); + } + + if ($claim->updateStatus($claim_id, 'approved', $_SESSION['user_id'])) { + $response['success'] = true; + $response['message'] = 'Claim approved successfully'; + } else { + throw new Exception("Failed to approve claim"); + } + break; + + case 'reject_claim': + // Admin rejection + if ($_SESSION['role'] !== 'admin') { + throw new Exception("Insufficient permissions"); + } + + $claim_id = $_POST['claim_id'] ?? 0; + $rejection_reason = $_POST['rejection_reason'] ?? ''; + + if (!$claim_id) { + throw new Exception("Invalid claim ID"); + } + + if (empty($rejection_reason)) { + throw new Exception("Rejection reason is required"); + } + + if ($claim->updateStatus($claim_id, 'rejected', null, $rejection_reason)) { + $response['success'] = true; + $response['message'] = 'Claim rejected successfully'; + } else { + throw new Exception("Failed to reject claim"); + } + break; + + default: + throw new Exception("Invalid action"); + } + +} catch (Exception $e) { + $response['message'] = $e->getMessage(); + error_log("Process Claim Error: " . $e->getMessage()); +} + +echo json_encode($response); +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/profile_handler.php b/jweb/ac1/src/api/profile_handler.php new file mode 100644 index 0000000000000000000000000000000000000000..9bfef647e938cdbd415683e51a6f2d352d08697b --- /dev/null +++ b/jweb/ac1/src/api/profile_handler.php @@ -0,0 +1,145 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch(PDOException $e) { + echo json_encode(['success' => false, 'message' => 'Database connection failed']); + exit; +} + +if (!isset($_SESSION['user_id'])) { + echo json_encode(['success' => false, 'message' => 'Not authenticated']); + exit; +} + +$user_id = $_SESSION['user_id']; +$action = $_POST['action'] ?? ''; + +switch($action) { + case 'get_profile': + getProfile($pdo, $user_id); + break; + case 'update_profile': + updateProfile($pdo, $user_id); + break; + case 'change_password': + changePassword($pdo, $user_id); + break; + case 'get_activity': + getActivity($pdo, $user_id); + break; + default: + echo json_encode(['success' => false, 'message' => 'Invalid action']); +} + +function getProfile($pdo, $user_id) { + $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); + $stmt->execute([$user_id]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user) { + unset($user['password_hash']); + echo json_encode(['success' => true, 'profile' => $user]); + } else { + echo json_encode(['success' => false, 'message' => 'User not found']); + } +} + +function updateProfile($pdo, $user_id) { + $allowed_fields = ['first_name', 'last_name', 'email', 'phone_number', 'country_code']; + $updates = []; + $params = []; + + foreach ($allowed_fields as $field) { + if (isset($_POST[$field]) && $_POST[$field] !== '') { + $updates[] = "$field = ?"; + $params[] = $_POST[$field]; + } + } + + if (empty($updates)) { + echo json_encode(['success' => false, 'message' => 'No valid fields to update']); + return; + } + + $params[] = $user_id; + $sql = "UPDATE users SET " . implode(', ', $updates) . ", last_updated = CURRENT_TIMESTAMP WHERE id = ?"; + + try { + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + + // Log the activity + logActivity($pdo, $user_id, 'profile_update', 'Updated profile information'); + + echo json_encode(['success' => true, 'message' => 'Profile updated successfully']); + } catch (PDOException $e) { + echo json_encode(['success' => false, 'message' => 'Update failed: ' . $e->getMessage()]); + } +} + +function changePassword($pdo, $user_id) { + if (!isset($_POST['current_password']) || !isset($_POST['new_password']) || !isset($_POST['confirm_password'])) { + echo json_encode(['success' => false, 'message' => 'All password fields are required']); + return; + } + + $current_password = $_POST['current_password']; + $new_password = $_POST['new_password']; + $confirm_password = $_POST['confirm_password']; + + if ($new_password !== $confirm_password) { + echo json_encode(['success' => false, 'message' => 'New passwords do not match']); + return; + } + + if (strlen($new_password) < 6) { + echo json_encode(['success' => false, 'message' => 'Password must be at least 6 characters long']); + return; + } + + // Get current password hash + $stmt = $pdo->prepare("SELECT password_hash FROM users WHERE id = ?"); + $stmt->execute([$user_id]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user || !password_verify($current_password, $user['password_hash'])) { + echo json_encode(['success' => false, 'message' => 'Current password is incorrect']); + return; + } + + // Update password + $new_password_hash = password_hash($new_password, PASSWORD_DEFAULT); + $stmt = $pdo->prepare("UPDATE users SET password_hash = ?, last_updated = CURRENT_TIMESTAMP WHERE id = ?"); + $stmt->execute([$new_password_hash, $user_id]); + + // Log the activity + logActivity($pdo, $user_id, 'password_change', 'Changed account password'); + + echo json_encode(['success' => true, 'message' => 'Password updated successfully']); +} + +function getActivity($pdo, $user_id) { + $stmt = $pdo->prepare("SELECT activity_type, description, timestamp FROM user_activity WHERE user_id = ? ORDER BY timestamp DESC LIMIT 50"); + $stmt->execute([$user_id]); + $activities = $stmt->fetchAll(PDO::FETCH_ASSOC); + + echo json_encode(['success' => true, 'activities' => $activities]); +} + +function logActivity($pdo, $user_id, $type, $description) { + $ip_address = $_SERVER['REMOTE_ADDR']; + $stmt = $pdo->prepare("INSERT INTO user_activity (user_id, activity_type, description, ip_address) VALUES (?, ?, ?, ?)"); + $stmt->execute([$user_id, $type, $description, $ip_address]); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/purchase-product.php b/jweb/ac1/src/api/purchase-product.php new file mode 100644 index 0000000000000000000000000000000000000000..44d4d20ca3c7ebb4cc8c802f53e98da76d2fa973 --- /dev/null +++ b/jweb/ac1/src/api/purchase-product.php @@ -0,0 +1,109 @@ + false, 'message' => 'Not authenticated']); + exit; +} + +// Include database configuration +require_once '../../db.php'; + +// Get user data from session +$user_id = $_SESSION['user_id'] ?? null; +$current_balance = $_SESSION['balance']; + +if (!$user_id) { + echo json_encode(['success' => false, 'message' => 'User not identified']); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $product_id = filter_input(INPUT_POST, 'product_id', FILTER_SANITIZE_NUMBER_INT); + + try { + // Start transaction + $pdo->beginTransaction(); + + // Get product details + $stmt = $pdo->prepare("SELECT * FROM products WHERE id = ? AND is_active = TRUE"); + $stmt->execute([$product_id]); + $product = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$product) { + throw new Exception("Product not available."); + } + + // Check if user has sufficient balance + if ($current_balance < $product['price']) { + throw new Exception("Insufficient balance to purchase this product. You need KES " . + number_format($product['price'] - $current_balance, 2) . " more."); + } + + // Deduct product price from user's balance + $new_balance = $current_balance - $product['price']; + $stmt = $pdo->prepare("UPDATE users SET balance = ? WHERE id = ?"); + $stmt->execute([$new_balance, $user_id]); + + // Record the transaction + $stmt = $pdo->prepare("INSERT INTO transactions (user_id, type, amount, description, balance_after) VALUES (?, 'product_purchase', ?, ?, ?)"); + $stmt->execute([ + $user_id, + $product['price'], + "Purchased: " . $product['name'], + $new_balance + ]); + + // Add to user's products + $stmt = $pdo->prepare("INSERT INTO user_products (user_id, product_id, purchase_price, cashback_received) VALUES (?, ?, ?, ?)"); + $stmt->execute([$user_id, $product_id, $product['price'], $product['cashback_amount']]); + + // If there's cashback, process it + if ($product['cashback_amount'] > 0) { + $cashback_balance = $new_balance + $product['cashback_amount']; + $stmt = $pdo->prepare("UPDATE users SET balance = ?, rewards = rewards + ? WHERE id = ?"); + $stmt->execute([$cashback_balance, $product['cashback_amount'], $user_id]); + + // Record cashback transaction + $stmt = $pdo->prepare("INSERT INTO transactions (user_id, type, amount, description, balance_after) VALUES (?, 'cashback', ?, ?, ?)"); + $stmt->execute([ + $user_id, + $product['cashback_amount'], + "Cashback for: " . $product['name'], + $cashback_balance + ]); + + $new_balance = $cashback_balance; + } + + // Update user package if this is a package product + if (stripos($product['name'], 'package') !== false || stripos($product['name'], 'bundle') !== false) { + $stmt = $pdo->prepare("UPDATE users SET package = ? WHERE id = ?"); + $stmt->execute([$product['name'], $user_id]); + + // Update session data + $_SESSION['package'] = $product['name']; + } + + // Update session balance + $_SESSION['balance'] = $new_balance; + + // Commit transaction + $pdo->commit(); + + // Return success response + echo json_encode([ + 'success' => true, + 'message' => 'Product purchased successfully!', + 'new_balance' => $new_balance, + 'product_name' => $product['name'], + 'redirect_url' => 'package-' . strtolower(str_replace(' ', '-', $product['name'])) . '.php' + ]); + + } catch (Exception $e) { + $pdo->rollBack(); + echo json_encode(['success' => false, 'message' => $e->getMessage()]); + } + exit; +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/purchase.php b/jweb/ac1/src/api/purchase.php new file mode 100644 index 0000000000000000000000000000000000000000..a2198f98d5a097553475bef159afcc1e749b5f46 --- /dev/null +++ b/jweb/ac1/src/api/purchase.php @@ -0,0 +1,60 @@ + false, 'message' => 'Insufficient balance.']); + exit; + } + + // Get package details + $stmt = $pdo->prepare("SELECT * FROM packages WHERE name = ?"); + $stmt->execute([$package_name]); + $package = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$package) { + echo json_encode(['success' => false, 'message' => 'Package not found.']); + exit; + } + + // Start transaction + $pdo->beginTransaction(); + + try { + // Deduct amount from user balance + $stmt = $pdo->prepare("UPDATE users SET balance = balance - ?, package = ? WHERE id = ?"); + $stmt->execute([$amount, $package_name, $user_id]); + + // Record transaction + $stmt = $pdo->prepare("INSERT INTO transactions (user_id, type, amount, description, status) VALUES (?, 'purchase', ?, ?, 'completed')"); + $stmt->execute([$user_id, $amount, "Purchased {$package_name} package"]); + + // Add to user packages + $stmt = $pdo->prepare("INSERT INTO user_packages (user_id, package_id, investment_amount, expected_return) VALUES (?, ?, ?, ?)"); + $stmt->execute([$user_id, $package['id'], $amount, $package['return_amount']]); + + // Update user package + $stmt = $pdo->prepare("UPDATE users SET package = ? WHERE id = ?"); + $stmt->execute([$package_name, $user_id]); + + // Commit transaction + $pdo->commit(); + + // Update session + $_SESSION['balance'] -= $amount; + $_SESSION['package'] = $package_name; + + echo json_encode(['success' => true, 'message' => 'Package purchased successfully!']); + } catch (Exception $e) { + $pdo->rollBack(); + echo json_encode(['success' => false, 'message' => 'Purchase failed: ' . $e->getMessage()]); + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/redeem.php b/jweb/ac1/src/api/redeem.php new file mode 100644 index 0000000000000000000000000000000000000000..74134a556d0f661ea414b4fb6e2c543094de898e --- /dev/null +++ b/jweb/ac1/src/api/redeem.php @@ -0,0 +1,47 @@ + false, 'message' => 'Not logged in']); + exit; +} + +// Include database connection +require_once '../../db.php'; +require_once '../classes/User.php'; +require_once '../classes/Transaction.php'; + +$database = new Database(); +$db = $database->getConnection(); +$user = new User($db); +$transaction = new Transaction($db); + +if ($user->getUserByUsername($_SESSION['username'])) { + // Process redemption + if ($user->rewards > 0) { + $amount = $user->rewards; + + // Add rewards to balance and reset rewards + $user->updateBalance($amount); + $user->updateRewards(-$amount); + + // Create transaction record + $transaction->user_id = $user->id; + $transaction->type = 'bonus'; + $transaction->amount = $amount; + $transaction->description = "Rewards redemption"; + $transaction->status = 'completed'; + + if ($transaction->create()) { + echo json_encode(['success' => true, 'message' => 'Rewards redeemed successfully']); + } else { + echo json_encode(['success' => false, 'message' => 'Failed to record transaction']); + } + } else { + echo json_encode(['success' => false, 'message' => 'No rewards to redeem']); + } +} else { + echo json_encode(['success' => false, 'message' => 'User not found']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/register.php b/jweb/ac1/src/api/register.php new file mode 100644 index 0000000000000000000000000000000000000000..827556b038b9442988eef0ded51973e0bf5501dd --- /dev/null +++ b/jweb/ac1/src/api/register.php @@ -0,0 +1,39 @@ +getConnection(); + +$referrer = null; +$referral_code = isset($_GET['ref']) ? $_GET['ref'] : ''; + +// Check if referral code is valid +if (!empty($referral_code)) { + $user = new User($db); + $referrer = $user->getUserByReferralCode($referral_code); +} + +if ($_POST) { + // Handle registration logic here + $username = $_POST['username']; + $email = $_POST['email']; + $password = password_hash($_POST['password'], PASSWORD_DEFAULT); + + // Create new user + $new_user = new User($db); + // ... your registration logic + + // If registration successful and referral code was used + if (!empty($referral_code) && $referrer) { + $referral = new Referral($db); + $referral->createReferral($referrer['id'], $new_user_id); + + $_SESSION['message'] = "Welcome! You were referred by " . $referrer['username']; + } +} +?> + + \ No newline at end of file diff --git a/jweb/ac1/src/api/revoke_token.php b/jweb/ac1/src/api/revoke_token.php new file mode 100644 index 0000000000000000000000000000000000000000..b1489de140060cd24f67e0ac53ca8877cfda5ed0 --- /dev/null +++ b/jweb/ac1/src/api/revoke_token.php @@ -0,0 +1,23 @@ + false, 'message' => 'Not logged in']); + exit; +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $token_id = (int) $_POST['token_id']; + $user_id = $_SESSION['user_id']; + + $sql = "UPDATE access_tokens SET status = 'revoked' WHERE id = ? AND user_id = ?"; + $stmt = $conn->prepare($sql); + $stmt->bind_param("ii", $token_id, $user_id); + + if ($stmt->execute()) { + echo json_encode(['success' => true]); + } else { + echo json_encode(['success' => false, 'message' => 'Failed to revoke token']); + } +} diff --git a/jweb/ac1/src/api/settings_handler.php b/jweb/ac1/src/api/settings_handler.php new file mode 100644 index 0000000000000000000000000000000000000000..d860b2b1a3aed9f956173590b2cb8c92b8d838be --- /dev/null +++ b/jweb/ac1/src/api/settings_handler.php @@ -0,0 +1,77 @@ +getConnection(); + +$user_id = $_SESSION['user_id']; + +if($_POST) { + try { + // Update general settings + if(isset($_POST['dark_mode']) || isset($_POST['language']) || isset($_POST['currency']) || isset($_POST['auto_logout'])) { + $dark_mode = isset($_POST['dark_mode']) ? 1 : 0; + $language = $_POST['language'] ?? 'en'; + $currency = $_POST['currency'] ?? 'KES'; + $auto_logout = isset($_POST['auto_logout']) ? 1 : 0; + + $query = "INSERT INTO user_settings (user_id, dark_mode, language, currency, auto_logout) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + dark_mode = VALUES(dark_mode), + language = VALUES(language), + currency = VALUES(currency), + auto_logout = VALUES(auto_logout)"; + + $stmt = $db->prepare($query); + $stmt->execute([$user_id, $dark_mode, $language, $currency, $auto_logout]); + + $_SESSION['success'] = "Settings updated successfully!"; + } + + // Handle password change + if(isset($_POST['current_password']) && isset($_POST['new_password'])) { + $current_password = $_POST['current_password']; + $new_password = $_POST['new_password']; + + // Verify current password + $query = "SELECT password_hash FROM users WHERE id = ?"; + $stmt = $db->prepare($query); + $stmt->execute([$user_id]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if(password_verify($current_password, $user['password_hash'])) { + $new_password_hash = password_hash($new_password, PASSWORD_BCRYPT); + + $query = "UPDATE users SET password_hash = ? WHERE id = ?"; + $stmt = $db->prepare($query); + $stmt->execute([$new_password_hash, $user_id]); + + $_SESSION['success'] = "Password updated successfully!"; + } else { + $_SESSION['error'] = "Current password is incorrect!"; + } + } + + } catch(PDOException $exception) { + $_SESSION['error'] = "Error updating settings: " . $exception->getMessage(); + } + + header("Location: ../pages/settings.php"); + exit(); +} + +// Get user settings +function getUserSettings($db, $user_id) { + $query = "SELECT * FROM user_settings WHERE user_id = ?"; + $stmt = $db->prepare($query); + $stmt->execute([$user_id]); + return $stmt->fetch(PDO::FETCH_ASSOC) ?: []; +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/submit_ticket.php b/jweb/ac1/src/api/submit_ticket.php new file mode 100644 index 0000000000000000000000000000000000000000..2129a22fe43678b681b03209c248e53301d9249d --- /dev/null +++ b/jweb/ac1/src/api/submit_ticket.php @@ -0,0 +1,124 @@ + false, 'message' => 'Unauthorized']); + exit; +} + +// Include database and classes +include_once '../../db.php'; +include_once '../models/User.php'; +include_once '../models/SupportTicket.php'; + +// Define TicketHandler class properly +class TicketHandler { + public static function notifyCustomerCare($ticket_number, $data) { + // Build message + $message = "New Support Ticket Created:\n"; + $message .= "Ticket Number: $ticket_number\n"; + $message .= "Issue Type: " . $data['issue_type'] . "\n"; + $message .= "Subject: " . $data['subject'] . "\n"; + $message .= "Priority: " . $data['priority'] . "\n"; + + // Example: Send email notification + $to = "customercare@jmotors.com"; + $subject = "New Support Ticket: $ticket_number"; + $headers = "From: support@jmotors.com\r\n"; + + @mail($to, $subject, $message, $headers); + + // Optional: Add WhatsApp, Slack, or API integration here + } +} + +// Get POST data +$data = json_decode(file_get_contents('php://input'), true); + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + try { + // Validate required fields + $required = ['issue_type', 'subject', 'description', 'priority']; + foreach ($required as $field) { + if (empty($data[$field])) { + throw new Exception("Missing required field: $field"); + } + } + + // Initialize database connection + $database = new Database(); + $db = $database->getConnection(); + + // Sync user data + $user = new User($db); + $user_id = $user->syncUser( + $_SESSION['username'], + $_SESSION['email'], + $_SESSION['tier'], + $_SESSION['package'] + ); + + if (!$user_id) { + throw new Exception("Failed to sync user data"); + } + + // Handle file uploads + $attachments = []; + if (!empty($_FILES['attachments'])) { + $upload_dir = '../uploads/support/'; + if (!is_dir($upload_dir)) { + mkdir($upload_dir, 0755, true); + } + + foreach ($_FILES['attachments']['tmp_name'] as $key => $tmp_name) { + if ($_FILES['attachments']['error'][$key] === UPLOAD_ERR_OK) { + $file_name = time() . '_' . basename($_FILES['attachments']['name'][$key]); + $file_path = $upload_dir . $file_name; + + if (move_uploaded_file($tmp_name, $file_path)) { + $attachments[] = $file_path; + } + } + } + } + + // Create support ticket + $ticket = new SupportTicket($db); + $ticket->user_id = $user_id; + $ticket->issue_type = $data['issue_type']; + $ticket->subject = $data['subject']; + $ticket->description = $data['description']; + $ticket->priority = $data['priority']; + $ticket->attachments = json_encode($attachments); + + $ticket_number = $ticket->create(); + + if ($ticket_number) { + // ✅ Correct way to call notification + TicketHandler::notifyCustomerCare($ticket_number, $data); + + echo json_encode([ + 'success' => true, + 'message' => 'Support ticket submitted successfully', + 'ticket_number' => $ticket_number + ]); + } else { + throw new Exception("Failed to create support ticket"); + } + + + } catch (Exception $e) { + http_response_code(400); + echo json_encode([ + 'success' => false, + 'message' => $e->getMessage() + ]); + } +} else { + http_response_code(405); + echo json_encode(['success' => false, 'message' => 'Method not allowed']); +} +?> diff --git a/jweb/ac1/src/api/upload-functions.php b/jweb/ac1/src/api/upload-functions.php new file mode 100644 index 0000000000000000000000000000000000000000..e438569b169c8632f548ae11e9e5d5f55d4e9d17 --- /dev/null +++ b/jweb/ac1/src/api/upload-functions.php @@ -0,0 +1,114 @@ +prepare("SELECT * FROM uploads WHERE user_id = ? AND created_at >= ? ORDER BY created_at DESC"); + $stmt->bind_param("is", $userId, $today); + $stmt->execute(); + $result = $stmt->get_result(); + + $uploads = []; + while ($row = $result->fetch_assoc()) { + $uploads[] = $row; + } + return $uploads; +} + +// =================================== +// Get today's upload stats for a user +// =================================== +function getTodaysUploadStats($userId) { + global $conn; + $today = date('Y-m-d 00:00:00'); + + $stmt = $conn->prepare(" + SELECT + COUNT(*) AS total_uploads, + SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) AS approved_uploads, + IFNULL(SUM(reward_amount), 0) AS total_earnings + FROM uploads + WHERE user_id = ? AND created_at >= ? + "); + $stmt->bind_param("is", $userId, $today); + $stmt->execute(); + $result = $stmt->get_result(); + + return $result->fetch_assoc(); +} + +// =================================== +// Process file upload +// =================================== +function processFileUpload($userId, $fileData) { + global $conn; + + $uploadDir = __DIR__ . '/../uploads/'; + if (!is_dir($uploadDir)) { + if (!mkdir($uploadDir, 0775, true)) { + return ['success' => false, 'error' => 'Failed to create upload directory.']; + } + } + + $fileName = uniqid() . '_' . basename($fileData['name']); + $filePath = $uploadDir . $fileName; + + if (move_uploaded_file($fileData['tmp_name'], $filePath)) { + // Calculate reward + $rewardAmount = calculateRewardAmount($fileData['type'], $fileData['size']); + + // Save upload record + $stmt = $conn->prepare(" + INSERT INTO uploads (user_id, file_name, file_type, file_size, status, reward_amount, created_at) + VALUES (?, ?, ?, ?, 'approved', ?, NOW()) + "); + $stmt->bind_param("issid", $userId, $fileName, $fileData['type'], $fileData['size'], $rewardAmount); + $stmt->execute(); + + // Update user earnings + $stmt2 = $conn->prepare("UPDATE users SET meta_earnings = meta_earnings + ? WHERE id = ?"); + $stmt2->bind_param("di", $rewardAmount, $userId); + $stmt2->execute(); + + return [ + 'success' => true, + 'upload_id' => $conn->insert_id, + 'reward' => $rewardAmount + ]; + } else { + return ['success' => false, 'error' => 'File upload failed. Check permissions.']; + } +} + +// =================================== +// Calculate reward amount +// =================================== +function calculateRewardAmount($fileType, $fileSize) { + $baseReward = 50; // KES per file + $typeBonus = 0; + + if (strpos($fileType, 'image/') === 0) { + $typeBonus = 10; + } elseif (strpos($fileType, 'video/') === 0) { + $typeBonus = 20; + } + + $sizeBonus = 0; + if ($fileSize > 10 * 1024 * 1024) { + $sizeBonus = 15; + } elseif ($fileSize > 5 * 1024 * 1024) { + $sizeBonus = 10; + } elseif ($fileSize > 1 * 1024 * 1024) { + $sizeBonus = 5; + } + + return $baseReward + $typeBonus + $sizeBonus; +} +?> diff --git a/jweb/ac1/src/api/upload_handler.php b/jweb/ac1/src/api/upload_handler.php new file mode 100644 index 0000000000000000000000000000000000000000..30196e07289176707ad5bec1afcaa6d125167258 --- /dev/null +++ b/jweb/ac1/src/api/upload_handler.php @@ -0,0 +1,68 @@ +uploadDir)) { + mkdir($this->uploadDir, 0755, true); + } + } + + public function upload($file) { + try { + // Check for errors + if ($file['error'] !== UPLOAD_ERR_OK) { + throw new Exception('Upload error: ' . $file['error']); + } + + // Check file size + if ($file['size'] > $this->maxSize) { + throw new Exception('File size exceeds maximum limit of 5MB'); + } + + // Check file type + $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + if (!in_array($fileExtension, $this->allowedTypes)) { + throw new Exception('Invalid file type. Allowed types: ' . implode(', ', $this->allowedTypes)); + } + + // Generate unique filename + $filename = uniqid() . '_' . time() . '.' . $fileExtension; + $filepath = $this->uploadDir . $filename; + + // Move uploaded file + if (!move_uploaded_file($file['tmp_name'], $filepath)) { + throw new Exception('Failed to move uploaded file'); + } + + return $filename; + + } catch (Exception $e) { + error_log("File Upload Error: " . $e->getMessage()); + return false; + } + } +} + +// Handle file upload via AJAX +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['evidence_file'])) { + $uploader = new FileUploader(); + $filename = $uploader->upload($_FILES['evidence_file']); + + if ($filename) { + echo json_encode(['success' => true, 'filename' => $filename]); + } else { + echo json_encode(['success' => false, 'message' => 'File upload failed']); + } + exit; +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/validate.php b/jweb/ac1/src/api/validate.php new file mode 100644 index 0000000000000000000000000000000000000000..f4d0e4f58cf41da4556293777b0e33a0691477de --- /dev/null +++ b/jweb/ac1/src/api/validate.php @@ -0,0 +1,47 @@ + 'No token provided']); + exit; +} + +// Get request details +$endpoint = $_SERVER['REQUEST_URI']; +$ip_address = $_SERVER['REMOTE_ADDR']; +$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? ''; +$method = $_SERVER['REQUEST_METHOD']; + +// Validate token +$result = $tokenManager->validateAndLogUsage($token, $endpoint, $ip_address, $user_agent, $method); + +if ($result['valid']) { + echo json_encode([ + 'valid' => true, + 'user' => $result['username'], + 'permissions' => $result['permissions'], + 'timestamp' => date('c') + ]); +} else { + http_response_code(401); + echo json_encode([ + 'valid' => false, + 'error' => $result['error'] + ]); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/api/withdraw.php b/jweb/ac1/src/api/withdraw.php new file mode 100644 index 0000000000000000000000000000000000000000..fb12628b58a19f6efad248343429829e8e8e4701 --- /dev/null +++ b/jweb/ac1/src/api/withdraw.php @@ -0,0 +1,57 @@ + false, 'message' => 'Not logged in']); + exit; +} + +// Get JSON input +$input = json_decode(file_get_contents('php://input'), true); + +if (!isset($input['amount']) || !isset($input['method'])) { + echo json_encode(['success' => false, 'message' => 'Invalid input']); + exit; +} + +// Include database connection +require_once '../../db.php'; +require_once '../classes/User.php'; +require_once '../classes/Transaction.php'; + +$database = new Database(); +$db = $database->getConnection(); +$user = new User($db); +$transaction = new Transaction($db); + +if ($user->getUserByUsername($_SESSION['username'])) { + // Process withdrawal + $amount = floatval($input['amount']); + $method = $input['method']; + + // Check if user has enough balance + if ($user->balance >= $amount) { + // Deduct amount from balance and add to withdrawals + $user->updateBalance(-$amount); + $user->updateWithdrawals($amount); + + // Create transaction record + $transaction->user_id = $user->id; + $transaction->type = 'withdrawal'; + $transaction->amount = $amount; + $transaction->description = "Withdrawal via $method"; + $transaction->status = 'pending'; // Withdrawals might need approval + + if ($transaction->create()) { + echo json_encode(['success' => true, 'message' => 'Withdrawal request submitted']); + } else { + echo json_encode(['success' => false, 'message' => 'Failed to record transaction']); + } + } else { + echo json_encode(['success' => false, 'message' => 'Insufficient balance']); + } +} else { + echo json_encode(['success' => false, 'message' => 'User not found']); +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/classes/Transaction.php b/jweb/ac1/src/classes/Transaction.php new file mode 100644 index 0000000000000000000000000000000000000000..6222730837c838bb38b965bf86d82e68b4043a94 --- /dev/null +++ b/jweb/ac1/src/classes/Transaction.php @@ -0,0 +1,82 @@ +conn = $db; + } + + // Create a new transaction + public function create() { + $query = "INSERT INTO " . $this->table_name . " + SET user_id=:user_id, type=:type, amount=:amount, + description=:description, status=:status, reference=:reference"; + + $stmt = $this->conn->prepare($query); + + // Sanitize inputs + $this->user_id = htmlspecialchars(strip_tags($this->user_id)); + $this->type = htmlspecialchars(strip_tags($this->type)); + $this->amount = htmlspecialchars(strip_tags($this->amount)); + $this->description = htmlspecialchars(strip_tags($this->description)); + $this->status = htmlspecialchars(strip_tags($this->status)); + $this->reference = htmlspecialchars(strip_tags($this->reference)); + + // Bind values + $stmt->bindParam(":user_id", $this->user_id); + $stmt->bindParam(":type", $this->type); + $stmt->bindParam(":amount", $this->amount); + $stmt->bindParam(":description", $this->description); + $stmt->bindParam(":status", $this->status); + $stmt->bindParam(":reference", $this->reference); + + if($stmt->execute()) { + return true; + } + return false; + } + + // Get transactions by user ID + public function getTransactionsByUserId($user_id, $limit = 10) { + $query = "SELECT * FROM " . $this->table_name . " + WHERE user_id = ? + ORDER BY created_at DESC + LIMIT ?"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->bindParam(2, $limit, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt; + } + + // Get transactions by type + public function getTransactionsByType($user_id, $type, $limit = 10) { + $query = "SELECT * FROM " . $this->table_name . " + WHERE user_id = ? AND type = ? + ORDER BY created_at DESC + LIMIT ?"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->bindParam(2, $type); + $stmt->bindParam(3, $limit, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt; + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/classes/User.php b/jweb/ac1/src/classes/User.php new file mode 100644 index 0000000000000000000000000000000000000000..ccd0638231b181a75f3279057d7e9afd4d775030 --- /dev/null +++ b/jweb/ac1/src/classes/User.php @@ -0,0 +1,127 @@ +conn = $db; + } + + // Get user by ID + public function getUserById($id) { + $query = "SELECT * FROM " . $this->table_name . " WHERE id = ? LIMIT 0,1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $id); + $stmt->execute(); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if($row) { + $this->id = $row['id']; + $this->username = $row['username']; + $this->email = $row['email']; + $this->tier = $row['tier']; + $this->package = $row['package']; + $this->balance = $row['balance']; + $this->total_deposits = $row['total_deposits']; + $this->total_withdrawals = $row['total_withdrawals']; + $this->rewards = $row['rewards']; + return true; + } + return false; + } + + // Get user by username + public function getUserByUsername($username) { + $query = "SELECT * FROM " . $this->table_name . " WHERE username = ? LIMIT 0,1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $username); + $stmt->execute(); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if($row) { + $this->id = $row['id']; + $this->username = $row['username']; + $this->email = $row['email']; + $this->tier = $row['tier']; + $this->package = $row['package']; + $this->balance = $row['balance']; + $this->total_deposits = $row['total_deposits']; + $this->total_withdrawals = $row['total_withdrawals']; + $this->rewards = $row['rewards']; + return true; + } + return false; + } + + // Update user balance + public function updateBalance($amount) { + $query = "UPDATE " . $this->table_name . " SET balance = balance + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->balance += $amount; + return true; + } + return false; + } + + // Update user deposits + public function updateDeposits($amount) { + $query = "UPDATE " . $this->table_name . " SET total_deposits = total_deposits + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->total_deposits += $amount; + return true; + } + return false; + } + + // Update user withdrawals + public function updateWithdrawals($amount) { + $query = "UPDATE " . $this->table_name . " SET total_withdrawals = total_withdrawals + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->total_withdrawals += $amount; + return true; + } + return false; + } + + // Update user rewards + public function updateRewards($amount) { + $query = "UPDATE " . $this->table_name . " SET rewards = rewards + ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $amount); + $stmt->bindParam(2, $this->id); + + if($stmt->execute()) { + $this->rewards += $amount; + return true; + } + return false; + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/config/database.php b/jweb/ac1/src/config/database.php new file mode 100644 index 0000000000000000000000000000000000000000..12b745365fbcfdf22689fc67ec2b833675be73f4 --- /dev/null +++ b/jweb/ac1/src/config/database.php @@ -0,0 +1,25 @@ +conn = null; + + try { + $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password); + $this->conn->exec("set names utf8"); + $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } catch(PDOException $exception) { + echo "Connection error: " . $exception->getMessage(); + } + + return $this->conn; + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/models/Referral.php b/jweb/ac1/src/models/Referral.php new file mode 100644 index 0000000000000000000000000000000000000000..636f33523c85c4186dc0b20fc373e8886b448898 --- /dev/null +++ b/jweb/ac1/src/models/Referral.php @@ -0,0 +1,47 @@ +conn = $db; + } + + // Create new referral + public function createReferral($referrer_id, $referred_id) { + $query = "INSERT INTO " . $this->table_name . " + (referrer_id, referred_id, status) + VALUES (?, ?, 'pending')"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $referrer_id); + $stmt->bindParam(2, $referred_id); + return $stmt->execute(); + } + + // Complete referral (when referred user makes deposit) + public function completeReferral($referred_id, $commission_amount) { + $query = "UPDATE " . $this->table_name . " + SET status = 'completed', commission_earned = ? + WHERE referred_id = ? AND status = 'pending'"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $commission_amount); + $stmt->bindParam(2, $referred_id); + return $stmt->execute(); + } + + // Get referral by referred user ID + public function getReferralByReferredId($referred_id) { + $query = "SELECT * FROM " . $this->table_name . " WHERE referred_id = ? LIMIT 1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $referred_id); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/models/TokenManager.php b/jweb/ac1/src/models/TokenManager.php new file mode 100644 index 0000000000000000000000000000000000000000..a02fd2b011a1e3e4869d9317b9414d9703689621 --- /dev/null +++ b/jweb/ac1/src/models/TokenManager.php @@ -0,0 +1,156 @@ +conn = $database->getConnection(); + + // Create tables if they don't exist + $this->createTablesIfNotExist(); + } + + private function createTablesIfNotExist() { + try { + // Create access_tokens table + $query = "CREATE TABLE IF NOT EXISTS access_tokens ( + id INT PRIMARY KEY AUTO_INCREMENT, + user_id INT NOT NULL, + token_name VARCHAR(100) NOT NULL, + token_value VARCHAR(255) UNIQUE NOT NULL, + permissions JSON NOT NULL, + ip_restrictions TEXT, + expires_at TIMESTAMP NULL, + is_active BOOLEAN DEFAULT TRUE, + last_used TIMESTAMP NULL, + usage_count INT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )"; + $this->conn->exec($query); + + // Create token_usage_logs table + $query = "CREATE TABLE IF NOT EXISTS token_usage_logs ( + id INT PRIMARY KEY AUTO_INCREMENT, + token_id INT NOT NULL, + user_id INT NOT NULL, + endpoint VARCHAR(100) NOT NULL, + ip_address VARCHAR(45), + user_agent TEXT, + request_method VARCHAR(10), + response_code INT, + processing_time_ms INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + )"; + $this->conn->exec($query); + + } catch (PDOException $e) { + error_log("Table creation error: " . $e->getMessage()); + } + } + + private function generateToken() { + return 'jm_' . bin2hex(random_bytes(24)); + } + + public function createToken($user_id, $token_name, $permissions, $expires_in_days = 30, $ip_restrictions = null) { + try { + $token_value = $this->generateToken(); + + $expires_at = null; + if ($expires_in_days > 0) { + $expires_at = date('Y-m-d H:i:s', strtotime("+{$expires_in_days} days")); + } + + $query = "INSERT INTO {$this->table_tokens} + (user_id, token_name, token_value, permissions, ip_restrictions, expires_at) + VALUES (:user_id, :token_name, :token_value, :permissions, :ip_restrictions, :expires_at)"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->bindParam(":token_name", $token_name); + $stmt->bindParam(":token_value", $token_value); + $stmt->bindParam(":permissions", json_encode($permissions)); + $stmt->bindParam(":ip_restrictions", $ip_restrictions); + $stmt->bindParam(":expires_at", $expires_at); + + if ($stmt->execute()) { + return [ + 'success' => true, + 'token' => $token_value, + 'id' => $this->conn->lastInsertId() + ]; + } + } catch (PDOException $e) { + error_log("Token creation error: " . $e->getMessage()); + } + + return ['success' => false, 'message' => 'Failed to create token']; + } + + public function getUserTokens($user_id) { + try { + $query = "SELECT * FROM {$this->table_tokens} + WHERE user_id = :user_id AND is_active = TRUE + ORDER BY created_at DESC"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->execute(); + + $tokens = []; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $row['permissions'] = json_decode($row['permissions'], true) ?? []; + $row['is_expired'] = $row['expires_at'] && strtotime($row['expires_at']) < time(); + $tokens[] = $row; + } + + return $tokens; + } catch (PDOException $e) { + error_log("Get tokens error: " . $e->getMessage()); + return []; + } + } + + public function revokeToken($token_id, $user_id) { + try { + $query = "UPDATE {$this->table_tokens} SET is_active = FALSE + WHERE id = :token_id AND user_id = :user_id"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":token_id", $token_id); + $stmt->bindParam(":user_id", $user_id); + + return $stmt->execute(); + } catch (PDOException $e) { + error_log("Revoke token error: " . $e->getMessage()); + return false; + } + } + + public function getRealtimeStats($user_id, $hours = 24) { + try { + $query = "SELECT + COUNT(*) as total_calls, + AVG(processing_time_ms) as avg_response_time + FROM {$this->table_usage} + WHERE user_id = :user_id + AND created_at >= DATE_SUB(NOW(), INTERVAL :hours HOUR)"; + + $stmt = $this->conn->prepare($query); + $stmt->bindParam(":user_id", $user_id); + $stmt->bindParam(":hours", $hours); + $stmt->execute(); + + return $stmt->fetch(PDO::FETCH_ASSOC) ?: ['total_calls' => 0, 'avg_response_time' => 0]; + } catch (PDOException $e) { + error_log("Stats error: " . $e->getMessage()); + return ['total_calls' => 0, 'avg_response_time' => 0]; + } + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/models/User.php b/jweb/ac1/src/models/User.php new file mode 100644 index 0000000000000000000000000000000000000000..d608d2adceb12b118e650c6bfe87fee8c7899c6c --- /dev/null +++ b/jweb/ac1/src/models/User.php @@ -0,0 +1,116 @@ +conn = $db; + } + + // Generate unique referral code + private function generateReferralCode($username) { + $base_code = strtoupper(substr(preg_replace('/[^a-zA-Z0-9]/', '', $username), 0, 3)); + $random_number = mt_rand(100, 999); + return $base_code . $random_number; + } + + // Get user by ID + public function getUserById($user_id) { + $query = "SELECT * FROM " . $this->table_name . " WHERE id = ? LIMIT 1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->execute(); + + if ($stmt->rowCount() > 0) { + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $this->id = $row['id']; + $this->username = $row['username']; + $this->email = $row['email']; + $this->tier = $row['tier']; + $this->package = $row['package']; + $this->balance = $row['balance']; + $this->total_deposits = $row['total_deposits']; + $this->total_withdrawals = $row['total_withdrawals']; + $this->rewards = $row['rewards']; + $this->referral_code = $row['referral_code']; + $this->referred_by = $row['referred_by']; + return true; + } + return false; + } + + // Get user by referral code + public function getUserByReferralCode($referral_code) { + $query = "SELECT id, username FROM " . $this->table_name . " WHERE referral_code = ? LIMIT 1"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $referral_code); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + // Create referral code for user + public function createReferralCode($user_id) { + $user = $this->getUserById($user_id); + if (!$user) return false; + + if (empty($this->referral_code)) { + $referral_code = $this->generateReferralCode($this->username); + + // Ensure uniqueness + while ($this->getUserByReferralCode($referral_code)) { + $referral_code = $this->generateReferralCode($this->username); + } + + $query = "UPDATE " . $this->table_name . " SET referral_code = ? WHERE id = ?"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $referral_code); + $stmt->bindParam(2, $user_id); + + if ($stmt->execute()) { + $this->referral_code = $referral_code; + return $referral_code; + } + } + return $this->referral_code; + } + + // Get team members + public function getTeamMembers($user_id) { + $query = "SELECT u.username, u.email, u.tier, u.created_at, r.status, r.commission_earned + FROM users u + JOIN referrals r ON u.id = r.referred_id + WHERE r.referrer_id = ? + ORDER BY u.created_at DESC"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + // Get team statistics + public function getTeamStats($user_id) { + $query = "SELECT + COUNT(*) as total_members, + SUM(CASE WHEN DATE(r.created_at) = CURDATE() THEN 1 ELSE 0 END) as active_today, + COALESCE(SUM(r.commission_earned), 0) as team_earnings + FROM referrals r + WHERE r.referrer_id = ? AND r.status = 'completed'"; + $stmt = $this->conn->prepare($query); + $stmt->bindParam(1, $user_id); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC); + } +} +?> \ No newline at end of file diff --git a/jweb/ac1/src/pages/access-token.php b/jweb/ac1/src/pages/access-token.php new file mode 100644 index 0000000000000000000000000000000000000000..3a071a4db7ab42cf8260ba4d00e6e5deaed004c3 --- /dev/null +++ b/jweb/ac1/src/pages/access-token.php @@ -0,0 +1,927 @@ +createToken($user_id, $token_name, $permissions, $expiry_days, $ip_restrictions); + + if ($result['success']) { + $_SESSION['new_token'] = $result['token']; + $_SESSION['token_details'] = [ + 'name' => $token_name, + 'expiry' => $expiry_days == 0 ? 'Never' : $expiry_days . ' days', + 'permissions' => implode(', ', $permissions) + ]; + header('Location: access-token.php?success=1'); + exit; + } + } + + if (isset($_POST['revoke_token'])) { + $token_id = $_POST['token_id']; + $tokenManager->revokeToken($token_id, $user_id); + header('Location: access-token.php?revoked=1'); + exit; + } +} + +// Get user's active tokens +$user_tokens = $tokenManager->getUserTokens($user_id); +$active_tokens_count = count($user_tokens); +$token_limit = 5; + +// Get real-time stats +$realtime_stats = $tokenManager->getRealtimeStats($user_id, 24); +?> + + + + + + + Japanese Motors — Access Token + + + + + + + +
+ + + + + +
+
+
+ +
jmotors
+
+ +
+ +
+ + +
+ Token generated successfully! +
+ + + +
+ Token revoked successfully! +
+ + + + +
+
+

+ Generate New Token +

+ +
+
+

Active Tokens

+

/

+
+
+

Token Limit

+

Maximum

+
+
+ + = $token_limit): ?> +
+ + You've reached the maximum number of active tokens. Please revoke an existing token to create a new one. +
+ +
+ + +
+ + +

Use a descriptive name to identify this token's purpose

+
+ +
+ + +
+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ + +

Comma-separated list of IP addresses or ranges

+
+ +
+ + +
+ +
+ +

Keep your tokens secure and never share them publicly. Each token will be shown only once after generation.

+
+ + +
+ +
+ +
+

+ Active Tokens +

+ + +
+

Last 24 Hours

+
+
+

API Calls

+

+
+
+

Avg. Response

+

ms

+
+
+
+ + +
+ +

No active tokens

+
+ +
+ +
+
+
+

+

permissions

+
+
+

+ +

+

+ +

+
+
+
+

Created:

+

Used: times

+
+
+ + +
+
+ +
+ + + + +
+

+ Token Security +

+
    +
  • + + Never share tokens in public repositories +
  • +
  • + + Rotate tokens regularly +
  • +
  • + + Use IP restrictions when possible +
  • +
+
+
+
+ +
+

+ Token Information +

+ +
+
+

What are access tokens used for?

+ +
+
+

Access tokens allow third-party applications to interact with the Jmotors API on your behalf. They provide a secure way to authenticate requests without sharing your password.

+
+
+ +
+
+

How do I use an access token?

+ +
+
+

Include the token in the Authorization header: Authorization: Bearer YOUR_TOKEN_HERE. Always use HTTPS for security.

+
+
+ +
+
+

What if my token is compromised?

+ +
+
+

Revoke it immediately from this page and generate a new one. Monitor your account for suspicious activity.

+
+
+
+
+
+ + + +
+ +
+ + + + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/agent-approval.php b/jweb/ac1/src/pages/agent-approval.php new file mode 100644 index 0000000000000000000000000000000000000000..7e788a7f6090adf5ece43a71da96f09a293271d8 --- /dev/null +++ b/jweb/ac1/src/pages/agent-approval.php @@ -0,0 +1,958 @@ + 0, + 'approved' => 5, + 'rejected' => 2, + 'documents_needed' => 0, + 'total_applications' => 7, + 'commission_this_month' => 12500.00, + 'total_commission' => 45000.00 + ]; + + // Mock functions for testing + function reviewAgentApplication($agentId, $adminId, $status, $notes = '') { + return true; // Mock success + } + + function searchAgents($sponsorId, $searchTerm) { + return []; // Mock empty results + } +} else { + // Include agent functions + require_once $agentFunctionsPath; + + // Handle form submissions + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (isset($_POST['action'])) { + if ($_POST['action'] === 'review_agent') { + $agentId = $_POST['agent_id'] ?? 0; + $status = $_POST['status'] ?? ''; + $notes = $_POST['notes'] ?? ''; + + if (reviewAgentApplication($agentId, $userId, $status, $notes)) { + $message = "Agent application " . ($status === 'approved' ? 'approved' : 'rejected') . " successfully!"; + } else { + $error = "Failed to update agent application."; + } + } elseif ($_POST['action'] === 'search_agents') { + $searchTerm = $_POST['search_term'] ?? ''; + $agents = searchAgents($userId, $searchTerm); + $pendingAgents = $agents; // Use search results + } + } + } + + // Get pending agents and stats + if (!isset($pendingAgents)) { + $pendingAgents = getPendingAgents($userId); + } + $agentStats = getAgentStats($userId); +} + +// Debug output +error_log("User ID: " . $userId); +error_log("Pending agents count: " . count($pendingAgents)); +?> + + + + + + + Japanese Motors — Agent Approval + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ +
+ +
+ + + +
+ +
+ + + + +
+
+

+ Pending Approvals +

+ +
+
+

Pending Applications

+

+
+
+

Approved This Month

+

Agents

+
+
+ + + +
+ +
+

No pending agent applications

+

New agent applications will appear here for review

+
+ + +
+
+
+

+

+
+ + + +
+
+
+

Applied On

+

+
+
+

Location

+

+
+
+
+ + +
+
+ + +
+ + +
+ +
+

+ Approval Stats +

+ +
+
+ Total Applications + +
+
+
+
+
+ +
+
+ Approved + +
+
+
+
+
+ +
+
+ Rejected + +
+
+
+
+
+ +
+
+ Pending + +
+
+
+
+
+ +
+

+ Commission Earnings +

+
+
+ This Month + KES +
+
+ Total Earned + KES +
+
+
+ + +
+
+ +
+

+ Agent Approval Guidelines +

+ +
+
+

What are the requirements for agent approval?

+ +
+
+

Agents must meet the following criteria: valid government ID, minimum age of 18 years, completed training program, and minimum initial deposit of 1,000 KES. Additional verification may be required based on location and other factors.

+
+
+ +
+
+

How long does the approval process take?

+ +
+
+

The standard approval process takes 24-48 hours after all required documents are submitted. Delays may occur if additional verification is needed. You will receive a notification once the application has been processed.

+
+
+ +
+
+

What commissions do I earn from approved agents?

+ +
+
+

You earn 10% commission on your direct agents' earnings for the first 3 months, then 5% ongoing. Additionally, you receive 2% override commission on your team's second level agents. Commissions are paid weekly every Monday.

+
+
+
+
+
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/agent-claim.php b/jweb/ac1/src/pages/agent-claim.php new file mode 100644 index 0000000000000000000000000000000000000000..1ea1f98b3cc64f44095f0ac1215fc1a30dbeabce --- /dev/null +++ b/jweb/ac1/src/pages/agent-claim.php @@ -0,0 +1,1357 @@ +getConnection(); + +// Initialize variables with default values +$username = $_SESSION['username'] ?? 'User'; +$email = $_SESSION['email'] ?? ''; +$tier = $_SESSION['tier'] ?? 'basic'; +$package = $_SESSION['package'] ?? 'Starter'; +$balance = $_SESSION['balance'] ?? 0; +$total_deposits = $_SESSION['total_deposits'] ?? 0; +$total_withdrawals = $_SESSION['total_withdrawals'] ?? 0; +$rewards = $_SESSION['rewards'] ?? 0; +$earnings = $total_deposits - $total_withdrawals; + + +try { + // Query to get user data + $query = "SELECT u.*, up.first_name, up.last_name, up.profile_picture + FROM users u + LEFT JOIN user_profiles up ON u.id = up.user_id + WHERE u.id = :user_id"; + + $stmt = $db->prepare($query); + $stmt->bindParam(':user_id', $user_id); + $stmt->execute(); + + if ($stmt->rowCount() > 0) { + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + // Assign session variables and local variables + $username = $_SESSION['username'] = $user['username']; + $email = $_SESSION['email'] = $user['email']; + $tier = $_SESSION['tier'] = $user['tier']; + $package = $_SESSION['package'] = $user['package']; + $balance = $_SESSION['balance'] = $user['balance']; + $total_deposits = $_SESSION['total_deposits'] = $user['total_deposits']; + $total_withdrawals = $_SESSION['total_withdrawals'] = $user['total_withdrawals']; + $rewards = $_SESSION['rewards'] = $user['rewards']; + $earnings = $total_deposits - $total_withdrawals; + + // Get active package info + $package_query = "SELECT p.* FROM user_packages up + JOIN packages p ON up.package_id = p.id + WHERE up.user_id = :user_id AND up.status = 'active' + ORDER BY up.end_date DESC LIMIT 1"; + + $package_stmt = $db->prepare($package_query); + $package_stmt->bindParam(':user_id', $user_id); + $package_stmt->execute(); + + if ($package_stmt->rowCount() > 0) { + $active_package = $package_stmt->fetch(PDO::FETCH_ASSOC); + $_SESSION['package_details'] = $active_package; + } + + // Get claim statistics + $claims_stats_query = " + SELECT + COUNT(*) as total_claims, + SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as approved_claims, + SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_claims, + SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected_claims, + SUM(CASE WHEN status = 'approved' THEN amount ELSE 0 END) as approved_amount, + SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_amount, + SUM(amount) as total_amount + FROM agent_claims + WHERE user_id = :user_id + "; + + $stats_stmt = $db->prepare($claims_stats_query); + $stats_stmt->bindParam(':user_id', $user_id); + $stats_stmt->execute(); + $claims_stats = $stats_stmt->fetch(PDO::FETCH_ASSOC); + + // Get pending claims + $pending_claims_query = " + SELECT ac.*, u.username, u.email + FROM agent_claims ac + JOIN users u ON ac.user_id = u.id + WHERE ac.user_id = :user_id AND ac.status = 'pending' + ORDER BY ac.created_at DESC + "; + + $pending_stmt = $db->prepare($pending_claims_query); + $pending_stmt->bindParam(':user_id', $user_id); + $pending_stmt->execute(); + $pending_claims = $pending_stmt->fetchAll(PDO::FETCH_ASSOC); + + // Get approved claims + $approved_claims_query = " + SELECT ac.*, u.username, u.email + FROM agent_claims ac + JOIN users u ON ac.user_id = u.id + WHERE ac.user_id = :user_id AND ac.status = 'approved' + ORDER BY ac.approved_at DESC + "; + + $approved_stmt = $db->prepare($approved_claims_query); + $approved_stmt->bindParam(':user_id', $user_id); + $approved_stmt->execute(); + $approved_claims = $approved_stmt->fetchAll(PDO::FETCH_ASSOC); + + // Get rejected claims + $rejected_claims_query = " + SELECT ac.*, u.username, u.email + FROM agent_claims ac + JOIN users u ON ac.user_id = u.id + WHERE ac.user_id = :user_id AND ac.status = 'rejected' + ORDER BY ac.updated_at DESC + "; + + $rejected_stmt = $db->prepare($rejected_claims_query); + $rejected_stmt->bindParam(':user_id', $user_id); + $rejected_stmt->execute(); + $rejected_claims = $rejected_stmt->fetchAll(PDO::FETCH_ASSOC); + + } else { + $error_message = "User not found in database."; + } +} catch(PDOException $exception) { + $error_message = "Database error: " . $exception->getMessage(); +} + +// Initialize empty arrays if queries failed +if (!isset($claims_stats)) { + $claims_stats = [ + 'total_claims' => 0, + 'approved_claims' => 0, + 'pending_claims' => 0, + 'rejected_claims' => 0, + 'approved_amount' => 0, + 'pending_amount' => 0, + 'total_amount' => 0 + ]; +} + +if (!isset($pending_claims)) { + $pending_claims = []; +} + +if (!isset($approved_claims)) { + $approved_claims = []; +} + +if (!isset($rejected_claims)) { + $rejected_claims = []; +} +?> + + + + + + + Japanese Motors — Agent Claims + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ +
+ +
+ + + +
+ +
+ + + + +
+
Pending Claims
+
Approved Claims
+
Rejected Claims
+
New Claim
+
+ +
+
+
+

+ Pending Claims +

+ +
+
+

Total Pending Claims

+

+
+
+

Total Amount

+

KES

+
+
+ + + +
+ + +
+
+
+

+

Claim ID: JM-

+
+ + + +
+
+
+

Claim Amount

+

KES

+
+
+

Submitted

+

+
+
+
+

Description

+

+
+
+

Claim Type

+

+
+
+ + +
+
+ + +
+ +

No pending claims to display

+

All your claims have been processed

+
+ +
+
+ +
+

+ Claims Statistics +

+ +
+
+ Total Claims + +
+
+
+
+
+ +
+
+ Approved + +
+
+
+
+
+ +
+
+ Pending + +
+
+
+
+
+ +
+
+ Rejected + +
+
+
+
+
+ +
+

+ Amount Summary +

+
+
+ Total Approved + KES +
+
+ Pending Approval + KES +
+
+ Total Processed + KES +
+
+
+ + +
+
+
+ +
+
+

+ Approved Claims +

+ + +
+ +
+
+
+

+

Claim ID: JM-

+
+ Approved +
+
+
+

Claim Amount

+

KES

+
+
+

Approved On

+

+
+
+
+

Description

+

+
+ +
+

Approval Notes

+

+
+ +
+ +
+ +
+ +

No approved claims to display

+

Approved claims will appear here

+
+ +
+
+ +
+
+

+ Rejected Claims +

+ + +
+ +
+
+
+

+

Claim ID: JM-

+
+ Rejected +
+
+
+

Claim Amount

+

KES

+
+
+

Rejected On

+

+
+
+
+

Description

+

+
+ +
+

Rejection Reason

+

+
+ +
+ +
+ +
+ +

No rejected claims to display

+

Rejected claims will appear here

+
+ +
+
+ +
+
+
+

+ Submit New Claim +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +

Upload screenshots, reports, or other proof to support your claim (Max: 5MB)

+
+ +
+ + +
+ + +
+
+ +
+

+ Claim Guidelines +

+ +
+

Commission Structure

+
    +
  • + + Direct Sales: 15% commission +
  • +
  • + + Team Override: 5% on level 1 agents +
  • +
  • + + Referral Bonus: 1,000 KES per agent +
  • +
+
+ +
+ +

Claims are processed within 3-5 business days. Ensure all information is accurate to avoid delays.

+
+ +
+

Need Help?

+

Contact support if you have questions about your claim

+ +
+
+
+
+ +
+

+ Claims FAQ +

+ +
+
+

How long does claim processing take?

+ +
+
+

Standard claims are processed within 3-5 business days. Complex claims or those requiring additional verification may take up to 10 business days. You will receive a notification once your claim has been processed.

+
+
+ +
+
+

What documents do I need to submit with my claim?

+ +
+
+

For commission claims: sales reports and transaction IDs. For bonus claims: performance metrics and achievement proof. For referral claims: agent IDs and signup dates. All claims benefit from supporting documentation like screenshots or exported reports.

+
+
+ +
+
+

When will I receive payment for approved claims?

+ +
+
+

Approved claims are paid out every Friday via your selected payment method. Payments are processed by 5 PM EAT. If your claim is approved on Thursday, you can expect payment the following day. Delays may occur during holidays or system maintenance.

+
+
+
+
+
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/customer-care.php b/jweb/ac1/src/pages/customer-care.php new file mode 100644 index 0000000000000000000000000000000000000000..46c071509aae7508cf87141d8ab48432b76607e7 --- /dev/null +++ b/jweb/ac1/src/pages/customer-care.php @@ -0,0 +1,924 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Get user's support tickets + $stmt = $pdo->prepare("SELECT * FROM support_tickets WHERE user_id = :user_id ORDER BY created_at DESC"); + $stmt->bindParam(':user_id', $_SESSION['user_id']); + $stmt->execute(); + $tickets = $stmt->fetchAll(PDO::FETCH_ASSOC); + + // Count tickets by status + $open_tickets = 0; + $in_progress_tickets = 0; + $resolved_tickets = 0; + + foreach ($tickets as $ticket) { + switch ($ticket['status']) { + case 'open': + $open_tickets++; + break; + case 'in_progress': + $in_progress_tickets++; + break; + case 'resolved': + $resolved_tickets++; + break; + } + } + +} catch(PDOException $e) { + // If database connection fails, set empty values + $tickets = []; + $open_tickets = 0; + $in_progress_tickets = 0; + $resolved_tickets = 0; + $db_error = "Database connection failed: " . $e->getMessage(); +} +?> + + + + + + Japanese Motors — Customer Care + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + + +
+
+
+
+ +
+
+

+

Open Tickets

+
+
+
+ +
+
+
+ +
+
+

+

In Progress

+
+
+
+ +
+
+
+ +
+
+

+

Resolved

+
+
+
+
+ +
+
+

+ Submit a Support Request +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +

Upload screenshots or documents that might help us understand your issue

+
+ + +
+
+ +
+

+ Contact Options +

+ +
+

+ Direct WhatsApp Support +

+

Chat directly with our support team for instant assistance

+ + Message Support + +
+ +
+

+ Email Support +

+

support@jmotors.com

+

help@jmotors.com

+

Response within 2 hours

+
+ +
+

+ Phone Support +

+

+254 756 709 823

+

+254 756 709 823

+

Available 24/7 for urgent issues

+
+ +
+

+ Office Visit +

+

Jmotors Headquarters

+

Nairobi, Kenya

+

By appointment only

+
+
+
+ + +
+

+ Your Support Tickets +

+ +
+ +
+ +

No support tickets yet. Submit your first request above.

+
+ + +
+
+

+ + + +
+

...

+
+
+ Ticket #: + + Type: + + Priority: +
+ +
+
+ + +
+
+ +
+

+ Frequently Asked Questions +

+ +
+
+

How do I reset my password?

+ +
+
+

To reset your password, go to the login page and click on "Forgot Password". Enter your registered email address and follow the instructions sent to your email. If you don't receive the email within 5 minutes, check your spam folder or contact support.

+
+
+ +
+
+

Why is my withdrawal pending?

+ +
+
+

Withdrawals typically process within 24 hours. If it's taking longer, it might be undergoing security verification. Contact support if pending for more than 48 hours. Make sure you've completed the required 5 uploads in the last 7 days to be eligible for withdrawal.

+
+
+ +
+
+

How do I join the affiliate program?

+ +
+
+

To join our affiliate program, go to your dashboard, click on "Team" and then "Become an Affiliate". Follow the registration process and start inviting members. You need to have been active for at least 30 days and completed 50 successful uploads to qualify.

+
+
+ +
+
+

What are the minimum withdrawal requirements?

+ +
+
+

The minimum withdrawal amount is KES 500. You must also have completed at least 5 successful uploads in the last 7 days to be eligible for withdrawal. There's a 2% processing fee for withdrawals below KES 2,000.

+
+
+ +
+
+

How do I upgrade my account level?

+ +
+
+

Account levels are automatically upgraded based on your activity and earnings. To move from Dormant to Active Marketer, you need to complete 10 uploads and earn at least KES 1,000. Higher levels require more activity and team members.

+
+
+ +
+
+

What should I do if my uploads are rejected?

+ +
+
+

If your uploads are frequently rejected, check the content guidelines in the Meta Uploads section. Make sure your content is original, follows platform rules, and meets the quality standards. If you believe your content was wrongly rejected, contact support with the upload ID.

+
+
+
+ +
+
+

+ Support Hours +

+
+
+ WhatsApp Support: + 24/7 +
+
+ Email Support: + Mon-Sun, 6am-11pm EAT +
+
+ Phone Support: + 24/7 for urgent issues +
+
+ Average Response Time: + Under 30 minutes +
+
+ Ticket Resolution Time: + Within 24 hours +
+
+
+ +
+

+ Community Support +

+

Join our community of marketers to get help from experienced members and share insights:

+ +
+
+

Community Forum

+

Get answers from other marketers in our community forum

+ Visit Forum +
+ +
+

Knowledge Base

+

Browse our comprehensive knowledge base articles

+ Browse Articles +
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/daily-product.php b/jweb/ac1/src/pages/daily-product.php new file mode 100644 index 0000000000000000000000000000000000000000..1dcfae391314abbcd7953ddeea4c339bcb959373 --- /dev/null +++ b/jweb/ac1/src/pages/daily-product.php @@ -0,0 +1,498 @@ + + + + + + + Japanese Motors— Daily Product + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+
+
+
🛍️
+
+

Today's Featured Products

+ Special offers updated daily +
+
+ +
+ +
+
+
💎
+
+

Diamond Package

+

Premium investment with high returns

+
+ KES 2,500 + +
+
+
+
+ + +
+
+
+
+

Starlight Bundle

+

Mid-range investment option

+
+ KES 1,000 + +
+
+
+
+ + +
+
+
🌱
+
+

Starter Pack

+

Perfect for new investors

+
+ KES 500 + +
+
+
+
+ + +
+
+
🔥
+
+

Hot Deal

+

Limited time exclusive offer

+
+ KES 3,500 + +
+
+
+
+
+ +
+ +
+
+
+ +
+ Products and offers are subject to availability. Japanese Motors © 2023 +
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/index.php b/jweb/ac1/src/pages/index.php new file mode 100644 index 0000000000000000000000000000000000000000..8aaa18a8608d48573a8cf409800dc3185c53f71d --- /dev/null +++ b/jweb/ac1/src/pages/index.php @@ -0,0 +1,1301 @@ + + + + + + + Japanese Motors Marketing Platform + + + + + + + + + + + +
+ +
+ + +
+
+ +
+
+
+
+ v + +
+
+ +
+
+
+ + +
+
+ +
+
+

Welcome back, !

+

User | Meta Package:

+
+
+ + +
+ + + +
+

Investment Packages

+
Grow your funds with our automated investment solutions.
+
+
+
+ +
NOVA 1,000.00 KES
+
+
    +
  • Auto Deposit
  • +
  • Auto Withdrawal
  • +
  • Instant Cashback
  • +
+
Award Returns 3,000.00 KES
+ +
+
+
+ +
SUPERIOR 2,500.00 KES
+
+
    +
  • Auto Deposit
  • +
  • Auto Withdrawal
  • +
  • Instant Cashback
  • +
+
Award Returns 7,500.00 KES
+ +
+
+
+ +
GOLD 5,500.00 KES
+
+
    +
  • Auto Deposit
  • +
  • Auto Withdrawal
  • +
  • Instant Cashback
  • +
+
Award Returns 16,500.00 KES
+ +
+
+
+
+ + +
+
+
Meta Balance
+
+
KES
+
+ + +
+
+
+
+ + +
+
+
Total Withdrawals
+
KES
+
+
+
Total Deposits
+
KES
+
+
+
Meta Earnings
+
KES
+
+
+
Rewards
+
KES
+
+
+ + +
+
+

Welcome back, !

+

Meta Balance

+

KES

+ +
+
+

Total Deposits

+

KES

+ +
+
+

Total Withdrawals

+

KES

+ +
+
+

Meta Earnings

+

KES

+
+
+

Rewards

+

KES

+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/loan.php b/jweb/ac1/src/pages/loan.php new file mode 100644 index 0000000000000000000000000000000000000000..0876514cfd89d260e18167952ccfbf3b0e60d905 --- /dev/null +++ b/jweb/ac1/src/pages/loan.php @@ -0,0 +1,1119 @@ + + + + + + + Japanese Motors — Loan Services + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+
Apply for Loan
+
Active Loans
+
Loan History
+
+ +
+
+
+

+ Loan Application +

+ +
+
+

Loan Limit

+

50,000 KES

+
+
+

Available

+

35,000 KES

+
+
+ +
+
+ + +

Min: 1,000 KES | Max: 35,000 KES

+
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+ + M-Pesa +
+

Instant to phone

+
+
+
+ + Bank Transfer +
+

1-2 business days

+
+
+
+ + PayPal +
+

International

+
+
+ +
+ + + + + + + +
+ + +

Where should we send loan status updates?

+
+ +
+ +
+

Loan Terms

+

Interest rate: 5% per month | Processing fee: 2% of loan amount

+

Late payment fee: 1% per day after due date

+
+
+ +
+ + +
+ + +
+
+ +
+

+ Loan Summary +

+ +
+
+ Loan Amount + 0 KES +
+
+ +
+
+ Processing Fee (2%) + 0 KES +
+
+ +
+
+ Total Repayment + 0 KES +
+
+ +
+
+ Due Date + -- +
+
+ +
+

+ Repayment Schedule +

+
+
+ Monthly Installment + 0 KES +
+
+ Number of Payments + -- +
+
+
+ +
+

Eligibility Criteria

+
    +
  • + + Minimum deposits of 20,000 KES OR +
  • +
  • + + Account age of 6+ months with sufficient activity +
  • +
  • + + No overdue loans +
  • +
+
+
+
+
+ +
+
+

+ Active Loans +

+
+ +

No active loans

+

You don't have any active loans at the moment

+
+
+
+ +
+
+

+ Loan History +

+
+ +

No loan history

+

Your loan history will appear here

+
+
+
+ +
+

+ Loan FAQ +

+ +
+
+

How long does loan approval take?

+ +
+
+

Loan applications are typically processed within 2-4 hours during business hours. For M-Pesa disbursement, funds are sent immediately after approval. Bank transfers may take 1-2 business days. You'll receive an SMS notification once your loan is approved and disbursed.

+
+
+ +
+
+

What are the interest rates and fees?

+ +
+
+

Our loans have a 5% monthly interest rate with a 2% processing fee. For example, a loan of 10,000 KES for 30 days would have a 500 KES interest charge and 200 KES processing fee, totaling 10,700 KES repayment. There are no hidden fees, and we clearly display all costs before you apply.

+
+
+ +
+
+

What happens if I can't repay on time?

+ +
+
+

If you're unable to repay on time, please contact our support team before the due date to discuss options. We may offer a repayment extension with revised terms. Late payments incur a 1% daily penalty fee. Consistent communication helps us work with you to find a solution.

+
+
+
+
+
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/meta-uploads.php b/jweb/ac1/src/pages/meta-uploads.php new file mode 100644 index 0000000000000000000000000000000000000000..d22634b48c975a50bd36ed3576ed7beb0492ee03 --- /dev/null +++ b/jweb/ac1/src/pages/meta-uploads.php @@ -0,0 +1,785 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + return $pdo; + } catch (PDOException $e) { + die("Database connection failed: " . $e->getMessage()); + } +} + +// Correct absolute upload directory - using a directory that's more likely to have write permissions +$uploadDir = __DIR__ . '/uploads/'; + +// Ensure uploads directory exists with correct permissions +$uploadDirCreated = false; +if (!is_dir($uploadDir)) { + if (!mkdir($uploadDir, 0755, true)) { + // Try an alternative location if the first one fails + $uploadDir = '/tmp/jweb_uploads/'; + if (!is_dir($uploadDir) && !mkdir($uploadDir, 0755, true)) { + $uploadError = "❌ Failed to create upload directory. Please contact administrator to fix permissions."; + } else { + $uploadDirCreated = true; + } + } else { + $uploadDirCreated = true; + } +} else { + $uploadDirCreated = true; +} + +// Check if directory is writable +if ($uploadDirCreated && !is_writable($uploadDir)) { + if (!chmod($uploadDir, 0755)) { + $uploadError = "❌ Upload directory is not writable. Please fix permissions for: " . $uploadDir; + $uploadDirCreated = false; + } +} + +// Handle file upload +$uploadResults = []; +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['uploaded_files']) && $uploadDirCreated) { + foreach ($_FILES['uploaded_files']['name'] as $key => $name) { + if ($_FILES['uploaded_files']['error'][$key] === UPLOAD_ERR_OK) { + $fileType = $_FILES['uploaded_files']['type'][$key]; + $fileSize = $_FILES['uploaded_files']['size'][$key]; + $tmpName = $_FILES['uploaded_files']['tmp_name'][$key]; + + // Allowed types & size + $allowedTypes = ['image/jpeg', 'image/png', 'video/mp4', 'application/pdf']; + $maxSize = 50 * 1024 * 1024; // 50 MB + + if (in_array($fileType, $allowedTypes) && $fileSize <= $maxSize) { + // Unique filename + $extension = pathinfo($name, PATHINFO_EXTENSION); + $filename = uniqid("file_", true) . '.' . $extension; + $filePath = $uploadDir . $filename; + + if (move_uploaded_file($tmpName, $filePath)) { + // Calculate reward based on file type + $reward = calculateReward($fileType, $fileSize); + + // Save to database + if (saveUploadToDB($userId, $name, $fileType, $fileSize, $filePath, $reward)) { + $uploadResults[] = [ + 'success' => true, + 'name' => $name, + 'reward' => $reward + ]; + + // Update user balance + updateUserBalance($userId, $reward); + } else { + $uploadResults[] = [ + 'success' => false, + 'name' => $name, + 'error' => 'Database error' + ]; + } + } else { + $uploadResults[] = [ + 'success' => false, + 'name' => $name, + 'error' => '❌ Cannot move uploaded file. Check folder permissions.' + ]; + } + } else { + $uploadResults[] = [ + 'success' => false, + 'name' => $name, + 'error' => 'Invalid file type or file too large' + ]; + } + } else { + $uploadResults[] = [ + 'success' => false, + 'name' => $name, + 'error' => 'Upload error: ' . $_FILES['uploaded_files']['error'][$key] + ]; + } + } + + // Refresh user session data + refreshUserSession($userId, $uploadResults); +} + +// Calculate reward based on file type and size +function calculateReward($fileType, $fileSize) { + $baseReward = 50; + + if (strpos($fileType, 'image/') === 0) { + $baseReward = 60; + } elseif (strpos($fileType, 'video/') === 0) { + $baseReward = 85; + } elseif ($fileType === 'application/pdf') { + $baseReward = 55; + } + + // Add bonus for larger files + if ($fileSize > 10 * 1024 * 1024) { // Over 10MB + $baseReward += 10; + } + + return $baseReward; +} + +// Save upload to database +function saveUploadToDB($userId, $fileName, $fileType, $fileSize, $filePath, $reward) { + try { + $pdo = getDBConnection(); + $stmt = $pdo->prepare("INSERT INTO uploads (user_id, file_name, file_type, file_size, file_path, reward_amount, status) + VALUES (?, ?, ?, ?, ?, ?, 'pending')"); + return $stmt->execute([$userId, $fileName, $fileType, $fileSize, $filePath, $reward]); + } catch (PDOException $e) { + error_log("Database error: " . $e->getMessage()); + return false; + } +} + +// Update user balance +function updateUserBalance($userId, $reward) { + try { + $pdo = getDBConnection(); + + // Update balance + $stmt = $pdo->prepare("UPDATE users SET balance = balance + ?, rewards = rewards + ?, meta_earnings = meta_earnings + ? WHERE id = ?"); + $stmt->execute([$reward, $reward, $reward, $userId]); + + return true; + } catch (PDOException $e) { + error_log("Database error: " . $e->getMessage()); + return false; + } +} + +// Function to refresh user session data +function refreshUserSession($userId, $uploadResults = []) { + $totalReward = 0; + + foreach ($uploadResults as $result) { + if ($result['success']) { + $totalReward += $result['reward']; + } + } + + if ($totalReward > 0) { + $_SESSION['balance'] = ($_SESSION['balance'] ?? 0) + $totalReward; + $_SESSION['rewards'] = ($_SESSION['rewards'] ?? 0) + $totalReward; + $_SESSION['meta_earnings'] = ($_SESSION['meta_earnings'] ?? 0) + $totalReward; + + // Update the global variables used in the page + global $balance, $rewards, $meta_earnings; + $balance = $_SESSION['balance']; + $rewards = $_SESSION['rewards']; + $meta_earnings = $_SESSION['meta_earnings']; + } +} + +// Get today's uploads +function getTodaysUploads($userId) { + try { + $pdo = getDBConnection(); + $stmt = $pdo->prepare("SELECT * FROM uploads WHERE user_id = ? AND DATE(created_at) = CURDATE() ORDER BY created_at DESC"); + $stmt->execute([$userId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Database error: " . $e->getMessage()); + return []; + } +} + +// Get today's upload stats +function getTodaysUploadStats($userId) { + try { + $pdo = getDBConnection(); + $stmt = $pdo->prepare("SELECT + COUNT(*) as total_uploads, + SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) as approved_uploads, + SUM(CASE WHEN status = 'approved' THEN reward_amount ELSE 0 END) as total_earnings + FROM uploads + WHERE user_id = ? AND DATE(created_at) = CURDATE()"); + $stmt->execute([$userId]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Database error: " . $e->getMessage()); + return ['total_uploads' => 0, 'approved_uploads' => 0, 'total_earnings' => 0]; + } +} + +// Display upload results if any +if (!empty($uploadResults)) { + $successCount = 0; + $errorCount = 0; + $totalReward = 0; + + foreach ($uploadResults as $result) { + if ($result['success']) { + $successCount++; + $totalReward += $result['reward']; + } else { + $errorCount++; + } + } + + if ($successCount > 0) { + $message = "Successfully uploaded $successCount files. Earned KES " . number_format($totalReward, 2); + } + + if ($errorCount > 0) { + $error = "Failed to upload $errorCount files."; + } +} + +// Get today's uploads and stats +$todaysUploads = getTodaysUploads($userId); +$uploadStats = getTodaysUploadStats($userId); +$totalUploads = $uploadStats['total_uploads'] ?? 0; +$approvedUploads = $uploadStats['approved_uploads'] ?? 0; +$todaysEarnings = $uploadStats['total_earnings'] ?? 0; + +// Helper functions +function getFileIcon($fileType) { + if (strpos($fileType, 'image/') === 0) return 'image'; + if (strpos($fileType, 'video/') === 0) return 'video'; + if ($fileType === 'application/pdf') return 'file-text'; + return 'file'; +} + +function formatFileSize($bytes) { + if ($bytes == 0) return '0 Bytes'; + $k = 1024; + $sizes = ['Bytes', 'KB', 'MB', 'GB']; + $i = floor(log($bytes) / log($k)); + return round($bytes / pow($k, $i), 2) . ' ' . $sizes[$i]; +} +?> + + + + + + Japanese Motors — Uploads + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + + +
+
+
📤
+
+

Meta Uploads

+ Upload content for rewards +
+
+ +
+
+ +

Drag & Drop files here

+

or click to browse files

+ > + +
+
+ +
+

Upload Instructions

+
    +
  • Upload at least 5 high-quality marketing content daily
  • +
  • Files should be in JPEG, PNG, MP4, or PDF format
  • +
  • Minimum resolution of 1280x720 for images/videos
  • +
  • Maximum file size: 50MB per file
  • +
  • Content must be original or properly licensed
  • +
  • Earn KES 50-85 per file based on type and quality
  • +
+
+
+ +
+
+

Today's Uploads (/5)

+
KES earned
+
+ +
+ +
+ +

No uploads today

+
+ + +
+
+ +
+
+
+ • + +
+
+
+
+ KES +
+
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/packages.php b/jweb/ac1/src/pages/packages.php new file mode 100644 index 0000000000000000000000000000000000000000..65c5253e036739f89c598ed3bf9c28567f6c17f9 --- /dev/null +++ b/jweb/ac1/src/pages/packages.php @@ -0,0 +1,1157 @@ +prepare("SELECT * FROM packages WHERE id = ?"); + $stmt->execute([$package_id]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting package details: " . $e->getMessage()); + return false; + } +} + +// Function to get all packages +function getAllPackages() { + global $pdo; + try { + $stmt = $pdo->prepare("SELECT * FROM packages ORDER BY min_investment ASC"); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting all packages: " . $e->getMessage()); + return []; + } +} + +// Function to get user's active investment +function getUserActiveInvestment($user_id) { + global $pdo; + try { + $stmt = $pdo->prepare(" + SELECT ui.*, p.name as package_name, p.daily_return, p.duration_days + FROM user_investments ui + JOIN packages p ON ui.package_id = p.id + WHERE ui.user_id = ? AND ui.status = 'active' AND ui.end_date > NOW() + ORDER BY ui.amount DESC + LIMIT 1 + "); + $stmt->execute([$user_id]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error getting user active investment: " . $e->getMessage()); + return false; + } +} + +// Get user data from session +$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']; + +// Get currency settings (default to KES if not set) +$currency = isset($_SESSION['currency']) ? $_SESSION['currency'] : 'KES'; +$currency_symbol = 'KSh'; +$exchange_rate = 1; + +// Set exchange rates and symbols based on currency +if ($currency === 'USD') { + $currency_symbol = '$'; + $exchange_rate = 0.0078; // 1 KES = 0.0078 USD +} elseif ($currency === 'EUR') { + $currency_symbol = '€'; + $exchange_rate = 0.0072; // 1 KES = 0.0072 EUR +} elseif ($currency === 'GBP') { + $currency_symbol = '£'; + $exchange_rate = 0.0062; // 1 KES = 0.0062 GBP +} + +// Package configurations +$package_configs = [ + 'Nova' => [ + 'min_investment' => 1000, + 'max_investment' => 50000, + 'daily_return' => 5, + 'duration_days' => 7, + 'features' => 'Basic Support,2% Referral Bonus' + ], + 'Superior' => [ + 'min_investment' => 2500, + 'max_investment' => 100000, + 'daily_return' => 7, + 'duration_days' => 14, + 'features' => 'Standard Support,3% Referral Bonus,Priority Withdrawal' + ], + 'Gold' => [ + 'min_investment' => 5500, + 'max_investment' => 250000, + 'daily_return' => 10, + 'duration_days' => 21, + 'features' => 'Priority Support,5% Referral Bonus,Instant Withdrawal,Advanced Tools' + ], + 'Diamond' => [ + 'min_investment' => 10000, + 'max_investment' => 500000, + 'daily_return' => 15, + 'duration_days' => 30, + 'features' => '24/7 Dedicated Support,7% Referral Bonus,Instant Withdrawal,All Advanced Tools' + ] +]; + +// Convert package amounts to selected currency +foreach ($package_configs as $name => $config) { + $package_configs[$name]['min_investment'] = $config['min_investment'] * $exchange_rate; + $package_configs[$name]['max_investment'] = $config['max_investment'] * $exchange_rate; +} + +// Get all available packages from database +$packages = getAllPackages(); + +// Get user's active investment +$active_investment = getUserActiveInvestment($user_id); + +// Map package names to IDs for JavaScript +$package_ids = []; +foreach ($packages as $pkg) { + $package_ids[strtolower($pkg['name'])] = $pkg['id']; +} +?> + + + + + + Japanese Motors — Packages + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+ +
+

Active Investment:

+

Amount: | + Daily Earnings:

+

Ends on:

+
+
+ + + + +
+
All Packages
+
Nova
+
Superior
+
Gold
+
Diamond
+
+ +
+
+ $config): ?> +
+
+

+ + + +
+
+
+
Minimum Investment
+
+
    +
  • % Daily Earnings
  • +
  • Day Duration
  • + +
  • + + +
  • Priority Support
  • +
  • Advanced Tools
  • + +
  • Advanced Tools
  • + +
+
+ +
+
Total Return
+
+ +
+ +
+
+ +
+

+ Package Comparison +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureNovaSuperiorGoldDiamond
Daily Return5%7%10%15%
Contract Duration7 days14 days21 days30 days
Referral Bonus2%3%5%7%
Withdrawal Speed24-48 hours12-24 hoursInstantInstant
SupportBasicStandardPriority24/7 Dedicated
Minimum Investment
+
+
+ +
+

+ Packages FAQ +

+ +
+
+

How do I earn from these packages?

+ +
+
+

When you invest in a package, you earn daily returns based on the percentage for that package. For example, if you invest 5,000 KES in the Standard package with 7% daily returns, you'll earn 350 KES per day. Earnings are credited to your account daily and can be withdrawn or reinvested.

+
+
+ +
+
+

When can I withdraw my earnings?

+ +
+
+

You can withdraw your earnings anytime after they are credited to your account. However, your initial investment is locked for the duration of the package contract. Higher-tier packages offer faster withdrawal processing times.

+
+
+ +
+
+

What happens when my package expires?

+ +
+
+

When your package contract expires, your initial investment is returned to your account balance along with any remaining earnings. You can then withdraw the full amount or reinvest in a new package to continue earning.

+
+
+ +
+
+

Can I upgrade my package?

+ +
+
+

Yes, you can upgrade your package at any time by investing additional funds. The new investment will be treated as a separate package contract with its own terms and earning schedule. You can have multiple packages active simultaneously.

+
+
+
+ +
+
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/profile.php b/jweb/ac1/src/pages/profile.php new file mode 100644 index 0000000000000000000000000000000000000000..974ef31d4ba7769c4081577f35bc6d01295ba553 --- /dev/null +++ b/jweb/ac1/src/pages/profile.php @@ -0,0 +1,1081 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); +} catch(PDOException $e) { + die("Database connection failed: " . $e->getMessage()); +} + +if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) { + header('Location: ../../index.php'); + exit; +} + +// Check if user_id is set in session +if (!isset($_SESSION['user_id']) && isset($_SESSION['username'])) { + $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$_SESSION['username']]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + if ($user) { + $_SESSION['user_id'] = $user['id']; + } +} + +if (!isset($_SESSION['user_id'])) { + header('Location: ../../index.php'); + exit; +} + +// Fetch user data from database +$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); +$stmt->execute([$_SESSION['user_id']]); +$user = $stmt->fetch(PDO::FETCH_ASSOC); + +if (!$user) { + session_destroy(); + header('Location: ../../index.php'); + exit; +} + +// Get user data from database +$username = $user['username'] ?? $_SESSION['username']; +$email = $user['email'] ?? $_SESSION['email']; +$tier = $user['tier'] ?? $_SESSION['tier']; +$package = $user['package'] ?? $_SESSION['package']; +$balance = $user['balance'] ?? $_SESSION['balance']; +$total_deposits = $user['total_deposits'] ?? $_SESSION['total_deposits']; +$total_withdrawals = $user['total_withdrawals'] ?? $_SESSION['total_withdrawals']; +$rewards = $user['rewards'] ?? $_SESSION['rewards']; + +// Get profile-specific data +$first_name = $user['first_name'] ?? 'Maha'; +$last_name = $user['last_name'] ?? 'Ibrahim'; +$phone_number = $user['phone_number'] ?? '254712345678'; +$country_code = $user['country_code'] ?? 'KE'; +$status = $user['status'] ?? 'Dormant'; +$member_since = $user['member_since'] ?? '2023-01-15'; + +// Format member since date +$member_since_formatted = date('d M Y', strtotime($member_since)); +$earnings = $total_deposits - $total_withdrawals; + +// Update session data +$_SESSION['username'] = $username; +$_SESSION['email'] = $email; +$_SESSION['tier'] = $tier; +$_SESSION['package'] = $package; +$_SESSION['balance'] = $balance; +$_SESSION['total_deposits'] = $total_deposits; +$_SESSION['total_withdrawals'] = $total_withdrawals; +$_SESSION['rewards'] = $rewards; +?> + + + + + + Profile | Japanese Motors + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+
+
+
+
+
+

+

Marketer

+

Member since:

+
+
+
+ +
+
Personal Info
+
Security
+
Activity
+
+ + +
+

Personal Information

+ +
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Security Settings

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+ +
+

Security Recommendations

+
    +
  • • Use a strong, unique password
  • +
  • • Enable two-factor authentication if available
  • +
  • • Regularly update your password
  • +
  • • Never share your password with anyone
  • +
+
+
+ + +
+

Recent Activity

+ +
+
+ +

Loading activity history...

+
+
+ +
+ +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/recharge.php b/jweb/ac1/src/pages/recharge.php new file mode 100644 index 0000000000000000000000000000000000000000..4220e802beb642a40960af8012420158a33ebf89 --- /dev/null +++ b/jweb/ac1/src/pages/recharge.php @@ -0,0 +1,641 @@ +initiateSTKPush($phone_number, $amount, $user_id); + + if (isset($result['success'])) { + $success = "M-Pesa prompt sent to your phone! Please enter your PIN to complete payment."; + + // Record pending transaction + $mainAccount = new MainAccount(); + $mainAccount->recordPendingTransaction($user_id, $amount, $phone_number, $result['CheckoutRequestID']); + + // Add detailed success message + $success .= " Check your phone for the M-Pesa prompt. Transaction ID: " . $result['CheckoutRequestID']; + + } else { + $error = "Payment initiation failed: " . ($result['message'] ?? 'Please try again later.'); + + // Log detailed error for debugging + error_log("M-Pesa Error: " . print_r($result, true)); + } + } elseif ($payment_method === 'Manual') { + // Redirect to manual payment page + $_SESSION['payment_amount'] = $amount; + $_SESSION['payment_phone'] = $phone_number; + header('Location: ../api/manual_payment.php'); + exit; + } + } +} + +// Get currency rates for display +require_once '../../db.php'; +$db = new Database(); +$conn = $db->getConnection(); +$currency_rates = []; +try { + $stmt = $conn->prepare("SELECT target_currency, exchange_rate FROM currency_rates"); + $stmt->execute(); + $currency_rates = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); +} catch (PDOException $e) { + error_log("Currency rates error: " . $e->getMessage()); +} +?> + + + + + + Recharge | Japanese Motors + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+
+ +
+ + +
+ + + +
+
+ + +
+
+

Check your phone for M-Pesa prompt. Enter your PIN to complete payment.

+
+
+ + + +
+

+ Quick Deposit +

+ +
+
+ + +
+ Minimum amount: 500 + +
+
+ +
+ +
+
+ +

M-Pesa STK Push

+ Instant • Automatic +
+
+ +

Manual Verification

+ Admin • Secure +
+
+ +
+ +
+ + +
+ + +
+
+ + +
+ +
+

+ M-Pesa STK Push +

+
    +
  • + + Instant payment processing +
  • +
  • + + Automatic balance update +
  • +
  • + + Receipt sent to your phone +
  • +
+
+ + +
+

+ Manual Verification +

+
    +
  • + + Upload payment screenshot +
  • +
  • + + Admin verification required +
  • +
  • + + ‎24/7 support available +
  • +
+
+

Send to: Paybill 542542

+

Account: 00106664176150

+
+
+
+ + +
+

Bonus Packages

+
+ 5, + 1000 => 15, + 2000 => 40, + 5000 => 120, + 10000 => 300, + 20000 => 700 + ]; + + foreach ($packages as $amount => $bonus): + $display_amount = $currency === 'KES' ? $amount : number_format($amount * ($currency_rates[$currency] ?? 1), 2); + ?> +
+
+
+ Bonus
+
+ +
+
+ + +
+

Currency Converter

+
+ $rate): ?> + +
+ 1 = + KES +
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/settings.php b/jweb/ac1/src/pages/settings.php new file mode 100644 index 0000000000000000000000000000000000000000..b61ac521e424f45f8e17e0b18e91040c9c9974b7 --- /dev/null +++ b/jweb/ac1/src/pages/settings.php @@ -0,0 +1,784 @@ +getConnection(); + +// Get user data from session +$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; + +// Get user settings from database +function getUserSettings($db, $user_id) { + $query = "SELECT * FROM user_settings WHERE user_id = ?"; + $stmt = $db->prepare($query); + $stmt->execute([$user_id]); + return $stmt->fetch(PDO::FETCH_ASSOC) ?: []; +} + +$user_settings = getUserSettings($db, $_SESSION['user_id']); + +// Handle form submissions +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $user_id = $_SESSION['user_id']; + + try { + // Update general settings + if (isset($_POST['update_settings'])) { + $dark_mode = isset($_POST['dark_mode']) ? 1 : 0; + $language = $_POST['language'] ?? 'en'; + $currency = $_POST['currency'] ?? 'KES'; + $auto_logout = isset($_POST['auto_logout']) ? 1 : 0; + + $query = "INSERT INTO user_settings (user_id, dark_mode, language, currency, auto_logout) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + dark_mode = VALUES(dark_mode), + language = VALUES(language), + currency = VALUES(currency), + auto_logout = VALUES(auto_logout)"; + + $stmt = $db->prepare($query); + if ($stmt->execute([$user_id, $dark_mode, $language, $currency, $auto_logout])) { + $_SESSION['success'] = "Settings updated successfully!"; + } + } + + // Handle password change + if (isset($_POST['change_password'])) { + $current_password = $_POST['current_password']; + $new_password = $_POST['new_password']; + $confirm_password = $_POST['confirm_password']; + + if ($new_password !== $confirm_password) { + $_SESSION['error'] = "New passwords do not match!"; + } else { + // Verify current password + $query = "SELECT password_hash FROM users WHERE id = ?"; + $stmt = $db->prepare($query); + $stmt->execute([$user_id]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user && password_verify($current_password, $user['password_hash'])) { + $new_password_hash = password_hash($new_password, PASSWORD_BCRYPT); + + $query = "UPDATE users SET password_hash = ? WHERE id = ?"; + $stmt = $db->prepare($query); + if ($stmt->execute([$new_password_hash, $user_id])) { + $_SESSION['success'] = "Password updated successfully!"; + } + } else { + $_SESSION['error'] = "Current password is incorrect!"; + } + } + } + + // Handle account deletion + if (isset($_POST['delete_account'])) { + $confirm_password = $_POST['confirm_password']; + + // Verify password + $query = "SELECT password_hash FROM users WHERE id = ?"; + $stmt = $db->prepare($query); + $stmt->execute([$user_id]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($user && password_verify($confirm_password, $user['password_hash'])) { + // Soft delete the account + $query = "UPDATE users SET is_active = 0 WHERE id = ?"; + $stmt = $db->prepare($query); + if ($stmt->execute([$user_id])) { + session_destroy(); + header('Location: ../../index.php?account_deleted=1'); + exit; + } + } else { + $_SESSION['error'] = "Password is incorrect!"; + } + } + + } catch(PDOException $exception) { + $_SESSION['error'] = "Error: " . $exception->getMessage(); + } + + // Refresh settings after update + $user_settings = getUserSettings($db, $_SESSION['user_id']); +} +?> + + + + + + Settings | Japanese Motors + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+
+ + +
+ +
+ + + +
+ +
+ + +
+ +

Settings

+
+ +
+
General
+
Security
+
Notifications
+
+ + +
+
+
+

General Settings

+ +
+
+

Dark Mode

+

Switch between light and dark theme

+
+ +
+ +
+
+

Language

+

Select your preferred language

+
+ +
+ +
+
+

Currency

+

Default currency for transactions

+
+ +
+ +
+
+

Auto Logout

+

Automatically logout after inactivity

+
+ +
+ + +
+
+
+ + + + + + + + +
+

Danger Zone

+
+
+

Delete Account

+

Permanently delete your account

+
+ +
+
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/team.php b/jweb/ac1/src/pages/team.php new file mode 100644 index 0000000000000000000000000000000000000000..1db03ad1bd0954c4d638dad3d970a3de85fb720b --- /dev/null +++ b/jweb/ac1/src/pages/team.php @@ -0,0 +1,446 @@ + + + + + + + Team | Japanese Motors + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ +
+
+

+ My Team +

+ +
+
+
Total Members
+
0
+
+
+
Active Today
+
0
+
+
+
Team Earnings
+
0 KES
+
+
+ + + +
+ +

No team members found

+ +
+
+ +
+

Referral Link

+
+ + +
+
+ Share this link to invite new members to your team. You'll earn 10% of their first deposit. +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/transactions.php b/jweb/ac1/src/pages/transactions.php new file mode 100644 index 0000000000000000000000000000000000000000..3c52a75f5650543e3c0c89273a01125d687e591a --- /dev/null +++ b/jweb/ac1/src/pages/transactions.php @@ -0,0 +1,565 @@ +getConnection(); + +// Initialize user and transaction objects +$user = new User($db); +$transaction = new Transaction($db); + +// Get user data from session +$username = $_SESSION['username']; + +// Default values if database operations fail +$email = $_SESSION['email'] ?? 'unknown@example.com'; +$tier = $_SESSION['tier'] ?? 'Basic'; +$package = $_SESSION['package'] ?? 'NOVA'; +$balance = $_SESSION['balance'] ?? 0; +$total_deposits = $_SESSION['total_deposits'] ?? 0; +$total_withdrawals = $_SESSION['total_withdrawals'] ?? 0; +$rewards = $_SESSION['rewards'] ?? 0; +$earnings = $total_deposits - $total_withdrawals; +$transactions = []; + +// Try to get user details from database +try { + if ($user->getUserByUsername($username)) { + // User exists in database + $email = $user->email; + $tier = $user->tier; + $package = $user->package; + $balance = $user->balance; + $total_deposits = $user->total_deposits; + $total_withdrawals = $user->total_withdrawals; + $rewards = $user->rewards; + $earnings = $total_deposits - $total_withdrawals; + + // Store updated values in session + $_SESSION['email'] = $email; + $_SESSION['tier'] = $tier; + $_SESSION['package'] = $package; + $_SESSION['balance'] = $balance; + $_SESSION['total_deposits'] = $total_deposits; + $_SESSION['total_withdrawals'] = $total_withdrawals; + $_SESSION['rewards'] = $rewards; + + // Get transactions for the user + $transactions_stmt = $transaction->getTransactionsByUserId($user->id, 20); + if ($transactions_stmt) { + $transactions = $transactions_stmt->fetchAll(PDO::FETCH_ASSOC); + } + } +} catch (Exception $e) { + // Database error - use session values instead + error_log("Database error: " . $e->getMessage()); +} +?> + + + + + + + Japanese Motors — Transactions + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+
+
💸
+
+

Transaction History

+ All your financial activities +
+
+ +
+
All
+
Deposits
+
Withdrawals
+
Earnings
+
+ +
+ + + +
+
+
+ +
+
+
+
+
+
+
+
+ + +
No transactions found
+ +
+
+
+ +
+
+
Sample Deposit
+
Today, 09:42 AM
+
+
+
+ KES 100
+
+ +
+
+
+ +
+
+
Sample Withdrawal
+
Yesterday, 3:15 PM
+
+
+
- KES 1,000
+
+ +
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/transfer.php b/jweb/ac1/src/pages/transfer.php new file mode 100644 index 0000000000000000000000000000000000000000..70da1ac361bbef144e1f155c8c15391f028ee86f --- /dev/null +++ b/jweb/ac1/src/pages/transfer.php @@ -0,0 +1,1212 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Create tables if they don't exist (including pin_setup table) + $pdo->exec(" + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + tier VARCHAR(20) DEFAULT 'Basic', + package VARCHAR(50) DEFAULT 'Starter', + balance DECIMAL(10, 2) DEFAULT 5000.00, + pin_hash VARCHAR(255) DEFAULT NULL, + pin_setup_complete BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS pin_setup ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + pin_hash VARCHAR(255) NOT NULL, + setup_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS transactions ( + id INT AUTO_INCREMENT PRIMARY KEY, + sender_id INT NOT NULL, + recipient_id INT NOT NULL, + amount DECIMAL(10, 2) NOT NULL, + message TEXT, + status ENUM('pending', 'completed', 'failed') DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + completed_at TIMESTAMP NULL + ); + + CREATE TABLE IF NOT EXISTS balance_history ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + transaction_id INT NULL, + amount DECIMAL(10, 2) NOT NULL, + balance_before DECIMAL(10, 2) NOT NULL, + balance_after DECIMAL(10, 2) NOT NULL, + type ENUM('transfer_sent', 'transfer_received', 'deposit', 'withdrawal'), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + "); + + // Check if demo user exists, if not create it + $stmt = $pdo->prepare("SELECT id, pin_setup_complete FROM users WHERE username = ?"); + $stmt->execute([$_SESSION['username']]); + $user = $stmt->fetch(); + + if (!$user) { + $stmt = $pdo->prepare("INSERT INTO users (username, email, tier, package, balance) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([ + $_SESSION['username'], + $_SESSION['email'], + $_SESSION['tier'], + $_SESSION['package'], + $_SESSION['balance'] + ]); + $user_id = $pdo->lastInsertId(); + + // Set pin_setup_complete to false for new users + $_SESSION['pin_setup_complete'] = false; + } else { + $_SESSION['pin_setup_complete'] = (bool)$user['pin_setup_complete']; + } +} catch (PDOException $e) { + // If database connection fails, continue with session data only + error_log("Database connection failed: " . $e->getMessage()); + $pdo = null; + $_SESSION['pin_setup_complete'] = $_SESSION['pin_setup_complete'] ?? false; +} + +// Get user data from session +$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']; +$pin_setup_complete = $_SESSION['pin_setup_complete']; +$earnings = $total_deposits - $total_withdrawals; + +// Process PIN setup if form is submitted +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['setup_pin'])) { + $new_pin = $_POST['new_pin']; + $confirm_pin = $_POST['confirm_pin']; + + // Validate PINs + if (strlen($new_pin) !== 4 || !is_numeric($new_pin)) { + $_SESSION['error'] = "PIN must be exactly 4 digits."; + header("Location: transfer.php"); + exit; + } + + if ($new_pin !== $confirm_pin) { + $_SESSION['error'] = "PINs do not match."; + header("Location: transfer.php"); + exit; + } + + // Check if PIN is not a simple pattern + if (preg_match('/^(\d)\1{3}$/', $new_pin) || + preg_match('/^0123|1234|2345|3456|4567|5678|6789|7890$/', $new_pin) || + preg_match('/^0987|9876|8765|7654|6543|5432|4321|3210$/', $new_pin)) { + $_SESSION['error'] = "Please choose a more secure PIN. Avoid simple patterns."; + header("Location: transfer.php"); + exit; + } + + if ($pdo) { + try { + // Hash the PIN (in a real application, use password_hash with a proper algorithm) + $pin_hash = password_hash($new_pin, PASSWORD_DEFAULT); + + // Update user record with PIN hash and mark setup as complete + $stmt = $pdo->prepare("UPDATE users SET pin_hash = ?, pin_setup_complete = TRUE WHERE username = ?"); + $stmt->execute([$pin_hash, $username]); + + // Record PIN setup in history + $stmt = $pdo->prepare("INSERT INTO pin_setup (user_id, pin_hash) SELECT id, ? FROM users WHERE username = ?"); + $stmt->execute([$pin_hash, $username]); + + // Update session + $_SESSION['pin_setup_complete'] = true; + $pin_setup_complete = true; + + $_SESSION['success'] = "PIN setup successful! You can now make transfers."; + } catch (Exception $e) { + $_SESSION['error'] = "PIN setup failed: " . $e->getMessage(); + } + } else { + // Fallback to session-only processing if database is not available + $_SESSION['pin_hash'] = $new_pin; // In a real app, this would be hashed + $_SESSION['pin_setup_complete'] = true; + $pin_setup_complete = true; + $_SESSION['success'] = "PIN setup successful! You can now make transfers."; + } + + header("Location: transfer.php"); + exit; +} + +// Process transfer if form is submitted +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['transfer'])) { + // Check if PIN is set up + if (!$pin_setup_complete) { + $_SESSION['error'] = "You must set up your security PIN before making transfers."; + header("Location: transfer.php"); + exit; + } + + $recipient_username = $_POST['recipient']; + $amount = floatval($_POST['amount']); + $pin = $_POST['pin']; + $message = $_POST['message'] ?? ''; + + // Validate inputs + if (empty($recipient_username) || $amount <= 0 || strlen($pin) !== 4 || !is_numeric($pin)) { + $_SESSION['error'] = "Invalid input data."; + header("Location: transfer.php"); + exit; + } + + if ($pdo) { + try { + // Begin transaction + $pdo->beginTransaction(); + + // Get sender info + $stmt = $pdo->prepare("SELECT id, balance, pin_hash FROM users WHERE username = ?"); + $stmt->execute([$username]); + $sender = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$sender) { + throw new Exception("Sender not found."); + } + + // Verify PIN + if (!password_verify($pin, $sender['pin_hash'])) { + throw new Exception("Invalid PIN."); + } + + // Check if sender has sufficient balance + if ($sender['balance'] < $amount) { + throw new Exception("Insufficient balance."); + } + + // Get recipient info + $stmt = $pdo->prepare("SELECT id, username, balance FROM users WHERE username = ?"); + $stmt->execute([$recipient_username]); + $recipient = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$recipient) { + // Create recipient if they don't exist (for demo purposes) + $stmt = $pdo->prepare("INSERT INTO users (username, email, balance) VALUES (?, ?, ?)"); + $stmt->execute([$recipient_username, $recipient_username . '@example.com', 5000.00]); + $recipient_id = $pdo->lastInsertId(); + + $stmt = $pdo->prepare("SELECT id, username, balance FROM users WHERE id = ?"); + $stmt->execute([$recipient_id]); + $recipient = $stmt->fetch(PDO::FETCH_ASSOC); + } + + if ($sender['id'] === $recipient['id']) { + throw new Exception("Cannot transfer to yourself."); + } + + // Create transaction record + $stmt = $pdo->prepare("INSERT INTO transactions (sender_id, recipient_id, amount, message, status) VALUES (?, ?, ?, ?, 'completed')"); + $stmt->execute([$sender['id'], $recipient['id'], $amount, $message]); + $transaction_id = $pdo->lastInsertId(); + + // Update sender balance + $new_sender_balance = $sender['balance'] - $amount; + $stmt = $pdo->prepare("UPDATE users SET balance = ? WHERE id = ?"); + $stmt->execute([$new_sender_balance, $sender['id']]); + + // Record sender balance change + $stmt = $pdo->prepare("INSERT INTO balance_history (user_id, transaction_id, amount, balance_before, balance_after, type) VALUES (?, ?, ?, ?, ?, 'transfer_sent')"); + $stmt->execute([$sender['id'], $transaction_id, -$amount, $sender['balance'], $new_sender_balance]); + + // Update recipient balance + $new_recipient_balance = $recipient['balance'] + $amount; + $stmt = $pdo->prepare("UPDATE users SET balance = ? WHERE id = ?"); + $stmt->execute([$new_recipient_balance, $recipient['id']]); + + // Record recipient balance change + $stmt = $pdo->prepare("INSERT INTO balance_history (user_id, transaction_id, amount, balance_before, balance_after, type) VALUES (?, ?, ?, ?, ?, 'transfer_received')"); + $stmt->execute([$recipient['id'], $transaction_id, $amount, $recipient['balance'], $new_recipient_balance]); + + // Update transaction status to completed + $stmt = $pdo->prepare("UPDATE transactions SET status = 'completed', completed_at = NOW() WHERE id = ?"); + $stmt->execute([$transaction_id]); + + // Commit transaction + $pdo->commit(); + + // Update session balance + $_SESSION['balance'] = $new_sender_balance; + $balance = $new_sender_balance; + + $_SESSION['success'] = "Transfer of KES " . number_format($amount, 2) . " to @$recipient_username was successful!"; + + // Store notification in session + if (!isset($_SESSION['notifications'])) { + $_SESSION['notifications'] = []; + } + + $_SESSION['notifications'][] = [ + 'type' => 'transfer_sent', + 'to' => $recipient_username, + 'amount' => $amount, + 'message' => $message, + 'timestamp' => time() + ]; + + } catch (Exception $e) { + $pdo->rollBack(); + $_SESSION['error'] = "Transfer failed: " . $e->getMessage(); + } + } else { + // Fallback to session-only processing if database is not available + if (!$pin_setup_complete || $pin !== ($_SESSION['pin_hash'] ?? '')) { + $_SESSION['error'] = "Invalid PIN or PIN not set up."; + } else if ($amount > $balance) { + $_SESSION['error'] = "Insufficient balance."; + } else { + // Update session balance + $_SESSION['balance'] = $balance - $amount; + $balance = $_SESSION['balance']; + + $_SESSION['success'] = "Transfer of KES " . number_format($amount, 2) . " to @$recipient_username was successful!"; + + // Store transaction in session + if (!isset($_SESSION['transactions'])) { + $_SESSION['transactions'] = []; + } + + $_SESSION['transactions'][] = [ + 'recipient' => $recipient_username, + 'amount' => $amount, + 'message' => $message, + 'timestamp' => time(), + 'status' => 'completed' + ]; + } + } + + header("Location: transfer.php"); + exit; +} + +// Get transaction history +$transactions = []; +if ($pdo) { + try { + $stmt = $pdo->prepare(" + SELECT t.*, + sender.username as sender_username, + recipient.username as recipient_username, + CASE + WHEN t.sender_id = u.id THEN 'sent' + ELSE 'received' + END as direction + FROM transactions t + JOIN users sender ON t.sender_id = sender.id + JOIN users recipient ON t.recipient_id = recipient.id + JOIN users u ON (t.sender_id = u.id OR t.recipient_id = u.id) + WHERE u.username = ? + ORDER BY t.created_at DESC LIMIT 10 + "); + $stmt->execute([$username]); + $transactions = $stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + error_log("Error fetching transactions: " . $e->getMessage()); + } +} else if (isset($_SESSION['transactions'])) { + // Fallback to session transactions + $transactions = $_SESSION['transactions']; +} + +// Get current balance from database if available +if ($pdo) { + try { + $stmt = $pdo->prepare("SELECT balance, pin_setup_complete FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user_data = $stmt->fetch(PDO::FETCH_ASSOC); + if ($user_data) { + $balance = $user_data['balance']; + $pin_setup_complete = (bool)$user_data['pin_setup_complete']; + $_SESSION['balance'] = $balance; + $_SESSION['pin_setup_complete'] = $pin_setup_complete; + } + } catch (PDOException $e) { + error_log("Error fetching balance: " . $e->getMessage()); + } +} +?> + + + + + + Japanese Motors — Transfer + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + +
+ +
+
+
💸
+
+

Send Money

+ Transfer to another Jmotors user +
+
+ +
+
+ + Available Balance: +
+
KES
+
+ + +
+
+ + Security PIN Required +
+

You need to set up a security PIN before making transfers.

+ +
+ + +
> + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+ + +
+
+
📜
+
+

Transfer History

+ Your recent transfer activity +
+
+ +
+ +
+ +
+
+ + + KES + +
+ +
""
+ +
+ + + + +
+
+ +
+ +
+ +

No transfers yet

+
Your transfer history will appear here
+
+ +
+
+
+ +
+ Jmotors Transfer System © +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/whatsapp-channel.php b/jweb/ac1/src/pages/whatsapp-channel.php new file mode 100644 index 0000000000000000000000000000000000000000..f850d7fb7a2aeadeb53670f8e91e1a6c63c76535 --- /dev/null +++ b/jweb/ac1/src/pages/whatsapp-channel.php @@ -0,0 +1,723 @@ +getConnection(); + +// Get user data from session +$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; + +// Fetch WhatsApp channels from database +$channels_query = "SELECT * FROM whatsapp_channels WHERE is_active = 1 ORDER BY channel_type"; +$channels_stmt = $db->prepare($channels_query); +$channels_stmt->execute(); +$channels = $channels_stmt->fetchAll(PDO::FETCH_ASSOC); + +// Fetch FAQs from database +$faqs_query = "SELECT * FROM faqs WHERE is_active = 1 ORDER BY category, id"; +$faqs_stmt = $db->prepare($faqs_query); +$faqs_stmt->execute(); +$faqs = $faqs_stmt->fetchAll(PDO::FETCH_ASSOC); + +// Handle support ticket submission +if ($_POST && isset($_POST['submit_ticket'])) { + $issue_type = $_POST['issue_type']; + $subject = $_POST['subject']; + $description = $_POST['description']; + $ticket_number = 'TKT' . date('Ymd') . rand(1000, 9999); + + $ticket_query = "INSERT INTO support_tickets (user_id, ticket_number, issue_type, subject, description) + VALUES (:user_id, :ticket_number, :issue_type, :subject, :description)"; + $ticket_stmt = $db->prepare($ticket_query); + $ticket_stmt->bindParam(':user_id', $_SESSION['user_id']); + $ticket_stmt->bindParam(':ticket_number', $ticket_number); + $ticket_stmt->bindParam(':issue_type', $issue_type); + $ticket_stmt->bindParam(':subject', $subject); + $ticket_stmt->bindParam(':description', $description); + + if ($ticket_stmt->execute()) { + $ticket_success = "Support ticket submitted successfully! Ticket ID: " . $ticket_number; + } else { + $ticket_error = "Error submitting support ticket. Please try again."; + } +} + +// Check user's active tickets +$active_tickets_query = "SELECT COUNT(*) as active_count FROM support_tickets WHERE user_id = :user_id AND status IN ('open', 'in_progress')"; +$active_tickets_stmt = $db->prepare($active_tickets_query); +$active_tickets_stmt->bindParam(':user_id', $_SESSION['user_id']); +$active_tickets_stmt->execute(); +$active_tickets = $active_tickets_stmt->fetch(PDO::FETCH_ASSOC); +?> + + + + + + Japanese Motors — Dashboard + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ + +
+ + +
+ +
+

+ + +

+

+
    + +
  • Daily marketing tips
  • +
  • Announcements & updates
  • +
  • Peer support network
  • + +
  • Official announcements
  • +
  • Product updates
  • +
  • No-chat broadcast only
  • + +
  • Daily marketing strategies
  • +
  • Performance optimization
  • +
  • Expert insights
  • + +
  • Exclusive opportunities
  • +
  • Bonus programs
  • +
  • Elite marketer network
  • + +
+ + + + +
+ +
+ +
+

+ WhatsApp Channel Guidelines +

+
+
+

Do's

+
    +
  • Be respectful to all members
  • +
  • Share relevant content only
  • +
  • Use appropriate language
  • +
  • Follow admin instructions
  • +
+
+
+

Don'ts

+
    +
  • No spamming or flooding
  • +
  • No inappropriate content
  • +
  • No external promotions
  • +
  • No harassment of any kind
  • +
+
+
+
+
+ + +
+
+

🛎️ Customer Care Support

+
+

We're here to help you with any issues or questions about your Jmotors account

+
+
⏰ 24/7 support   •   Quick response   •   Expert help
+
+ + +
+ +
+ + + +
+ +
+ + +
+
+

+ Submit a Support Request +

+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ +
+

+ Contact Options +

+ +
+

Direct WhatsApp Support

+

Chat directly with our support team

+ + Message Support + +
+ +
+

Email Support

+

support@jmotors.com

+

Response within 2 hours

+
+ +
+

Phone Support

+

+254 756 709 823

+

Available 24/7 for urgent issues

+
+
+
+ +
+

+ Frequently Asked Questions +

+ + +
+
+

+ +
+
+

+
+
+ +
+ +
+
+

+ Support Hours +

+
+
+ WhatsApp Support: + 24/7 +
+
+ Email Support: + Mon-Sun, 6am-11pm +
+
+ Phone Support: + 24/7 for urgent issues +
+
+ Average Response Time: + Under 30 minutes +
+
+
+ +
+

+ Support Tickets +

+
+ +

Active Tickets:

+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/jweb/ac1/src/pages/withdraw.php b/jweb/ac1/src/pages/withdraw.php new file mode 100644 index 0000000000000000000000000000000000000000..fb387ca11cd7aa38b77704e3c9695d743d816015 --- /dev/null +++ b/jweb/ac1/src/pages/withdraw.php @@ -0,0 +1,1096 @@ +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()); + } +} + +// Get user data from session +$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']; + +// Get user's available balance (balance minus any holds) +$available_balance = $balance; +$pending_withdrawals = 0; + +// Check for pending withdrawals +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) { + // Log error but continue + error_log("Error fetching pending withdrawals: " . $e->getMessage()); +} + +// Get withdrawal destinations for this user +$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()); +} + +// Process withdrawal request +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'] ?? ''; + + // Validate input + 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; + } + + // Check if user has sufficient available balance + if ($available_balance < $amount) { + echo json_encode(['success' => false, 'message' => 'Insufficient available balance.']); + exit; + } + + // Check minimum and maximum limits + $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; + } + + // Calculate fee (15 KES or 1.5%, whichever is higher) + $fee = max(15, $amount * 0.015); + $net_amount = $amount - $fee; + + // Start transaction + $pdo->beginTransaction(); + + try { + // Check for idempotency (prevent duplicate requests) + $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) { + // Idempotent response - return 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; + } + + // Lock user row for update + $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.'); + } + + // Deduct from user balance + $stmt = $pdo->prepare("UPDATE users SET balance = balance - ?, total_withdrawals = total_withdrawals + ? WHERE id = ?"); + $stmt->execute([$amount, $amount, $user_id]); + + // Create withdrawal record + $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(); + + // Record transaction in ledger + $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}"]); + + // Commit transaction + $pdo->commit(); + + // Update session + $_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; +} + +// Get recent withdrawals for this user +$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()); +} +?> + + + + + + Japanese Motors — Withdraw Funds + + + + + + + + + + + +
+
+
+ +
Jmotors
+
+ +
+ +
+ + +
+
+

+ Withdrawal Details +

+ +
+
+

Available Balance

+

KES

+
+
+

Pending Clearance

+

KES

+
+
+ +
+ + +
+ +
+ + +
+
+
+ + + Default + +
+ +
+

+
+ + +

No withdrawal destinations found. Add one in settings.

+ +
+ +
+ +
+ + +

Minimum: 100 KES | Maximum: 70,000 KES per day | Fee: 15 KES or 1.5% (whichever is higher)

+
+ +
+ + +
+ +
+ + +
+ +
+ +

Withdrawals are typically processed within 24-72 hours. You'll receive a confirmation message when completed.

+
+ + +
+
+ +
+

+ Recent Withdrawals +

+ + +
+ +

No recent withdrawals

+
+ +
+ +
+
+
+

+

+
+
+

KES

+ + + +
+
+

+ + | Ref: +

+
+ +
+ + + View Full History + +
+

+ Quick Cashout +

+
+ + + + +
+
+
+
+ +
+

+ Withdrawal Information +

+ +
+
+

What are the withdrawal limits?

+ +
+
+

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.

+
+
+ +
+
+

How long do withdrawals take?

+ +
+
+

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.

+
+
+ +
+
+

Why was my withdrawal declined?

+ +
+
+

Withdrawals may be declined due to: insufficient balance, incorrect recipient details, security checks, exceeded limits, or account restrictions. Contact support if you need assistance.

+
+
+
+
+
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/jweb/index.html b/jweb/index.html new file mode 100644 index 0000000000000000000000000000000000000000..6cb72e9df1618bf01bf2eb106a1507447f654aa2 --- /dev/null +++ b/jweb/index.html @@ -0,0 +1,1016 @@ + + + + + + Japanese Motors - Premium Automotive Excellence + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+

Experience Japanese Engineering

+

Premium imported vehicles with unmatched performance and reliability

+ +
+
+
+
+ + +
+
+
+

Featured Vehicles

+
+

Discover our curated selection of premium Japanese automobiles

+
+ +
+ +
+
+ Nissan GT-R +
NEW
+
+ +
+
+
+

Nissan GT-R

+
+ + 2023 Model + + Tokyo +
+

The legendary Godzilla with 565HP twin-turbo V6 engine.

+
+ $112,000 + +
+
+
+ + +
+
+ Toyota Supra +
+ +
+
+
+

Toyota Supra

+
+ + 2023 Model + + Osaka +
+

The iconic sports car returns with 382HP turbocharged inline-6.

+
+ $52,000 + +
+
+
+ + +
+
+ Honda NSX +
LIMITED
+
+ +
+
+
+

Honda NSX

+
+ + 2023 Model + + Yokohama +
+

573HP hybrid supercar with cutting-edge technology.

+
+ $169,000 + +
+
+
+
+ + +
+
+ + + + + +
+
+
+

Why Choose Japanese Motors

+
+

Our commitment to excellence sets us apart

+
+ +
+
+
+ +
+

Authentic Imports

+

Directly sourced from Japan with full documentation and service history.

+
+ +
+
+ +
+

Rigorous Inspection

+

Each vehicle undergoes 150-point inspection by certified technicians.

+
+ +
+
+ +
+

Premium Warranty

+

Comprehensive 3-year warranty on all vehicles for your peace of mind.

+
+
+
+
+ + +
+
+
+

Stay Updated

+

Subscribe to our newsletter for exclusive offers, new arrivals, and automotive insights.

+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+

What Our Clients Say

+
+

Hear from our satisfied customers worldwide

+
+ +
+
+
+
+ Michael R. +
+
+

Michael R.

+
+ + + + + +
+
+
+

"The team at Japanese Motors made importing my dream GT-R from Japan seamless. The car arrived in perfect condition, exactly as described. Their attention to detail is unmatched."

+
+ + 32 people found this helpful +
+
+ +
+
+
+ Sarah K. +
+
+

Sarah K.

+
+ + + + + +
+
+
+

"I was hesitant about importing a car internationally, but Japanese Motors guided me through every step. My Supra is flawless and I saved thousands compared to local dealers."

+
+ + 28 people found this helpful +
+
+
+ + +
+
+ + +
+
+

Ready to Own Your Dream Japanese Car?

+

Contact our specialists today to start your import journey

+ + +
+
+ + + + + + + + + + + +
+
+ + +
+
+ + + + \ No newline at end of file