diff --git a/application/controllers/MktgLeadController.php b/application/controllers/MktgLeadController.php new file mode 100644 index 0000000..e142e72 --- /dev/null +++ b/application/controllers/MktgLeadController.php @@ -0,0 +1,195 @@ +email = new EmailService(); + } + + /** + * POST /api/mktg-lead + */ + public function submit(): void + { + // Rate limit: 3 richieste / 10 min per IP + $ip = $this->getClientIP(); + $cacheFile = sys_get_temp_dir() . '/nis2_mktglead_' . md5($ip); + $now = time(); + + $cache = file_exists($cacheFile) ? json_decode(file_get_contents($cacheFile), true) : ['requests' => []]; + $cache['requests'] = array_filter($cache['requests'] ?? [], fn($t) => $t > ($now - 600)); + if (count($cache['requests']) >= 3) { + $this->jsonError('Troppe richieste. Riprova tra qualche minuto.', 429); + return; + } + $cache['requests'][] = $now; + file_put_contents($cacheFile, json_encode($cache)); + + // Input + $body = $this->getRequestBody(); + + // Supporta sia campi IT (form NIS2) che campi EN (standard mktg) + $name = trim($body['name'] ?? $body['nome'] ?? ''); + $email = trim($body['email'] ?? ''); + $phone = trim($body['phone'] ?? $body['telefono'] ?? ''); + $company = trim($body['company'] ?? $body['azienda'] ?? ''); + $role = trim($body['role'] ?? $body['tipo'] ?? ''); + $size = trim($body['size'] ?? $body['n_dipendenti'] ?? ''); + $interest = trim($body['product_interest'] ?? $body['interesse'] ?? ''); + $notes = trim($body['notes'] ?? $body['messaggio'] ?? ''); + $source = trim($body['source'] ?? self::SOURCE); + + // Validazione + if (!$name || !$email || !$company) { + $this->jsonError('Compila i campi obbligatori: nome, email, azienda.', 422); + return; + } + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $this->jsonError('Indirizzo email non valido.', 422); + return; + } + + // Sanitize + $name = htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); + $email = htmlspecialchars($email, ENT_QUOTES, 'UTF-8'); + $phone = htmlspecialchars($phone, ENT_QUOTES, 'UTF-8'); + $company = htmlspecialchars($company, ENT_QUOTES, 'UTF-8'); + $role = htmlspecialchars($role, ENT_QUOTES, 'UTF-8'); + $size = htmlspecialchars($size, ENT_QUOTES, 'UTF-8'); + $interest = htmlspecialchars($interest, ENT_QUOTES, 'UTF-8'); + $source = htmlspecialchars($source, ENT_QUOTES, 'UTF-8'); + + // Componi notes con campi aggiuntivi + $noteParts = []; + if ($role) $noteParts[] = "Ruolo: {$role}"; + if ($size) $noteParts[] = "Dimensioni: {$size}"; + if ($interest) $noteParts[] = "Interesse: {$interest}"; + if ($notes) $noteParts[] = $notes; + $fullNotes = implode(' | ', $noteParts); + + // Payload standard mktg + $payload = [ + 'name' => $name, + 'email' => $email, + 'phone' => $phone, + 'company' => $company, + 'product_interest' => $interest ?: self::PRODUCT, + 'source' => $source, + 'notes' => $fullNotes, + ]; + + // 1. Prova webhook mktg.agile.software + $webhookOk = $this->sendWebhook($payload); + + // 2. Fallback email se webhook fallisce + if (!$webhookOk) { + $this->sendFallbackEmail($payload, $ip); + } + + $this->jsonSuccess(null, 'Richiesta inviata! Ti contatteremo entro 24 ore con il tuo codice di accesso.'); + } + + private function sendWebhook(array $payload): bool + { + $ch = curl_init(self::WEBHOOK_URL); + curl_setopt_array($ch, [ + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode($payload), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'X-Webhook-Key: ' . self::WEBHOOK_KEY, + ], + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 8, + CURLOPT_SSL_VERIFYPEER => true, + ]); + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + curl_close($ch); + + if ($error || $httpCode < 200 || $httpCode >= 300) { + error_log("MktgLead webhook failed [{$httpCode}]: {$error}"); + return false; + } + return true; + } + + private function sendFallbackEmail(array $payload, string $ip): void + { + $date = date('d/m/Y H:i'); + $rows = ''; + $labels = [ + 'name' => 'Nome', + 'email' => 'Email', + 'phone' => 'Telefono', + 'company' => 'Azienda', + 'product_interest' => 'Interesse', + 'source' => 'Source', + 'notes' => 'Note', + ]; + foreach ($labels as $key => $label) { + $val = $payload[$key] ?? ''; + if (!$val) continue; + $rows .= "
Ricevuto il {$date} ยท IP: {$ip}
+
+ โ ๏ธ Webhook mktg.agile.software non raggiungibile โ lead salvato via email.
+ Genera il codice invito: licenseExt.html
+