Docker Compose with 24+ services: - PostgreSQL (PostGIS), Valkey, MinIO, Qdrant - Vault (PKI/TLS), Nginx (Reverse Proxy) - Backend Core API, Consent Service, Billing Service - RAG Service, Embedding Service - Gitea, Woodpecker CI/CD - Night Scheduler, Health Aggregator - Jitsi (Web/XMPP/JVB/Jicofo), Mailpit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/breakpilot/billing-service/internal/config"
|
|
"github.com/breakpilot/billing-service/internal/database"
|
|
"github.com/breakpilot/billing-service/internal/handlers"
|
|
"github.com/breakpilot/billing-service/internal/middleware"
|
|
"github.com/breakpilot/billing-service/internal/services"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func main() {
|
|
// Load configuration
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
// Initialize database
|
|
db, err := database.Connect(cfg.DatabaseURL)
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
// Run migrations
|
|
if err := database.Migrate(db); err != nil {
|
|
log.Fatalf("Failed to run migrations: %v", err)
|
|
}
|
|
|
|
// Setup Gin router
|
|
if cfg.Environment == "production" {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
}
|
|
|
|
router := gin.Default()
|
|
|
|
// Global middleware
|
|
router.Use(middleware.CORS())
|
|
router.Use(middleware.RequestLogger())
|
|
router.Use(middleware.RateLimiter())
|
|
|
|
// Health check (no auth required)
|
|
router.GET("/health", func(c *gin.Context) {
|
|
c.JSON(200, gin.H{
|
|
"status": "healthy",
|
|
"service": "billing-service",
|
|
"version": "1.0.0",
|
|
})
|
|
})
|
|
|
|
// Initialize services
|
|
subscriptionService := services.NewSubscriptionService(db)
|
|
|
|
// Create Stripe service (mock or real depending on config)
|
|
var stripeService *services.StripeService
|
|
if cfg.IsMockMode() {
|
|
log.Println("Starting in MOCK MODE - Stripe API calls will be simulated")
|
|
stripeService = services.NewMockStripeService(
|
|
cfg.BillingSuccessURL,
|
|
cfg.BillingCancelURL,
|
|
cfg.TrialPeriodDays,
|
|
subscriptionService,
|
|
)
|
|
} else {
|
|
stripeService = services.NewStripeService(
|
|
cfg.StripeSecretKey,
|
|
cfg.StripeWebhookSecret,
|
|
cfg.BillingSuccessURL,
|
|
cfg.BillingCancelURL,
|
|
cfg.TrialPeriodDays,
|
|
subscriptionService,
|
|
)
|
|
}
|
|
|
|
entitlementService := services.NewEntitlementService(db, subscriptionService)
|
|
usageService := services.NewUsageService(db, entitlementService)
|
|
|
|
// Initialize handlers
|
|
billingHandler := handlers.NewBillingHandler(
|
|
db,
|
|
subscriptionService,
|
|
stripeService,
|
|
entitlementService,
|
|
usageService,
|
|
)
|
|
webhookHandler := handlers.NewWebhookHandler(
|
|
db,
|
|
cfg.StripeWebhookSecret,
|
|
subscriptionService,
|
|
entitlementService,
|
|
)
|
|
|
|
// API v1 routes
|
|
v1 := router.Group("/api/v1/billing")
|
|
{
|
|
// Stripe webhook (no auth - uses Stripe signature)
|
|
v1.POST("/webhook", webhookHandler.HandleStripeWebhook)
|
|
|
|
// =============================================
|
|
// User Endpoints (require JWT auth)
|
|
// =============================================
|
|
user := v1.Group("")
|
|
user.Use(middleware.AuthMiddleware(cfg.JWTSecret))
|
|
{
|
|
// Subscription status and management
|
|
user.GET("/status", billingHandler.GetBillingStatus)
|
|
user.GET("/plans", billingHandler.GetPlans)
|
|
user.POST("/trial/start", billingHandler.StartTrial)
|
|
user.POST("/change-plan", billingHandler.ChangePlan)
|
|
user.POST("/cancel", billingHandler.CancelSubscription)
|
|
user.GET("/portal", billingHandler.GetCustomerPortal)
|
|
}
|
|
|
|
// =============================================
|
|
// Internal Endpoints (service-to-service)
|
|
// =============================================
|
|
internal := v1.Group("")
|
|
internal.Use(middleware.InternalAPIKeyMiddleware(cfg.InternalAPIKey))
|
|
{
|
|
// Entitlements
|
|
internal.GET("/entitlements/:userId", billingHandler.GetEntitlements)
|
|
internal.GET("/entitlements/check/:userId/:feature", billingHandler.CheckEntitlement)
|
|
|
|
// Usage tracking
|
|
internal.POST("/usage/track", billingHandler.TrackUsage)
|
|
internal.GET("/usage/check/:userId/:type", billingHandler.CheckUsage)
|
|
}
|
|
}
|
|
|
|
// Start server
|
|
port := cfg.Port
|
|
if port == "" {
|
|
port = "8083"
|
|
}
|
|
|
|
log.Printf("Starting Billing Service on port %s", port)
|
|
if err := router.Run(":" + port); err != nil {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}
|