package wallet import ( "context" "errors" // "fmt" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "go.uber.org/zap" // "github.com/SamuelTariku/FortuneBet-Backend/internal/event" ) var ( ErrBalanceInsufficient = errors.New("wallet balance is insufficient") ErrWalletIsDisabled = errors.New("wallet is disabled") ) func (s *Service) CreateWallet(ctx context.Context, wallet domain.CreateWallet) (domain.Wallet, error) { return s.walletStore.CreateWallet(ctx, wallet) } func (s *Service) CreateCustomerWallet(ctx context.Context, customerID int64) (domain.CustomerWallet, error) { regularWallet, err := s.CreateWallet(ctx, domain.CreateWallet{ IsWithdraw: true, IsBettable: true, IsTransferable: true, UserID: customerID, Type: domain.RegularWalletType, }) if err != nil { return domain.CustomerWallet{}, err } staticWallet, err := s.CreateWallet(ctx, domain.CreateWallet{ IsWithdraw: false, IsBettable: true, IsTransferable: true, UserID: customerID, Type: domain.StaticWalletType, }) if err != nil { return domain.CustomerWallet{}, err } return s.walletStore.CreateCustomerWallet(ctx, domain.CreateCustomerWallet{ CustomerID: customerID, RegularWalletID: regularWallet.ID, StaticWalletID: staticWallet.ID, }) } func (s *Service) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) { return s.walletStore.GetWalletByID(ctx, id) } func (s *Service) GetAllWallets(ctx context.Context) ([]domain.Wallet, error) { return s.walletStore.GetAllWallets(ctx) } func (s *Service) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error) { return s.walletStore.GetWalletsByUser(ctx, id) } func (s *Service) GetCompanyByWalletID(ctx context.Context, walletID int64) (domain.Company, error) { return s.walletStore.GetCompanyByWalletID(ctx, walletID) } func (s *Service) GetBranchByWalletID(ctx context.Context, walletID int64) (domain.Branch, error) { return s.walletStore.GetBranchByWalletID(ctx, walletID) } func (s *Service) GetAllCustomerWallet(ctx context.Context) ([]domain.GetCustomerWallet, error) { return s.walletStore.GetAllCustomerWallets(ctx) } func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) { return s.walletStore.GetCustomerWallet(ctx, customerID) } func (s *Service) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) { return s.walletStore.GetAllBranchWallets(ctx) } func (s *Service) UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error { wallet, err := s.GetWalletByID(ctx, id) if err != nil { return err } if !wallet.IsActive { return ErrWalletIsDisabled } err = s.walletStore.UpdateBalance(ctx, id, balance) if err != nil { return err } // go func() { // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.UserID), event.WalletEvent{ // EventType: event.WalletBalanceUpdated, // WalletID: wallet.ID, // UserID: wallet.UserID, // Balance: balance, // WalletType: wallet.Type, // Trigger: "UpdateBalance", // }) // }() if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil { s.mongoLogger.Info("Failed to send wallet update notification", zap.Int64("wallet_id", wallet.ID), zap.Error(err)) } return nil } func (s *Service) AddToWallet( ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, paymentDetails domain.PaymentDetails, message string) (domain.Transfer, error) { wallet, err := s.GetWalletByID(ctx, id) if err != nil { return domain.Transfer{}, err } if !wallet.IsActive { return domain.Transfer{}, ErrWalletIsDisabled } err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance+amount) if err != nil { return domain.Transfer{}, err } // go func() { // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{ // EventType: event.WalletBalanceUpdated, // WalletID: wallet.ID, // UserID: wallet.UserID, // Balance: wallet.Balance + amount, // WalletType: wallet.Type, // Trigger: "AddToWallet", // }) // }() if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil { s.mongoLogger.Info("Failed to send wallet update notification", zap.Int64("wallet_id", wallet.ID), zap.Error(err)) } // Log the transfer here for reference newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ Message: message, Amount: amount, Verified: true, ReceiverWalletID: domain.ValidInt64{ Value: wallet.ID, Valid: true, }, CashierID: cashierID, PaymentMethod: paymentMethod, Type: domain.DEPOSIT, ReferenceNumber: paymentDetails.ReferenceNumber.Value, }) return newTransfer, err } func (s *Service) DeductFromWallet(ctx context.Context, id int64, amount domain.Currency, cashierID domain.ValidInt64, paymentMethod domain.PaymentMethod, message string) (domain.Transfer, error) { wallet, err := s.GetWalletByID(ctx, id) if err != nil { return domain.Transfer{}, err } if !wallet.IsActive { return domain.Transfer{}, ErrWalletIsDisabled } if wallet.Balance < amount { // Send Wallet low to admin if wallet.Type == domain.CompanyWalletType || wallet.Type == domain.BranchWalletType { s.SendAdminWalletInsufficientNotification(ctx, wallet, amount) } else { s.SendCustomerWalletInsufficientNotification(ctx, wallet, amount) } return domain.Transfer{}, ErrBalanceInsufficient } if wallet.Type == domain.BranchWalletType || wallet.Type == domain.CompanyWalletType { var thresholds []float32 if wallet.Type == domain.CompanyWalletType { thresholds = []float32{100000, 50000, 25000, 10000, 5000, 3000, 1000, 500} } else { thresholds = []float32{5000, 3000, 1000, 500} } balance := wallet.Balance.Float32() for _, thresholds := range thresholds { if thresholds < balance && thresholds > (balance-amount.Float32()) { s.SendAdminWalletLowNotification(ctx, wallet) break } } } err = s.walletStore.UpdateBalance(ctx, id, wallet.Balance-amount) if err != nil { return domain.Transfer{}, nil } // go func() { // s.kafkaProducer.Publish(ctx, fmt.Sprint(wallet.ID), event.WalletEvent{ // EventType: event.WalletBalanceUpdated, // WalletID: wallet.ID, // UserID: wallet.UserID, // Balance: wallet.Balance - amount, // WalletType: wallet.Type, // Trigger: "DeductFromWallet", // }) // }() if err := s.SendWalletUpdateNotification(ctx, wallet); err != nil { s.mongoLogger.Info("Failed to send wallet update notification", zap.Int64("wallet_id", wallet.ID), zap.Error(err)) } // Log the transfer here for reference newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ Message: message, Amount: amount, Verified: true, SenderWalletID: domain.ValidInt64{ Value: wallet.ID, Valid: true, }, CashierID: cashierID, PaymentMethod: paymentMethod, Type: domain.WITHDRAW, ReferenceNumber: "", }) return newTransfer, err } // Directly Refilling wallet without // func (s *Service) RefillWallet(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) { // receiverWallet, err := s.GetWalletByID(ctx, transfer.ReceiverWalletID) // if err != nil { // return domain.Transfer{}, err // } // // Add to receiver // senderWallet, err := s.GetWalletByID(ctx, transfer.SenderWalletID) // if err != nil { // return domain.Transfer{}, err // } else if senderWallet.Balance < transfer.Amount { // return domain.Transfer{}, ErrInsufficientBalance // } // err = s.walletStore.UpdateBalance(ctx, receiverWallet.ID, receiverWallet.Balance+transfer.Amount) // if err != nil { // return domain.Transfer{}, err // } // // Log the transfer so that if there is a mistake, it can be reverted // newTransfer, err := s.transferStore.CreateTransfer(ctx, domain.CreateTransfer{ // CashierID: transfer.CashierID, // ReceiverWalletID: transfer.ReceiverWalletID, // Amount: transfer.Amount, // Type: domain.DEPOSIT, // PaymentMethod: transfer.PaymentMethod, // Verified: true, // }) // if err != nil { // return domain.Transfer{}, err // } // return newTransfer, nil // } func (s *Service) UpdateWalletActive(ctx context.Context, id int64, isActive bool) error { return s.walletStore.UpdateWalletActive(ctx, id, isActive) }