Add Stripe billing, free trials, and cross-platform subscription guards
- Stripe integration: add StripeService with checkout sessions, customer portal, and webhook handling for subscription lifecycle events. - Free trials: auto-start configurable trial on first subscription check, with admin-controllable duration and enable/disable toggle. - Cross-platform guard: prevent duplicate subscriptions across iOS, Android, and Stripe by checking existing platform before allowing purchase. - Subscription model: add Stripe fields (customer_id, subscription_id, price_id), trial fields (trial_start, trial_end, trial_used), and SubscriptionSource/IsTrialActive helpers. - API: add trial and source fields to status response, update OpenAPI spec. - Clean up stale migration and audit docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -58,7 +58,13 @@ func SetupRouter(deps *Dependencies) *echo.Echo {
|
||||
e.Use(custommiddleware.RequestIDMiddleware())
|
||||
e.Use(utils.EchoRecovery())
|
||||
e.Use(custommiddleware.StructuredLogger())
|
||||
e.Use(middleware.BodyLimit("1M")) // 1MB default for JSON payloads
|
||||
e.Use(middleware.BodyLimitWithConfig(middleware.BodyLimitConfig{
|
||||
Limit: "1M", // 1MB default for JSON payloads
|
||||
Skipper: func(c echo.Context) bool {
|
||||
// Allow larger payloads for webhook endpoints (Apple/Google/Stripe notifications)
|
||||
return strings.HasPrefix(c.Request().URL.Path, "/api/subscription/webhook")
|
||||
},
|
||||
}))
|
||||
e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
|
||||
Timeout: 30 * time.Second,
|
||||
Skipper: func(c echo.Context) bool {
|
||||
@@ -143,11 +149,15 @@ func SetupRouter(deps *Dependencies) *echo.Echo {
|
||||
residenceService.SetSubscriptionService(subscriptionService) // Wire up subscription service for tier limit enforcement
|
||||
taskTemplateService := services.NewTaskTemplateService(taskTemplateRepo)
|
||||
|
||||
// Initialize Stripe service
|
||||
stripeService := services.NewStripeService(subscriptionRepo, userRepo)
|
||||
|
||||
// Initialize webhook event repo for deduplication
|
||||
webhookEventRepo := repositories.NewWebhookEventRepository(deps.DB)
|
||||
|
||||
// Initialize webhook handler for Apple/Google subscription notifications
|
||||
// Initialize webhook handler for Apple/Google/Stripe subscription notifications
|
||||
subscriptionWebhookHandler := handlers.NewSubscriptionWebhookHandler(subscriptionRepo, userRepo, webhookEventRepo, cfg.Features.WebhooksEnabled)
|
||||
subscriptionWebhookHandler.SetStripeService(stripeService)
|
||||
|
||||
// Initialize middleware
|
||||
authMiddleware := custommiddleware.NewAuthMiddleware(deps.DB, deps.Cache)
|
||||
@@ -166,7 +176,7 @@ func SetupRouter(deps *Dependencies) *echo.Echo {
|
||||
contractorHandler := handlers.NewContractorHandler(contractorService)
|
||||
documentHandler := handlers.NewDocumentHandler(documentService, deps.StorageService)
|
||||
notificationHandler := handlers.NewNotificationHandler(notificationService)
|
||||
subscriptionHandler := handlers.NewSubscriptionHandler(subscriptionService)
|
||||
subscriptionHandler := handlers.NewSubscriptionHandler(subscriptionService, stripeService)
|
||||
staticDataHandler := handlers.NewStaticDataHandler(residenceService, taskService, contractorService, taskTemplateService, deps.Cache)
|
||||
taskTemplateHandler := handlers.NewTaskTemplateHandler(taskTemplateService)
|
||||
|
||||
@@ -458,6 +468,8 @@ func setupSubscriptionRoutes(api *echo.Group, subscriptionHandler *handlers.Subs
|
||||
subscription.POST("/purchase/", subscriptionHandler.ProcessPurchase)
|
||||
subscription.POST("/cancel/", subscriptionHandler.CancelSubscription)
|
||||
subscription.POST("/restore/", subscriptionHandler.RestoreSubscription)
|
||||
subscription.POST("/checkout/", subscriptionHandler.CreateCheckoutSession)
|
||||
subscription.POST("/portal/", subscriptionHandler.CreatePortalSession)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +511,7 @@ func setupWebhookRoutes(api *echo.Group, webhookHandler *handlers.SubscriptionWe
|
||||
{
|
||||
webhooks.POST("/apple/", webhookHandler.HandleAppleWebhook)
|
||||
webhooks.POST("/google/", webhookHandler.HandleGoogleWebhook)
|
||||
webhooks.POST("/stripe/", webhookHandler.HandleStripeWebhook)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user