Skip to main content

Affiliates Calculator Service

๐Ÿ“– Overviewโ€‹

Service Path: internal/api/v1/affiliates/providers/calculator.js

The Affiliates Calculator service handles all affiliate program calculations and analytics. Core responsibilities include:

  • Commission Calculation: 40% commission on software subscriptions, 10% on managed subscriptions
  • Statistics Aggregation: Real-time stats for clicks, referrals, earnings across subscription types
  • Leaderboard Ranking: Monthly and all-time affiliate rankings with movement tracking
  • Referral Tracking: Detailed referral purchase history with pagination and sorting
  • Revenue Attribution: Separate tracking for software vs managed subscription commissions
  • Payout Management: Paid vs unpaid earnings tracking

๐Ÿ—„๏ธ Collections Usedโ€‹

  • crm.contacts (documentation unavailable) - Business contact information

๐Ÿ”„ Data Flowโ€‹

Commission Calculation Flowโ€‹

flowchart TD
A[Affiliate Referral] --> B[New Account Signup]
B --> C[Account.referred_by = affiliate_id]

C --> D[Account Subscribes]
D --> E{Subscription Type?}

E -->|Software| F[Get Software Prices]
E -->|Managed| G[Get Managed Prices]

F --> H[Invoice Created]
G --> H

H --> I[Invoice Status = Paid]
I --> J{Calculate Commission}

J -->|Software| K[40% Commission]
J -->|Managed| L[10% Commission]

K --> M[Create Payout Record]
L --> M

M --> N{Payout Status}
N -->|Pending| O[Add to Unpaid Earnings]
N -->|Paid| P[Add to Lifetime Earnings]

O --> Q[Update Stats]
P --> Q
Q --> R[Update Leaderboard Rank]

style K fill:#e8f5e9
style L fill:#fff4e6
style M fill:#e3f2fd
style R fill:#f3e5f5

Leaderboard Ranking Flowโ€‹

sequenceDiagram
participant Cron as Cron Job
participant Calculator
participant Leaderboard
participant Payouts
participant Accounts

Cron->>Calculator: Calculate rankings
Calculator->>Payouts: Aggregate total earnings per affiliate
Payouts-->>Calculator: Earnings data

Calculator->>Calculator: Sort by total earnings
Calculator->>Calculator: Assign ranks (1-N)

loop For each affiliate
Calculator->>Leaderboard: Update rank
Calculator->>Leaderboard: Store previous_rank
Calculator->>Leaderboard: Set type (monthly/all-time)
end

Calculator->>Accounts: Join contact/owner data
Accounts-->>Calculator: Affiliate display info

Calculator-->>Cron: Leaderboard updated

Referral Tracking Flowโ€‹

flowchart LR
A[Get Referrals Request] --> B[Find Referred Accounts]
B --> C[Get Software Prices]
B --> D[Get Managed Prices]

C --> E[Lookup Software Invoices]
D --> F[Lookup Managed Invoices]

E --> G[Sum Software Revenue]
F --> H[Sum Managed Revenue]

G --> I[Lookup Software Payouts]
H --> J[Lookup Managed Payouts]

I --> K[Calculate Software Commission]
J --> L[Calculate Managed Commission]

K --> M[Combine Results]
L --> M

M --> N{Sort By}
N -->|Purchases| O[Sort by Revenue]
N -->|Commission| P[Sort by Commission]
N -->|Date| Q[Sort by Signup Date]

O --> R[Apply Pagination]
P --> R
Q --> R

R --> S[Return Referrals]

style E fill:#e8f5e9
style F fill:#fff4e6
style K fill:#e8f5e9
style L fill:#fff4e6

๐Ÿ”ง Business Logic & Functionsโ€‹

evaluateCommission(total)โ€‹

Purpose: Calculate 40% commission on subscription revenue (used for software subscriptions)

Parameters:

  • total (Number, default: 0) - Total subscription amount in cents

Returns:

Number; // Commission amount in cents (rounded)

Business Logic Flow:

  1. Apply Commission Rate

    let commission = 0.4; // 40% commission rate
    return Math.round(total * commission);

Key Business Rules:

  • Fixed Rate: 40% commission (primarily for software subscriptions)
  • Rounding: Commission rounded to nearest cent
  • Currency: Assumes amounts in cents

