Інтеграція CRM-системи amoCRM з сайтом

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Інтеграція CRM-системи amoCRM з сайтом
Середня
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Інтеграція CRM-системи amoCRM з сайтом

amoCRM зорієнтована на відділи продажів: воронка, контакти, завдання, чати. REST API охоплює всі основні сутності. Автентифікація — тільки через OAuth 2.0 (не webhook-токени як у Bitrix24), що вимагає налаштування додатку та зберігання refresh-токена.

OAuth 2.0 для server-to-server

amoCRM використовує Authorization Code flow з довгоживучим refresh-токеном (60 днів). Алгоритм:

  1. Створюємо інтеграцію в акаунті amoCRM: Налаштування → Інтеграції → Створити інтеграцію
  2. Отримуємо client_id, client_secret, redirect_uri
  3. Перша авторизація — ручний крок через браузер (генерація code)
  4. Обмін code на access + refresh токени — програмно
  5. Автоматичне оновлення access-токена через refresh
class AmoCrmTokenStorage {
    private const CACHE_KEY = 'amocrm_tokens';

    public function getAccessToken(): string {
        $tokens = Cache::get(self::CACHE_KEY);
        if (!$tokens || now()->gte($tokens['expires_at'])) {
            $tokens = $this->refreshTokens($tokens['refresh_token']);
        }
        return $tokens['access_token'];
    }

    private function refreshTokens(string $refreshToken): array {
        $response = Http::post(config('amocrm.base_url') . '/oauth2/access_token', [
            'client_id'     => config('amocrm.client_id'),
            'client_secret' => config('amocrm.client_secret'),
            'grant_type'    => 'refresh_token',
            'refresh_token' => $refreshToken,
            'redirect_uri'  => config('amocrm.redirect_uri'),
        ]);

        $data = $response->json();
        $tokens = [
            'access_token'  => $data['access_token'],
            'refresh_token' => $data['refresh_token'],
            'expires_at'    => now()->addSeconds($data['expires_in'] - 60),
        ];

        Cache::put(self::CACHE_KEY, $tokens, now()->addDays(60));
        // Також зберігаємо у БД як резерв (кеш може очиститися)
        DB::table('settings')->updateOrInsert(
            ['key' => 'amocrm_tokens'],
            ['value' => json_encode($tokens), 'updated_at' => now()]
        );

        return $tokens;
    }
}

API-клієнт

class AmoCrmClient {
    private AmoCrmTokenStorage $tokenStorage;
    private string $baseUrl;

    public function __construct(AmoCrmTokenStorage $tokenStorage) {
        $this->tokenStorage = $tokenStorage;
        $this->baseUrl = rtrim(config('amocrm.base_url'), '/') . '/api/v4';
    }

    public function get(string $endpoint, array $params = []): array {
        return Http::withToken($this->tokenStorage->getAccessToken())
            ->timeout(15)
            ->get($this->baseUrl . $endpoint, $params)
            ->throw()
            ->json();
    }

    public function post(string $endpoint, array $data): array {
        return Http::withToken($this->tokenStorage->getAccessToken())
            ->timeout(15)
            ->post($this->baseUrl . $endpoint, $data)
            ->throw()
            ->json();
    }

    public function patch(string $endpoint, array $data): array {
        return Http::withToken($this->tokenStorage->getAccessToken())
            ->timeout(15)
            ->patch($this->baseUrl . $endpoint, $data)
            ->throw()
            ->json();
    }
}

Створення контакту та угоди

amoCRM будується на трьох основних сутностях: Contact, Lead (угода), Company. Новий лід з сайту — це пара Contact + Lead:

