Skip to content

Instantly share code, notes, and snippets.

@charanjit-singh
Created September 24, 2025 10:32
Show Gist options
  • Save charanjit-singh/b183df63c9830dd0f943f7399b65631c to your computer and use it in GitHub Desktop.
Save charanjit-singh/b183df63c9830dd0f943f7399b65631c to your computer and use it in GitHub Desktop.
import Cookies from 'js-cookie';
// Referral cookie name
const REFERRAL_COOKIE_NAME = 'indiekit_referrals';
// Cookie expiration days (45 days)
const COOKIE_EXPIRY_DAYS = 45;
// Max referrals to store
const MAX_REFERRALS = 5;
interface ReferralEntry {
code: string;
timestamp: number;
}
/**
* Get all stored referrals from the cookie
*/
export function getStoredReferrals(): ReferralEntry[] {
try {
const cookieValue = Cookies.get(REFERRAL_COOKIE_NAME);
if (!cookieValue) return [];
return JSON.parse(cookieValue);
} catch (error) {
console.error('Error parsing referral cookie:', error);
// If the cookie is corrupt, reset it
Cookies.remove(REFERRAL_COOKIE_NAME);
return [];
}
}
/**
* Add a new referral code to the cookie
*/
export function addReferral(refCode: string): void {
if (!refCode) return;
try {
const referrals = getStoredReferrals();
// Check if this referral code already exists
const existingReferralIndex = referrals.findIndex(ref => ref.code === refCode);
if (existingReferralIndex >= 0) {
// Code already exists, do nothing to preserve the original timestamp
return;
}
// Add new referral with current timestamp
const newReferral: ReferralEntry = {
code: refCode,
timestamp: Date.now()
};
// Add to the beginning, limit to max number of referrals
const updatedReferrals = [newReferral, ...referrals].slice(0, MAX_REFERRALS);
// Save back to cookie
Cookies.set(REFERRAL_COOKIE_NAME, JSON.stringify(updatedReferrals), {
expires: COOKIE_EXPIRY_DAYS,
sameSite: 'lax',
path: '/'
});
} catch (error) {
console.error('Error updating referral cookie:', error);
}
}
/**
* Clean up expired referrals (older than 45 days)
*/
export function cleanupExpiredReferrals(): void {
try {
const referrals = getStoredReferrals();
const now = Date.now();
const expiryTime = COOKIE_EXPIRY_DAYS * 24 * 60 * 60 * 1000; // 45 days in milliseconds
// Filter out expired referrals
const validReferrals = referrals.filter(ref => {
return (now - ref.timestamp) < expiryTime;
});
// Only update if we removed some expired referrals
if (validReferrals.length < referrals.length) {
Cookies.set(REFERRAL_COOKIE_NAME, JSON.stringify(validReferrals), {
expires: COOKIE_EXPIRY_DAYS,
sameSite: 'lax',
path: '/'
});
}
} catch (error) {
console.error('Error cleaning up referrals:', error);
}
}
/**
* Get the first/oldest valid referral within the attribution window
*/
export function getApplicableReferral(): string | null {
try {
cleanupExpiredReferrals();
const referrals = getStoredReferrals();
if (referrals.length === 0) return null;
// Return the oldest (last in array) valid referral code
// This implements "first touch" attribution
return referrals[referrals.length - 1].code;
} catch (error) {
console.error('Error getting applicable referral:', error);
return null;
}
}
/**
* Check if a URL search param has a referral code and add it to the cookie
*/
export function handleReferralFromURL(): void {
if (typeof window === 'undefined') return;
try {
const urlParams = new URLSearchParams(window.location.search);
const refCode = urlParams.get('ref');
if (refCode) {
addReferral(refCode);
}
} catch (error) {
console.error('Error handling referral from URL:', error);
}
}
/**
* Add referral code to payment link
*/
export function addReferralToPaymentLink(baseLink: string): string {
try {
const refCode = getApplicableReferral();
if (!refCode) return baseLink;
// Check if URL already has parameters
const separator = baseLink.includes('?') ? '&' : '?';
return `${baseLink}${separator}metadata_ref=${encodeURIComponent(refCode)}`;
} catch (error) {
console.error('Error adding referral to payment link:', error);
return baseLink;
}
}
import {
timestamp,
pgTable,
text,
numeric,
jsonb,
} from "drizzle-orm/pg-core";
import { users } from "./user";
// Schema to track referrals (who referred whom and commission amount)
export const referrals = pgTable("referrals", {
id: text("id")
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
referrerId: text("referrerId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
referredId: text("referredId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
referredEmail: text("referredEmail").notNull(),
purchaseAmount: numeric("purchaseAmount").notNull(),
commissionAmount: numeric("commissionAmount").notNull(),
planCodename: text("planCodename").notNull(),
status: text("status").notNull().default("pending"), // pending, approved, rejected
createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(),
settledIn: text("settledIn").references(() => payouts.id), // Link to the payout this referral was settled in
});
// Schema to track payouts to referrers
export const payouts = pgTable("payouts", {
id: text("id")
.primaryKey()
.$defaultFn(() => crypto.randomUUID()),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
amount: numeric("amount").notNull(),
status: text("status").notNull().default("pending"), // pending, processing, completed, rejected
paymentMethod: text("paymentMethod").notNull(), // paypal, bank_transfer, etc.
paymentDetails: jsonb("paymentDetails"), // Store payment details like PayPal email, bank account info
screenshot: text("screenshot"), // URL to proof of payment screenshot
notes: text("notes"),
createdAt: timestamp("createdAt", { mode: "date" }).defaultNow(),
completedAt: timestamp("completedAt", { mode: "date" }),
});
// Referral code management
try {
const metadata = body.data.metadata || {};
// Type assertion to fix TypeScript error
const referralCode = metadata.ref as string | undefined;
if (referralCode) {
console.log(`Referral code found: ${referralCode}`);
// Get the referrer user
const referrer = await db
.select()
.from(users)
.where(eq(users.referralCode, referralCode))
.limit(1)
.then((users) => users[0]);
if (!referrer) {
console.log(`Referrer not found for code: ${referralCode}`);
} else if (referrer.id === user.id) {
// Self-referral detected
console.log(`Self-referral detected for user: ${user.email}`);
await sendMail(
user.email,
"Self-referral detected",
`
<h1>Self-referral detected</h1>
<p>We noticed that you used your own referral code when making a purchase. Self-referrals are not eligible for commission payouts.</p>
<p>If you believe this is an error, please contact our support team.</p>
`
);
} else {
const currency = body.data.currency;
const amountPaid = body.data.total_amount / 100; // Convert from cents to dollars
const taxPaid = body.data.tax / 100; // Convert from cents to dollars
let purchaseAmount = amountPaid - taxPaid; // Convert from cents to dollars
if (currency !== "USD") {
const response = await fetch(
`https://api.currencyfreaks.com/v2.0/rates/latest?apikey=b656eb60121d44c5a21cf3fdb86b91f7&symbols=${currency}`
);
// {"date":"2025-04-08 00:00:00+00","base":"USD","rates":{"INR":"85.8825"}}
const data = await response.json();
const exchangeRate = data.rates[currency];
purchaseAmount = purchaseAmount / exchangeRate; // Convert to USD
console.log(`Purchase amount in USD: ${purchaseAmount}`);
console.log(`Exchange rate: ${exchangeRate}`);
console.log(`Currency: ${currency}`);
}
// Valid referral - calculate commission amount (30%)
const commissionRate = 0.3; // 30% commission
const commissionAmount = purchaseAmount * commissionRate;
// Create a referral record
await db.insert(referrals).values({
referrerId: referrer.id,
referredId: user.id,
referredEmail: user.email,
purchaseAmount: purchaseAmount.toString(),
commissionAmount: commissionAmount.toString(),
planCodename: planCodename,
status: "pending", // Pending approval
});
// Send email to referrer
await sendMail(
referrer.email!,
"You earned a commission!",
`
<h1>You earned a commission!</h1>
<p>Great news! Someone just purchased Indie Kit Pro using your referral link.</p>
<p><strong>Purchase details:</strong></p>
<ul>
<li>Product: ${planCodename}</li>
<li>Purchase amount: $${purchaseAmount.toFixed(2)}</li>
<li>Your commission (30%): $${commissionAmount.toFixed(2)}</li>
</ul>
<p>Your commission is now pending approval and will be processed in the next payout cycle.</p>
<p><a href="https://indiekit.pro/app/my-referrals">View your referrals dashboard</a> to track this and other commissions.</p>
<p>Thank you for being part of our affiliate program!</p>
`
);
console.log(
`Referral recorded successfully for referrer: ${
referrer.email
}, amount: $${commissionAmount.toFixed(2)}`
);
}
}
} catch (error) {
console.error("Error processing referral:", error);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment