Skip to content

Conversation

@0-don
Copy link

@0-don 0-don commented Jan 26, 2026

Summary

  • Add payment-based referral commission system where inviters receive a percentage of referred users' recharges
  • Configurable commission percentage and max recharge limit
  • Commission credited to inviter's AffQuota (affiliate quota pool)

Closes #128, #187, #1852

Changes

  • Add ReferralCommissionEnabled, ReferralCommissionPercent, ReferralCommissionMaxRecharges settings
  • Add CreditReferralCommission() helper function in model/user.go
  • Call commission logic in all payment handlers:
    • Stripe (model/topup.goRecharge())
    • Creem (model/topup.goRechargeCreem())
    • Manual completion (model/topup.goManualCompleteTopUp())
    • Epay/易支付 (controller/topup.go → callback handler)
  • Add admin UI section in Credit Limit settings to configure commission
  • Add i18n translations for all 6 languages (en, zh, ru, ja, fr, vi)

Summary by CodeRabbit

  • New Features

    • Added a referral commission system: inviters receive a percentage of referred users' recharges.
    • New settings UI to enable/disable the feature, set commission percentage, and cap eligible recharges.
    • Referral credits are applied automatically after successful recharges across supported top-up flows.
  • Localization

    • Added translations for the referral commission UI in EN/FR/JA/RU/VI/ZH.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Warning

Rate limit exceeded

@0-don has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 12 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Adds a referral commission feature: new configuration options, UI settings, option handling, a CreditReferralCommission function, and calls to credit inviters after successful top-ups across multiple recharge flows.

Changes

Cohort / File(s) Summary
Configuration
common/constants.go
Add three public config vars: ReferralCommissionEnabled (bool), ReferralCommissionPercent (float64), ReferralCommissionMaxRecharges (int).
Option management
model/option.go
Initialize and handle updates for the three new options in the option map (parse bool/float/int and assign to commons).
Commission logic
model/user.go
Add CreditReferralCommission(userId int, rechargeAmount float64) error — feature-gated; validates inputs and limits; computes commission; updates inviter quotas; persists and logs.
Top-up integration
model/topup.go, controller/topup.go
Invoke CreditReferralCommission (errors ignored) after successful top-ups in standard, admin-complete, and Creem recharge flows (including EpayNotify).
UI / i18n
web/src/pages/Setting/Operation/SettingsCreditLimit.jsx, web/src/i18n/locales/{en,fr,ja,ru,vi,zh}.json
Add settings UI for referral commission (toggle, percent, max recharges) and add corresponding localized strings across six locales.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WebUI
  participant Controller
  participant TopUpModel
  participant UserModel
  participant DB

  User->>WebUI: submit top-up
  WebUI->>Controller: POST /topup (or callback)
  Controller->>TopUpModel: process top-up, update topUp record
  TopUpModel->>DB: persist top-up and quota changes
  TopUpModel->>UserModel: CreditReferralCommission(userId, amount)
  UserModel->>DB: read recharging user, read inviter, update inviter quotas & history
  UserModel->>DB: persist inviter changes
  UserModel-->>Controller: return (err ignored by caller)
  Controller-->>WebUI: respond success
  WebUI-->>User: show success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • seefs001

Poem

🐰
I nibble on code and hop with cheer,
When friends recharge, rewards appear—
A nibble here, a carrot share,
Invitations bloom everywhere,
Hop, invite, and spread the cheer! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add payment-based referral commission system' clearly summarizes the main change: introducing a new feature for referral commissions tied to payments.
Linked Issues check ✅ Passed The PR implements a payment-based referral commission system addressing the core coding requirement from issue #128: adding proportional commission tied to referred users' payments to incentivize inviters.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the referral commission system: configuration variables, option mapping, commission calculation logic, UI for settings, and integration into payment handlers. No unrelated modifications detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

When a referred user recharges, the inviter receives a configurable
percentage as commission credited to their AffQuota.

New settings:
- ReferralCommissionEnabled: Toggle commission feature
- ReferralCommissionPercent: Commission percentage (0-100)
- ReferralCommissionMaxRecharges: Limit commission to first N recharges

Commission is calculated in all payment handlers:
- Stripe (Recharge)
- Creem (RechargeCreem)
- Manual completion (ManualCompleteTopUp)

