diff --git a/backend/app/routes/focus_group_ai.py b/backend/app/routes/focus_group_ai.py index de19dec6..91576851 100755 --- a/backend/app/routes/focus_group_ai.py +++ b/backend/app/routes/focus_group_ai.py @@ -35,6 +35,7 @@ from app.models.credit_transaction import CreditTransaction from app.models.app_settings import get_settings from app.utils.rate_limiter import rate_limit from app.utils import active_required, with_user_context +from app.db import get_db # Create the blueprint focus_group_ai_bp = Blueprint('focus_group_ai', __name__) @@ -765,26 +766,43 @@ async def start_autonomous_conversation(focus_group_id): run_user_id = get_jwt_identity() settings = await get_settings() run_cost = settings.get("run_cost", 40) - user_data = await User.find_by_id(run_user_id) - balance = (user_data or {}).get("credits_balance", 0) - if balance < run_cost: - return jsonify({ - "error": "Insufficient credits", - "message": f"You need {run_cost} credits to run a focus group session. Current balance: {balance}.", - "credits_required": run_cost, - "credits_balance": balance, - }), 402 - new_balance = await User.deduct_credits(run_user_id, run_cost) - if new_balance is None: - return jsonify({"error": "Insufficient credits", "message": "Credit balance changed. Please try again."}), 402 - await CreditTransaction.record( - user_id=run_user_id, - tx_type="debit", - amount=-run_cost, - balance_after=new_balance, - description=f"Focus group session run", - ref={"focus_group_id": focus_group_id}, - ) + + # Skip charge if this focus group was already charged within the last 4 hours + # (handles server restarts and error-triggered restarts without double-billing) + from datetime import datetime, timezone, timedelta + db = await get_db() + recent_cutoff = datetime.now(timezone.utc) - timedelta(hours=4) + recent_charge = await db.credit_transactions.find_one({ + "user_id": run_user_id, + "ref.focus_group_id": focus_group_id, + "description": "Focus group session run", + "ts": {"$gte": recent_cutoff}, + }) + + if recent_charge is None: + user_data = await User.find_by_id(run_user_id) + balance = (user_data or {}).get("credits_balance", 0) + if balance < run_cost: + return jsonify({ + "error": "Insufficient credits", + "message": f"You need {run_cost} credits to run a focus group session. Current balance: {balance}.", + "credits_required": run_cost, + "credits_balance": balance, + }), 402 + new_balance = await User.deduct_credits(run_user_id, run_cost) + if new_balance is None: + return jsonify({"error": "Insufficient credits", "message": "Credit balance changed. Please try again."}), 402 + await CreditTransaction.record( + user_id=run_user_id, + tx_type="debit", + amount=-run_cost, + balance_after=new_balance, + description="Focus group session run", + ref={"focus_group_id": focus_group_id}, + ) + current_app.logger.info(f"Charged {run_cost} credits for focus group {focus_group_id}") + else: + current_app.logger.info(f"Focus group {focus_group_id} already charged within 4h window — skipping deduction") # Create autonomous conversation controller current_app.logger.info("Creating AutonomousConversationController...") diff --git a/src/pages/FocusGroupSession.tsx b/src/pages/FocusGroupSession.tsx index bbc1f0de..89c15a8c 100755 --- a/src/pages/FocusGroupSession.tsx +++ b/src/pages/FocusGroupSession.tsx @@ -1762,13 +1762,13 @@ const FocusGroupSession = () => { // Show loading state while fetching if (isLoading) { return ( -
Loading focus group...
+Loading focus group...
We couldn't find the focus group you're looking for.
+We couldn't find the focus group you're looking for.