Когда впервые пишешь собственную интеграцию с amoCRM через REST API, сами запросы к сделкам, контактам или компаниям обычно оказываются не самой сложной частью.
Основной вопрос возникает раньше:
как один раз авторизовать интеграцию, а затем поддерживать доступ к amoCRM без постоянного ручного получения ключей?
1–2 года назад я реализовывал такую интеграцию на PHP. Токены хранили в MySQL, а работу с OAuth разбили на несколько отдельных файлов.
Сейчас решил восстановить общую архитектуру этой реализации.
Если убрать детали, OAuth‑интеграция выглядит довольно просто:
Authorization Code → Access Token + Refresh Token → сохранение в БД → запросы к REST API → обновление токенов → повторное сохранение в БД.
Код авторизации берется в AmoCRM:

В общем все эти значения помещаются в data и потом отсылаются через curl:
subdomain = 'https://xxx.amocrm.ru/'; //Поддомен нужного аккаунта $link = 'https:/xxx.amocrm.ru/oauth2/access_token'; //Формируем URL для запроса $data = [ 'client_id' => '6a2d4741-xxxx-xxxx-xxxx-92277723dcea', // ID интеграции 'client_secret' => 'PZp1wm7VpAUBvpTzADj4CRHjmuvRR4PwVS7EqVxxxNvpMglIWK8uy6KWSgxxvdjY', //секретный ключ 'grant_type' => 'authorization_code', 'code' => 'def50200exxxx8a701f977d7e9151d7b02da0000001d28e4a100000004c2e7350000xxb0f8fcd6b982615f86242baf3eea6ee5cbe282f19e8fd3d973ab858afa6691701f83c61f3e02af1029334772747c6242dc50e6e0bfe31e45666a8be5d93f0d5242300e2006c4bfe8cd4fa7c661f1ed5a4f290b8b283b44c3c22e247aa6abd587d7ce666ee6956ec8cb281ef6813ba6494f4eba97a8b20486cd3dff0ae6d486a195ae50755e13cf00f9608d3e699c5d7baefdbfc22da8ee21d1af69e6183abba81ba2a174341ce1e62c2dd7bbf956aee3624265d5bd9c098ffc24d9071d63b7c6e6db1ba5d55a853483ac77e42d6ed8ca32be8ccb2e7ee488716da9f8c21be27489f7d6ceadebe8d120b22233e053c1659fd872b4eefc756cd4770d23e9a48707d492051e417995193f51e6322c339d669e3298aee671ef2caa8c61b662d34685b7f8186be04a5f495a2123bacebfa7f2f8a9901260f8ba4757ab252849af7920445a8fe8397c78bbb94900e0f7f8984f18efb235bb378d5413c26100338c91d1d21dd15e4641ee85dc2b3187f6bb20a34fb94993302b18afe4ea577fdc9f8bcaf4a550eb7fdc41c206b318f7f210f696787fe0cbfe50843ce86849cf5dc6b834cb752cbe6e86df89b3402b7d3f4c28c3588f5908afac5452ee76a1b1b768aa25a5769cf272fa993101f07de20c28', 'redirect_uri' => 'https://xxx.ru/', ]; $curl = curl_init(); curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0'); curl_setopt($curl,CURLOPT_URL, $link); curl_setopt($curl,CURLOPT_HTTPHEADER,['Content-Type:application/json']); curl_setopt($curl,CURLOPT_HEADER, false); curl_setopt($curl,CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2); $out = curl_exec($curl); var_dump($out); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); $code = (int)$code; $errors = [ 400 => 'Bad request', 401 => 'Unauthorized', 403 => 'Forbidden', 404 => 'Not found', 500 => 'Internal server error', 502 => 'Bad gateway', 503 => 'Service unavailable', ]; try { if ($code < 200 || $code > 204) { throw new Exception(isset($errors[$code]) ? $errors[$code] : 'Undefined error', $code); } } catch(\Exception $e) { die('Ошибка: ' . $e->getMessage() . PHP_EOL . 'Код ошибки: ' . $e->getCode()); }
Схема такая:
Authorization Code
↓
POST /oauth2/access_token
↓
Access Token
Refresh Token
По итогу получает в обратку Access Token и Refresh Token и их записываем в БД себе, мы в MySQL записывали.
function save_token ($access_token, $refresh_token) { $config = [ 'host' => '127.0.0.1', 'db_name' => 'xxx', 'username' => 'xxxx', 'password' => "xxx", 'port' => 3308, ]; $d = "mysql:host={$config ['host']};port={$config["port"]};dbname={$config ["db_name"]};charset=utf8"; try { $pdo = new PDO($d, $config ["username"], $config ["password"]); echo('<br> Подключение успешно к БД <br>'); } catch (PDOException $e) { echo "Connect failed: ". $e -> getMessage(); } $insert = "INSERT INTO `AmoAuth`(`access_token`, `refresh_token`) VALUES ('$access_token','$refresh_token')"; try { $pdo -> query($insert); echo("Токены успешно сохранены"); } catch (\Throwable $th) { echo("Ошибка записи в БД $th"); } }
Все, $access_token есть. Зачем? Что бы потом его использовать в запросах к AmoCRM, например:
GET /api/v4/leads
GET /api/v4/contacts
POST /api/v4/leads
Потому что если access_token не валиден, то запросы не пройдут, он нужен далее для работы.
Access token amoCRM имеет ограниченный срок действия, поэтому его надо постоянно обновлять и для этого нужен Refresh Token.
Логика получается следующая:
Есть Access Token — Токен ещё действителен?
да → REST API
нет → Refresh Token → новый Access Token + новый Refresh Token → База
Если Access Token истек, то надо переобновить.
А что бы понять — надо проверить на валидность:
public function access_token_valid ($access_token) { //проверка access_token на валидность $this -> errorslogservice -> errors_log ("access_token_valid запускается"); $url = 'https://xxx.amocrm.ru/api/v4/account'; $headers = [ 'Authorization: Bearer ' . $access_token ]; $curl = curl_init(); curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); curl_setopt($curl,CURLOPT_ENCODING, ''); curl_setopt($curl,CURLOPT_MAXREDIRS, 10); curl_setopt($curl,CURLOPT_TIMEOUT, 0); curl_setopt($curl,CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl,CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0'); curl_setopt($curl,CURLOPT_URL, $url); curl_setopt($curl,CURLOPT_HTTPHEADER, $headers); curl_setopt($curl,CURLOPT_HEADER, false); curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2); $out = curl_exec($curl); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); $this -> errorslogservice -> errors_log ($out); $res = json_decode($out); return isset($res -> status); }
Как взять токен с БД писать не буду и так понятно, обычный SQL запрос внутри PHP с pdo по типу такого $query = $this → pdo → query($request).
Если оказался не валидным — надо запросить новый Access Token, то формируем код где дата Refresh Token:
private function refresh_amo_token ($refresh_token) { //получение нового access_token и refresh_token через refresh_token $this -> errorslogservice -> errors_log ("refresh_amo_token запускается"); $url = 'https://xxx.amocrm.ru/oauth2/access_token'; $data = [ 'client_id' => 'xxxxxxxx-84f7-4bc9-911c-92277723dcea', 'client_secret' => 'PZp1xxxxxxxxxxxxxxRR4PwVS7EqV7G1NvpMglIWK8uy6KWSg0AvdjY', 'grant_type' => 'refresh_token', 'refresh_token' => $refresh_token, 'redirect_uri' => 'https://xxx.ru/', ]; $headers = ['Content-Type:application/json']; $curl = curl_init(); curl_setopt($curl,CURLOPT_RETURNTRANSFER, true); curl_setopt($curl,CURLOPT_ENCODING, ''); curl_setopt($curl,CURLOPT_MAXREDIRS, 10); curl_setopt($curl,CURLOPT_TIMEOUT, 0); curl_setopt($curl,CURLOPT_FOLLOWLOCATION, true); curl_setopt($curl,CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_setopt($curl,CURLOPT_USERAGENT,'amoCRM-oAuth-client/1.0'); curl_setopt($curl,CURLOPT_URL, $url); curl_setopt($curl,CURLOPT_HTTPHEADER, $headers); curl_setopt($curl,CURLOPT_HEADER, false); curl_setopt($curl,CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($curl,CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($curl,CURLOPT_SSL_VERIFYPEER, 1); curl_setopt($curl,CURLOPT_SSL_VERIFYHOST, 2); $out = curl_exec($curl); $this -> errorslogservice -> errors_log ($out); $code = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($code >= 400) { throw new Exception ("ошибка API AmoCRM $out"); } $tokens = json_decode($out); if (!isset ($tokens -> access_token)) { throw new Exception ("Ошибка получения токена".json_encode($tokens)); } return $tokens; }
В ответ получаем новую пару Access Token и Refresh Token и перезаписываем их в БД себе.
И так по кругу, Access Token — не валидный — запросили новый — перезаписали в БД — используем в запросах Rest API к Амо.
Как вариант слышал что можно ещё сохранять expires_at что бы каждый раз не проверять валидность. Мы же делали каждый раз в curl проверяя, наверное это не очень оптимально если запросов будет много, но при малом‑среднем количестве работает нормально:
$access_token = $this -> tokensService -> get_token(); if ($this -> tokensService -> access_token_valid ($access_token)) { $access_token = $this -> tokensService -> refresh_token (); };
И погнало по кругу.
Если свести всё к нескольким правилам:
Authorization Code нужен в основном для первоначальной авторизации.
Он обменивается на access_token и refresh_token.
Access_token используется для REST‑запросов.
Когда access token заканчивается, используется refresh_token.
После refresh amoCRM выдаёт новую пару Access + Refresh.
Оба новых токена необходимо сохранить.
Старый Refresh token после обмена использовать нельзя.
При колхозном подходе все это можно запихать в 1 файл PHP, но по правильному разбивать на: Authamo.php, AmoAPIService.php, TokenService.php — что бы каждый выполнял свою функцию.
Authamo.php — авторизация первый раз
AmoAPIService.php — это как раз регулярные запросы к API, когда токен уже есть
TokensService.php — это как раз работа с токенами если при проверке IF в AmoAPIService.php вылетело что невалидный.