Example Usage:

// $100.00 subscription (10000 cents)
const commission = evaluateCommission(10000);
// Returns: 4000 (40% = $40.00)

// $29.99 subscription (2999 cents)
const commission = evaluateCommission(2999);
// Returns: 1200 (40% โ‰ˆ $12.00)

Side Effects:

  • None (pure calculation function)

getStats(account_id, { affiliate })โ€‹

Purpose: Aggregate comprehensive affiliate statistics including referrals, earnings, and monthly revenue

Parameters:

  • account_id (String|ObjectId) - Affiliate account ID
  • affiliate (Object, optional) - Affiliate metadata
    • clicks (Number) - Total referral link clicks

Returns:

{
total_clicks: Number, // Referral link clicks
trialing_referrals: Number, // Accounts in trial period
active_referrals: Number, // Active paid subscriptions
all_referrals: Number, // Total referred accounts
this_month_so_far: {
currency: String, // '$'
value: Number // Commission earned this month (cents)
},
to_be_paid: {
currency: String, // '$'
value: Number // Pending unpaid commission (cents)
},
lifetime_earning: {
currency: String, // '$'
value: Number // Total paid commission (cents)
},
platform_lifetime_earning: {
currency: String, // '$'
value: Number // Software subscription commission (cents)
},
fulfillment_lifetime_earning: {
currency: String, // '$'
value: Number // Managed subscription commission (cents)
}
}

Business Logic Flow:

  1. Identify Software Prices

    let softwarePrices = await StorePrice.find({
    'metadata.software': 'true',
    $or: [
    {
    platform_type: 'dashclicks',
    connected_account: 'platform',
    },
    ],
    });

    softwarePrices = softwarePrices.reduce((a, c) => {
    c = c.toJSON();
    a.push(c.stripe_id);
    return a;
    }, []);
    • Filters store prices with software metadata flag
    • Platform-level subscriptions only
    • Collects Stripe price IDs
  2. Get Referred Account IDs

    const monthStart = moment().startOf('M');

    let referredIds = await Account.find({ referred_by: account_id }, '_id');
    referredIds = referredIds.map(a => a._id);
    • Finds all accounts referred by this affiliate
    • Extracts account IDs for aggregation
  3. Aggregate Subscription Statistics

    const subscriptionStats = await Subscription.aggregate([
    {
    $match: {
    $and: [{ account: { $in: referredIds } }, { status: { $in: ['trialing', 'active'] } }],
    },
    },
    {
    $facet: {
    trialing: [
    {
    $match: {
    connected_account: { $eq: 'platform' },
    'plan.id': { $in: softwarePrices },
    status: 'trialing',
    },
    },
    {
    $group: {
    _id: 'total',
    total: { $sum: 1 },
    },
    },
    ],
    active: [
    {
    $match: {
    connected_account: { $eq: 'platform' },
    'plan.id': { $in: softwarePrices },
    status: 'active',
    },
    },
    {
    $group: {
    _id: 'total',
    total: { $sum: 1 },
    },
    },
    ],
    this_month_software: [
    {
    $match: {
    status: 'active',
    connected_account: { $eq: 'platform' },
    'plan.id': { $in: softwarePrices },
    current_period_start: { $gte: monthStart.unix() },
    },
    },
    {
    $group: {
    _id: 'total',
    total: { $sum: '$plan.amount' },
    },
    },
    ],
    this_month_fulfillment: [
    {
    $match: {
    status: 'active',
    connected_account: { $eq: 'platform' },
    'plan.id': { $nin: softwarePrices },
    current_period_start: { $gte: monthStart.unix() },
    },
    },
    {
    $group: {
    _id: 'total',
    total: { $sum: '$plan.amount' },
    },
    },
    ],
    },
    },
    ]);
    • trialing: Counts trial subscriptions
    • active: Counts active paid subscriptions
    • this_month_software: Sums software subscription revenue for current month
    • this_month_fulfillment: Sums managed subscription revenue for current month
  4. Aggregate Payout Earnings

    let earnings = await AffiliatesPayouts.aggregate([
    {
    $match: {
    account: new ObjectId(account_id),
    },
    },
    {
    $facet: {
    paid_earnings: [
    {
    $match: {
    status: 'PAID',
    },
    },
    {
    $group: {
    _id: null,
    total: { $sum: '$amount.total' },
    software: { $sum: '$amount.software' },
    fulfillment: { $sum: '$amount.fulfillment' },
    },
    },
    ],
    unpaid_earnings: [
    {
    $match: {
    status: 'PENDING',
    },
    },
    {
    $group: {
    _id: null,
    total: { $sum: '$amount.total' },
    software: { $sum: '$amount.software' },
    fulfillment: { $sum: '$amount.fulfillment' },
    },
    },
    ],
    },
    },
    ]);
    • paid_earnings: Total commission already paid
    • unpaid_earnings: Pending commission not yet paid
    • Separates software vs fulfillment commission
  5. Build Response Object

    return {
    total_clicks: affiliate?.clicks || 0,
    trialing_referrals: referredIds.length - (subscriptionStats?.[0]?.active?.[0]?.total || 0),
    active_referrals: subscriptionStats?.[0]?.active?.[0]?.total || 0,
    all_referrals: referredIds.length,
    this_month_so_far: {
    currency: '$',
    value: evaluateCommission(
    subscriptionStats?.[0]?.this_month_software?.[0]?.total,
    subscriptionStats?.[0]?.active?.[0]?.total,
    ),
    },
    to_be_paid: {
    currency: '$',
    value: earnings?.[0].unpaid_earnings?.[0]?.total || 0,
    },
    lifetime_earning: {
    currency: '$',
    value: earnings?.[0].paid_earnings?.[0]?.total || 0,
    },
    platform_lifetime_earning: {
    currency: '$',
    value: earnings?.[0].paid_earnings?.[0]?.software || 0,
    },
    fulfillment_lifetime_earning: {
    currency: '$',
    value: earnings?.[0].paid_earnings?.[0]?.fulfillment || 0,
    },
    };

