Интеграция 1С-Битрикс с POS-терминалами
Интернет-магазин принимает оплату онлайн, а в точках самовывоза — через POS-терминал. Заказы из онлайна попадают в 1С, но статус оплаты на терминале и в Битрикс расходятся: кассир видит «оплачен», менеджер в CRM — «ожидает оплаты». Плюс 54-ФЗ требует, чтобы при оплате через POS выбивался фискальный чек. Интеграция решает три задачи одновременно: синхронизацию статуса оплаты, выдачу чека и обновление остатков.
Варианты POS-оборудования и протоколы
Работа с POS в Битрикс зависит от конкретного оборудования и его API:
-
Эвотор — REST API, есть готовый модуль на Marketplace Битрикс (
evotor.kassa) -
Атол Онлайн — REST API для фискализации, интеграция через модуль
atol.onlineили кастомно - МТС Касса, Лайтбокс — REST API, кастомная интеграция
- Ingenico, VeriFone — нет публичного HTTP API, интеграция через middleware (1С или отдельный сервис)
Для встроенных POS с API на уровне REST — интеграция напрямую из Битрикс. Для терминалов без прямого API — через посредника, обычно 1С:Розница или отдельный Windows-сервис.
Схема интеграции через Эвотор
Эвотор — наиболее типовой случай для российского ритейла. Схема:
[Битрикс] <---> [Эвотор API] <---> [Смарт-терминал Эвотор]
- Заказ в Битрикс переходит в статус «Ожидает оплаты в магазине»
- Кассир открывает заказ на терминале (или он передаётся автоматически)
- Покупатель оплачивает на POS
- Эвотор фиксирует транзакцию, отправляет webhook в Битрикс
- Битрикс переводит заказ в «Оплачен», создаёт запись об оплате
Обработчик платёжной системы в Битрикс
Интеграция с POS реализуется как кастомная платёжная система:
// /local/modules/custom.pos/install/handlers/pos.php
namespace Custom\Pos\Handler;
use Bitrix\Sale\PaySystem\ServiceHandler;
use Bitrix\Sale\PaySystem\ServiceResult;
use Bitrix\Main\Request;
use Bitrix\Sale\Order;
class PosPaymentHandler extends ServiceHandler
{
// Вызывается при инициации оплаты через POS
public function initiatePay(
\Bitrix\Sale\Payment $payment,
Request $request = null
): ServiceResult {
$result = new ServiceResult();
$order = $payment->getOrder();
$orderId = $order->getId();
$amount = $payment->getSum();
$currency = $payment->getCurrencyCode();
// Передаём заказ в Эвотор
$evotor = new EvoktorApiClient($this->service->getField('EVOTOR_TOKEN'));
$response = $evotor->createReceipt([
'order_id' => $orderId,
'total' => $amount,
'currency' => $currency,
'items' => $this->buildReceiptItems($order),
]);
if (!$response['success']) {
$result->addError(new \Bitrix\Main\Error($response['error']));
return $result;
}
// Сохраняем transaction_id для последующей проверки
$payment->setField('PS_INVOICE_ID', $response['transaction_id']);
return $result;
}
// Webhook: Эвотор сообщает об успешной оплате
public function processRequest(
\Bitrix\Sale\Payment $payment,
Request $request
): ServiceResult {
$result = new ServiceResult();
$data = json_decode($request->getInput(), true);
// Верифицируем подпись Эвотора
$signature = $request->getHeader('X-Evotor-Signature');
if (!$this->verifySignature($data, $signature)) {
$result->addError(new \Bitrix\Main\Error('Invalid signature'));
return $result;
}
if ($data['status'] === 'PAID') {
$result->setOperationType(ServiceResult::MONEY_COMING);
$result->setPaid('Y');
}
return $result;
}
private function buildReceiptItems(Order $order): array
{
$items = [];
/** @var \Bitrix\Sale\Basket $basket */
$basket = $order->getBasket();
foreach ($basket as $basketItem) {
$items[] = [
'name' => $basketItem->getField('NAME'),
'quantity' => $basketItem->getQuantity(),
'price' => $basketItem->getPrice(),
'vat' => 'VAT20', // НДС 20%
];
}
return $items;
}
}
Фискализация: 54-ФЗ
При оплате через POS обязателен кассовый чек. Три варианта:
- POS со встроенным ФН (Эвотор, МТС Касса) — чек выбивается прямо на терминале при оплате
- POS без ФН + онлайн-касса — терминал фиксирует факт оплаты, Битрикс отправляет данные в облачную кассу (Атол Онлайн, OFD.ru)
- Интеграция через 1С — 1С:Розница фискализирует, Битрикс получает статус через обмен
Если используется вариант 2, в Битрикс нужен обработчик события OnSaleOrderPaid:
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale', 'OnSaleOrderPaid',
function (\Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
$payment = $event->getParameter('PAYMENT');
// Проверяем, что это POS-оплата (не онлайн)
$paySystemId = $payment->getPaymentSystemId();
if ($paySystemId !== POS_PAYSYSTEM_ID) {
return;
}
// Отправляем в Атол Онлайн
AtolOnlineService::sendReceipt($order, $payment, 'sell');
}
);
Синхронизация остатков после POS-продажи
Когда через POS-терминал продаётся товар из магазина (не под онлайн-заказ), остатки нужно списать и в Битрикс:
// Списание остатков на складе при POS-продаже
\Bitrix\Catalog\StoreProductTable::decreaseProductQuantity(
$productId,
$storeId,
$quantity
);
// Пересчёт доступного количества в каталоге
CCatalogProduct::getProductData($productId, ['QUANTITY' => true]);
На практике синхронизация остатков при POS-продажах — наиболее болезненная часть: терминал может работать оффлайн, транзакции накапливаются и приходят пакетом. Нужна очередь операций и идемпотентная обработка: если одна и та же транзакция пришла дважды (повтор webhook), не списывать остатки повторно.
Идемпотентность webhook-обработчика
// Проверяем, не обработан ли уже этот transaction_id
$existing = \Bitrix\Sale\PaySystem\Manager::getList([
'filter' => ['PS_INVOICE_ID' => $transactionId],
'select' => ['ID'],
])->fetch();
if ($existing) {
// Уже обработано — возвращаем 200 без повторного действия
return (new ServiceResult())->setOperationType(ServiceResult::MONEY_COMING);
}
Сроки разработки
| Этап | Содержание | Срок |
|---|---|---|
| Анализ POS-оборудования и API | Документация, тест-стенд терминала | 1–2 дня |
| Разработка платёжного обработчика | Модуль в /local/, webhook-эндпоинт | 3–5 дней |
| Фискализация (если нет встроенной) | Интеграция с Атол Онлайн или аналогом | 2–3 дня |
| Синхронизация остатков | Списание через StoreProductTable | 1–2 дня |
| Тестирование на реальном терминале | Оплата, отмена, чек, остатки | 2–3 дня |







