prepare("UPDATE outbound_queue SET status = 'sent', wam_id = ?, updated_at = NOW() WHERE id = ?"); $stmt->execute([$result['wam_id'], $item['id']]); $processed++; } else { $attempts = (int)$item['attempts'] + 1; $max = (int)$item['max_attempts']; if ($attempts >= $max) { $stmt = db()->prepare("UPDATE outbound_queue SET status = 'failed', attempts = ?, last_error = ?, updated_at = NOW() WHERE id = ?"); $stmt->execute([$attempts, $result['error'], $item['id']]); } else { $stmt = db()->prepare("UPDATE outbound_queue SET status = 'queued', attempts = ?, last_error = ?, updated_at = NOW() WHERE id = ?"); $stmt->execute([$attempts, $result['error'], $item['id']]); } $errors[] = "msg#{$item['id']}: {$result['error']}"; } } catch (\Exception $e) { $errors[] = "msg#{$item['id']}: " . $e->getMessage(); } } return ['processed' => $processed, 'errors' => $errors]; } private static function dequeue(): array { $db = db(); $db->beginTransaction(); try { $stmt = $db->prepare(" SELECT q.*, c.phone_number_id FROM outbound_queue q JOIN companies c ON c.id = q.company_id WHERE q.status = 'queued' AND c.is_active = 1 ORDER BY q.id ASC LIMIT ? FOR UPDATE SKIP LOCKED "); $stmt->execute([self::BATCH_SIZE]); $items = $stmt->fetchAll(); if (!empty($items)) { $ids = array_column($items, 'id'); $placeholders = implode(',', array_fill(0, count($ids), '?')); $db->prepare("UPDATE outbound_queue SET status = 'sending', updated_at = NOW() WHERE id IN ({$placeholders})")->execute($ids); } $db->commit(); return $items; } catch (\Exception $e) { $db->rollBack(); throw $e; } } private static function process(array $item): array { $payload = json_decode($item['payload'], true); if (!is_array($payload)) { return ['success' => false, 'error' => 'Payload inválido']; } $phoneNumberId = $item['phone_number_id'] ?: env('WHATSAPP_DEFAULT_PHONE_NUMBER_ID', ''); $type = $item['message_type']; $to = $item['to_number']; return match ($type) { 'text' => WhatsAppSender::sendText($to, $payload['text'] ?? '', $phoneNumberId), 'image' => WhatsAppSender::sendImage($to, $payload['media_id'] ?? $payload['url'] ?? '', $phoneNumberId, $payload['caption'] ?? null), 'template'=> WhatsAppSender::sendTemplate($to, $payload['template_name'] ?? '', $phoneNumberId, $payload['components'] ?? []), 'interactive' => WhatsAppSender::sendInteractive($to, $payload['interactive'] ?? [], $phoneNumberId), default => ['success' => false, 'error' => "Tipo no soportado: {$type}"], }; } }