Key Business Rules:

  • Trialing Calculation: Total referrals - active subscriptions = trialing
  • Month Boundary: Uses moment().startOf('M') for current month start
  • Software vs Fulfillment: Separates commission by subscription type
  • Currency: Currently supports USD only (TODO: multi-currency support)
  • Paid vs Unpaid: Tracks commission lifecycle (PENDING โ†’ PAID)

Example Usage:

const stats = await getStats('507f1f77bcf86cd799439011', {
affiliate: { clicks: 1250 },
});

// Returns:
// {
// total_clicks: 1250,
// trialing_referrals: 12,
// active_referrals: 43,
// all_referrals: 55,
// this_month_so_far: { currency: '$', value: 4580 }, // $45.80
// to_be_paid: { currency: '$', value: 12400 }, // $124.00
// lifetime_earning: { currency: '$', value: 387500 }, // $3,875.00
// platform_lifetime_earning: { currency: '$', value: 310000 }, // $3,100.00
// fulfillment_lifetime_earning: { currency: '$', value: 77500 } // $775.00
// }

Side Effects:

  • None (read-only aggregations)

Performance Considerations:

  • Multiple Aggregations: 2 large aggregations (subscriptions + payouts)
  • Large Datasets: Referral IDs list can grow large
  • Index Requirements:
    • accounts.referred_by
    • subscriptions.account
    • payouts.account

getLeaderboard(type)โ€‹

Purpose: Retrieve top 10 ranked affiliates with ranking movement indicators

Parameters:

  • type (String) - Leaderboard type: 'monthly' or 'all-time'

Returns:

[
{
id: ObjectId, // Affiliate account ID
name: String, // Owner or business name
avatar: String, // Profile image URL
change: String, // 'up', 'down', or 'none'
order: Number, // Current rank (1-10)
},
];

