Yimaru-BackEnd/internal/web_server/handlers/transfer_handler.go

298 lines
10 KiB
Go

package handlers
import (
"fmt"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type TransferWalletRes struct {
ID int64 `json:"id"`
Amount float32 `json:"amount"`
Verified bool `json:"verified"`
Message string `json:"message"`
Type string `json:"type"`
PaymentMethod string `json:"payment_method"`
ReceiverWalletID *int64 `json:"receiver_wallet_id,omitempty"`
SenderWalletID *int64 `json:"sender_wallet_id,omitempty"`
DepositorID *int64 `json:"depositor_id,omitempty"`
DepositorFirstName string `json:"depositor_first_name"`
DepositorLastName string `json:"depositor_last_name"`
DepositorPhoneNumber string `json:"depositor_phone_number"`
ReferenceNumber string `json:"reference_number"` // ← Add this
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type RefillRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
Verified bool `json:"verified" example:"true"`
Type string `json:"type" example:"transfer"`
PaymentMethod string `json:"payment_method" example:"bank"`
ReceiverWalletID *int64 `json:"receiver_wallet_id" example:"1"`
SenderWalletID *int64 `json:"sender_wallet_id" example:"1"`
CashierID *int64 `json:"cashier_id" example:"789"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
UpdatedAt time.Time `json:"updated_at" example:"2025-04-08T12:30:00Z"`
}
func convertTransfer(t domain.Transfer) TransferWalletRes {
var receiverID *int64
if t.ReceiverWalletID.Valid {
receiverID = &t.ReceiverWalletID.Value
}
var senderID *int64
if t.SenderWalletID.Valid {
senderID = &t.SenderWalletID.Value
}
var depositorID *int64
if t.DepositorID.Valid {
depositorID = &t.DepositorID.Value
}
return TransferWalletRes{
ID: t.ID,
Amount: t.Amount.Float32(),
Verified: t.Verified,
Message: t.Message,
Type: string(t.Type),
PaymentMethod: string(t.PaymentMethod),
ReceiverWalletID: receiverID,
SenderWalletID: senderID,
DepositorID: depositorID,
ReferenceNumber: t.ReferenceNumber,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,
}
}
func convertTransferDetail(t domain.TransferDetail) TransferWalletRes {
var receiverID *int64
if t.ReceiverWalletID.Valid {
receiverID = &t.ReceiverWalletID.Value
}
var senderID *int64
if t.SenderWalletID.Valid {
senderID = &t.SenderWalletID.Value
}
var depositorID *int64
if t.DepositorID.Valid {
depositorID = &t.DepositorID.Value
}
return TransferWalletRes{
ID: t.ID,
Amount: t.Amount.Float32(),
Verified: t.Verified,
Message: t.Message,
Type: string(t.Type),
PaymentMethod: string(t.PaymentMethod),
ReceiverWalletID: receiverID,
SenderWalletID: senderID,
DepositorID: depositorID,
DepositorFirstName: t.DepositorFirstName,
DepositorLastName: t.DepositorLastName,
DepositorPhoneNumber: t.DepositorPhoneNumber,
ReferenceNumber: t.ReferenceNumber,
CreatedAt: t.CreatedAt,
UpdatedAt: t.UpdatedAt,
}
}
type CreateTransferReq struct {
Amount float32 `json:"amount" example:"100.0"`
PaymentMethod string `json:"payment_method" example:"cash"`
}
type CreateRefillReq struct {
Amount float32 `json:"amount" example:"100.0"`
}
// GetTransfersByWallet godoc
// @Summary Get transfer by wallet
// @Description Get transfer by wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param transferToWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/wallet/{id} [get]
func (h *Handler) GetTransfersByWallet(c *fiber.Ctx) error {
walletID := c.Params("id")
id, err := strconv.ParseInt(walletID, 10, 64)
if err != nil {
h.logger.Error("Invalid wallet ID", "walletID", walletID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
}
transfers, err := h.walletSvc.GetTransfersByWallet(c.Context(), int64(id))
if err != nil {
h.logger.Error("Failed to get transfers by wallet", "walletID", walletID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve transfers", err, nil)
}
var transferResponses []TransferWalletRes
for _, transfer := range transfers {
transferResponses = append(transferResponses, convertTransferDetail(transfer))
}
return response.WriteJSON(c, fiber.StatusOK, "Transfers retrieved successfully", transferResponses, nil)
}
// TransferToWallet godoc
// @Summary Create a transfer to wallet
// @Description Create a transfer to wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param transferToWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/wallet/:id [post]
func (h *Handler) TransferToWallet(c *fiber.Ctx) error {
receiverIDString := c.Params("id")
receiverID, err := strconv.ParseInt(receiverIDString, 10, 64)
if err != nil {
h.logger.Error("Invalid wallet ID", "walletID", receiverID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
}
// Get sender ID from the cashier
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
fmt.Printf("\n\nCompany ID: %v\n\n", companyID.Value)
var senderID int64
//TODO: check to make sure that the cashiers aren't transferring TO branch wallet
switch role {
case domain.RoleCustomer:
h.logger.Error("Unauthorized access", "userID", userID, "role", role)
return response.WriteJSON(c, fiber.StatusUnauthorized, "Unauthorized access", nil, nil)
case domain.RoleAdmin:
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to fetch company",
Error: err.Error(),
})
// return response.WriteJSON(c, fiber.StatusInternalServerError, "Error fetching company", err, nil)
}
senderID = company.WalletID
h.logger.Error("Will", "userID", userID, "role", role)
case domain.RoleSuperAdmin:
return response.WriteJSON(c, fiber.StatusBadRequest, "Super Admin does not have a wallet", err, nil)
case domain.RoleBranchManager:
return response.WriteJSON(c, fiber.StatusBadRequest, "Branch Manager does not have a wallet", err, nil)
case domain.RoleCashier:
cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
if err != nil {
h.logger.Error("Failed to get branch", "user ID", userID, "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to retrieve cashier branch", err, nil)
}
senderID = cashierBranch.WalletID
default:
return response.WriteJSON(c, fiber.StatusInternalServerError, "Unknown Role", err, nil)
}
var req CreateTransferReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("CreateTransferReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
transfer, err := h.walletSvc.TransferToWallet(c.Context(),
senderID, receiverID, domain.ToCurrency(req.Amount), domain.PaymentMethod(req.PaymentMethod),
domain.ValidInt64{Value: userID, Valid: true},
fmt.Sprintf("Transferred %v from wallet to another", req.Amount),
)
if err != nil {
h.mongoLoggerSvc.Error("Failed to transfer money to wallet", zap.Error(err))
return response.WriteJSON(c, fiber.StatusInternalServerError, "Transfer Failed", err, nil)
}
res := convertTransfer(transfer)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}
// RefillWallet godoc
// @Summary Refill wallet
// @Description Super Admin route to refill a wallet
// @Tags transfer
// @Accept json
// @Produce json
// @Param refillWallet body CreateTransferReq true "Create Transfer"
// @Success 200 {object} TransferWalletRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /transfer/refill/:id [post]
func (h *Handler) RefillWallet(c *fiber.Ctx) error {
receiverIDString := c.Params("id")
userID := c.Locals("user_id").(int64)
receiverID, err := strconv.ParseInt(receiverIDString, 10, 64)
if err != nil {
h.logger.Error("Invalid wallet ID", "walletID", receiverID, "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid wallet ID", err, nil)
}
// Get sender ID from the cashier
var req CreateRefillReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("CreateRefillReq failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
transfer, err := h.walletSvc.AddToWallet(
c.Context(), receiverID, domain.ToCurrency(req.Amount), domain.ValidInt64{
Value: userID,
Valid: true,
}, domain.TRANSFER_BANK, domain.PaymentDetails{}, fmt.Sprintf("Added %v to wallet directly by super-admin", req.Amount))
if !ok {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Creating Transfer Failed", err, nil)
}
res := convertTransfer(transfer)
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
}