apiKey = getSetting('cybermail_api_key', defined('CYBERMAIL_API_KEY') ? CYBERMAIL_API_KEY : ''); $this->fromEmail = defined('SENDER_EMAIL') ? SENDER_EMAIL : 'noreply@tomsjavajive.com'; $this->fromName = defined('SENDER_NAME') ? SENDER_NAME : "Tom's Java Jive"; } private function logEmail(string $to, string $subject, string $html, array $result, array $options = []): void { try { $preview = strip_tags($html); $preview = preg_replace('/\s+/', ' ', trim($preview)); $preview = substr($preview, 0, 500); // Find customer_id by email $customer = db()->fetch( "SELECT customer_id FROM customers WHERE email = :email LIMIT 1", ['email' => $to] ); db()->insert('email_log', [ 'customer_id' => $customer['customer_id'] ?? null, 'recipient_email'=> $to, 'subject' => $subject, 'preview' => $preview, 'message_id' => $result['message_id'] ?? null, 'status' => $result['success'] ? 'sent' : 'failed', 'error_message' => $result['success'] ? null : ($result['error'] ?? null), 'tags' => isset($options['tags']) ? implode(',', $options['tags']) : null, ]); } catch (\Throwable $e) { // Never let logging break email sending } } public function checkDeliveryStatus(string $messageId): array { $ch = curl_init("https://platform.cyberpersons.com/email/v1/messages/" . urlencode($messageId)); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->apiKey], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($ch); curl_close($ch); $body = json_decode($response, true); return $body['data'] ?? []; } public function send(string $to, string $subject, string $html, ?string $text = null, array $options = []): array { $payload = array_merge([ 'from' => $this->fromEmail, 'to' => $to, 'subject' => $subject, 'html' => $html, ], $options); if ($text) $payload['text'] = $text; $ch = curl_init('https://platform.cyberpersons.com/email/v1/send'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json'], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => false, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $body = json_decode($response, true); if ($httpCode === 202) { $result = ['success' => true, 'message_id' => $body['data']['message_id'] ?? null]; $this->logEmail($to, $subject, $html, $result, $options); return $result; } $errorMsg = $body['error']['message'] ?? 'Unknown error'; $result = ['success' => false, 'error' => $errorMsg, 'code' => $httpCode]; $this->logEmail($to, $subject, $html, $result, $options); return $result; } public function sendOrderConfirmation(array $order): array { $items = json_decode($order['items'], true); $itemsHtml = ''; foreach ($items as $item) { $itemsHtml .= sprintf( '%s %d %s', htmlspecialchars($item['name']), $item['quantity'], formatCurrency($item['total']) ); } $html = $this->getTemplate('order_confirmation', [ 'order_number' => $order['order_number'], 'customer_name' => $order['customer_name'] ?? 'Valued Customer', 'items_html' => $itemsHtml, 'subtotal' => formatCurrency($order['subtotal']), 'tax' => formatCurrency($order['tax']), 'discount' => $order['discount'] > 0 ? '-' . formatCurrency($order['discount']) : '$0.00', 'total' => formatCurrency($order['total']), 'payment_method' => ucfirst($order['payment_method'] ?? 'N/A'), 'order_date' => date('F j, Y', strtotime($order['created_at'])) ]); return $this->send( $order['customer_email'], "Order Confirmation - #{$order['order_number']}", $html, null, ['tags' => ['order', 'confirmation'], 'metadata' => ['order_id' => $order['order_number']]] ); } public function sendShippingNotification(array $order): array { $html = $this->getTemplate('shipping_notification', [ 'order_number' => $order['order_number'], 'customer_name' => $order['customer_name'] ?? 'Valued Customer', 'tracking_number' => $order['tracking_number'], 'tracking_url' => $order['tracking_url'] ?? '#', 'carrier' => $order['shipping_carrier'] ?? 'Our shipping partner' ]); return $this->send( $order['customer_email'], "Your Order Has Shipped - #{$order['order_number']}", $html, null, ['tags' => ['order', 'shipping'], 'metadata' => ['order_id' => $order['order_number']]] ); } public function sendPasswordReset(string $email, string $resetToken, string $name = ''): array { $resetUrl = SITE_URL . '/reset-password.php?token=' . $resetToken; $html = $this->getTemplate('password_reset', [ 'customer_name' => $name ?: 'there', 'reset_url' => $resetUrl, 'expires' => '1 hour' ]); return $this->send($email, "Reset Your Password - Tom's Java Jive", $html, null, ['tags' => ['password-reset']]); } public function sendWelcome(string $email, string $name = ''): array { $html = $this->getTemplate('welcome', [ 'customer_name' => $name ?: 'Coffee Lover', 'shop_url' => SITE_URL . '/shop.php' ]); return $this->send($email, "Welcome to Tom's Java Jive!", $html, null, ['tags' => ['welcome']]); } public function sendAbandonedCartReminder(array $cart): array { $items = json_decode($cart['items'], true); $itemsHtml = ''; foreach ($items as $item) { $itemsHtml .= sprintf( '
%s - %s
', htmlspecialchars($item['name']), formatCurrency($item['price']) ); } $html = $this->getTemplate('abandoned_cart', [ 'items_html' => $itemsHtml, 'total' => formatCurrency($cart['subtotal']), 'cart_url' => SITE_URL . '/cart.php' ]); return $this->send($cart['customer_email'], "You left something behind!", $html, null, ['tags' => ['abandoned-cart']]); } private function getTemplate(string $name, array $vars = []): string { $year = date('Y'); $templates = [ 'order_confirmation' => '

Order Confirmed!

Hi {{customer_name}},

Thank you for your order! We\'ve received it and will begin processing right away.

Order #{{order_number}}

{{order_date}}

{{items_html}}
Item Qty Price

Subtotal: {{subtotal}}

Tax: {{tax}}

Discount: {{discount}}

Total: {{total}}

Payment Method: {{payment_method}}

© ' . $year . ' Tom\'s Java Jive. All rights reserved.

', 'shipping_notification' => '

Your Order Has Shipped!

Hi {{customer_name}},

Great news! Your order #{{order_number}} is on its way.

Tracking Number

{{tracking_number}}

Carrier: {{carrier}}

Track Your Package

© ' . $year . ' Tom\'s Java Jive. All rights reserved.

', 'password_reset' => '

Reset Your Password

Hi {{customer_name}},

We received a request to reset your password. Click the button below to create a new one:

Reset Password

This link expires in {{expires}}. If you didn\'t request this, ignore this email.

© ' . $year . ' Tom\'s Java Jive. All rights reserved.

', 'welcome' => '

Welcome to the Family!

Hi {{customer_name}},

Welcome to Tom\'s Java Jive! We\'re thrilled to have you join our community of coffee lovers.

Here\'s what you can look forward to:

Start Shopping

Cheers,
The Tom\'s Java Jive Team

© ' . $year . ' Tom\'s Java Jive. All rights reserved.

', 'abandoned_cart' => '

Forget Something?

Hey there!

We noticed you left some amazing items in your cart. Don\'t let them get away!

{{items_html}}
Total: {{total}}
Complete Your Order

© ' . $year . ' Tom\'s Java Jive. All rights reserved.

' ]; $template = $templates[$name] ?? '

Email template not found.

'; foreach ($vars as $key => $value) { $template = str_replace('{{' . $key . '}}', $value, $template); } return $template; } } function emailService(): Email { static $instance = null; if ($instance === null) { $instance = new Email(); } return $instance; }