Business Logic Flow:

  1. Fetch Top 10 Rankings

    let rankings = await AffiliatesLeaderboard.aggregate([
    {
    $match: {
    type, // 'monthly' or 'all-time'
    },
    },
    {
    $sort: {
    rank: 1, // Ascending (1 = best)
    },
    },
    {
    $limit: 10,
    },
    // ... joins
    ]);
  2. Join Business Contact Data

    {
    $lookup: {
    from: 'crm.contacts',
    localField: 'account',
    foreignField: 'account',
    as: 'business',
    },
    },
    {
    $set: {
    business: { $first: '$business' },
    },
    }
  3. Join Account Owner Data

    {
    $lookup: {
    from: '_users',
    as: 'owner',
    let: { account: '$account' },
    pipeline: [
    {
    $match: {
    $expr: {
    $eq: ['$account', '$$account'],
    },
    is_owner: true,
    },
    },
    ],
    },
    },
    {
    $set: {
    owner: { $first: '$owner' },
    },
    }
  4. Calculate Ranking Movement

    rankings = rankings.map(r => {
    return {
    id: r.account,
    name: r.owner?.name || r.business?.name,
    avatar: r.owner?.image || r.business?.image,
    change:
    (r.previous_rank &&
    ((r.rank > r.previous_rank && 'down') ||
    (r.rank < r.previous_rank && 'up') ||
    'none')) ||
    'up', // New entries default to 'up'
    order: r.rank,
    };
    });
    • Improved: rank decreased (lower number = better)
    • Declined: rank increased (higher number = worse)
    • Same: rank unchanged
    • New: No previous rank (defaults to 'up')

Key Business Rules:

  • Top 10 Only: Limits to top performers
  • Type Filtering: Separate monthly and all-time leaderboards
  • Name Priority: Uses owner name if available, fallback to business name
  • Avatar Priority: Uses owner image if available, fallback to business image
  • Movement: Calculated from previous_rank comparison

Example Usage:

// Get monthly leaderboard
const monthlyTop = await getLeaderboard('monthly');

// Returns:
// [
// {
// id: '507f1f77bcf86cd799439011',
// name: 'John Doe',
// avatar: 'https://cdn.example.com/avatar1.jpg',
// change: 'up', // Moved up in rankings
// order: 1
// },
// {
// id: '507f1f77bcf86cd799439012',
// name: 'Acme Marketing',
// avatar: 'https://cdn.example.com/avatar2.jpg',
// change: 'down', // Dropped in rankings
// order: 2
// },
// // ... 8 more
// ]

// Get all-time leaderboard
const allTimeTop = await getLeaderboard('all-time');

Side Effects:

  • None (read-only query)

getReferrals(account_id, { page, limit, sortField, sortOrder })โ€‹

Purpose: Retrieve paginated list of referred accounts with revenue and commission details

Parameters:

  • account_id (String|ObjectId) - Affiliate account ID
  • page (Number, default: 1) - Page number for pagination
  • limit (Number, default: 25) - Results per page
  • sortField (String, default: 'signed_up') - Sort field: 'signed_up', 'purchases', 'commission'
  • sortOrder (String, default: 'desc') - Sort direction: 'asc' or 'desc'

Returns:

{
referrals: [
{
id: ObjectId, // Referred account ID
created_at: Date, // Account signup date
subscription_type: String, // 'software' or 'managed'
purchases: Number, // Total revenue in cents
software_commission: Number, // Software commission in cents
fulfillment_commission: Number, // Managed commission in cents
commission_rate: Number // Commission rate percentage (40 or 10)
}
],
pagination: {
total: Number,
page: Number,
limit: Number,
pages: Number,
has_next: Boolean,
has_prev: Boolean
}
}

Business Logic Flow:

  1. Parse Pagination Parameters

    limit = parseInt(limit, 10);
    page = page ? parseInt(page, 10) : 0;
    const skip = page ? (page - 1) * limit : 0;
  2. Identify Subscription Types

    const filteredManagedSubs = MANAGED_SUBSCRIPTIONS.filter(
    sub => !['site', 'listings'].includes(sub),
    );

    const [softwarePricesResult, managedSubscriptionsResult] = await Promise.all([
    StorePrice.aggregate([
    {
    $match: {
    'metadata.software': 'true',
    platform_type: 'dashclicks',
    connected_account: 'platform',
    },
    },
    {
    $group: {
    _id: null,
    stripe_ids: { $addToSet: '$stripe_id' },
    },
    },
    ]),
    StorePrice.aggregate([
    {
    $match: {
    'metadata.product_type': { $in: filteredManagedSubs },
    platform_type: 'dashclicks',
    connected_account: 'platform',
    },
    },
    {
    $group: {
    _id: null,
    stripe_ids: { $addToSet: '$stripe_id' },
    },
    },
    ]),
    ]);

    const softwarePrices = softwarePricesResult[0]?.stripe_ids || [];
    const managedSubscriptions = managedSubscriptionsResult[0]?.stripe_ids || [];
    • Software Prices: Platform software subscriptions
    • Managed Prices: Fulfillment service subscriptions (excluding site/listings)
  3. Aggregate Referral Data

    The aggregation pipeline:

    const result = await Account.aggregate([
    // 1. Find referred accounts
    {
    $match: {
    referred_by: account_id,
    },
    },

    // 2. Lookup software invoice totals
    {
    $lookup: {
    from: '_store.invoices',
    as: 'software_invoices',
    let: { rid: '$_id' },
    pipeline: [
    {
    $match: {
    connected_account: 'platform',
    status: 'paid',
    'lines.data': {
    $elemMatch: {
    'price.id': { $in: softwarePrices },
    type: 'subscription',
    },
    },
    $expr: { $eq: ['$metadata.account_id', '$$rid'] },
    created: { $gt: 1643086800 }, // After Jan 25, 2022
    },
    },
    {
    $group: {
    _id: 'purchases',
    total: {
    $sum: {
    $reduce: {
    input: { $ifNull: ['$lines.data', []] },
    initialValue: 0,
    in: {
    $cond: [
    {
    $and: [
    { $eq: ['$$this.type', 'subscription'] },
    { $in: ['$$this.price.id', softwarePrices] },
    ],
    },
    {
    $add: [
    '$$value',
    {
    $subtract: [
    '$$this.amount',
    {
    $sum: {
    $map: {
    input: { $ifNull: ['$$this.discount_amounts', []] },
    as: 'discount',
    in: '$$discount.amount',
    },
    },
    },
    ],
    },
    ],
    },
    '$$value',
    ],
    },
    },
    },
    },
    },
    },
    ],
    },
    },

    // 3. Lookup managed subscription invoice totals
    {
    $lookup: {
    from: '_store.invoices',
    as: 'managed_invoices',
    let: { rid: '$_id' },
    pipeline: [
    {
    $match: {
    connected_account: 'platform',
    status: 'paid',
    'lines.data': {
    $elemMatch: {
    $and: [
    { 'price.id': { $in: managedSubscriptions } },
    { 'price.id': { $nin: softwarePrices } },
    { type: 'subscription' },
    ],
    },
    },
    $expr: { $eq: ['$metadata.account_id', '$$rid'] },
    created: { $gt: 1751328000 }, // After July 1, 2025
    },
    },
    // Similar $reduce aggregation for managed subs
    ],
    },
    },

    // 4. Lookup software commission payouts
    {
    $lookup: {
    from: '_affiliates.payouts.accounts',
    as: 'software_payouts',
    let: { referred: '$_id' },
    pipeline: [
    {
    $match: {
    $expr: {
    $and: [{ $eq: ['$account', '$$referred'] }, { $gt: ['$amount.software', 0] }],
    },
    },
    },
    {
    $group: {
    _id: 'software_commissions',
    software: { $sum: '$amount.software' },
    },
    },
    ],
    },
    },

    // 5. Lookup managed commission payouts
    {
    $lookup: {
    from: '_affiliates.payouts.accounts',
    as: 'managed_payouts',
    let: { referred: '$_id' },
    pipeline: [
    {
    $match: {
    $expr: {
    $and: [
    { $eq: ['$account', '$$referred'] },
    { $gt: ['$amount.managed_subscriptions', 0] },
    ],
    },
    },
    },
    {
    $group: {
    _id: 'managed_commissions',
    fulfillment: { $sum: '$amount.managed_subscriptions' },
    },
    },
    ],
    },
    },

    // 6. Extract totals from lookup arrays
    {
    $set: {
    software_invoices_total: { $arrayElemAt: ['$software_invoices.total', 0] },
    managed_invoices_total: { $arrayElemAt: ['$managed_invoices.total', 0] },
    software_payouts_total: { $arrayElemAt: ['$software_payouts.software', 0] },
    managed_payouts_total: { $arrayElemAt: ['$managed_payouts.fulfillment', 0] },
    },
    },

    // 7. Separate into software and managed facets
    {
    $facet: {
    software_subscriptions: [
    {
    $match: {
    $or: [
    { software_invoices_total: { $gt: 0 } },
    { software_payouts_total: { $gt: 0 } },
    ],
    },
    },
    {
    $addFields: {
    subscription_type: 'software',
    purchases: { $ifNull: ['$software_invoices_total', 0] },
    software_commission: { $ifNull: ['$software_payouts_total', 0] },
    fulfillment_commission: 0,
    commission_rate: COMMISSION_RATE.SOFTWARE * 100, // 40
    total_commission: { $ifNull: ['$software_payouts_total', 0] },
    },
    },
    {
    $project: {
    id: '$_id',
    created_at: 1,
    subscription_type: 1,
    purchases: 1,
    software_commission: 1,
    fulfillment_commission: 1,
    commission_rate: 1,
    total_commission: 1,
    },
    },
    ],
    managed_subscriptions: [
    {
    $match: {
    $or: [{ managed_invoices_total: { $gt: 0 } }, { managed_payouts_total: { $gt: 0 } }],
    },
    },
    {
    $addFields: {
    subscription_type: 'managed',
    purchases: { $ifNull: ['$managed_invoices_total', 0] },
    software_commission: 0,
    fulfillment_commission: { $ifNull: ['$managed_payouts_total', 0] },
    commission_rate: COMMISSION_RATE.MANAGED_SUBSCRIPTIONS * 100, // 10
    total_commission: { $ifNull: ['$managed_payouts_total', 0] },
    },
    },
    {
    $project: {
    id: '$_id',
    created_at: 1,
    subscription_type: 1,
    purchases: 1,
    software_commission: 1,
    fulfillment_commission: 1,
    commission_rate: 1,
    total_commission: 1,
    },
    },
    ],
    },
    },

    // 8. Combine and flatten results
    {
    $project: {
    all_referrals: {
    $concatArrays: ['$software_subscriptions', '$managed_subscriptions'],
    },
    },
    },
    {
    $unwind: '$all_referrals',
    },
    {
    $replaceRoot: {
    newRoot: '$all_referrals',
    },
    },

    // 9. Apply sorting and pagination
    {
    $facet: {
    total_count: [{ $count: 'count' }],
    data: [
    // Sort by field
    ...(sortField === 'purchases'
    ? [{ $sort: { purchases: sortOrder === 'desc' ? -1 : 1 } }]
    : sortField === 'commission'
    ? [{ $sort: { total_commission: sortOrder === 'desc' ? -1 : 1 } }]
    : [{ $sort: { created_at: sortOrder === 'desc' ? -1 : 1 } }]),
    { $skip: skip },
    { $limit: limit },
    {
    $project: {
    id: 1,
    created_at: 1,
    subscription_type: 1,
    purchases: 1,
    software_commission: 1,
    fulfillment_commission: 1,
    commission_rate: 1,
    },
    },
    ],
    },
    },
    ]);
  4. Build Pagination Response

    const totalCount = result[0]?.total_count[0]?.count || 0;
    const referrals = result[0]?.data || [];

    return {
    referrals,
    pagination: generatePagination(limit, page, totalCount),
    };

