Add subcription feature

This commit is contained in:
Urtzi Alfaro
2026-01-13 22:22:38 +01:00
parent b931a5c45e
commit 6ddf608d37
61 changed files with 7915 additions and 1238 deletions

View File

@@ -420,6 +420,207 @@ class TenantServiceClient(BaseServiceClient):
logger.error("Tenant service health check failed", error=str(e))
return False
# ================================================================
# PAYMENT CUSTOMER MANAGEMENT
# ================================================================
async def create_payment_customer(
self,
user_data: Dict[str, Any],
payment_method_id: Optional[str] = None
) -> Optional[Dict[str, Any]]:
"""
Create a payment customer for a user
This method creates a payment customer record in the tenant service
during user registration or onboarding. It handles the integration
with payment providers and returns the payment customer details.
Args:
user_data: User data including:
- user_id: User ID (required)
- email: User email (required)
- full_name: User full name (required)
- name: User name (optional, defaults to full_name)
payment_method_id: Optional payment method ID to attach to the customer
Returns:
Dict with payment customer details including:
- success: boolean
- payment_customer_id: string
- payment_method: dict with payment method details
- customer: dict with customer details
Returns None if creation fails
"""
try:
logger.info("Creating payment customer via tenant service",
user_id=user_data.get('user_id'),
email=user_data.get('email'))
# Prepare data for tenant service
tenant_data = {
"user_data": user_data,
"payment_method_id": payment_method_id
}
# Call tenant service endpoint
result = await self.post("/payment-customers/create", tenant_data)
if result and result.get("success"):
logger.info("Payment customer created successfully via tenant service",
user_id=user_data.get('user_id'),
payment_customer_id=result.get('payment_customer_id'))
return result
else:
logger.error("Payment customer creation failed via tenant service",
user_id=user_data.get('user_id'),
error=result.get('detail') if result else 'No detail provided')
return None
except Exception as e:
logger.error("Failed to create payment customer via tenant service",
user_id=user_data.get('user_id'),
error=str(e))
return None
async def create_subscription_for_registration(
self,
user_data: Dict[str, Any],
plan_id: str,
payment_method_id: str,
billing_cycle: str = "monthly",
coupon_code: Optional[str] = None
) -> Optional[Dict[str, Any]]:
"""
Create a tenant-independent subscription during user registration
This method creates a subscription that is not linked to any tenant yet.
The subscription will be linked to a tenant during the onboarding flow
when the user creates their bakery/tenant.
Args:
user_data: User data including:
- user_id: User ID (required)
- email: User email (required)
- full_name: User full name (required)
- name: User name (optional, defaults to full_name)
plan_id: Subscription plan ID (starter, professional, enterprise)
payment_method_id: Stripe payment method ID
billing_cycle: Billing cycle (monthly or yearly), defaults to monthly
coupon_code: Optional coupon code for discounts/trials
Returns:
Dict with subscription creation results including:
- success: boolean
- subscription_id: string (Stripe subscription ID)
- customer_id: string (Stripe customer ID)
- status: string (subscription status)
- plan: string (plan name)
- billing_cycle: string (billing interval)
- trial_period_days: int (if trial applied)
- coupon_applied: boolean
Returns None if creation fails
"""
try:
logger.info("Creating tenant-independent subscription for registration",
user_id=user_data.get('user_id'),
plan_id=plan_id,
billing_cycle=billing_cycle)
# Prepare data for tenant service
subscription_data = {
"user_data": user_data,
"plan_id": plan_id,
"payment_method_id": payment_method_id,
"billing_interval": billing_cycle,
"coupon_code": coupon_code
}
# Call tenant service endpoint
result = await self.post("/subscriptions/create-for-registration", subscription_data)
if result and result.get("success"):
data = result.get("data", {})
logger.info("Tenant-independent subscription created successfully",
user_id=user_data.get('user_id'),
subscription_id=data.get('subscription_id'),
plan=data.get('plan'))
return data
else:
logger.error("Subscription creation failed via tenant service",
user_id=user_data.get('user_id'),
error=result.get('detail') if result else 'No detail provided')
return None
except Exception as e:
logger.error("Failed to create subscription for registration via tenant service",
user_id=user_data.get('user_id'),
plan_id=plan_id,
error=str(e))
return None
async def link_subscription_to_tenant(
self,
tenant_id: str,
subscription_id: str,
user_id: str
) -> Optional[Dict[str, Any]]:
"""
Link a pending subscription to a tenant
This completes the registration flow by associating the subscription
created during registration with the tenant created during onboarding.
Args:
tenant_id: Tenant ID to link subscription to
subscription_id: Subscription ID (from registration)
user_id: User ID performing the linking (for validation)
Returns:
Dict with linking results:
- success: boolean
- tenant_id: string
- subscription_id: string
- status: string
Returns None if linking fails
"""
try:
logger.info("Linking subscription to tenant",
tenant_id=tenant_id,
subscription_id=subscription_id,
user_id=user_id)
# Prepare data for tenant service
linking_data = {
"subscription_id": subscription_id,
"user_id": user_id
}
# Call tenant service endpoint
result = await self.post(
f"/tenants/{tenant_id}/link-subscription",
linking_data
)
if result and result.get("success"):
logger.info("Subscription linked to tenant successfully",
tenant_id=tenant_id,
subscription_id=subscription_id)
return result
else:
logger.error("Subscription linking failed via tenant service",
tenant_id=tenant_id,
subscription_id=subscription_id,
error=result.get('detail') if result else 'No detail provided')
return None
except Exception as e:
logger.error("Failed to link subscription to tenant via tenant service",
tenant_id=tenant_id,
subscription_id=subscription_id,
error=str(e))
return None
# Factory function for dependency injection
def create_tenant_client(config: BaseServiceSettings) -> TenantServiceClient: