Support subcription payments

This commit is contained in:
Urtzi Alfaro
2025-09-25 14:30:47 +02:00
parent f02a980c87
commit 89b75bd7af
22 changed files with 2119 additions and 364 deletions

View File

@@ -8,6 +8,7 @@ from typing import List, Dict, Any, Optional
from uuid import UUID
from app.services.subscription_limit_service import SubscriptionLimitService
from app.services.payment_service import PaymentService
from app.repositories import SubscriptionRepository
from app.models.tenants import Subscription
from shared.auth.decorators import get_current_user_dep, require_admin_role_dep
@@ -27,6 +28,13 @@ def get_subscription_limit_service():
logger.error("Failed to create subscription limit service", error=str(e))
raise HTTPException(status_code=500, detail="Service initialization failed")
def get_payment_service():
try:
return PaymentService()
except Exception as e:
logger.error("Failed to create payment service", error=str(e))
raise HTTPException(status_code=500, detail="Payment service initialization failed")
def get_subscription_repository():
try:
from app.core.config import settings
@@ -182,7 +190,7 @@ async def validate_plan_upgrade(
"""Validate if tenant can upgrade to a new plan"""
try:
# TODO: Add access control - verify user has admin access to tenant
# TODO: Add access control - verify user is owner/admin of tenant
result = await limit_service.validate_plan_upgrade(str(tenant_id), new_plan)
return result
@@ -241,9 +249,9 @@ async def upgrade_subscription_plan(
detail="Failed to upgrade subscription plan"
)
@router.get("/plans/available")
@router.get("/plans")
async def get_available_plans():
"""Get all available subscription plans with features and pricing"""
"""Get all available subscription plans with features and pricing - Public endpoint"""
try:
# This could be moved to a config service or database
@@ -294,7 +302,7 @@ async def get_available_plans():
"description": "Ideal para cadenas con obradores centrales",
"monthly_price": 399.0,
"max_users": -1, # Unlimited
"max_locations": -1, # Unlimited
"max_locations": -1, # Unlimited
"max_products": -1, # Unlimited
"features": {
"inventory_management": "multi_location",
@@ -321,4 +329,93 @@ async def get_available_plans():
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get available plans"
)
)
# New endpoints for payment processing during registration
@router.post("/subscriptions/register-with-subscription")
async def register_with_subscription(
user_data: Dict[str, Any],
plan_id: str = Query(..., description="Plan ID to subscribe to"),
payment_method_id: str = Query(..., description="Payment method ID from frontend"),
use_trial: bool = Query(False, description="Whether to use trial period for pilot users"),
payment_service: PaymentService = Depends(get_payment_service)
):
"""Process user registration with subscription creation"""
try:
result = await payment_service.process_registration_with_subscription(
user_data,
plan_id,
payment_method_id,
use_trial
)
return {
"success": True,
"message": "Registration and subscription created successfully",
"data": result
}
except Exception as e:
logger.error("Failed to register with subscription", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to register with subscription"
)
@router.post("/subscriptions/{tenant_id}/cancel")
async def cancel_subscription(
tenant_id: UUID = Path(..., description="Tenant ID"),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
payment_service: PaymentService = Depends(get_payment_service)
):
"""Cancel subscription for a tenant"""
try:
# TODO: Add access control - verify user is owner/admin of tenant
# In a real implementation, you would need to retrieve the subscription ID from the database
# For now, this is a placeholder
subscription_id = "sub_test" # This would come from the database
result = await payment_service.cancel_subscription(subscription_id)
return {
"success": True,
"message": "Subscription cancelled successfully",
"data": {
"subscription_id": result.id,
"status": result.status
}
}
except Exception as e:
logger.error("Failed to cancel subscription", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to cancel subscription"
)
@router.get("/subscriptions/{tenant_id}/invoices")
async def get_invoices(
tenant_id: UUID = Path(..., description="Tenant ID"),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
payment_service: PaymentService = Depends(get_payment_service)
):
"""Get invoices for a tenant"""
try:
# TODO: Add access control - verify user has access to tenant
# In a real implementation, you would need to retrieve the customer ID from the database
# For now, this is a placeholder
customer_id = "cus_test" # This would come from the database
invoices = await payment_service.get_invoices(customer_id)
return {
"success": True,
"data": invoices
}
except Exception as e:
logger.error("Failed to get invoices", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get invoices"
)