Key Business Rules:

  • Revenue Calculation: Sums invoice line items minus discounts
  • Commission Rates:
    • Software: 40% (COMMISSION_RATE.SOFTWARE = 0.4)
    • Managed: 10% (COMMISSION_RATE.MANAGED_SUBSCRIPTIONS = 0.1)
  • Date Filters:
    • Software invoices: After Jan 25, 2022 (1643086800)
    • Managed invoices: After July 1, 2025 (1751328000)
  • Subscription Types: Single account can have both software and managed entries
  • Excludes: Site and listings from managed subscriptions
  • Only Paid: Only counts paid invoices for revenue

Example Usage:

// Get referrals sorted by commission
const result = await getReferrals('507f1f77bcf86cd799439011', {
page: 1,
limit: 25,
sortField: 'commission',
sortOrder: 'desc',
});

// Returns:
// {
// referrals: [
// {
// id: '507f1f77bcf86cd799439012',
// created_at: '2024-06-15T10:30:00Z',
// subscription_type: 'software',
// purchases: 999900, // $9,999.00 in revenue
// software_commission: 399960, // $3,999.60 (40%)
// fulfillment_commission: 0,
// commission_rate: 40
// },
// {
// id: '507f1f77bcf86cd799439013',
// created_at: '2025-08-01T14:20:00Z',
// subscription_type: 'managed',
// purchases: 500000, // $5,000.00 in revenue
// software_commission: 0,
// fulfillment_commission: 50000, // $500.00 (10%)
// commission_rate: 10
// }
// ],
// pagination: {
// total: 55,
// page: 1,
// limit: 25,
// pages: 3,
// has_next: true,
// has_prev: false
// }
// }

