Add subcription feature 8

This commit is contained in:
Urtzi Alfaro
2026-01-16 16:09:32 +01:00
parent 5e01b34cc0
commit fa7b62bd6c
2 changed files with 115 additions and 47 deletions

View File

@@ -130,20 +130,37 @@ class AuthService:
) -> Dict[str, Any]:
"""
Complete registration after successful payment verification.
NEW ARCHITECTURE: This calls tenant service to create subscription
AFTER SetupIntent verification. No subscription exists until this point.
This is the CORE method that creates the subscription and user.
It is called by complete_registration_with_verified_payment in both scenarios:
1. After 3DS authentication (requires_action=True flow)
2. Immediately after start-registration (requires_action=False flow)
CRITICAL: This is the FIRST and ONLY place where:
- Subscription is created via tenant service
- User record is created in auth database
- Onboarding progress is saved
- Auth tokens are generated
This ensures that subscriptions are only created after SetupIntent verification,
preventing duplicate subscriptions and maintaining payment security.
Args:
setup_intent_id: Verified SetupIntent ID
setup_intent_id: Verified SetupIntent ID (must not be None)
user_data: User registration data
payment_setup_result: Optional payment setup result with customer_id etc.
Returns:
Complete registration result
Complete registration result with:
- user: Created user data
- subscription_id: Created subscription ID
- payment_customer_id: Stripe customer ID
- status: Subscription status
- access_token: JWT access token
- refresh_token: JWT refresh token
Raises:
RegistrationError: If registration completion fails
RegistrationError: If registration completion fails (e.g., missing setup_intent_id)
"""
try:
logger.info(f"Completing registration after verification, email={user_data.email}, setup_intent_id={setup_intent_id}")
@@ -250,13 +267,28 @@ class AuthService:
) -> Dict[str, Any]:
"""
Start secure registration flow with SetupIntent-first approach
Main entry point for new registration architecture
This is the FIRST step in the atomic registration flow:
1. Creates Stripe customer
2. Creates SetupIntent with confirm=True
3. Returns SetupIntent data to frontend
IMPORTANT: NO subscription is created in this step!
Subscription creation happens in complete_registration_after_payment_verification
Two possible outcomes:
- requires_action=True: 3DS required, frontend must confirm SetupIntent
- requires_action=False: No 3DS required, but frontend STILL must call complete-registration
Args:
user_data: User registration data
Returns:
Registration flow result (may require 3DS)
Registration flow result with SetupIntent data
- requires_action: True if 3DS required, False if not
- setup_intent_id: SetupIntent ID for verification
- client_secret: For 3DS authentication (when requires_action=True)
- Other SetupIntent metadata
Raises:
RegistrationError: If registration flow fails
@@ -292,23 +324,20 @@ class AuthService:
# No 3DS required - SetupIntent already succeeded
logger.info(f"Registration SetupIntent succeeded without 3DS, email={user_data.email}, setup_intent_id={payment_setup_result.get('setup_intent_id')}")
# Complete registration - create subscription now
setup_intent_id = payment_setup_result.get('setup_intent_id')
registration_result = await self.complete_registration_after_payment_verification(
setup_intent_id,
user_data,
payment_setup_result
)
# Note: NO subscription created yet - frontend must call complete-registration
# This ensures consistent flow whether 3DS is required or not
return {
'requires_action': False,
'user': registration_result.get('user'),
'subscription_id': registration_result.get('subscription_id'),
'payment_customer_id': registration_result.get('payment_customer_id'),
'status': registration_result.get('status'),
'access_token': registration_result.get('access_token'),
'refresh_token': registration_result.get('refresh_token'),
'message': 'Registration completed successfully'
'setup_intent_id': payment_setup_result.get('setup_intent_id'),
'customer_id': payment_setup_result.get('customer_id'),
'payment_customer_id': payment_setup_result.get('payment_customer_id'),
'plan_id': payment_setup_result.get('plan_id'),
'payment_method_id': payment_setup_result.get('payment_method_id'),
'billing_cycle': payment_setup_result.get('billing_cycle'),
'trial_period_days': payment_setup_result.get('trial_period_days', 0),
'coupon_code': payment_setup_result.get('coupon_code'),
'email': payment_setup_result.get('email'),
'message': 'SetupIntent succeeded without 3DS. Frontend must call complete-registration to create subscription.'
}
except Exception as e:
@@ -321,15 +350,29 @@ class AuthService:
user_data: UserRegistration
) -> Dict[str, Any]:
"""
Complete registration after frontend confirms SetupIntent (3DS handled)
This is called by frontend after user completes 3DS authentication
Complete registration after frontend confirms SetupIntent
This is the SECOND step in the atomic registration flow and is called in TWO scenarios:
1. After user completes 3DS authentication (when requires_action=True)
2. Immediately after start-registration (when requires_action=False)
In BOTH cases, this method:
- Verifies the SetupIntent status with Stripe
- Calls tenant service to create the subscription (first time subscription is created)
- Creates the user record in auth database
- Saves onboarding progress
- Generates auth tokens for auto-login
This ensures a CONSISTENT flow whether 3DS is required or not:
Step 1: start-registration creates SetupIntent only (NO subscription yet)
Step 2: complete-registration verifies SetupIntent and creates subscription
Args:
setup_intent_id: SetupIntent ID that was confirmed
setup_intent_id: SetupIntent ID that was confirmed (either by Stripe or frontend)
user_data: User registration data
Returns:
Complete registration result
Complete registration result with user data, tokens, and subscription info
Raises:
RegistrationError: If completion fails