config issues prod
This commit is contained in:
@@ -4,10 +4,13 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import Tuple, Optional
|
from typing import Tuple, Optional
|
||||||
import enum
|
import enum
|
||||||
|
import logging
|
||||||
|
|
||||||
from .. import crud, models, schemas, database
|
from .. import crud, models, schemas, database
|
||||||
from ..services import payment_service
|
from ..services import payment_service
|
||||||
import logging
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AuthNetResponse = object
|
AuthNetResponse = object
|
||||||
@@ -161,6 +164,80 @@ def add_card_to_customer(customer_id: int, card_info: schemas.CardCreate, db: Se
|
|||||||
# This will catch any other unexpected errors, like from the database
|
# This will catch any other unexpected errors, like from the database
|
||||||
raise HTTPException(status_code=500, detail="An internal server error occurred.")
|
raise HTTPException(status_code=500, detail="An internal server error occurred.")
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/customers/{customer_id}/cards/{card_id}", summary="Update an existing payment card for a customer")
|
||||||
|
def update_card_for_customer(customer_id: int, card_id: int, card_info: schemas.CardCreate, db: Session = Depends(database.get_db)):
|
||||||
|
"""
|
||||||
|
Updates an existing credit card for a customer.
|
||||||
|
- If the card has an existing Authorize.Net payment profile, deletes it first.
|
||||||
|
- Creates a new payment profile with updated card information.
|
||||||
|
- Updates the database card record with the new payment_profile_id.
|
||||||
|
Returns the new payment_profile_id from Authorize.Net.
|
||||||
|
"""
|
||||||
|
db_customer = crud.get_customer(db, customer_id=customer_id)
|
||||||
|
if not db_customer:
|
||||||
|
raise HTTPException(status_code=404, detail="Customer not found")
|
||||||
|
|
||||||
|
db_card = crud.get_card_by_id(db, card_id=card_id)
|
||||||
|
if not db_card or db_card.user_id != customer_id:
|
||||||
|
raise HTTPException(status_code=404, detail="Card not found for this customer")
|
||||||
|
|
||||||
|
if not db_customer.auth_net_profile_id:
|
||||||
|
raise HTTPException(status_code=400, detail="Customer does not have an Authorize.Net profile")
|
||||||
|
|
||||||
|
customer_schema = schemas.Customer.from_orm(db_customer)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# If payment profile ID is null, refresh from Authorize.Net to sync
|
||||||
|
if not db_card.auth_net_payment_profile_id:
|
||||||
|
logger.info(f"Payment profile ID is null for card {card_id}, refreshing from Authorize.Net")
|
||||||
|
from ..services import user_create
|
||||||
|
user_create.refresh_customer_payment_profiles(db, customer_id, db_customer.auth_net_profile_id)
|
||||||
|
|
||||||
|
# Re-fetch the card to get the updated payment profile ID
|
||||||
|
db.refresh(db_card)
|
||||||
|
logger.info(f"After refresh, card {card_id} has payment_profile_id: {db_card.auth_net_payment_profile_id}")
|
||||||
|
|
||||||
|
# Delete existing payment profile if it exists
|
||||||
|
if db_card.auth_net_payment_profile_id:
|
||||||
|
from ..services import user_delete
|
||||||
|
delete_success = user_delete._delete_payment_profile(
|
||||||
|
db_customer.auth_net_profile_id, db_card.auth_net_payment_profile_id
|
||||||
|
)
|
||||||
|
if delete_success:
|
||||||
|
logger.info(f"Successfully deleted old payment profile {db_card.auth_net_payment_profile_id} for card {card_id}")
|
||||||
|
# Clear the payment profile ID since it was deleted
|
||||||
|
db_card.auth_net_payment_profile_id = None
|
||||||
|
db.add(db_card)
|
||||||
|
db.commit()
|
||||||
|
else:
|
||||||
|
logger.warning(f"Failed to delete old payment profile {db_card.auth_net_payment_profile_id} for card {card_id}")
|
||||||
|
|
||||||
|
# Create new payment profile with updated card information
|
||||||
|
new_payment_profile_id = payment_service.add_payment_profile_to_customer(
|
||||||
|
customer_profile_id=db_customer.auth_net_profile_id,
|
||||||
|
customer=customer_schema,
|
||||||
|
card_info=card_info
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update the database card with the new payment profile ID
|
||||||
|
db_card.auth_net_payment_profile_id = new_payment_profile_id
|
||||||
|
db.add(db_card)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
logger.info(f"Successfully updated card {card_id} with new payment profile {new_payment_profile_id}")
|
||||||
|
|
||||||
|
# Return the new payment_profile_id
|
||||||
|
return {"payment_profile_id": new_payment_profile_id}
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
db.rollback()
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
except Exception as e:
|
||||||
|
db.rollback()
|
||||||
|
print(f"Failed to update card {card_id}: {str(e)}")
|
||||||
|
raise HTTPException(status_code=500, detail="An internal server error occurred.")
|
||||||
|
|
||||||
@router.post("/charge/saved-card/{customer_id}", response_model=schemas.Transaction, summary="Charge a customer using a saved card")
|
@router.post("/charge/saved-card/{customer_id}", response_model=schemas.Transaction, summary="Charge a customer using a saved card")
|
||||||
def charge_saved_card(customer_id: int, transaction_req: schemas.TransactionCreateByCardID, db: Session = Depends(database.get_db)):
|
def charge_saved_card(customer_id: int, transaction_req: schemas.TransactionCreateByCardID, db: Session = Depends(database.get_db)):
|
||||||
db_customer = crud.get_customer(db, customer_id=customer_id)
|
db_customer = crud.get_customer(db, customer_id=customer_id)
|
||||||
@@ -200,46 +277,147 @@ def authorize_saved_card(customer_id: int, transaction_req: schemas.TransactionA
|
|||||||
Creates a pre-authorization on a customer's saved card.
|
Creates a pre-authorization on a customer's saved card.
|
||||||
This validates the card and reserves the funds.
|
This validates the card and reserves the funds.
|
||||||
"""
|
"""
|
||||||
|
print("🐛 DEBUG: ENTERING authorize_saved_card ROUTER FUNCTION")
|
||||||
|
|
||||||
db_customer = crud.get_customer(db, customer_id=customer_id)
|
db_customer = crud.get_customer(db, customer_id=customer_id)
|
||||||
db_card = crud.get_card_by_id(db, card_id=transaction_req.card_id)
|
db_card = crud.get_card_by_id(db, card_id=transaction_req.card_id)
|
||||||
|
|
||||||
if not db_customer or not db_card or db_card.user_id != customer_id:
|
if not db_customer or not db_card or db_card.user_id != customer_id:
|
||||||
raise HTTPException(status_code=404, detail="Customer or card not found for this account")
|
raise HTTPException(status_code=404, detail="Customer or card not found for this account")
|
||||||
|
|
||||||
# Add CRITICAL DEBUGGING to see exactly what's in the database
|
print("🐛 DEBUG: CUSTOMER AND CARD FOUND, STARTING PRE-VALIDATION")
|
||||||
print(f"ROUTER DEBUG: Customer ID: {customer_id}")
|
|
||||||
print(f"ROUTER DEBUG: db_customer: {db_customer}")
|
|
||||||
print(f"ROUTER DEBUG: db_customer.auth_net_profile_id: '{db_customer.auth_net_profile_id}' (type: {type(db_customer.auth_net_profile_id)})")
|
|
||||||
print(f"ROUTER DEBUG: db_card: {db_card}")
|
|
||||||
print(f"ROUTER DEBUG: db_card.auth_net_payment_profile_id: '{db_card.auth_net_payment_profile_id}' (type: {type(db_card.auth_net_payment_profile_id)})")
|
|
||||||
|
|
||||||
# Check for specific problem values
|
# 🚨 ENHANCED PRE-TRANSACTION VALIDATION 🚨
|
||||||
if db_card.auth_net_payment_profile_id is None:
|
# Proactively check and fix payment profile issues before attempting transaction
|
||||||
print("ROUTER DEBUG: CRITICAL - payment_profile_id is None - this will cause E00121!")
|
print(f"🐛 DEBUG: Starting enhanced pre-validation for customer {customer_id}, card {db_card.id}")
|
||||||
elif db_card.auth_net_payment_profile_id == "":
|
logger.info(f"🔍 PRE-TRANSACTION CHECK: Customer {customer_id}, Card {db_card.id}")
|
||||||
print("ROUTER DEBUG: CRITICAL - payment_profile_id is empty string - this will cause E00121!")
|
logger.info(f"🔍 Current auth_net_profile_id: '{db_customer.auth_net_profile_id}'")
|
||||||
elif str(db_card.auth_net_payment_profile_id).lower() == "none":
|
logger.info(f"🔍 Current payment_profile_id: '{db_card.auth_net_payment_profile_id}'")
|
||||||
print("ROUTER DEBUG: CRITICAL - payment_profile_id is string 'None' - this will cause E00121!")
|
|
||||||
|
# Check for missing payment profiles OR test validity of existing ones
|
||||||
|
needs_recovery = False
|
||||||
|
print(f"🐛 DEBUG: Checking payment profiles - customer_id: {db_customer.auth_net_profile_id}, card_id: {db_card.auth_net_payment_profile_id}")
|
||||||
|
|
||||||
|
if (not db_customer.auth_net_profile_id or
|
||||||
|
not db_card.auth_net_payment_profile_id or
|
||||||
|
db_card.auth_net_payment_profile_id.strip() == "" or
|
||||||
|
str(db_card.auth_net_payment_profile_id).lower() == "none" or
|
||||||
|
db_card.auth_net_payment_profile_id is None):
|
||||||
|
# Missing/null payment profile - needs recovery
|
||||||
|
needs_recovery = True
|
||||||
|
print("🐛 DEBUG: NULL/MISSING PAYMENT PROFILE DETECTED")
|
||||||
|
logger.warning(f"🔧 NULL/MISSING PAYMENT PROFILE DETECTED - Triggering auto-recovery")
|
||||||
else:
|
else:
|
||||||
print(f"ROUTER DEBUG: payment_profile_id appears valid: '{db_card.auth_net_payment_profile_id}'")
|
# Payment profile exists in DB, but let's test if it's valid in Authorize.net
|
||||||
|
print(f"🐛 DEBUG: Payment profile exists, testing validity in Authorize.net...")
|
||||||
|
logger.info(f"🔍 Payment profile exists, testing validity in Authorize.net...")
|
||||||
|
try:
|
||||||
|
# Quick test: try to retrieve customer payment profiles to see if our ID is valid
|
||||||
|
print(f"🐛 DEBUG: Calling get_customer_payment_profiles for profile_id: {db_customer.auth_net_profile_id}")
|
||||||
|
test_profiles = payment_service.get_customer_payment_profiles(db_customer.auth_net_profile_id)
|
||||||
|
print(f"🐛 DEBUG: Got profiles from Authorize.net: {test_profiles}")
|
||||||
|
current_id = str(db_card.auth_net_payment_profile_id)
|
||||||
|
profile_ids_as_strings = [str(pid) for pid in test_profiles if pid]
|
||||||
|
print(f"🐛 DEBUG: Checking if '{current_id}' is in: {profile_ids_as_strings}")
|
||||||
|
|
||||||
|
if current_id not in profile_ids_as_strings:
|
||||||
|
needs_recovery = True
|
||||||
|
print(f"🐛 DEBUG: PAYMENT PROFILE {current_id} NOT FOUND - NEEDS RECOVERY")
|
||||||
|
logger.warning(f"🔧 PAYMENT PROFILE {db_card.auth_net_payment_profile_id} NOT FOUND IN AUTHORIZE.NET - Invalid ID!")
|
||||||
|
logger.warning(f"🔧 Available profiles in Authorize.net: {test_profiles}")
|
||||||
|
else:
|
||||||
|
print(f"🐛 DEBUG: Payment profile {db_card.auth_net_payment_profile_id} exists in Authorize.net, testing usability...")
|
||||||
|
# Profile exists in Authorize.net, but test if it's actually USABLE
|
||||||
|
# by doing a quick test authorization with minimal amount
|
||||||
|
try:
|
||||||
|
print(f"🐛 DEBUG: Testing if payment profile is actually usable...")
|
||||||
|
# Create a tiny test transaction (like $0.01) to validate the card works
|
||||||
|
test_transaction_req = schemas.TransactionAuthorizeByCardID(
|
||||||
|
card_id=db_card.id,
|
||||||
|
preauthorize_amount="0.01" # Minimal test amount
|
||||||
|
)
|
||||||
|
|
||||||
|
test_response = payment_service.authorize_customer_profile(
|
||||||
|
customer_profile_id=db_customer.auth_net_profile_id,
|
||||||
|
payment_profile_id=db_card.auth_net_payment_profile_id,
|
||||||
|
transaction_req=test_transaction_req,
|
||||||
|
db_session=None, # Don't pass DB session for test transaction
|
||||||
|
customer_id=None,
|
||||||
|
card_id=None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if the test authorization worked
|
||||||
|
from ..services import payment_service as ps # Need access to _parse_authnet_response
|
||||||
|
test_status, _, test_reason = _parse_authnet_response(test_response)
|
||||||
|
|
||||||
|
if "E00121" in str(test_reason) or test_status == 1: # 1 = DECLINED
|
||||||
|
print(f"🐛 DEBUG: TEST AUTH FAILED - Payment profile exists but is INVALID!")
|
||||||
|
needs_recovery = True
|
||||||
|
logger.warning(f"🔧 PAYMENT PROFILE {db_card.auth_net_payment_profile_id} EXISTS BUT IS UNUSABLE - E00121 detected during test!")
|
||||||
|
logger.warning(f"🔧 Test transaction failed: {test_reason}")
|
||||||
|
else:
|
||||||
|
print(f"🐛 DEBUG: Payment profile {db_card.auth_net_payment_profile_id} is VALID and USABLE")
|
||||||
|
logger.info(f"✅ Payment profile {db_card.auth_net_payment_profile_id} is valid and usable in Authorize.net")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"🐛 DEBUG: Exception during usability test: {str(e)} - assuming profile needs recreation")
|
||||||
|
needs_recovery = True
|
||||||
|
logger.warning(f"🔧 Could not test payment profile usability: {str(e)} - assuming it needs recreation")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"🐛 DEBUG: Exception during profile validation: {str(e)}")
|
||||||
|
logger.warning(f"🔧 Could not verify payment profile validity in Authorize.net: {str(e)}")
|
||||||
|
# If we can't verify, assume it's okay and let the transaction proceed
|
||||||
|
# (better to try and fail than to block legitimate transactions)
|
||||||
|
logger.info(f"⚠️ Unable to verify profile validity - proceeding with transaction anyway")
|
||||||
|
|
||||||
|
if needs_recovery:
|
||||||
|
logger.warning(f"🔧 DETECTED PAYMENT PROFILE ISSUE - Triggering auto-recovery for customer {customer_id}")
|
||||||
|
|
||||||
|
# Auto-recover: Refresh payment profiles before transaction
|
||||||
|
from ..services import user_create
|
||||||
|
recovery_success = user_create.refresh_customer_payment_profiles(
|
||||||
|
db, customer_id, db_customer.auth_net_profile_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if recovery_success:
|
||||||
|
logger.info("✅ Auto-recovery successful - proceeding with transaction")
|
||||||
|
|
||||||
|
# Re-fetch card data to get updated payment profile ID
|
||||||
|
db.refresh(db_card)
|
||||||
|
db.refresh(db_customer)
|
||||||
|
logger.info(f"🔍 After recovery - payment_profile_id: '{db_card.auth_net_payment_profile_id}'")
|
||||||
|
else:
|
||||||
|
logger.error("❌ Auto-recovery failed - cannot proceed with transaction")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Payment profile setup error detected and auto-recovery failed. Please contact support."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Final validation before proceeding
|
||||||
if not db_customer.auth_net_profile_id or not db_card.auth_net_payment_profile_id:
|
if not db_customer.auth_net_profile_id or not db_card.auth_net_payment_profile_id:
|
||||||
print(f"ROUTER DEBUG: WILL THROW HTTP 400 ERROR: auth_net_profile_id='{db_customer.auth_net_profile_id}', payment_profile_id='{db_card.auth_net_payment_profile_id}'")
|
logger.error(f"❌ CRITICAL: Payment profile validation failed after recovery attempt")
|
||||||
raise HTTPException(status_code=400, detail="Payment profile is not set up correctly for this customer/card")
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Payment profile is not set up correctly for this customer/card"
|
||||||
|
)
|
||||||
|
|
||||||
# Call the service function for authorization with auto-recovery enabled
|
logger.info(f"✅ Payment profile validation passed - proceeding with authorization")
|
||||||
|
|
||||||
|
# 🚨 ENHANCED E00121 ERROR HANDLING 🚨
|
||||||
|
# If transaction still fails with E00121 despite pre-validation, force-nuke the problematic ID
|
||||||
auth_net_response = payment_service.authorize_customer_profile(
|
auth_net_response = payment_service.authorize_customer_profile(
|
||||||
customer_profile_id=db_customer.auth_net_profile_id,
|
customer_profile_id=db_customer.auth_net_profile_id,
|
||||||
payment_profile_id=db_card.auth_net_payment_profile_id,
|
payment_profile_id=db_card.auth_net_payment_profile_id,
|
||||||
transaction_req=transaction_req,
|
transaction_req=transaction_req,
|
||||||
db_session=db, # For auto-recovery
|
db_session=db,
|
||||||
customer_id=customer_id, # For auto-recovery
|
customer_id=customer_id,
|
||||||
card_id=db_card.id # For auto-recovery
|
card_id=db_card.id
|
||||||
)
|
)
|
||||||
|
|
||||||
status, auth_net_transaction_id, rejection_reason = _parse_authnet_response(auth_net_response)
|
# Parse the transaction response (no need for E00121 nuclear cleanup since pre-validation should have caught it)
|
||||||
|
transaction_status, auth_net_transaction_id, rejection_reason = _parse_authnet_response(auth_net_response)
|
||||||
|
|
||||||
print(transaction_req)
|
print(transaction_req)
|
||||||
# Create the transaction record in your database with the correct type
|
|
||||||
transaction_data = schemas.TransactionBase(
|
transaction_data = schemas.TransactionBase(
|
||||||
preauthorize_amount=transaction_req.preauthorize_amount,
|
preauthorize_amount=transaction_req.preauthorize_amount,
|
||||||
transaction_type=TransactionType.AUTHORIZE, # This is key
|
transaction_type=TransactionType.AUTHORIZE, # This is key
|
||||||
@@ -254,10 +432,99 @@ def authorize_saved_card(customer_id: int, transaction_req: schemas.TransactionA
|
|||||||
db=db,
|
db=db,
|
||||||
transaction=transaction_data,
|
transaction=transaction_data,
|
||||||
customer_id=customer_id,
|
customer_id=customer_id,
|
||||||
status=status,
|
status=transaction_status,
|
||||||
auth_net_transaction_id=auth_net_transaction_id
|
auth_net_transaction_id=auth_net_transaction_id
|
||||||
)
|
)
|
||||||
|
|
||||||
return db_transaction
|
return db_transaction
|
||||||
|
|
||||||
|
|
||||||
|
def _nuclear_e00121_payment_profile_cleanup(db: Session, customer_id: int, card_id: int, corrupted_profile_id: str, customer_profile_id: str) -> bool:
|
||||||
|
"""
|
||||||
|
DEDICATED E00121 NUKER: Forcefully removes problematic payment profile IDs from both database and Authorize.net.
|
||||||
|
This is the nuclear option for when E00121 payment profile errors persist despite all attempts to recover.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
customer_id: Customer ID
|
||||||
|
card_id: Card ID that has the corrupted profile
|
||||||
|
corrupted_profile_id: The problematic payment profile ID causing E00121
|
||||||
|
customer_profile_id: Customer profile ID in Authorize.net
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if cleanup successful and card can be retried
|
||||||
|
"""
|
||||||
|
print("💣 NUCLEAR E00121 CLEANUP INITIATED! 💣")
|
||||||
|
print(f"💣 Target: Card {card_id}, Profile {corrupted_profile_id}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .. import crud
|
||||||
|
from ..services import user_delete
|
||||||
|
|
||||||
|
# Step 1: Get the card from database
|
||||||
|
card = crud.get_card_by_id(db, card_id)
|
||||||
|
if not card:
|
||||||
|
print(f"💣 ERROR: Card {card_id} not found in database")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Step 2: NUKE FROM AUTHORIZE.NET FIRST
|
||||||
|
print(f"💣 Deleting corrupted profile {corrupted_profile_id} from Authorize.net...")
|
||||||
|
try:
|
||||||
|
delete_success = user_delete._delete_payment_profile(customer_profile_id, corrupted_profile_id)
|
||||||
|
if delete_success:
|
||||||
|
print("✅ Successfully deleted corrupted profile from Authorize.net")
|
||||||
|
else:
|
||||||
|
print("⚠️ Profile may not have existed in Authorize.net or delete failed")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Exception deleting from Authorize.net: {str(e)} - continuing anyway")
|
||||||
|
|
||||||
|
# Step 3: NUKE FROM DATABASE REGARDLESS
|
||||||
|
print(f"💣 Clearing corrupted profile ID {corrupted_profile_id} from database card {card_id}")
|
||||||
|
card.auth_net_payment_profile_id = None
|
||||||
|
db.add(card)
|
||||||
|
db.commit()
|
||||||
|
print("✅ Successfully cleared corrupted profile ID from database")
|
||||||
|
|
||||||
|
# Step 4: Attempt immediate recreation of payment profile
|
||||||
|
print("💣 Attempting immediate recreation of payment profile...")
|
||||||
|
try:
|
||||||
|
customer = crud.get_customer(db, customer_id)
|
||||||
|
|
||||||
|
# Format card data for recreation
|
||||||
|
exp_year = card.expiration_year.zfill(4) if len(card.expiration_year) < 4 else card.expiration_year
|
||||||
|
exp_month = card.expiration_month.zfill(2) if len(card.expiration_month) < 2 else card.expiration_month
|
||||||
|
exp_date = f"{exp_year}-{exp_month}"
|
||||||
|
|
||||||
|
card_create_data = schemas.CardCreate(
|
||||||
|
card_number=card.card_number,
|
||||||
|
expiration_date=exp_date,
|
||||||
|
cvv=card.security_number
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..services import payment_service
|
||||||
|
new_profile_id = payment_service.add_payment_profile_to_customer(
|
||||||
|
customer_profile_id, customer, card_create_data, is_default=(card.main_card == True)
|
||||||
|
)
|
||||||
|
|
||||||
|
if new_profile_id:
|
||||||
|
print(f"✅ IMMEDIATE RECREATION SUCCESSFUL: New profile {new_profile_id}")
|
||||||
|
card.auth_net_payment_profile_id = str(new_profile_id)
|
||||||
|
db.add(card)
|
||||||
|
db.commit()
|
||||||
|
print(f"💣 Nuclear cleanup COMPLETE - Card {card_id} has new profile {new_profile_id}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("❌ Immediate recreation failed - no new profile ID")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as recreate_e:
|
||||||
|
print(f"❌ Immediate recreation failed: {str(recreate_e)}")
|
||||||
|
print("💣 Database cleared but recreation failed - needs manual recreation later")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as nuke_e:
|
||||||
|
print(f"💣 CRITICAL ERROR in nuclear cleanup: {str(nuke_e)}")
|
||||||
|
db.rollback()
|
||||||
|
return False
|
||||||
|
|
||||||
# --- CAPTURE ENDPOINT MOVED TO TRANSACTION ROUTER ---
|
# --- CAPTURE ENDPOINT MOVED TO TRANSACTION ROUTER ---
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
|
|||||||
# Set Authorize.net environment based on configuration
|
# Set Authorize.net environment based on configuration
|
||||||
if ApplicationConfig.CURRENT_SETTINGS == 'PRODUCTION':
|
if ApplicationConfig.CURRENT_SETTINGS == 'PRODUCTION':
|
||||||
constants.environment = constants.PRODUCTION
|
constants.environment = constants.PRODUCTION
|
||||||
|
VALIDATION_MODE = "liveMode"
|
||||||
else:
|
else:
|
||||||
constants.environment = constants.SANDBOX
|
constants.environment = constants.SANDBOX
|
||||||
|
VALIDATION_MODE = "testMode"
|
||||||
|
|
||||||
constants.show_url_on_request = True # Very useful for debugging
|
constants.show_url_on_request = True # Very useful for debugging
|
||||||
|
|
||||||
@@ -191,7 +193,7 @@ def add_payment_profile_to_customer(customer_profile_id: str, customer: schemas.
|
|||||||
customerProfileId=customer_profile_id,
|
customerProfileId=customer_profile_id,
|
||||||
paymentProfile=paymentProfile,
|
paymentProfile=paymentProfile,
|
||||||
# ========= CHANGE 2.B: USE liveMode =========
|
# ========= CHANGE 2.B: USE liveMode =========
|
||||||
validationMode="testMode"
|
validationMode=VALIDATION_MODE
|
||||||
)
|
)
|
||||||
|
|
||||||
controller = createCustomerPaymentProfileController(request)
|
controller = createCustomerPaymentProfileController(request)
|
||||||
@@ -243,6 +245,7 @@ def authorize_customer_profile(customer_profile_id: str, payment_profile_id: str
|
|||||||
# CHECK FOR E00121 ERROR - "invalid payment profile ID"
|
# CHECK FOR E00121 ERROR - "invalid payment profile ID"
|
||||||
if db_session and customer_id and card_id and _is_e00121_response(response):
|
if db_session and customer_id and card_id and _is_e00121_response(response):
|
||||||
logger.warning(f"🚨 E00121 DETECTED! Invalid payment profile {payment_profile_id}")
|
logger.warning(f"🚨 E00121 DETECTED! Invalid payment profile {payment_profile_id}")
|
||||||
|
logger.warning("POOOP")
|
||||||
logger.info(f"🔄 AUTO-RECOVERING: Starting payment profile refresh for customer {customer_id}")
|
logger.info(f"🔄 AUTO-RECOVERING: Starting payment profile refresh for customer {customer_id}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -263,3 +263,158 @@ def create_user_account(db: Session, customer_id: int) -> dict:
|
|||||||
"message": f"An unexpected error occurred: {str(e)}",
|
"message": f"An unexpected error occurred: {str(e)}",
|
||||||
"profile_id": None
|
"profile_id": None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_customer_payment_profiles(db: Session, customer_id: int, auth_profile_id: str) -> bool:
|
||||||
|
"""
|
||||||
|
Refresh payment profiles for a customer by syncing from Authorize.net to database.
|
||||||
|
This ensures database payment_profile_ids stay in sync with Authorize.net.
|
||||||
|
NOW WITH RECREATION FALLBACK: If no payment profiles exist in Authorize.net,
|
||||||
|
recreates them from stored card data instead of failing.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
db: Database session
|
||||||
|
customer_id: ID of the customer
|
||||||
|
auth_profile_id: Authorize.net customer profile ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if refresh successful, False otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Import here to avoid circular imports
|
||||||
|
from .. import crud
|
||||||
|
|
||||||
|
# Get customer's current cards from database
|
||||||
|
cards_before = crud.get_customer_cards(db, customer_id)
|
||||||
|
customer = crud.get_customer(db, customer_id)
|
||||||
|
logger.info(f"🔄 Refresh START: customer_id={customer_id}, profile_id={auth_profile_id}, current cards={len(cards_before)}")
|
||||||
|
|
||||||
|
# STEP 1: Try to get actual payment profiles from Authorize.net
|
||||||
|
payment_profile_ids = []
|
||||||
|
try:
|
||||||
|
payment_profile_ids = payment_service.get_customer_payment_profiles(auth_profile_id)
|
||||||
|
logger.info(f"🔄 Retrieved {len(payment_profile_ids)} payment profiles from Authorize.net: {payment_profile_ids}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"🔄 Could not retrieve payment profiles from Authorize.net: {str(e)}")
|
||||||
|
logger.info("🔄 Will attempt to recreate missing payment profiles")
|
||||||
|
|
||||||
|
# STEP 2: Check if we have enough payment profiles for our cards
|
||||||
|
cards_need_update = []
|
||||||
|
for card in cards_before:
|
||||||
|
if not card.auth_net_payment_profile_id or card.auth_net_payment_profile_id.strip() == "":
|
||||||
|
cards_need_update.append(card)
|
||||||
|
logger.info(f"🔄 Card {card.id} needs payment profile ID assignment")
|
||||||
|
elif str(card.auth_net_payment_profile_id) not in [str(pid) for pid in payment_profile_ids if pid]:
|
||||||
|
# Payment profile ID exists in DB but not found in Authorize.net - likely invalid
|
||||||
|
cards_need_update.append(card)
|
||||||
|
logger.warning(f"🔄 Card {card.id} has payment profile ID {card.auth_net_payment_profile_id} but it's not found in Authorize.net - NEEDS RECREATION")
|
||||||
|
else:
|
||||||
|
# Profile exists in Authorize.net, but let's double-check it's usable by doing a quick test
|
||||||
|
logger.info(f"🔄 Card {card.id} has payment profile ID {card.auth_net_payment_profile_id} in Authorize.net - testing usability...")
|
||||||
|
try:
|
||||||
|
test_req = schemas.TransactionAuthorizeByCardID(
|
||||||
|
card_id=card.id,
|
||||||
|
preauthorize_amount="0.01"
|
||||||
|
)
|
||||||
|
test_response = payment_service.authorize_customer_profile(
|
||||||
|
customer_profile_id=customer.auth_net_profile_id,
|
||||||
|
payment_profile_id=card.auth_net_payment_profile_id,
|
||||||
|
transaction_req=test_req,
|
||||||
|
db_session=None, customer_id=None, card_id=None
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check test result
|
||||||
|
_, _, test_reason = payment_service._parse_authnet_response(test_response)
|
||||||
|
if "E00121" in str(test_reason):
|
||||||
|
cards_need_update.append(card)
|
||||||
|
logger.warning(f"🔄 Card {card.id} has profile {card.auth_net_payment_profile_id} that EXISTS but is CORRUPTED - NEEDS RECREATION")
|
||||||
|
# Explicitly delete the corrupted profile first
|
||||||
|
try:
|
||||||
|
from . import user_delete
|
||||||
|
delete_success = user_delete._delete_payment_profile(customer.auth_net_profile_id, card.auth_net_payment_profile_id)
|
||||||
|
if delete_success:
|
||||||
|
logger.info(f"🔄 Successfully deleted corrupted payment profile {card.auth_net_payment_profile_id}")
|
||||||
|
card.auth_net_payment_profile_id = None
|
||||||
|
db.add(card)
|
||||||
|
except Exception as del_e:
|
||||||
|
logger.warning(f"🔄 Failed to delete corrupted profile {card.auth_net_payment_profile_id}: {str(del_e)}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"🔄 Card {card.id} has valid and usable payment profile ID {card.auth_net_payment_profile_id}")
|
||||||
|
except Exception as test_e:
|
||||||
|
logger.warning(f"🔄 Could not test usability of profile {card.auth_net_payment_profile_id} for card {card.id}: {str(test_e)} - assuming it's okay")
|
||||||
|
|
||||||
|
# STEP 3: If we don't have enough valid payment profiles, recreate missing ones
|
||||||
|
if len(cards_need_update) > 0:
|
||||||
|
logger.info(f"🔄 Need to recreate {len(cards_need_update)} payment profiles")
|
||||||
|
|
||||||
|
# Clear payment profile IDs for cards that need recreation (they're invalid anyway)
|
||||||
|
for card in cards_need_update:
|
||||||
|
if card.auth_net_payment_profile_id:
|
||||||
|
logger.info(f"🔄 Clearing invalid payment profile ID {card.auth_net_payment_profile_id} for card {card.id}")
|
||||||
|
card.auth_net_payment_profile_id = None
|
||||||
|
db.add(card)
|
||||||
|
|
||||||
|
# Recreate payment profiles for cards that need them
|
||||||
|
recreated_cards = []
|
||||||
|
for card in cards_need_update:
|
||||||
|
try:
|
||||||
|
# Format expiration date for recreation
|
||||||
|
exp_year = card.expiration_year.zfill(4) if len(card.expiration_year) < 4 else card.expiration_year
|
||||||
|
exp_month = card.expiration_month.zfill(2) if len(card.expiration_month) < 2 else card.expiration_month
|
||||||
|
exp_date = f"{exp_year}-{exp_month}"
|
||||||
|
|
||||||
|
card_create_data = schemas.CardCreate(
|
||||||
|
card_number=card.card_number,
|
||||||
|
expiration_date=exp_date,
|
||||||
|
cvv=card.security_number
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"🔄 Recreating payment profile for card {card.id} (**** **** **** {card.last_four_digits})")
|
||||||
|
new_payment_profile_id = payment_service.add_payment_profile_to_customer(
|
||||||
|
auth_profile_id, customer, card_create_data, is_default=(card.main_card == True)
|
||||||
|
)
|
||||||
|
|
||||||
|
if new_payment_profile_id:
|
||||||
|
card.auth_net_payment_profile_id = str(new_payment_profile_id)
|
||||||
|
db.add(card)
|
||||||
|
recreated_cards.append(card)
|
||||||
|
logger.info(f"✅ Successfully recreated payment profile {new_payment_profile_id} for card {card.id}")
|
||||||
|
else:
|
||||||
|
logger.error(f"❌ Failed to recreate payment profile for card {card.id} - no ID returned")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Failed to recreate payment profile for card {card.id}: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if recreated_cards:
|
||||||
|
db.commit()
|
||||||
|
logger.info(f"✅ Successfully recreated and saved {len(recreated_cards)} payment profiles")
|
||||||
|
else:
|
||||||
|
logger.error("❌ No payment profiles could be recreated - this is a critical failure")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logger.info(f"🔄 All {len(cards_before)} cards have valid payment profile IDs")
|
||||||
|
|
||||||
|
# STEP 4: Final verification that everything looks good
|
||||||
|
cards_final = crud.get_customer_cards(db, customer_id)
|
||||||
|
logger.info("🔄 FINAL VERIFICATION:")
|
||||||
|
all_valid = True
|
||||||
|
for card in cards_final:
|
||||||
|
status = "✅ VALID" if card.auth_net_payment_profile_id and card.auth_net_payment_profile_id.strip() else "❌ INVALID"
|
||||||
|
logger.info(f"🔄 {status} Card {card.id}: auth_net_payment_profile_id='{card.auth_net_payment_profile_id}'")
|
||||||
|
|
||||||
|
if not card.auth_net_payment_profile_id or card.auth_net_payment_profile_id.strip() == "":
|
||||||
|
all_valid = False
|
||||||
|
|
||||||
|
if all_valid:
|
||||||
|
logger.info(f"🔄 Refresh COMPLETE: All {len(cards_final)} cards have valid payment profile IDs")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning("🔄 Refresh PARTIAL: Some cards may still have invalid payment profile IDs")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"🔄 Refresh FAILED for customer {customer_id}: {str(e)}")
|
||||||
|
logger.error(f"🔄 Refresh traceback: {traceback.format_exc()}")
|
||||||
|
db.rollback()
|
||||||
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user