Manual return processing is a bottleneck for an e-commerce store on 1C-Bitrix with 100+ orders per day. Managers confuse statuses, make errors in amounts, and applications get lost. From our practice: one client with 500 orders per day spent up to 10 days on returns, with an error rate of 30%. After implementing our system, processing time dropped to 1 day and operating expenses decreased by 1.5 million rubles per year. Turnkey automation solves these problems: the customer gets money back in 1-2 days, managers spend no more than 5 minutes per return. The system processes returns 10 times faster than manual handling. To get an exact plan for your store, request a consultation.
Problems we solve
Status chaos is the main pain point. Standard sales statuses don't suit returns; a separate chain with clear stages is needed. Manual refunds via the payment gateway's personal account lead to amount errors and delays. Lack of warehouse integration after return: stock is not updated, or data is duplicated during exchange with 1C. Each of these problems individually reduces customer loyalty and burdens managers with routine. In our practice, debugging a single return used to take several hours until we automated the entire cycle.
How we do it: tech stack and real case
We use the standard sale module and infoblocks v2.0. The main entity is \Bitrix\Sale\OrderReturn. It links the return to the order, stores the type (money/exchange/credit) and item list. We build the process around it. Below is an example of creating a return via API.
namespace Local\Returns;
use Bitrix\Sale;
class ReturnManager
{
/**
* Create a return for an order
*
* @param int $orderId Order ID
* @param array $items [['basket_id' => int, 'quantity' => float, 'reason' => string], ...]
* @param string $returnType 'MONEY' | 'EXCHANGE' | 'CREDIT'
*/
public function createReturn(int $orderId, array $items, string $returnType = 'MONEY'): int
{
\Bitrix\Main\Loader::includeModule('sale');
$order = Sale\Order::load($orderId);
if (!$order) {
throw new \RuntimeException("Order #{$orderId} not found");
}
// Check that the order is paid
if (!$order->isPaid()) {
throw new \RuntimeException("Order #{$orderId} is not paid");
}
$returnCollection = $order->getPaymentCollection();
// Create a return object
$orderReturn = Sale\OrderReturn::create($order);
$orderReturn->setField('TYPE', $returnType);
$orderReturn->setField('REASON', 'Customer request');
// Add return items
$basketCollection = $order->getBasket();
foreach ($items as $item) {
$basketItem = $basketCollection->getItemById($item['basket_id']);
if (!$basketItem) continue;
$maxQty = $basketItem->getQuantity();
$qty = min((float)$item['quantity'], $maxQty);
$returnItem = $orderReturn->getReturn()->createItem($basketItem);
$returnItem->setField('QUANTITY', $qty);
$returnItem->setField('REASON', $item['reason'] ?? '');
}
$result = $orderReturn->save();
if (!$result->isSuccess()) {
throw new \RuntimeException('Return creation failed: ' . implode('; ', $result->getErrorMessages()));
}
return $orderReturn->getId();
}
}
Developing a custom personal account component takes 2-3 times less time than writing from scratch, thanks to using the standard component bitrix:sale.order.return.edit. From our practice: implementing a return system for a large marketplace reduced the full cycle time from 14 to 2 days.
Setting up the return lifecycle
Return statuses: standard set
In the admin panel, we create statuses covering each step of the business process. A minimal set: WAIT, REVIEW, APPROVED, RECEIVED, REFUND, REJECTED, EXCHANGE. Each status logically covers a stage.
| Code | Name | Description |
|---|---|---|
| WAIT | Awaiting review | New request, not processed |
| REVIEW | Under review | Manager reviews the request |
| APPROVED | Approved | Return approved, awaiting goods |
| RECEIVED | Goods received | Warehouse accepted the returned goods |
| REFUND | Money refunded | Payment completed |
| REJECTED | Rejected | Return rejected with reason |
| EXCHANGE | Exchange | Replacement with another product |
The event OnSaleOrderReturnStatusChange is the standard mechanism for syncing return statuses with external systems, as described in the 1C-Bitrix documentation.
Why correct status configuration matters?
Errors in statuses lead to desynchronization with 1C and incorrect stock calculation. For example, if the warehouse receives goods but the status does not change to RECEIVED, 1C won't release the reserve. The result is negative stock balances in 1C. We attach handlers to status changes to prevent such situations.
How to integrate returns with 1C?
When goods are returned to the warehouse, we need to update stock. If the warehouse is managed in 1C, we send a notification to 1C via a queue when the return status changes to 'Goods received'.
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleOrderReturnStatusChange',
function (\Bitrix\Main\Event $event) {
$returnId = $event->getParameter('RETURN_ID');
$newStatus = $event->getParameter('NEW_STATUS_ID');
if ($newStatus === 'RECEIVED') {
\Local\OneC\StockSync::scheduleReturnSync($returnId);
}
if ($newStatus === 'REFUND') {
\Local\Returns\RefundProcessor::processPaymentReturn($returnId);
}
}
);
How does automatic refund work?
Most Bitrix payment systems (YooKassa, Tinkoff, Sber) support API refunds. In Bitrix, this is implemented via a payment system handler:
namespace Local\Returns;
class RefundProcessor
{
public static function processPaymentReturn(int $returnId): bool
{
\Bitrix\Main\Loader::includeModule('sale');
$return = \Bitrix\Sale\OrderReturn::loadById($returnId);
if (!$return) return false;
$order = \Bitrix\Sale\Order::load($return->getField('ORDER_ID'));
$payments = $order->getPaymentCollection();
$amount = $return->getField('REFUND_AMOUNT'); // amount to refund
foreach ($payments as $payment) {
if (!$payment->isPaid()) continue;
// Refund method depends on payment system
$paySystem = $payment->getPaySystem();
if (!$paySystem) continue;
$result = $paySystem->refund($payment, $amount);
if ($result->isSuccess()) {
$return->setField('STATUS_ID', 'REFUND');
$return->setField('REFUND_DATE', new \Bitrix\Main\Type\DateTime());
$return->save();
return true;
}
}
return false;
}
}
Automatic refund via API speeds up the process 5-10 times compared to manual — the customer receives money the same day.
Personal account and access rights
The standard component bitrix:sale.order.return.edit allows customers to create a return request from their order history. Connecting it in the personal account template:
$APPLICATION->IncludeComponent(
'bitrix:sale.order.return.edit',
'default',
[
'ORDER_ID' => (int)$_GET['ORDER_ID'],
'RETURN_ID' => (int)$_GET['RETURN_ID'],
'SITE_ID' => SITE_ID,
'PATH_TO_RETURN_LIST' => '/personal/returns/',
]
);
Access rights to returns are managed through roles in the sale module: return manager (view, change up to 'Approved'), senior manager (full rights), customer (create request). Setting up rights takes no more than an hour. For partial returns, we create a return with specific items in ReturnManager — the handler adjusts the refund amount proportionally.
What's included in returns management setup
- Audit of current return processes and requirements gathering.
- Development of technical specifications and status scheme.
- Creation of a separate return status chain with necessary codes.
- Setup of events and handlers for status changes.
- Integration with 1C (stock and document sync).
- Connection of automatic refund via payment gateway APIs.
- Development or customization of the customer's personal account component.
- Testing all scenarios (including partial return and refund).
- Training managers on the system.
- Provision of administration documentation.
- Support for 6 months after deployment.
Work process and timelines
| Stage | What we do | Result |
|---|---|---|
| Analytics | Audit current return processes, gather requirements, describe business process | Technical specifications |
| Design | Develop status scheme, link with 1C, select payment systems | Project documentation |
| Implementation | Configure statuses, create personal account component, event handlers, integration with payments and 1C | Working prototype on test environment |
| Testing | Check all scenarios: create return, change statuses, refund, sync | Test report |
| Deployment | Deploy to production, train managers, hand over documentation | Acceptance certificate |
Basic setup with personal account and statuses — from 1 to 2 weeks. Full system with automatic refund and 1C integration — from 3 to 5 weeks. We'll assess your project after analyzing the technical specifications.
We guarantee 6 months of support after implementation. All solutions undergo code review and load testing.
Contact us for a free consultation — we'll tell you how much time and resources automation will save. Request an audit of your current return process to get an accurate work plan.







