๐ค Affiliates Module
๐ Overviewโ
The Affiliates module manages DashClicks' partner and affiliate program, enabling revenue sharing through referral tracking, commission calculations, and affiliate link management. Partners earn commissions when referred users sign up and purchase services.
File Path: internal/api/v1/affiliates/
Key Featuresโ
- Referral Tracking: Track signups via affiliate links
- Commission Calculation: Automatic commission calculations based on tiers
- Revenue Sharing: Multi-tier commission structures
- Affiliate Links: Custom branded referral URLs
- Click Tracking: Monitor link clicks and conversions
- Commission Payouts: Track earned and paid commissions
- Performance Analytics: Affiliate dashboard metrics
๏ฟฝ Directory Structureโ
affiliates/
โโโ ๐ index.js - Module router
โโโ ๐ controllers/
โ โโโ redirector.js - Affiliate link click handler
โ โโโ web.js - Dashboard API endpoints
โโโ ๐ providers/
โ โโโ calculator.js - Commission calculation logic
โโโ ๐ routes/
โโโ index.js - Route definitions
๏ฟฝ๐๏ธ Collections Usedโ
accountsโ
- Purpose: Store affiliate program data within account documents
- Model:
shared/models/account.js - Affiliate Subdocument Fields:
affiliate.code(String, unique) - Affiliate tracking codeaffiliate.clicks(Number) - Total click countaffiliate.enabled(Boolean) - Program participation statusreferred_by(ObjectId) - Parent account that referred this account
affiliate-payoutโ
- Purpose: Track commission payouts for affiliates
- Model:
shared/models/affiliate-payout.js - Key Fields:
account(ObjectId) - Affiliate account referenceamount.total(Number) - Total payout amount (cents)amount.software(Number) - Software commission (cents)amount.fulfillment(Number) - Fulfillment commission (cents)status('PENDING' | 'PAID') - Payout statusperiod_start(Date) - Payout period startperiod_end(Date) - Payout period endpaid_at(Date) - Payment completion timestamp
affiliates-leaderboardโ
- Purpose: Store affiliate performance rankings
- Model:
shared/models/affiliates-leaderboard.js - Key Fields:
account(ObjectId) - Affiliate accounttype(String) - Ranking type ('monthly', 'yearly', 'all-time')rank(Number) - Current positionprevious_rank(Number) - Last period positionscore(Number) - Performance metricupdated_at(Date)
_store.invoicesโ
- Purpose: Track paid invoices for commission calculations
- Model:
shared/models/store-invoice.js - Key Fields (for affiliate tracking):
metadata.account_id(ObjectId) - Referred accountconnected_account('platform') - DashClicks platform invoicesstatus('paid') - Only paid invoices countlines.data[].price.id(String) - Stripe price IDlines.data[].type('subscription') - Subscription chargescreated(Number) - Unix timestamp
_store.pricesโ
- Purpose: Categorize products for commission calculation
- Model:
shared/models/store-price.js - Key Fields:
stripe_id(String) - Stripe price identifiermetadata.software('true') - Software subscription flagmetadata.product_type(String) - Product categoryplatform_type('dashclicks')connected_account('platform')
๐ Data Flowโ
Referral Tracking Flowโ
sequenceDiagram
participant Visitor
participant Redirector as Affiliate Redirector
participant DB as MongoDB (accounts)
participant Auth as Auth Service
participant Signup as Signup Process
Visitor->>Redirector: GET /v1/:code (e.g., /v1/PARTNER123)
Redirector->>DB: Find account by affiliate.code
alt Affiliate Found
DB-->>Redirector: Account with affiliate data
Redirector->>DB: Increment affiliate.clicks counter
Redirector->>Redirector: Build redirect URL with account ID
Redirector-->>Visitor: 302 Redirect to auth.dashclicks.com/signup?a={account_id}
Visitor->>Auth: Visit signup page with affiliate parameter
Auth->>Signup: User completes registration
Signup->>DB: Create account with referred_by field
Note over Visitor,DB: Referral relationship established
else Affiliate Not Found
Redirector-->>Visitor: 302 Redirect to auth.dashclicks.com/signup (no tracking)
end
Commission Calculation Flowโ
flowchart TD
A[Referred Account Makes First Purchase] --> B[Query Store Invoices]
B --> C{Invoice Status?}
C -->|Paid| D[Separate Software vs Fulfillment]
C -->|Unpaid| Z[No Commission]
D --> E[Filter Software Subscriptions]
D --> F[Filter Fulfillment Services]
E --> G[Calculate Software Total]
F --> H[Calculate Fulfillment Total]
G --> I[Apply 40% Commission Rate]
H --> I
I --> J[Round to Cents]
J --> K[Create/Update Payout Record]
K --> L{Payout Status?}
L -->|PENDING| M[Add to Unpaid Balance]
L -->|PAID| N[Add to Lifetime Earnings]
M --> O[Display in Stats API]
N --> O
๐ง Core Functionsโ
Redirectorโ
File: controllers/redirector.js
Function: Handles affiliate link clicks and redirects with tracking.
Route: GET /v1/:code
Actual Implementation:
const redirector = async (req, res, next) => {
try {
// 1. Find account by affiliate code
let account = await Account.findOne({ 'affiliate.code': req.params.code });
// 2. If not found, redirect to signup without tracking
if (!account) {
return res.redirect(`https://auth.dashclicks.com/signup`);
}
// 3. Increment click counter
account.affiliate.clicks = (account.affiliate.clicks || 0) + 1;
await account.save();
// 4. Build redirect URL with account ID parameter
let rURL = `https://auth.dashclicks.com/signup?a=${account.id.toString()}`;
// 5. Redirect to signup with affiliate tracking
res.redirect(rURL);
} catch (err) {
next(err);
}
};
Key Details:
- No cookie tracking - uses URL parameter
?a={account_id} - Simple click counting stored in affiliate.clicks field
- Always redirects to auth.dashclicks.com (centralized auth service)
- No complex session or database click records
Commission Calculatorโ
File: providers/calculator.js (700 lines)
Function: Calculate affiliate statistics, payouts, and referral performance.
Main Functions:
1. getStats(account_id, { affiliate })โ
Calculates comprehensive affiliate performance metrics.
Returns:
{
total_clicks: 1250, // From account.affiliate.clicks
trialing_referrals: 15, // Referrals still in trial
active_referrals: 42, // Converted paying customers
all_referrals: 57, // Total referred accounts
this_month_so_far: { // Current month earnings
currency: '$',
value: 18500 // Cents (40% of subscriptions)
},
to_be_paid: { // Pending payouts
currency: '$',
value: 45000 // From PENDING payout records
},
lifetime_earning: { // All-time paid out
currency: '$',
value: 123000 // From PAID payout records
},
platform_lifetime_earning: { // Software commissions only
currency: '$',
value: 89000
},
fulfillment_lifetime_earning: { // Fulfillment commissions only
currency: '$',
value: 34000
}
}
Commission Calculation:
const evaluateCommission = (total = 0) => {
let commission = 0.4; // 40% commission rate
return Math.round(total * commission);
};
Key Logic:
- Queries
_store.invoicesfor paid invoices from referred accounts - Separates software subscriptions from fulfillment services
- Only counts invoices created after Jan 25, 2022 (Unix: 1643086800)
- Applies 40% flat commission rate across all products
- Aggregates totals using MongoDB aggregation pipelines
2. getLeaderboard(type)โ
Retrieves top 10 affiliates by ranking.
Parameters:
type(String) - Ranking period: 'monthly', 'yearly', 'all-time'
Returns:
[
{
id: 'account_id',
name: 'John Doe',
avatar: 'https://...',
change: 'up', // 'up', 'down', 'none'
order: 1, // Rank position
},
// ... top 10
];
Logic:
- Queries
affiliates-leaderboardcollection - Joins with
crm.contactsfor business name - Joins with
_usersfor owner details - Calculates rank change from previous period
- Sorts by rank ascending, limits to 10
3. getReferrals(account_id, { page, limit, sortField, sortOrder })โ
Lists detailed referral information with pagination.
Parameters:
page(Number, default: 1) - Page numberlimit(Number, default: 25) - Results per pagesortField(String, default: 'signed_up') - Sort columnsortOrder(String, default: 'desc') - Sort direction
Returns:
{
referrals: [
{
account_id: "...",
business_name: "...",
signed_up: "2023-10-15",
status: "active",
software_purchases: 29900, // Cents
fulfillment_purchases: 15000, // Cents
total_commission: 17960 // 40% of total
}
],
pagination: {
total: 57,
page: 1,
pages: 3
}
}
Complex Aggregation:
- Joins
accountswith_store.invoicesfor purchase history - Separates software vs fulfillment purchases
- Filters by Stripe price IDs (software and managed subscriptions)
- Only includes paid invoices
- Calculates commission per referral (40% of purchases)
- Supports sorting and pagination
Web Controllerโ
File: controllers/web.js
Endpoints:
GET /api/v1/affiliates/statsโ
Returns affiliate performance metrics for authenticated user.
Authentication: Required (JWT token)
Response: Output from getStats() function (see above)
GET /api/v1/affiliates/leaderboard?type={period}โ
Returns top 10 affiliates for specified period.
Query Parameters:
type(String) - 'monthly', 'yearly', or 'all-time'
Response: Array of top 10 rankings
GET /api/v1/affiliates/referralsโ
Lists all referrals for authenticated affiliate with pagination.
Query Parameters:
page(Number, default: 1)limit(Number, default: 25)sortField(String, default: 'signed_up')sortOrder(String, default: 'desc')
Response: Referral list with pagination metadata
GET /api/v1/affiliates/trial-setting/:idโ
Gets trial settings for a specific product/service.
Parameters:
id(String) - Product/service identifier
Response: Trial length configuration
๐ Integration Pointsโ
Signup Processโ
Integration: User registration receives affiliate tracking parameter.
Flow:
- User clicks affiliate link:
/v1/PARTNER123 - Redirector increments clicks and redirects to:
https://auth.dashclicks.com/signup?a={account_id} - Auth service receives
?a=parameter during signup - New account document created with
referred_byfield set to affiliate account ID
// In auth service during signup
const affiliateAccountId = req.query.a;
if (affiliateAccountId) {
newAccount.referred_by = affiliateAccountId;
await newAccount.save();
}
### Store Invoices
**Integration**: Paid invoices are aggregated for commission calculations.
**Commission Calculation Process**:
1. Query all accounts where `referred_by` equals affiliate account ID
2. Find all `_store.invoices` for those referred accounts where:
- `status` = 'paid'
- `connected_account` = 'platform' (DashClicks services)
- `created` > 1643086800 (after Jan 25, 2022)
- Invoice lines contain subscription charges
3. Separate invoices by product type:
- **Software**: Prices with `metadata.software = 'true'`
- **Fulfillment**: Prices with managed subscription types
4. Calculate totals for each category
5. Apply 40% commission rate
6. Create/update payout records in `affiliate-payout` collection
**Example Query**:
```javascript
const invoices = await Invoice.aggregate([
{
$match: {
'metadata.account_id': { $in: referredAccountIds },
connected_account: 'platform',
status: 'paid',
created: { $gt: 1643086800 }
}
},
{
$group: {
_id: null,
total: { $sum: '$amount_paid' }
}
}
]);
const commission = Math.round(invoices[0].total * 0.4);
Payout Systemโ
Integration: Monthly batch processing creates payout records.
Process:
- Calculate period totals (monthly aggregation)
- Create
affiliate-payoutdocument with status='PENDING' - Admin reviews and approves payouts
- Status changes to 'PAID' after processing
- Affiliates see updated earnings in Stats API
โ๏ธ Configurationโ
Commission Structureโ
Commission Rate: 40% flat rate across all products
Product Categories:
- Software Subscriptions: 40% commission (identified by
metadata.software = 'true') - Fulfillment Services: 40% commission (managed subscriptions)
Tracking Cutoff: January 25, 2022 (Unix timestamp: 1643086800)
- Only invoices created after this date count toward commissions
Constants (from utilities/constants):
COMMISSION_RATE: 0.4 (40%)MANAGED_SUBSCRIPTIONS: Array of fulfillment product types
Tracking Methodโ
No Cookie Tracking: System uses URL parameters instead of cookies.
Redirect Pattern:
Affiliate Link: https://app.dashclicks.com/v1/PARTNER123
โ
Redirects to: https://auth.dashclicks.com/signup?a={account_id}
Referral Storage: account.referred_by field stores affiliate account ID
Payout Processingโ
- Status Flow: PENDING โ PAID
- Manual Review: Admin approval required before payment
- Tracking: Separate totals for software vs fulfillment commissions
โ ๏ธ Important Notesโ
- ๐ URL Parameter Tracking: Uses
?a={account_id}in signup URL (no cookies) - ๐ฐ Lifetime Commissions: Affiliates earn 40% on ALL purchases by referred accounts (not just first)
- ๏ฟฝ Historical Cutoff: Only tracks purchases after Jan 25, 2022
- ๐ Simple Click Tracking: Just increments counter, no detailed analytics
- ๐ณ Two-Status System: Payouts are either PENDING or PAID
- ๐ฏ Flat Rate: No tiers - 40% commission for all affiliates
- ๐ Leaderboard System: Rankings tracked by period (monthly/yearly/all-time)
๐งช Edge Cases & Special Handlingโ
Case: No Affiliate Code Foundโ
Condition: User visits /v1/INVALID_CODE
Handling: Redirects to https://auth.dashclicks.com/signup without tracking parameter
if (!account) {
return res.redirect(`https://auth.dashclicks.com/signup`);
}
Case: Referred Account Makes Multiple Purchasesโ
Condition: Referred customer continues buying over time
Handling:
- All paid invoices count toward affiliate commissions (no limit)
- Aggregation queries sum lifetime purchase value
- 40% commission applies to each invoice
Case: Software vs Fulfillment Separationโ
Condition: Need to track different commission sources
Handling:
- Separate aggregation pipelines for each product type
amount.softwareandamount.fulfillmentstored separately in payout records- Frontend can display breakdown in affiliate dashboard
Case: Account Has No Affiliate Programโ
Condition: Account doesn't have affiliate subdocument
Handling:
- Query returns null, redirects to default signup
- Click counter initialization:
account.affiliate.clicks || 0 - Safe handling prevents errors for non-affiliate accounts
Case: Historical Data Before Cutoffโ
Condition: Referred account has purchases before Jan 25, 2022
Handling:
- Aggregation filter:
created: { $gt: 1643086800 } - Old invoices excluded from commission calculations
- Prevents retroactive commission claims
๐ Performance Considerationsโ
Indexing Requirementsโ
Critical Indexes:
// accounts collection
{ 'affiliate.code': 1 } // For redirector lookups
{ referred_by: 1 } // For finding referrals
// affiliate-payout collection
{ account: 1, status: 1 } // For stats aggregation
// affiliates-leaderboard collection
{ type: 1, rank: 1 } // For leaderboard queries
// _store.invoices collection
{ 'metadata.account_id': 1, status: 1, created: 1 } // For commission calc
{ connected_account: 1, status: 1, 'lines.data.price.id': 1 }
Aggregation Performanceโ
Complex Queries:
getStats(): Multiple aggregation pipelines (software, fulfillment, payouts)getReferrals(): Joins accounts โ invoices with filtering- Consider caching stats for 15-minute intervals
Optimization Tips:
- Aggregations run on
_store.invoiceswhich can be large - Filter early:
created > 1643086800,status = 'paid' - Use
$matchbefore$lookupto reduce join set size - Separate software/fulfillment queries run in parallel
Caching Strategyโ
- Redirector: Direct database lookup (fast with index on affiliate.code)
- Stats API: Consider caching per affiliate (15-min TTL)
- Leaderboard: Cache by type (hourly refresh)
- Referrals List: No caching (needs real-time accuracy)
Last Updated: 2025-01-08
Module Path:internal/api/v1/affiliates/
Main Routes:
GET /v1/:code(redirector)GET /api/v1/affiliates/stats(dashboard)GET /api/v1/affiliates/leaderboard(rankings)GET /api/v1/affiliates/referrals(list)