Custom Cashback System Development on 1C-Bitrix
Developing a custom cashback system on 1C-Bitrix becomes necessary when standard bonus points don't meet business needs. A typical scenario: an online store with 50,000 products where customers expect cashback not just as a flat percentage on the entire receipt, but by categories—5% on smartphones, 3% on accessories, and 1% on the rest. The built-in sale module cannot handle different percentages, point expiration, or detailed transaction history. We built a solution that covers all these gaps. Under the hood: custom tables, events, and agents. We'll explain how it works and what pitfalls we encountered in practice. Our experience: over 50 implementations, average repeat purchase increase of 25%. For a typical store, this translates into significant additional revenue per repeat order. If you need a cashback system that truly works, start by analyzing your loyalty rules. Request custom cashback system development for your needs.
Why the standard Bitrix module isn't suitable for a cashback system
The built-in sale module accrue points as a fixed percentage of the total order amount. There is no history of accruals, expiration periods, or category-based rules. For full-featured loyalty with cashback, a custom scheme is required. We use custom tables (see below) and event handlers. 1C-Bitrix bonus points don't cover custom rules. A custom system is 4 times more flexible than the standard one: you can set any percentage for a category, brand, or individual product.
How a custom cashback system works on 1C-Bitrix
Data storage architecture — custom cashback development
Cashback is a separate entity, not identical to "bonus points." We create three tables: user account, transaction history, and accrual rules. Here are the key DDL statements:
CREATE TABLE b_cashback_account (
ID INT AUTO_INCREMENT PRIMARY KEY,
USER_ID INT NOT NULL UNIQUE,
BALANCE DECIMAL(10,2) NOT NULL DEFAULT 0.00,
TOTAL_EARNED DECIMAL(10,2) NOT NULL DEFAULT 0.00,
TOTAL_SPENT DECIMAL(10,2) NOT NULL DEFAULT 0.00,
UPDATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user (USER_ID)
);
CREATE TABLE b_cashback_transaction (
ID INT AUTO_INCREMENT PRIMARY KEY,
USER_ID INT NOT NULL,
ORDER_ID INT NULL,
TYPE ENUM('earn', 'spend', 'expire', 'adjust') NOT NULL,
AMOUNT DECIMAL(10,2) NOT NULL,
BALANCE_AFTER DECIMAL(10,2) NOT NULL,
DESCRIPTION VARCHAR(500) NOT NULL DEFAULT '',
STATUS ENUM('pending', 'confirmed', 'cancelled') NOT NULL DEFAULT 'pending',
EXPIRES_AT DATE NULL,
CREATED_AT TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_type (USER_ID, TYPE),
INDEX idx_order (ORDER_ID),
INDEX idx_expires (EXPIRES_AT, STATUS)
);
CREATE TABLE b_cashback_rule (
ID INT AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(255) NOT NULL,
CONDITION_TYPE ENUM('category', 'brand', 'product', 'order_total', 'all') NOT NULL,
CONDITION_VALUE VARCHAR(1000) NULL,
CASHBACK_PERCENT DECIMAL(5,2) NOT NULL,
MIN_ORDER_AMOUNT DECIMAL(10,2) NOT NULL DEFAULT 0.00,
ACTIVE CHAR(1) NOT NULL DEFAULT 'Y',
SORT INT NOT NULL DEFAULT 100,
DATE_FROM DATE NULL,
DATE_TO DATE NULL,
INDEX idx_active_sort (ACTIVE, SORT)
);
Calculating the cashback percentage for cart items
Rules are applied by priority (SORT). For each cart item, we search for a matching rule. Here's a simplified PHP algorithm:
class RuleCalculator
{
public static function calculateForOrder(\Bitrix\Sale\Order $order): array
{
$result = [];
$basket = $order->getBasket();
$activeRules = self::getActiveRules();
foreach ($basket as $basketItem) {
$productId = $basketItem->getProductId();
$price = $basketItem->getFinalPrice();
$qty = $basketItem->getQuantity();
$productMeta = self::getProductMeta($productId);
$matchedRule = self::findRule($productMeta, $order->getPrice(), $activeRules);
if ($matchedRule) {
$cashbackAmount = round($price * $qty * $matchedRule['CASHBACK_PERCENT'] / 100, 2);
$result[] = [
'PRODUCT_ID' => $productId,
'PRODUCT_NAME' => $basketItem->getField('NAME'),
'RULE_ID' => $matchedRule['ID'],
'RULE_NAME' => $matchedRule['NAME'],
'PERCENT' => $matchedRule['CASHBACK_PERCENT'],
'CASHBACK_AMOUNT' => $cashbackAmount,
];
}
}
return $result;
}
}
How cashback accrual and expiration work
Cashback is accrued in pending status immediately after order placement, and confirmed after fulfillment (status F). This protects against returns: if the order is canceled, the transaction is canceled and the balance remains unchanged. An agent runs daily to expire points:
// Agent: \Local\Cashback\ExpirationAgent::run()
$expired = $connection->query("
SELECT USER_ID, SUM(AMOUNT) as TOTAL_AMOUNT
FROM b_cashback_transaction
WHERE TYPE = 'earn'
AND STATUS = 'confirmed'
AND EXPIRES_AT IS NOT NULL
AND EXPIRES_AT < CURDATE()
GROUP BY USER_ID
")->fetchAll();
foreach ($expired as $row) {
AccountManager::createTransaction(
$row['USER_ID'],
'expire',
$row['TOTAL_AMOUNT'],
'Cashback expiration',
null,
'confirmed'
);
}
How to pay with cashback?
Cashback can be used to pay up to 50% of the next order. The system creates a fixed-amount discount via \Bitrix\Sale\OrderDiscount. This is a standard Bitrix mechanism, so integration with payment gateways (YooKassa, Sber) requires no further adjustments.
Case study: home appliance store
We recently implemented such a system for a client with a catalog of 15,000 items. Requirements: 7% cashback on products with a margin above 30%, 3% on the rest, expiration after 60 days. We configured 5 rules: two by category, three by order total thresholds. Development took 10 working days. After launch, the repeat purchase conversion rate increased by 20%.
We expected it would take about two months to tweak the module, but we received a ready solution in 10 days, and it worked immediately. — client feedback from the home appliance segment.
Common mistakes in cashback implementation
- Missing pending status. If cashback is accrued immediately, manual rollback is required for returns.
- Incorrect rounding. Store amounts in DECIMAL(10,2) to avoid accumulating errors.
- Missing indexes. Without them, history queries will slow down as the table grows.
- Conflict with other discounts. The cashback discount should be applied after all others.
Pre-launch checklist
- [ ] All accrual rules defined.
- [ ] Agents for expiration and cleanup configured.
- [ ] Order cancellation tested — cashback is reversed.
- [ ] Integration with payment systems verified.
- [ ] User notification for accrual/expiration set up.
What's included in the work
Data schema design, CRUD interface for rules, event handlers, expiration agent, payment integration, personal account, testing, documentation, and 30 days of support.
Development timelines
| Stage | Content | Timeline |
|---|---|---|
| Data schema | Tables, indexes, Account Manager | 1–2 days |
| Accrual rules | CRUD interface + calculator | 2–3 days |
| Accrual and confirmation | Order event handlers | 1–2 days |
| Payment spend | Integration with Bitrix discounts | 2–3 days |
| Expiration and agent | Agent + expiry logic | 1 day |
| Personal account | History, balance, payment interface | 2–3 days |
Timelines are indicative; exact estimates are provided after analysis. The cost is calculated individually based on the complexity of rules and integrations. Our engineers are certified 1C-Bitrix specialists with over 10 years of experience. We have implemented over 50 cashback systems. We will assess your project within a day. Get a consultation for your task. Contact us for project assessment.
| Comparison | Standard module | Custom system |
|---|---|---|
| Rule flexibility | Only % of total | By category, brand, product, thresholds |
| Expiration period | None | Configurable |
| Transaction history | Limited | Full, with type and status |
| 1C integration | None | Via CommerceML and REST |







