Реферальная система с реферальными ссылками
Реферальная система стимулирует пользователей привлекать новых клиентов. Механика: уникальная ссылка → регистрация → атрибуция → вознаграждение. Детали — в бизнес-логике: скидка, кредит на счёт, процент от оплаты.
Схема данных
model ReferralCode {
id String @id @default(cuid())
code String @unique // уникальный код: "JOHN2024"
userId String @unique // один код на пользователя
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
referrals Referral[]
}
model Referral {
id String @id @default(cuid())
referralCodeId String
referredUserId String @unique // пользователь может быть реферален один раз
status ReferralStatus @default(PENDING)
rewardAmount Int? // в копейках/центах
rewardPaidAt DateTime?
createdAt DateTime @default(now())
referralCode ReferralCode @relation(fields: [referralCodeId], references: [id])
referredUser User @relation(fields: [referredUserId], references: [id])
}
enum ReferralStatus {
PENDING // зарегистрировался, но не оплатил
QUALIFIED // выполнил условие (первая оплата)
REWARDED // вознаграждение выплачено
CANCELLED // отменён (возврат и т.д.)
}
Генерация кода и ссылки
// lib/referral.ts
import { customAlphabet } from 'nanoid';
const generateCode = customAlphabet('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', 8);
export async function getOrCreateReferralCode(userId: string): Promise<string> {
const existing = await db.referralCode.findUnique({
where: { userId }
});
if (existing) return existing.code;
// Пробуем создать уникальный код
for (let attempt = 0; attempt < 5; attempt++) {
const code = generateCode();
try {
const created = await db.referralCode.create({
data: { userId, code }
});
return created.code;
} catch (e) {
// Уникальность нарушена — пробуем снова
}
}
throw new Error('Failed to generate unique referral code');
}
export function buildReferralUrl(code: string, baseUrl?: string): string {
const base = baseUrl ?? process.env.APP_URL!;
return `${base}/?ref=${code}`;
}
Трекинг реферала при регистрации
// Middleware: сохраняем ref в cookie
// middleware.ts
export function middleware(request: NextRequest) {
const response = NextResponse.next();
const ref = request.nextUrl.searchParams.get('ref');
if (ref && !request.cookies.get('referral_code')) {
// Cookie живёт 30 дней — даже если пользователь не зарегистрировался сразу
response.cookies.set('referral_code', ref, {
maxAge: 60 * 60 * 24 * 30,
httpOnly: true,
sameSite: 'lax',
});
}
return response;
}
// При регистрации нового пользователя
export async function registerUser(email: string, password: string, referralCode?: string) {
const user = await createUser(email, password);
if (referralCode) {
const code = await db.referralCode.findUnique({
where: { code: referralCode },
});
if (code && code.userId !== user.id) { // нельзя ссылаться на себя
await db.referral.create({
data: {
referralCodeId: code.id,
referredUserId: user.id,
status: 'PENDING',
}
});
}
}
return user;
}
Начисление вознаграждения
// При первой успешной оплате (webhook от Stripe)
export async function handleFirstPayment(userId: string, paymentAmount: number) {
const referral = await db.referral.findFirst({
where: {
referredUserId: userId,
status: 'PENDING',
},
include: { referralCode: true }
});
if (!referral) return;
// Вознаграждение: 20% от первого платежа
const rewardAmount = Math.floor(paymentAmount * 0.20);
await db.$transaction([
db.referral.update({
where: { id: referral.id },
data: {
status: 'QUALIFIED',
rewardAmount,
}
}),
// Начисляем кредит реферреру
db.userBalance.upsert({
where: { userId: referral.referralCode.userId },
create: {
userId: referral.referralCode.userId,
balance: rewardAmount,
},
update: {
balance: { increment: rewardAmount }
}
}),
]);
// Уведомляем реферрера
await sendReferralRewardEmail({
userId: referral.referralCode.userId,
amount: rewardAmount,
});
}
Дашборд реферральной программы
// app/dashboard/referrals/page.tsx
export default async function ReferralsPage() {
const session = await auth();
const code = await getOrCreateReferralCode(session!.user.id);
const referralUrl = buildReferralUrl(code);
const stats = await db.referral.groupBy({
by: ['status'],
where: { referralCode: { userId: session!.user.id } },
_count: true,
_sum: { rewardAmount: true },
});
return (
<div>
<h1>Реферальная программа</h1>
<ReferralLinkCopy url={referralUrl} code={code} />
<ReferralStats stats={stats} />
<ReferralHistory userId={session!.user.id} />
</div>
);
}
Разработка реферальной системы с трекингом, атрибуцией и начислением вознаграждений — 3–5 рабочих дней.







