package handler import ( "net/http" "strconv" "bugulma/backend/internal/domain" "bugulma/backend/internal/service" "github.com/gin-gonic/gin" ) type SubscriptionHandler struct { subscriptionService *service.SubscriptionService } func NewSubscriptionHandler(subscriptionService *service.SubscriptionService) *SubscriptionHandler { return &SubscriptionHandler{ subscriptionService: subscriptionService, } } // GetSubscription returns the current user's subscription // @Summary Get current subscription // @Tags subscription // @Produce json // @Success 200 {object} domain.Subscription // @Router /api/v1/subscription [get] func (h *SubscriptionHandler) GetSubscription(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } subscription, err := h.subscriptionService.GetSubscription(c.Request.Context(), userID.(string)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, subscription) } // CreateSubscription creates a new subscription // @Summary Create subscription // @Tags subscription // @Accept json // @Produce json // @Param request body CreateSubscriptionRequest true "Subscription request" // @Success 201 {object} domain.Subscription // @Router /api/v1/subscription [post] func (h *SubscriptionHandler) CreateSubscription(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } var req CreateSubscriptionRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } subscription, err := h.subscriptionService.CreateSubscription( c.Request.Context(), userID.(string), domain.SubscriptionPlan(req.Plan), domain.BillingPeriod(req.BillingPeriod), ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, subscription) } type CreateSubscriptionRequest struct { Plan string `json:"plan" binding:"required"` BillingPeriod string `json:"billingPeriod" binding:"required"` } // GetPlans returns available subscription plans // @Summary Get available plans // @Tags subscription // @Produce json // @Success 200 {object} map[string]interface{} // @Router /api/v1/subscription/plans [get] func (h *SubscriptionHandler) GetPlans(c *gin.Context) { plans := map[string]interface{}{ "free": map[string]interface{}{ "id": "free", "name": "Free", "description": "Perfect for getting started", "price": map[string]int{ "monthly": 0, "yearly": 0, }, "features": []string{}, "limits": map[string]interface{}{ "organizations": 3, "users": 1, "storage": 100, "apiCalls": 1000, }, }, "basic": map[string]interface{}{ "id": "basic", "name": "Basic", "description": "For small businesses", "price": map[string]int{ "monthly": 29, "yearly": 290, }, "features": []string{"team_collaboration"}, "limits": map[string]interface{}{ "organizations": 10, "users": 5, "storage": 1000, "apiCalls": 10000, }, }, "professional": map[string]interface{}{ "id": "professional", "name": "Professional", "description": "For growing businesses", "price": map[string]int{ "monthly": 99, "yearly": 990, }, "features": []string{ "unlimited_organizations", "advanced_analytics", "api_access", "team_collaboration", "priority_support", }, "limits": map[string]interface{}{ "organizations": -1, "users": 20, "storage": 10000, "apiCalls": 100000, }, "popular": true, }, "enterprise": map[string]interface{}{ "id": "enterprise", "name": "Enterprise", "description": "For large organizations", "price": map[string]int{ "monthly": 499, "yearly": 4990, }, "features": []string{ "unlimited_organizations", "advanced_analytics", "api_access", "custom_domain", "sso", "team_collaboration", "dedicated_support", "white_label", }, "limits": map[string]interface{}{ "organizations": -1, "users": -1, "storage": -1, "apiCalls": -1, "customDomains": 5, }, }, } c.JSON(http.StatusOK, gin.H{"plans": plans}) } // UpgradeSubscription upgrades a subscription // @Summary Upgrade subscription // @Tags subscription // @Accept json // @Produce json // @Param request body UpgradeSubscriptionRequest true "Upgrade request" // @Success 200 {object} domain.Subscription // @Router /api/v1/subscription/upgrade [post] func (h *SubscriptionHandler) UpgradeSubscription(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } var req UpgradeSubscriptionRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } subscription, err := h.subscriptionService.GetSubscription(c.Request.Context(), userID.(string)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } updated, err := h.subscriptionService.UpdateSubscription( c.Request.Context(), subscription.ID, domain.SubscriptionPlan(req.Plan), ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, updated) } type UpgradeSubscriptionRequest struct { Plan string `json:"plan" binding:"required"` } // CancelSubscription cancels a subscription // @Summary Cancel subscription // @Tags subscription // @Success 200 {object} map[string]string // @Router /api/v1/subscription/cancel [post] func (h *SubscriptionHandler) CancelSubscription(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } subscription, err := h.subscriptionService.GetSubscription(c.Request.Context(), userID.(string)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if err := h.subscriptionService.CancelSubscription(c.Request.Context(), subscription.ID); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Subscription canceled"}) } // GetInvoices returns invoices for the current user // @Summary Get invoices // @Tags subscription // @Produce json // @Param limit query int false "Limit" default(10) // @Param offset query int false "Offset" default(0) // @Success 200 {object} map[string]interface{} // @Router /api/v1/subscription/invoices [get] func (h *SubscriptionHandler) GetInvoices(c *gin.Context) { // TODO: Implement invoice retrieval c.JSON(http.StatusOK, gin.H{"invoices": []interface{}{}, "total": 0}) } // GetPaymentMethods returns payment methods for the current user // @Summary Get payment methods // @Tags subscription // @Produce json // @Success 200 {object} map[string]interface{} // @Router /api/v1/subscription/payment-methods [get] func (h *SubscriptionHandler) GetPaymentMethods(c *gin.Context) { // TODO: Implement payment method retrieval c.JSON(http.StatusOK, gin.H{"paymentMethods": []interface{}{}}) } // AddPaymentMethod adds a payment method // @Summary Add payment method // @Tags subscription // @Accept json // @Produce json // @Param request body AddPaymentMethodRequest true "Payment method request" // @Success 201 {object} domain.PaymentMethod // @Router /api/v1/subscription/payment-methods [post] func (h *SubscriptionHandler) AddPaymentMethod(c *gin.Context) { // TODO: Implement payment method addition c.JSON(http.StatusNotImplemented, gin.H{"error": "Not implemented"}) } type AddPaymentMethodRequest struct { Type string `json:"type" binding:"required"` } // Webhook handles payment provider webhooks // @Summary Handle webhook // @Tags subscription // @Accept json // @Produce json // @Success 200 {object} map[string]string // @Router /api/v1/subscription/webhook [post] func (h *SubscriptionHandler) Webhook(c *gin.Context) { // TODO: Implement webhook handling c.JSON(http.StatusNotImplemented, gin.H{"error": "Not implemented"}) } // GetUsageStats returns usage statistics for the current user // @Summary Get usage statistics // @Tags subscription // @Produce json // @Success 200 {object} map[string]interface{} // @Router /api/v1/subscription/usage [get] func (h *SubscriptionHandler) GetUsageStats(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } stats, err := h.subscriptionService.GetUsageStats(c.Request.Context(), userID.(string)) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"usage": stats}) } // CheckFeature checks if user has access to a feature // @Summary Check feature access // @Tags subscription // @Produce json // @Param feature query string true "Feature name" // @Success 200 {object} map[string]bool // @Router /api/v1/subscription/check-feature [get] func (h *SubscriptionHandler) CheckFeature(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } feature := c.Query("feature") if feature == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Feature parameter required"}) return } hasAccess, err := h.subscriptionService.CheckFeatureAccess( c.Request.Context(), userID.(string), service.SubscriptionFeature(feature), ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"hasAccess": hasAccess}) } // CheckLimits checks if user is within limits // @Summary Check limits // @Tags subscription // @Produce json // @Param limitType query string true "Limit type" // @Param current query int true "Current usage" // @Success 200 {object} map[string]interface{} // @Router /api/v1/subscription/check-limits [get] func (h *SubscriptionHandler) CheckLimits(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } limitTypeStr := c.Query("limitType") currentStr := c.Query("current") if limitTypeStr == "" || currentStr == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "limitType and current parameters required"}) return } current, err := strconv.ParseInt(currentStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid current value"}) return } withinLimits, remaining, err := h.subscriptionService.CheckLimits( c.Request.Context(), userID.(string), domain.UsageLimitType(limitTypeStr), current, ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "withinLimits": withinLimits, "remaining": remaining, }) }