package handlers import ( "encoding/json" "errors" "Yimaru-Backend/internal/domain" "Yimaru-Backend/internal/services/chapa" "github.com/gofiber/fiber/v2" ) // HandleChapaWebhook godoc // @Summary Handle Chapa webhook // @Description Processes payment notifications from Chapa (charge.success, etc.) // @Tags payments // @Accept json // @Produce json // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Router /api/v1/payments/webhook [post] func (h *Handler) HandleChapaWebhook(c *fiber.Ctx) error { body := c.Body() signature := c.Get("x-chapa-signature") if signature == "" { signature = c.Get("chapa-signature") } if err := h.chapaSvc.VerifyWebhookSignature(body, signature); err != nil { h.logger.Error("Invalid Chapa webhook signature", "error", err) return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ Message: "Invalid webhook signature", }) } var payload domain.ChapaWebhookPayload if err := json.Unmarshal(body, &payload); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid webhook payload", Error: err.Error(), }) } if err := h.chapaSvc.ProcessPaymentWebhook(c.Context(), payload); err != nil { if errors.Is(err, chapa.ErrPaymentAlreadyPaid) { return c.JSON(domain.Response{Message: "Webhook already processed"}) } h.logger.Error("Failed to process Chapa webhook", "error", err, "tx_ref", payload.TxRef) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to process webhook", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Webhook processed successfully", }) } // HandleChapaCallback godoc // @Summary Chapa payment callback // @Description Verifies payment after Chapa redirects to callback_url // @Tags payments // @Produce json // @Param trx_ref query string false "Transaction reference" // @Param ref_id query string false "Chapa reference ID" // @Param status query string false "Payment status" // @Success 200 {object} domain.Response // @Router /api/v1/payments/chapa/callback [get] func (h *Handler) HandleChapaCallback(c *fiber.Ctx) error { query := domain.ChapaCallbackQuery{ TrxRef: c.Query("trx_ref"), RefID: c.Query("ref_id"), Status: c.Query("status"), } if query.TrxRef == "" { query.TrxRef = c.Query("tx_ref") } if query.TrxRef == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "trx_ref is required", }) } if err := h.chapaSvc.ProcessCallback(c.Context(), query); err != nil { if errors.Is(err, chapa.ErrPaymentAlreadyPaid) { return c.JSON(domain.Response{Message: "Payment already processed"}) } h.logger.Error("Failed to process Chapa callback", "error", err, "trx_ref", query.TrxRef) return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to process callback", Error: err.Error(), }) } return c.JSON(domain.Response{ Message: "Callback processed successfully", }) } // GetChapaPaymentMethods godoc // @Summary Get Chapa payment methods // @Description Returns payment methods available on Chapa checkout // @Tags payments // @Produce json // @Success 200 {object} domain.Response // @Router /api/v1/payments/methods [get] func (h *Handler) GetChapaPaymentMethods(c *fiber.Ctx) error { return c.JSON(domain.Response{ Message: "Payment methods retrieved successfully", Data: h.chapaSvc.GetPaymentMethods(), }) }