Integration of 1C-Bitrix with BelVEB Internet acquiring (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
    1173
  • 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
    745
  • 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 BelVEB Internet Acquiring (Belarus)

BelVEB (Belarusian Bank for Development and Reconstruction) provides internet acquiring for Belarusian online stores. The bank is focused on the corporate segment, and its payment gateway has a number of technical characteristics that distinguish it from more common solutions. Integration with 1C-Bitrix requires developing a custom payment system handler — ready-made modules on the Marketplace are practically absent.

Gateway Technical Parameters

BelVEB provides a payment gateway based on 3DS v2 (EMV 3-D Secure) technology. API — REST/JSON, authentication via a Bearer token obtained through a separate request to /oauth/token.

Main endpoints (test environment: test-api.belveb.by, production: api.belveb.by):

POST /v1/payments          — create a payment
GET  /v1/payments/{id}     — payment status
POST /v1/payments/{id}/capture   — capture (two-stage)
POST /v1/payments/{id}/cancel    — cancel
POST /v1/refunds           — refund

Obtaining an Access Token

class BelvebAuth
{
    private const TOKEN_URL = 'https://api.belveb.by/oauth/token';

    public function getToken(string $clientId, string $clientSecret): string
    {
        $ch = curl_init(self::TOKEN_URL);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => http_build_query([
                'grant_type'    => 'client_credentials',
                'client_id'     => $clientId,
                'client_secret' => $clientSecret,
                'scope'         => 'payments',
            ]),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/x-www-form-urlencoded'],
        ]);
        $result = json_decode(curl_exec($ch), true);
        curl_close($ch);

        return $result['access_token'] ?? throw new \RuntimeException('Token request failed');
    }
}

The token has a limited TTL (typically 1 hour). Cache it in \Bitrix\Main\Data\Cache with TTL minus 60 seconds — otherwise the token may expire mid-processing.

Creating a Payment

public function createPayment(array $data, string $token): array
{
    $payload = [
        'amount'      => [
            'value'    => number_format($data['amount'], 2, '.', ''),
            'currency' => 'BYN',
        ],
        'description' => 'Order #' . $data['orderNumber'],
        'orderId'     => $data['orderNumber'],
        'returnUrl'   => $data['returnUrl'],
        'cancelUrl'   => $data['cancelUrl'],
        'notifyUrl'   => $data['notifyUrl'],
        'capture'     => true,
        'customer'    => [
            'email' => $data['customerEmail'],
        ],
    ];

    $ch = curl_init('https://api.belveb.by/v1/payments');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode($payload),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $token,
            'X-Idempotency-Key: ' . $data['idempotencyKey'],
        ],
    ]);

    $result = json_decode(curl_exec($ch), true);
    curl_close($ch);
    return $result;
}

The X-Idempotency-Key field — a UUID string, unique for each payment creation attempt. Ensures idempotency: a repeated request with the same key returns the same payment without creating a duplicate.

Webhook Notifications

BelVEB signs notifications with HMAC-SHA256. The signing key is issued during onboarding.

public function verifyWebhook(string $body, string $signature, string $secret): bool
{
    $computed = base64_encode(hash_hmac('sha256', $body, $secret, true));
    return hash_equals($computed, $signature);
}

// In the handler:
$body = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_SIGNATURE'] ?? '';

if (!$gateway->verifyWebhook($body, $signature, $webhookSecret)) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);

// Handle only succeeded
if ($event['type'] === 'payment.succeeded') {
    $payment->setPaid('Y');
    $payment->save();
}

Payment statuses:

Status Meaning
pending Created, awaiting payment
processing Processing
succeeded Successfully paid
failed Rejected
cancelled Cancelled
refunded Refunded

Belarus-Specific Notes

BelVEB supports payment by VISA, Mastercard, and Belkart cards. For Belkart cards, 3DS verification works through the national authentication system — it is important to test this scenario separately.

Currency — BYN; amount is transmitted in rubles with two decimal places (not in kopecks, unlike some other gateways). This is a frequent source of errors when porting code from other integrations.

Timeline

Task Duration
Handler development + OAuth authorization 2–3 days
Full cycle testing (payment, refund, webhook) 1 day
Live connection 1 day