1C-Bitrix integration with installment plan Shopping Card (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
    1183
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    813
  • 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
    657
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Integration of 1C-Bitrix with the Karta Pokupok Installment Service (Belarus)

"Karta Pokupok" is an installment program from Priorbank (Belarus). The customer pays for a purchase in interest-free installments; the store receives the full amount. The workflow is similar to other installment cards, but the Priorbank API has its own specifics: authentication via SSL certificate and separate documentation for eCommerce partners.

Priorbank API Specifics

Priorbank uses mutual TLS for partner connections. This means: in addition to verifying the bank's server certificate, the bank verifies the client certificate (your server). Upon becoming a partner, you receive:

  • a client SSL certificate (.crt)
  • a private key (.key)
  • the bank's CA certificate (for server verification)

cURL configuration for requests with a client certificate:

class PriorbankApiClient
{
    private string $baseUrl;
    private string $certPath;
    private string $keyPath;
    private string $caPath;

    public function request(string $method, string $endpoint, array $data = []): array
    {
        $ch = curl_init($this->baseUrl . $endpoint);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSLCERT        => $this->certPath,
            CURLOPT_SSLKEY         => $this->keyPath,
            CURLOPT_CAINFO         => $this->caPath,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_CUSTOMREQUEST  => strtoupper($method),
            CURLOPT_POSTFIELDS     => json_encode($data),
            CURLOPT_HTTPHEADER     => [
                'Content-Type: application/json',
                'Accept: application/json',
            ],
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode >= 400) {
            throw new \RuntimeException("Priorbank API: HTTP {$httpCode}: {$response}");
        }

        return json_decode($response, true);
    }
}

Certificates are stored outside the web server document root; their paths are configured in the module options.

Creating a Payment Session

public function initiatePay(\Bitrix\Sale\Payment $payment, \Bitrix\Main\Request $request = null)
{
    $order = $payment->getOrder();
    $basket = $order->getBasket();
    $items = [];

    foreach ($basket as $item) {
        $items[] = [
            'name'     => $item->getField('NAME'),
            'quantity' => $item->getQuantity(),
            'price'    => $item->getPrice(),
            'sku'      => $item->getProductId(),
        ];
    }

    $payload = [
        'partner_id'       => $this->getBusinessValue($payment, 'PARTNER_ID'),
        'order_ref'        => (string)$order->getId(),
        'amount'           => $payment->getSum(),
        'currency'         => 'BYN',
        'term'             => (int)$this->getBusinessValue($payment, 'INSTALLMENT_TERM'), // months
        'items'            => $items,
        'customer'         => [
            'first_name' => $order->getPropertyValueByCode('NAME'),
            'last_name'  => $order->getPropertyValueByCode('LAST_NAME'),
            'phone'      => $order->getPropertyValueByCode('PHONE'),
            'email'      => $order->getPropertyValueByCode('EMAIL'),
        ],
        'success_url'      => $this->getSuccessUrl($payment),
        'fail_url'         => $this->getFailUrl($payment),
        'callback_url'     => $this->getNotificationUrl($payment),
    ];

    $response = $this->apiClient->request('POST', '/installment/create', $payload);

    if (empty($response['payment_url'])) {
        throw new \RuntimeException('No payment_url received from Priorbank');
    }

    $this->storeSessionId($payment, $response['session_id']);

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

Status Model

Bank status Meaning Action in Bitrix
APPROVED Installment approved and activated $payment->setPaid('Y')
PENDING Awaiting customer confirmation Wait
REJECTED Bank declined the application Notify customer
CANCELLED Customer cancelled Notify customer
REFUNDED Refund processed refund() in Bitrix

The bank's callback is verified by comparing signatures: the bank signs the request body with its private key; we verify it with the bank's public key (included in the API documentation package).

Refunds

When a refund is processed in Bitrix (event OnSalePaymentEntitySaved when PAID = N for a previously paid payment), send a refund request to Priorbank:

public function refund(\Bitrix\Sale\Payment $payment, $refundableSum)
{
    $sessionId = $this->getStoredSessionId($payment);
    $response  = $this->apiClient->request('POST', '/installment/refund', [
        'session_id' => $sessionId,
        'amount'     => $refundableSum,
        'reason'     => 'customer_request',
    ]);
    return !empty($response['refund_id']);
}

Displaying Installments on the Site

On the product page and in the cart, add an installment widget: for an order totaling N BYN — "Pay in installments from X BYN/month". Frontend calculation:

const months = 12; // from settings
const monthlyPayment = Math.ceil(totalPrice / months);
document.getElementById('installment-badge').textContent =
    `Installments from ${monthlyPayment} BYN/month × ${months} months`;

Timeline

Phase Duration
SSL certificate setup and test environment 1 day
API client with mutual TLS 1 day
Payment system handler 2–3 days
Callback and signature verification 1–2 days
Refunds 1 day
Testing 2 days
Total 9–11 days