company_id; $session = \App\Core\Database::selectOne( "SELECT * FROM meta_cloud_sessions WHERE company_id = ? LIMIT 1", [$companyId] ); if (!$session) { $response->json([ 'status' => 'success', 'data' => null ]); return; } // Hide token from frontend unset($session['access_token']); unset($session['verify_token']); $response->json([ 'status' => 'success', 'data' => $session ]); } /** * Connect or update a Cloud API session */ public function connect(Request $request, Response $response) { $companyId = $request->company_id; $body = $request->getBody(); if (empty($body['phone_number_id']) || empty($body['waba_id']) || empty($body['access_token'])) { $response->status(400)->json(['error' => 'Missing required fields']); return; } $session = \App\Core\Database::selectOne( "SELECT id FROM meta_cloud_sessions WHERE company_id = ? LIMIT 1", [$companyId] ); $verifyToken = 'nabeh_cloud_' . bin2hex(random_bytes(8)); if ($session) { \App\Core\Database::execute( "UPDATE meta_cloud_sessions SET phone_number_id = ?, waba_id = ?, access_token = ?, status = 'active' WHERE id = ?", [$body['phone_number_id'], $body['waba_id'], $body['access_token'], $session['id']] ); } else { \App\Core\Database::execute( "INSERT INTO meta_cloud_sessions (company_id, phone_number_id, waba_id, access_token, verify_token, status) VALUES (?, ?, ?, ?, ?, 'active')", [$companyId, $body['phone_number_id'], $body['waba_id'], $body['access_token'], $verifyToken] ); } $updated = \App\Core\Database::selectOne( "SELECT * FROM meta_cloud_sessions WHERE company_id = ? LIMIT 1", [$companyId] ); unset($updated['access_token']); $response->json([ 'status' => 'success', 'message' => 'WhatsApp Cloud API connected successfully', 'data' => $updated ]); } /** * Disconnect Cloud API */ public function disconnect(Request $request, Response $response) { $companyId = $request->company_id; \App\Core\Database::execute( "DELETE FROM meta_cloud_sessions WHERE company_id = ?", [$companyId] ); $response->json([ 'status' => 'success', 'message' => 'Disconnected successfully' ]); } /** * Fetch templates from Meta and sync them locally */ public function syncTemplates(Request $request, Response $response) { $companyId = $request->company_id; $session = \App\Core\Database::selectOne( "SELECT * FROM meta_cloud_sessions WHERE company_id = ? LIMIT 1", [$companyId] ); if (!$session || $session['status'] !== 'active') { $response->status(400)->json(['error' => 'Cloud API not connected']); return; } try { // Call the Node.js Cloud API Service via Gateway $gatewayUrl = rtrim(getenv('WHATSAPP_GATEWAY_URL') ?: 'http://localhost:3722', '/'); $syncUrl = $gatewayUrl . '/api/cloud/templates/sync'; $payload = json_encode([ 'waba_id' => $session['waba_id'], 'access_token' => $session['access_token'] ]); $ch = curl_init($syncUrl); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'X-Webhook-Secret: ' . getenv('WEBHOOK_SECRET') ]); curl_setopt($ch, CURLOPT_TIMEOUT, 15); $res = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $resData = json_decode($res, true); $templates = $resData['data'] ?? []; // Clear old templates and insert new ones \App\Core\Database::execute("DELETE FROM message_templates WHERE company_id = ?", [$companyId]); foreach ($templates as $tpl) { \App\Core\Database::execute( "INSERT INTO message_templates (company_id, meta_template_id, name, language, category, status, body_text) VALUES (?, ?, ?, ?, ?, ?, ?)", [$companyId, $tpl['id'], $tpl['name'], $tpl['language'], strtolower($tpl['category']), strtolower($tpl['status']), 'Synced from Meta'] ); } $response->json([ 'status' => 'success', 'message' => 'Templates synced successfully', 'count' => count($templates) ]); } else { $response->status($httpCode)->json(['error' => 'Failed to sync templates from Meta']); } } catch (\Exception $e) { $response->status(500)->json(['error' => $e->getMessage()]); } } /** * Webhook to receive incoming messages from Meta */ public function webhook(Request $request, Response $response) { // Meta GET verification request if ($request->getMethod() === 'GET') { $mode = $request->get('hub_mode'); $token = $request->get('hub_verify_token'); $challenge = $request->get('hub_challenge'); if ($mode === 'subscribe' && $token) { $session = \App\Core\Database::selectOne( "SELECT * FROM meta_cloud_sessions WHERE verify_token = ? LIMIT 1", [$token] ); if ($session) { echo $challenge; exit; } } $response->status(403)->send('Forbidden'); return; } // Handle POST incoming messages $body = $request->getBody(); if (isset($body['entry'][0]['changes'][0]['value']['messages'])) { $metaMessages = $body['entry'][0]['changes'][0]['value']['messages']; $metadata = $body['entry'][0]['changes'][0]['value']['metadata']; $phoneNumberId = $metadata['phone_number_id']; $session = \App\Core\Database::selectOne( "SELECT * FROM meta_cloud_sessions WHERE phone_number_id = ? AND status = 'active' LIMIT 1", [$phoneNumberId] ); if ($session) { foreach ($metaMessages as $msg) { // Log incoming message $msgText = $msg['text']['body'] ?? '[Media/Other]'; \App\Models\MessageLog::logMessage([ 'company_id' => $session['company_id'], 'contact_phone' => $msg['from'], 'direction' => 'inbound', 'message_type' => $msg['type'], 'message_body' => $msgText, 'whatsapp_message_id' => $msg['id'], 'status' => 'read' ]); // Note: Here you can integrate Gemini AI for auto-replies over Cloud API // Similar to triggerAutoReply() in WhatsAppController } } } $response->status(200)->send('EVENT_RECEIVED'); } }