nextHandler = $nextHandler; } public function handle($data, $crmData) { if ($this->nextHandler) { return $this->nextHandler->handle($data, $crmData); } return ['success' => true, 'message' => 'Форма обработана']; } public function formatData($data) { if (is_string($data)) { parse_str($data, $parsedData); // Преобразуем строку в массив $data = $parsedData; } $formattedMessage = "Данные формы:\n\n"; // Проходим по всем полям данных и добавляем их в сообщение foreach ($data as $key => $value) { $formattedMessage .= sprintf("%s: %s\n", $key, $value); } return $formattedMessage; } } define('WEBHOOK_URL', 'https://b24-f0tsii.bitrix24.ru/rest/10/z5k521mjfkmk7pqj/'); $utm_data = get_utm_data(); class b24Handler extends FormHandler { public function handle($data, $crmData) { $this->b24_add_lead( $crmData['phone'], $crmData['name'], $crmData['email'], $crmData['msg'], $crmData['url'], $crmData['stage'], $crmData['fName'], $crmData['order_total'], $crmData['user_id'], $crmData['method'], $crmData['form_title'], $crmData['is_subscribe'], $crmData['order_id'], ); // Вызываем следующий обработчик в цепочке return parent::handle($data, $crmData); } function b24_get_or_create_contact($phone, $name, $email, $userID = null, $isSubscribe) { $contactId = null; $error = null; $foundContacts = []; if (empty($email) && empty($phone) && empty($userID)) { return ['contact_id' => null, 'error' => 'Не указаны контактные данные']; } // Получаем telegram username если указан userID $tg_username = ''; if (!empty($userID)) { $tg_username = get_user_meta($userID, 'tg_username', true); } // 1. Поиск по всем возможным комбинациям (включая userID) $searchFilters = []; if (!empty($userID)) { $searchFilters[] = ['UF_CRM_1751610129971' => $userID]; } if (!empty($phone)) $searchFilters[] = ['PHONE' => $phone]; if (!empty($email)) $searchFilters[] = ['EMAIL' => $email]; foreach ($searchFilters as $filter) { $response = $this->b24_request('crm.contact.list', [ 'filter' => $filter, 'select' => ['ID', 'NAME', 'PHONE', 'EMAIL', 'UF_CRM_1751610129971', 'IM'] ]); if (!empty($response['result'])) { $foundContacts = array_merge($foundContacts, $response['result']); } } // 2. Обработка найденных контактов if (!empty($foundContacts)) { $bestMatch = null; $maxMatches = 0; foreach ($foundContacts as $contact) { $matches = 0; if (!empty($userID) && isset($contact['UF_CRM_1751610129971']) && $contact['UF_CRM_1751610129971'] == $userID) { $matches += 3; } if (!empty($phone) && $this->valueExists($contact['PHONE'] ?? [], $phone)) $matches++; if (!empty($email) && $this->valueExists($contact['EMAIL'] ?? [], $email)) $matches++; if ($matches > $maxMatches) { $maxMatches = $matches; $bestMatch = $contact; } } if ($bestMatch) { $contactId = $bestMatch['ID']; $updateFields = []; if ($name && (empty($bestMatch['NAME']) || $bestMatch['NAME'] != $name)) { $updateFields['NAME'] = $name; } if (!empty($phone) && !$this->valueExists($bestMatch['PHONE'] ?? [], $phone)) { $updateFields['PHONE'][] = ['VALUE' => $phone, 'VALUE_TYPE' => 'MOBILE']; } if (!empty($email) && !$this->valueExists($bestMatch['EMAIL'] ?? [], $email)) { $updateFields['EMAIL'][] = ['VALUE' => $email, 'VALUE_TYPE' => 'WORK']; } // Добавляем Telegram если есть username и его еще нет в контакте if (!empty($tg_username) && !imExists($bestMatch['IM'] ?? [], $tg_username)) { $updateFields['IM'][] = [ 'VALUE' => $tg_username, 'VALUE_TYPE' => 'TELEGRAM', 'TYPE_ID' => 'TELEGRAM' ]; } if (!empty($utm)) { $updateFields['UTM_SOURCE'] = $utm; } if (!empty($userID) && (!isset($bestMatch['UF_CRM_1751610129971']) || $bestMatch['UF_CRM_1751610129971'] != $userID)) { $updateFields['UF_CRM_1751610129971'] = $userID; } if ($isSubscribe) { $updateFields['UF_CRM_1744562461053'] = true; } if (!empty($updateFields)) { $updateResponse = $this->b24_request('crm.contact.update', [ 'id' => $contactId, 'fields' => $updateFields ]); if (!empty($updateResponse['error'])) { $error = 'Ошибка обновления: '.$updateResponse['error']; } } } } // 3. Создание нового контакта если не найдено совпадений else { $utm_source = get_utm_data()['utm_source'] ?? ''; $utm_medium = get_utm_data()['utm_medium'] ?? ''; $utm_campaign = get_utm_data()['utm_campaign'] ?? ''; $utm_content = get_utm_data()['utm_content'] ?? ''; $utm_term = get_utm_data()['utm_term'] ?? ''; $newContact = [ 'fields' => [ 'NAME' => $name, 'SOURCE_ID' => "WEB", 'TYPE_ID' => "CLIENT", 'OPENED' => "Y", 'UTM_SOURCE' => $utm_source, 'UTM_MEDIUM' => $utm_medium, 'UTM_CAMPAIGN' => $utm_campaign, 'UTM_CONTENT' => $utm_content, 'UTM_TERM' => $utm_term, ] ]; if (!empty($phone)) { $newContact['fields']['PHONE'][] = ['VALUE' => $phone, 'VALUE_TYPE' => 'MOBILE']; } if (!empty($email)) { $newContact['fields']['EMAIL'][] = ['VALUE' => $email, 'VALUE_TYPE' => 'WORK']; } if (!empty($utm)) { $newContact['fields']['UTM_SOURCE'] = $utm_source; } if (!empty($userID)) { $newContact['fields']['UF_CRM_1751610129971'] = $userID; } if ($isSubscribe) { $newContact['fields']['UF_CRM_1744562461053'] = true; } // Добавляем Telegram при создании нового контакта if (!empty($tg_username)) { $newContact['fields']['IM'][] = [ 'VALUE' => $tg_username, 'VALUE_TYPE' => 'TELEGRAM', 'TYPE_ID' => 'TELEGRAM' ]; } $response = $this->b24_request('crm.contact.add', $newContact); $contactId = $response['result'] ?? null; $error = $response['error'] ?? null; } return ['contact_id' => $contactId, 'error' => $error]; } // Вспомогательная функция для проверки значений function valueExists($items, $value) { foreach ($items as $item) { if ($item['VALUE'] == $value) return true; } return false; } function b24_request($method, $params = []) { $url = WEBHOOK_URL . $method; // ТУТ ВСТАВЛЯЕМ ХУК ОТ ИНТЕГРАТОРА $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($params), CURLOPT_TIMEOUT => 3, // Максимальное время выполнения запроса (в секундах) CURLOPT_CONNECTTIMEOUT => 2, // Максимальное время ожидания соединения (в секундах) ]); $response = curl_exec($curl); if ($response === false) { // Обработка ошибок return ['error' => curl_error($curl)]; } error_log('CURL URL: ' . $url); error_log('CURL RESP: ' . $response); curl_close($curl); return json_decode($response, true); } function b24_send_lead($contactId, $msg, $stage, $fName, $order_total, $method, $form_title, $order_id) { // Собираем UTM-метки из URL или POST данных $utm_data = get_utm_data(); $utm_source = $utm_data['utm_source'] ?? ''; $utm_medium = $utm_data['utm_medium'] ?? ''; $utm_campaign = $utm_data['utm_campaign'] ?? ''; $utm_content = $utm_data['utm_content'] ?? ''; $utm_term = $utm_data['utm_term'] ?? ''; // Подготавливаем данные для сделки $dealData = [ 'fields' => [ 'TITLE' => $form_title . ' - ' . $_SERVER['HTTP_HOST'], "STAGE_ID"=> $stage, 'CONTACT_ID' => $contactId, 'CATEGORY_ID' => $fName, 'SOURCE_ID' => "WEB", 'SOURCE_DESCRIPTION' => '', 'UTM_SOURCE' => $utm_source, 'UTM_MEDIUM' => $utm_medium, 'UTM_CAMPAIGN' => $utm_campaign, 'UTM_CONTENT' => $utm_content, 'UTM_TERM' => $utm_term, 'COMMENTS' => $msg, 'OPPORTUNITY' => $order_total, ] ]; if ($order_id){ $dealData['fields']['UF_CRM_1745741833259'] = $order_id; } error_log('DEAL DATA: ' . json_encode($dealData)); // Отправляем данные в Bitrix24 $newDealResponse = $this->b24_request($method, $dealData); error_log('DEAL RESP: ' . json_encode($newDealResponse)); // Обработка ответа $error = null; if (!empty($newDealResponse['error'])) { $error = $newDealResponse['error']; } elseif (empty($newDealResponse['result'])) { $error = 'Ошибка создания сделки'; } return [ 'success' => empty($error), 'contact_id' => $contactId, 'error' => $error, 'utm' => [ 'source' => $utm_source, 'medium' => $utm_medium, 'campaign' => $utm_campaign, 'content' => $utm_content, 'term' => $utm_term ] ]; } // Function to add lead (wraps b24_get_or_create_contact and b24_send_lead) function b24_add_lead($phone, $name, $email, $msg, $url, $stage, $fName, $order_total = 0, $userID = null, $method = 'crm.deal.add', $form_title, $isSubscribe = false, $order_id = '') { $contact = $this->b24_get_or_create_contact($phone, $name, $email, $userID, $isSubscribe); error_log(json_encode($contact)); if (!empty($contact['contact_id'])) { $contactId = $contact['contact_id']; } else { return ['error' => $contact['error']]; } $msg = $msg . "\nОтправлено со страницы: " . $url; return $this->b24_send_lead($contactId, $msg, $stage, $fName, $order_total, $method, $form_title, $order_id); } // Function to update contact based on userID function b24_update_contact_by_user_id($userID, $newData) { if (empty($userID)) { return ['success' => false, 'error' => 'Не указан ID пользователя']; } $user = get_user_by('ID', $userID); if (!$user) { return ['success' => false, 'error' => 'Пользователь не найден']; } // Получаем telegram username $tg_username = get_user_meta($userID, 'tg_username', true); // Ищем контакт по ID пользователя $response = $this->b24_request('crm.contact.list', [ 'filter' => ['UF_CRM_1751610129971' => $userID], 'select' => ['ID', 'NAME', 'PHONE', 'EMAIL', 'UF_CRM_1751610129971', 'IM', 'UF_CRM_1744562461053'] ]); if (empty($response['result'])) { return ['success' => false, 'error' => 'Контакт не найден в Bitrix24']; } $contact = $response['result'][0]; $contactId = $contact['ID']; $updateFields = []; // Update name if changed if (isset($newData['name']) && $newData['name'] !== $contact['NAME']) { $updateFields['NAME'] = $newData['name']; } // Update phone if changed if (isset($newData['phone'])) { $phoneExists = false; foreach ($contact['PHONE'] ?? [] as $phoneItem) { if ($phoneItem['VALUE'] === $newData['phone']) { $phoneExists = true; break; } } if (!$phoneExists) { $updateFields['PHONE'] = [['VALUE' => $newData['phone'], 'VALUE_TYPE' => 'MOBILE']]; } } // Update email if changed if (isset($newData['email'])) { $emailExists = false; foreach ($contact['EMAIL'] ?? [] as $emailItem) { if ($emailItem['VALUE'] === $newData['email']) { $emailExists = true; break; } } if (!$emailExists) { $updateFields['EMAIL'] = [['VALUE' => $newData['email'], 'VALUE_TYPE' => 'WORK']]; } } // Update Telegram if changed if (!empty($tg_username) && !imExists($contact['IM'] ?? [], $tg_username)) { $updateFields['IM'][] = [ 'VALUE' => $tg_username, 'VALUE_TYPE' => 'TELEGRAM', 'TYPE_ID' => 'TELEGRAM' ]; } if (empty($updateFields)) { return ['success' => true, 'message' => 'Нет изменений для обновления']; } // Send update request $updateResponse = $this->b24_request('crm.contact.update', [ 'id' => $contactId, 'fields' => $updateFields ]); if (!empty($updateResponse['error'])) { return ['success' => false, 'error' => 'Ошибка обновления: '.$updateResponse['error']]; } return ['success' => true, 'contact_id' => $contactId]; } function b24_update_deal_stage_by_order_id($order_id, $new_stage = 'C2:FINAL_INVOICE') { if (empty($order_id)) { return ['success' => false, 'error' => 'Не указан order_id']; } $b24 = new b24Handler(); // 1. Найдём сделку по полю UF_CRM_1745741833259 $searchResponse = $b24->b24_request('crm.deal.list', [ 'filter' => ['UF_CRM_1745741833259' => $order_id], 'select' => ['ID', 'TITLE', 'STAGE_ID'] ]); if (!empty($searchResponse['error'])) { return ['success' => false, 'error' => 'Ошибка поиска сделки: ' . $searchResponse['error']]; } $deals = $searchResponse['result'] ?? []; if (empty($deals)) { return ['success' => false, 'error' => 'Сделка с таким order_id не найдена']; } $dealId = $deals[0]['ID']; // 2. Обновим стадию сделки $updateResponse = $b24->b24_request('crm.deal.update', [ 'id' => $dealId, 'fields' => ['STAGE_ID' => $new_stage] ]); if (!empty($updateResponse['error'])) { return ['success' => false, 'error' => 'Ошибка обновления стадии: ' . $updateResponse['error']]; } return ['success' => true, 'deal_id' => $dealId]; } } class zohoHandler extends FormHandler { public function handle($data, $crmData) { // Логика отправки в HubSpot error_log("Отправка в Zoho: " . json_encode($data)); return parent::handle($data, $crmData); } } class tgHandler extends FormHandler { public function handle($data, $crmData) { global $site_env; if ($site_env->site_region == 'ru'){ $botToken = '7689949404:AAHLTKmopffoNkndHJt-rOmAIZ7Hb6aKf80'; $chatId = '-1002591296904'; } else if ($site_env->site_region == 'ae'){ $botToken = '7589204557:AAHh2qa5mUwRBq4fUyjQzqARpk4jDfUu5Yc'; $chatId = '-1002537433508'; } // Форматируем данные для отправки в Telegram $message = strip_tags($this->formatData($data)); // Отправляем сообщение в Telegram $this->sendToTelegram($botToken, $chatId, $message); // Вызываем следующий обработчик в цепочке return parent::handle($data, $crmData); } // Функция для отправки данных в Telegram private function sendToTelegram($botToken, $chatId, $message) { $url = "https://api.telegram.org/bot$botToken/sendMessage"; $maxLength = 4096; // Разбиваем сообщение на части, если оно длиннее лимита $messages = str_split($message, $maxLength); foreach ($messages as $msgPart) { $data = [ 'chat_id' => $chatId, 'text' => $msgPart, 'parse_mode' => 'HTML', ]; $options = [ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/x-www-form-urlencoded\r\n", 'content' => http_build_query($data), ], ]; $context = stream_context_create($options); $response = file_get_contents($url, false, $context); // Логируем HTTP-код и тело ответа $httpCode = isset($http_response_header[0]) ? $http_response_header[0] : 'No HTTP response'; } } } class emailHandler extends FormHandler { public function handle($data, $crmData) { $to = 'pro@cosmopet.shop'; // Укажите email, на который нужно отправить данные $subject = 'Форма обработана'; // Тема письма $message = $this->formatData($data); // Форматируем данные $headers = ['Content-Type: text/plain; charset=UTF-8']; // Заголовки письма // Отправляем письмо wp_mail($to, $subject, $message, $headers); // Вызываем следующий обработчик в цепочке return parent::handle($data, $crmData); } } class FormHandlerFactory { public static function getHandler(array $enabledHandlers) { global $site_env; $handler = null; if (in_array('email', $enabledHandlers)) { $handler = new emailHandler($handler); } if (in_array('zoho', $enabledHandlers)) { $handler = new zohoHandler($handler); } if (in_array('tg', $enabledHandlers)) { $handler = new tgHandler($handler); } if (in_array('b24', $enabledHandlers) && $site_env->site_region == 'ru') { $handler = new b24Handler($handler); } return $handler ?: new DefaultHandler(); } } // обработка обновления профиля пользователя add_action('profile_update', 'handle_profile_update', 10, 2); function handle_profile_update($userID, $old_user_data) { $user = get_user_by('ID', $userID); $email = $user->user_email; $phone = get_user_meta($userID, 'billing_phone', true); $first_name = get_user_meta($userID, 'first_name', true); $last_name = get_user_meta($userID, 'last_name', true); $name = trim($first_name . ' ' . $last_name); $newData = [ 'name' => $name, 'email' => $email, 'phone' => $phone, ]; $handler = new b24Handler(); $result = $handler->b24_update_contact_by_user_id($userID, $newData); if (!$result['success']) { error_log('Ошибка обновления контакта в Bitrix24: ' . $result['error']); } } function get_utm_data() { $utm_data = []; $utm_params = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; foreach ($utm_params as $param) { $utm_data[$param] = $_GET[$param] ?? $_COOKIE[$param] ?? ''; } return $utm_data; } // Функция для сохранения UTM в куки (если нужно хранить между сессиями) function save_utm_to_cookies() { $utm_params = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term']; foreach ($utm_params as $param) { if (isset($_GET[$param])) { setcookie($param, $_GET[$param], time() + 3600 * 24 * 30, '/'); } } } save_utm_to_cookies();