Turnkey 1C-Bitrix Return Processing Automation

Manual return processing takes up managers' time and risks errors when refunding money. We automate the full return cycle in 1C-Bitrix: from changing the request status to processing the payment and notifying the customer. Our team delivers the project turnkey — with 1C and payment system integration, event model setup, and ongoing support.

Our competencies:

Frequently Asked Questions

Setting Up Automatic Return Processing in 1C-Bitrix

Return automation is needed where the volume exceeds 20–30 requests per day and the manager spends significant time on routine operations: checking order payment, creating a return in 1C, processing the refund, sending an email to the customer.

Each of these actions can be triggered automatically when the request status changes — through the sale module event handlers and Bitrix agents.

Our team, over 8 years of working with the platform, has implemented more than 40 return automations for online stores of various scales.

We provide full integration with 1C and payment gateways, which reduces the average processing time of a single request from 15 minutes to 2 minutes.

The savings for a store with a flow of 50 returns per day amount to about 10 hours of manager work per week — this frees up resources for more important tasks. Automation also eliminates human errors in refund processing and working with 1C.

Return Event Model

The sale module generates events when working with returns. Key points for automation:

  • OnSaleOrderReturnSaved — creating or updating a request
  • OnSaleOrderReturnStatusChange — status change
  • OnSalePaymentCollectionReturnAdd — adding a payment return

Register handlers in /local/php_interface/init.php:

$eventManager = \Bitrix\Main\EventManager::getInstance();
$eventManager->addEventHandler('sale', 'OnSaleOrderReturnSaved', [\Local\Returns\AutoProcessor::class, 'onReturnSaved']);
$eventManager->addEventHandler('sale', 'OnSaleOrderReturnStatusChange', [\Local\Returns\AutoProcessor::class, 'onStatusChange']);

Automatic actions when creating a return request

namespace Local\Returns;

class AutoProcessor
{
    public static function onReturnSaved(\Bitrix\Main\Event $event): void
    {
        $return = $event->getParameter('ENTITY');
        if (!$return instanceof \Bitrix\Sale\OrderReturn) return;

// Only on creation (not on update) if (!$event->getParameter('IS_NEW')) return; $returnId = $return->getId(); // 1. Assign a responsible person by rotation self::assignResponsible($returnId);

        // 2. Send confirmation to the customer
        Notifications::sendToCustomer($returnId, 'RETURN_CREATED');

// 3. Notify the manager Notifications::sendToManager($returnId, 'NEW_RETURN'); // 4. If the return amount is small — auto-approve self::tryAutoApprove($return); }

    public static function onStatusChange(\Bitrix\Main\Event $event): void
    {
        $returnId = $event->getParameter('RETURN_ID');
        $newStatus = $event->getParameter('NEW_STATUS_ID');
        $oldStatus = $event->getParameter('OLD_STATUS_ID');

        $handler = new self();
        $handler->handleStatusTransition($returnId, $oldStatus, $newStatus);
    }

    private function handleStatusTransition(int $returnId, string $from, string $to): void
    {
        match ($to) {
            'APPROVED' => $this->onApproved($returnId),
            'RECEIVED' => $this->onReceived($returnId),
            'REJECTED' => $this->onRejected($returnId),
            'REFUND' => $this->onRefunded($returnId),
            default => null,
        };
    }
}

Auto-approval of small returns

Returns below a certain amount (configurable threshold) do not require manual review:

private static function tryAutoApprove(\Bitrix\Sale\OrderReturn $return): void
{
    $autoApproveLimit = (float)\Bitrix\Main\Config\Option::get(
        'local.returns',
        'auto_approve_limit',
        500
    );
    $returnAmount = (float)$return->getField('REFUND_AMOUNT');
    if ($returnAmount <= 0 || $returnAmount > $autoApproveLimit) {
        return;
    }
    // Check: customer has no history of disputed returns
    $userId = $return->getOrder()->getUserId();
    if (self::hasDisputeHistory($userId)) {
        return;
    }
    // Automatically change status to APPROVED
    $return->setField('STATUS_ID', 'APPROVED');
    $return->setField('MANAGER_COMMENT', 'Auto-approved: amount up to ' . $autoApproveLimit . ' RUB');
    $result = $return->save();
    if ($result->isSuccess()) {
        \CEventLog::Add([
            'SEVERITY' => 'INFO',
            'AUDIT_TYPE_ID' => 'RETURN_AUTO_APPROVED',
            'MODULE_ID' => 'local.returns',
            'DESCRIPTION' => "Return #{$return->getId()} auto-approved, amount: {$returnAmount}",
        ]);
    }
}

private static function hasDisputeHistory(int $userId): bool
{
    // Count rejected returns over the last 6 months
    $dateFrom = new \Bitrix\Main\Type\Date();
    $dateFrom->add('-180 days');
    $result = \Bitrix\Sale\OrderReturnTable::getList([
        'filter' => [
            '=ORDER.USER_ID' => $userId,
            'STATUS_ID' => 'REJECTED',
            '>=DATE_INSERT' => $dateFrom,
        ],
        'select' => ['ID'],
    ]);
    return (bool)$result->fetch();
}

Automatic refund upon approval

When transitioning to the "Approved" status — we automatically initiate a refund via the payment system API:

private function onApproved(int $returnId): void {
    $return = \Bitrix\Sale\OrderReturn::loadById($returnId);
    if (!$return) return;

$order = $return->getOrder(); $payments = $order->getPaymentCollection(); foreach ($payments as $payment) { if (!$payment->isPaid()) continue;

        $psHandler = \Bitrix\Sale\PaySystem\Manager::getObjectById(
            $payment->getPaymentSystemId()
        );
        if (!$psHandler) continue;

        $supportsRefund = method_exists($psHandler, 'refund');
        if (!$supportsRefund) {
            // The payment system does not support auto refund — queue it for manual processing
            $this->flagForManualRefund($returnId, 'PS does not support auto refund');
            continue;
        }

        $refundAmount = (float)$return->getField('REFUND_AMOUNT');
        $result = $psHandler->refund($payment, $refundAmount);

        if ($result->isSuccess()) {
            $return->setField('STATUS_ID', 'REFUND');
            $return->setField('REFUND_DATE', new \Bitrix\Main\Type\DateTime());
            $return->save();
            Notifications::sendToCustomer($returnId, 'RETURN_REFUNDED');
        } else {
            $this->flagForManualRefund($returnId, implode('; ', $result->getErrorMessages()));
        }
    }
}

Automatic verification of goods receipt via tracking

If the buyer sends the item back using a tracking number, delivery can be tracked automatically:

class TrackingWatcher { // Agent launched every 2 hours
    public static function checkPendingReturns(): string {
        $returns = \Bitrix\Sale\OrderReturnTable::getList([
            'filter' => [
                'STATUS_ID' => 'APPROVED',
                'UF_TRACKING' => ['!=', '', false], // requests with a tracking number
            ],
            'select' => ['ID', 'UF_TRACKING', 'UF_CARRIER'],
        ]);
        $checker = new \Local\Delivery\TrackingChecker();
        while ($row = $returns->fetch()) {
            $status = $checker->getStatus($row['UF_TRACKING'], $row['UF_CARRIER'] ?? 'pochta');
            if ($status === 'delivered') {
                $return = \Bitrix\Sale\OrderReturn::loadById($row['ID']);
                $return->setField('STATUS_ID', 'RECEIVED');
                $return->save();
            }
        }
        return 'checkPendingReturns();'; // restart the agent
    }
}

Escalation of overdue requests

Agent for return SLA monitoring:

class SlaWatcher {
    private const SLA_HOURS = [
        'WAIT' => 24, // review within 24 hours
        'REVIEW' => 48, // make a decision within 48 hours
    ];

