package handlers import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/gofiber/fiber/v2" ) // CreateCheckoutSessionHandler initializes a checkout session with Arifpay. // // @Summary Create Arifpay Checkout Session // @Description Creates a payment session using Arifpay and returns a redirect URL. // @Tags Arifpay // @Accept json // @Produce json // @Param request body domain.CheckoutSessionClientRequest true "Checkout session request payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/arifpay/checkout [post] func (h *Handler) CreateCheckoutSessionHandler(c *fiber.Ctx) error { userId, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ Error: "missing user id", Message: "Unauthorized", }) } var req domain.CheckoutSessionClientRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to process your request", }) } data, err := h.arifpaySvc.CreateCheckoutSession(req, true, userId) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to process your request", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Checkout session created successfully", Data: data, Success: true, StatusCode: fiber.StatusOK, }) } // CancelCheckoutSessionHandler cancels an existing Arifpay checkout session. // // @Summary Cancel Arifpay Checkout Session // @Description Cancels a payment session using Arifpay before completion. // @Tags Arifpay // @Accept json // @Produce json // @Param sessionId path string true "Checkout session ID" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/arifpay/checkout/cancel/{sessionId} [post] func (h *Handler) CancelCheckoutSessionHandler(c *fiber.Ctx) error { sessionID := c.Params("sessionId") if sessionID == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: "missing session ID", Message: "Session ID is required", }) } data, err := h.arifpaySvc.CancelCheckoutSession(c.Context(), sessionID) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to cancel checkout session", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Checkout session canceled successfully", Data: data, Success: true, StatusCode: fiber.StatusOK, }) } // HandleWebhook processes Arifpay webhook notifications. // // @Summary Handle Arifpay C2B Webhook // @Description Handles webhook notifications from Arifpay for C2B transfers and updates transfer + wallet status. // @Tags Arifpay // @Accept json // @Produce json // @Param request body domain.WebhookRequest true "Arifpay webhook payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/arifpay/c2b-webhook [post] func (h *Handler) HandleArifpayC2BWebhook(c *fiber.Ctx) error { var req domain.WebhookRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Invalid webhook payload", }) } // 🚨 Decide how to get userId: // If you get it from auth context/middleware, extract it here. // For now, let's assume userId comes from your auth claims: // userId, ok := c.Locals("user_id").(int64) // if !ok { // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ // Error: "missing user id", // Message: "Unauthorized", // }) // } err := h.arifpaySvc.ProcessWebhook(c.Context(), req, true) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to process webhook", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Webhook processed successfully", Success: true, StatusCode: fiber.StatusOK, }) } // HandleWebhook processes Arifpay webhook notifications. // // @Summary Handle Arifpay B2C Webhook // @Description Handles webhook notifications from Arifpay for B2C transfers and updates transfer + wallet status. // @Tags Arifpay // @Accept json // @Produce json // @Param request body domain.WebhookRequest true "Arifpay webhook payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/arifpay/b2c-webhook [post] func (h *Handler) HandleArifpayB2CWebhook(c *fiber.Ctx) error { var req domain.WebhookRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Invalid webhook payload", }) } // 🚨 Decide how to get userId: // If you get it from auth context/middleware, extract it here. // For now, let's assume userId comes from your auth claims: // userId, ok := c.Locals("user_id").(int64) // if !ok { // return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ // Error: "missing user id", // Message: "Unauthorized", // }) // } err := h.arifpaySvc.ProcessWebhook(c.Context(), req, false) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to process webhook", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Webhook processed successfully", Success: true, StatusCode: fiber.StatusOK, }) } // ArifpayVerifyByTransactionIDHandler godoc // @Summary Verify Arifpay Transaction // @Description Verifies a transaction using transaction ID and payment type // @Tags Arifpay // @Accept json // @Produce json // @Param request body domain.ArifpayVerifyByTransactionIDRequest true "Transaction verification payload" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 502 {object} domain.ErrorResponse // @Router /api/v1/arifpay/transaction-id/verify-transaction [post] func (h *Handler) ArifpayVerifyByTransactionIDHandler(c *fiber.Ctx) error { var req domain.ArifpayVerifyByTransactionIDRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to parse request body", }) } resp, err := h.arifpaySvc.VerifyTransactionByTransactionID(c.Context(), req) if err != nil { return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to verify transaction", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Transaction verified successfully", Data: resp, Success: true, StatusCode: fiber.StatusOK, }) } // ArifpayVerifyBySessionIDHandler godoc // @Summary Verify Arifpay Transaction by Session ID // @Description Verifies an Arifpay transaction using a session ID // @Tags Arifpay // @Accept json // @Produce json // @Param session_id query string true "Arifpay Session ID" // @Success 200 {object} domain.Response // @Failure 400 {object} domain.ErrorResponse // @Failure 502 {object} domain.ErrorResponse // @Router /api/v1/arifpay/session-id/verify-transaction/{session_id} [get] func (h *Handler) ArifpayVerifyBySessionIDHandler(c *fiber.Ctx) error { sessionID := c.Query("session_id") if sessionID == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: "missing session_id", Message: "session_id query parameter is required", }) } resp, err := h.arifpaySvc.VerifyTransactionBySessionID(c.Context(), sessionID) if err != nil { return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to verify transaction", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Transaction verified successfully", Data: resp, Success: true, StatusCode: fiber.StatusOK, }) } // ExecuteTransfer handles B2C transfers via Telebirr, CBE, or MPESA. // // @Summary Execute B2C Transfer // @Description Initiates a B2C transfer using Telebirr, CBE, or MPESA depending on the "type" query parameter // @Tags Arifpay // @Accept json // @Produce json // @Param type query string true "Transfer type (telebirr, cbe, mpesa)" // @Param request body domain.CheckoutSessionClientRequest true "Transfer request payload" // @Success 200 {object} map[string]string "message: transfer executed successfully" // @Failure 400 {object} map[string]string "error: invalid request or unsupported transfer type" // @Failure 500 {object} map[string]string "error: internal server error" // @Router /api/v1/arifpay/b2c/transfer [post] func (h *Handler) ExecuteArifpayB2CTransfer(c *fiber.Ctx) error { transferType := c.Query("type") if transferType == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to process your withdrawal request", Error: "missing query parameter: type (telebirr, cbe, mpesa)", }) } userId, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusUnauthorized).JSON(domain.ErrorResponse{ Error: "missing user id", Message: "Unauthorized", }) } var req domain.CheckoutSessionClientRequest if err := c.BodyParser(&req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to process your withdrawal request", Error: "invalid request body", }) } var err error switch transferType { case "telebirr": err = h.arifpaySvc.ExecuteTelebirrB2CTransfer(c.Context(), req, userId) case "cbe": err = h.arifpaySvc.ExecuteCBEB2CTransfer(c.Context(), req, userId) case "mpesa": err = h.arifpaySvc.ExecuteMPesaB2CTransfer(c.Context(), req, userId) default: return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to process your withdrawal request", Error: "unsupported transfer type, must be one of: telebirr, cbe, mpesa", }) } if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to process your withdrawal request", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Withdrawal process initiated successfully", Success: true, StatusCode: fiber.StatusOK, }) } // GetPaymentMethodsHandler returns the list of all Arifpay payment methods // // @Summary List Arifpay Payment Methods // @Description Returns all payment method IDs and names for Arifpay // @Tags Arifpay // @Produce json // @Success 200 {object} []domain.ARIFPAYPaymentMethod // @Router /api/v1/arifpay/payment-methods [get] func (h *Handler) GetArifpayPaymentMethodsHandler(c *fiber.Ctx) error { methods := h.arifpaySvc.GetPaymentMethodsMapping() return c.Status(fiber.StatusOK).JSON(domain.Response{ Success: true, Message: "Arifpay payment methods fetched successfully", Data: methods, StatusCode: fiber.StatusOK, }) }