package referralservice import ( "Yimaru-Backend/internal/config" "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/ports" "Yimaru-Backend/internal/services/settings" "context" "crypto/rand" "encoding/base32" "errors" "log/slog" "go.uber.org/zap" ) type Service struct { repo ports.ReferralStore settingSvc settings.Service config *config.Config logger *slog.Logger mongoLogger *zap.Logger } func New( repo ports.ReferralStore, settingSvc settings.Service, cfg *config.Config, logger *slog.Logger, mongoLogger *zap.Logger, ) *Service { return &Service{ repo: repo, settingSvc: settingSvc, config: cfg, logger: logger, mongoLogger: mongoLogger, } } var ( ErrInvalidReferral = errors.New("invalid or expired referral") ErrUserNotFound = errors.New("user not found") ErrNoReferralFound = errors.New("no referral found for this user") ErrUserAlreadyHasReferralCode = errors.New("user already has an active referral code") ErrMaxReferralCountLimitReached = errors.New("referral count limit has been reached") ) func (s *Service) GenerateReferralCode() (string, error) { b := make([]byte, 8) if _, err := rand.Read(b); err != nil { s.mongoLogger.Error("Failed to generate random bytes for referral code", zap.Error(err)) return "", err } code := base32.StdEncoding.EncodeToString(b)[:10] s.mongoLogger.Debug("Generated referral code", zap.String("code", code)) return code, nil } func (s *Service) CreateReferralCode(ctx context.Context, userID int64, companyID int64) (domain.ReferralCode, error) { settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) if err != nil { s.mongoLogger.Error("Failed to fetch settings", zap.Error(err)) return domain.ReferralCode{}, err } // check if user already has an active referral code referralCodes, err := s.repo.GetReferralCodesByUser(ctx, userID) if err != nil { s.mongoLogger.Error("Failed to check if user already has active referral code", zap.Int64("userID", userID), zap.Error(err)) return domain.ReferralCode{}, err } if len(referralCodes) != 0 { s.mongoLogger.Error("user already has an active referral code", zap.Int64("userID", userID), zap.Any("codes", referralCodes), zap.Error(err)) return domain.ReferralCode{}, ErrUserAlreadyHasReferralCode } code, err := s.GenerateReferralCode() if err != nil { return domain.ReferralCode{}, err } newReferralCode, err := s.repo.CreateReferralCode(ctx, domain.CreateReferralCode{ ReferrerID: userID, ReferralCode: code, CompanyID: companyID, NumberOfReferrals: settingsList.DefaultMaxReferrals, RewardAmount: settingsList.ReferralRewardAmount, }) if err != nil { return domain.ReferralCode{}, err } return newReferralCode, nil } func (s *Service) ProcessReferral(ctx context.Context, referredID int64, referralCode string, companyID int64) error { paramLogger := s.mongoLogger.With( zap.Int64("referredID", referredID), zap.String("referralCode", referralCode), zap.Int64("companyID", companyID), ) referral, err := s.repo.GetReferralCode(ctx, referralCode) if err != nil { paramLogger.Error("Failed to get referral by code", zap.Error(err)) return err } // wallets, err := s.walletSvc.GetCustomerWallet(ctx, referral.ReferrerID) // if err != nil { // paramLogger.Error("Failed to get referrer wallets", zap.Error(err)) // return err // } // _, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, // referral.RewardAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, // fmt.Sprintf("Added %v to static wallet due to %v referral code being used", referral.RewardAmount, referral.ReferralCode), // ) // if err != nil { // paramLogger.Error("Failed to add referral reward to static wallet", zap.Int64("static_wallet_id", wallets.StaticID), zap.Error(err)) // return err // } _, err = s.repo.CreateUserReferral(ctx, domain.CreateUserReferrals{ ReferredID: referredID, ReferralCodeID: referral.ID, }) if err != nil { paramLogger.Error("Failed to create user referral", zap.Error(err)) return err } paramLogger.Info("Referral processed successfully", zap.String("rewardAmount", referral.ReferralCode)) return nil } func (s *Service) GetReferralStats(ctx context.Context, userID int64, companyID int64) (domain.ReferralStats, error) { paramLogger := s.mongoLogger.With(zap.Int64("userID", userID), zap.Int64("companyID", companyID)) stats, err := s.repo.GetReferralStats(ctx, userID, companyID) if err != nil { paramLogger.Error("Failed to get referral stats", zap.Error(err)) return domain.ReferralStats{}, err } return stats, nil } func (s *Service) GetUserReferralCount(ctx context.Context, referrerID int64) (int64, error) { count, err := s.repo.GetUserReferralCount(ctx, referrerID) if err != nil { s.mongoLogger.Error("Failed to get referral count", zap.Int64("referrerID", referrerID), zap.Error(err)) return 0, err } return count, nil } func (s *Service) GetReferralCodesByUser(ctx context.Context, userID int64) ([]domain.ReferralCode, error) { return s.repo.GetReferralCodesByUser(ctx, userID) } func (s *Service) GetReferralCode(ctx context.Context, code string) (domain.ReferralCode, error) { return s.repo.GetReferralCode(ctx, code) } func (s *Service) UpdateReferralCode(ctx context.Context, referral domain.UpdateReferralCode) error { return s.repo.UpdateReferralCode(ctx, referral) } func (s *Service) GetUserReferral(ctx context.Context, referrerID int64) (domain.UserReferral, error) { return s.repo.GetUserReferral(ctx, referrerID) } func (s *Service) GetUserReferralsByCode(ctx context.Context, code string) ([]domain.UserReferral, error) { return s.repo.GetUserReferralsByCode(ctx, code) } // func (s *Service) ProcessDepositBonus(ctx context.Context, userID int64, amount float32, companyID int64) error { // settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID) // if err != nil { // s.logger.Error("Failed to fetch settings", "error", err) // return err // } // s.logger.Info("Processing deposit bonus", "amount", amount) // customerWallet, err := s.walletSvc.GetCustomerWallet(ctx, userID) // if err != nil { // s.logger.Error("Failed to get wallets for user", "userID", userID, "error", err) // return err // } // bonus := amount * settingsList.CashbackPercentage // _, err = s.walletSvc.AddToWallet(ctx, customerWallet.StaticID, domain.ToCurrency(bonus), domain.ValidInt64{}, // domain.TRANSFER_DIRECT, domain.PaymentDetails{}, // fmt.Sprintf("Added to bonus wallet because of Deposit Cashback Bonus %d", bonus)) // if err != nil { // s.logger.Error("Failed to add deposit bonus to wallet", "staticWalletID", customerWallet.StaticID, "userID", userID, "bonus", bonus, "error", err) // return err // } // s.logger.Info("Deposit bonus processed successfully", "bonus", bonus) // return nil // } // func (s *Service) ProcessBetReferral(ctx context.Context, userId int64, betAmount float64) error { // s.logger.Info("Processing bet referral", "userID", userId, "betAmount", betAmount) // settings, err := s.repo.GetSettings(ctx) // if err != nil { // s.logger.Error("Failed to get referral settings", "error", err) // return err // } // referral, err := s.repo.GetReferralByReferredID(ctx, userId) // if err != nil { // s.logger.Error("Failed to get referral by referred ID", "userId", userId, "error", err) // return err // } // if referral == nil || referral.Status != domain.ReferralCompleted { // s.logger.Warn("No valid referral found", "userId", userId, "status", referral.Status) // return ErrNoReferralFound // } // wallets, err := s.walletSvc.GetWalletsByUser(ctx, referral.ReferrerID) // if err != nil { // s.logger.Error("Failed to get wallets for referrer", "referrerID", referral.ReferrerID, "error", err) // return err // } // if len(wallets) == 0 { // s.logger.Error("Referrer has no wallet", "referrerID", referral.ReferrerID) // return errors.New("referrer has no wallet") // } // bonusPercentage := settings.BetReferralBonusPercentage // if bonusPercentage == 0 { // bonusPercentage = 5.0 // s.logger.Debug("Using default bet referral bonus percentage", "percentage", bonusPercentage) // } // bonus := betAmount * (bonusPercentage / 100) // walletID := wallets[0].ID // currentBalance := float64(wallets[0].Balance) // _, err = s.walletSvc.AddToWallet(ctx, walletID, domain.ToCurrency(float32(currentBalance+bonus)), domain.ValidInt64{}, // domain.TRANSFER_DIRECT, domain.PaymentDetails{}, // fmt.Sprintf("Added %v to static wallet because of bet referral", referral.RewardAmount)) // if err != nil { // s.logger.Error("Failed to add bet referral bonus to wallet", "walletID", walletID, "referrerID", referral.ReferrerID, "bonus", bonus, "error", err) // return err // } // s.logger.Info("Bet referral processed successfully", "referrer ID", referral.ReferrerID, "referrerID", referral.ReferrerID, "bonus", bonus) // return nil // }