From 9660f06ca5a86e6bd19b60e25f27009d09c45faa Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 12 Oct 2025 11:03:22 +0000 Subject: [PATCH] full --- admin/customer_delete.php | 5 +- admin/customer_edit.php | 12 +- admin/customers.php | 4 +- admin/header.php | 1 + admin/orders.php | 25 +++- admin/page_delete.php | 12 ++ admin/page_edit.php | 55 ++++++++ admin/pages.php | 37 +++++ api/ai_support.php | 75 ++++++++++ api/process_signup.php | 77 ++++++++++ assets/js/signup.js | 88 +++++------- db/migrations/005_create_pages_table.sql | 7 + footer.php | 13 ++ header.php | 35 +++++ index.php | 77 +++------- order_confirmation.php | 172 +++++++++++------------ page.php | 36 +++++ signup.php | 112 +++++++-------- support.php | 107 ++++++++++++++ 19 files changed, 677 insertions(+), 273 deletions(-) create mode 100644 admin/page_delete.php create mode 100644 admin/page_edit.php create mode 100644 admin/pages.php create mode 100644 api/ai_support.php create mode 100644 api/process_signup.php create mode 100644 db/migrations/005_create_pages_table.sql create mode 100644 footer.php create mode 100644 header.php create mode 100644 page.php create mode 100644 support.php diff --git a/admin/customer_delete.php b/admin/customer_delete.php index 3b32155..0519e84 100644 --- a/admin/customer_delete.php +++ b/admin/customer_delete.php @@ -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']]); diff --git a/admin/customer_edit.php b/admin/customer_edit.php index 47d77e3..8c29db1 100644 --- a/admin/customer_edit.php +++ b/admin/customer_edit.php @@ -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') {
- - + +
Cancel diff --git a/admin/customers.php b/admin/customers.php index 4c96ee9..5a0f5d6 100644 --- a/admin/customers.php +++ b/admin/customers.php @@ -17,7 +17,7 @@ $customers = $stmt->fetchAll(); ID Name Email - Address + Service Address Joined On Actions @@ -33,7 +33,7 @@ $customers = $stmt->fetchAll(); - + Edit diff --git a/admin/header.php b/admin/header.php index 684aa5b..fa187f6 100644 --- a/admin/header.php +++ b/admin/header.php @@ -43,6 +43,7 @@ Customers Orders Plans + Pages
diff --git a/admin/orders.php b/admin/orders.php index d95491e..7d06edb 100644 --- a/admin/orders.php +++ b/admin/orders.php @@ -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'; + } +} ?>
@@ -17,6 +32,7 @@ $orders = $stmt->fetchAll(); Order ID Customer Plan + Amount Status Order Date @@ -24,7 +40,7 @@ $orders = $stmt->fetchAll(); - No orders found. + No orders found. @@ -32,8 +48,9 @@ $orders = $stmt->fetchAll(); - - + $ + + diff --git a/admin/page_delete.php b/admin/page_delete.php new file mode 100644 index 0000000..f38b611 --- /dev/null +++ b/admin/page_delete.php @@ -0,0 +1,12 @@ +prepare('DELETE FROM pages WHERE id = ?'); + $stmt->execute([$id]); +} + +header('Location: pages.php'); +exit; diff --git a/admin/page_edit.php b/admin/page_edit.php new file mode 100644 index 0000000..0eefdd5 --- /dev/null +++ b/admin/page_edit.php @@ -0,0 +1,55 @@ +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'; +?> + +

+ +
+
+ + +
+
+ + +
The slug is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.
+
+
+ + +
+ + Cancel +
+ + diff --git a/admin/pages.php b/admin/pages.php new file mode 100644 index 0000000..641ba9a --- /dev/null +++ b/admin/pages.php @@ -0,0 +1,37 @@ +query('SELECT * FROM pages ORDER BY title ASC')->fetchAll(); + +include 'header.php'; +?> + +
+

Pages

+ Create Page +
+ + + + + + + + + + + + + + + + + + +
TitleSlugActions
+ Edit + Delete + View +
+ + diff --git a/api/ai_support.php b/api/ai_support.php new file mode 100644 index 0000000..e53bc31 --- /dev/null +++ b/api/ai_support.php @@ -0,0 +1,75 @@ +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.']); +} + diff --git a/api/process_signup.php b/api/process_signup.php new file mode 100644 index 0000000..9b74fe0 --- /dev/null +++ b/api/process_signup.php @@ -0,0 +1,77 @@ +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()]); +} diff --git a/assets/js/signup.js b/assets/js/signup.js index 2095787..3d8a305 100644 --- a/assets/js/signup.js +++ b/assets/js/signup.js @@ -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; - - if (!name || !email || !address) { + const planId = form.dataset.planId; + + 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, + body: JSON.stringify({ + 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"; @@ -110,4 +90,4 @@ function setLoading(isLoading) { document.querySelector("#spinner").style.display = "none"; document.querySelector("#button-text").style.display = "inline"; } -} \ No newline at end of file +} diff --git a/db/migrations/005_create_pages_table.sql b/db/migrations/005_create_pages_table.sql new file mode 100644 index 0000000..e296374 --- /dev/null +++ b/db/migrations/005_create_pages_table.sql @@ -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 +); diff --git a/footer.php b/footer.php new file mode 100644 index 0000000..7d87b6a --- /dev/null +++ b/footer.php @@ -0,0 +1,13 @@ + + +
+
+

