Skip to main content

Referral Program — Technical Reference

← Back to Referral Program

Built from scratch (Fase D.7 / roadmap 4.15). Before this, the only trace of referrals was a stale @method processReferralBonus line in a JSDoc comment with no implementation behind it.

Where this lives

Backend

Frontend

Technical implementation checklist

  • referralCode / referredById on users; unique code generated lazily
  • myReferralInfo query — ReferralPage.tsx
  • referralCode on UserRegistrationInput; captured from ?ref= and applied at signup
  • Configurable bonuses on SystemSetting, editable from the admin payments-settings page
  • Bonuses granted via the standard coin-transaction path (reward / relatedType referral), idempotent
  • Referrer notified (referral_bonus notification) when their bonus is granted
  • Affiliate commission on ongoing purchases — not built (one-time signup bonus only)

GraphQL API

# The caller's referral code + stats (code generated on first access)
query MyReferralInfo {
myReferralInfo { referralCode referralCount totalEarnedCoins }
}

# Registration input gained an optional referralCode
mutation Register($input: UserRegistrationInput!) {
register(input: $input) { token user { id username } }
}
# input: { username, email, password, dateOfBirth, referralCode: "ABCD2345" }

Design notes

  • One-time signup bonus, not a running commission. The reward is granted once, when a referred user registers. A true affiliate commission (a cut of everything the referred user later buys) would need hooks into each purchase flow (coin purchases, subscriptions, shop, exclusive posts) and its own accounting — intentionally out of scope for this pass; the model records referredById permanently, so that data is available if commission is added later.
  • Off by default. Both bonus amounts default to 0, so no coins move until an admin sets them — the same "admin-configurable, default off" pattern as verificationPriceCoins.
  • Idempotent & best-effort. referredById is only ever set once (a second signup attempt with a code is a no-op), and the whole apply step is wrapped so a referral failure never blocks a successful registration.
  • Bonuses use the standard coin path. coinTransactionManager.createTransaction({ transactionType: 'reward', relatedType: 'referral' }) handles balance + UserCoinBalance bookkeeping, so referral earnings show up in the earnings history and count exactly like any other reward.