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

@@ -0,0 +1,152 @@
"""
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