package handlers import ( "errors" "net/http" apperrors "veza-backend-api/internal/errors" "veza-backend-api/internal/core/marketplace" "veza-backend-api/internal/services" "github.com/gin-gonic/gin" "go.uber.org/zap" "gorm.io/gorm" ) // SellHandler handles Stripe Connect seller payout endpoints type SellHandler struct { db *gorm.DB stripeConnect *services.StripeConnectService logger *zap.Logger } // NewSellHandler creates a new SellHandler func NewSellHandler(db *gorm.DB, stripeConnect *services.StripeConnectService, logger *zap.Logger) *SellHandler { return &SellHandler{ db: db, stripeConnect: stripeConnect, logger: logger, } } // ConnectOnboardRequest is the request body for onboarding type ConnectOnboardRequest struct { ReturnURL string `json:"return_url"` RefreshURL string `json:"refresh_url"` } // ConnectOnboard starts Stripe Connect onboarding and returns the onboarding URL func (h *SellHandler) ConnectOnboard(c *gin.Context) { if h.stripeConnect == nil { RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled")) return } userID, ok := GetUserIDUUID(c) if !ok { return } var req ConnectOnboardRequest _ = c.ShouldBindJSON(&req) returnURL := req.ReturnURL if returnURL == "" { returnURL = c.Request.URL.Scheme + "://" + c.Request.Host + "/sell/dashboard?onboarded=success" } refreshURL := req.RefreshURL if refreshURL == "" { refreshURL = c.Request.URL.Scheme + "://" + c.Request.Host + "/sell/dashboard?onboarded=refresh" } url, err := h.stripeConnect.CreateOnboardingLink(c.Request.Context(), userID, returnURL, refreshURL) if err != nil { if errors.Is(err, services.ErrStripeConnectDisabled) { RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled")) return } h.logger.Error("CreateOnboardingLink failed", zap.Error(err), zap.String("user_id", userID.String())) RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to create onboarding link", err)) return } RespondSuccess(c, http.StatusOK, gin.H{"onboarding_url": url}) } // ConnectCallback syncs account status after Stripe redirect (called by frontend after return) func (h *SellHandler) ConnectCallback(c *gin.Context) { if h.stripeConnect == nil { RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled")) return } userID, ok := GetUserIDUUID(c) if !ok { return } if err := h.stripeConnect.HandleOnboardingCallback(c.Request.Context(), userID); err != nil { if errors.Is(err, services.ErrNoStripeAccount) { RespondWithAppError(c, apperrors.NewNotFoundError("Stripe account")) return } if errors.Is(err, services.ErrStripeConnectDisabled) { RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled")) return } h.logger.Error("HandleOnboardingCallback failed", zap.Error(err), zap.String("user_id", userID.String())) RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to sync account status", err)) return } RespondSuccess(c, http.StatusOK, gin.H{"message": "Account synced"}) } // GetBalance returns the seller's Stripe Connect balance func (h *SellHandler) GetBalance(c *gin.Context) { if h.stripeConnect == nil { RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled")) return } userID, ok := GetUserIDUUID(c) if !ok { return } bal, err := h.stripeConnect.GetBalance(c.Request.Context(), userID) if err != nil { if errors.Is(err, services.ErrStripeConnectDisabled) { RespondWithAppError(c, apperrors.NewServiceUnavailableError("Stripe Connect is not enabled")) return } h.logger.Error("GetBalance failed", zap.Error(err), zap.String("user_id", userID.String())) RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to get balance", err)) return } RespondSuccess(c, http.StatusOK, gin.H{ "connected": bal.Connected, "available": bal.Available, "pending": bal.Pending, }) } // GetSellerTransfers returns the transfer history for the authenticated seller (v0.603) func (h *SellHandler) GetSellerTransfers(c *gin.Context) { userID, ok := GetUserIDUUID(c) if !ok { return } var transfers []marketplace.SellerTransfer if err := h.db.Where("seller_id = ?", userID).Order("created_at DESC").Find(&transfers).Error; err != nil { h.logger.Error("GetSellerTransfers failed", zap.Error(err), zap.String("user_id", userID.String())) RespondWithAppError(c, apperrors.NewInternalErrorWrap("Failed to fetch transfers", err)) return } RespondSuccess(c, http.StatusOK, transfers) }