395 lines
13 KiB
Go
395 lines
13 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
// InitiateDeposit godoc
|
|
// @Summary Initiate a deposit
|
|
// @Description Starts a new deposit process using Chapa payment gateway
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security ApiKeyAuth
|
|
// @Param request body domain.ChapaDepositRequestPayload true "Deposit request"
|
|
// @Success 200 {object} domain.ChapaDepositResponse
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/payments/deposit [post]
|
|
func (h *Handler) InitiateDeposit(c *fiber.Ctx) error {
|
|
// Get user ID from context (set by your auth middleware)
|
|
userID, ok := c.Locals("user_id").(int64)
|
|
if !ok {
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
Error: "invalid user ID",
|
|
Message: "User ID is required to initiate a deposit",
|
|
})
|
|
}
|
|
|
|
var req domain.ChapaDepositRequestPayload
|
|
|
|
if err := c.BodyParser(&req); err != nil {
|
|
// fmt.Println("We first first are here init Chapa payment")
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
Error: err.Error(),
|
|
Message: "Failed to parse request body",
|
|
})
|
|
}
|
|
|
|
amount := domain.Currency(req.Amount * 100)
|
|
|
|
fmt.Println("We are here init Chapa payment")
|
|
|
|
checkoutURL, err := h.chapaSvc.InitiateDeposit(c.Context(), userID, amount)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Error: err.Error(),
|
|
Message: "Failed to initiate Chapa deposit",
|
|
})
|
|
}
|
|
|
|
// get static wallet of user
|
|
// wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), userID)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
// Error: err.Error(),
|
|
// Message: "Failed to initiate Chapa deposit",
|
|
// })
|
|
// }
|
|
|
|
// var multiplier float32 = 1
|
|
// bonusMultiplier, err := h.bonusSvc.GetBonusMultiplier(c.Context())
|
|
// if err == nil {
|
|
// multiplier = bonusMultiplier[0].Multiplier
|
|
// }
|
|
|
|
// var balanceCap int64 = 0
|
|
// bonusBalanceCap, err := h.bonusSvc.GetBonusBalanceCap(c.Context())
|
|
// if err == nil {
|
|
// balanceCap = bonusBalanceCap[0].BalanceCap
|
|
// }
|
|
|
|
// capedBalanceAmount := domain.Currency((math.Min(req.Amount, float64(balanceCap)) * float64(multiplier)) * 100)
|
|
|
|
// _, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, capedBalanceAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{},
|
|
// fmt.Sprintf("Added %v to static wallet because of deposit bonus using multiplier %v", capedBalanceAmount, multiplier),
|
|
// )
|
|
// if err != nil {
|
|
// h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err)
|
|
// return err
|
|
// }
|
|
|
|
// if err := h.bonusSvc.ProcessWelcomeBonus(c.Context(), domain.ToCurrency(float32(req.Amount)), 0, userID); err != nil {
|
|
// return err
|
|
// }
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Chapa deposit process initiated successfully",
|
|
Data: checkoutURL,
|
|
StatusCode: 200,
|
|
Success: true,
|
|
})
|
|
}
|
|
|
|
// WebhookCallback godoc
|
|
// @Summary Chapa payment webhook callback (used by Chapa)
|
|
// @Description Handles payment notifications from Chapa
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body domain.ChapaWebhookPayload true "Webhook payload"
|
|
// @Success 200 {object} map[string]interface{}
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/payments/webhook/verify [post]
|
|
func (h *Handler) WebhookCallback(c *fiber.Ctx) error {
|
|
|
|
chapaTransactionType := new(domain.ChapaTransactionType)
|
|
|
|
if parseTypeErr := c.BodyParser(chapaTransactionType); parseTypeErr != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
switch chapaTransactionType.Type {
|
|
case h.Cfg.CHAPA_PAYMENT_TYPE:
|
|
chapaTransferVerificationRequest := new(domain.ChapaWebHookTransfer)
|
|
|
|
if err := c.BodyParser(chapaTransferVerificationRequest); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
err := h.chapaSvc.HandleVerifyDepositWebhook(c.Context(), *chapaTransferVerificationRequest)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to verify Chapa depposit",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
StatusCode: 200,
|
|
Message: "Chapa deposit transaction verified successfully",
|
|
Data: chapaTransferVerificationRequest,
|
|
Success: true,
|
|
})
|
|
case h.Cfg.CHAPA_TRANSFER_TYPE:
|
|
chapaPaymentVerificationRequest := new(domain.ChapaWebHookPayment)
|
|
if err := c.BodyParser(chapaPaymentVerificationRequest); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
err := h.chapaSvc.HandleVerifyWithdrawWebhook(c.Context(), *chapaPaymentVerificationRequest)
|
|
if err != nil {
|
|
return domain.UnExpectedErrorResponse(c)
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
StatusCode: 200,
|
|
Message: "Chapa withdrawal transaction verified successfully",
|
|
Data: chapaPaymentVerificationRequest,
|
|
Success: true,
|
|
})
|
|
|
|
}
|
|
|
|
// Return a 400 Bad Request if the type does not match any known case
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
Message: "Invalid Chapa webhook type",
|
|
Error: "Unknown transaction type",
|
|
})
|
|
}
|
|
|
|
// VerifyPayment godoc
|
|
// @Summary Verify a payment manually
|
|
// @Description Manually verify a payment using Chapa's API
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param tx_ref path string true "Transaction Reference"
|
|
// @Success 200 {object} domain.ChapaVerificationResponse
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/payments/manual/verify/{tx_ref} [get]
|
|
func (h *Handler) ManualVerifyTransaction(c *fiber.Ctx) error {
|
|
txRef := c.Params("tx_ref")
|
|
if txRef == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
Message: "Failed to verify Chapa transaction",
|
|
Error: "Transaction reference is required",
|
|
})
|
|
}
|
|
|
|
verification, err := h.chapaSvc.ManuallyVerify(c.Context(), txRef)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to verify Chapa transaction",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.ChapaVerificationResponse{
|
|
Status: string(verification.Status),
|
|
Amount: verification.Amount,
|
|
Currency: verification.Currency,
|
|
TxRef: txRef,
|
|
})
|
|
}
|
|
|
|
// GetSupportedBanks godoc
|
|
// @Summary Get supported banks
|
|
// @Description Get list of banks supported by Chapa
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} domain.Response
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/banks [get]
|
|
func (h *Handler) GetSupportedBanks(c *fiber.Ctx) error {
|
|
banks, err := h.chapaSvc.GetSupportedBanks(c.Context())
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Error: err.Error(),
|
|
Message: "Failed to fetch banks",
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Banks fetched successfully",
|
|
StatusCode: 200,
|
|
Success: true,
|
|
Data: banks,
|
|
})
|
|
}
|
|
|
|
// InitiateWithdrawal initiates a withdrawal request via Chapa payment gateway
|
|
// @Summary Initiate a withdrawal
|
|
// @Description Initiates a withdrawal request to transfer funds to a bank account via Chapa
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Security ApiKeyAuth
|
|
// @Param request body domain.ChapaWithdrawalRequest true "Withdrawal request details"
|
|
// @Success 201 {object} domain.Response "Chapa withdrawal process initiated successfully"
|
|
// @Failure 400 {object} domain.ErrorResponse "Invalid request body"
|
|
// @Failure 401 {object} domain.ErrorResponse "Unauthorized"
|
|
// @Failure 422 {object} domain.ErrorResponse "Unprocessable entity"
|
|
// @Failure 500 {object} domain.ErrorResponse "Internal server error"
|
|
// @Router /api/v1/chapa/payments/withdraw [post]
|
|
func (h *Handler) InitiateWithdrawal(c *fiber.Ctx) error {
|
|
userID, ok := c.Locals("user_id").(int64)
|
|
if !ok {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
var req domain.ChapaWithdrawalRequest
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
withdrawal, err := h.chapaSvc.InitiateWithdrawal(c.Context(), userID, req)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to initiate Chapa withdrawal",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusCreated).JSON(domain.Response{
|
|
Message: "Chapa withdrawal process initiated successfully",
|
|
StatusCode: 201,
|
|
Success: true,
|
|
Data: withdrawal,
|
|
})
|
|
}
|
|
|
|
// GetPaymentReceipt godoc
|
|
// @Summary Get Chapa Payment Receipt URL
|
|
// @Description Retrieve the Chapa payment receipt URL using the reference ID
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param chapa_ref path string true "Chapa Reference ID"
|
|
// @Success 200 {object} domain.Response
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/payments/receipt/{chapa_ref} [get]
|
|
func (h *Handler) GetPaymentReceipt(c *fiber.Ctx) error {
|
|
chapaRef := c.Params("chapa_ref")
|
|
if chapaRef == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
Message: "Failed to get Chapa payment receipt",
|
|
Error: "Chapa reference ID is required",
|
|
})
|
|
}
|
|
|
|
receiptURL, err := h.chapaSvc.GetPaymentReceiptURL(c.Context(), chapaRef)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to get Chapa payment receipt",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Payment receipt URL generated successfully",
|
|
Data: receiptURL,
|
|
StatusCode: 200,
|
|
Success: true,
|
|
})
|
|
}
|
|
|
|
// GetAllTransfers godoc
|
|
// @Summary Get all Chapa transfers
|
|
// @Description Retrieve all transfer records from Chapa
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} domain.Response
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/transfers [get]
|
|
func (h *Handler) GetAllTransfers(c *fiber.Ctx) error {
|
|
transfers, err := h.chapaSvc.GetAllTransfers(c.Context())
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to fetch Chapa transfers",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Chapa transfers retrieved successfully",
|
|
Data: transfers,
|
|
StatusCode: fiber.StatusOK,
|
|
Success: true,
|
|
})
|
|
}
|
|
|
|
// GetAccountBalance godoc
|
|
// @Summary Get Chapa account balance
|
|
// @Description Retrieve Chapa account balance, optionally filtered by currency code (e.g., ETB, USD)
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param currency_code query string false "Currency code (optional)"
|
|
// @Success 200 {object} domain.Response
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/balances [get]
|
|
func (h *Handler) GetAccountBalance(c *fiber.Ctx) error {
|
|
currencyCode := c.Query("currency_code", "")
|
|
|
|
balances, err := h.chapaSvc.GetAccountBalance(c.Context(), currencyCode)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to fetch Chapa account balance",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Chapa account balance retrieved successfully",
|
|
Data: balances,
|
|
StatusCode: fiber.StatusOK,
|
|
Success: true,
|
|
})
|
|
}
|
|
|
|
// InitiateSwap godoc
|
|
// @Summary Initiate a currency swap
|
|
// @Description Perform a USD to ETB currency swap using Chapa's API
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param payload body domain.SwapRequest true "Swap Request Payload"
|
|
// @Success 200 {object} domain.Response
|
|
// @Failure 400 {object} domain.ErrorResponse
|
|
// @Failure 500 {object} domain.ErrorResponse
|
|
// @Router /api/v1/chapa/swap [post]
|
|
func (h *Handler) InitiateSwap(c *fiber.Ctx) error {
|
|
var req domain.SwapRequest
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
Message: "Invalid request payload",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
swapResult, err := h.chapaSvc.InitiateSwap(c.Context(), req.Amount, req.From, req.To)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
|
|
Message: "Failed to initiate currency swap",
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Currency swap initiated successfully",
|
|
Data: swapResult,
|
|
StatusCode: fiber.StatusOK,
|
|
Success: true,
|
|
})
|
|
}
|