Closes QuantumNous#128, QuantumNous#187, QuantumNous#1852
@0-don 0-don force-pushed the feat/payment-based-referral branch from 982619b to dd7ddf5 Compare January 26, 2026 16:29
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In `@model/topup.go`:
- Around line 103-105: The call to CreditReferralCommission(topUp.UserId,
topUp.Money) currently discards errors; change it to capture the returned error,
and if non-nil log and/or emit a metric but do not fail the top-up flow—e.g.,
err := CreditReferralCommission(topUp.UserId, topUp.Money); if err != nil { use
the project logger (e.g., processLogger or log) to log a warning with
topUp.UserId and topUp.Money and increment a referral-credit failure metric } so
inviters/issues are visible while keeping the top-up success unaffected.

In `@web/src/i18n/locales/en.json`:
- Around line 2405-2414: Remove the duplicate translation key "次" from the
en.json fragment in this diff: locate the second occurrence of the key "次" in
the block that contains "邀请充值返佣设置"/"Referral Commission Settings" and delete
that duplicate entry so only the original definition (the one defined earlier at
the key "次") remains, ensuring no other keys or surrounding JSON punctuation are
altered.

In `@web/src/i18n/locales/fr.json`:
- Around line 2407-2415: The fr.json file contains a duplicate translation key
"次" (newly added near the commission settings block) which overrides the earlier
definition ("Fois"); remove the newly added duplicate entry and instead
reference or update the existing "次" key so translations remain consistent, or
if this string needs a different wording/context create a new unique key (e.g.,
"commission_times") and use that key in the commission-related UI code; ensure
only one "次" entry remains and that the commission labels point to the correct
key.

In `@web/src/i18n/locales/ja.json`:
- Around line 2390-2398: Remove the duplicate JSON key "次" introduced in the
block containing "邀请充值返佣设置"/"招待コミッション設定" and its Japanese value; instead rely on
the existing "次" key already defined earlier in the file (the one at the other
occurrence) so there is only a single "次" key in the ja.json locale. Locate the
duplicate within the object that includes "启用充值返佣"/"チャージコミッションを有効化" and delete
that "次" entry, leaving the other translated entries intact.

In `@web/src/i18n/locales/ru.json`:
- Around line 2420-2428: Remove the duplicate translation key "次" in ru.json:
locate the second occurrence of the key (the one inside the "邀请充值返佣设置" block)
and delete that entry so only the original "次" translation remains; ensure the
surrounding JSON stays valid (commas adjusted) after removal.

In `@web/src/i18n/locales/vi.json`:
- Around line 2917-2925: Remove the duplicate translation entry for the key "次"
in vi.json by deleting the later occurrence in the "邀请充值返佣设置" block (the
trailing `"次": "lần",` entry) so only the original definition earlier in the
file remains; ensure you leave the earlier key-value intact and run the i18n
linter/validator to confirm no duplicates remain.

In `@web/src/i18n/locales/zh.json`:
- Around line 2390-2398: Remove the duplicate JSON key "次" from the zh locale
file by deleting the redundant entry (the later occurrence in the block
containing "邀请充值返佣设置" ...), leaving only the original "次" definition (the
earlier entry around line 1471) to avoid duplicate keys in
web/src/i18n/locales/zh.json and ensure the locale JSON contains a single "次"
key.
♻️ Duplicate comments (2)
model/topup.go (2)

309-312: Same issue as above: handle referral commission errors.

Same pattern as Line 104 — please add error logging/telemetry here too.


383-385: Same issue as above: handle referral commission errors.

Same pattern as Line 104 — please add error logging/telemetry here too.

🧹 Nitpick comments (2)
model/user.go (2)

362-369: Consider adding error handling for the count query.

The DB.Model().Count() call on line 365 does not check for errors. If the database query fails, rechargeCount will remain 0, and the commission will be paid regardless of actual recharge history. This could lead to paying commissions even when the limit should have been reached.

