Yimaru-BackEnd/internal/services/wallet/transfer.go

171 lines
5.2 KiB
Go

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{
Message: fmt.Sprintf("Transferring %d to another wallet", amount),
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
}