153 lines
6.1 KiB
Python
153 lines
6.1 KiB
Python
"""
|
|
Payment Service for handling subscription payments
|
|
This service abstracts payment provider interactions and makes the system payment-agnostic
|
|
"""
|
|
|
|
import structlog
|
|
from typing import Dict, Any, Optional
|
|
import uuid
|
|
|
|
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.models.tenants import Subscription as SubscriptionModel
|
|
|
|
logger = structlog.get_logger()
|
|
|
|
|
|
class PaymentService:
|
|
"""Service for handling payment provider interactions"""
|
|
|
|
def __init__(self):
|
|
# 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
|
|
|
|
async def create_customer(self, user_data: Dict[str, Any]) -> PaymentCustomer:
|
|
"""Create a customer in the payment provider system"""
|
|
try:
|
|
customer_data = {
|
|
'email': user_data.get('email'),
|
|
'name': user_data.get('full_name'),
|
|
'metadata': {
|
|
'user_id': user_data.get('user_id'),
|
|
'tenant_id': user_data.get('tenant_id')
|
|
}
|
|
}
|
|
|
|
return await self.payment_provider.create_customer(customer_data)
|
|
except Exception as e:
|
|
logger.error("Failed to create customer in payment provider", error=str(e))
|
|
raise e
|
|
|
|
async def create_subscription(
|
|
self,
|
|
customer_id: str,
|
|
plan_id: str,
|
|
payment_method_id: str,
|
|
trial_period_days: Optional[int] = None
|
|
) -> Subscription:
|
|
"""Create a subscription for a customer"""
|
|
try:
|
|
return await self.payment_provider.create_subscription(
|
|
customer_id,
|
|
plan_id,
|
|
payment_method_id,
|
|
trial_period_days
|
|
)
|
|
except Exception as e:
|
|
logger.error("Failed to create subscription in payment provider", error=str(e))
|
|
raise e
|
|
|
|
async def process_registration_with_subscription(
|
|
self,
|
|
user_data: Dict[str, Any],
|
|
plan_id: str,
|
|
payment_method_id: str,
|
|
use_trial: bool = False
|
|
) -> 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
|
|
|
|
# Create subscription
|
|
subscription = await self.create_subscription(
|
|
customer.id,
|
|
plan_id,
|
|
payment_method_id,
|
|
trial_period_days
|
|
)
|
|
|
|
# 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({
|
|
'id': str(uuid.uuid4()),
|
|
'tenant_id': user_data.get('tenant_id'),
|
|
'customer_id': customer.id,
|
|
'subscription_id': subscription.id,
|
|
'plan_id': plan_id,
|
|
'status': subscription.status,
|
|
'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 {
|
|
'customer_id': customer.id,
|
|
'subscription_id': subscription.id,
|
|
'status': subscription.status,
|
|
'trial_period_days': trial_period_days
|
|
}
|
|
except Exception as e:
|
|
logger.error("Failed to process registration with subscription", error=str(e))
|
|
raise e
|
|
|
|
async def cancel_subscription(self, subscription_id: str) -> Subscription:
|
|
"""Cancel a subscription in the payment provider"""
|
|
try:
|
|
return await self.payment_provider.cancel_subscription(subscription_id)
|
|
except Exception as e:
|
|
logger.error("Failed to cancel subscription in payment provider", error=str(e))
|
|
raise e
|
|
|
|
async def update_payment_method(self, customer_id: str, payment_method_id: str) -> PaymentMethod:
|
|
"""Update the payment method for a customer"""
|
|
try:
|
|
return await self.payment_provider.update_payment_method(customer_id, payment_method_id)
|
|
except Exception as e:
|
|
logger.error("Failed to update payment method in payment provider", error=str(e))
|
|
raise e
|
|
|
|
async def get_invoices(self, customer_id: str) -> list:
|
|
"""Get invoices for a customer from the payment provider"""
|
|
try:
|
|
return await self.payment_provider.get_invoices(customer_id)
|
|
except Exception as e:
|
|
logger.error("Failed to get invoices from payment provider", error=str(e))
|
|
raise e
|
|
|
|
async def get_subscription(self, subscription_id: str) -> Subscription:
|
|
"""Get subscription details from the payment provider"""
|
|
try:
|
|
return await self.payment_provider.get_subscription(subscription_id)
|
|
except Exception as e:
|
|
logger.error("Failed to get subscription from payment provider", error=str(e))
|
|
raise e
|