full
This commit is contained in:
parent
222ac63dcc
commit
9660f06ca5
@ -8,10 +8,7 @@ if (!isset($_GET['id'])) {
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// We should also delete related orders to maintain data integrity
|
||||
$stmt = $pdo->prepare('DELETE FROM orders WHERE customer_id = ?');
|
||||
$stmt->execute([$_GET['id']]);
|
||||
|
||||
// The ON DELETE CASCADE constraint on the orders table will automatically delete related orders.
|
||||
$stmt = $pdo->prepare('DELETE FROM customers WHERE id = ?');
|
||||
$stmt->execute([$_GET['id']]);
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ $customer = [
|
||||
'id' => '',
|
||||
'name' => '',
|
||||
'email' => '',
|
||||
'address' => ''
|
||||
'service_address' => ''
|
||||
];
|
||||
$page_title = 'Edit Customer';
|
||||
|
||||
@ -30,12 +30,12 @@ if (!$customer) {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$email = $_POST['email'] ?? '';
|
||||
$address = $_POST['address'] ?? '';
|
||||
$service_address = $_POST['service_address'] ?? '';
|
||||
$customer_id = $_POST['id'] ?? null;
|
||||
|
||||
if ($customer_id) {
|
||||
$stmt = $pdo->prepare('UPDATE customers SET name = ?, email = ?, address = ? WHERE id = ?');
|
||||
$stmt->execute([$name, $email, $address, $customer_id]);
|
||||
$stmt = $pdo->prepare('UPDATE customers SET name = ?, email = ?, service_address = ? WHERE id = ?');
|
||||
$stmt->execute([$name, $email, $service_address, $customer_id]);
|
||||
header('Location: customers.php');
|
||||
exit;
|
||||
}
|
||||
@ -55,8 +55,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($customer['email']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">Address</label>
|
||||
<input type="text" class="form-control" id="address" name="address" value="<?php echo htmlspecialchars($customer['address']); ?>" required>
|
||||
<label for="service_address" class="form-label">Service Address</label>
|
||||
<input type="text" class="form-control" id="service_address" name="service_address" value="<?php echo htmlspecialchars($customer['service_address']); ?>" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save Customer</button>
|
||||
<a href="customers.php" class="btn btn-secondary">Cancel</a>
|
||||
|
||||
@ -17,7 +17,7 @@ $customers = $stmt->fetchAll();
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Address</th>
|
||||
<th>Service Address</th>
|
||||
<th>Joined On</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
@ -33,7 +33,7 @@ $customers = $stmt->fetchAll();
|
||||
<td><?php echo htmlspecialchars($customer['id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($customer['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($customer['email']); ?></td>
|
||||
<td><?php echo htmlspecialchars($customer['address']); ?></td>
|
||||
<td><?php echo htmlspecialchars($customer['service_address']); ?></td>
|
||||
<td><?php echo htmlspecialchars(date('Y-m-d', strtotime($customer['created_at']))); ?></td>
|
||||
<td>
|
||||
<a href="customer_edit.php?id=<?php echo $customer['id']; ?>" class="btn btn-sm btn-primary">Edit</a>
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
<a class="nav-link" href="/admin/customers.php"><i class="fa fa-users"></i>Customers</a>
|
||||
<a class="nav-link" href="/admin/orders.php"><i class="fa fa-box-open"></i>Orders</a>
|
||||
<a class="nav-link" href="/admin/plans.php"><i class="fa fa-list-alt"></i>Plans</a>
|
||||
<a class="nav-link" href="/admin/pages.php"><i class="fa fa-file-alt"></i>Pages</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
@ -3,8 +3,23 @@ include __DIR__ . '/../db/config.php';
|
||||
include 'header.php';
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query('SELECT o.id, c.name as customer_name, p.name as plan_name, o.status, o.created_at FROM orders o JOIN customers c ON o.customer_id = c.id JOIN plans p ON o.plan_id = p.id ORDER BY o.created_at DESC');
|
||||
$stmt = $pdo->query('SELECT o.id, c.name as customer_name, p.name as plan_name, o.order_status, o.amount, o.created_at FROM orders o JOIN customers c ON o.customer_id = c.id JOIN plans p ON o.plan_id = p.id ORDER BY o.created_at DESC');
|
||||
$orders = $stmt->fetchAll();
|
||||
|
||||
function get_status_badge($status) {
|
||||
switch (strtolower($status)) {
|
||||
case 'completed':
|
||||
case 'active':
|
||||
return 'bg-success';
|
||||
case 'pending':
|
||||
return 'bg-warning';
|
||||
case 'failed':
|
||||
case 'cancelled':
|
||||
return 'bg-danger';
|
||||
default:
|
||||
return 'bg-secondary';
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
@ -17,6 +32,7 @@ $orders = $stmt->fetchAll();
|
||||
<th>Order ID</th>
|
||||
<th>Customer</th>
|
||||
<th>Plan</th>
|
||||
<th>Amount</th>
|
||||
<th>Status</th>
|
||||
<th>Order Date</th>
|
||||
</tr>
|
||||
@ -24,7 +40,7 @@ $orders = $stmt->fetchAll();
|
||||
<tbody>
|
||||
<?php if (empty($orders)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">No orders found.</td>
|
||||
<td colspan="6" class="text-center">No orders found.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
@ -32,8 +48,9 @@ $orders = $stmt->fetchAll();
|
||||
<td><?php echo htmlspecialchars($order['id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($order['customer_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($order['plan_name']); ?></td>
|
||||
<td><span class="badge bg-success"><?php echo htmlspecialchars($order['status']); ?></span></td>
|
||||
<td><?php echo htmlspecialchars(date('Y-m-d', strtotime($order['created_at']))); ?></td>
|
||||
<td>$<?php echo htmlspecialchars(number_format($order['amount'], 2)); ?></td>
|
||||
<td><span class="badge <?php echo get_status_badge($order['order_status']); ?>"><?php echo htmlspecialchars(ucfirst($order['order_status'])); ?></span></td>
|
||||
<td><?php echo htmlspecialchars(date('Y-m-d H:i', strtotime($order['created_at']))); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
12
admin/page_delete.php
Normal file
12
admin/page_delete.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
if ($id) {
|
||||
$stmt = db()->prepare('DELETE FROM pages WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
|
||||
header('Location: pages.php');
|
||||
exit;
|
||||
55
admin/page_edit.php
Normal file
55
admin/page_edit.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
$page = [];
|
||||
$title = 'Create Page';
|
||||
|
||||
if ($id) {
|
||||
$stmt = db()->prepare('SELECT * FROM pages WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
$page = $stmt->fetch();
|
||||
$title = 'Edit Page';
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$slug = $_POST['slug'];
|
||||
$page_title = $_POST['title'];
|
||||
$content = $_POST['content'];
|
||||
|
||||
if ($id) {
|
||||
$stmt = db()->prepare('UPDATE pages SET slug = ?, title = ?, content = ? WHERE id = ?');
|
||||
$stmt->execute([$slug, $page_title, $content, $id]);
|
||||
} else {
|
||||
$stmt = db()->prepare('INSERT INTO pages (slug, title, content) VALUES (?, ?, ?)');
|
||||
$stmt->execute([$slug, $page_title, $content]);
|
||||
}
|
||||
|
||||
header('Location: pages.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<h1><?php echo $title; ?></h1>
|
||||
|
||||
<form method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input type="text" class="form-control" id="title" name="title" value="<?php echo htmlspecialchars($page['title'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="slug" class="form-label">Slug</label>
|
||||
<input type="text" class="form-control" id="slug" name="slug" value="<?php echo htmlspecialchars($page['slug'] ?? ''); ?>" required>
|
||||
<div class="form-text">The slug is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">Content</label>
|
||||
<textarea class="form-control" id="content" name="content" rows="10" required><?php echo htmlspecialchars($page['content'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<a href="pages.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
37
admin/pages.php
Normal file
37
admin/pages.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
|
||||
$pages = db()->query('SELECT * FROM pages ORDER BY title ASC')->fetchAll();
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Pages</h1>
|
||||
<a href="page_edit.php" class="btn btn-primary">Create Page</a>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Slug</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($pages as $page): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($page['title']); ?></td>
|
||||
<td><?php echo htmlspecialchars($page['slug']); ?></td>
|
||||
<td>
|
||||
<a href="page_edit.php?id=<?php echo $page['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
<a href="page_delete.php?id=<?php echo $page['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')">Delete</a>
|
||||
<a href="/page.php?slug=<?php echo htmlspecialchars($page['slug']); ?>" class="btn btn-sm btn-outline-secondary" target="_blank">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
75
api/ai_support.php
Normal file
75
api/ai_support.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// 1. Fetch plans from the database to create a knowledge base.
|
||||
$knowledge_base = "";
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT name, speed_mbps, price_monthly, contract_length_months FROM plans ORDER BY price_monthly ASC");
|
||||
$plans = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($plans) {
|
||||
$knowledge_base .= "Here is a list of our current internet plans:\n\n";
|
||||
foreach ($plans as $plan) {
|
||||
$knowledge_base .= "- Plan Name: " . $plan['name'] . "\n";
|
||||
$knowledge_base .= " - Speed: " . $plan['speed_mbps'] . " Mbps\n";
|
||||
$knowledge_base .= " - Price: $" . $plan['price_monthly'] . " per month\n";
|
||||
$knowledge_base .= " - Contract: " . $plan['contract_length_months'] . " months\n\n";
|
||||
}
|
||||
$knowledge_base .= "You are a helpful and friendly customer support assistant for a telecommunications company. Your goal is to answer customer questions based ONLY on the information provided in this knowledge base. Do not invent or assume any details. If the answer is not in the knowledge base, say 'I'm sorry, I don't have that information, but I can connect you with a human agent.' Keep your answers concise and to the point.";
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// In a real app, you'd log this error.
|
||||
// For now, we'll proceed with an empty knowledge base on failure.
|
||||
}
|
||||
|
||||
// 2. Get the user's question
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$user_question = $data['question'] ?? '';
|
||||
|
||||
if (empty($user_question)) {
|
||||
echo json_encode(['error' => 'No question provided.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. Prepare the data for the Gemini API
|
||||
// IMPORTANT: You must replace getenv('GEMINI_API_KEY') with your actual API key.
|
||||
$api_key = getenv('GEMINI_API_KEY');
|
||||
$api_url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=' . $api_key;
|
||||
|
||||
$payload = [
|
||||
'contents' => [
|
||||
[
|
||||
'parts' => [
|
||||
['text' => $knowledge_base . "\n\nCustomer Question: " . $user_question]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// 4. Use cURL to make the API call
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $api_url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // For local development only
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
// 5. Process and return the response
|
||||
if ($http_code === 200) {
|
||||
$result = json_decode($response, true);
|
||||
$ai_response = $result['candidates'][0]['content']['parts'][0]['text'] ?? 'I am unable to answer at this time.';
|
||||
echo json_encode(['reply' => $ai_response]);
|
||||
} else {
|
||||
// Log the error response for debugging
|
||||
error_log("Gemini API Error: HTTP " . $http_code . " - " . $response);
|
||||
echo json_encode(['error' => 'Failed to get a response from the AI assistant. Please try again later.']);
|
||||
}
|
||||
|
||||
77
api/process_signup.php
Normal file
77
api/process_signup.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
\Stripe\Stripe::setApiKey('sk_test_51Hh9Y2L9s5P2Q8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$json_str = file_get_contents('php://input');
|
||||
$json_obj = json_decode($json_str);
|
||||
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
// 1. Validation
|
||||
if (empty($json_obj->plan_id) || empty($json_obj->name) || empty($json_obj->email) || empty($json_obj->address)) {
|
||||
throw new Exception('Incomplete data provided.');
|
||||
}
|
||||
|
||||
// 2. Fetch Plan
|
||||
$stmt = $pdo->prepare("SELECT * FROM plans WHERE id = ?");
|
||||
$stmt->execute([$json_obj->plan_id]);
|
||||
$plan = $stmt->fetch(PDO::FETCH_OBJ);
|
||||
if (!$plan) {
|
||||
throw new Exception('Plan not found.');
|
||||
}
|
||||
$order_amount = $plan->price_monthly; // Amount in dollars
|
||||
|
||||
// 3. Create Stripe Customer
|
||||
$stripe_customer = \Stripe\Customer::create([
|
||||
'name' => $json_obj->name,
|
||||
'email' => $json_obj->email,
|
||||
'address' => [
|
||||
'line1' => $json_obj->address
|
||||
],
|
||||
]);
|
||||
|
||||
// 4. Create Local Customer
|
||||
// For now, using a placeholder for the password. In a real app, this should be properly hashed.
|
||||
$password_placeholder = password_hash('password123', PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare("INSERT INTO customers (name, email, password, service_address, stripe_customer_id) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$json_obj->name, $json_obj->email, $password_placeholder, $json_obj->address, $stripe_customer->id]);
|
||||
$customer_id = $pdo->lastInsertId();
|
||||
|
||||
// 5. Create Local Order
|
||||
$stmt = $pdo->prepare("INSERT INTO orders (customer_id, plan_id, order_status, amount) VALUES (?, ?, 'pending', ?)");
|
||||
$stmt->execute([$customer_id, $plan->id, $order_amount]);
|
||||
$order_id = $pdo->lastInsertId();
|
||||
|
||||
// 6. Create Stripe Payment Intent
|
||||
$paymentIntent = \Stripe\PaymentIntent::create([
|
||||
'customer' => $stripe_customer->id,
|
||||
'amount' => round($order_amount * 100), // Amount in cents
|
||||
'currency' => 'aud',
|
||||
'automatic_payment_methods' => [
|
||||
'enabled' => true,
|
||||
],
|
||||
'metadata' => [
|
||||
'order_id' => $order_id,
|
||||
'customer_id' => $customer_id,
|
||||
'plan_id' => $plan->id
|
||||
]
|
||||
]);
|
||||
|
||||
// 7. Update Local Order with Payment Intent ID
|
||||
$stmt = $pdo->prepare("UPDATE orders SET stripe_payment_intent_id = ? WHERE id = ?");
|
||||
$stmt->execute([$paymentIntent->id, $order_id]);
|
||||
|
||||
// 8. Return Client Secret
|
||||
echo json_encode([
|
||||
'clientSecret' => $paymentIntent->client_secret,
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@ -1,36 +1,21 @@
|
||||
// This is your test publishable key. Don't hardcode this in a real app.
|
||||
const stripe = Stripe('pk_test_51Hh9Y2L9s5P2Q8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4');
|
||||
|
||||
let elements;
|
||||
let clientSecret;
|
||||
const form = document.querySelector("#signup-form");
|
||||
form.addEventListener("submit", handleSubmit);
|
||||
|
||||
initialize();
|
||||
// Initially, disable the submit button until the form is filled
|
||||
document.querySelector("#submit").disabled = true;
|
||||
|
||||
document
|
||||
.querySelector("#signup-form")
|
||||
.addEventListener("submit", handleSubmit);
|
||||
// Create and mount the Payment Element
|
||||
const elements = stripe.elements();
|
||||
const paymentElement = elements.create("payment");
|
||||
paymentElement.mount("#payment-element");
|
||||
|
||||
// Fetches a payment intent and captures the client secret
|
||||
async function initialize() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const planId = urlParams.get('plan_id');
|
||||
// Re-enable the submit button when the Payment Element is ready
|
||||
paymentElement.on('ready', () => {
|
||||
document.querySelector("#submit").disabled = false;
|
||||
});
|
||||
|
||||
const response = await fetch("/api/create_payment_intent.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ plan_id: planId }),
|
||||
});
|
||||
const data = await response.json();
|
||||
clientSecret = data.clientSecret;
|
||||
|
||||
const appearance = {
|
||||
theme: 'stripe',
|
||||
};
|
||||
elements = stripe.elements({ appearance, clientSecret });
|
||||
|
||||
const paymentElement = elements.create("payment");
|
||||
paymentElement.mount("#payment-element");
|
||||
}
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
@ -39,69 +24,64 @@ async function handleSubmit(e) {
|
||||
const name = document.getElementById('name').value;
|
||||
const email = document.getElementById('email').value;
|
||||
const address = document.getElementById('address').value;
|
||||
const planId = form.dataset.planId;
|
||||
|
||||
if (!name || !email || !address) {
|
||||
if (!name || !email || !address || !planId) {
|
||||
showMessage("Please fill out all fields.");
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const payment_intent_id = clientSecret.split('_secret')[0];
|
||||
|
||||
// Update the payment intent with customer details
|
||||
await fetch("/api/update_payment_intent.php", {
|
||||
// Create customer, order, and payment intent on the server.
|
||||
const response = await fetch("/api/process_signup.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
payment_intent_id: payment_intent_id,
|
||||
plan_id: planId,
|
||||
name: name,
|
||||
email: email,
|
||||
address: address
|
||||
}),
|
||||
});
|
||||
|
||||
const { error } = await stripe.confirmPayment({
|
||||
const { clientSecret, error } = await response.json();
|
||||
|
||||
if (error) {
|
||||
showMessage(error);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Confirm the payment with the client secret.
|
||||
const { error: stripeError } = await stripe.confirmPayment({
|
||||
elements,
|
||||
clientSecret,
|
||||
confirmParams: {
|
||||
// Make sure to change this to your payment completion page
|
||||
return_url: window.location.origin + "/order_confirmation.php",
|
||||
receipt_email: email,
|
||||
},
|
||||
});
|
||||
|
||||
// This point will only be reached if there is an immediate error when
|
||||
// confirming the payment. Otherwise, your customer will be redirected to
|
||||
// your `return_url`. For some payment methods like iDEAL, your customer will
|
||||
// be redirected to an intermediate site first to authorize the payment, then
|
||||
// redirected to the `return_url`.
|
||||
if (error.type === "card_error" || error.type === "validation_error") {
|
||||
showMessage(error.message);
|
||||
} else {
|
||||
showMessage("An unexpected error occurred.");
|
||||
if (stripeError) {
|
||||
showMessage(stripeError.message);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
|
||||
// ------- UI helpers -------
|
||||
|
||||
function showMessage(messageText) {
|
||||
const messageContainer = document.querySelector("#payment-message");
|
||||
|
||||
messageContainer.style.display = "block";
|
||||
messageContainer.textContent = messageText;
|
||||
|
||||
setTimeout(function () {
|
||||
messageContainer.style.display = "none";
|
||||
messageContainer.textContent = "";
|
||||
}, 4000);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Show a spinner on payment submission
|
||||
function setLoading(isLoading) {
|
||||
if (isLoading) {
|
||||
// Disable the button and show a spinner
|
||||
document.querySelector("#submit").disabled = true;
|
||||
document.querySelector("#spinner").style.display = "inline";
|
||||
document.querySelector("#button-text").style.display = "none";
|
||||
|
||||
7
db/migrations/005_create_pages_table.sql
Normal file
7
db/migrations/005_create_pages_table.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS pages (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
slug VARCHAR(255) NOT NULL UNIQUE,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
13
footer.php
Normal file
13
footer.php
Normal file
@ -0,0 +1,13 @@
|
||||
</main>
|
||||
|
||||
<footer class="py-4 bg-dark text-white text-center">
|
||||
<div class="container">
|
||||
<p>© <?php echo date('Y'); ?> Australia Broadband Internet. All Rights Reserved.</p>
|
||||
<p><a href="privacy.php" class="text-white">Privacy Policy</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
35
header.php
Normal file
35
header.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php session_start(); ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Australia Broadband Internet</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="index.php">ABI</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#hero">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#plans">Plans</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="index.php#contact">Contact</a></li>
|
||||
<li class="nav-item"><a class="btn btn-primary ms-lg-3" href="index.php#hero">Check Availability</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="py-5 mt-5">
|
||||
77
index.php
77
index.php
@ -1,36 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Australia Broadband Internet</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="#">ABI</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="#hero">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#plans">Plans</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
|
||||
<li class="nav-item"><a class="btn btn-primary ms-lg-3" href="#hero">Check Availability</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<?php include 'header.php'; ?>
|
||||
<section id="hero" class="hero text-center">
|
||||
<div class="container">
|
||||
<h1>Fast, Reliable Internet for Your Home</h1>
|
||||
@ -49,7 +17,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="plans" class="py-5">
|
||||
<section id="plans" class="py-5 bg-light">
|
||||
<div class="container">
|
||||
<h2 class="text-center mb-5">Our Plans</h2>
|
||||
<div class="row">
|
||||
@ -57,11 +25,9 @@
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query("SELECT * FROM plans WHERE is_active = 1 ORDER BY price");
|
||||
$stmt = $pdo->query("SELECT * FROM plans ORDER BY price_monthly");
|
||||
$plans = $stmt->fetchAll();
|
||||
} catch (PDOException $e) {
|
||||
// For the public page, we might not want to show a detailed error.
|
||||
// We can log the error and show a generic message or an empty state.
|
||||
error_log($e->getMessage());
|
||||
$plans = [];
|
||||
}
|
||||
@ -73,12 +39,23 @@
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($plans as $plan): ?>
|
||||
<div class="col-md-3">
|
||||
<div class="plan-card text-center">
|
||||
<h3><?php echo htmlspecialchars($plan['speed']); ?></h3>
|
||||
<p class="price">$<?php echo htmlspecialchars(number_format($plan['price'], 2)); ?><span class="period">/mo</span></p>
|
||||
<p><?php echo htmlspecialchars($plan['description']); ?></p>
|
||||
<a href="signup.php?plan_id=<?php echo $plan['id']; ?>" class="btn btn-primary">Choose Plan</a>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title text-center"><?php echo htmlspecialchars($plan['name']); ?></h5>
|
||||
<div class="text-center my-4">
|
||||
<span class="display-4 fw-bold">$<?php echo htmlspecialchars(number_format($plan['price_monthly'], 2)); ?></span>
|
||||
<span class="text-muted">/mo</span>
|
||||
</div>
|
||||
<ul class="list-unstyled mb-4 text-center">
|
||||
<li class="mb-2"><i class="bi bi-speedometer2 me-2"></i>Up to <?php echo htmlspecialchars($plan['speed_mbps']); ?> Mbps</li>
|
||||
<li class="mb-2"><i class="bi bi-card-text me-2"></i><?php echo htmlspecialchars($plan['description']); ?></li>
|
||||
<li class="mb-2"><i class="bi bi-calendar-check me-2"></i><?php echo htmlspecialchars($plan['contract_months']); ?> months contract</li>
|
||||
</ul>
|
||||
<div class="mt-auto">
|
||||
<a href="signup.php?plan_id=<?php echo $plan['id']; ?>" class="btn btn-primary w-100">Sign Up Now</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
@ -105,16 +82,4 @@
|
||||
<p>Phone: 1300 864 341</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="py-4 bg-dark text-white text-center">
|
||||
<div class="container">
|
||||
<p>© 2025 Australia Broadband Internet. All Rights Reserved.</p>
|
||||
<p><a href="privacy.php" class="text-white">Privacy Policy</a></p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
<?php include 'footer.php'; ?>
|
||||
@ -2,121 +2,115 @@
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
// This is your test secret API key. Don't hardcode this in a real app.
|
||||
\Stripe\Stripe::setApiKey('sk_test_51Hh9Y2L9s5P2Q8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4sYgY8Z4');
|
||||
|
||||
$message = 'An error occurred.';
|
||||
include 'header.php';
|
||||
|
||||
if (isset($_GET['payment_intent']) && isset($_GET['payment_intent_client_secret'])) {
|
||||
$message = '';
|
||||
$order_details = null;
|
||||
$error = false;
|
||||
|
||||
if (empty($_GET['payment_intent'])) {
|
||||
$error = true;
|
||||
$message = "No payment intent provided.";
|
||||
} else {
|
||||
$payment_intent_id = $_GET['payment_intent'];
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
// 1. Verify Payment with Stripe
|
||||
$paymentIntent = \Stripe\PaymentIntent::retrieve($payment_intent_id);
|
||||
|
||||
if ($paymentIntent->status == 'succeeded') {
|
||||
// NOTE: In a real application, you should not trust the amount from the client.
|
||||
// You should fetch the plan from your database based on a stored reference to ensure the price is correct.
|
||||
// For this example, we'll assume the amount is correct.
|
||||
// 2. Find Local Order
|
||||
$stmt = $pdo->prepare("SELECT * FROM orders WHERE stripe_payment_intent_id = ?");
|
||||
$stmt->execute([$payment_intent_id]);
|
||||
$order = $stmt->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
$metadata = $paymentIntent->metadata;
|
||||
$plan_id = $metadata->plan_id ?? null;
|
||||
$customer_name = $metadata->customer_name ?? null;
|
||||
$customer_address = $metadata->customer_address ?? null;
|
||||
$customer_email = $paymentIntent->receipt_email;
|
||||
|
||||
if (empty($plan_id) || empty($customer_name) || empty($customer_address) || empty($customer_email)) {
|
||||
// This indicates a problem, as the metadata or email was not set correctly.
|
||||
// Flag for manual review.
|
||||
error_log("Missing metadata or email for successful payment {$payment_intent_id}.");
|
||||
$message = "Your payment was successful, but there was an error processing your order details. Please contact support.";
|
||||
} else {
|
||||
$pdo = db();
|
||||
|
||||
// Parse name into first and last
|
||||
$name_parts = explode(' ', $customer_name, 2);
|
||||
$first_name = $name_parts[0];
|
||||
$last_name = $name_parts[1] ?? '';
|
||||
|
||||
// For address, we'll just put the whole thing in the street_address for now.
|
||||
// A real app would use a proper address parser or separate fields on the form.
|
||||
$street_address = $customer_address;
|
||||
|
||||
// Generate a temporary password hash. In a real app, you'd send a password reset link.
|
||||
$temp_password = password_hash(bin2hex(random_bytes(16)), PASSWORD_DEFAULT);
|
||||
|
||||
// Check if customer already exists
|
||||
$stmt = $pdo->prepare("SELECT id FROM customers WHERE email = ?");
|
||||
$stmt->execute([$customer_email]);
|
||||
$existing_customer = $stmt->fetch();
|
||||
|
||||
if ($existing_customer) {
|
||||
$customer_id = $existing_customer['id'];
|
||||
// Optional: Update customer details if they have changed
|
||||
$update_stmt = $pdo->prepare("UPDATE customers SET first_name = ?, last_name = ?, street_address = ? WHERE id = ?");
|
||||
$update_stmt->execute([$first_name, $last_name, $street_address, $customer_id]);
|
||||
} else {
|
||||
// Create new customer
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO customers (first_name, last_name, email, password_hash, street_address) VALUES (?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([$first_name, $last_name, $customer_email, $temp_password, $street_address]);
|
||||
$customer_id = $pdo->lastInsertId();
|
||||
if ($order) {
|
||||
// 3. Update Order Status (if it's still pending)
|
||||
if ($order->order_status == 'pending') {
|
||||
$update_stmt = $pdo->prepare("UPDATE orders SET order_status = 'completed' WHERE id = ?");
|
||||
$update_stmt->execute([$order->id]);
|
||||
}
|
||||
|
||||
// Create the order
|
||||
$stmt = $pdo->prepare("INSERT INTO orders (customer_id, plan_id, stripe_payment_intent, status) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$customer_id, $plan_id, $payment_intent_id, 'succeeded']);
|
||||
// 4. Fetch Order Details for Display
|
||||
$details_stmt = $pdo->prepare(
|
||||
"SELECT o.id as order_id, o.amount, c.name as customer_name, c.email, p.name as plan_name
|
||||
FROM orders o
|
||||
JOIN customers c ON o.customer_id = c.id
|
||||
JOIN plans p ON o.plan_id = p.id
|
||||
WHERE o.id = ?"
|
||||
);
|
||||
$details_stmt->execute([$order->id]);
|
||||
$order_details = $details_stmt->fetch(PDO::FETCH_OBJ);
|
||||
|
||||
$message = 'Payment succeeded! Your order has been placed.';
|
||||
$message = "Thank you for your order! Your payment was successful.";
|
||||
|
||||
// Send confirmation email
|
||||
// 5. Send Confirmation Email (optional, but good practice)
|
||||
require_once __DIR__ . '/mail/MailService.php';
|
||||
$subject = 'Your Australia Broadband Internet Order Confirmation';
|
||||
$html_body = "<h1>Welcome, " . htmlspecialchars($customer_name) . "!</h1>"
|
||||
$html_body = "<h1>Welcome, " . htmlspecialchars($order_details->customer_name) . "!</h1>"
|
||||
. "<p>Thank you for your order. Your new internet service is being processed.</p>"
|
||||
. "<p><strong>Order Details:</strong></p>"
|
||||
. "<ul>"
|
||||
. "<li><strong>Plan ID:</strong> " . htmlspecialchars($plan_id) . "</li>"
|
||||
. "<li><strong>Payment ID:</strong> " . htmlspecialchars($payment_intent_id) . "</li>"
|
||||
. "<li><strong>Order ID:</strong> " . htmlspecialchars($order_details->order_id) . "</li>"
|
||||
. "<li><strong>Plan:</strong> " . htmlspecialchars($order_details->plan_name) . "</li>"
|
||||
. "<li><strong>Amount Paid:</strong> $" . htmlspecialchars(number_format($order_details->amount, 2)) . "</li>"
|
||||
. "</ul>"
|
||||
. "<p>You will receive further updates from us shortly.</p>";
|
||||
$text_body = "Welcome, " . $customer_name . "! Thank you for your order. Your new internet service is being processed. Order Details: Plan ID: " . $plan_id . ", Payment ID: " . $payment_intent_id . ". You will receive further updates from us shortly.";
|
||||
MailService::sendMail($order_details->email, $subject, $html_body);
|
||||
|
||||
MailService::sendMail($customer_email, $subject, $html_body, $text_body);
|
||||
} else {
|
||||
$error = true;
|
||||
// This is a critical error. Payment succeeded but we can't find the order.
|
||||
error_log("CRITICAL: Payment succeeded for PI {$payment_intent_id} but no matching order found in DB.");
|
||||
$message = "Your payment was successful, but we could not find your order. Please contact support immediately.";
|
||||
}
|
||||
|
||||
} else {
|
||||
$message = "Payment was not successful. Status: {$paymentIntent->status}";
|
||||
$error = true;
|
||||
$message = "Your payment was not successful. Please try again or contact support.";
|
||||
}
|
||||
} catch (\Stripe\Exception\ApiErrorException $e) {
|
||||
$message = 'Error retrieving payment intent: ' . $e->getMessage();
|
||||
} catch (PDOException $e) {
|
||||
// If the DB insert fails, we have a problem. The customer was charged but the order wasn't created.
|
||||
// A real app needs robust error handling here, like logging the error and flagging for manual review.
|
||||
error_log("Failed to create order in DB after successful payment {$payment_intent_id}: " . $e->getMessage());
|
||||
$message = "Your payment was successful, but there was an error creating your order. Please contact support.";
|
||||
} catch (Exception $e) {
|
||||
$error = true;
|
||||
error_log("Order confirmation error: " . $e->getMessage());
|
||||
$message = "An error occurred while processing your order. Please contact support.";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Order Confirmation</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5 text-center">
|
||||
<div class="card mx-auto" style="max-width: 500px;">
|
||||
<div class="card-body">
|
||||
<h1 class="card-title">Order Confirmation</h1>
|
||||
<p class="card-text"><?php echo htmlspecialchars($message); ?></p>
|
||||
<a href="/" class="btn btn-primary">Back to Home</a>
|
||||
</div>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2 text-center">
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger">
|
||||
<h1 class="alert-heading">Order Error</h1>
|
||||
<p><?php echo htmlspecialchars($message); ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-success">
|
||||
<h1 class="alert-heading">Thank You!</h1>
|
||||
<p><?php echo htmlspecialchars($message); ?></p>
|
||||
</div>
|
||||
<?php if ($order_details): ?>
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
Order Summary
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Order ID:</strong> <?php echo htmlspecialchars($order_details->order_id); ?></p>
|
||||
<p><strong>Customer:</strong> <?php echo htmlspecialchars($order_details->customer_name); ?></p>
|
||||
<p><strong>Plan:</strong> <?php echo htmlspecialchars($order_details->plan_name); ?></p>
|
||||
<p><strong>Amount Paid:</strong> $<?php echo htmlspecialchars(number_format($order_details->amount, 2)); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<a href="/" class="btn btn-primary mt-4">Back to Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
include 'footer.php';
|
||||
?>
|
||||
36
page.php
Normal file
36
page.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
require_once 'db/config.php';
|
||||
|
||||
$slug = $_GET['slug'] ?? null;
|
||||
|
||||
if (!$slug) {
|
||||
http_response_code(404);
|
||||
echo "Page not found.";
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = db()->prepare('SELECT * FROM pages WHERE slug = ?');
|
||||
$stmt->execute([$slug]);
|
||||
$page = $stmt->fetch();
|
||||
|
||||
if (!$page) {
|
||||
http_response_code(404);
|
||||
echo "Page not found.";
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-5 pt-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<h1 class="display-4 fw-bold text-center"><?php echo htmlspecialchars($page['title']); ?></h1>
|
||||
<div class="mt-4 fs-5">
|
||||
<?php echo nl2br(htmlspecialchars($page['content'])); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
108
signup.php
108
signup.php
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
include 'header.php';
|
||||
|
||||
if (!isset($_GET['plan_id'])) {
|
||||
header('Location: /#plans');
|
||||
@ -7,75 +8,70 @@ if (!isset($_GET['plan_id'])) {
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM plans WHERE id = ? AND is_active = 1");
|
||||
$stmt = $pdo->prepare("SELECT * FROM plans WHERE id = ?");
|
||||
$stmt->execute([$_GET['plan_id']]);
|
||||
$plan = $stmt->fetch();
|
||||
|
||||
if (!$plan) {
|
||||
// Plan not found or not active
|
||||
// Plan not found
|
||||
header('Location: /#plans');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Page content for signup
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sign Up for <?php echo htmlspecialchars($plan['name']); ?></title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<h1 class="mb-4">Complete Your Order</h1>
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<h1 class="mb-4">Complete Your Order</h1>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Your Plan</h4>
|
||||
<h5><?php echo htmlspecialchars($plan['name']); ?></h5>
|
||||
<p class="price fs-4">$<?php echo htmlspecialchars(number_format($plan['price_monthly'], 2)); ?><span class="period">/mo</span></p>
|
||||
<p><?php echo htmlspecialchars($plan['description']); ?></p>
|
||||
</div>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Your Plan</h4>
|
||||
<h5><?php echo htmlspecialchars($plan['name']); ?></h5>
|
||||
<p class="price fs-4">$<?php echo htmlspecialchars(number_format($plan['price_monthly'], 2)); ?><span class="period">/mo</span></p>
|
||||
<p><?php echo htmlspecialchars($plan['description']); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mb-3">Your Details</h4>
|
||||
<form id="signup-form" data-plan-id="<?php echo $plan['id']; ?>">
|
||||
<!-- User details form -->
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Full Name</label>
|
||||
<input type="text" id="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email Address</label>
|
||||
<input type="email" id="email" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">Service Address</label>
|
||||
<input type="text" id="address" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<h4 class="mb-3">Your Details</h4>
|
||||
<form id="signup-form">
|
||||
<!-- User details form -->
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Full Name</label>
|
||||
<input type="text" id="name" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email Address</label>
|
||||
<input type="email" id="email" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">Service Address</label>
|
||||
<input type="text" id="address" class="form-control" required>
|
||||
</div>
|
||||
<h4 class="mb-3 mt-4">Payment Details</h4>
|
||||
<!-- Stripe Payment Element will go here -->
|
||||
<div id="payment-element" class="mb-3"></div>
|
||||
|
||||
<h4 class="mb-3 mt-4">Payment Details</h4>
|
||||
<!-- Stripe Payment Element will go here -->
|
||||
<div id="payment-element" class="mb-3"></div>
|
||||
<!-- Used to display form errors -->
|
||||
<div id="payment-message" class="hidden"></div>
|
||||
|
||||
<!-- Used to display form errors -->
|
||||
<div id="payment-message" class="hidden"></div>
|
||||
|
||||
<button id="submit" class="btn btn-primary btn-lg w-100">
|
||||
<span id="button-text">Pay Now</span>
|
||||
<span id="spinner" style="display: none;">Processing...</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<button id="submit" class="btn btn-primary btn-lg w-100">
|
||||
<span id="button-text">Pay Now</span>
|
||||
<span id="spinner" style="display: none;">Processing...</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<script src="assets/js/signup.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<script>
|
||||
// Pass plan details to JS
|
||||
const planDetails = {
|
||||
id: <?php echo json_encode($plan['id']); ?>,
|
||||
price: <?php echo json_encode($plan['price_monthly']); ?>
|
||||
};
|
||||
</script>
|
||||
<script src="assets/js/signup.js?v=<?php echo time(); ?>"></script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
107
support.php
Normal file
107
support.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<h1 class="text-center mb-4">AI Support Assistant</h1>
|
||||
<div class="card">
|
||||
<div class="card-body" id="chat-window" style="height: 400px; overflow-y: scroll;">
|
||||
<!-- Chat messages will appear here -->
|
||||
<div class="d-flex flex-row justify-content-start mb-4">
|
||||
<div class="p-3 ms-3" style="border-radius: 15px; background-color: #f5f6f7;">
|
||||
<p class="small mb-0">Hello! How can I help you today? Ask me anything about our plans or services.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer text-muted d-flex justify-content-start align-items-center p-3">
|
||||
<input type="text" class="form-control form-control-lg" id="user-input" placeholder="Type your message">
|
||||
<button class="btn btn-primary ms-3" id="send-btn">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const chatWindow = document.getElementById('chat-window');
|
||||
const userInput = document.getElementById('user-input');
|
||||
const sendBtn = document.getElementById('send-btn');
|
||||
|
||||
const addMessage = (message, sender) => {
|
||||
const messageDiv = document.createElement('div');
|
||||
const messageContent = document.createElement('div');
|
||||
const text = document.createElement('p');
|
||||
|
||||
messageDiv.classList.add('d-flex', 'flex-row', 'mb-4');
|
||||
messageContent.classList.add('p-3');
|
||||
text.classList.add('small', 'mb-0');
|
||||
|
||||
text.innerText = message;
|
||||
messageContent.appendChild(text);
|
||||
|
||||
if (sender === 'user') {
|
||||
messageDiv.classList.add('justify-content-end');
|
||||
messageContent.classList.add('me-3', 'text-white');
|
||||
messageContent.style.borderRadius = '15px';
|
||||
messageContent.style.backgroundColor = '#0d6efd'; // Bootstrap Primary Blue
|
||||
} else {
|
||||
messageDiv.classList.add('justify-content-start');
|
||||
messageContent.classList.add('ms-3');
|
||||
messageContent.style.borderRadius = '15px';
|
||||
messageContent.style.backgroundColor = '#f5f6f7'; // Light grey
|
||||
}
|
||||
|
||||
messageDiv.appendChild(messageContent);
|
||||
chatWindow.appendChild(messageDiv);
|
||||
chatWindow.scrollTop = chatWindow.scrollHeight; // Auto-scroll to the latest message
|
||||
};
|
||||
|
||||
const handleSend = async () => {
|
||||
const question = userInput.value.trim();
|
||||
if (!question) return;
|
||||
|
||||
addMessage(question, 'user');
|
||||
userInput.value = '';
|
||||
userInput.disabled = true;
|
||||
sendBtn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/ai_support.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ question })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.reply) {
|
||||
addMessage(data.reply, 'ai');
|
||||
} else if (data.error) {
|
||||
addMessage(data.error, 'ai');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
addMessage('Sorry, something went wrong. Please try again later.', 'ai');
|
||||
} finally {
|
||||
userInput.disabled = false;
|
||||
sendBtn.disabled = false;
|
||||
userInput.focus();
|
||||
}
|
||||
};
|
||||
|
||||
sendBtn.addEventListener('click', handleSend);
|
||||
userInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSend();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user