465 lines
15 KiB
Go
465 lines
15 KiB
Go
package handlers
|
|
|
|
import (
|
|
// "bytes"
|
|
// "encoding/json"
|
|
// "fmt"
|
|
// "io"
|
|
// "net/http"
|
|
|
|
"fmt"
|
|
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
"github.com/gofiber/fiber/v2"
|
|
)
|
|
|
|
// // GetBanks godoc
|
|
// // @Summary Get list of banks
|
|
// // @Description Fetch all supported banks from Chapa
|
|
// // @Tags Chapa
|
|
// // @Accept json
|
|
// // @Produce json
|
|
// // @Success 200 {object} domain.ChapaSupportedBanksResponse
|
|
// // @Router /api/v1/chapa/banks [get]
|
|
// func (h *Handler) GetBanks(c *fiber.Ctx) error {
|
|
// httpReq, err := http.NewRequest("GET", h.Cfg.CHAPA_BASE_URL+"/banks", nil)
|
|
// // log.Printf("\n\nbase url is: %v\n\n", h.Cfg.CHAPA_BASE_URL)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{"error": "Failed to create request", "details": err.Error()})
|
|
// }
|
|
// httpReq.Header.Set("Authorization", "Bearer "+h.Cfg.CHAPA_SECRET_KEY)
|
|
|
|
// resp, err := http.DefaultClient.Do(httpReq)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{"error": "Failed to fetch banks", "details": err.Error()})
|
|
// }
|
|
// defer resp.Body.Close()
|
|
|
|
// body, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{"error": "Failed to read response", "details": err.Error()})
|
|
// }
|
|
|
|
// return c.Status(resp.StatusCode).Type("json").Send(body)
|
|
// }
|
|
|
|
// // InitializePayment godoc
|
|
// // @Summary Initialize a payment transaction
|
|
// // @Description Initiate a payment through Chapa
|
|
// // @Tags Chapa
|
|
// // @Accept json
|
|
// // @Produce json
|
|
// // @Param payload body domain.InitPaymentRequest true "Payment initialization request"
|
|
// // @Success 200 {object} domain.InitPaymentResponse
|
|
// // @Router /api/v1/chapa/payments/initialize [post]
|
|
// func (h *Handler) InitializePayment(c *fiber.Ctx) error {
|
|
// var req InitPaymentRequest
|
|
// if err := c.BodyParser(&req); err != nil {
|
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
// "error": "Invalid request body",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// // Generate and assign a unique transaction reference
|
|
// req.TxRef = uuid.New().String()
|
|
|
|
// payload, err := json.Marshal(req)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to serialize request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// httpReq, err := http.NewRequest("POST", h.Cfg.CHAPA_BASE_URL+"/transaction/initialize", bytes.NewBuffer(payload))
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to create request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
// httpReq.Header.Set("Authorization", "Bearer "+h.Cfg.CHAPA_SECRET_KEY)
|
|
// httpReq.Header.Set("Content-Type", "application/json")
|
|
|
|
// resp, err := http.DefaultClient.Do(httpReq)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to initialize payment",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
// defer resp.Body.Close()
|
|
|
|
// body, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to read response",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// return c.Status(resp.StatusCode).Type("json").Send(body)
|
|
// }
|
|
|
|
// // VerifyTransaction godoc
|
|
// // @Summary Verify a payment transaction
|
|
// // @Description Verify the transaction status from Chapa using tx_ref
|
|
// // @Tags Chapa
|
|
// // @Accept json
|
|
// // @Produce json
|
|
// // @Param tx_ref path string true "Transaction Reference"
|
|
// // @Success 200 {object} domain.VerifyTransactionResponse
|
|
// // @Router /api/v1/chapa/payments/verify/{tx_ref} [get]
|
|
// func (h *Handler) VerifyTransaction(c *fiber.Ctx) error {
|
|
// txRef := c.Params("tx_ref")
|
|
// if txRef == "" {
|
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
// "error": "Missing transaction reference",
|
|
// })
|
|
// }
|
|
|
|
// url := fmt.Sprintf("%s/transaction/verify/%s", h.Cfg.CHAPA_BASE_URL, txRef)
|
|
|
|
// httpReq, err := http.NewRequest("GET", url, nil)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to create request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
// httpReq.Header.Set("Authorization", "Bearer "+h.Cfg.CHAPA_SECRET_KEY)
|
|
|
|
// resp, err := http.DefaultClient.Do(httpReq)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to verify transaction",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
// defer resp.Body.Close()
|
|
|
|
// body, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return c.Status(500).JSON(fiber.Map{
|
|
// "error": "Failed to read response",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// return c.Status(resp.StatusCode).Type("json").Send(body)
|
|
// }
|
|
|
|
// // ReceiveWebhook godoc
|
|
// // @Summary Receive Chapa webhook
|
|
// // @Description Endpoint to receive webhook payloads from Chapa
|
|
// // @Tags Chapa
|
|
// // @Accept json
|
|
// // @Produce json
|
|
// // @Param payload body object true "Webhook Payload (dynamic)"
|
|
// // @Success 200 {string} string "ok"
|
|
// // @Router /api/v1/chapa/payments/callback [post]
|
|
// func (h *Handler) ReceiveWebhook(c *fiber.Ctx) error {
|
|
// var payload map[string]interface{}
|
|
// if err := c.BodyParser(&payload); err != nil {
|
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
// "error": "Invalid webhook data",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// h.logger.Info("Chapa webhook received", "payload", payload)
|
|
|
|
// // Optional: you can verify tx_ref here again if needed
|
|
|
|
// return c.SendStatus(fiber.StatusOK)
|
|
// }
|
|
|
|
// // CreateTransfer godoc
|
|
// // @Summary Create a money transfer
|
|
// // @Description Initiate a transfer request via Chapa
|
|
// // @Tags Chapa
|
|
// // @Accept json
|
|
// // @Produce json
|
|
// // @Param payload body domain.TransferRequest true "Transfer request body"
|
|
// // @Success 200 {object} domain.CreateTransferResponse
|
|
// // @Router /api/v1/chapa/transfers [post]
|
|
// func (h *Handler) CreateTransfer(c *fiber.Ctx) error {
|
|
// var req TransferRequest
|
|
// if err := c.BodyParser(&req); err != nil {
|
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
// "error": "Invalid request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// // Inject unique transaction reference
|
|
// req.Reference = uuid.New().String()
|
|
|
|
// payload, err := json.Marshal(req)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Failed to serialize request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// httpReq, err := http.NewRequest("POST", h.Cfg.CHAPA_BASE_URL+"/transfers", bytes.NewBuffer(payload))
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Failed to create HTTP request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// httpReq.Header.Set("Authorization", "Bearer "+h.Cfg.CHAPA_SECRET_KEY)
|
|
// httpReq.Header.Set("Content-Type", "application/json")
|
|
|
|
// resp, err := http.DefaultClient.Do(httpReq)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Transfer request failed",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
// defer resp.Body.Close()
|
|
|
|
// body, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Failed to read response",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// return c.Status(resp.StatusCode).Type("json").Send(body)
|
|
// }
|
|
|
|
// // VerifyTransfer godoc
|
|
// // @Summary Verify a transfer
|
|
// // @Description Check the status of a money transfer via reference
|
|
// // @Tags Chapa
|
|
// // @Accept json
|
|
// // @Produce json
|
|
// // @Param transfer_ref path string true "Transfer Reference"
|
|
// // @Success 200 {object} domain.VerifyTransferResponse
|
|
// // @Router /api/v1/chapa/transfers/verify/{transfer_ref} [get]
|
|
// func (h *Handler) VerifyTransfer(c *fiber.Ctx) error {
|
|
// transferRef := c.Params("transfer_ref")
|
|
// if transferRef == "" {
|
|
// return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
// "error": "Missing transfer reference in URL",
|
|
// })
|
|
// }
|
|
|
|
// url := fmt.Sprintf("%s/transfers/verify/%s", h.Cfg.CHAPA_BASE_URL, transferRef)
|
|
|
|
// httpReq, err := http.NewRequest("GET", url, nil)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Failed to create HTTP request",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// httpReq.Header.Set("Authorization", "Bearer "+h.Cfg.CHAPA_SECRET_KEY)
|
|
|
|
// resp, err := http.DefaultClient.Do(httpReq)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Verification request failed",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
// defer resp.Body.Close()
|
|
|
|
// body, err := io.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
|
// "error": "Failed to read response body",
|
|
// "details": err.Error(),
|
|
// })
|
|
// }
|
|
|
|
// return c.Status(resp.StatusCode).Type("json").Send(body)
|
|
// }
|
|
|
|
// VerifyChapaPayment godoc
|
|
// @Summary Verifies Chapa webhook transaction
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param payload body domain.ChapaTransactionType true "Webhook Payload"
|
|
// @Success 200 {object} domain.Response
|
|
// @Router /api/v1/chapa/payments/verify [post]
|
|
func (h *Handler) VerifyChapaPayment(c *fiber.Ctx) error {
|
|
var txType domain.ChapaTransactionType
|
|
if err := c.BodyParser(&txType); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
switch txType.Type {
|
|
case "Payout":
|
|
var payload domain.ChapaWebHookTransfer
|
|
if err := c.BodyParser(&payload); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
if err := h.chapaSvc.HandleChapaTransferWebhook(c.Context(), payload); err != nil {
|
|
return domain.FiberErrorResponse(c, err)
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Chapa transfer verified successfully",
|
|
Success: true,
|
|
StatusCode: fiber.StatusOK,
|
|
})
|
|
|
|
case "API":
|
|
var payload domain.ChapaWebHookPayment
|
|
if err := c.BodyParser(&payload); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
if err := h.chapaSvc.HandleChapaPaymentWebhook(c.Context(), payload); err != nil {
|
|
return domain.FiberErrorResponse(c, err)
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Chapa payment verified successfully",
|
|
Success: true,
|
|
StatusCode: fiber.StatusOK,
|
|
})
|
|
|
|
default:
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.Response{
|
|
Message: "Invalid Chapa transaction type",
|
|
Success: false,
|
|
StatusCode: fiber.StatusBadRequest,
|
|
})
|
|
}
|
|
}
|
|
|
|
// WithdrawUsingChapa godoc
|
|
// @Summary Withdraw using Chapa
|
|
// @Description Initiates a withdrawal transaction using Chapa for the authenticated user.
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param request body domain.ChapaWithdrawRequest true "Chapa Withdraw Request"
|
|
// @Success 200 {object} domain.Response{data=string} "Withdrawal requested successfully"
|
|
// @Failure 400 {object} domain.Response "Invalid request"
|
|
// @Failure 401 {object} domain.Response "Unauthorized"
|
|
// @Failure 422 {object} domain.Response "Unprocessable Entity"
|
|
// @Failure 500 {object} domain.Response "Internal Server Error"
|
|
// @Router /api/v1/chapa/payments/withdraw [post]
|
|
func (h *Handler) WithdrawUsingChapa(c *fiber.Ctx) error {
|
|
var req domain.ChapaWithdrawRequest
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
userID, ok := c.Locals("user_id").(int64)
|
|
if !ok || userID == 0 {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(domain.Response{
|
|
Message: "Unauthorized",
|
|
Success: false,
|
|
StatusCode: fiber.StatusUnauthorized,
|
|
})
|
|
}
|
|
|
|
if err := h.chapaSvc.WithdrawUsingChapa(c.Context(), userID, req); err != nil {
|
|
return domain.FiberErrorResponse(c, err)
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.Response{
|
|
Message: "Withdrawal requested successfully",
|
|
Success: true,
|
|
StatusCode: fiber.StatusOK,
|
|
})
|
|
}
|
|
|
|
// DepositUsingChapa godoc
|
|
// @Summary Deposit money into user wallet using Chapa
|
|
// @Description Deposits money into user wallet from user account using Chapa
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param payload body domain.ChapaDepositRequest true "Deposit request payload"
|
|
// @Success 200 {object} domain.ChapaPaymentUrlResponseWrapper
|
|
// @Failure 400 {object} domain.Response "Invalid request"
|
|
// @Failure 422 {object} domain.Response "Validation error"
|
|
// @Failure 500 {object} domain.Response "Internal server error"
|
|
// @Router /api/v1/chapa/payments/deposit [post]
|
|
func (h *Handler) DepositUsingChapa(c *fiber.Ctx) error {
|
|
// Extract user info from token (adjust as per your auth middleware)
|
|
userID, ok := c.Locals("user_id").(int64)
|
|
if !ok || userID == 0 {
|
|
return c.Status(fiber.StatusUnauthorized).JSON(domain.Response{
|
|
Message: "Unauthorized",
|
|
Success: false,
|
|
StatusCode: fiber.StatusUnauthorized,
|
|
})
|
|
}
|
|
|
|
var req domain.ChapaDepositRequest
|
|
if err := c.BodyParser(&req); err != nil {
|
|
return domain.UnProcessableEntityResponse(c)
|
|
}
|
|
|
|
// Validate input in domain/domain (you may have a Validate method)
|
|
if err := req.Validate(); err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(domain.Response{
|
|
Message: err.Error(),
|
|
Success: false,
|
|
StatusCode: fiber.StatusBadRequest,
|
|
})
|
|
}
|
|
|
|
// Call service to handle the deposit logic and get payment URL
|
|
paymentUrl, svcErr := h.chapaSvc.DepositUsingChapa(c.Context(), userID, req)
|
|
if svcErr != nil {
|
|
return domain.FiberErrorResponse(c, svcErr)
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.ResponseWDataFactory[domain.ChapaPaymentUrlResponse]{
|
|
Data: domain.ChapaPaymentUrlResponse{
|
|
PaymentURL: paymentUrl,
|
|
},
|
|
Response: domain.Response{
|
|
Message: "Deposit process started on wallet, fulfill payment using the URL provided",
|
|
Success: true,
|
|
StatusCode: fiber.StatusOK,
|
|
},
|
|
})
|
|
}
|
|
|
|
// ReadChapaBanks godoc
|
|
// @Summary fetches chapa supported banks
|
|
// @Tags Chapa
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {object} domain.ChapaSupportedBanksResponseWrapper
|
|
// @Failure 400,401,404,422,500 {object} domain.Response
|
|
// @Router /api/v1/chapa/banks [get]
|
|
func (h *Handler) ReadChapaBanks(c *fiber.Ctx) error {
|
|
banks, err := h.chapaSvc.GetSupportedBanks()
|
|
fmt.Printf("\n\nhandler fetched banks: %+v\n\n", banks)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(domain.Response{
|
|
Message: "Internal server error",
|
|
Success: false,
|
|
StatusCode: fiber.StatusInternalServerError,
|
|
})
|
|
}
|
|
|
|
return c.Status(fiber.StatusOK).JSON(domain.ResponseWDataFactory[[]domain.ChapaSupportedBank]{
|
|
Data: banks,
|
|
Response: domain.Response{
|
|
Message: "read successful on chapa supported banks",
|
|
Success: true,
|
|
StatusCode: fiber.StatusOK,
|
|
},
|
|
})
|
|
}
|