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, }, }) }