class AmoCrmLeadService {
    public function createLeadFromForm(array $formData): array {
        $amo = app(AmoCrmClient::class);

        // Шукаємо існуючий контакт за телефоном
        $existing = $amo->get('/contacts', [
            'query' => $formData['phone'],
            'with'  => 'leads',
        ]);

        $contactId = null;
        if (!empty($existing['_embedded']['contacts'])) {
            $contactId = $existing['_embedded']['contacts'][0]['id'];
        }

        // Створюємо контакт якщо не знайдено
        if (!$contactId) {
            $result    = $amo->post('/contacts', [[
                'name'             => $formData['name'],
                'custom_fields_values' => [
                    [
                        'field_code' => 'PHONE',
                        'values'     => [['value' => $formData['phone'], 'enum_code' => 'WORK']],
                    ],
                    [
                        'field_code' => 'EMAIL',
                        'values'     => [['value' => $formData['email'] ?? '', 'enum_code' => 'WORK']],
                    ],
                ],
            ]]);
            $contactId = $result['_embedded']['contacts'][0]['id'];
        }

        // Створюємо угоду
        $pipelineId = config('amocrm.pipeline_id');
        $stageId    = config('amocrm.initial_stage_id');

        $leadResult = $amo->post('/leads', [[
            'name'        => 'Заявка з сайту: ' . $formData['name'],
            'pipeline_id' => $pipelineId,
            'status_id'   => $stageId,
            '_embedded'   => [
                'contacts' => [['id' => $contactId]],
            ],
            'custom_fields_values' => [
                [
                    'field_id' => config('amocrm.fields.source'),
                    'values'   => [['value' => $formData['source'] ?? 'Сайт']],
                ],
                [
                    'field_id' => config('amocrm.fields.utm_campaign'),
                    'values'   => [['value' => session('utm_campaign') ?? '']],
                ],
            ],
        ]]);

        $leadId = $leadResult['_embedded']['leads'][0]['id'];

        // Додаємо примітку
        $amo->post('/leads/' . $leadId . '/notes', [[
            'note_type' => 'common',
            'params'    => ['text' => 'Повідомлення: ' . ($formData['message'] ?? '')],
        ]]);

        return ['lead_id' => $leadId, 'contact_id' => $contactId];
    }
}

Webhook від amoCRM

Налаштування webhook у amoCRM: Налаштування → Інтеграції → Webhooks. При зміні статусу угоди amoCRM шле POST:

// routes/api.php
Route::post('/webhooks/amocrm', [AmoCrmWebhookController::class, 'handle']);

class AmoCrmWebhookController extends Controller {
    public function handle(Request $request): Response {
        // amoCRM шле дані у form-encoded форматі, не JSON!
        $data = $request->all();

        if (isset($data['leads']['status'])) {
            foreach ($data['leads']['status'] as $lead) {
                $this->syncLeadStatus($lead['id'], $lead['status_id']);
            }
        }

        if (isset($data['leads']['add'])) {
            // Нова угода створена в amoCRM (не з сайту)
            foreach ($data['leads']['add'] as $lead) {
                Log::info('New lead in amoCRM', ['id' => $lead['id']]);
            }
        }

        return response('ok');
    }

    private function syncLeadStatus(int $leadId, int $statusId): void {
        $order = Order::where('amocrm_lead_id', $leadId)->first();
        if (!$order) return;

        $map = [
            config('amocrm.stages.won')        => 'completed',
            config('amocrm.stages.lost')       => 'cancelled',
            config('amocrm.stages.in_work')    => 'processing',
        ];

        if ($status = $map[$statusId] ?? null) {
            $order->update(['status' => $status]);
        }
    }
}

Користувацькі поля

amoCRM підтримує користувацькі поля для угод та контактів. ID полів повинні бути отримані через API та збережені у конфігу:

// Отримуємо список полів воронки
$fields = $amo->get('/leads/custom_fields');
foreach ($fields['_embedded']['custom_fields'] as $field) {
    echo $field['id'] . ' — ' . $field['name'] . PHP_EOL;
}

Потім в config/amocrm.php:

'fields' => [
    'source'       => 12345,
    'utm_source'   => 12346,
    'utm_campaign' => 12347,
    'order_number' => 12348,
],

Оновлення угоди при оплаті

add_action('order_paid', function(Order $order) {
    if (!$order->amocrm_lead_id) return;

    app(AmoCrmClient::class)->patch('/leads/' . $order->amocrm_lead_id, [
        'price'     => $order->total,
        'status_id' => config('amocrm.stages.won'),
        'custom_fields_values' => [
            [
                'field_id' => config('amocrm.fields.order_number'),
                'values'   => [['value' => $order->number]],
            ],
        ],
    ]);
});

Терміни реалізації

OAuth-авторизація, базова відправка лідів з форм, збереження токена: 1–2 дні. Повна інтеграція: двустороння синхронізація статусів, обробник webhook, користувацькі поля, UTM-теги: 3–4 дні. Синхронізація каталогу, створення завдань за подіями сайту, інтеграція чату: плюс 2–3 дні.