// Sort by signup date
const chronological = await getReferrals('507f1f77bcf86cd799439011', {
sortField: 'signed_up',
sortOrder: 'asc',
});

// Sort by revenue
const topEarners = await getReferrals('507f1f77bcf86cd799439011', {
sortField: 'purchases',
sortOrder: 'desc',
limit: 10,
});

Side Effects:

  • None (read-only aggregation)

Performance Considerations:

  • Complex Aggregation: 5 lookups + multiple facets
  • Large Datasets: Invoice and payout collections can be large
  • Index Requirements:
    • accounts.referred_by
    • invoices.metadata.account_id
    • invoices.created
    • payouts.account
  • Optimization: Uses $facet to separate software/managed in single pipeline

findAffiliates()โ€‹

Purpose: Get list of all account IDs that have made referrals

Parameters: None

Returns:

[ObjectId]; // Array of affiliate account IDs

Business Logic Flow:

  1. Aggregate Unique Referrers

    let accounts = await Account.aggregate([
    {
    $match: {
    $and: [{ referred_by: { $exists: true } }, { referred_by: { $ne: null } }],
    },
    },
    {
    $group: {
    _id: null,
    affiliates: {
    $addToSet: '$referred_by',
    },
    },
    },
    ]);

    return accounts[0]?.affiliates || [];
    • Finds all accounts with a referrer
    • Groups to get unique referred_by IDs
    • Returns deduplicated affiliate list

Key Business Rules:

  • Active Affiliates Only: Only accounts that have successfully referred someone
  • Deduplication: Uses $addToSet for unique IDs

Example Usage:

const affiliateIds = await findAffiliates();

// Returns:
// [
// ObjectId('507f1f77bcf86cd799439011'),
// ObjectId('507f1f77bcf86cd799439012'),
// ObjectId('507f1f77bcf86cd799439013')
// ]

// Use for bulk operations
for (const affiliateId of affiliateIds) {
await calculateCommissions(affiliateId);
}

Side Effects:

  • None (read-only query)

Use Cases:

  • Cron Jobs: Process all affiliates for commission calculation
  • Leaderboard Updates: Update rankings for all active affiliates
  • Reporting: Generate affiliate program reports
  • Notifications: Send affiliate performance emails

findTrialSettings(id)โ€‹

Purpose: Get custom trial length for affiliate referrals

Parameters:

  • id (String|ObjectId) - Account ID

Returns:

Number; // Trial length in days (default: 14)

Business Logic Flow:

  1. Validate Input

    if (!id) throw new Error('Account id was not provided');
  2. Fetch Affiliate Settings

    let accounts = await Account.findById(id).select('affiliate');

    return accounts?._doc?.affiliate?.trial_length || 14;
    • Fetches only affiliate field for performance
    • Returns custom trial length or 14-day default

Key Business Rules:

  • Default Trial: 14 days if not configured
  • Custom Trials: Affiliates can offer extended trial periods
  • Validation: Throws error if account ID missing

Example Usage:

// Get trial length for new referral
const trialDays = await findTrialSettings('507f1f77bcf86cd799439011');
// Returns: 14 (or custom value like 30)

// Apply to new subscription
const trialEnd = moment().add(trialDays, 'days').unix();
await createSubscription({
account_id: newAccountId,
trial_end: trialEnd,
});

Error Handling:

try {
const trialDays = await findTrialSettings(null);
} catch (error) {
console.error(error.message);
// "Account id was not provided"
}

Side Effects:

  • None (read-only query)

๐Ÿ”€ Integration Pointsโ€‹

External Servicesโ€‹

Stripe (via store-invoices):

  • Invoice data for revenue tracking
  • Line item details for commission calculation
  • Discount amounts for net revenue

Internal Servicesโ€‹

Store Module:

  • Subscriptions: Active subscription status and amounts
  • Prices: Product metadata (software vs managed)
  • Invoices: Paid invoice history for revenue

Accounts Module:

  • Referral Tracking: referred_by field linking
  • Trial Settings: Custom trial length configuration

