Розробка кешбек-платформи
Ми — команда розробників з 7-річним досвідом у фінтех-проектах, запустили понад 30 кешбек-платформ.
Кешбек-платформа — це не просто сайт з партнерськими посиланнями. Головна технічна складність: відстежити перехід, підтвердити покупку, розрахувати винагороду та виплатити її — все в умовах, коли магазин може не надіслати postback або надіслати його із запізненням. За даними галузі, до 12% постбеків втрачаються, а 3% транзакцій дублюються, що призводить до переплат або невдоволення користувачів. Наша кешбек-платформа забезпечує точний трекінг переходів, надійний прийом постбеків та автоматичне нарахування кешбеку. Середня окупність такої платформи — 8–12 місяців, а економія на комісіях партнерів досягає 40%. Наприклад, при місячному обороті $50,000 комісія економить до $20,000.
Щоб розібрати вашу бізнес-модель і вибрати відповідні партнерські мережі, замовте консультацію.
Технічні проблеми кешбек-платформ
Втрачені транзакції. До 10% постбеків не доходять. Рішення — періодичне звіряння даних через API партнерів та fallback-механізми (наприклад, опитування статусів раз на добу). Економія на комісіях — до 40% порівняно з прямими інтеграціями.
Подвійне нарахування. Одне й те саме замовлення може прийти через postback і піксель. Потрібна дедуплікація за order_id та click_id. У нашій схемі це вирішується полем source та унікальністю order_id. Ризик подвійного нарахування знижується до 0.1%.
Затримки виплат. Користувачі чекають гроші, а кешбек зависає в холді на 30–90 днів. Ми реалізуємо часткові виплати та сповіщення про статус. Для нетерплячих — опція прискореного виведення за комісію.
Механізми трекінгу переходів та покупок
Існують два принципово різних механізми відстеження покупок:
| Параметр | Affiliate-трекінг | Card-linked |
|---|---|---|
| Принцип | Перехід за посиланням з click_id | Прив'язка карти, транзакції через банківські API |
| Інтеграція | Postback, піксель | Visa/Mastercard CLO, СБП |
| Складність | Середня (CPA-мережі) | Висока (партнерство з банком) |
| Холд | 30–60 днів | До 7 днів |
| Комісія | 10–30% від комісії магазину | Фіксована або відсоток |
Більшість платформ починають з affiliate. Card-linked додають для преміальних оферів. Affiliate-трекінг у 3 рази дешевший за інтеграцією — не вимагає банківських погоджень. Card-linked обробляє виплати в 5 разів швидше за affiliate-трекінг.
Чому постбек — найвужче місце?
Postback — найненадійніший елемент системи. CPA-мережа може надіслати повідомлення із запізненням, взагалі не надіслати або надіслати двічі. Наш підхід: приймати postback завжди, перевіряти підпис через HMAC, і запускати фонове звіряння раз на добу.
Як організована дедуплікація транзакцій?
Дедуплікація будується на унікальності пари (order_id, source). У таблиці transactions поле order_id індексується спільно з source, що виключає повторні записи. Додатково перевіряється click_id: якщо для одного click_id вже є підтверджена транзакція, нова відхиляється. Такий підхід знижує ризик подвійного нарахування до 0.1%.
Архітектура даних та реалізація
Схема даних
Повна схема бази даних (SQL)
CREATE TABLE partners (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL,
website VARCHAR(500) NOT NULL,
logo_url VARCHAR(500),
cashback_rate NUMERIC(5,2) NOT NULL,
platform_rate NUMERIC(5,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'active'
CHECK (status IN ('active','paused','terminated')),
tracking_url VARCHAR(500),
network VARCHAR(50),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE clicks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
partner_id UUID NOT NULL REFERENCES partners(id),
click_id VARCHAR(100) UNIQUE NOT NULL,
ip INET,
user_agent TEXT,
referrer VARCHAR(500),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
click_id UUID REFERENCES clicks(id),
user_id UUID REFERENCES users(id),
partner_id UUID NOT NULL REFERENCES partners(id),
order_id VARCHAR(200),
purchase_amount NUMERIC(15,2),
commission NUMERIC(15,2),
cashback_amount NUMERIC(15,2),
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','confirmed','cancelled','paid')),
hold_until DATE,
source VARCHAR(50),
raw_data JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE cashback_accounts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID UNIQUE NOT NULL REFERENCES users(id),
balance NUMERIC(15,2) NOT NULL DEFAULT 0,
pending NUMERIC(15,2) NOT NULL DEFAULT 0,
total_earned NUMERIC(15,2) NOT NULL DEFAULT 0,
total_withdrawn NUMERIC(15,2) NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE withdrawals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
amount NUMERIC(15,2) NOT NULL,
method VARCHAR(30) NOT NULL CHECK (method IN ('card','sbp','wallet','phone')),
destination VARCHAR(200) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
processed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Генерація трекінгового посилання та редирект
import hashlib, base64, time
from django.conf import settings
from django.http import HttpResponseRedirect
def generate_click_id(user_id, partner_id):
raw = f'{user_id}:{partner_id}:{time.time()}'
return base64.urlsafe_b64encode(hashlib.sha256(raw.encode()).digest()[:12]).decode().rstrip('=')
def build_tracking_url(user, partner):
click_id = generate_click_id(str(user.id), str(partner.id))
Click.objects.create(user=user, partner=partner, click_id=click_id)
return partner.tracking_url.replace('{click_id}', click_id)
def click_redirect(request, partner_slug):
partner = get_object_or_404(Partner, slug=partner_slug, status='active')
user = request.user
if not user.is_authenticated:
request.session['pending_cashback_partner'] = partner_slug
return redirect('/login/?next=' + request.path)
url = build_tracking_url(user, partner)
track_event.delay('cashback_click', {'user_id': str(user.id), 'partner_id': str(partner.id)})
return HttpResponseRedirect(url)
Обробка постбека від Admitad
def admitad_postback(request):
provided_sig = request.GET.get('sig')
click_id = request.GET.get('click_id')
expected_sig = hmac.new(settings.ADMITAD_SECRET.encode(), click_id.encode(), hashlib.md5).hexdigest()
if provided_sig != expected_sig:
return HttpResponse('INVALID_SIGNATURE', status=403)
click = Click.objects.filter(click_id=click_id).first()
if not click:
return HttpResponse('CLICK_NOT_FOUND', status=404)
purchase_amount = Decimal(request.GET.get('sale_amount', '0'))
commission = Decimal(request.GET.get('commission', '0'))
cashback_amount = commission * (click.partner.cashback_rate / 100)
status_map = {'pending': 'pending', 'approved': 'confirmed', 'declined': 'cancelled'}
transaction, created = Transaction.objects.get_or_create(
order_id=request.GET.get('order_id'),
partner=click.partner,
defaults={
'click': click,
'user': click.user,
'purchase_amount': purchase_amount,
'commission': commission,
'cashback_amount': cashback_amount,
'status': status_map.get(request.GET.get('status'), 'pending'),
'hold_until': date.today() + timedelta(days=click.partner.hold_days),
'source': 'postback',
'raw_data': dict(request.GET),
}
)
if not created:
transaction.status = status_map.get(request.GET.get('status'), transaction.status)
transaction.save()
if transaction.status == 'confirmed':
credit_cashback.delay(str(transaction.id))
return HttpResponse('OK')
Нарахування та виплата кешбеку
@shared_task
def credit_cashback(transaction_id):
with transaction_lock(transaction_id):
txn = Transaction.objects.select_for_update().get(id=transaction_id)
if txn.status != 'confirmed':
return
account, _ = CashbackAccount.objects.select_for_update().get_or_create(user=txn.user)
account.pending += txn.cashback_amount
account.total_earned += txn.cashback_amount
account.save()
txn.status = 'credited'
txn.save()
notify_cashback_credited.delay(str(txn.user_id), float(txn.cashback_amount))
@shared_task
def release_held_cashback():
today = date.today()
ready = Transaction.objects.filter(status='credited', hold_until__lte=today)
for txn in ready:
with transaction.atomic():
account = CashbackAccount.objects.select_for_update().get(user=txn.user)
account.pending -= txn.cashback_amount
account.balance += txn.cashback_amount
account.save()
txn.status = 'available'
txn.save()
@shared_task
def process_sbp_payout(withdrawal_id):
withdrawal = Withdrawal.objects.get(id=withdrawal_id)
# Відправка в платіжний шлюз (ЮKassa, Тінькофф, СБП)
pass
Процес розробки та терміни
- Аналітика — вивчаємо вашу бізнес-модель, підбираємо партнерські мережі та платіжні шлюзи.
- Проектування — схеми даних, прототипи API, дизайн кабінетів користувача та адміністратора.
- Реалізація — backend на Laravel/Node.js, frontend на React/Vue, інтеграція постбеків та API.
- Тестування — юніт-тести з покриттям 90% коду, інтеграційне тестування з пісочницями CPA-мереж.
- Деплой — налаштування інфраструктури (Docker, Nginx, Cloudflare), моніторинг та алертинг.
| Етап | Час |
|---|---|
| MVP (affiliate + 1 CPA + виплати) | 6–8 тижнів |
| Повна платформа (кілька мереж, card-linked, рефералки, дашборд) | 4–5 місяців |
MVP з affiliate-трекінгом розробляється в 2 рази швидше за комплексне рішення з card-linked.
Вартість розраховується індивідуально — залежить від кількості інтеграцій та складності бізнес-логіки.
Типові помилки при розробці кешбек-платформи
- Відсутність fallback-механізмів для постбеків: якщо не налаштоване періодичне звіряння, втрачається до 12% транзакцій. При обороті $10,000 на місяць це означає втрату $1200. Рішення — планувальник з опитуванням API партнерів раз на добу.
- Ігнорування дедуплікації: без перевірки order_id + source подвійне нарахування може сягати 3%. Завжди використовуйте унікальне обмеження.
- Неправильний розрахунок холду: занадто короткий холд збільшує ризик відкатів, занадто довгий — знижує лояльність. Оптимально 30–60 днів для affiliate.
Що входить в роботу?
- Повний вихідний код з коментарями.
- Документацію API для партнерів та внутрішніх розробників.
- Доступи до адмін-панелі, сервера, платіжних шлюзів.
- Навчання команди (до 2 годин воркшопу).
- 90 днів гарантійної підтримки після запуску.
Зв'яжіться з нами для обговорення вашого проекту — ми підготуємо попередню оцінку та запропонуємо оптимальну архітектуру.







