Інтеграція системи ЄРИП на сайт

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Інтеграція системи ЄРИП на сайт
Складна
~2-3 робочих дні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Інтеграція системи ЕРИП на сайт

ЕРИП (Єдине розрахункове та інформаційне простір) — державна платіжна система Беларуси. Дозволяє приймати платежи через інтернет-банкінг, банкомати, платіжні термінали, мобільні додатки банків. Охоплення — більше 99% білоруських банків та платіжних терміналів. Для більшості білоруських сервісів інтеграція ЕРИП обов'язкова: значна частина користувачів переважає платити саме через ЕРИП, особливо через СДБО Сбербанку та Беларусбанку.

Архітектура ЕРИП

Є два шляхи підключення:

  1. Через банк-агент — банк (Беларусбанк, Приорбанк, БСБ Банк та ін.) надає шлюз до ЕРИП. Договір з одним банком, API банку.
  2. Прямо через ОСМП/ФТОС — потребує окремого договору з оператором ЕРИП, складніша інфраструктура.

Для більшості проектів вибирається шлях через банк-агент. Розглянемо інтеграцію через найпоширеніший варіант — БСБ Банк (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);
}