CRM Module:

  • Contacts: Business information for leaderboard display

Users Module:

  • Owner Data: User profile information for leaderboard

Cron Jobsโ€‹

Commission Calculation:

// Daily cron job
const affiliates = await findAffiliates();

for (const affiliateId of affiliates) {
const stats = await getStats(affiliateId);
// Process commissions
await createPayouts(affiliateId, stats);
}

Leaderboard Updates:

// Monthly cron job
await updateLeaderboard('monthly');

// Weekly all-time update
await updateLeaderboard('all-time');

๐Ÿงช Edge Cases & Special Handlingโ€‹

Zero Revenue Referralsโ€‹

Accounts with No Purchases:

// getReferrals filters these out
{
$match: {
$or: [
{ software_invoices_total: { $gt: 0 } },
{ software_payouts_total: { $gt: 0 } },
],
},
}

// Only includes accounts that generated revenue or commission

Multi-Subscription Type Accountsโ€‹

Same Account, Multiple Entries:

// Single referral can appear twice if they have both subscription types
[
{
id: '507f1f77bcf86cd799439011',
subscription_type: 'software',
purchases: 10000,
software_commission: 4000,
},
{
id: '507f1f77bcf86cd799439011', // Same account
subscription_type: 'managed',
purchases: 5000,
fulfillment_commission: 500,
},
];

Trial Period Calculationโ€‹

Trialing vs Active:

// Trialing = Total referrals - Active referrals
trialing_referrals: referredIds.length - (subscriptionStats?.[0]?.active?.[0]?.total || 0);

// Accounts in trial may convert to active
// Active count increases, trialing decreases

Date Range Filteringโ€‹

Different Cutoff Dates:

// Software commissions: After Jan 25, 2022
created: {
$gt: 1643086800;
}

// Managed commissions: After July 1, 2025
created: {
$gt: 1751328000;
}

// Separate date filters for different subscription types

Discount Handlingโ€‹

Net Revenue Calculation:

{
$subtract: [
'$$this.amount', // Gross amount
{
$sum: {
$map: {
input: { $ifNull: ['$$this.discount_amounts', []] },
as: 'discount',
in: '$$discount.amount',
},
},
},
],
}

// Commission calculated on net amount (after discounts)

Empty Leaderboardโ€‹

New Affiliate Program:

const rankings = await getLeaderboard('monthly');
// Returns: [] if no affiliates ranked yet

// Frontend should handle empty state
if (!rankings.length) {
// Show "No affiliates ranked yet" message
}

Rounding Precisionโ€‹

Commission Rounding:

return Math.round(total * commission);

// Example: $99.99 (9999 cents) * 0.4 = 3999.6
// Rounds to: 4000 cents ($40.00)

// Small amounts may lose fractional cents
// $0.25 (25 cents) * 0.4 = 10 cents (not 10.0)

โš ๏ธ Important Notesโ€‹

  1. Commission Rates: Two-tier system:

    • Software Subscriptions: 40% commission (evaluateCommission uses 0.4)
    • Managed Subscriptions: 10% commission (from COMMISSION_RATE constant)
  2. Revenue Attribution: Separate tracking for software vs managed subscriptions. Same referral can generate both types of commission.

  3. Date Filters: Different cutoff dates for software (Jan 2022) vs managed (July 2025) reflect program evolution.

  4. Currency: Currently USD-only. Multi-currency support is TODO for international affiliates.

  5. Payout Lifecycle:

    • PENDING: Commission earned but not yet paid
    • PAID: Commission already disbursed
  6. Leaderboard Movement: Ranking changes calculated by comparing current rank with previous_rank. New entries default to "up" indicator.

  7. Trial Settings: Affiliates can customize trial length for their referrals. Default is 14 days.

  8. Excluded Products: Sites and listings are excluded from managed subscription commissions.

  9. Performance: getReferrals performs complex aggregation with multiple lookups. Consider caching for high-traffic affiliates.

  10. Index Requirements: Heavily relies on indexes:

    • accounts.referred_by
    • subscriptions.account + status
    • invoices.metadata.account_id + created
    • payouts.account + status
  11. Setup Fees: Only subscription line items count toward commission, not one-time setup fees.

  12. Discount Impact: Commission calculated on net revenue (after discounts applied).

๐Ÿ’ฌ

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