Mails included

This commit is contained in:
Flatlogic Bot 2025-12-28 14:19:17 +00:00
parent d4180cc06e
commit 76dbc45562
10 changed files with 748 additions and 4714 deletions

View File

@ -34,9 +34,64 @@ if (!$order_id) {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['status'])) {
$new_status = $_POST['status'];
$stmt = $pdo->prepare("SELECT status FROM orders WHERE id = ?");
$stmt->execute([$order_id]);
$old_status = $stmt->fetchColumn();
$update_stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE id = ?");
$update_stmt->execute([$new_status, $order_id]);
// If status changed to 'paid', send notifications
if ($new_status === 'paid' && $old_status !== 'paid') {
require_once __DIR__ . '/../mail/MailService.php';
// Fetch order and client details for email
$stmt = $pdo->prepare("
SELECT o.*, c.name as client_company_name, u.email as client_email
FROM orders o
JOIN clients c ON o.client_id = c.id
JOIN users u ON c.id = u.client_id
WHERE o.id = ? AND u.id = (SELECT MIN(id) FROM users WHERE client_id = o.client_id)
");
$stmt->execute([$order_id]);
$order_details_for_email = $stmt->fetch(PDO::FETCH_ASSOC);
if ($order_details_for_email && !empty($order_details_for_email['client_email'])) {
$client_email = $order_details_for_email['client_email'];
$order_details_link = get_site_url() . '/order_details.php?id=' . $order_id;
$admin_email = getenv('MAIL_TO') ?: 'admin@example.com';
// 4. Client - Payment Confirmation
$subject_client = "Payment Confirmation for Order #{$order_id}";
$body_html_client = "<p>Your payment for order #{$order_id} has been confirmed.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<p><strong>Paid Amount:</strong> " . format_currency($order_details_for_email['total_amount']) . "</p>"
. "<p>Thank you for your purchase!</p>"
. "<p>You can view your order details here: <a href='{$order_details_link}'>{$order_details_link}</a></p>";
$body_text_client = "Your payment for order #{$order_id} has been confirmed.\n"
. "Order Number: {$order_id}\n"
. "Paid Amount: " . format_currency($order_details_for_email['total_amount']) . "\n"
. "Thank you for your purchase!\n"
. "View your order details here: {$order_details_link}";
MailService::sendMail($client_email, $subject_client, $body_html_client, $body_text_client);
// 5. Admin - Payment Confirmation
$subject_admin = "Payment Received for Order #{$order_id}";
$body_html_admin = "<p>Payment has been confirmed for order #{$order_id}.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<p><strong>Client:</strong> {$order_details_for_email['client_company_name']}</p>"
. "<p><strong>Paid Amount:</strong> " . format_currency($order_details_for_email['total_amount']) . "</p>"
. "<p><strong>Payment Reference ID:</strong> (Online Payment)</p>"; // Placeholder
$body_text_admin = "Payment has been confirmed for order #{$order_id}.\n"
. "Order Number: {$order_id}\n"
. "Client: {$order_details_for_email['client_company_name']}\n"
. "Paid Amount: " . format_currency($order_details_for_email['total_amount']) . "\n"
. "Payment Reference ID: (Online Payment)"; // Placeholder
MailService::sendMail($admin_email, $subject_admin, $body_html_admin, $body_text_admin);
}
}
header("Location: order_details.php?id=$order_id");
exit;
}

View File

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS suppliers (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<?php
require_once __DIR__ . '/../db/config.php';
$user_role = get_user_role();
$current_lang = get_lang();

View File

@ -151,4 +151,10 @@ function getEffectivePrice(PDO $db, int $productId, ?int $clientId): array {
file_put_contents($logFile, "FINAL: Returning Net: $net, Gross: $gross\n---\n", FILE_APPEND);
return ['net' => $net, 'gross' => $gross];
}
function get_site_url() {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
$host = $_SERVER['HTTP_HOST'];
return $protocol . $host;
}

View File

@ -1,4 +1,8 @@
<?php
ini_set('log_errors', 1);
ini_set('error_log', '/home/ubuntu/.gemini/tmp/ec2c6b8cb2ac3d6ef62b888a61e62d5ab1e71e46b3c4963f2a040a3c33c7038b/php_errors.log');
error_reporting(E_ALL);
require_once __DIR__ . '/includes/init.php';
$page_title = t('catalog_title');
@ -127,7 +131,7 @@ try {
<p class="card-text fw-bold"><?= format_money($prices['gross'], $_SESSION['lang'] ?? 'pl', $pdo) ?> <?= t('gross') ?></p>
</div>
</div>
<div class="card-footer bg-white border-top-0 pb-3">
<div class.card-footer bg-white border-top-0 pb-3">
<form action="cart_actions.php" method="POST" class="d-grid">
<input type="hidden" name="action" value="add">
<input type="hidden" name="product_id" value="<?= $product['id'] ?>">

View File

@ -158,20 +158,28 @@ class MailService
{
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
try {
$mail->SMTPDebug = 2; // Enable verbose debug output
$mail->isSMTP();
$mail->Host = $cfg['smtp_host'] ?? '';
error_log('SMTP Host: ' . $mail->Host);
$mail->Port = (int)($cfg['smtp_port'] ?? 587);
error_log('SMTP Port: ' . $mail->Port);
$secure = $cfg['smtp_secure'] ?? 'tls';
if ($secure === 'ssl') $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS;
elseif ($secure === 'tls') $mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
else $mail->SMTPSecure = false;
error_log('SMTP Secure: ' . $secure);
$mail->SMTPAuth = true;
$mail->Username = $cfg['smtp_user'] ?? '';
error_log('SMTP User: ' . $mail->Username);
$mail->Password = $cfg['smtp_pass'] ?? '';
// Do not log password for security reasons
$fromEmail = $cfg['from_email'] ?? 'no-reply@localhost';
$fromName = $cfg['from_name'] ?? 'App';
$mail->setFrom($fromEmail, $fromName);
error_log('From Email: ' . $fromEmail);
error_log('From Name: ' . $fromName);
// Use Reply-To for the user's email to avoid spoofing From
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {

View File

@ -1,76 +1,51 @@
<?php
// Mail configuration sourced from environment variables.
// No secrets are stored here; the file just maps env -> config array for MailService.
function env_val(string $key, $default = null) {
$v = getenv($key);
return ($v === false || $v === null || $v === '') ? $default : $v;
}
// Fallback: if critical vars are missing from process env, try to parse executor/.env
// This helps in web/Apache contexts where .env is not exported.
// Supports simple KEY=VALUE lines; ignores quotes and comments.
function load_dotenv_if_needed(array $keys): void {
$missing = array_filter($keys, fn($k) => getenv($k) === false || getenv($k) === '');
if (empty($missing)) return;
static $loaded = false;
if ($loaded) return;
$envPath = realpath(__DIR__ . '/../../.env'); // executor/.env
if ($envPath && is_readable($envPath)) {
$lines = @file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
foreach ($lines as $line) {
if ($line[0] === '#' || trim($line) === '') continue;
if (!str_contains($line, '=')) continue;
[$k, $v] = array_map('trim', explode('=', $line, 2));
// Strip potential surrounding quotes
$v = trim($v, "\"' ");
// Do not override existing env
if ($k !== '' && (getenv($k) === false || getenv($k) === '')) {
putenv("{$k}={$v}");
}
}
$loaded = true;
if (!function_exists('get_env_var')) {
function get_env_var($key, $default = null) {
if (isset($_ENV[$key])) return $_ENV[$key];
$val = getenv($key);
return $val === false ? $default : $val;
}
}
load_dotenv_if_needed([
'MAIL_TRANSPORT','SMTP_HOST','SMTP_PORT','SMTP_SECURE','SMTP_USER','SMTP_PASS',
'MAIL_FROM','MAIL_FROM_NAME','MAIL_REPLY_TO','MAIL_TO',
'DKIM_DOMAIN','DKIM_SELECTOR','DKIM_PRIVATE_KEY_PATH'
]);
if (!function_exists('load_dot_env')) {
function load_dot_env($path) {
if (!file_exists($path) || !is_readable($path)) return;
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) continue;
list($key, $val) = explode('=', $line, 2) + [null, null];
if ($key === null) continue;
$key = trim($key);
$val = trim($val);
if (preg_match('/^"(.*)"$/', $val, $matches)) $val = $matches[1];
elseif (preg_match("/'(.*)'/ ", $val, $matches)) $val = $matches[1];
if (!array_key_exists($key, $_SERVER) && !array_key_exists($key, $_ENV)) {
putenv("$key=$val");
$_ENV[$key] = $val;
}
}
}
}
$transport = env_val('MAIL_TRANSPORT', 'smtp');
$smtp_host = env_val('SMTP_HOST');
$smtp_port = (int) env_val('SMTP_PORT', 587);
$smtp_secure = env_val('SMTP_SECURE', 'tls'); // tls | ssl | null
$smtp_user = env_val('SMTP_USER');
$smtp_pass = env_val('SMTP_PASS');
$from_email = env_val('MAIL_FROM', 'no-reply@localhost');
$from_name = env_val('MAIL_FROM_NAME', 'App');
$reply_to = env_val('MAIL_REPLY_TO');
$dkim_domain = env_val('DKIM_DOMAIN');
$dkim_selector = env_val('DKIM_SELECTOR');
$dkim_private_key_path = env_val('DKIM_PRIVATE_KEY_PATH');
$dotenv_path = __DIR__ . '/../.env';
if (file_exists($dotenv_path)) {
load_dot_env($dotenv_path);
}
return [
'transport' => $transport,
'transport' => get_env_var('MAIL_TRANSPORT', 'smtp'),
'smtp_host' => get_env_var('SMTP_HOST'),
'smtp_port' => get_env_var('SMTP_PORT', 587),
'smtp_secure' => get_env_var('SMTP_SECURE', 'tls'), // tls or ssl
'smtp_user' => get_env_var('SMTP_USER'),
'smtp_pass' => get_env_var('SMTP_PASS'),
'from_email' => get_env_var('MAIL_FROM'),
'from_name' => get_env_var('MAIL_FROM_NAME'),
'reply_to' => get_env_var('MAIL_REPLY_TO'),
// SMTP
'smtp_host' => $smtp_host,
'smtp_port' => $smtp_port,
'smtp_secure' => $smtp_secure,
'smtp_user' => $smtp_user,
'smtp_pass' => $smtp_pass,
// From / Reply-To
'from_email' => $from_email,
'from_name' => $from_name,
'reply_to' => $reply_to,
// DKIM (optional)
'dkim_domain' => $dkim_domain,
'dkim_selector' => $dkim_selector,
'dkim_private_key_path' => $dkim_private_key_path,
// Optional DKIM signing
'dkim_domain' => get_env_var('DKIM_DOMAIN', ''),
'dkim_selector' => get_env_var('DKIM_SELECTOR', 'default'),
'dkim_private_key_path' => get_env_var('DKIM_PRIVATE_KEY_PATH', ''), // Path to the private key file
];

View File

@ -2,10 +2,12 @@
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once 'mail/MailService.php';
require_once 'includes/lang.php';
require_once 'includes/auth.php';
require_login();
require_once 'includes/helpers.php';
require_once 'includes/currency.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: checkout.php');
@ -110,16 +112,183 @@ try {
// 5. Commit the transaction
$pdo->commit();
// 6. Clear the cart and store order ID in session for the confirmation page
// 6. Send email notifications
// --- Data Fetching for Emails ---
$user_id = $_SESSION['user_id'] ?? null;
$stmt = $pdo->prepare('SELECT email FROM users WHERE id = ?');
$stmt->execute([$user_id]);
$user = $stmt->fetch();
$client_email = $user['email'] ?? null;
$stmt = $pdo->prepare('SELECT name FROM clients WHERE id = ?');
$stmt->execute([$client_id]);
$client = $stmt->fetch();
$client_company_name = $client['name'] ?? 'N/A';
$product_list_html = '<ul>';
$product_list_text = '';
$product_ids_for_names = array_map(function($item) { return $item['product_id']; }, $order_items_data);
$placeholders = implode(',', array_fill(0, count($product_ids_for_names), '?'));
$stmt = $pdo->prepare("SELECT id, name FROM products WHERE id IN ($placeholders)");
$stmt->execute($product_ids_for_names);
$products_by_id_for_email = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
foreach ($order_items_data as $item) {
$product_name = $products_by_id_for_email[$item['product_id']] ?? 'Unknown Product';
$product_list_html .= "<li>{$product_name} (Quantity: {$item['quantity']})</li>";
$product_list_text .= "- {$product_name} (Quantity: {$item['quantity']})\n";
}
$product_list_html .= '</ul>';
$order_details_link = get_site_url() . '/order_details.php?id=' . $order_id;
$admin_order_details_link = get_site_url() . '/admin/order_details.php?id=' . $order_id;
$admin_email = getenv('MAIL_TO') ?: 'admin@example.com';
// --- Email Sending Logic ---
if ($client_email) {
// 1. Client - Order Confirmation
$subject_client = "Your Order Confirmation (#{$order_id})";
$body_html_client = "<p>Thank you for your order.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<p><strong>Order Date:</strong> " . date('Y-m-d') . "</p>"
. "<strong>Products:</strong>{$product_list_html}"
. "<p><strong>Total Amount:</strong> " . format_money($total_amount_gross, $lang, $pdo) . "</p>"
. "<p><strong>Payment Method:</strong> {" . strtoupper($_POST['payment_method']) . "}</p>"
. "<p>You can view your order details here: <a href='{$order_details_link}'>{$order_details_link}</a></p>";
$body_text_client = "Thank you for your order.\n"
. "Order Number: {$order_id}\n"
. "Order Date: " . date('Y-m-d') . "\n"
. "Products:\n{$product_list_text}"
. "Total Amount: " . format_money($total_amount_gross, $lang, $pdo) . "\n"
. "Payment Method: {" . strtoupper($_POST['payment_method']) . "}\n"
. "View your order details here: {$order_details_link}";
MailService::sendMail($client_email, $subject_client, $body_html_client, $body_text_client);
// 3. Client - Payment Instructions (Bank Transfer)
if ($_POST['payment_method'] === 'bank_transfer') {
// TODO: Fetch bank details from a settings table or config
$bank_account_details = "Bank: Example Bank\nAccount Number: 123456789";
$transfer_title = "Order #{$order_id}";
$payment_deadline = date('Y-m-d', strtotime('+7 days'));
$subject_bank = "Payment Instructions for Order #{$order_id}";
$body_html_bank = "<p>Please make a payment for your order.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<p><strong>Amount:</strong> " . format_money($total_amount_gross, $lang, $pdo) . "</p>"
. "<p><strong>Bank Account Details:</strong><br>" . nl2br($bank_account_details) . "</p>"
. "<p><strong>Transfer Title:</strong> {$transfer_title}</p>"
. "<p><strong>Payment Deadline:</strong> {$payment_deadline}</p>";
$body_text_bank = "Please make a payment for your order.\n"
. "Order Number: {$order_id}\n"
. "Amount: " . format_money($total_amount_gross, $lang, $pdo) . "\n"
. "Bank Account Details:\n" . $bank_account_details . "\n"
. "Transfer Title: {$transfer_title}\n"
. "Payment Deadline: {$payment_deadline}";
MailService::sendMail($client_email, $subject_bank, $body_html_bank, $body_text_bank);
}
// 6. Client - Credit Payment Confirmation
if ($_POST['payment_method'] === 'credit') {
$subject_credit = "Your Order Paid with Trade Credit (#{$order_id})";
$body_html_credit = "<p>Your order has been successfully placed using your trade credit.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<p><strong>Amount:</strong> " . format_money($total_amount_gross, $lang, $pdo) . "</p>"
. "<p><strong>Remaining Credit Limit:</strong> " . format_money($new_balance, $lang, $pdo) . "</p>"
. "<p>You can view your order details here: <a href='{$order_details_link}'>{$order_details_link}</a></p>";
$body_text_credit = "Your order has been successfully placed using your trade credit.\n"
. "Order Number: {$order_id}\n"
. "Amount: " . format_money($total_amount_gross, $lang, $pdo) . "\n"
. "Remaining Credit Limit: " . format_money($new_balance, $lang, $pdo) . "\n"
. "View your order details here: {$order_details_link}";
MailService::sendMail($client_email, $subject_credit, $body_html_credit, $body_text_credit);
}
}
// 2. Admin - New Order Notification
$subject_admin = "New Order Received (#{$order_id})";
$body_html_admin = "<p>A new order has been placed.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<p><strong>Client:</strong> {$client_company_name}</p>"
. "<p><strong>Total Amount:</strong> " . format_money($total_amount_gross, $lang, $pdo) . "</p>"
. "<p><strong>Payment Method:</strong> {" . strtoupper($_POST['payment_method']) . "}</p>"
. "<p><strong>Delivery Source:</strong> {$delivery_source}</p>"
. "<p>View the order here: <a href='{$admin_order_details_link}'>{$admin_order_details_link}</a></p>";
$body_text_admin = "A new order has been placed.\n"
. "Order Number: {$order_id}\n"
. "Client: {$client_company_name}\n"
. "Total Amount: " . format_money($total_amount_gross, $lang, $pdo) . "\n"
. "Payment Method: {" . strtoupper($_POST['payment_method']) . "}\n"
. "Delivery Source: {$delivery_source}\n"
. "View the order here: {$admin_order_details_link}";
MailService::sendMail($admin_email, $subject_admin, $body_html_admin, $body_text_admin);
// 7. Supplier - New Order Items
$stmt = $pdo->prepare("\n SELECT p.supplier_id, s.name as supplier_name, s.email as supplier_email, p.name as product_name, oi.quantity\n FROM order_items oi\n JOIN products p ON oi.product_id = p.id\n JOIN suppliers s ON p.supplier_id = s.id\n WHERE oi.order_id = ? AND p.supplier_id IS NOT NULL\n ");
$stmt->execute([$order_id]);
$supplier_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
$items_by_supplier = [];
foreach ($supplier_items as $item) {
$items_by_supplier[$item['supplier_id']]['name'] = $item['supplier_name'];
$items_by_supplier[$item['supplier_id']]['email'] = $item['supplier_email'];
$items_by_supplier[$item['supplier_id']]['items'][] = [
'name' => $item['product_name'],
'quantity' => $item['quantity'],
];
}
foreach ($items_by_supplier as $supplier_id => $supplier_data) {
if (!empty($supplier_data['email'])) {
$product_list_supplier_html = '<ul>';
$product_list_supplier_text = '';
foreach ($supplier_data['items'] as $product) {
$product_list_supplier_html .= "<li>{$product['name']}\n (Quantity: {$product['quantity']})</li>";
$product_list_supplier_text .= "- {$product['name']}\n (Quantity: {$product['quantity']})\n";
}
$product_list_supplier_html .= '</ul>';
$subject_supplier = "New Items in Order #{$order_id}";
$body_html_supplier = "<p>You have new items in order #{$order_id}.</p>"
. "<p><strong>Order Number:</strong> {$order_id}</p>"
. "<strong>Products:</strong>{$product_list_supplier_html}";
$body_text_supplier = "You have new items in order #{$order_id}.\n"
. "Order Number: {$order_id}\n"
. "Products:\n{$product_list_supplier_text}";
MailService::sendMail($supplier_data['email'], $subject_supplier, $body_html_supplier, $body_text_supplier);
}
}
// 8. Clear the cart and store order ID in session for the confirmation page
unset($_SESSION['cart']);
$_SESSION['latest_order_id'] = $order_id;
// 7. Redirect to confirmation page
// 9. Redirect to confirmation page
header('Location: order_confirmation.php');
exit;
} catch (PDOException $e) {
$pdo->rollBack();
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
// In a real application, log this error
die("Błąd podczas przetwarzania zamówienia: " . $e->getMessage());
} catch (PDOException $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
// In a real application, log this error
die("Błąd podczas przetwarzania zamówienia: " . $e->getMessage());
} catch (Throwable $t) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
die("An unexpected error occurred: " . $t->getMessage());
}

1
test_error.php Normal file
View File

@ -0,0 +1 @@
<?php error_log('This is a test error from test_error.php'); echo 'Error test complete.'; ?>