package wallet import ( "context" "errors" "fmt" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" ) var ( ErrWalletNotTransferable = errors.New("wallet is not transferable") ErrInsufficientBalance = errors.New("wallet balance is insufficient") ) func (s *Service) CreateTransfer(ctx context.Context, transfer domain.CreateTransfer) (domain.Transfer, error) { // This is just a transfer log when return s.transferStore.CreateTransfer(ctx, transfer) } func (s *Service) GetAllTransfers(ctx context.Context) ([]domain.Transfer, error) { return s.transferStore.GetAllTransfers(ctx) } func (s *Service) GetTransferByReference(ctx context.Context, reference string) (domain.Transfer, error) { return s.transferStore.GetTransferByReference(ctx, reference) } func (s *Service) GetTransferByID(ctx context.Context, id int64) (domain.Transfer, error) { return s.transferStore.GetTransferByID(ctx, id) } func (s *Service) GetTransfersByWallet(ctx context.Context, walletID int64) ([]domain.Transfer, error) { return s.transferStore.GetTransfersByWallet(ctx, walletID) } func (s *Service) UpdateTransferVerification(ctx context.Context, id int64, verified bool) error { return s.transferStore.UpdateTransferVerification(ctx, id, verified) } func (s *Service) UpdateTransferStatus(ctx context.Context, id int64, status string) error { return s.transferStore.UpdateTransferStatus(ctx, id, status) } func (s *Service) TransferToWallet(ctx context.Context, senderID int64, receiverID int64, amount domain.Currency, paymentMethod domain.PaymentMethod, cashierID domain.ValidInt64) (domain.Transfer, error) { senderWallet, err := s.GetWalletByID(ctx, senderID) if err != nil { return domain.Transfer{}, err } if senderWallet.IsTransferable { return domain.Transfer{}, ErrWalletNotTransferable } receiverWallet, err := s.GetWalletByID(ctx, receiverID) if err != nil { return domain.Transfer{}, err } if receiverWallet.IsTransferable { return domain.Transfer{}, ErrWalletNotTransferable } // Deduct from sender if senderWallet.Balance < amount { return domain.Transfer{}, ErrBalanceInsufficient } err = s.walletStore.UpdateBalance(ctx, senderID, senderWallet.Balance-amount) if err != nil { return domain.Transfer{}, err } // Add to receiver err = s.walletStore.UpdateBalance(ctx, receiverID, receiverWallet.Balance+amount) if err != nil { return domain.Transfer{}, err } // Log the transfer so that if there is a mistake, it can be reverted transfer, err := s.CreateTransfer(ctx, domain.CreateTransfer{ SenderWalletID: domain.ValidInt64{ Value: senderID, Valid: true, }, ReceiverWalletID: domain.ValidInt64{ Value: receiverID, Valid: true, }, CashierID: cashierID, Amount: amount, Type: domain.WALLET, PaymentMethod: paymentMethod, Verified: true, }) if err != nil { return domain.Transfer{}, err } return transfer, nil } func (s *Service) SendTransferNotification(ctx context.Context, senderWallet domain.Wallet, receiverWallet domain.Wallet, senderRole domain.Role, receiverRole domain.Role, amount domain.Currency) error { // Send notification to sender (this could be any role) that money was transferred senderWalletReceiverSide := domain.ReceiverFromRole(senderRole) senderNotify := &domain.Notification{ RecipientID: senderWallet.UserID, Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS, Level: domain.NotificationLevelSuccess, Reciever: senderWalletReceiverSide, DeliveryChannel: domain.DeliveryChannelInApp, Payload: domain.NotificationPayload{ Headline: "Wallet has been deducted", Message: fmt.Sprintf(`%s %d has been transferred from your wallet`,senderWallet.Currency, amount), }, Priority: 2, Metadata: []byte(fmt.Sprintf(`{ "transfer_amount": %d, "current_balance": %d, "wallet_id": %d, "notification_type": "customer_facing" }`, amount, senderWallet.Balance, senderWallet.ID)), } // Sender notifications if err := s.notificationStore.SendNotification(ctx, senderNotify); err != nil { s.logger.Error("failed to send sender notification", "user_id", "", "error", err) return err } receiverWalletReceiverSide := domain.ReceiverFromRole(receiverRole) receiverNotify := &domain.Notification{ RecipientID: receiverWallet.UserID, Type: domain.NOTIFICATION_TYPE_TRANSFER_SUCCESS, Level: domain.NotificationLevelSuccess, Reciever: receiverWalletReceiverSide, DeliveryChannel: domain.DeliveryChannelInApp, Payload: domain.NotificationPayload{ Headline: "Wallet has been credited", Message: fmt.Sprintf(`%s %d has been transferred to your wallet`,receiverWallet.Currency, amount), }, Priority: 2, Metadata: []byte(fmt.Sprintf(`{ "transfer_amount": %d, "current_balance": %d, "wallet_id": %d, "notification_type": "customer_facing" }`, amount, receiverWallet.Balance, receiverWallet.ID)), } // Sender notifications if err := s.notificationStore.SendNotification(ctx, receiverNotify); err != nil { s.logger.Error("failed to send sender notification", "user_id", "", "error", err) return err } return nil }