JezK
Edit File: zoho-create-lead.php
<?php error_reporting(E_ALL); ini_set('display_errors', 1); if (!session_id() && !headers_sent()) { ini_set('session.cookie_httponly', 1); if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') { ini_set('session.cookie_secure', 1); } ini_set('session.use_only_cookies', 1); ini_set('session.cookie_samesite', 'Strict'); session_start(); } if (isset($_GET['action']) && $_GET['action'] === 'get_token') { $token = bin2hex(random_bytes(64)); $_SESSION['csrf_token'] = $token; $_SESSION['csrf_token_time'] = time(); $_SESSION['csrf_token_hash'] = hash('sha256', $token . ($_SERVER['HTTP_USER_AGENT'] ?? '') . ($_SERVER['REMOTE_ADDR'] ?? '')); header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: DENY'); echo json_encode(['csrf_token' => $token]); exit; } require_once('/home/authentica.com/public_html/wp-content/plugins/zoho-crm-api/zoho-auth.php'); if (!defined('ABSPATH')) { exit; } $rate_limit_key = 'rate_limit_' . md5($_SERVER['REMOTE_ADDR'] ?? 'unknown'); $rate_limit_time = 'rate_limit_time_' . md5($_SERVER['REMOTE_ADDR'] ?? 'unknown'); $max_requests = 10; $time_period = 60 * 15; if (isset($_SESSION[$rate_limit_key])) { if (time() - $_SESSION[$rate_limit_time] > $time_period) { $_SESSION[$rate_limit_key] = 1; $_SESSION[$rate_limit_time] = time(); } else { $_SESSION[$rate_limit_key]++; if ($_SESSION[$rate_limit_key] > $max_requests) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); http_response_code(429); echo json_encode(['error' => 'Too many requests. Please try again later.']); exit; } } } else { $_SESSION[$rate_limit_key] = 1; $_SESSION[$rate_limit_time] = time(); } $request_body = file_get_contents("php://input"); $decoded_input = json_decode($request_body, true); if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!isset($decoded_input['csrf_token'])) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); http_response_code(403); echo json_encode(['error' => 'Missing CSRF token', 'http_code' => 403]); exit; } if (!isset($_SESSION['csrf_token']) || !isset($_SESSION['csrf_token_hash']) || $decoded_input['csrf_token'] !== $_SESSION['csrf_token'] || $_SESSION['csrf_token_hash'] !== hash('sha256', $decoded_input['csrf_token'] . ($_SERVER['HTTP_USER_AGENT'] ?? '') . ($_SERVER['REMOTE_ADDR'] ?? '')) ) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); http_response_code(403); echo json_encode(['error' => 'Invalid CSRF token', 'http_code' => 403]); exit; } if (!isset($_SESSION['csrf_token_time']) || (time() - $_SESSION['csrf_token_time'] > 1800)) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); http_response_code(403); echo json_encode(['error' => 'Expired CSRF token', 'http_code' => 403]); exit; } } $auth_response = get_zoho_access_token(); if (!isset($auth_response['response']['access_token'])) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); echo json_encode(['error' => 'Failed to fetch access token', 'details' => $auth_response]); exit; } $access_token = $auth_response['response']['access_token']; function sanitizeInput($input) { if (is_array($input)) { $out = []; foreach ($input as $k => $v) { $key = preg_replace('/[^a-zA-Z0-9_]/', '', (string)$k); if (is_array($v)) { $out[$key] = sanitizeInput($v); } else { $val = trim(strip_tags((string)$v)); $out[$key] = $val; } } return $out; } return trim(strip_tags((string)$input)); } function apd_add_note_to_lead($lead_id, $title, $content, $access_token) { if (!$lead_id || !$content) return null; $payload = [ 'data' => [[ 'Note_Title' => (string)$title, 'Note_Content' => (string)$content, ]], ]; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => "https://www.zohoapis.com/crm/v2/Leads/{$lead_id}/Notes", CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer $access_token", "Content-Type: application/json", ], CURLOPT_SSL_VERIFYPEER => true, ]); $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return ['http_code' => $code, 'response' => json_decode($resp, true)]; } function apd_create_lead($input, $access_token) { if (empty($input['First_Name']) || empty($input['Last_Name']) || empty($input['Email'])) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); http_response_code(400); echo json_encode(['error' => 'Missing required fields (First_Name, Last_Name, Email)']); exit; } if (!filter_var($input['Email'], FILTER_VALIDATE_EMAIL)) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); http_response_code(400); echo json_encode(['error' => 'Invalid email format', 'field' => 'Email']); exit; } $form_type = $input['form_type']; $source_label = $form_type === 'apd_webinar' ? 'AI Program Designer - Webinar' : 'AI Program Designer - Waitlist'; // Minimal payload — no Layout override, no picklists. // Zoho default layout doesn't require Lead_Type / Contact_Type / Lead_Source / Program_Type. $payloadRow = [ "Last_Name" => $input['Last_Name'], "First_Name" => $input['First_Name'], "Email" => $input['Email'], "Description" => $source_label . " (submitted " . date('c') . ")", ]; $optional_field_keys = [ 'Institution_or_organization', 'Your_role', 'Your_most_immediate_use_case', 'How_many_faculty_led_programs_does_your_institutio', 'Which_would_you_like_check_all_that_apply', 'Will_you_be_at_NAFSA_20261', ]; foreach ($optional_field_keys as $k) { if (!empty($input[$k])) { $payloadRow[$k] = $input[$k]; } } $lead_data = [ "data" => [$payloadRow], "trigger" => ["workflow"], ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://www.zohoapis.com/crm/v2/Leads"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($lead_data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer $access_token", "Content-Type: application/json", ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error_msg = curl_error($ch); curl_close($ch); $decoded_response = json_decode($response, true); $lead_id = null; if (in_array($http_code, [200, 201]) && isset($decoded_response['data'][0]['details']['id'])) { $lead_id = $decoded_response['data'][0]['details']['id']; } $notes_info = []; if ($lead_id) { if ($form_type === 'apd_waitlist') { if (!empty($input['pain_point'])) { $notes_info['pain_point'] = apd_add_note_to_lead($lead_id, "What's the single biggest pain point in your current proposal workflow?", $input['pain_point'], $access_token); } if (!empty($input['notes'])) { $notes_info['notes'] = apd_add_note_to_lead($lead_id, "Anything else we should know?", $input['notes'], $access_token); } } else if ($form_type === 'apd_webinar') { if (!empty($input['question'])) { $notes_info['question'] = apd_add_note_to_lead($lead_id, "One question you'd like answered", $input['question'], $access_token); } } } header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); echo json_encode([ 'http_code' => $http_code, 'error' => $error_msg, 'response' => $decoded_response, 'lead_id' => $lead_id, 'form_type' => $form_type, 'notes' => $notes_info, ]); exit; } $decoded_input = sanitizeInput($decoded_input); if (!empty($decoded_input['form_type']) && in_array($decoded_input['form_type'], ['apd_waitlist', 'apd_webinar'], true)) { apd_create_lead($decoded_input, $access_token); exit; } // Original (non-APD) flow — unchanged if (empty($decoded_input['First_Name']) || empty($decoded_input['Last_Name']) || empty($decoded_input['Email'])) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); echo json_encode(['error' => 'Missing required fields (First_Name, Last_Name, Email)']); exit; } if (!filter_var($decoded_input['Email'], FILTER_VALIDATE_EMAIL)) { header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); echo json_encode(['error' => 'Invalid email format', 'field' => 'Email']); exit; } unset($decoded_input['csrf_token']); $payloadRow = [ "Last_Name" => $decoded_input['Last_Name'], "First_Name" => $decoded_input['First_Name'], "Email" => $decoded_input['Email'], "Layout" => [ "id" => "2683211000044052001" ], "Program_Type" => "standard" ]; $optionalKeys = array_diff(array_keys($decoded_input), ['First_Name','Last_Name','Email']); foreach ($optionalKeys as $k) { $payloadRow[$k] = $decoded_input[$k]; } $lead_data = [ "data" => [ $payloadRow ], "trigger" => ["workflow"] ]; $zoho_api_url = "https://www.zohoapis.com/crm/v2/Leads"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $zoho_api_url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($lead_data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ "Authorization: Bearer $access_token", "Content-Type: application/json" ]); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error_msg = curl_error($ch); curl_close($ch); $decoded_response = json_decode($response, true); $lead_id = null; if (in_array($http_code, [200, 201]) && isset($decoded_response['data'][0]['details']['id'])) { $lead_id = $decoded_response['data'][0]['details']['id']; } header('Content-Type: application/json'); header('X-Content-Type-Options: nosniff'); echo json_encode([ 'http_code' => $http_code, 'error' => $error_msg, 'response' => $decoded_response, 'lead_id' => $lead_id ]); exit;