turash/bugulma/backend/internal/handler/subscription_handler.go

402 lines
11 KiB
Go

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,
})
}