Improve public pages
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user