Imagine an e-commerce store with 50,000 monthly visitors launching a referral program. A month later, 40% of referral link clicks are unattributed — users came from social media, didn't register immediately, and returned after a week. Without proper attribution, referrals are lost and the program doesn't pay off. According to our data, correct cookie and tracking setup can recover up to 40% of lost profit. That's why we emphasize secure cookies with httpOnly and sameSite: lax, lasting 30 days.
How a referral program solves key business problems?
The main challenges include attribution, cheating, code collisions, and inflexible reward logic. Without a systematic approach, the program loses effectiveness. Our solution addresses each issue: from generating unique codes to automatic bonus accrual.
Why is referral attribution critical?
Without correct attribution, referrals are lost. Our data shows that 80% of referrals come within the first 7 days, but if the cookie lives shorter, up to 30% won't be counted. That's why we use a 30-day cookie with httpOnly and sameSite: lax.
Generating unique codes without collisions
Code collisions are a typical issue with off-the-shelf modules. With thousands of users, short codes repeat. We use the nanoid library with an alphabet that avoids confusing characters (1, l, I, 0, O) and a length of 8 characters. We make up to 5 attempts on collision — the probability of repetition is negligible. To speed up collision checking, we use hashing and cache existing codes in Redis.
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) {
// if collision — try again
}
}
throw new Error('Failed to generate unique referral code');
}
How to prevent referral fraud?
Fraud is another headache. Fraudsters register multiple accounts from the same IP or refer themselves. Our approach includes several layers:
- Unique referral check (one user — one referral).
- Self-referral prohibition (referrer cannot equal referee).
- Verification by first payment.
- Time-limited cookie (30 days).
- Optionally: CAPTCHA or registration limit per IP.
These measures reduce fraud risk by 90%.
How we do it: stack and real case
We use Next.js, Prisma, and PostgreSQL. All critical operations are wrapped in transactions. Consider a typical case from our practice: an e-commerce store with 50,000 products and seasonal promotions. They needed different bonuses for different categories: 10% on electronics, 5% on clothing, fixed amount on new items. Flexible business logic is implemented through custom rules in a separate RewardRules table. Our client noted that after implementing correct attribution, the referral program generated 40% more orders.
Tracking with cookies
Next.js middleware reads the ?ref= parameter from the URL and sets a cookie for 30 days, even if the user doesn't register. On registration, the code from the cookie is read and a referral relationship is created.
// 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')) {
response.cookies.set('referral_code', ref, {
maxAge: 60 * 60 * 24 * 30,
httpOnly: true,
sameSite: 'lax',
});
}
return response;
}
On registration, we extract the code from the cookie and verify that the referrer is not the same as the referee. If the code is valid, we create a Referral record with status PENDING.
Reward accrual
After the first successful payment (e.g., via Stripe), a webhook calls the handleFirstPayment function. It finds the associated referral, calculates the reward amount (e.g., 20% of the payment), and in a transaction updates the status and credits the referrer's balance. Notification is sent automatically.
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;
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 });
}
User dashboard
Each user sees their referral link, statistics on invited users (how many pending, qualified, rewarded) and total bonuses earned. A conversion chart and payout history can be added. The dashboard is implemented as a Next.js server component, data fetched via server-side Prisma queries.
Comparison of custom vs. off-the-shelf modules
| Criteria | Our solution | Off-the-shelf (e.g., AffiliateWP) |
|---|---|---|
| Business logic flexibility | Absolute — any scenario | Limited by fixed rules |
| Backend integration | Full data ownership | Relies on third-party API |
| Security | Control at each level | Possible plugin vulnerabilities |
| Total cost | Tailored development | License + customizations |
A custom system is justified if you have non-standard reward rules, multiple currencies, or deep ERP integration. A pre-built plugin is faster to implement but rarely offers the needed flexibility.
Process and timeline
| Stage | Duration | Outcome |
|---|---|---|
| Analysis | 1–2 days | Technical specification |
| Design | 1–2 days | Data model, API specification |
| Implementation | 3–5 days | Working code |
| Testing | 1–2 days | Test report |
| Deployment | 1 day | Production launch |
Implementation details
- Prisma schema includes models: User, ReferralCode, Referral, RewardRule, UserBalance.
- All database operations are wrapped in transactions for consistency.
- To avoid race conditions during code generation, we use a unique index and a retry loop.
What's included in development
- Prisma migration schema
- Middleware for referral tracking (30-day cookie)
- Unique code generator with 5 retry attempts
- API routes: code creation, registration, payment webhook, dashboard
- Payment gateway integration (Stripe, YooKassa) — webhook setup
- User dashboard with link copying and statistics
- Email notifications for bonus accrual
- API documentation (Swagger/OpenAPI)
- 2 weeks of technical support after launch
Why choose us
We have over 5 years of experience developing complex web systems, having implemented 20+ referral programs for e-commerce, SaaS, and info-products. We guarantee correct attribution and scalability — handling up to 100,000 referrals per day without performance loss. All code is open and documented.
Order a referral system development that will deliver real results. Get a consultation — we'll assess your project and offer the best solution. Contact us to discuss the details.







