Fixed e00007 error

This commit is contained in:
2025-09-28 10:17:35 -04:00
parent 5b4a05566a
commit 940be20852
10 changed files with 179 additions and 124 deletions

View File

@@ -13,4 +13,4 @@ RUN pip install -r requirements.txt
COPY . . COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--log-level", "info"]

View File

@@ -28,6 +28,9 @@ class TransactionType(enum.IntEnum):
CHARGE = 0 CHARGE = 0
AUTHORIZE = 1 AUTHORIZE = 1
CAPTURE = 3 CAPTURE = 3
# --- NEW CIM CORE FUNCTIONS --- # --- NEW CIM CORE FUNCTIONS ---
STATE_ID_TO_ABBREVIATION = { STATE_ID_TO_ABBREVIATION = {
0: "MA", 0: "MA",
@@ -54,7 +57,7 @@ def _parse_authnet_response(response: Optional[AuthNetResponse]) -> Tuple[Transa
else: else:
print("DEBUG: No messages attribute") print("DEBUG: No messages attribute")
if response is not None and hasattr(response, 'messages') and response.messages.resultCode == "Ok": if response.messages.resultCode == "Ok":
print("DEBUG: Taking APPROVED path") print("DEBUG: Taking APPROVED path")
status = TransactionStatus.APPROVED status = TransactionStatus.APPROVED
auth_net_transaction_id = None auth_net_transaction_id = None
@@ -491,8 +494,11 @@ def _nuclear_e00121_payment_profile_cleanup(db: Session, customer_id: int, card_
customer = crud.get_customer(db, customer_id) customer = crud.get_customer(db, customer_id)
# Format card data for recreation # Format card data for recreation
exp_year = card.expiration_year.zfill(4) if len(card.expiration_year) < 4 else card.expiration_year # Convert to string first to handle cases where database returns int instead of string
exp_month = card.expiration_month.zfill(2) if len(card.expiration_month) < 2 else card.expiration_month exp_year_str = str(card.expiration_year)
exp_month_str = str(card.expiration_month)
exp_year = exp_year_str.zfill(4) if len(exp_year_str) < 4 else exp_year_str
exp_month = exp_month_str.zfill(2) if len(exp_month_str) < 2 else exp_month_str
exp_date = f"{exp_year}-{exp_month}" exp_date = f"{exp_year}-{exp_month}"
card_create_data = schemas.CardCreate( card_create_data = schemas.CardCreate(

View File

@@ -24,6 +24,10 @@ def test_transaction_router():
"""Test endpoint to verify transaction router is loaded""" """Test endpoint to verify transaction router is loaded"""
return {"test": "transaction router is working"} return {"test": "transaction router is working"}
@transaction_router.get("/transaction/delivery/{delivery_id}", summary="Get pre-authorization transaction for a delivery") @transaction_router.get("/transaction/delivery/{delivery_id}", summary="Get pre-authorization transaction for a delivery")
def get_delivery_transaction(delivery_id: int, db: Session = Depends(database.get_db)): def get_delivery_transaction(delivery_id: int, db: Session = Depends(database.get_db)):
""" """
@@ -98,7 +102,7 @@ def _parse_authnet_response(response):
""" """
Parse Authorize.Net response for transaction status Parse Authorize.Net response for transaction status
""" """
if response is not None and hasattr(response, 'messages') and response.messages.resultCode == "Ok": if response.messages.resultCode == "Ok":
status = TransactionStatus.APPROVED status = TransactionStatus.APPROVED
rejection_reason = None rejection_reason = None
else: else:

View File

@@ -1,12 +1,9 @@
import logging
import authorizenet.apicontrollers as controllers import authorizenet.apicontrollers as controllers
from authorizenet import apicontractsv1 from authorizenet import apicontractsv1
from .. import crud, database, schemas from .. import crud, database, schemas
from config import load_config from config import load_config
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
# Load Authorize.net credentials # Load Authorize.net credentials
ApplicationConfig = load_config() ApplicationConfig = load_config()
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
@@ -60,7 +57,7 @@ def verify_customer_authorize_account(db: Session, customer_id: int) -> dict:
# Enhanced profile validation - check multiple conditions for profile existence # Enhanced profile validation - check multiple conditions for profile existence
profile_valid = _is_profile_valid(response) profile_valid = _is_profile_valid(response)
if not profile_valid: if not profile_valid:
logger.info(f"Profile {customer.auth_net_profile_id} for customer {customer_id} is invalid/not found. Nulling out in database.") print(f"Profile {customer.auth_net_profile_id} for customer {customer_id} is invalid/not found. Nulling out in database.")
# Profile not found or invalid - set auth_net_profile_id to NULL in database # Profile not found or invalid - set auth_net_profile_id to NULL in database
try: try:
@@ -68,9 +65,9 @@ def verify_customer_authorize_account(db: Session, customer_id: int) -> dict:
customer.auth_net_profile_id = None customer.auth_net_profile_id = None
db.add(customer) # Mark for update db.add(customer) # Mark for update
db.commit() # Persist the change db.commit() # Persist the change
logger.info(f"Successfully nulled out auth_net_profile_id for customer {customer_id}") print(f"Successfully nulled out auth_net_profile_id for customer {customer_id}")
except Exception as update_error: except Exception as update_error:
logger.error(f"Failed to update customer auth_net_profile_id to NULL: {update_error}") print(f"Failed to update customer auth_net_profile_id to NULL: {update_error}")
db.rollback() # Rollback on error db.rollback() # Rollback on error
return { return {
@@ -99,7 +96,7 @@ def verify_customer_authorize_account(db: Session, customer_id: int) -> dict:
} }
except Exception as e: except Exception as e:
logger.error(f"Error verifying customer authorize account for customer {customer_id}: {str(e)}") print(f"Error verifying customer authorize account for customer {customer_id}: {str(e)}")
return { return {
"profile_exists": False, "profile_exists": False,
"has_payment_methods": False, "has_payment_methods": False,
@@ -150,7 +147,7 @@ def _is_profile_valid(response) -> bool:
return True return True
except Exception as e: except Exception as e:
logger.error(f"Error validating profile response: {str(e)}") print(f"Error validating profile response: {str(e)}")
return False return False
@@ -176,11 +173,12 @@ def _get_customer_profile(profile_id: str):
) )
controller = controllers.getCustomerProfileController(request) controller = controllers.getCustomerProfileController(request)
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
return response return response
except Exception as e: except Exception as e:
logger.error(f"Error getting customer profile {profile_id}: {str(e)}") print(f"Error getting customer profile {profile_id}: {str(e)}")
return None return None

View File

@@ -1,6 +1,5 @@
## File: your_app/services/payment_service.py ## File: your_app/services/payment_service.py
import logging
import pprint import pprint
import traceback import traceback
import re import re
@@ -15,20 +14,22 @@ from authorizenet.constants import constants
from .. import schemas from .. import schemas
from config import load_config # Assuming you have this from config import load_config # Assuming you have this
logger = logging.getLogger(__name__)
# Load Authorize.net credentials # Load Authorize.net credentials
ApplicationConfig = load_config() ApplicationConfig = load_config()
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
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
print("Payment Service Production")
VALIDATION_MODE = "liveMode" VALIDATION_MODE = "liveMode"
API_LOGIN_ID = '4d2Mn6H23R'
TRANSACTION_KEY = '7B94d8xfTQXv37WS'
else: else:
constants.environment = constants.SANDBOX constants.environment = constants.SANDBOX
VALIDATION_MODE = "testMode" VALIDATION_MODE = "testMode"
print("Payment Service Sandbox")
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
constants.show_url_on_request = True # Very useful for debugging constants.show_url_on_request = True # Very useful for debugging
@@ -55,7 +56,7 @@ def _get_authnet_error_message(response):
text = msg.text if hasattr(msg, 'text') else 'No details provided.' text = msg.text if hasattr(msg, 'text') else 'No details provided.'
return f"Error {code}: {text}" return f"Error {code}: {text}"
except Exception as e: except Exception as e:
logger.error(f"Error while parsing Auth.Net error message: {e}") print(f"Error while parsing Auth.Net error message: {e}")
return "An unparsable error occurred with the payment gateway." return "An unparsable error occurred with the payment gateway."
return "An unknown error occurred with the payment gateway." return "An unknown error occurred with the payment gateway."
@@ -65,10 +66,15 @@ def create_customer_profile(customer: schemas.Customer, card_info: schemas.CardC
Creates a new customer profile in Authorize.Net (payment profiles added separately). Creates a new customer profile in Authorize.Net (payment profiles added separately).
This version sanitizes and trims customer data before sending. This version sanitizes and trims customer data before sending.
""" """
logger.info(f"Attempting to create Auth.Net profile for customer ID: {customer.id}") print(f"Attempting to create Auth.Net profile for customer ID: {customer.id}")
print("dogpoop")
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY) print(ApplicationConfig.API_LOGIN_ID)
print(ApplicationConfig.TRANSACTION_KEY)
try:
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
except Exception as e:
print(e)
print("here1234")
# --- DATA SANITIZATION LOGIC --- # --- DATA SANITIZATION LOGIC ---
def sanitize(text, max_len, allow_spaces=False, is_zip=False): def sanitize(text, max_len, allow_spaces=False, is_zip=False):
if not text: if not text:
@@ -97,38 +103,54 @@ def create_customer_profile(customer: schemas.Customer, card_info: schemas.CardC
controller = createCustomerProfileController(request) controller = createCustomerProfileController(request)
try: try:
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
print("************")
if response is not None and response.messages.resultCode == "Ok": print(response)
print(response.messages)
print(response.messages.resultCode)
print("************")
except Exception as e:
print("^^^")
print(response)
print(response.messages)
print(response.messages.resultCode)
print("^^^")
print(f"API execution failed: {traceback.format_exc()}")
raise ValueError("API call execution error")
try:
if response.messages.resultCode == "Ok":
profile_id = response.customerProfileId profile_id = response.customerProfileId
print(profile_id)
# Payment profile ID is not available since profiles are added separately # Payment profile ID is not available since profiles are added separately
payment_id = "" payment_id = ""
logger.info(f"SUCCESS: Created customer profile: {profile_id} (payment profiles added separately)") print(f"SUCCESS: Created customer profile: {profile_id} (payment profiles added separately)")
# Add detailed logging # Add detailed logging
logger.info(f"API Response - Profile ID: {profile_id}") print(f"API Response - Profile ID: {profile_id}")
logger.info(f"Returning: profile_id='{str(profile_id)}', payment_id=''") print(f"Returning: profile_id='{str(profile_id)}', payment_id=''")
return str(profile_id), "" return str(profile_id), ""
else: else:
error_msg = _get_authnet_error_message(response) error_msg = _get_authnet_error_message(response)
logger.error(f"Failed to create customer profile (API Error): {error_msg}") print(f"Failed to create customer profile (API Error): {error_msg}")
logger.error(f"Full API Response: {pprint.pformat(vars(response))}") print(f"Full API Response: {pprint.pformat(vars(response))}")
raise ValueError(error_msg) raise ValueError(error_msg)
except ValueError: except ValueError:
# Re-raise specific ValueError messages we already set above (like E00039) # Re-raise specific ValueError messages we already set above (like E00039)
raise raise
except Exception as e: except Exception as e:
logger.error(f"A critical exception occurred during the API call: {traceback.format_exc()}") print(f"A critical exception occurred during the API call: {traceback.format_exc()}")
raise ValueError("Could not connect to the payment gateway.") raise ValueError("Could not connect to the payment gateway.")
def add_payment_profile_to_customer(customer_profile_id: str, customer: schemas.Customer, card_info: schemas.CardCreate, is_default: bool = False): def add_payment_profile_to_customer(customer_profile_id: str, customer: schemas.Customer, card_info: schemas.CardCreate, is_default: bool = False):
logger.info(f"Adding {'default ' if is_default else ''}payment profile to Auth.Net customer profile ID: {customer_profile_id}") print(f"Adding {'default ' if is_default else ''}payment profile to Auth.Net customer profile ID: {customer_profile_id}")
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY) merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
@@ -164,7 +186,7 @@ def add_payment_profile_to_customer(customer_profile_id: str, customer: schemas.
else: else:
expiration_date = "0325" expiration_date = "0325"
logger.info(f"Parsed expiration date for card: {card_info.expiration_date} -> {expiration_date}") print(f"Parsed expiration date for card: {card_info.expiration_date} -> {expiration_date}")
creditCard = apicontractsv1.creditCardType( creditCard = apicontractsv1.creditCardType(
cardNumber=card_info.card_number, cardNumber=card_info.card_number,
@@ -199,29 +221,28 @@ def add_payment_profile_to_customer(customer_profile_id: str, customer: schemas.
controller = createCustomerPaymentProfileController(request) controller = createCustomerPaymentProfileController(request)
try: try:
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
if response is not None and response.messages.resultCode == "Ok": if response.messages.resultCode == "Ok":
# Fix: Proper payment profile ID extraction (same bug fix as above) # Fix: Proper payment profile ID extraction (same bug fix as above)
if hasattr(response, 'customerPaymentProfileId') and response.customerPaymentProfileId: if hasattr(response, 'customerPaymentProfileId') and response.customerPaymentProfileId:
return str(response.customerPaymentProfileId) return str(response.customerPaymentProfileId)
else: else:
logger.warning("WARNING: Added payment profile but no ID returned") print("WARNING: Added payment profile but no ID returned")
raise ValueError("Payment profile created but ID not found in response") raise ValueError("Payment profile created but ID not found in response")
else: else:
error_msg = _get_authnet_error_message(response) error_msg = _get_authnet_error_message(response)
logger.error(f"Failed to add payment profile: {error_msg}") print(f"Failed to add payment profile: {error_msg}")
logger.error(f"SANITIZED DATA SENT FOR ADD PROFILE: FirstName='{first_name}', LastName='{last_name}', Address='{address}', City='{city}', State='{state}', Zip='{zip_code}'") print(f"SANITIZED DATA SENT FOR ADD PROFILE: FirstName='{first_name}', LastName='{last_name}', Address='{address}', City='{city}', State='{state}', Zip='{zip_code}'")
logger.error(f"Card info: number='{card_info.card_number}', exp='{card_info.expiration_date}', cvv='{card_info.cvv}'") # Mask if sensitive
logger.error(pprint.pformat(vars(billTo)))
logger.error(pprint.pformat(vars(request)))
print(f"Card info: number='{card_info.card_number}', exp='{card_info.expiration_date}', cvv='{card_info.cvv}'") # Mask if sensitive
print(pprint.pformat(vars(billTo)))
print(pprint.pformat(vars(request)))
raise ValueError(error_msg) raise ValueError(error_msg)
except Exception as e: except Exception as e:
logger.error(f"A critical exception occurred during the API call: {traceback.format_exc()}") print(f"A critical exception occurred during the API call: {traceback.format_exc()}")
raise ValueError("Could not connect to the payment gateway.") raise ValueError("Could not connect to the payment gateway.")
def authorize_customer_profile(customer_profile_id: str, payment_profile_id: str, transaction_req: schemas.TransactionAuthorizeByCardID, db_session=None, customer_id=None, card_id=None): def authorize_customer_profile(customer_profile_id: str, payment_profile_id: str, transaction_req: schemas.TransactionAuthorizeByCardID, db_session=None, customer_id=None, card_id=None):
@@ -229,24 +250,24 @@ def authorize_customer_profile(customer_profile_id: str, payment_profile_id: str
Creates an AUTH_ONLY transaction against a customer profile with automatic E00121 recovery. Creates an AUTH_ONLY transaction against a customer profile with automatic E00121 recovery.
This holds funds but doesn't capture them, and automatically recovers from invalid payment profiles. This holds funds but doesn't capture them, and automatically recovers from invalid payment profiles.
""" """
logger.info(f"🔐 Authorizing profile {customer_profile_id} / payment {payment_profile_id} for ${transaction_req.preauthorize_amount}") print(f"Authorizing profile {customer_profile_id} / payment {payment_profile_id} for ${transaction_req.preauthorize_amount}")
# Validate inputs # Validate inputs
if not customer_profile_id or customer_profile_id.strip() == "": if not customer_profile_id or customer_profile_id.strip() == "":
logger.error("INVALID: customer_profile_id is None or empty") print("INVALID: customer_profile_id is None or empty")
if not payment_profile_id or payment_profile_id.strip() == "": if not payment_profile_id or payment_profile_id.strip() == "":
logger.error("INVALID: payment_profile_id is None or empty") print("INVALID: payment_profile_id is None or empty")
logger.error("Payment profile ID must be a valid, non-empty string") print("Payment profile ID must be a valid, non-empty string")
# FIRST ATTEMPT - Normal authorization # FIRST ATTEMPT - Normal authorization
logger.info("🔐 TRANSACTION ATTEMPT 1: Standard authorization") print("TRANSACTION ATTEMPT 1: Standard authorization")
response = _perform_authorization(customer_profile_id, payment_profile_id, transaction_req) response = _perform_authorization(customer_profile_id, payment_profile_id, transaction_req)
# 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}") print(f"E00121 DETECTED! Invalid payment profile {payment_profile_id}")
logger.warning("POOOP") print("POOOP")
logger.info(f"🔄 AUTO-RECOVERING: Starting payment profile refresh for customer {customer_id}") print(f"AUTO-RECOVERING: Starting payment profile refresh for customer {customer_id}")
try: try:
# GET CUSTOMER PROFILE ID (since we have customer_id but need profile_id) # GET CUSTOMER PROFILE ID (since we have customer_id but need profile_id)
@@ -254,7 +275,7 @@ def authorize_customer_profile(customer_profile_id: str, payment_profile_id: str
customer = crud.get_customer(db_session, customer_id) customer = crud.get_customer(db_session, customer_id)
if customer: if customer:
# REFRESH ALL PAYMENT PROFILES FOR THIS CUSTOMER # REFRESH ALL PAYMENT PROFILES FOR THIS CUSTOMER
logger.info(f"🔄 CALLING REFRESH: customer_id={customer_id}, profile_id={customer.auth_net_profile_id}") print(f"CALLING REFRESH: customer_id={customer_id}, profile_id={customer.auth_net_profile_id}")
from .user_create import refresh_customer_payment_profiles from .user_create import refresh_customer_payment_profiles
refresh_customer_payment_profiles(db_session, customer_id, customer.auth_net_profile_id) refresh_customer_payment_profiles(db_session, customer_id, customer.auth_net_profile_id)
@@ -262,25 +283,25 @@ def authorize_customer_profile(customer_profile_id: str, payment_profile_id: str
updated_card = crud.get_card_by_id(db_session, card_id) updated_card = crud.get_card_by_id(db_session, card_id)
if updated_card and updated_card.auth_net_payment_profile_id != payment_profile_id: if updated_card and updated_card.auth_net_payment_profile_id != payment_profile_id:
new_payment_profile_id = updated_card.auth_net_payment_profile_id new_payment_profile_id = updated_card.auth_net_payment_profile_id
logger.info(f"🔄 RECOVERY SUCCESS: Old ID '{payment_profile_id}' → New ID '{new_payment_profile_id}'") print(f"RECOVERY SUCCESS: Old ID '{payment_profile_id}' → New ID '{new_payment_profile_id}'")
# SECOND ATTEMPT - With refreshed payment profile ID # SECOND ATTEMPT - With refreshed payment profile ID
logger.info("🔐 TRANSACTION ATTEMPT 2: Retry with refreshed payment profile") print("TRANSACTION ATTEMPT 2: Retry with refreshed payment profile")
response = _perform_authorization(customer_profile_id, new_payment_profile_id, transaction_req) response = _perform_authorization(customer_profile_id, new_payment_profile_id, transaction_req)
if _is_e00121_response(response): if _is_e00121_response(response):
logger.error("E00121 STILL PERSISTS after refresh - manual intervention may be needed") print("E00121 STILL PERSISTS after refresh - manual intervention may be needed")
logger.error(f"Payment profile {new_payment_profile_id} also rejected by Authorize.Net") print(f"Payment profile {new_payment_profile_id} also rejected by Authorize.Net")
else: else:
logger.info(f"SUCCESS! E00121 RESOLVED - Transaction succeeded with refreshed payment profile {new_payment_profile_id}") print(f"SUCCESS! E00121 RESOLVED - Transaction succeeded with refreshed payment profile {new_payment_profile_id}")
else: else:
logger.error(f"RECOVERY FAILED: No updated payment profile ID found for card {card_id}") print(f"RECOVERY FAILED: No updated payment profile ID found for card {card_id}")
logger.error("Database refresh did not provide new payment profile ID") print("Database refresh did not provide new payment profile ID")
else: else:
logger.error(f"RECOVERY FAILED: Customer {customer_id} not found in database") print(f"RECOVERY FAILED: Customer {customer_id} not found in database")
except Exception as e: except Exception as e:
logger.error(f"AUTO-RECOVERY FAILED: {str(e)}") print(f"AUTO-RECOVERY FAILED: {str(e)}")
logger.error("Exception during payment profile refresh process") print("Exception during payment profile refresh process")
return response return response
@@ -310,15 +331,16 @@ def _perform_authorization(customer_profile_id: str, payment_profile_id: str, tr
) )
controller = createTransactionController(createtransactionrequest) controller = createTransactionController(createtransactionrequest)
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
# Log response details # Log response details
if response and hasattr(response, 'messages'): if response and hasattr(response, 'messages'):
result_code = getattr(response.messages, 'resultCode', 'Unknown') result_code = getattr(response.messages, 'resultCode', 'Unknown')
logger.info(f"Authorize response: resultCode='{result_code}'") print(f"Authorize response: resultCode='{result_code}'")
else: else:
logger.info("Authorize response: No standard response structure") print("Authorize response: No standard response structure")
return response return response
@@ -340,25 +362,25 @@ def _is_e00121_response(response):
if isinstance(message, list): if isinstance(message, list):
for msg in message: for msg in message:
if getattr(msg, 'code', '') == 'E00121': if getattr(msg, 'code', '') == 'E00121':
logger.info("🔍 E00121 detected in message list") print("E00121 detected in message list")
return True return True
# Handle single message # Handle single message
elif hasattr(message, 'code'): elif hasattr(message, 'code'):
if message.code == 'E00121': if message.code == 'E00121':
logger.info(f"🔍 E00121 detected: '{getattr(message, 'text', 'No details')}'") print(f"E00121 detected: '{getattr(message, 'text', 'No details')}'")
return True return True
else: else:
logger.debug(f"🔍 Message code: '{message.code}' (not E00121)") print(f"Message code: '{message.code}' (not E00121)")
return False return False
except Exception as e: except Exception as e:
logger.warning(f"🔍 Error checking for E00121: {str(e)}") print(f"Error checking for E00121: {str(e)}")
return False return False
def capture_authorized_transaction(transaction_req: schemas.TransactionCapture): def capture_authorized_transaction(transaction_req: schemas.TransactionCapture):
"""Captures a previously authorized transaction.""" """Captures a previously authorized transaction."""
logger.info(f"Capturing transaction {transaction_req.auth_net_transaction_id} for {transaction_req.charge_amount}") print(f"Capturing transaction {transaction_req.auth_net_transaction_id} for {transaction_req.charge_amount}")
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY) merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
@@ -374,6 +396,7 @@ def capture_authorized_transaction(transaction_req: schemas.TransactionCapture):
) )
controller = createTransactionController(createtransactionrequest) controller = createTransactionController(createtransactionrequest)
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
return controller.getresponse() return controller.getresponse()
@@ -384,7 +407,7 @@ def get_customer_payment_profiles(customer_profile_id: str):
Retrieves all payment profile IDs for a given customer profile from Authorize.net. Retrieves all payment profile IDs for a given customer profile from Authorize.net.
Returns a list of payment profile IDs in the order they were created. Returns a list of payment profile IDs in the order they were created.
""" """
logger.info(f"Retrieving payment profiles for customer profile ID: {customer_profile_id}") print(f"Retrieving payment profiles for customer profile ID: {customer_profile_id}")
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY) merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
@@ -397,24 +420,25 @@ def get_customer_payment_profiles(customer_profile_id: str):
controller = getCustomerProfileController(request) controller = getCustomerProfileController(request)
try: try:
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
if response is not None and response.messages.resultCode == "Ok" and response.profile is not None: if response.messages.resultCode == "Ok":
payment_profile_ids = [] payment_profile_ids = []
if response.profile.paymentProfiles is not None: if response.profile.paymentProfiles is not None:
for profile in response.profile.paymentProfiles: for profile in response.profile.paymentProfiles:
payment_profile_ids.append(str(profile.customerPaymentProfileId)) payment_profile_ids.append(str(profile.customerPaymentProfileId))
logger.info(f"Retrieved {len(payment_profile_ids)} payment profile IDs for profile {customer_profile_id}") print(f"Retrieved {len(payment_profile_ids)} payment profile IDs for profile {customer_profile_id}")
return payment_profile_ids return payment_profile_ids
else: else:
error_msg = _get_authnet_error_message(response) error_msg = _get_authnet_error_message(response)
logger.error(f"Failed to retrieve customer profile {customer_profile_id}: {error_msg}") print(f"Failed to retrieve customer profile {customer_profile_id}: {error_msg}")
raise ValueError(f"Could not retrieve customer profile: {error_msg}") raise ValueError(f"Could not retrieve customer profile: {error_msg}")
except Exception as e: except Exception as e:
logger.error(f"Critical exception while retrieving customer profile {customer_profile_id}: {traceback.format_exc()}") print(f"Critical exception while retrieving customer profile {customer_profile_id}: {traceback.format_exc()}")
raise ValueError("Could not connect to the payment gateway.") raise ValueError("Could not connect to the payment gateway.")
@@ -423,7 +447,7 @@ def charge_customer_profile(customer_profile_id: str, payment_profile_id: str, t
Creates an AUTH_CAPTURE transaction (charge now) against a customer profile. Creates an AUTH_CAPTURE transaction (charge now) against a customer profile.
This charges the customer immediately for the full amount. This charges the customer immediately for the full amount.
""" """
logger.info(f"Charging profile {customer_profile_id} / payment {payment_profile_id} for ${transaction_req.charge_amount}") print(f"Charging profile {customer_profile_id} / payment {payment_profile_id} for ${transaction_req.charge_amount}")
merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY) merchantAuth = apicontractsv1.merchantAuthenticationType(name=API_LOGIN_ID, transactionKey=TRANSACTION_KEY)
@@ -443,6 +467,7 @@ def charge_customer_profile(customer_profile_id: str, payment_profile_id: str, t
) )
controller = createTransactionController(createtransactionrequest) controller = createTransactionController(createtransactionrequest)
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
# The response is returned directly to the router to be parsed there # The response is returned directly to the router to be parsed there
return controller.getresponse() return controller.getresponse()

View File

@@ -17,18 +17,29 @@ logger = logging.getLogger(__name__)
# Load Authorize.net credentials # Load Authorize.net credentials
ApplicationConfig = load_config() ApplicationConfig = load_config()
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
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
print("Constants:", ApplicationConfig.CURRENT_SETTINGS)
print("Authorize.Net URL:", constants.environment)
API_LOGIN_ID = '4d2Mn6H23R'
TRANSACTION_KEY = '7B94d8xfTQXv37WS'
# Override to standard production endpoint
constants.environment = "https://api.authorize.net/xml/v1/request.api"
print("Overridden Authorize.Net URL:", constants.environment)
else: else:
print("Constants:", ApplicationConfig.CURRENT_SETTINGS)
constants.environment = constants.SANDBOX constants.environment = constants.SANDBOX
print("Authorize.Net URL:", constants.environment)
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
constants.show_url_on_request = True constants.show_url_on_request = True
def _get_error_message(response): def _get_error_message(response):
""" """
Robust error parsing function that correctly handles the API's response format. Robust error parsing function that correctly handles the API's response format.
@@ -81,7 +92,7 @@ def create_user_account(db: Session, customer_id: int) -> dict:
"message": f"Customer {customer_id} not found", "message": f"Customer {customer_id} not found",
"profile_id": None "profile_id": None
} }
print(customer)
# Get customer's cards from database # Get customer's cards from database
cards = crud.get_customer_cards(db, customer_id) cards = crud.get_customer_cards(db, customer_id)
if not cards: if not cards:
@@ -96,15 +107,17 @@ def create_user_account(db: Session, customer_id: int) -> dict:
# Get the first card to use for initial profile creation # Get the first card to use for initial profile creation
first_card = cards[0] first_card = cards[0]
logger.info(f"Using first card ID={first_card.id} for profile creation") logger.info(f"Using first card ID={first_card.id} for profile creation")
logger.info(f"Card Number: {first_card.card_number[:4]}**** ****{first_card.card_number[-4:]}") logger.info(f"Card Number: {first_card.card_number[:4]}**** ****{first_card.card_number[-4:]}")
logger.info(f"Expiration: {first_card.expiration_month}/{first_card.expiration_year}") logger.info(f"Expiration: {first_card.expiration_month}/{first_card.expiration_year}")
# Create CardCreate object for the first card # Create CardCreate object for the first card
# Format expiration date for string values - pad year to 4 digits and month to 2 digits # Format expiration date for string values - pad year to 4 digits and month to 2 digits
expiration_year = first_card.expiration_year.zfill(4) if len(first_card.expiration_year) < 4 else first_card.expiration_year # Convert to string first to handle cases where database returns int instead of string
expiration_month = first_card.expiration_month.zfill(2) if len(first_card.expiration_month) < 2 else first_card.expiration_month exp_year_str = str(first_card.expiration_year)
exp_month_str = str(first_card.expiration_month)
expiration_year = exp_year_str.zfill(4) if len(exp_year_str) < 4 else exp_year_str
expiration_month = exp_month_str.zfill(2) if len(exp_month_str) < 2 else exp_month_str
expiration_date = f"{expiration_year}-{expiration_month}" expiration_date = f"{expiration_year}-{expiration_month}"
card_info = schemas.CardCreate( card_info = schemas.CardCreate(
card_number=first_card.card_number, card_number=first_card.card_number,
@@ -161,8 +174,11 @@ def create_user_account(db: Session, customer_id: int) -> dict:
for card in cards[1:]: for card in cards[1:]:
try: try:
# Format expiration date for string values # Format expiration date for string values
exp_year = card.expiration_year.zfill(4) if len(card.expiration_year) < 4 else card.expiration_year # Convert to string first to handle cases where database returns int instead of string
exp_month = card.expiration_month.zfill(2) if len(card.expiration_month) < 2 else card.expiration_month exp_year_str = str(card.expiration_year)
exp_month_str = str(card.expiration_month)
exp_year = exp_year_str.zfill(4) if len(exp_year_str) < 4 else exp_year_str
exp_month = exp_month_str.zfill(2) if len(exp_month_str) < 2 else exp_month_str
exp_date = f"{exp_year}-{exp_month}" exp_date = f"{exp_year}-{exp_month}"
card_info_additional = schemas.CardCreate( card_info_additional = schemas.CardCreate(
@@ -359,8 +375,11 @@ def refresh_customer_payment_profiles(db: Session, customer_id: int, auth_profil
for card in cards_need_update: for card in cards_need_update:
try: try:
# Format expiration date for recreation # Format expiration date for recreation
exp_year = card.expiration_year.zfill(4) if len(card.expiration_year) < 4 else card.expiration_year # Convert to string first to handle cases where database returns int instead of string
exp_month = card.expiration_month.zfill(2) if len(card.expiration_month) < 2 else card.expiration_month exp_year_str = str(card.expiration_year)
exp_month_str = str(card.expiration_month)
exp_year = exp_year_str.zfill(4) if len(exp_year_str) < 4 else exp_year_str
exp_month = exp_month_str.zfill(2) if len(exp_month_str) < 2 else exp_month_str
exp_date = f"{exp_year}-{exp_month}" exp_date = f"{exp_year}-{exp_month}"
card_create_data = schemas.CardCreate( card_create_data = schemas.CardCreate(

View File

@@ -1,4 +1,3 @@
import logging
import traceback import traceback
from authorizenet import apicontractsv1 from authorizenet import apicontractsv1
from authorizenet.apicontrollers import ( from authorizenet.apicontrollers import (
@@ -9,19 +8,19 @@ from authorizenet.constants import constants
from config import load_config from config import load_config
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
logger = logging.getLogger(__name__)
# Load Authorize.net credentials # Load Authorize.net credentials
ApplicationConfig = load_config() ApplicationConfig = load_config()
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
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
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
else: else:
constants.environment = constants.SANDBOX constants.environment = constants.SANDBOX
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
constants.show_url_on_request = True constants.show_url_on_request = True
@@ -44,7 +43,7 @@ def _get_authnet_error_message(response):
text = msg.text if hasattr(msg, 'text') else 'No details provided.' text = msg.text if hasattr(msg, 'text') else 'No details provided.'
return f"Error {code}: {text}" return f"Error {code}: {text}"
except Exception as e: except Exception as e:
logger.error(f"Error while parsing Auth.Net error message: {e}") print(f"Error while parsing Auth.Net error message: {e}")
return "An unparsable error occurred with the payment gateway." return "An unparsable error occurred with the payment gateway."
return "An unknown error occurred with the payment gateway." return "An unknown error occurred with the payment gateway."
@@ -89,29 +88,29 @@ def delete_user_account(db: Session, customer_id: int) -> dict:
# Get customer's payment profiles/cards from database # Get customer's payment profiles/cards from database
cards = crud.get_customer_cards(db, customer_id) cards = crud.get_customer_cards(db, customer_id)
logger.info(f"Starting deletion of Authorize.net account for customer {customer_id} (Profile ID: {profile_id_to_delete})") print(f"Starting deletion of Authorize.net account for customer {customer_id} (Profile ID: {profile_id_to_delete})")
# Step 1: Delete payment profiles first (must delete these before customer profile) # Step 1: Delete payment profiles first (must delete these before customer profile)
deleted_payment_profiles = [] deleted_payment_profiles = []
if cards: if cards:
logger.info(f"Found {len(cards)} cards to delete from Authorize.net") print(f"Found {len(cards)} cards to delete from Authorize.net")
for card_index, card in enumerate(cards): for card_index, card in enumerate(cards):
if card.auth_net_payment_profile_id: if card.auth_net_payment_profile_id:
try: try:
logger.info(f"Deleting payment profile {card.auth_net_payment_profile_id} for card {card.id}") print(f"Deleting payment profile {card.auth_net_payment_profile_id} for card {card.id}")
# Delete payment profile from Authorize.net # Delete payment profile from Authorize.net
success = _delete_payment_profile(profile_id_to_delete, card.auth_net_payment_profile_id) success = _delete_payment_profile(profile_id_to_delete, card.auth_net_payment_profile_id)
if success: if success:
deleted_payment_profiles.append(card.auth_net_payment_profile_id) deleted_payment_profiles.append(card.auth_net_payment_profile_id)
logger.info(f"Successfully deleted payment profile {card.auth_net_payment_profile_id}") print(f"Successfully deleted payment profile {card.auth_net_payment_profile_id}")
else: else:
logger.warning(f"Failed to delete payment profile {card.auth_net_payment_profile_id} - it may not exist or already deleted") print(f"Failed to delete payment profile {card.auth_net_payment_profile_id} - it may not exist or already deleted")
except Exception as e: except Exception as e:
logger.error(f"Error deleting payment profile {card.auth_net_payment_profile_id}: {str(e)}") print(f"Error deleting payment profile {card.auth_net_payment_profile_id}: {str(e)}")
# Continue with other payment profiles - we want to delete as much as possible # Continue with other payment profiles - we want to delete as much as possible
# Always null out the payment profile ID in database (even if API delete failed) # Always null out the payment profile ID in database (even if API delete failed)
@@ -119,7 +118,7 @@ def delete_user_account(db: Session, customer_id: int) -> dict:
db.add(card) db.add(card)
# Step 2: Delete customer profile # Step 2: Delete customer profile
logger.info(f"Deleting customer profile {profile_id_to_delete}") print(f"Deleting customer profile {profile_id_to_delete}")
profile_deleted_success = _delete_customer_profile(profile_id_to_delete) profile_deleted_success = _delete_customer_profile(profile_id_to_delete)
# Step 3: Update database regardless of API results # Step 3: Update database regardless of API results
@@ -130,7 +129,7 @@ def delete_user_account(db: Session, customer_id: int) -> dict:
db.commit() db.commit()
if profile_deleted_success: if profile_deleted_success:
logger.info(f"Successfully deleted Authorize.net account for customer {customer_id}") print(f"Successfully deleted Authorize.net account for customer {customer_id}")
return { return {
"success": True, "success": True,
"message": f"Successfully deleted Authorize.net account with profile ID {profile_id_to_delete}", "message": f"Successfully deleted Authorize.net account with profile ID {profile_id_to_delete}",
@@ -139,7 +138,7 @@ def delete_user_account(db: Session, customer_id: int) -> dict:
"deleted_payment_profiles": deleted_payment_profiles "deleted_payment_profiles": deleted_payment_profiles
} }
else: else:
logger.warning(f"Customer profile {profile_id_to_delete} may not have been completely removed from Authorize.net, but database has been updated") print(f"Customer profile {profile_id_to_delete} may not have been completely removed from Authorize.net, but database has been updated")
return { return {
"success": False, "success": False,
"message": f"Profile {profile_id_to_delete} may not have been completely removed from Authorize.net, but database has been cleaned up", "message": f"Profile {profile_id_to_delete} may not have been completely removed from Authorize.net, but database has been cleaned up",
@@ -149,7 +148,7 @@ def delete_user_account(db: Session, customer_id: int) -> dict:
} }
except Exception as e: except Exception as e:
logger.error(f"Critical exception during account deletion for customer {customer_id}: {traceback.format_exc()}") print(f"Critical exception during account deletion for customer {customer_id}: {traceback.format_exc()}")
db.rollback() db.rollback()
return { return {
"success": False, "success": False,
@@ -180,27 +179,28 @@ def _delete_customer_profile(profile_id: str) -> bool:
) )
controller = deleteCustomerProfileController(request) controller = deleteCustomerProfileController(request)
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
if response is None: if response is None:
logger.warning(f"No response received when trying to delete profile {profile_id}") print(f"No response received when trying to delete profile {profile_id}")
return False return False
if hasattr(response, 'messages') and response.messages.resultCode == "Ok": if hasattr(response, 'messages') and response.messages.resultCode == "Ok":
logger.info(f"Successfully deleted customer profile {profile_id}") print(f"Successfully deleted customer profile {profile_id}")
return True return True
else: else:
error_msg = _get_authnet_error_message(response) error_msg = _get_authnet_error_message(response)
logger.warning(f"Failed to delete customer profile {profile_id}: {error_msg}") print(f"Failed to delete customer profile {profile_id}: {error_msg}")
# Still count as success if the profile was already deleted/not found # Still count as success if the profile was already deleted/not found
if "not found" in error_msg.lower() or "E00040" in error_msg or "E00035" in error_msg: if "not found" in error_msg.lower() or "E00040" in error_msg or "E00035" in error_msg:
logger.info(f"Profile {profile_id} was already deleted or doesn't exist") print(f"Profile {profile_id} was already deleted or doesn't exist")
return True return True
return False return False
except Exception as e: except Exception as e:
logger.error(f"Exception during delete customer profile {profile_id}: {str(e)}") print(f"Exception during delete customer profile {profile_id}: {str(e)}")
return False return False
@@ -228,25 +228,26 @@ def _delete_payment_profile(customer_profile_id: str, payment_profile_id: str) -
) )
controller = deleteCustomerPaymentProfileController(request) controller = deleteCustomerPaymentProfileController(request)
controller.setenvironment(constants.PRODUCTION)
controller.execute() controller.execute()
response = controller.getresponse() response = controller.getresponse()
if response is None: if response is None:
logger.warning(f"No response received when trying to delete payment profile {payment_profile_id}") print(f"No response received when trying to delete payment profile {payment_profile_id}")
return False return False
if hasattr(response, 'messages') and response.messages.resultCode == "Ok": if hasattr(response, 'messages') and response.messages.resultCode == "Ok":
logger.info(f"Successfully deleted payment profile {payment_profile_id}") print(f"Successfully deleted payment profile {payment_profile_id}")
return True return True
else: else:
error_msg = _get_authnet_error_message(response) error_msg = _get_authnet_error_message(response)
logger.warning(f"Failed to delete payment profile {payment_profile_id}: {error_msg}") print(f"Failed to delete payment profile {payment_profile_id}: {error_msg}")
# Still count as success if the payment profile was already deleted/not found # Still count as success if the payment profile was already deleted/not found
if "not found" in error_msg.lower() or "E00040" in error_msg or "E00035" in error_msg: if "not found" in error_msg.lower() or "E00040" in error_msg or "E00035" in error_msg:
logger.info(f"Payment profile {payment_profile_id} was already deleted or doesn't exist") print(f"Payment profile {payment_profile_id} was already deleted or doesn't exist")
return True return True
return False return False
except Exception as e: except Exception as e:
logger.error(f"Exception during delete payment profile {payment_profile_id}: {str(e)}") print(f"Exception during delete payment profile {payment_profile_id}: {str(e)}")
return False return False

View File

@@ -13,14 +13,13 @@ def load_config(mode=os.environ.get('MODE')):
return ApplicationConfig return ApplicationConfig
elif mode == 'DEVELOPMENT': elif mode == 'DEVELOPMENT':
print("poop")
print("poop")
print("poop")
from settings_dev import ApplicationConfig from settings_dev import ApplicationConfig
return ApplicationConfig return ApplicationConfig
else: else:
pass pass
except ImportError: except ImportError:
from settings_local import ApplicationConfig
from settings_dev import ApplicationConfig
return ApplicationConfig return ApplicationConfig

View File

@@ -4,8 +4,8 @@ class ApplicationConfig:
""" """
Basic Configuration for a generic User Basic Configuration for a generic User
""" """
API_LOGIN_ID = '9U6w96gZmX'
TRANSACTION_KEY = '94s6Qy458mMNJr7G' print("USING TESTING APPLICATIONCONFIG!!!!!")
CURRENT_SETTINGS = 'DEVELOPMENT' CURRENT_SETTINGS = 'DEVELOPMENT'
# databases info # databases info
POSTGRES_USERNAME = 'postgres' POSTGRES_USERNAME = 'postgres'
@@ -30,7 +30,9 @@ class ApplicationConfig:
"http://localhost:9516", # Authorize service port "http://localhost:9516", # Authorize service port
] ]
API_LOGIN_ID = '9U6w96gZmX'
TRANSACTION_KEY = '94s6Qy458mMNJr7G'
# # Authorize.net credentials (Sandbox Test Credentials) # # # Authorize.net credentials (Sandbox Test Credentials)
# API_LOGIN_ID = '5KP3u95bQpv' # API_LOGIN_ID = '5KP3u95bQpv'
# TRANSACTION_KEY = '346HZ32z3fP4hTG2' # TRANSACTION_KEY = '346HZ32z3fP4hTG2'

View File

@@ -2,6 +2,7 @@ class ApplicationConfig:
""" """
Basic Configuration for a generic User Basic Configuration for a generic User
""" """
print("USING PROD APPLICATIONCONFIG!!!!!")
CURRENT_SETTINGS = 'PRODUCTION' CURRENT_SETTINGS = 'PRODUCTION'
# databases info # databases info
POSTGRES_USERNAME = 'postgres' POSTGRES_USERNAME = 'postgres'
@@ -22,5 +23,5 @@ class ApplicationConfig:
] ]
# Authorize.net credentials # Authorize.net credentials
API_LOGIN_ID = 'bizdev05' API_LOGIN_ID = '4d2Mn6H23R'
TRANSACTION_KEY = '4kJd237rZu59qAZd' TRANSACTION_KEY = '7B94d8xfTQXv37WS'