1C-Bitrix integration with Magnit installment plan (Belarus)

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

1C-Bitrix Integration with Magnit Instalment Service (Belarus)

"Magnit" is an instalment programme from VTB Bank (Belarus). It is one of the most common consumer credit instruments in the Belarusian market. The buyer arranges instalment payments for 6–36 months and the store receives the amount from VTB. Integration with a 1C-Bitrix website is a standard payment system handler with a redirect to the bank's page.

VTB Belarus Partner API

VTB provides a REST API for online stores. Authentication — Basic Auth (partner login/password) or OAuth 2.0, depending on the connection plan. When requesting a connection, clarify: the authentication type, test environment, and notification format.

Basic scheme:

  1. The store sends POST /api/v1/credits/applications with order and customer data
  2. VTB returns application_id and redirect_url
  3. The customer fills in the application in the VTB interface
  4. VTB sends a webhook with the decision
  5. On approval — the funds are transferred to the store

Payment System Handler

class MagnitVtbHandler extends \Bitrix\Sale\PaySystem\ServiceHandler
{
    public function initiatePay(
        \Bitrix\Sale\Payment $payment,
        \Bitrix\Main\Request $request = null
    ) {
        $order = $payment->getOrder();

        // Available instalment terms (months)
        $availableTerms = array_map('intval', explode(',',
            $this->getBusinessValue($payment, 'AVAILABLE_TERMS') ?? '6,12,24,36'
        ));

        $payload = [
            'partnerOrderId' => 'BX-' . $order->getId(),
            'totalAmount'    => $payment->getSum(),
            'currency'       => 'BYN',
            'terms'          => $availableTerms,
            'goods'          => $this->buildGoodsList($order),
            'client'         => [
                'firstName'   => $order->getPropertyValueByCode('NAME'),
                'lastName'    => $order->getPropertyValueByCode('LAST_NAME'),
                'middleName'  => $order->getPropertyValueByCode('SECOND_NAME'),
                'mobilePhone' => $this->normalizePhone($order->getPropertyValueByCode('PHONE')),
                'email'       => $order->getPropertyValueByCode('EMAIL'),
            ],
            'urls' => [
                'success'  => $this->getSuccessUrl($payment),
                'fail'     => $this->getFailUrl($payment),
                'callback' => $this->getNotificationUrl($payment),
            ],
        ];

        $response = $this->sendRequest('POST', '/api/v1/credits/applications', $payload);

        if (empty($response['redirectUrl'])) {
            $this->setError('VTB Magnit: redirectUrl not received');
            return \Bitrix\Sale\PaySystem\ServiceResult::create();
        }

        $this->saveApplicationId($payment->getField('ID'), $response['applicationId']);

        $result = new \Bitrix\Sale\PaySystem\ServiceResult();
        $result->setPaymentUrl($response['redirectUrl']);
        return $result;
    }

    private function buildGoodsList(\Bitrix\Sale\Order $order): array
    {
        $goods = [];
        foreach ($order->getBasket() as $item) {
            $goods[] = [
                'name'     => mb_substr($item->getField('NAME'), 0, 200),
                'price'    => round($item->getPrice(), 2),
                'quantity' => (int)$item->getQuantity(),
                'article'  => (string)$item->getProductId(),
                'brand'    => $item->getField('DETAIL_PAGE_URL'), // or from product property
            ];
        }
        return $goods;
    }

    private function normalizePhone(string $phone): string
    {
        $digits = preg_replace('/\D/', '', $phone);
        // Belarusian format: +375XXXXXXXXX
        if (strlen($digits) === 11 && $digits[0] === '8') {
            $digits = '375' . substr($digits, 1);
        }
        return '+' . ltrim($digits, '+');
    }
}

Webhook from VTB

VTB sends a POST to callback_url when the application status changes. Verification via HMAC signature or IP whitelist (VTB IP ranges):

public function processRequest(\Bitrix\Sale\Payment $payment, \Bitrix\Main\Request $request)
{
    $allowedIps = ['194.XXX.XXX.0/24']; // VTB Belarus IP range
    if (!$this->isAllowedIp($request->getServer()->get('REMOTE_ADDR'), $allowedIps)) {
        http_response_code(403);
        exit;
    }

    $data   = json_decode(file_get_contents('php://input'), true);
    $result = new \Bitrix\Sale\PaySystem\ServiceResult();

    switch ($data['status'] ?? '') {
        case 'APPROVED':
        case 'ISSUED':  // Credit issued, funds received by the store
            $result->setOperationType(\Bitrix\Sale\PaySystem\ServiceResult::MONEY_COMING);
            $payment->setPaid('Y');
            break;
        case 'DECLINED':
            // Application declined — notify the customer
            \Bitrix\Main\Mail\Event::send([
                'EVENT_NAME' => 'INSTALLMENT_DECLINED',
                'LID'        => SITE_ID,
                'C_FIELDS'   => ['ORDER_ID' => $payment->getOrderId()],
            ]);
            break;
    }

    http_response_code(200);
    echo json_encode(['status' => 'ok']);
    return $result;
}

"Calculate Instalment" Widget on the Site

On the product page and in the cart we show a monthly payment calculator:

function calcMagnitInstallment(price) {
    const terms = [6, 12, 24, 36];
    const container = document.getElementById('magnit-installment');
    container.innerHTML = terms.map(t =>
        `<div class="term-option">
            <span>${t} mo.</span>
            <strong>${Math.ceil(price / t)} /mo.</strong>
        </div>`
    ).join('');
}

Timeline

Phase Duration
API connection setup and test environment 1 day
Payment system handler 2–3 days
Webhook and status handling 1–2 days
Refunds via API 1 day
Instalment calculator widget 0.5 days
Testing 2 days
Total 8–10 days