Add subcription feature 3
This commit is contained in:
@@ -211,7 +211,8 @@ async def clone_demo_data(
|
||||
subscription_data.get('cancellation_effective_date'),
|
||||
session_time,
|
||||
"cancellation_effective_date"
|
||||
)
|
||||
),
|
||||
is_tenant_linked=True # Required for check constraint when tenant_id is set
|
||||
)
|
||||
|
||||
db.add(subscription)
|
||||
@@ -245,7 +246,8 @@ async def clone_demo_data(
|
||||
"api_access": True,
|
||||
"priority_support": True
|
||||
},
|
||||
next_billing_date=datetime.now(timezone.utc) + timedelta(days=90)
|
||||
next_billing_date=datetime.now(timezone.utc) + timedelta(days=90),
|
||||
is_tenant_linked=True # Required for check constraint when tenant_id is set
|
||||
)
|
||||
|
||||
db.add(subscription)
|
||||
@@ -323,7 +325,8 @@ async def clone_demo_data(
|
||||
max_users=-1, # Unlimited for demo
|
||||
max_locations=max_locations,
|
||||
max_products=-1, # Unlimited for demo
|
||||
features={}
|
||||
features={},
|
||||
is_tenant_linked=True # Required for check constraint when tenant_id is set
|
||||
)
|
||||
db.add(demo_subscription)
|
||||
|
||||
@@ -699,7 +702,8 @@ async def create_child_outlet(
|
||||
max_users=10, # Demo limits
|
||||
max_locations=1, # Single location for outlet
|
||||
max_products=200,
|
||||
features={}
|
||||
features={},
|
||||
is_tenant_linked=True # Required for check constraint when tenant_id is set
|
||||
)
|
||||
db.add(child_subscription)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1122,6 +1122,88 @@ async def upgrade_subscription_plan(
|
||||
detail="Failed to upgrade subscription plan"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# REGISTRATION ORCHESTRATION (SECURE 3DS FLOW)
|
||||
# ============================================================================
|
||||
|
||||
@router.post("/registration-payment-setup")
|
||||
async def registration_payment_setup(
|
||||
user_data: Dict[str, Any],
|
||||
payment_service: PaymentService = Depends(get_payment_service)
|
||||
):
|
||||
"""
|
||||
Orchestrate initial payment setup for a new registration.
|
||||
This creates the customer and SetupIntent for 3DS verification.
|
||||
"""
|
||||
try:
|
||||
logger.info("Orchestrating registration payment setup",
|
||||
email=user_data.get('email'))
|
||||
|
||||
result = await payment_service.complete_registration_payment_flow(user_data)
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error("Registration payment setup orchestration failed",
|
||||
email=user_data.get('email'),
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Registration payment setup failed: {str(e)}"
|
||||
)
|
||||
|
||||
@router.post("/verify-and-complete-registration")
|
||||
async def verify_and_complete_registration(
|
||||
registration_data: Dict[str, Any],
|
||||
payment_service: PaymentService = Depends(get_payment_service),
|
||||
tenant_service: EnhancedTenantService = Depends(get_enhanced_tenant_service)
|
||||
):
|
||||
"""
|
||||
Final step: Verify SetupIntent and create subscription + tenant.
|
||||
Called after frontend confirms 3DS with Stripe.
|
||||
"""
|
||||
try:
|
||||
setup_intent_id = registration_data.get('setup_intent_id')
|
||||
user_data = registration_data.get('user_data', {})
|
||||
|
||||
logger.info("Verifying and completing registration",
|
||||
email=user_data.get('email'),
|
||||
setup_intent_id=setup_intent_id)
|
||||
|
||||
# 1. Complete subscription creation after verification
|
||||
payment_result = await payment_service.complete_subscription_after_verification(
|
||||
setup_intent_id, user_data
|
||||
)
|
||||
|
||||
# 2. Create the bakery/tenant record
|
||||
# Note: In a real flow, we'd use the payment result (customer_id, subscription_id)
|
||||
# to properly link the new tenant.
|
||||
bakery_registration = BakeryRegistration(
|
||||
name=user_data.get('name', f"{user_data.get('full_name')}'s Bakery"),
|
||||
subdomain=user_data.get('subdomain', user_data.get('email').split('@')[0]),
|
||||
business_type=user_data.get('business_type', 'bakery'),
|
||||
link_existing_subscription=True,
|
||||
subscription_id=payment_result['subscription_id']
|
||||
)
|
||||
|
||||
# We need the user_id from the auth service call
|
||||
user_id = user_data.get('user_id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="Missing user_id in registration data")
|
||||
|
||||
tenant_result = await tenant_service.create_bakery(bakery_registration, user_id)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"tenant": tenant_result,
|
||||
"payment": payment_result
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error("Verify and complete registration failed",
|
||||
error=str(e))
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Registration completion failed: {str(e)}"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# PAYMENT OPERATIONS
|
||||
# ============================================================================
|
||||
@@ -1244,7 +1326,7 @@ async def register_with_subscription(
|
||||
**result
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error("Failed to register with subscription", error=str(e))
|
||||
logger.error(f"Failed to register with subscription: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to register with subscription"
|
||||
|
||||
@@ -88,7 +88,10 @@ async def stripe_webhook(
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error("Error processing Stripe webhook", error=str(e), exc_info=True)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Webhook processing error"
|
||||
)
|
||||
# Return 200 OK even on processing errors to prevent Stripe retries
|
||||
# Only return 4xx for signature verification failures
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Webhook processing error",
|
||||
"details": str(e)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user