382 lines
12 KiB
Go
382 lines
12 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 /api/v1/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.mongoLoggerSvc.Error("Invalid wallet ID",
|
|
zap.String("walletID", walletID),
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID")
|
|
}
|
|
|
|
transfers, err := h.walletSvc.GetTransfersByWallet(c.Context(), int64(id))
|
|
if err != nil {
|
|
h.mongoLoggerSvc.Error("Failed to get transfers by wallet",
|
|
zap.String("walletID", walletID),
|
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
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 /api/v1/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.mongoLoggerSvc.Info("Invalid wallet ID",
|
|
zap.Int64("walletID", receiverID),
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID")
|
|
}
|
|
// 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.mongoLoggerSvc.Error("Unauthorized access",
|
|
zap.Int64("userID", userID),
|
|
zap.Int("status_code", fiber.StatusForbidden),
|
|
zap.String("role", string(role)),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusForbidden, "Unauthorized access")
|
|
case domain.RoleAdmin:
|
|
company, err := h.companySvc.GetCompanyByID(c.Context(), companyID.Value)
|
|
if err != nil {
|
|
h.mongoLoggerSvc.Error("Failed to fetch company",
|
|
zap.Int64("userID", userID),
|
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
}
|
|
senderID = company.WalletID
|
|
// h.logger.Error("Will", "userID", userID, "role", role)
|
|
|
|
case domain.RoleSuperAdmin:
|
|
return fiber.NewError(fiber.StatusBadRequest, "Super Admin does not have a wallet")
|
|
case domain.RoleBranchManager:
|
|
return fiber.NewError(fiber.StatusBadRequest, "Branch Manager does not have a wallet")
|
|
case domain.RoleCashier:
|
|
cashierBranch, err := h.branchSvc.GetBranchByCashier(c.Context(), userID)
|
|
if err != nil {
|
|
h.mongoLoggerSvc.Error("Failed to get branch by cashier",
|
|
zap.Int64("userID", userID),
|
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
}
|
|
senderID = cashierBranch.WalletID
|
|
default:
|
|
h.mongoLoggerSvc.Error("Unknown Role",
|
|
zap.Int64("userID", userID),
|
|
zap.String("role", string(role)),
|
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusInternalServerError, "Unknown Role")
|
|
}
|
|
|
|
var req CreateTransferReq
|
|
|
|
if err := c.BodyParser(&req); err != nil {
|
|
h.mongoLoggerSvc.Error("CreateTransferReq failed to parse body",
|
|
zap.Int64("userID", userID),
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
|
|
}
|
|
|
|
valErrs, ok := h.validator.Validate(c, req)
|
|
if !ok {
|
|
var errMsg string
|
|
for field, msg := range valErrs {
|
|
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
|
|
}
|
|
|
|
h.mongoLoggerSvc.Error("Failed to validate CreateTransferReq",
|
|
zap.Int64("userID", userID),
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
|
|
return fiber.NewError(fiber.StatusBadRequest, errMsg)
|
|
}
|
|
|
|
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.Int64("userID", userID),
|
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
}
|
|
|
|
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 /api/v1/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.mongoLoggerSvc.Error("Invalid wallet ID",
|
|
zap.Int64("walletID", receiverID),
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid wallet ID")
|
|
}
|
|
// Get sender ID from the cashier
|
|
|
|
var req CreateRefillReq
|
|
|
|
if err := c.BodyParser(&req); err != nil {
|
|
h.mongoLoggerSvc.Info("CreateRefillReq failed to parse CreateRefillReq",
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
|
|
}
|
|
|
|
valErrs, ok := h.validator.Validate(c, req)
|
|
if !ok {
|
|
var errMsg string
|
|
for field, msg := range valErrs {
|
|
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
|
|
}
|
|
h.mongoLoggerSvc.Info("Failed to validate CreateRefillReq",
|
|
zap.Int64("userID", userID),
|
|
zap.String("errMsg", errMsg),
|
|
zap.Int("status_code", fiber.StatusBadRequest),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusBadRequest, errMsg)
|
|
|
|
}
|
|
|
|
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 {
|
|
h.mongoLoggerSvc.Error("Creating Transfer Failed",
|
|
zap.Int64("userID", userID),
|
|
zap.Float32("Amount", req.Amount),
|
|
zap.Int("status_code", fiber.StatusInternalServerError),
|
|
zap.Error(err),
|
|
zap.Time("timestamp", time.Now()),
|
|
)
|
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
|
|
}
|
|
|
|
res := convertTransfer(transfer)
|
|
|
|
return response.WriteJSON(c, fiber.StatusOK, "Transfer Successful", res, nil)
|
|
|
|
}
|