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 |







