Интеграция с OrangeData для 1С-Битрикс
OrangeData — облачный фискальный регистратор, который работает по схеме «касса как сервис». Физической кассы у магазина нет: OrangeData сам хранит и обслуживает оборудование, а вы получаете API для отправки чеков. Это удобно, пока не начинаете разбираться в деталях: авторизация через сертификаты X509, асинхронная фискализация, специфическая схема атрибутов чека под 54-ФЗ. Стандартного модуля в Битрикс нет, всё реализуется через кастомный обработчик платёжной системы.
Как работает OrangeData API
OrangeData использует REST API с взаимной TLS-аутентификацией. Это не просто токен в заголовке — нужно клиентское X509-сертификат, который выдаётся при регистрации. Каждый запрос к API подписывается приватным ключом из этого сертификата.
Основные эндпоинты:
-
POST /api/v2/documents/— отправить документ (чек прихода, возврата, коррекции) -
GET /api/v2/documents/{id}— получить результат фискализации по идентификатору
Важный момент: фискализация асинхронная. После POST вы получаете 202 Accepted, а не готовый чек. Реальный фискальный признак появляется через несколько секунд или минут — нужно опрашивать GET-эндпоинт или ждать callback.
Структура запроса на отправку чека
$document = [
'id' => uniqid('', true), // уникальный идентификатор документа
'inn' => $inn,
'group' => 'Main',
'key' => $signatureKeyName, // имя ключа из личного кабинета OrangeData
'content' => [
'type' => 1, // 1 - приход, 2 - возврат прихода
'positions' => $this->buildPositions($payment),
'checkClose' => [
'payments' => [
[
'type' => $this->getPaymentType($payment), // 1-наличные, 2-безнал
'amount' => $payment->getSum(),
],
],
'taxationSystem' => 0, // 0-ОСН, 1-УСН доход, 2-УСН доход-расход
],
'customerContact' => $this->getCustomerContact($order),
],
];
Поле positions — массив позиций чека. Каждая позиция содержит quantity, price, tax (код ставки НДС), text (наименование), paymentMethodType и paymentSubjectType. Последние два поля — требование 54-ФЗ с 2019 года: нужно явно указать, что продаётся (товар, услуга, работа) и каким способом (предоплата, полный расчёт).
private function buildPositions(\Bitrix\Sale\Payment $payment): array
{
$order = $payment->getOrder();
$basket = $order->getBasket();
$positions = [];
foreach ($basket as $item) {
$positions[] = [
'quantity' => $item->getQuantity(),
'price' => $item->getPrice(),
'tax' => $this->mapVatRate($item->getField('VAT_RATE')),
'text' => $item->getField('NAME'),
'paymentMethodType' => 4, // 4 - полный расчёт
'paymentSubjectType' => 1, // 1 - товар
];
}
// Доставка как отдельная позиция
if ($order->getDeliveryPrice() > 0) {
$positions[] = [
'quantity' => 1,
'price' => $order->getDeliveryPrice(),
'tax' => 6, // без НДС
'text' => 'Доставка',
'paymentMethodType' => 4,
'paymentSubjectType' => 4, // 4 - услуга
];
}
return $positions;
}
TLS-аутентификация: настройка в PHP
Отправка запросов с клиентским сертификатом через cURL:
private function apiRequest(string $method, string $path, array $data = []): array
{
$ch = curl_init('https://api.orangedata.ru:12003' . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_SSLCERT => $this->certPath, // путь к .crt
CURLOPT_SSLKEY => $this->keyPath, // путь к .key
CURLOPT_CAINFO => $this->caPath, // корневой сертификат OrangeData
CURLOPT_SSL_VERIFYPEER => true,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 202) {
return ['status' => 'queued'];
}
return json_decode($response, true);
}
Сертификаты и ключи хранятся вне webroot — например, в /local/certs/orangedata/. Пути передаются через настройки обработчика платёжной системы. Никогда не кладите .key-файл в public_html.
Асинхронная фискализация: polling через агентов Битрикс
После отправки документа нужно дождаться фискального признака. Реализация через агента:
- После успешного
POSTсохраняем вb_sale_order_propsполяORANGEDATA_DOC_IDиORANGEDATA_STATUS = pending. - Агент
OrangeDataCheckAgentзапускается каждую минуту, выбирает заказы сORANGEDATA_STATUS = pending, опрашиваетGET /api/v2/documents/{id}. - Когда статус меняется на
done— сохраняем фискальные данные (fn,fd,fpd, ссылка на чек) и меняемORANGEDATA_STATUS = done. - Фискальные данные отправляются покупателю по email через событие
SALE_NEW_ORDERили отдельным письмом.
public static function run(): string
{
$pendingOrders = self::getPendingOrders();
foreach ($pendingOrders as $orderId => $docId) {
$result = self::checkDocumentStatus($docId);
if (($result['status'] ?? '') === 'done') {
self::saveFiscalData($orderId, $result);
}
}
return __CLASS__ . '::run();';
}
Возвраты
При возврате отправляется документ с type: 2 (возврат прихода). Сумма и состав позиций должны совпадать с оригиналом (при полном возврате) или содержать только возвращаемые позиции (при частичном). OrangeData не проверяет связь с исходным документом по ID — это ваша ответственность.
Возврат запускается из обработчика события OnSaleOrderRefund или из административной части при ручной обработке возврата.
Тестовый контур
OrangeData предоставляет тестовый API: https://apip.orangedata.ru:12003. Тестовые сертификаты выдаются отдельно. В тестовом режиме фискализация проходит через виртуальную кассу, реальных ФН нет. Переключение между контурами — через параметр обработчика USE_TEST_API.
Сроки
| Состав | Срок |
|---|---|
| Базовая интеграция (приход + возврат) | 4–5 дней |
| + Асинхронный polling + email с чеком | +2 дня |
| + Частичные возвраты + маппинг ставок НДС | +1 день |







