package wallet import ( "context" "errors" "fmt" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/event" ) var ( ErrBalanceInsufficient = errors.New("wallet balance is insufficient") ) 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 { err := s.walletStore.UpdateBalance(ctx, id, balance) if err != nil { return err } wallet, err := s.GetWalletByID(ctx, id) 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", }) }() 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 } 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", }) }() // 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.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() if balance < thresholds[0] { s.SendAdminWalletLowNotification(ctx, wallet) } } 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", }) }() // 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) }