Настройка защиты от мошеннических заказов 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Настройка защиты от мошеннических заказов 1С-Битрикс
Простая
~1 рабочий день
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    745
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Настройка защиты от мошеннических заказов 1С-Битрикс

Мошеннические заказы имеют характерные паттерны: один IP — десятки заказов за час, временные email, несовпадение города доставки и IP-геолокации, заказы на крупную сумму без истории покупок. Набор проверок в Битрикс закрывает 80-90% таких случаев без внешних сервисов.

Многоуровневая проверка

Защита встраивается в событие OnBeforeOrderFinalAction. Три уровня действий: пропустить, пометить для ручной проверки, заблокировать.

// /local/php_interface/init.php
AddEventHandler('sale', 'OnBeforeOrderFinalAction', ['\Local\Fraud\OrderGuard', 'check']);
namespace Local\Fraud;

class OrderGuard
{
    public static function check(\Bitrix\Sale\Order $order): \Bitrix\Main\EventResult
    {
        if ($order->getId() > 0) {
            return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS);
        }

        $violations = self::runChecks($order);

        if (in_array('block', array_column($violations, 'action'), true)) {
            return new \Bitrix\Main\EventResult(
                \Bitrix\Main\EventResult::ERROR,
                new \Bitrix\Main\Error('Заказ заблокирован системой безопасности. Обратитесь в поддержку.')
            );
        }

        if (!empty($violations)) {
            $comment = implode('; ', array_column($violations, 'reason'));
            $order->setField('COMMENTS', '[REVIEW] ' . $comment);
        }

        return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS);
    }

    private static function runChecks(\Bitrix\Sale\Order $order): array
    {
        $violations = [];
        $ip    = $_SERVER['REMOTE_ADDR'] ?? '';
        $props = $order->getPropertyCollection();
        $email = $props->getItemByOrderPropertyCode('EMAIL')?->getValue() ?? '';
        $phone = $props->getItemByOrderPropertyCode('PHONE')?->getValue() ?? '';

        // 1. Превышение лимита заказов с IP
        $ipOrders = self::countOrdersByIp($ip, 1); // за 1 час
        if ($ipOrders >= 10) {
            $violations[] = ['action' => 'block',  'reason' => "IP {$ip}: {$ipOrders} orders/hour"];
        } elseif ($ipOrders >= 3) {
            $violations[] = ['action' => 'review', 'reason' => "IP: {$ipOrders} orders/hour"];
        }

        // 2. Временный email
        if (self::isDisposableEmail($email)) {
            $violations[] = ['action' => 'review', 'reason' => 'Disposable email'];
        }

        // 3. Несовпадение города доставки и IP
        $deliveryCity = $props->getItemByOrderPropertyCode('CITY')?->getValue() ?? '';
        if ($deliveryCity && self::isCityMismatch($ip, $deliveryCity)) {
            $violations[] = ['action' => 'review', 'reason' => 'City/IP mismatch'];
        }

        // 4. Крупный заказ от нового пользователя
        $userId = (int)$order->getUserId();
        if ($order->getPrice() > 50000 && $userId > 0 && self::getPreviousOrderCount($userId) === 0) {
            $violations[] = ['action' => 'review', 'reason' => 'High amount + new user'];
        }

        return $violations;
    }

    private static function countOrdersByIp(string $ip, int $hours): int
    {
        $ip   = \Bitrix\Main\Application::getConnection()->getSqlHelper()->forSql($ip);
        $from = date('Y-m-d H:i:s', time() - $hours * 3600);

        return (int)\Bitrix\Main\Application::getConnection()->query(
            "SELECT COUNT(*) cnt FROM b_sale_order
             WHERE CREATED_BY_IP = '{$ip}' AND DATE_INSERT >= '{$from}'"
        )->fetch()['cnt'];
    }

    private static function isDisposableEmail(string $email): bool
    {
        $domain  = strtolower(substr(strrchr($email, '@'), 1));
        $domains = ['mailinator.com', 'guerrillamail.com', 'tempmail.com', 'throwam.com',
                    'yopmail.com', '10minutemail.com', 'trashmail.com', 'dispostable.com'];
        return in_array($domain, $domains, true);
    }

    private static function isCityMismatch(string $ip, string $deliveryCity): bool
    {
        // Простая проверка через geoip: если расхождение > 1000 км
        if (!function_exists('geoip_record_by_name')) return false;
        $geo = @geoip_record_by_name($ip);
        if (!$geo || empty($geo['city'])) return false;

        // Нормализуем и сравниваем
        $geoCity      = mb_strtolower(trim($geo['city']));
        $deliveryNorm = mb_strtolower(trim($deliveryCity));

        return $geoCity !== '' && !str_contains($deliveryNorm, $geoCity)
               && !str_contains($geoCity, $deliveryNorm);
    }

    private static function getPreviousOrderCount(int $userId): int
    {
        return (int)\Bitrix\Main\Application::getConnection()->query(
            "SELECT COUNT(*) cnt FROM b_sale_order
             WHERE USER_ID = {$userId} AND STATUS_ID NOT IN ('C')"
        )->fetch()['cnt'];
    }
}

Уведомление менеджера о подозрительных заказах

При статусе [REVIEW] агент каждые 15 минут проверяет новые заказы с этой меткой и отправляет уведомление менеджеру:

$suspiciousOrders = \Bitrix\Sale\OrderTable::getList([
    'filter' => [
        'STATUS_ID' => 'N',
        '%COMMENTS' => '[REVIEW]',
        '>=DATE_INSERT' => new \Bitrix\Main\Type\DateTime(date('Y-m-d H:i:s', time() - 900)),
    ],
    'select' => ['ID', 'PRICE', 'COMMENTS', 'DATE_INSERT'],
])->fetchAll();

Сроки реализации

Конфигурация Срок
Базовые проверки (IP, email, лимиты) 2–3 дня
+ геолокация, уведомления менеджеру +1–2 дня
+ административный интерфейс управления +2 дня