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:
-
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 IDaffiliate(Object, optional) - Affiliate metadataclicks(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:
-
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
-
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
-
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
-
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
-
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:
-
Fetch Top 10 Rankings
let rankings = await AffiliatesLeaderboard.aggregate([
{
$match: {
type, // 'monthly' or 'all-time'
},
},
{
$sort: {
rank: 1, // Ascending (1 = best)
},
},
{
$limit: 10,
},
// ... joins
]); -
Join Business Contact Data
{
$lookup: {
from: 'crm.contacts',
localField: 'account',
foreignField: 'account',
as: 'business',
},
},
{
$set: {
business: { $first: '$business' },
},
} -
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' },
},
} -
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 IDpage(Number, default: 1) - Page number for paginationlimit(Number, default: 25) - Results per pagesortField(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:
-
Parse Pagination Parameters
limit = parseInt(limit, 10);
page = page ? parseInt(page, 10) : 0;
const skip = page ? (page - 1) * limit : 0; -
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)
-
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,
},
},
],
},
},
]); -
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:
-
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:
-
Validate Input
if (!id) throw new Error('Account id was not provided'); -
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โ
-
Commission Rates: Two-tier system:
- Software Subscriptions: 40% commission (evaluateCommission uses 0.4)
- Managed Subscriptions: 10% commission (from COMMISSION_RATE constant)
-
Revenue Attribution: Separate tracking for software vs managed subscriptions. Same referral can generate both types of commission.
-
Date Filters: Different cutoff dates for software (Jan 2022) vs managed (July 2025) reflect program evolution.
-
Currency: Currently USD-only. Multi-currency support is TODO for international affiliates.
-
Payout Lifecycle:
- PENDING: Commission earned but not yet paid
- PAID: Commission already disbursed
-
Leaderboard Movement: Ranking changes calculated by comparing current rank with previous_rank. New entries default to "up" indicator.
-
Trial Settings: Affiliates can customize trial length for their referrals. Default is 14 days.
-
Excluded Products: Sites and listings are excluded from managed subscription commissions.
-
Performance: getReferrals performs complex aggregation with multiple lookups. Consider caching for high-traffic affiliates.
-
Index Requirements: Heavily relies on indexes:
- accounts.referred_by
- subscriptions.account + status
- invoices.metadata.account_id + created
- payouts.account + status
-
Setup Fees: Only subscription line items count toward commission, not one-time setup fees.
-
Discount Impact: Commission calculated on net revenue (after discounts applied).
๐ Related Documentationโ
- Store Subscriptions - Subscription management