Full Refund on 1C-Bitrix: API, Receipts, and 54-FZ Compliance

Refunds in 1C-Bitrix often turn into chaos: stuck statuses, receipt errors, and fines for violating 54-FZ. We automate the refund process via APIs of popular acquirers — Tinkoff, YooKassa, and Sberbank — and implement it turnkey, from setup to support. Our team ensures stable system operation so refunds are processed in one click and without failures.

Our competencies:

Frequently Asked Questions

Refunds are an operation that often causes errors in Bitrix: stuck statuses, duplicate receipts, violations of 54-FZ. Especially when the acquirer's deadlines have passed, but the client demands their money back.

Over 10 years, we have carried out more than 50 acquirer integrations and encountered all the typical problems: an incorrect token, the wrong amount format, a missing refund receipt. Developers often forget about API limits — this leads to blocking.

In this material, we will look at how to set up automated refunds without manual work and fines, using Tinkoff, YooKassa, and Sberbank as examples.

We design the logic taking into account all edge cases: partial refunds, canceling a payment before confirmation, refunding the acquirer's commission. Our goal is for the refund to be completed in one click for the manager and without errors on the payment system's side. Get a consultation on refund automation.

Time limits on refunds

Different acquirers have different timeframes within which a refund is technically possible:

Acquirer Refund period Method after the period
Tinkoff Up to 365 days Manual refund via the bank
YooKassa Up to 365 days Via YooKassa support
Sberbank Up to 3 years Via the personal account
CloudPayments Up to 365 days Request to support

If the period has passed, the refund is made manually by bank transfer. The payment system is not involved here.

Typical mistakes when setting up a refund:

  • Incorrect token or request signature.
  • Missing refund receipt (violation of 54-FZ).
  • Exceeding the refund amount limit.
  • Stuck order status due to missing response handling.

Step-by-step return setup instructions

  1. Identify the acquirer and its API. Each has its own protocol and requirements for request signing.
  2. Develop a single wrapper function. It will call the required API by the gateway key.
  3. Configure limit checks. At Tinkoff, for example, the maximum refund amount cannot exceed the original amount.
  4. Connect fiscalization. Generate a refund receipt with the same items as in the original receipt.
  5. Test all scenarios: full refund, partial refund, cancellation before confirmation, and error handling.

Automation reduces refund processing time from 2 hours per day (for 100 refunds) to 5 minutes — saving up to 96% of the manager's time.

How to set up a full refund via API?

Instead of three separate scripts, we use a wrapper that calls the required API depending on the acquirer. This makes the code easier to maintain and extend.

function processRefund(string $gateway, int $paymentId, string $externalId, ?float $amount = null): void {
    switch ($gateway) {
        case 'tinkoff':
            $params = [
                'TerminalKey' => TINKOFF_TERMINAL,
                'PaymentId' => $externalId,
            ];
            $params['Token'] = tinkoffSign($params, TINKOFF_SECRET);
            $result = tinkoffPost('/v2/Cancel', $params);
            if ($result['Status'] === 'REFUNDED') {
                setPaymentRefunded($paymentId);
            }
            break;
        case 'yookassa':
            $refund = $client->createRefund([
                'payment_id' => $externalId,
                'amount' => [
                    'value' => number_format($amount, 2, '.', ''),
                    'currency' => 'USD',
                ],
                'receipt' => buildRefundReceipt($order),
            ], uniqid('', true));
            if ($refund->getStatus() === 'succeeded') {
                setPaymentRefunded($paymentId);
            }
            break;
        case 'sberbank':
            $params = [
                'userName' => SBER_LOGIN,
                'password' => SBER_PASSWORD,
                'orderId' => $externalId,
                'amount' => (int)($amount * 100),
            ];
            $response = file_get_contents(
                'https://securepayments.sberbank.ru/payment/rest/refund.do?' . http_build_query($params)
            );
            $result = json_decode($response, true);
            if ($result['errorCode'] === '0') {
                setPaymentRefunded($paymentId);
            }
            break;
    }
}

Why is it important to update the order status in Bitrix?

Without a correct status update, the order will remain paid. This leads to a repeat shipment or incorrect accounting. We use the standard API Bitrix\Sale\Payment:

function setPaymentRefunded(int $paymentId): void {
    $payment = Bitrix\Sale\Payment::loadById($paymentId);
    if (!$payment) return;
    $payment->setPaid('N');
    $payment->setField('PS_STATUS', 'Y');
    $payment->setField('PS_STATUS_CODE', 'refunded');
    $payment->setField('PS_STATUS_MESSAGE', 'Refund from ' . date('d.m.Y H:i'));
    $payment->save();
    $order = $payment->getCollection()->getOrder();
    // Optional: move the order to the "Refund" status
    $order->setField('STATUS_ID', 'RF');
    $order->save();
    sendRefundNotification($order, $payment->getSum());
}

What is included in the refund setup?

When ordering refund setup for 1C-Bitrix, we provide:

  • Analysis of the current acquirer integration and payment scheme.
  • Development of a unified API wrapper for all supported payment systems.
  • Setup of refund receipt generation in accordance with Federal Law No. 54-FZ.
  • Integration with online cash registers (ATOL, Shtrikh-M, etc.) for automatic receipt sending.
  • Updating order statuses and sending notifications to customers.
  • Optionally — adding a "Refund payment" button to the administrative panel.
  • Testing of all scenarios, including partial refunds and error handling.
  • Documentation for the new functionality.

Timeline — from 0.5 to 3 days turnkey. We will assess your project and get back with a cost calculated after audit. Order turnkey refund setup.

How to correctly generate a refund receipt?

If the cash register is connected, the API will reject the request without a refund receipt. The receipt mirrors the original one. More details on the requirements of Federal Law No. 54-FZ.

function buildRefundReceipt(Bitrix\Sale\Order $order): array {
    $receipt = [
        'customer' => ['email' => getBuyerEmail($order)],
        'items' => []
    ];

    foreach ($order->getBasket() as $item) {
        $receipt['items'][] = [
            'description' => $item->getField('NAME'),
            'quantity' => $item->getQuantity(),
            'amount' => [
                'value' => number_format($item->getPrice(), 2, '.', ''),
                'currency' => 'USD',
            ],
            'vat_code' => getItemVatCode($item),
            'payment_subject' => 'commodity',
            'payment_mode' => 'full_payment',
        ];
    }

    // Delivery as a separate line item
    $deliveryPrice = $order->getDeliveryPrice();
    if ($deliveryPrice > 0) {
        $receipt['items'][] = [
            'description' => 'Delivery',
            'quantity' => 1,
            'amount' => [
                'value' => number_format($deliveryPrice, 2, '.', ''),
                'currency' => 'USD'
            ],
            'vat_code' => 1,
            'payment_subject' => 'service',
            'payment_mode' => 'full_payment',
        ];
    }

    return $receipt;
}

Partial refunds and error handling

Partial refunds are a common need when canceling multiple items. For YooKassa, this is a standard feature: the refund amount is passed in the amount field. For Tinkoff, only a full refund of a confirmed payment is possible. With partial refunds, it is important to check limits and adjust the remaining amount. Error handling: each API returns different codes. In our function, we log every response and, in case of failure, send a notification to the administrator. Caching API requests by payment key prevents duplicate charges. We also add a check for an existing refund to avoid double operations.

Manager Interface

The standard Bitrix interface allows initiating a refund through the order card. If not — we add a "Refund payment" button via a custom action in the administrative part:

// local/templates/admin/sale_order_detail/refund_button.php
if ($order->isPaid() && $order->getField('STATUS_ID') !== 'RF') {
    echo '<a href="/local/admin/refund.php?order_id=' . $order->getId() . ' class="adm-btn adm-btn-red">Refund payment</a>';
}

Setup Timeline

Task Timeline
Full refund without fiscalization 0.5–1 day
Full refund + refund receipt (54-FZ) 1–3 days
Interface in the admin panel 0.5–1 day

Automating refunds via API is 10 times faster than manually entering data in your bank's personal account. Contact us to discuss setting up refunds for your project. Get a consultation on integrating payment acquirers with automatic fiscalization. Our experience — over 10 years of working with Bitrix and over 50 integrations with payment systems. We guarantee compliance with 54-FZ and full automation of the process. Order automatic refund setup for your Bitrix project. Our engineers will integrate any acquirer with full fiscalization.