© Australia Broadband Internet. All Rights Reserved.

+

Privacy Policy

+
+
+ + + + + diff --git a/header.php b/header.php new file mode 100644 index 0000000..aa4515b --- /dev/null +++ b/header.php @@ -0,0 +1,35 @@ + + + + + + + Australia Broadband Internet + + + + + + + + + + + +
diff --git a/index.php b/index.php index 2cfc0e5..22857cc 100644 --- a/index.php +++ b/index.php @@ -1,36 +1,4 @@ - - - - - - Australia Broadband Internet - - - - - - - - - - -
+

Fast, Reliable Internet for Your Home

@@ -49,7 +17,7 @@
-
+

Our Plans

@@ -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 @@
-
-
-

-

$/mo

-

- Choose Plan +
+
+
+
+
+ $ + /mo +
+
    +
  • Up to Mbps
  • +
  • +
  • months contract
  • +
+ +
@@ -105,16 +82,4 @@

Phone: 1300 864 341

-
- -
-
-

© 2025 Australia Broadband Internet. All Rights Reserved.

-

Privacy Policy

-
-
- - - - - \ No newline at end of file + \ No newline at end of file diff --git a/order_confirmation.php b/order_confirmation.php index cfec4ca..17e2af0 100644 --- a/order_confirmation.php +++ b/order_confirmation.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 = "

Welcome, " . htmlspecialchars($customer_name) . "!

" + $html_body = "

Welcome, " . htmlspecialchars($order_details->customer_name) . "!

" . "

Thank you for your order. Your new internet service is being processed.

" . "

Order Details:

" . "
    " - . "
  • Plan ID: " . htmlspecialchars($plan_id) . "
  • " - . "
  • Payment ID: " . htmlspecialchars($payment_intent_id) . "
  • " + . "
  • Order ID: " . htmlspecialchars($order_details->order_id) . "
  • " + . "
  • Plan: " . htmlspecialchars($order_details->plan_name) . "
  • " + . "
  • Amount Paid: $" . htmlspecialchars(number_format($order_details->amount, 2)) . "
  • " . "
" . "

You will receive further updates from us shortly.

"; - $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($customer_email, $subject, $html_body, $text_body); - } + MailService::sendMail($order_details->email, $subject, $html_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."; } } - ?> - - - - - - Order Confirmation - - - - -
-
-
-

Order Confirmation

-

- Back to Home -
+ +
+
+
+ +
+

Order Error

+

+
+ +
+

Thank You!

+

+
+ +
+
+ Order Summary +
+
+

Order ID: order_id); ?>

+

Customer: customer_name); ?>

+

Plan: plan_name); ?>

+

Amount Paid: $amount, 2)); ?>

+
+
+ + + Back to Home
- - +
+ + \ No newline at end of file diff --git a/page.php b/page.php new file mode 100644 index 0000000..023faf1 --- /dev/null +++ b/page.php @@ -0,0 +1,36 @@ +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'; +?> + +
+
+
+

+
+ +
+
+
+
+ + diff --git a/signup.php b/signup.php index 299ad4e..af9bb0e 100644 --- a/signup.php +++ b/signup.php @@ -1,5 +1,6 @@ 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 ?> - - - - - - Sign Up for <?php echo htmlspecialchars($plan['name']); ?> - - - - -
-
-
-

Complete Your Order

- -
-
-

Your Plan

-
-

$/mo

-

-
+
+
+
+

Complete Your Order

+ +
+
+

Your Plan

+
+

$/mo

+

+
+
+ +

Your Details

+
+ +
+ + +
+
+ + +
+
+ +
-

Your Details

- - -
- - -
-
- - -
-
- - -
+

Payment Details

+ +
+ + + -

Payment Details

- -
- - - - - -
-
+ +
+
- - - - + + + + + diff --git a/support.php b/support.php new file mode 100644 index 0000000..0242926 --- /dev/null +++ b/support.php @@ -0,0 +1,107 @@ + + +
+
+
+

AI Support Assistant

+
+
+ +
+
+

Hello! How can I help you today? Ask me anything about our plans or services.

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