    public static function checkOverdue(): string {
        foreach (self::SLA_HOURS as $statusId => $maxHours) {
            $deadline = new \Bitrix\Main\Type\DateTime();
            $deadline->add('-' . $maxHours . ' hours');

            $overdueReturns = \Bitrix\Sale\OrderReturnTable::getList([
                'filter' => [
                    'STATUS_ID' => $statusId,
                    '<=DATE_STATUS' => $deadline,
                ],
                'select' => ['ID', 'RESPONSIBLE_ID', 'ORDER_ID'],
            ]);

            while ($row = $overdueReturns->fetch()) {
                Notifications::escalate($row['ID'], $row['RESPONSIBLE_ID'], $statusId, $maxHours);
            }
        }

        return 'checkOverdue();';
    }
}

Scope of work

  • Event handlers: OnSaleOrderReturnSaved, OnSaleOrderReturnStatusChange
  • Auto-approval logic based on amount and customer history
  • Automatic refunds via payment system APIs (YuKassa, Tinkoff, etc.)
  • Inbound parcel tracking agent (Russian Post, CDEK, Boxberry)
  • SLA monitoring agent with escalation of overdue requests
  • Email automation: templates for each status

Timelines: basic automation (notifications + automatic refunds) — 2–3 weeks. Full stack with SLA monitoring and tracking — 4–6 weeks.

Detailed process description

Integration requires careful planning and analysis of the current processes in your company. We audit existing systems, identify integration points and possible conflicts.

At every stage, we provide detailed documentation and perform testing in a staging environment before launching in production. Our solutions are designed with industry best practices in mind and can significantly reduce operational costs.

Our quality guarantee

We take full responsibility for the quality of the work performed. Within 12 months after project completion, we fix any issues identified completely free of charge. Technical support includes consultations, training for your team, and assistance with any questions that arise.

Every project concludes with a final report describing the implemented functionality, optimization recommendations, and a system development plan.

Examples of successful projects

Over the course of our work, we have implemented integrations for companies of various sizes: from small online stores to large wholesale distributors.

Each project required an individual approach, and we are proud of the result, which significantly improved the operational efficiency of our clients.

Advantages of our approach

We provide a comprehensive solution, not isolated fixes. Every project includes documentation, testing, and team training.

Our clients appreciate that we don't just do the work, but also explain every step of the process, enabling your team to maintain the system independently in the future. For our part, we guarantee support for a year after the project is completed.

Contacts and next steps

If your company is facing the described problem, get in touch with us. We will analyze your system and propose the optimal solution.

The project cost depends on the complexity and scope of work, but for typical solutions we always provide an accurate estimate based on a preliminary requirements analysis.

Technologies and tools

In our work we use a modern stack: D7 event handlers, Bitrix ORM for database operations, caching via the main module, integrations with external APIs via the Guzzle HTTP client.

For payment gateways we use official SDKs from banks and payment systems — this guarantees the stability and security of refund operations. Testing is carried out on a staging environment with a full copy of production data before rolling out changes to the live environment.