package handlers import ( "fmt" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/gofiber/fiber/v2" ) // InitiateDeposit godoc // @Summary Initiate a deposit // @Description Starts a new deposit process using Chapa payment gateway // @Tags Chapa // @Accept json // @Produce json // @Security ApiKeyAuth // @Param request body domain.ChapaDepositRequestPayload true "Deposit request" // @Success 200 {object} domain.ChapaDepositResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/chapa/payments/deposit [post] func (h *Handler) InitiateDeposit(c *fiber.Ctx) error { // Get user ID from context (set by your auth middleware) userID, ok := c.Locals("user_id").(int64) if !ok { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: "invalid user ID", Message: "User ID is required to initiate a deposit", }) } var req domain.ChapaDepositRequestPayload if err := c.BodyParser(&req); err != nil { // fmt.Println("We first first are here init Chapa payment") return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to parse request body", }) } amount := domain.Currency(req.Amount * 100) fmt.Println("We are here init Chapa payment") checkoutURL, err := h.chapaSvc.InitiateDeposit(c.Context(), userID, amount) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to initiate Chapa deposit", }) } // get static wallet of user // wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), userID) // if err != nil { // return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ // Error: err.Error(), // Message: "Failed to initiate Chapa deposit", // }) // } // var multiplier float32 = 1 // bonusMultiplier, err := h.bonusSvc.GetBonusMultiplier(c.Context()) // if err == nil { // multiplier = bonusMultiplier[0].Multiplier // } // var balanceCap int64 = 0 // bonusBalanceCap, err := h.bonusSvc.GetBonusBalanceCap(c.Context()) // if err == nil { // balanceCap = bonusBalanceCap[0].BalanceCap // } // capedBalanceAmount := domain.Currency((math.Min(req.Amount, float64(balanceCap)) * float64(multiplier)) * 100) // _, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, capedBalanceAmount, domain.ValidInt64{}, domain.TRANSFER_DIRECT, domain.PaymentDetails{}, // fmt.Sprintf("Added %v to static wallet because of deposit bonus using multiplier %v", capedBalanceAmount, multiplier), // ) // if err != nil { // h.logger.Error("Failed to add bonus to static wallet", "walletID", wallet.StaticID, "user id", userID, "error", err) // return err // } // if err := h.bonusSvc.ProcessWelcomeBonus(c.Context(), domain.ToCurrency(float32(req.Amount)), 0, userID); err != nil { // return err // } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Chapa deposit process initiated successfully", Data: checkoutURL, StatusCode: 200, Success: true, }) } // WebhookCallback godoc // @Summary Chapa payment webhook callback (used by Chapa) // @Description Handles payment notifications from Chapa // @Tags Chapa // @Accept json // @Produce json // @Param request body domain.ChapaWebhookPayload true "Webhook payload" // @Success 200 {object} map[string]interface{} // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/chapa/payments/webhook/verify [post] func (h *Handler) WebhookCallback(c *fiber.Ctx) error { chapaTransactionType := new(domain.ChapaTransactionType) if parseTypeErr := c.BodyParser(chapaTransactionType); parseTypeErr != nil { return domain.UnProcessableEntityResponse(c) } switch chapaTransactionType.Type { case h.Cfg.CHAPA_PAYMENT_TYPE: chapaTransferVerificationRequest := new(domain.ChapaWebHookTransfer) if err := c.BodyParser(chapaTransferVerificationRequest); err != nil { return domain.UnProcessableEntityResponse(c) } err := h.chapaSvc.HandleVerifyDepositWebhook(c.Context(), *chapaTransferVerificationRequest) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to verify Chapa depposit", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ StatusCode: 200, Message: "Chapa deposit transaction verified successfully", Data: chapaTransferVerificationRequest, Success: true, }) case h.Cfg.CHAPA_TRANSFER_TYPE: chapaPaymentVerificationRequest := new(domain.ChapaWebHookPayment) if err := c.BodyParser(chapaPaymentVerificationRequest); err != nil { return domain.UnProcessableEntityResponse(c) } err := h.chapaSvc.HandleVerifyWithdrawWebhook(c.Context(), *chapaPaymentVerificationRequest) if err != nil { return domain.UnExpectedErrorResponse(c) } return c.Status(fiber.StatusOK).JSON(domain.Response{ StatusCode: 200, Message: "Chapa withdrawal transaction verified successfully", Data: chapaPaymentVerificationRequest, Success: true, }) } // Return a 400 Bad Request if the type does not match any known case return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Invalid Chapa webhook type", Error: "Unknown transaction type", }) } // VerifyPayment godoc // @Summary Verify a payment manually // @Description Manually verify a payment using Chapa's API // @Tags Chapa // @Accept json // @Produce json // @Param tx_ref path string true "Transaction Reference" // @Success 200 {object} domain.ChapaVerificationResponse // @Failure 400 {object} domain.ErrorResponse // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/chapa/payments/manual/verify/{tx_ref} [get] func (h *Handler) ManualVerifyTransaction(c *fiber.Ctx) error { txRef := c.Params("tx_ref") if txRef == "" { return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{ Message: "Failed to verify Chapa transaction", Error: "Transaction reference is required", }) } verification, err := h.chapaSvc.ManuallyVerify(c.Context(), txRef) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to verify Chapa transaction", Error: err.Error(), }) } return c.Status(fiber.StatusOK).JSON(domain.ChapaVerificationResponse{ Status: string(verification.Status), Amount: verification.Amount, Currency: verification.Currency, TxRef: txRef, }) } // GetSupportedBanks godoc // @Summary Get supported banks // @Description Get list of banks supported by Chapa // @Tags Chapa // @Accept json // @Produce json // @Success 200 {object} domain.Response // @Failure 500 {object} domain.ErrorResponse // @Router /api/v1/chapa/banks [get] func (h *Handler) GetSupportedBanks(c *fiber.Ctx) error { banks, err := h.chapaSvc.GetSupportedBanks(c.Context()) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Error: err.Error(), Message: "Failed to fetch banks", }) } return c.Status(fiber.StatusOK).JSON(domain.Response{ Message: "Banks fetched successfully", StatusCode: 200, Success: true, Data: banks, }) } // InitiateWithdrawal initiates a withdrawal request via Chapa payment gateway // @Summary Initiate a withdrawal // @Description Initiates a withdrawal request to transfer funds to a bank account via Chapa // @Tags Chapa // @Accept json // @Produce json // @Security ApiKeyAuth // @Param request body domain.ChapaWithdrawalRequest true "Withdrawal request details" // @Success 201 {object} domain.Response "Chapa withdrawal process initiated successfully" // @Failure 400 {object} domain.ErrorResponse "Invalid request body" // @Failure 401 {object} domain.ErrorResponse "Unauthorized" // @Failure 422 {object} domain.ErrorResponse "Unprocessable entity" // @Failure 500 {object} domain.ErrorResponse "Internal server error" // @Router /api/v1/chapa/payments/withdraw [post] func (h *Handler) InitiateWithdrawal(c *fiber.Ctx) error { userID, ok := c.Locals("user_id").(int64) if !ok { return domain.UnProcessableEntityResponse(c) } var req domain.ChapaWithdrawalRequest if err := c.BodyParser(&req); err != nil { return domain.UnProcessableEntityResponse(c) } withdrawal, err := h.chapaSvc.InitiateWithdrawal(c.Context(), userID, req) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{ Message: "Failed to initiate Chapa withdrawal", Error: err.Error(), }) } return c.Status(fiber.StatusCreated).JSON(domain.Response{ Message: "Chapa withdrawal process initiated successfully", StatusCode: 201, Success: true, Data: withdrawal, }) }