1C-Bitrix Integration with the YooKassa Payment System
YooKassa (formerly Yandex.Kassa) is one of the most widely used payment aggregators in Russian e-commerce. Behind the apparent simplicity of "install a module from the marketplace" lie several technical nuances: handling notifications, fiscal receipts under Federal Law 54-FZ, refund processing, and selecting the correct payment confirmation mode. Let's go through everything in order.
How YooKassa Works Technically
YooKassa provides a REST API (api.yookassa.ru/v3/). The classic payment flow:
- The store sends
POST /paymentswith the amount, currency, andconfirmation.type=redirect— YooKassa returnspayment.idandconfirmation.confirmation_url - The buyer is redirected to the YooKassa payment page
- After payment, YooKassa sends a webhook notification to the store's
return_urland aPOSTto the configured notification URL - The store calls
GET /payments/{id}for a final status check
Authentication — HTTP Basic: shopId:secretKey or an OAuth token.
Supported payment methods: bank cards, SBP, YooMoney, SberPay, Tinkoff, QIWI (with limitations), cash via terminals. The method is passed in payment_method_type or selected by the buyer on the YooKassa form.
Integration Options in 1C-Bitrix
Official module. YooKassa provides an official Bitrix module, available for free via the Marketplace (yoomoney.cms.bitrix) and on GitHub. Installed via /bitrix/admin/update_system.php, configured under Shop → Settings → Payment Systems. The module uses the official PHP library yoomoney/yookassa-sdk-php.
Custom handler is required when: non-standard order status logic is needed, the project does not use the standard sale module, or custom fiscalization is required. Placed in /local/php_interface/include/sale_payment/yookassa_custom/.
Key Request Parameters
// Minimal payment creation request via SDK
use YooKassa\Client;
$client = new Client();
$client->setAuth($shopId, $secretKey);
$payment = $client->createPayment([
'amount' => [
'value' => number_format($order->getPrice(), 2, '.', ''),
'currency' => 'RUB',
],
'confirmation' => [
'type' => 'redirect',
'return_url' => 'https://shop.ru/personal/order/detail/' . $order->getId() . '/',
],
'capture' => true, // false for two-stage payments
'description' => 'Order #' . $order->getAccountNumber(),
'metadata' => ['bitrix_order_id' => $order->getId()],
'receipt' => $receiptData, // required when a fiscal register is connected
], uniqid('', true)); // idempotency key
capture: true — single-stage payment, funds are charged immediately. capture: false — two-stage: YooKassa holds the amount, the store calls POST /payments/{id}/capture upon shipment.
Idempotency key (third parameter) — mandatory. Without it, a repeated request on a network error will create a duplicate payment.
Fiscalization (Federal Law 54-FZ)
If receipt transmission is enabled in the YooKassa contract, the receipt object in the request becomes mandatory. Without it the transaction will be rejected. Structure:
$receiptData = [
'customer' => [
'email' => $buyer->getEmail(),
'phone' => $buyer->getPersonalPhone(), // at least one of the two
],
'items' => [],
];
foreach ($basket->getOrderableItems() as $item) {
$receiptData['items'][] = [
'description' => $item->getField('NAME'),
'quantity' => $item->getQuantity(),
'amount' => [
'value' => number_format($item->getPrice(), 2, '.', ''),
'currency' => 'RUB',
],
'vat_code' => 1, // 1=no VAT, 2=0%, 3=10%, 4=20%
'payment_subject' => 'commodity', // item type indicator
'payment_mode' => 'full_payment', // payment method indicator
];
}
// Don't forget delivery as a separate line item
if ($delivery->getPrice() > 0) {
$receiptData['items'][] = [
'description' => 'Delivery',
'quantity' => 1,
'amount' => ['value' => number_format($delivery->getPrice(), 2, '.', ''), 'currency' => 'RUB'],
'vat_code' => 1,
'payment_subject' => 'service',
'payment_mode' => 'full_payment',
];
}
The total of all line items in the receipt must exactly match the payment amount — YooKassa validates this and rejects any discrepancy.
Handling Webhook Notifications
YooKassa sends a POST to the configured URL on every payment status change. The standard handler URL in Bitrix: /bitrix/tools/sale_ps_result.php.
Critical points when processing:
// 1. Verify the source IP address (YooKassa publishes its IP list)
$allowedIPs = ['185.71.76.0/27', '185.71.77.0/27', '77.75.153.0/25', '77.75.156.11', '77.75.156.35'];
// 2. Read the request body
$body = file_get_contents('php://input');
$notification = json_decode($body, true);
// 3. Verify via API (do not trust webhook data directly)
$payment = $client->getPaymentInfo($notification['object']['id']);
// 4. Handle only terminal statuses
switch ($payment->getStatus()) {
case 'succeeded':
$bitrixPayment->setPaid('Y');
$bitrixPayment->save();
break;
case 'canceled':
// Log the cancellation reason from $payment->getCancellationDetails()
break;
}
YooKassa payment status table:
| Status | Meaning | Action |
|---|---|---|
pending |
Awaiting buyer action | Do nothing |
waiting_for_capture |
Awaiting store confirmation | Call capture or cancel |
succeeded |
Paid | Confirm in Bitrix |
canceled |
Cancelled | Analyze cancellation_details |
Refunds
YooKassa supports partial and full refunds via POST /refunds:
$refund = $client->createRefund([
'payment_id' => $externalPaymentId,
'amount' => ['value' => $refundAmount, 'currency' => 'RUB'],
'description' => 'Refund for order #' . $orderNumber,
'receipt' => $refundReceiptData, // required when fiscal register is connected
], uniqid('', true));
When a fiscal register is connected, a refund without a receipt is rejected. The refund receipt mirrors the line items of the original receipt with type refund.
Testing
YooKassa provides a test environment with the same endpoints. In test mode (shopId=test_...) payments proceed with test cards:
-
5555555555554477— successful payment -
5555555555554444— decline
Be sure to test: successful payment, cancellation, webhook with a delay (buyer closed the browser before redirect), and partial refund.
Timeline
| Configuration | Duration |
|---|---|
| Ready-made module, no fiscal register | 1–2 days |
| Ready-made module + 54-FZ fiscal | 2–4 days |
| Custom handler + fiscal register + two-stage payments | 5–8 days |







