Skip to content
arminrad edited this page Mar 16, 2026 · 2 revisions

Referral System

User referral program with automatic bonus rewards


Overview

Users can invite friends and earn rewards. When a referred user makes their first purchase of $10+, both users receive a $10 bonus.

Key Features

  • $10 minimum purchase requirement
  • $10 bonus for both referrer and referee
  • 10 uses per code maximum
  • First purchase only bonus
  • Automatic credit addition via webhook

How It Works

1. Alice registers → Gets code "ABC12345"
2. Bob registers with Alice's code → Stores referred_by_code
3. Bob makes first purchase $10+ → Webhook processes
4. System validates:
   ✓ First purchase?
   ✓ Has referral code?
   ✓ Purchase >= $10?
5. Apply rewards:
   → Bob: $10 (payment) + $10 (bonus) = $20
   → Alice: $10 (bonus)
6. Mark Bob's first purchase complete
7. Create referral record

Quick Start

Get Your Referral Code

curl http://localhost:8000/referral/code \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "referral_code": "ABC12345",
  "user_id": 1,
  "username": "alice"
}

Register with Referral Code

curl -X POST http://localhost:8000/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "bob",
    "email": "bob@example.com",
    "auth_method": "email",
    "referral_code": "ABC12345"
  }'

Check Referral Stats

curl http://localhost:8000/referral/stats \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "referral_code": "ABC12345",
  "total_uses": 2,
  "remaining_uses": 8,
  "max_uses": 10,
  "total_earned": 20.0,
  "referrals": [
    {
      "user_id": 5,
      "username": "bob",
      "bonus_earned": 10.0
    }
  ]
}

API Endpoints

1. Get Referral Code

GET /referral/code

Auth: Required (Bearer token)

Response:

  • Creates code if user doesn't have one
  • Code format: 8 chars (uppercase + digits)
  • Each code is unique

2. Validate Referral Code

POST /referral/validate

Request:

{
  "referral_code": "ABC12345"
}

Response (Valid):

{
  "valid": true,
  "message": "Referral code is valid",
  "referrer": {
    "username": "alice",
    "email": "alice@example.com"
  },
  "remaining_uses": 8
}

Response (Invalid):

{
  "valid": false,
  "message": "Invalid referral code",
  "error": "Code not found"
}

3. Get Referral Statistics

GET /referral/stats

Auth: Required

Response Fields:

  • referral_code - Your code
  • total_uses - Times code was used
  • remaining_uses - Uses left (max 10)
  • total_earned - Total bonus earned
  • current_balance - Current credit balance
  • referred_by_code - Code you used (if any)
  • referrals - List of users who used your code

Database Schema

Users Table

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  username TEXT UNIQUE,
  email TEXT UNIQUE,
  credits NUMERIC,                    -- Credit balance
  referral_code TEXT(8) UNIQUE,       -- User's code
  referred_by_code TEXT(8),           -- Code used at signup
  has_made_first_purchase BOOLEAN,    -- Bonus eligibility
  api_key TEXT
);

Referrals Table

CREATE TABLE referrals (
  id INTEGER PRIMARY KEY,
  referrer_id INTEGER,                -- Code owner
  referred_user_id INTEGER,           -- Code user
  referral_code TEXT,                 -- Code used
  bonus_amount NUMERIC DEFAULT 10.0,  -- Bonus given
  status TEXT,                        -- 'completed'
  completed_at TIMESTAMP
);

Implementation Details

Code Generation

Location: src/db/referral.py

def generate_referral_code() -> str:
    """Generate unique 8-character code"""
    import random, string
    chars = string.ascii_uppercase + string.digits
    return ''.join(random.choices(chars, k=8))

Bonus Application

Location: src/routes/payments.py (webhook handler)

Triggers when:

  1. Stripe webhook receives checkout.session.completed
  2. User has referred_by_code set
  3. has_made_first_purchase is False
  4. Purchase amount >= $10

Actions:

  1. Add $10 to referee's credits
  2. Add $10 to referrer's credits
  3. Set has_made_first_purchase = True
  4. Create referral record

Business Rules

Minimum Purchase

  • Amount: $10.00 USD
  • Applies to: First purchase only
  • Currency: USD (1000 cents)

Code Limits

  • Max uses: 10 per code
  • Format: 8 characters (A-Z, 0-9)
  • Uniqueness: Globally unique

Bonus Rules

  • Referrer bonus: $10.00
  • Referee bonus: $10.00
  • Timing: Immediately after payment
  • Method: Automatic (webhook)

Testing Flow

Complete End-to-End Test

# 1. Alice registers
curl -X POST http://localhost:8000/auth/register \
  -H "Content-Type: application/json" \
  -d '{"username":"alice","email":"alice@example.com","auth_method":"email"}'

# Save API key: ALICE_API_KEY

