Інтеграція системи ЕРИП на сайт
ЕРИП (Єдине розрахункове та інформаційне простір) — державна платіжна система Беларуси. Дозволяє приймати платежи через інтернет-банкінг, банкомати, платіжні термінали, мобільні додатки банків. Охоплення — більше 99% білоруських банків та платіжних терміналів. Для більшості білоруських сервісів інтеграція ЕРИП обов'язкова: значна частина користувачів переважає платити саме через ЕРИП, особливо через СДБО Сбербанку та Беларусбанку.
Архітектура ЕРИП
Є два шляхи підключення:
- Через банк-агент — банк (Беларусбанк, Приорбанк, БСБ Банк та ін.) надає шлюз до ЕРИП. Договір з одним банком, API банку.
- Прямо через ОСМП/ФТОС — потребує окремого договору з оператором ЕРИП, складніша інфраструктура.
Для більшості проектів вибирається шлях через банк-агент. Розглянемо інтеграцію через найпоширеніший варіант — БСБ Банк (API WSCP) та Беларусбанк (webservice).
Концепція роботи
ЕРИП працює в режимі Pull: це не redirect-схема. Покупець вводит ваш номер послуги в ЕРИП (наприклад, «Інтернет-магазини → ВашМагазин → Введіть номер замовлення»). ЕРИП запитує у вашого сервера інформацію про замовлення (Check-запит), потім після оплати відправляє сповіщення (Pay-запит).
Ваш сервер повинен реалізувати обробник XML-запитів від ЕРИП.
Реалізація обробника
class EripController extends Controller
{
public function handle(Request $request): Response
{
$xml = simplexml_load_string($request->getContent());
$command = (string)$xml->command['type'];
return match ($command) {
'check' => $this->check($xml),
'pay' => $this->pay($xml),
default => $this->error('Unknown command'),
};
}
private function check(\SimpleXMLElement $xml): Response
{
$accountNo = (string)$xml->customer->account;
$order = Order::where('erip_account', $accountNo)
->where('status', 'pending')
->first();
if (!$order) {
return $this->xmlResponse(200, 'Послуга не знайдена');
}
$responseXml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<response>
<result>
<errorCode>0</errorCode>
<errorDescription>Успішно</errorDescription>
</result>
<customer>
<account>{$accountNo}</account>
</customer>
<fields>
<field tag="250" tagName="Сума до оплаты" value="{$order->total}"/>
<field tag="251" tagName="Номер замовлення" value="{$order->id}"/>
</fields>
<amount>{$order->total}</amount>
<currency>933</currency>
<info>Замовлення #{$order->id} від {$order->created_at->format('d.m.Y')}</info>
</response>
XML;
return response($responseXml, 200)
->header('Content-Type', 'application/xml; charset=utf-8');
}
private function pay(\SimpleXMLElement $xml): Response
{
$accountNo = (string)$xml->customer->account;
$transId = (string)$xml->transaction['id'];
$amount = (float)$xml->transaction->amount;
$paymentDate = (string)$xml->transaction['date'];
$order = Order::where('erip_account', $accountNo)
->where('status', 'pending')
->lockForUpdate()
->first();
if (!$order) {
return $this->xmlResponse(100, 'Рахунок не знайдено');
}
if (abs($amount - $order->total) > 0.01) {
return $this->xmlResponse(200, 'Невірна сума');
}
// Ідемпотентність — не обробляти повторно
if (EripTransaction::where('transaction_id', $transId)->exists()) {
return $this->xmlResponse(0, 'Вже оброблено');
}
DB::transaction(function () use ($order, $transId, $amount, $paymentDate) {
EripTransaction::create([
'transaction_id' => $transId,
'order_id' => $order->id,
'amount' => $amount,
'paid_at' => $paymentDate,
]);
$order->update(['status' => 'paid']);
});
return $this->xmlResponse(0, 'Прийнято');
}
private function xmlResponse(int $code, string $message): Response
{
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<response>
<result>
<errorCode>{$code}</errorCode>
<errorDescription>{$message}</errorDescription>
</result>
</response>
XML;
return response($xml, 200)
->header('Content-Type', 'application/xml; charset=utf-8');
}
}
Номер лицевого рахунку ЕРИП
Кожному замовленню присвоюється унікальний номер лицевого рахунку. Звичайно це сам ID замовлення, але деякі банки вимагають певного формату (наприклад, 15-значний номер). Цей номер відображається покупцю та вводиться у терміналі ЕРИП.
Номер рахунку потрібно сгенерувати заранее та показати покупцю на сторінці оформлення:
// Генерація номера ЕРИП-рахунку
$order->erip_account = str_pad($order->id, 12, '0', STR_PAD_LEFT);
QR-код для ЕРИП
Для спрощення оплати через мобільні додатки можна генерувати QR-код з даними ЕРИП. Формат QR визначається банком-агентом; звичайно це глибока посилання вида:
erip://pay?service_code=XXXXX&account=000000012345
use Endroid\QrCode\QrCode;
$qrCode = QrCode::create("erip://pay?service_code={$serviceCode}&account={$order->erip_account}")
->setSize(200);
Налаштування в дереві послуг ЕРИП
Ваш сервіс повинен бути зареєстрований у дереві послуг ЕРИП — це робиться через банк-агент. Процедура включає: заповнення заявки, узгодження місця в дереві (наприклад, «Інтернет-магазини → [Регіон] → Назва магазину»), тестування. Термін реєстрації та тестування — від 5 до 15 робочих днів. Зміна позиції в дереві після реєстрації — окрема заявка та ще 5–10 днів.
Безпека
Запити від ЕРИП потрібно приймати тільки з IP-адрес банку-агента. Список IP надає банк. Додатково — HTTPS з взаємною аутентифікацією (mutual TLS) або HMAC-підпис залежно від банку.
// Middleware для перевірки IP
public function handle(Request $request, Closure $next): Response
{
$allowedIps = explode(',', env('ERIP_ALLOWED_IPS'));
if (!in_array($request->ip(), $allowedIps)) {
return response('Forbidden', 403);
}
return $next($request);
}







