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

232 lines
7.3 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.Sprintln("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.Sprintln("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",
})
}
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_TRANSFER_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_PAYMENT_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.ManualVerifTransaction(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,
})
}