Розробка кешбек-платформи
Ми — команда розробників з 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 днів гарантійної підтримки після запуску.
Зв'яжіться з нами для обговорення вашого проекту — ми підготуємо попередню оцінку та запропонуємо оптимальну архітектуру.







