39038-vm/includes/NotificationService.php
2026-03-13 14:05:01 +00:00

189 lines
6.7 KiB
PHP

<?php
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../mail/MailService.php';
require_once __DIR__ . '/app.php';
class NotificationService
{
/**
* Send a notification for a specific event.
*
* @param string $eventName The event name (e.g., 'shipment_created')
* @param array $user The recipient user array (must contain 'id', 'email', 'full_name', 'role')
* @param array $data Data to replace in placeholders (e.g., ['shipment_id' => 123])
* @param string|null $lang 'en' or 'ar'. If null, sends both combined.
*/
public static function send(string $eventName, array $user, array $data = [], ?string $lang = null)
{
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM notification_templates WHERE event_name = ?");
$stmt->execute([$eventName]);
$template = $stmt->fetch();
if (!$template) {
error_log("Notification template not found: $eventName");
return;
}
// Prepare data for replacement
$placeholders = [];
$values = [];
$data['user_name'] = $user['full_name'] ?? 'User';
foreach ($data as $key => $val) {
$placeholders[] = '{' . $key . '}';
$values[] = $val;
}
// Determine Subject and Body based on Lang
$subject = '';
$body = '';
$whatsapp = '';
if ($lang === 'en') {
$subject = $template['email_subject_en'];
$body = $template['email_body_en'];
$whatsapp = $template['whatsapp_body_en'];
} elseif ($lang === 'ar') {
$subject = $template['email_subject_ar'];
$body = $template['email_body_ar'];
$whatsapp = $template['whatsapp_body_ar'];
} else {
// Combined
$subject = $template['email_subject_en'] . ' / ' . $template['email_subject_ar'];
$body = $template['email_body_en'] . "\n\n---\n\n" . $template['email_body_ar'];
$whatsapp = $template['whatsapp_body_en'] . "\n\n" . $template['whatsapp_body_ar'];
}
// Replace
$subject = str_replace($placeholders, $values, $subject);
$body = str_replace($placeholders, $values, $body);
$whatsapp = str_replace($placeholders, $values, $whatsapp);
// Send Email
if (!empty($user['email'])) {
// Convert newlines to BR for HTML
$htmlBody = nl2br(htmlspecialchars($body));
MailService::sendMail($user['email'], $subject, $htmlBody, $body);
}
// Send WhatsApp if enabled
if (get_setting('whatsapp_enabled') === '1') {
$phone = self::getPhoneNumber($user);
if ($phone) {
// Return value ignored in async/background context, but logged inside
self::sendWhatsApp($phone, $whatsapp);
} else {
error_log("WHATSAPP Notification skipped for {$user['email']}: Phone number not found.");
}
}
}
/**
* Retrieve phone number for a user, fetching from profile if necessary.
*/
private static function getPhoneNumber(array $user): ?string
{
if (!empty($user['phone'])) {
return $user['phone'];
}
if (empty($user['id']) || empty($user['role'])) {
return null;
}
$pdo = db();
if ($user['role'] === 'shipper') {
$stmt = $pdo->prepare("SELECT phone FROM shipper_profiles WHERE user_id = ?");
$stmt->execute([$user['id']]);
return $stmt->fetchColumn() ?: null;
} elseif ($user['role'] === 'truck_owner') {
$stmt = $pdo->prepare("SELECT phone FROM truck_owner_profiles WHERE user_id = ?");
$stmt->execute([$user['id']]);
return $stmt->fetchColumn() ?: null;
}
return null;
}
/**
* Send a WhatsApp message via Wablas API.
* @return array ['success' => bool, 'message' => string, 'response' => mixed]
*/
public static function sendWhatsApp(string $phone, string $message): array
{
// 1. Format Phone Number
// Remove non-numeric characters
$phone = preg_replace('/[^0-9]/', '', $phone);
// If 8 digits, prepend 968 (Oman country code)
if (strlen($phone) === 8) {
$phone = '968' . $phone;
}
// 2. Get Settings
$domain = get_setting('wablas_domain');
$token = get_setting('wablas_api_token');
if (!$domain || !$token) {
error_log("WHATSAPP Error: Wablas domain or token not configured.");
return ['success' => false, 'message' => 'Domain or Token missing'];
}
// Ensure domain has no trailing slash and correct scheme
$domain = rtrim($domain, '/');
// If domain doesn't start with http/https, prepend https://
if (!preg_match('/^https?:\/\//', $domain)) {
$domain = 'https://' . $domain;
}
// 3. Send API Request
// Endpoint: /api/send-message (StandardWablas)
$url = $domain . "/api/send-message";
$payload = json_encode([
'phone' => $phone,
'message' => $message,
]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: $token",
"Content-Type: application/json"
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErr = curl_error($ch);
curl_close($ch);
if ($curlErr) {
error_log("WHATSAPP cURL Error: $curlErr");
return ['success' => false, 'message' => "Connection error: $curlErr"];
}
$decoded = json_decode($response, true);
$statusMsg = $decoded['message'] ?? $response;
// Log result
error_log("WHATSAPP Sent to $phone. Status: $httpCode. Response: " . substr($response, 0, 100));
// Wablas usually returns {'status': true, ...} or {'status': 'pending'}
// We consider 200 OK and no explicit 'status': false as success for now
$isSuccess = ($httpCode >= 200 && $httpCode < 300);
if (isset($decoded['status']) && $decoded['status'] === false) {
$isSuccess = false;
}
return [
'success' => $isSuccess,
'message' => "API ($httpCode): " . (is_string($statusMsg) ? $statusMsg : json_encode($statusMsg)),
'response' => $decoded
];
}
}