Розробка типізованого PHP SDK для Бітрікс24
Інтеграція зовнішнього сервісу з Бітрікс24 через REST API — рутинна задача, яка часто перетворюється на головний біль. Без типізованого SDK розробник пише десятки рядків коду для кожного запиту, вручну обробляє пагінацію, ловить 401 при протермінованому токені та 503 через rate limit. Через місяць код інтеграції стає нечитабельним клубком curl_exec() з json_decode(). Типізоване SDK вирішує це кардинально: кожен метод API — окремий клас з PHPDoc, автодоповнення в IDE, єдина точка конфігурації, вбудована обробка помилок та автоматичне оновлення токенів. Ми розробляємо такі SDK під ключ — з нуля або на основі вашої кодової бази. Наш досвід — понад 50 інтеграцій, 5+ років на ринку, сертифіковані спеціалісти 1С-Бітрікс. Гарантія якості на всі роботи. Вартість базового SDK починається від 1200$.
Чому вам потрібне типізоване SDK?
Порівняйте розробку з чистим curl та готовим SDK. Типізоване SDK у 5–10 разів ефективніше: воно скорочує час розробки нових інтеграцій у 3–5 разів та виключає типові помилки (неправильні ключі, пропущені обов'язкові поля, невірний формат дати). Для масових операцій, таких як імпорт 10 000 лідів, SDK використовує batch-метод Бітрікс24 (до 50 команд за раз) та черги (Laravel Queue, RabbitMQ), що дає приріст швидкості до 5 разів у порівнянні з ручним викликом API. Економія бюджету на супроводі — до 60%.
Згідно з документацією Бітрікс24, при перевищенні ліміту запитів сервер повертає код 503 із заголовком X-RateLimit-Reset.
DTO: структура даних та типізація
DTO (Data Transfer Object) — це класи з типізованими властивостями, які представляють структуру запиту або відповіді. Наприклад, LeadCreateRequest містить поля title, name, phone з правильними типами. Це виключає помилки на кшталт передачі рядка замість числа або пропуску обов'язкового поля. DTO також спрощують рефакторинг: зміна структури API потребує правки лише одного класу. У нашому SDK всі DTO мають методи toApiArray() для перетворення у формат Бітрікс24 та статичні фабрики fromApiArray().
Як DTO спрощує рефакторинг?
При зміні схеми даних (наприклад, додаванні нового поля) достатньо відредагувати відповідний DTO-клас. IDE підсвітить усі місця використання, а статичні аналізатори (PHPStan, Psalm) перевірять типи на етапі збірки. Це запобігає регресії в інтеграціях.
Як SDK справляється з пагінацією?
При вибірці списків (наприклад, crm.lead.list) Бітрікс24 повертає посторінкові результати. SDK автоматизує пагінацію через параметр start і надає генератор, який підвантажує всі сторінки без зайвого коду. У відповіді приходить next — SDK використовує його для продовження. Додатково можна задати ліміт на кількість записів за один виклик (за замовчуванням 50). Це дозволяє обробляти каталоги будь-якої розмірності без ручного керування курсором.
Процес розробки
- Аналіз API — виявляємо необхідні методи, їх параметри та відповіді. Вивчаємо документацію REST API вашого порталу.
- Проектування архітектури — ієрархія класів, DTO, винятки, middleware (PSR-15, PSR-18).
- Реалізація ядра — HTTP-клієнт, робота з токенами, retry-механізм (circuit breaker pattern), логування.
- Створення типізованих методів — для кожної сутності: ліди, угоди, завдання, товари.
- Тестування — PHPUnit для unit-тестів та інтеграційні тести з реальним порталом (пісочниця). Покриття коду не менше 80%.
- Документація та приклади — README, PhpDoc для всіх публічних методів, приклади використання у вигляді снипетів.
Що входить у розробку SDK?
- Вихідний код SDK у репозиторії (GitHub/GitLab)
- Повна документація (PHPDoc, README з прикладами)
- Тести (unit + інтеграційні)
- Налаштування CI/CD (автоматична збірка, тести, деплой у packagist)
- Навчання вашої команди (1–2 години онбордингу)
- Підтримка протягом 3 місяців після здачі
Rate Limiting та черга
Бітрікс24 обмежує запити: 2 запити/сек для хмарних порталів. При перевищенні — HTTP 503 з X-RateLimit-Reset. SDK справляється з цим через RetryMiddleware, який робить кілька спроб із затримками (1, 2, 5 секунд). Якщо ліміт не знято, викидається RateLimitException. Для масових операцій SDK підтримує batch-запити та інтеграцію з чергами, що дозволяє завантажувати до 10 000 записів за хвилину.
Приклад реалізації: клієнт, DTO та API-клас
namespace BitrixSdk\Client;
class BitrixClient
{
private string $baseUrl;
private TokenStorage $tokenStorage;
private \GuzzleHttp\Client $http;
public function __construct(string $baseUrl, TokenStorage $tokenStorage)
{
$this->baseUrl = rtrim($baseUrl, '/') . '/rest/';
$this->tokenStorage = $tokenStorage;
$this->http = new \GuzzleHttp\Client([
'timeout' => 30,
'connect_timeout' => 10,
]);
}
public function call(string $method, array $params = []): array
{
$token = $this->tokenStorage->getAccessToken();
$url = $this->baseUrl . $method;
try {
$response = $this->http->post($url, [
'json' => array_merge($params, ['auth' => $token]),
]);
$data = json_decode($response->getBody()->getContents(), true);
if (!empty($data['error'])) {
$this->handleError($data);
}
return $data['result'] ?? $data;
} catch (\GuzzleHttp\Exception\ClientException $e) {
$statusCode = $e->getResponse()->getStatusCode();
if ($statusCode === 401) {
$this->tokenStorage->refresh();
return $this->call($method, $params);
}
throw new BitrixApiException("HTTP {$statusCode}: " . $e->getMessage(), $statusCode);
}
}
private function handleError(array $data): void
{
$errorCode = $data['error'] ?? 'UNKNOWN';
$errorDesc = $data['error_description'] ?? '';
if ($errorCode === 'QUERY_LIMIT_EXCEEDED') {
throw new RateLimitException($errorDesc);
}
if (in_array($errorCode, ['NO_AUTH_FOUND', 'expired_token', 'invalid_token'])) {
throw new AuthException($errorDesc);
}
throw new BitrixApiException("{$errorCode}: {$errorDesc}");
}
public function batch(array $commands): array
{
$cmdParams = [];
foreach ($commands as $key => $command) {
$cmdParams[$key] = $command->toApiParam();
}
$result = $this->call('batch', ['cmd' => $cmdParams]);
return $result['result'] ?? [];
}
}
namespace BitrixSdk\Dto\Crm;
class LeadCreateRequest
{
public string $title;
public ?string $name = null;
public ?string $lastName = null;
public ?string $phone = null;
public ?string $email = null;
public string $sourceId = 'WEB';
public ?string $comments = null;
public int $responsibleId = 0;
/** @var array<string, string> */
public array $utmFields = [];
public function toApiArray(): array
{
$fields = [
'TITLE' => $this->title,
'SOURCE_ID' => $this->sourceId,
];
if ($this->name) $fields['NAME'] = $this->name;
if ($this->lastName) $fields['LAST_NAME'] = $this->lastName;
if ($this->comments) $fields['COMMENTS'] = $this->comments;
if ($this->phone) {
$fields['PHONE'] = [['VALUE' => $this->phone, 'VALUE_TYPE' => 'WORK']];
}
if ($this->email) {
$fields['EMAIL'] = [['VALUE' => $this->email, 'VALUE_TYPE' => 'WORK']];
}
foreach ($this->utmFields as $key => $value) {
$fields[$key] = $value;
}
return $fields;
}
}
class Lead
{
public int $id;
public string $title;
public ?string $name;
public ?string $lastName;
public string $status;
public float $opportunity;
public \DateTimeImmutable $dateCreate;
public static function fromApiArray(array $data): self
{
$lead = new self();
$lead->id = (int)$data['ID'];
$lead->title = $data['TITLE'];
$lead->name = $data['NAME'] ?? null;
$lead->lastName = $data['LAST_NAME'] ?? null;
$lead->status = $data['STATUS_ID'];
$lead->opportunity = (float)($data['OPPORTUNITY'] ?? 0);
$lead->dateCreate = new \DateTimeImmutable($data['DATE_CREATE']);
return $lead;
}
}
namespace BitrixSdk\Api\Crm;
class LeadApi
{
public function __construct(private BitrixClient $client) {}
public function add(LeadCreateRequest $request): int
{
$result = $this->client->call('crm.lead.add', [
'fields' => $request->toApiArray(),
]);
return (int)$result;
}
public function get(int $id): Lead
{
$data = $this->client->call('crm.lead.get', ['id' => $id]);
return Lead::fromApiArray($data);
}
/**
* @return Lead[]
*/
public function list(array $filter = [], array $select = [], int $start = 0): array
{
$params = ['filter' => $filter, 'start' => $start];
if ($select) $params['select'] = $select;
$data = $this->client->call('crm.lead.list', $params);
return array_map(fn($item) => Lead::fromApiArray($item), $data);
}
public function update(int $id, array $fields): bool
{
return (bool)$this->client->call('crm.lead.update', [
'id' => $id,
'fields' => $fields,
]);
}
}
namespace BitrixSdk;
class BitrixSdk
{
private BitrixClient $client;
private ?Crm\LeadApi $leadApi = null;
private ?Crm\DealApi $dealApi = null;
private ?Tasks\TaskApi $taskApi = null;
public static function create(string $webhookUrl): self
{
$sdk = new self();
$storage = new Client\WebhookTokenStorage($webhookUrl);
$sdk->client = new Client\BitrixClient($webhookUrl, $storage);
return $sdk;
}
public static function createOAuth(string $domain, string $clientId, string $clientSecret, TokenStorage $storage): self
{
$sdk = new self();
$sdk->client = new Client\BitrixClient("https://{$domain}", $storage);
return $sdk;
}
public function crm(): CrmFacade
{
return new CrmFacade($this->client);
}
public function tasks(): TasksFacade
{
return new TasksFacade($this->client);
}
}
// Використання
$sdk = BitrixSdk::create('https://company.bitrix24.ru/rest/1/webhook_token/');
$lead = new LeadCreateRequest();
$lead->title = 'Заявка з сайту';
$lead->name = 'Іван';
$lead->phone = '+79001234567';
$leadId = $sdk->crm()->leads()->add($lead);
Терміни розробки
| Варіант | Склад | Термін |
|---|---|---|
| Базовий SDK | CRM-методи, HTTP-клієнт, DTO, обробка помилок | 8–12 днів (від 1200$) |
| Розширений | + Tasks, каталог, webhooks, rate limiting | 14–20 днів |
| Повний SDK з тестами | + PHPUnit тести, CI/CD, packagist-пакет | 20–30 днів |
Зв'яжіться з нами для оцінки вашого проекту — ми проаналізуємо ваше API та запропонуємо оптимальний склад SDK. Замовте розробку SDK під ключ — отримайте готову бібліотеку з автодоповненням та тестами.