♻️ Proposed fix
 	// Check max recharges limit (if configured)
 	if common.ReferralCommissionMaxRecharges > 0 {
 		// Count successful recharges for this user
 		var rechargeCount int64
-		DB.Model(&TopUp{}).Where("user_id = ? AND status = ?", userId, common.TopUpStatusSuccess).Count(&rechargeCount)
+		if err := DB.Model(&TopUp{}).Where("user_id = ? AND status = ?", userId, common.TopUpStatusSuccess).Count(&rechargeCount).Error; err != nil {
+			return err // Fail safely if we can't verify recharge count
+		}
 		if int(rechargeCount) > common.ReferralCommissionMaxRecharges {
 			return nil // Max recharges reached, no more commission
 		}
 	}

383-394: Consider using a transaction for atomicity.

The inviter's quota update is not protected by a transaction. If DB.Save(inviter) fails after GetUserById succeeds, the system state could be inconsistent between retries. While the caller ignores errors from this function (making it "best effort"), using a transaction would ensure the quota fields are updated atomically.

Additionally, concurrent recharges by the same referred user could cause a race condition where both read the same AffQuota value before either writes, potentially resulting in lost updates.

♻️ Proposed fix using atomic update
 	// Get inviter and credit commission to their AffQuota
-	inviter, err := GetUserById(user.InviterId, true)
-	if err != nil {
-		return err
-	}
-
-	inviter.AffQuota += commission
-	inviter.AffHistoryQuota += commission
-	err = DB.Save(inviter).Error
+	err = DB.Model(&User{}).Where("id = ?", user.InviterId).Updates(map[string]interface{}{
+		"aff_quota":   gorm.Expr("aff_quota + ?", commission),
+		"aff_history": gorm.Expr("aff_history + ?", commission),
+	}).Error
 	if err != nil {
 		return err
 	}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@model/user.go`:
- Around line 383-394: The current read-modify-write using GetUserById and
DB.Save can lose concurrent updates; instead perform atomic DB-side increments
for the inviter quotas. Replace the read/modify/save (GetUserById,
inviter.AffQuota += commission, inviter.AffHistoryQuota += commission,
DB.Save(inviter)) with a single transactional atomic update such as using GORM's
UpdateColumn/UpdateColumns with gorm.Expr to increment aff_quota and
aff_history_quota (e.g. DB.Transaction(...) and DB.Model(&User{}).Where("id =
?", user.InviterId).UpdateColumns(map[string]interface{}{"aff_quota":
gorm.Expr("aff_quota + ?", commission), "aff_history_quota":
gorm.Expr("aff_history_quota + ?", commission)})), and handle the case where no
rows are affected as an error.
♻️ Duplicate comments (3)
web/src/i18n/locales/ru.json (1)

2420-2428: Remove the duplicate "次" key to fix linting.

Line 2428 redefines a key already present at Line 1504; this trips noDuplicateObjectKeys and silently overrides the earlier value.

🧹 Proposed fix
     "保存返佣设置": "Сохранить настройки комиссии",
-    "次": "раз",
web/src/i18n/locales/fr.json (1)

2407-2415: Duplicate translation key overrides existing value.

"次" is already defined earlier in this file. Keeping a second "次" here silently overwrites the previous translation and can change unrelated UI labels. Remove the duplicate or use a unique key for the commission section.

🧹 Proposed fix
-    "次": "fois",
web/src/i18n/locales/zh.json (1)

2390-2398: Remove the duplicate "次" key to avoid JSON override.
This duplicate has already been flagged in a previous review; please delete the later occurrence.

✂️ Proposed fix
@@
-    "次": "次",
🧹 Nitpick comments (1)
controller/topup.go (1)

288-291: Log referral commission failures for visibility.

The call ignores errors entirely, so misconfig or DB issues will be invisible. Consider logging when commission crediting fails.

🔧 Suggested tweak
-            // Credit referral commission to inviter (if enabled)
-            _ = model.CreditReferralCommission(topUp.UserId, topUp.Money)
+            // Credit referral commission to inviter (if enabled)
+            if err := model.CreditReferralCommission(topUp.UserId, topUp.Money); err != nil {
+                log.Printf("credit referral commission failed for user %d: %v", topUp.UserId, err)
+            }

- Log referral commission errors instead of silently ignoring them
- Use atomic DB update for inviter quota to prevent race conditions
- Remove duplicate "次" translation keys from all locale files
@0-don 0-don force-pushed the feat/payment-based-referral branch from 51f348f to 8f9265a Compare January 26, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

邀请赠送的额度,在消费后解锁

1 participant