# 2. Get Alice's referral code
curl http://localhost:8000/referral/code \
  -H "Authorization: Bearer $ALICE_API_KEY"

# Save code: ALICE_CODE="ABC12345"

# 3. Bob registers with Alice's code
curl -X POST http://localhost:8000/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "username":"bob",
    "email":"bob@example.com",
    "auth_method":"email",
    "referral_code":"ABC12345"
  }'

# Save API key: BOB_API_KEY

# 4. Bob creates checkout for $10
curl -X POST http://localhost:8000/api/stripe/checkout-session \
  -H "Authorization: Bearer $BOB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount":1000,"currency":"usd"}'

# 5. Complete payment at returned URL
# Use test card: 4242 4242 4242 4242

# 6. Verify Alice got bonus
curl http://localhost:8000/referral/stats \
  -H "Authorization: Bearer $ALICE_API_KEY"
# Should show: total_earned: 10.0

# 7. Verify Bob got payment + bonus
curl http://localhost:8000/user/balance \
  -H "Authorization: Bearer $BOB_API_KEY"
# Should show: credits: 20.0

Frontend Integration

Display Referral Code

interface ReferralData {
  referral_code: string;
  total_uses: number;
  remaining_uses: number;
  total_earned: number;
}

async function getReferralCode(): Promise<ReferralData> {
  const response = await fetch('/referral/code', {
    headers: {
      'Authorization': `Bearer ${apiKey}`
    }
  });
  return await response.json();
}

// Usage
const data = await getReferralCode();
console.log(`Your code: ${data.referral_code}`);
console.log(`Share link: https://app.gatewayz.ai/signup?ref=${data.referral_code}`);

Validate Code on Signup

async function validateReferralCode(code: string): Promise<boolean> {
  const response = await fetch('/referral/validate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ referral_code: code })
  });
  const result = await response.json();
  return result.valid;
}

Show Referral Stats

function ReferralStats() {
  const [stats, setStats] = useState(null);

  useEffect(() => {
    fetch('/referral/stats', {
      headers: { 'Authorization': `Bearer ${apiKey}` }
    })
      .then(res => res.json())
      .then(setStats);
  }, []);

  if (!stats) return <div>Loading...</div>;

  return (
    <div className="referral-stats">
      <h3>Your Referral Stats</h3>
      <div>Code: <strong>{stats.referral_code}</strong></div>
      <div>Used: {stats.total_uses} / {stats.max_uses}</div>
      <div>Total Earned: ${stats.total_earned}</div>
      <div>
        <h4>Referrals ({stats.referrals.length})</h4>
        <ul>
          {stats.referrals.map(r => (
            <li key={r.user_id}>
              {r.username} - ${r.bonus_earned}
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}

Edge Cases

Code Already Used 10 Times

  • Validation returns valid: false
  • Error: "Referral code has reached maximum uses"

User Already Made Purchase

  • Bonus not applied
  • User still gets purchased credits
  • No referral record created

Invalid Code

  • Registration succeeds
  • No bonus applied
  • referred_by_code remains NULL

Purchase < $10

  • No bonus applied
  • User gets purchased credits only
  • First purchase flag remains False

Monitoring

Check Referral Activity

-- Top referrers
SELECT u.username, u.referral_code, COUNT(*) as referrals
FROM users u
JOIN referrals r ON u.id = r.referrer_id
GROUP BY u.id
ORDER BY referrals DESC
LIMIT 10;

-- Recent referrals
SELECT * FROM referrals
ORDER BY completed_at DESC
LIMIT 20;

-- Total bonus paid
SELECT SUM(bonus_amount * 2) as total_bonuses
FROM referrals;

Analytics Queries

-- Conversion rate
SELECT
  COUNT(DISTINCT referred_user_id) as total_referred,
  COUNT(*) as total_converted,
  (COUNT(*) * 100.0 / COUNT(DISTINCT referred_user_id)) as conversion_rate
FROM referrals;

-- Average time to first purchase
SELECT AVG(completed_at - created_at) as avg_time
FROM referrals r
JOIN users u ON r.referred_user_id = u.id;

Configuration

Adjust Bonus Amount

Edit src/routes/payments.py:

REFERRAL_BONUS = 10.0  # Change to desired amount
MIN_PURCHASE = 10.0    # Minimum purchase for bonus

Change Code Format

Edit src/db/referral.py:

def generate_referral_code() -> str:
    # Change length or character set
    return ''.join(random.choices(chars, k=8))  # k=length

Modify Max Uses

Edit src/routes/referral.py:

MAX_REFERRAL_USES = 10  # Change maximum uses

Related Documentation


Statistics

  • Bonus per referral: $20 total ($10 each)
  • Max uses per code: 10
  • Minimum purchase: $10
  • Implementation: src/db/referral.py, src/routes/referral.py

Last Updated: December 2024 Status: Production Ready

For questions: See API Reference or Troubleshooting


Related

Clone this wiki locally