Improve public pages

This commit is contained in:
Urtzi Alfaro
2025-10-17 18:14:28 +02:00
parent d4060962e4
commit 7e089b80cf
46 changed files with 5734 additions and 1084 deletions

View File

@@ -6,12 +6,14 @@ This service abstracts payment provider interactions and makes the system paymen
import structlog
from typing import Dict, Any, Optional
import uuid
from sqlalchemy.orm import Session
from app.core.config import settings
from shared.clients.payment_client import PaymentProvider, PaymentCustomer, Subscription, PaymentMethod
from shared.clients.stripe_client import StripeProvider
from shared.database.base import create_database_manager
from app.repositories.subscription_repository import SubscriptionRepository
from app.repositories.coupon_repository import CouponRepository
from app.models.tenants import Subscription as SubscriptionModel
logger = structlog.get_logger()
@@ -19,18 +21,19 @@ logger = structlog.get_logger()
class PaymentService:
"""Service for handling payment provider interactions"""
def __init__(self):
def __init__(self, db_session: Optional[Session] = None):
# Initialize payment provider based on configuration
# For now, we'll use Stripe, but this can be swapped for other providers
self.payment_provider: PaymentProvider = StripeProvider(
api_key=settings.STRIPE_SECRET_KEY,
webhook_secret=settings.STRIPE_WEBHOOK_SECRET
)
# Initialize database components
self.database_manager = create_database_manager(settings.DATABASE_URL, "tenant-service")
self.subscription_repo = SubscriptionRepository(SubscriptionModel, None) # Will be set in methods
self.db_session = db_session # Optional session for coupon operations
async def create_customer(self, user_data: Dict[str, Any]) -> PaymentCustomer:
"""Create a customer in the payment provider system"""
@@ -68,35 +71,121 @@ class PaymentService:
logger.error("Failed to create subscription in payment provider", error=str(e))
raise e
def validate_coupon_code(
self,
coupon_code: str,
tenant_id: str,
db_session: Session
) -> Dict[str, Any]:
"""
Validate a coupon code for a tenant.
Returns validation result with discount preview.
"""
try:
coupon_repo = CouponRepository(db_session)
validation = coupon_repo.validate_coupon(coupon_code, tenant_id)
return {
"valid": validation.valid,
"error_message": validation.error_message,
"discount_preview": validation.discount_preview,
"coupon": {
"code": validation.coupon.code,
"discount_type": validation.coupon.discount_type.value,
"discount_value": validation.coupon.discount_value
} if validation.coupon else None
}
except Exception as e:
logger.error("Failed to validate coupon", error=str(e), coupon_code=coupon_code)
return {
"valid": False,
"error_message": "Error al validar el cupón",
"discount_preview": None,
"coupon": None
}
def redeem_coupon(
self,
coupon_code: str,
tenant_id: str,
db_session: Session,
base_trial_days: int = 14
) -> tuple[bool, Optional[Dict[str, Any]], Optional[str]]:
"""
Redeem a coupon for a tenant.
Returns (success, discount_applied, error_message)
"""
try:
coupon_repo = CouponRepository(db_session)
success, redemption, error = coupon_repo.redeem_coupon(
coupon_code,
tenant_id,
base_trial_days
)
if success and redemption:
return True, redemption.discount_applied, None
else:
return False, None, error
except Exception as e:
logger.error("Failed to redeem coupon", error=str(e), coupon_code=coupon_code)
return False, None, f"Error al aplicar el cupón: {str(e)}"
async def process_registration_with_subscription(
self,
user_data: Dict[str, Any],
plan_id: str,
self,
user_data: Dict[str, Any],
plan_id: str,
payment_method_id: str,
use_trial: bool = False
use_trial: bool = False,
coupon_code: Optional[str] = None,
db_session: Optional[Session] = None
) -> Dict[str, Any]:
"""Process user registration with subscription creation"""
try:
# Create customer in payment provider
customer = await self.create_customer(user_data)
# Determine trial period
trial_period_days = None
if use_trial:
trial_period_days = 90 # 3 months trial for pilot users
# Determine trial period (default 14 days)
trial_period_days = 14 if use_trial else 0
# Apply coupon if provided
coupon_discount = None
if coupon_code and db_session:
# Redeem coupon
success, discount, error = self.redeem_coupon(
coupon_code,
user_data.get('tenant_id'),
db_session,
trial_period_days
)
if success and discount:
coupon_discount = discount
# Update trial period if coupon extends it
if discount.get("type") == "trial_extension":
trial_period_days = discount.get("total_trial_days", trial_period_days)
logger.info(
"Coupon applied successfully",
coupon_code=coupon_code,
extended_trial_days=trial_period_days
)
else:
logger.warning("Failed to apply coupon", error=error, coupon_code=coupon_code)
# Create subscription
subscription = await self.create_subscription(
customer.id,
plan_id,
payment_method_id,
trial_period_days
trial_period_days if trial_period_days > 0 else None
)
# Save subscription to database
async with self.database_manager.get_session() as session:
self.subscription_repo.session = session
subscription_record = await self.subscription_repo.create({
subscription_data = {
'id': str(uuid.uuid4()),
'tenant_id': user_data.get('tenant_id'),
'customer_id': customer.id,
@@ -106,15 +195,26 @@ class PaymentService:
'current_period_start': subscription.current_period_start,
'current_period_end': subscription.current_period_end,
'created_at': subscription.created_at,
'trial_period_days': trial_period_days
})
return {
'trial_period_days': trial_period_days if trial_period_days > 0 else None
}
subscription_record = await self.subscription_repo.create(subscription_data)
result = {
'customer_id': customer.id,
'subscription_id': subscription.id,
'status': subscription.status,
'trial_period_days': trial_period_days
}
# Include coupon info if applied
if coupon_discount:
result['coupon_applied'] = {
'code': coupon_code,
'discount': coupon_discount
}
return result
except Exception as e:
logger.error("Failed to process registration with subscription", error=str(e))
raise e