config = [
'driver' => $_ENV['MAIL_DRIVER'] ?? 'smtp',
'host' => $_ENV['MAIL_HOST'] ?? 'localhost',
'port' => (int) ($_ENV['MAIL_PORT'] ?? 587),
'username' => $_ENV['MAIL_USERNAME'] ?? '',
'password' => $_ENV['MAIL_PASSWORD'] ?? '',
'encryption' => $_ENV['MAIL_ENCRYPTION'] ?? 'tls',
'from_address' => $_ENV['MAIL_FROM_ADDRESS'] ?? 'noreply@example.com',
'from_name' => $_ENV['MAIL_FROM_NAME'] ?? 'E-commerce Store'
];
$this->templates = [
'verification' => $this->getVerificationTemplate(),
'password_reset' => $this->getPasswordResetTemplate(),
'welcome' => $this->getWelcomeTemplate(),
'order_confirmation' => $this->getOrderConfirmationTemplate()
];
}
/**
* Send verification email
*/
public function sendVerificationEmail(string $email, string $name, string $token): bool
{
$verificationUrl = $this->getAppUrl()."/api/auth/verify-email/{$token}";
$subject = 'Verify Your Email Address';
$body = $this->renderTemplate('verification', [
'name' => $name,
'verification_url' => $verificationUrl,
'app_name' => $this->config['from_name']
]);
return $this->sendEmail($email, $subject, $body);
}
/**
* Send password reset email
*/
public function sendPasswordResetEmail(string $email, string $name, string $token): bool
{
$resetUrl = $this->getAppUrl()."/reset-password?token={$token}";
$subject = 'Reset Your Password';
$body = $this->renderTemplate('password_reset', [
'name' => $name,
'reset_url' => $resetUrl,
'app_name' => $this->config['from_name']
]);
return $this->sendEmail($email, $subject, $body);
}
/**
* Send welcome email
*/
public function sendWelcomeEmail(string $email, string $name): bool
{
$subject = 'Welcome to '.$this->config['from_name'];
$body = $this->renderTemplate('welcome', [
'name' => $name,
'app_name' => $this->config['from_name'],
'app_url' => $this->getAppUrl()
]);
return $this->sendEmail($email, $subject, $body);
}
/**
* Send order confirmation email
*/
public function sendOrderConfirmationEmail(string $email, string $name, array $orderData): bool
{
$subject = 'Order Confirmation - '.$orderData['order_number'];
$body = $this->renderTemplate('order_confirmation', [
'name' => $name,
'order' => $orderData,
'app_name' => $this->config['from_name']
]);
return $this->sendEmail($email, $subject, $body);
}
/**
* Send email using configured driver
*/
private function sendEmail(string $to, string $subject, string $body): bool
{
try {
switch ($this->config['driver']) {
case 'smtp':
return $this->sendViaSMTP($to, $subject, $body);
case 'mail':
return $this->sendViaMail($to, $subject, $body);
case 'log':
return $this->sendViaLog($to, $subject, $body);
default:
throw new Exception('Unsupported mail driver: '.$this->config['driver']);
}
} catch (Exception $e) {
error_log('Email sending failed: '.$e->getMessage());
return false;
}
}
/**
* Send email via SMTP
*/
private function sendViaSMTP(string $to, string $subject, string $body): bool
{
// For production, you would implement proper SMTP sending
// For now, we'll log the email
return $this->sendViaLog($to, $subject, $body);
}
/**
* Send email via PHP mail() function
*/
private function sendViaMail(string $to, string $subject, string $body): bool
{
$headers = [
'From: '.$this->config['from_name'].' <'.$this->config['from_address'].'>',
'Reply-To: '.$this->config['from_address'],
'Content-Type: text/html; charset=UTF-8',
'MIME-Version: 1.0'
];
return mail($to, $subject, $body, implode("\r\n", $headers));
}
/**
* Log email instead of sending (for development)
*/
private function sendViaLog(string $to, string $subject, string $body): bool
{
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'to' => $to,
'subject' => $subject,
'body' => $body
];
$logFile = __DIR__.'/../../storage/logs/emails.log';
$logDir = dirname($logFile);
if (!is_dir($logDir)) {
@mkdir($logDir, 0755, true);
}
return file_put_contents($logFile, json_encode($logEntry)."\n", FILE_APPEND | LOCK_EX) !== false;
}
/**
* Render email template
*/
private function renderTemplate(string $template, array $variables): string
{
if (!isset($this->templates[$template])) {
throw new Exception("Email template '{$template}' not found");
}
$content = $this->templates[$template];
// Replace variables
foreach ($variables as $key => $value) {
if (is_array($value)) {
// Handle complex variables (like order data)
$content = $this->replaceComplexVariable($content, $key, $value);
} else {
$content = str_replace('{{'.$key.'}}', $value, $content);
}
}
return $content;
}
/**
* Replace complex variables in templates
*/
private function replaceComplexVariable(string $content, string $key, array $data): string
{
// Handle order data
if ($key === 'order') {
$content = str_replace('{{order.number}}', $data['order_number'] ?? '', $content);
$content = str_replace('{{order.total}}', number_format($data['total_amount'] ?? 0, 2), $content);
$content = str_replace('{{order.status}}', $data['status'] ?? '', $content);
$content = str_replace('{{order.date}}', date('F j, Y', strtotime($data['created_at'] ?? 'now')), $content);
// Handle order items
if (isset($data['items'])) {
$itemsHtml = '';
foreach ($data['items'] as $item) {
$itemsHtml .= '
';
$itemsHtml .= '| '.htmlspecialchars($item['product_name'] ?? '').' | ';
$itemsHtml .= ''.($item['quantity'] ?? 0).' | ';
$itemsHtml .= '฿'.number_format($item['price'] ?? 0, 2).' | ';
$itemsHtml .= '฿'.number_format($item['total'] ?? 0, 2).' | ';
$itemsHtml .= '
';
}
$content = str_replace('{{order.items}}', $itemsHtml, $content);
}
}
return $content;
}
/**
* Get application URL
*/
private function getAppUrl(): string
{
return $_ENV['APP_URL'] ?? 'http://localhost';
}
/**
* Get verification email template
*/
private function getVerificationTemplate(): string
{
return '
Verify Your Email
Verify Your Email Address
Hello {{name}},
Thank you for registering with {{app_name}}. Please click the button below to verify your email address:
Verify Email
If you cannot click the button, copy and paste this link into your browser:
{{verification_url}}
If you did not create an account, please ignore this email.
';
}
/**
* Get password reset email template
*/
private function getPasswordResetTemplate(): string
{
return '
Reset Your Password
Reset Your Password
Hello {{name}},
You have requested to reset your password. Click the button below to reset it:
Reset Password
If you cannot click the button, copy and paste this link into your browser:
{{reset_url}}
This link will expire in 1 hour for security reasons.
If you did not request a password reset, please ignore this email.
';
}
/**
* Get welcome email template
*/
private function getWelcomeTemplate(): string
{
return '
Welcome
Welcome {{name}}!
Thank you for joining {{app_name}}. We are excited to have you as part of our community.
You can now:
- Browse our products
- Add items to your wishlist
- Track your orders
- Manage your profile and addresses
Start Shopping
If you have any questions, feel free to contact our support team.
';
}
/**
* Get order confirmation email template
*/
private function getOrderConfirmationTemplate(): string
{
return '
Order Confirmation
Thank you for your order, {{name}}!
Your order has been received and is being processed.
Order Details
Order Number: {{order.number}}
Order Date: {{order.date}}
Status: {{order.status}}
Items Ordered
| Product |
Quantity |
Price |
Total |
{{order.items}}
Total: ฿{{order.total}}
We will send you another email when your order ships.
';
}
/**
* Send order status update email
*/
public function sendOrderStatusUpdate(string $email, string $name, array $orderData, string $newStatus): bool
{
$statusMessages = [
'confirmed' => 'Your order has been confirmed and is being prepared',
'processing' => 'Your order is being processed and will be shipped soon',
'shipped' => 'Your order has been shipped and is on its way',
'delivered' => 'Your order has been delivered successfully',
'cancelled' => 'Your order has been cancelled'
];
$subject = 'Order Status Update - '.$orderData['order_number'];
$message = $this->getOrderStatusTemplate([
'customer_name' => $name,
'order_number' => $orderData['order_number'],
'new_status' => ucfirst($newStatus),
'status_message' => $statusMessages[$newStatus] ?? 'Your order status has been updated',
'order_total' => number_format($orderData['total_amount'], 2),
'tracking_url' => $this->getAppUrl()."/orders/{$orderData['order_number']}/track",
'app_name' => $this->config['from_name']
]);
return $this->sendEmail($email, $subject, $message);
}
/**
* Send order cancellation email
*/
public function sendOrderCancellation(array $orderData): bool
{
$shippingAddress = is_string($orderData['shipping_address']) ?
json_decode($orderData['shipping_address'], true) :
$orderData['shipping_address'];
$customerName = $shippingAddress['first_name'].' '.$shippingAddress['last_name'];
$subject = 'Order Cancelled - '.$orderData['order_number'];
$message = $this->getCancellationTemplate([
'customer_name' => $customerName,
'order_number' => $orderData['order_number'],
'order_total' => number_format($orderData['total_amount'], 2),
'cancellation_reason' => $orderData['notes'] ?? 'Order cancelled by customer',
'refund_info' => 'If payment was made, refund will be processed within 3-5 business days',
'app_name' => $this->config['from_name']
]);
return $this->sendEmail($orderData['email'], $subject, $message);
}
/**
* Send low stock alert to admin
*/
public function sendLowStockAlert(array $productData): bool
{
$adminEmail = $_ENV['ADMIN_EMAIL'] ?? 'admin@example.com';
$subject = 'Low Stock Alert - '.$productData['name'];
$message = $this->getLowStockTemplate([
'product_name' => $productData['name'],
'product_sku' => $productData['sku'],
'current_stock' => $productData['current_stock'],
'min_stock_level' => $productData['min_stock_level'],
'product_url' => $this->getAppUrl()."/products/{$productData['id']}",
'app_name' => $this->config['from_name']
]);
return $this->sendEmail($adminEmail, $subject, $message);
}
/**
* Get order status update template
*/
private function getOrderStatusTemplate(array $data): string
{
$template = '
Order Status Update
Order Status Update
Dear {{customer_name}},
{{status_message}}
Order Number: {{order_number}}
Status: {{new_status}}
Order Total: ฿{{order_total}}
Track Your Order
Thank you for shopping with us!
';
foreach ($data as $key => $value) {
$template = str_replace('{{'.$key.'}}', $value, $template);
}
return $template;
}
/**
* Get cancellation email template
*/
private function getCancellationTemplate(array $data): string
{
$template = '
Order Cancelled
Order Cancellation
Dear {{customer_name}},
Your order has been cancelled as requested.
Order Number: {{order_number}}
Order Total: ฿{{order_total}}
Reason: {{cancellation_reason}}
{{refund_info}}
We apologize for any inconvenience and hope to serve you again in the future.
';
foreach ($data as $key => $value) {
$template = str_replace('{{'.$key.'}}', $value, $template);
}
return $template;
}
/**
* Get low stock alert template
*/
private function getLowStockTemplate(array $data): string
{
$template = '
Low Stock Alert
⚠️ Low Stock Alert
Action Required: The following product is running low on stock and needs to be restocked.
Product: {{product_name}}
SKU: {{product_sku}}
Current Stock: {{current_stock}} units
Minimum Level: {{min_stock_level}} units
Manage Product
Please restock this item as soon as possible to avoid stockouts.
';
foreach ($data as $key => $value) {
$template = str_replace('{{'.$key.'}}', $value, $template);
}
return $template;
}
}