Yimaru-BackEnd/internal/web_server/handlers/chapa.go
2025-05-23 20:11:26 +03:00

284 lines
8.0 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
)
// 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)
}