Skip to main content

๐Ÿค 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 code
    • affiliate.clicks (Number) - Total click count
    • affiliate.enabled (Boolean) - Program participation status
    • referred_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 reference
    • amount.total (Number) - Total payout amount (cents)
    • amount.software (Number) - Software commission (cents)
    • amount.fulfillment (Number) - Fulfillment commission (cents)
    • status ('PENDING' | 'PAID') - Payout status
    • period_start (Date) - Payout period start
    • period_end (Date) - Payout period end
    • paid_at (Date) - Payment completion timestamp

affiliates-leaderboardโ€‹

  • Purpose: Store affiliate performance rankings
  • Model: shared/models/affiliates-leaderboard.js
  • Key Fields:
    • account (ObjectId) - Affiliate account
    • type (String) - Ranking type ('monthly', 'yearly', 'all-time')
    • rank (Number) - Current position
    • previous_rank (Number) - Last period position
    • score (Number) - Performance metric
    • updated_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 account
    • connected_account ('platform') - DashClicks platform invoices
    • status ('paid') - Only paid invoices count
    • lines.data[].price.id (String) - Stripe price ID
    • lines.data[].type ('subscription') - Subscription charges
    • created (Number) - Unix timestamp

_store.pricesโ€‹

  • Purpose: Categorize products for commission calculation
  • Model: shared/models/store-price.js
  • Key Fields:
    • stripe_id (String) - Stripe price identifier
    • metadata.software ('true') - Software subscription flag
    • metadata.product_type (String) - Product category
    • platform_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.invoices for 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-leaderboard collection
  • Joins with crm.contacts for business name
  • Joins with _users for 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 number
  • limit (Number, default: 25) - Results per page
  • sortField (String, default: 'signed_up') - Sort column
  • sortOrder (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 accounts with _store.invoices for 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:

  1. User clicks affiliate link: /v1/PARTNER123
  2. Redirector increments clicks and redirects to: https://auth.dashclicks.com/signup?a={account_id}
  3. Auth service receives ?a= parameter during signup
  4. New account document created with referred_by field 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:

  1. Calculate period totals (monthly aggregation)
  2. Create affiliate-payout document with status='PENDING'
  3. Admin reviews and approves payouts
  4. Status changes to 'PAID' after processing
  5. 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.software and amount.fulfillment stored 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.invoices which can be large
  • Filter early: created > 1643086800, status = 'paid'
  • Use $match before $lookup to 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)
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM