PunchOtti unveils 80-bed hospital in AbiaESPN DeportesXavi fue la tercera opción de DT en Países BajosThe Jerusalem PostYair Netanyahu identified as PM's son who was target of Iranian assassination plot - reportוואלהילד בן 10 נפצע קשה בהתהפכות טרקטורון חקלאי סמוך לכינרתInquirer EntertainmentNirvana’s ‘Nevermind’ turns 35: The day the underground broke throughBollywood HungamaSHOCKING: The Odyssey FORCED out of IMAX despite HISTORIC Rs. 90 cr collections from 34 IMAX screens and 43% occupancy on Day 40; Toxic takes over ALL showsRTP DesportoSurfista lusa Francisca Veselko junta-se a Hopkins na segunda ronda do Fiji ProESPNWill Álvarez join Barcelona or Arsenal in summer's last major transfer? Or stay at Atlético?CNN TürkSON DAKİKA! Cumhurbaşkanı Erdoğan açıklamalarda bulunuyorSky TG24Italia tra ondate di caldo ed eventi estremi, 3 miliardi di danni per siccità e alluvioniVanguardDangote to launch own shipping fleet to boost maritime operationThe Sydney Morning HeraldQueensland premier secures data centre power victory over PM
The Daily Newsstand · Free, Always
Wednesday, August 26, 2026

OAuth 2.0 в amoCRM REST API на PHP простым языком: получение, хранение и обновление токенов

Translate

Когда впервые пишешь собственную интеграцию с 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);
}

Если ответ приходит что не валидный - запрашиваем снова:

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 ();
   };

И погнало по кругу.

Если свести всё к нескольким правилам:

  1. Authorization Code нужен в основном для первоначальной авторизации.

  2. Он обменивается на access_token и refresh_token.

  3. Access_token используется для REST-запросов.

  4. Когда access token заканчивается, используется refresh_token.

  5. После refresh amoCRM выдаёт новую пару Access + Refresh.

  6. Оба новых токена необходимо сохранить.

  7. Старый Refresh token после обмена использовать нельзя.

При колхозном подходе все это можно запихать в 1 файл PHP, но по правильному разбивать на: Authamo.php, AmoAPIService.php, TokenService.php - что бы каждый выполнял свою функцию.

Authamo.php - авторизация первый раз

AmoAPIService.php - это как раз регулярные запросы к API, когда токен уже есть

TokensService.php - это как раз работа с токенами если при проверке IF в AmoAPIService.php вылетело что невалидный.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.