Development of a 1C-Bitrix referral program module

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages

Referral Program Module Development for 1C-Bitrix

The task of a referral program is to track the "who referred whom" chain and correctly credit the referrer with a reward for each payment made by a referral. It sounds simple, but in practice questions arise: how do you persist the referral link when a user switches devices or browsers? How do you avoid double-crediting on an order return? How do you build a multi-level system without recursive queries? Bitrix has no built-in tools for this.

Data Model

Module vendor.referral:

  • b_vendor_ref_link — referral links: id, user_id, code (unique 8-character code), clicks, registrations, orders_count, earned_total, created_at, is_active
  • b_vendor_ref_relation — referrer-referral relationships: id, referrer_id, referral_id, link_id, created_at
  • b_vendor_ref_transaction — accruals: id, referrer_id, referral_id, order_id, level (1/2/3 for multi-level), amount, status (pending/approved/paid/cancelled), created_at
  • b_vendor_ref_withdrawal — withdrawal requests: id, user_id, amount, method (bank/card/wallet), details (JSON), status, processed_at

Click and Registration Tracking

When a user clicks a referral link ?ref=ABCD1234:

// In init.php — intercept the parameter
if ($refCode = $_GET['ref'] ?? null) {
    setcookie('ref_code', $refCode, time() + 30 * 86400, '/', '', true, true);
    // 30-day cookie, httponly, secure
    RefLinkTable::incrementClicks($refCode);
}

When a new user registers:

AddEventHandler('main', 'OnAfterUserAdd', ['\Vendor\Referral\EventHandler', 'onUserRegistered']);

public static function onUserRegistered(array &$fields): void
{
    $refCode = $_COOKIE['ref_code'] ?? null;
    if (!$refCode) return;

    $link = RefLinkTable::getByCode($refCode);
    if (!$link || $link['USER_ID'] === $fields['ID']) return; // no self-referral

    RefRelationTable::add([
        'REFERRER_ID' => $link['USER_ID'],
        'REFERRAL_ID' => $fields['ID'],
        'LINK_ID'     => $link['ID'],
    ]);

    RefLinkTable::incrementRegistrations($link['ID']);
}

Reward Accrual

A reward is accrued when the referral's order is paid:

AddEventHandler('sale', 'OnSaleOrderPaid', ['\Vendor\Referral\EventHandler', 'onOrderPaid']);

public static function onOrderPaid(\Bitrix\Main\Event $event): void
{
    $orderId  = $event->getParameter('id');
    $order    = \Bitrix\Sale\Order::load($orderId);
    $referral = $order->getUserId();

    // Build the referrer chain (up to 3 levels)
    $chain = RefRelationTable::getChain($referral, 3);
    $rules = Option::get('vendor.referral', 'reward_rules'); // JSON with % per level

    foreach ($chain as $level => $referrerId) {
        $percent = $rules["level_{$level}"] ?? 0;
        if (!$percent) continue;

        $amount = $order->getPrice() * $percent / 100;

        RefTransactionTable::add([
            'REFERRER_ID' => $referrerId,
            'REFERRAL_ID' => $referral,
            'ORDER_ID'    => $orderId,
            'LEVEL'       => $level,
            'AMOUNT'      => $amount,
            'STATUS'      => 'pending', // awaiting approval
        ]);
    }
}

Accrual Statuses and Fraud Protection

An accrual is created with pending status. It is moved to approved only after:

  1. N days have passed since the payment date (return period)
  2. The order was not cancelled or returned

When an order is cancelled the transaction is moved to cancelled. A daily agent checks for expired return periods and approves pending transactions.

Additional protections:

  • A referrer cannot be their own referral
  • Registration from the same IP as the referrer is flagged for manual review
  • Daily accrual limit per referral (protection against mass fake orders)

Referrer Personal Account

  • Unique referral link with QR code
  • Statistics: clicks, registrations, referral orders, amount earned
  • Accrual history broken down by referrals and orders
  • Withdrawable balance and payout history
  • Withdrawal request form

Multi-Level Structure

Level 1: referrer receives 5% of every direct referral's order
Level 2: 2% of referrals' referrals' orders
Level 3: 1% from the third level

The chain is built with a recursive query on b_vendor_ref_relation with a depth limit.

Development Timeline

Stage Duration
ORM tables, link generation 1 day
Click tracking (cookie) 1 day
Referral binding on registration 1 day
Reward accrual, multi-level logic 2 days
Fraud protection, approval agent 2 days
Withdrawal requests, admin interface 2 days
Referrer personal account 2 days
Testing 1 day

Total: 12 working days. Integration with payment systems for automatic reward payouts is estimated separately.