236 lines
11 KiB
Python
236 lines
11 KiB
Python
import logging
|
|
from authorizenet import apicontractsv1
|
|
from authorizenet.apicontrollers import createTransactionController
|
|
from .. import schemas
|
|
from config import load_config
|
|
|
|
# Set up logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Load Authorize.net credentials from config
|
|
ApplicationConfig = load_config()
|
|
API_LOGIN_ID = ApplicationConfig.API_LOGIN_ID
|
|
TRANSACTION_KEY = ApplicationConfig.TRANSACTION_KEY
|
|
# For sandbox, endpoint is https://apitest.authorize.net/xml/v1/request.api
|
|
|
|
def safe_string_convert(value):
|
|
"""
|
|
Safely convert any value to string, handling lxml objects properly.
|
|
"""
|
|
if value is None:
|
|
return None
|
|
# Check if it's an lxml object with text attribute
|
|
if hasattr(value, 'text'):
|
|
return value.text
|
|
# Otherwise use standard string conversion
|
|
return str(value)
|
|
|
|
def charge_credit_card(transaction: schemas.TransactionCreate):
|
|
logger.info(f"Processing charge for amount: {transaction.charge_amount}")
|
|
|
|
merchantAuth = apicontractsv1.merchantAuthenticationType()
|
|
merchantAuth.name = API_LOGIN_ID
|
|
merchantAuth.transactionKey = TRANSACTION_KEY
|
|
|
|
creditCard = apicontractsv1.creditCardType()
|
|
creditCard.cardNumber = transaction.card_number
|
|
creditCard.expirationDate = transaction.expiration_date
|
|
creditCard.cardCode = transaction.cvv
|
|
|
|
payment = apicontractsv1.paymentType()
|
|
payment.creditCard = creditCard
|
|
|
|
transactionRequest = apicontractsv1.transactionRequestType()
|
|
transactionRequest.transactionType = "authCaptureTransaction"
|
|
transactionRequest.amount = transaction.charge_amount # ✅ Fixed: Use charge_amount
|
|
transactionRequest.payment = payment
|
|
|
|
createtransactionrequest = apicontractsv1.createTransactionRequest()
|
|
createtransactionrequest.merchantAuthentication = merchantAuth
|
|
createtransactionrequest.refId = "ref_id" # Optional reference ID
|
|
createtransactionrequest.transactionRequest = transactionRequest
|
|
|
|
controller = createTransactionController(createtransactionrequest)
|
|
controller.execute()
|
|
|
|
response = controller.getresponse()
|
|
|
|
# Extract rejection reason if payment failed
|
|
rejection_reason = None
|
|
if response is not None and response.messages is not None:
|
|
logger.info(f"Charge response: {response.messages.resultCode}")
|
|
|
|
# If payment was declined (resultCode is "Error"), extract error details
|
|
if response.messages.resultCode == "Error":
|
|
rejection_reason = "Authorize.Net Charge Error"
|
|
if hasattr(response.messages, 'message'):
|
|
try:
|
|
if len(response.messages.message) > 0:
|
|
for msg in response.messages.message:
|
|
if hasattr(msg, 'code') and hasattr(msg, 'text'):
|
|
# Convert lxml StringElement objects to Python strings
|
|
code_str = msg.code.text if msg.code else "Unknown"
|
|
text_str = msg.text.text if msg.text else "No details available"
|
|
rejection_reason = f"{code_str}: {text_str}"
|
|
break # Use the first error message
|
|
elif hasattr(msg, 'text'):
|
|
# Convert lxml StringElement to Python string
|
|
text_str = msg.text.text if msg.text else "No details available"
|
|
rejection_reason = f"Error: {text_str}"
|
|
break
|
|
else:
|
|
rejection_reason = "Charge declined - no specific error details available"
|
|
except Exception as e:
|
|
rejection_reason = f"Charge declined - error details could not be parsed: {str(e)}"
|
|
else:
|
|
rejection_reason = "Charge declined - no error message available"
|
|
|
|
if hasattr(response.messages, 'message') and len(response.messages.message) > 0:
|
|
for msg in response.messages.message:
|
|
logger.info(f"Message: {msg.text.text if msg.text else 'No message text'}")
|
|
else:
|
|
logger.error("No response from Authorize.net")
|
|
rejection_reason = "No response received from Authorize.Net"
|
|
|
|
# Attach rejection reason to response for the router to use
|
|
if response is not None:
|
|
response._rejection_reason = rejection_reason
|
|
|
|
return response
|
|
|
|
def authorize_credit_card(transaction: schemas.TransactionAuthorize):
|
|
logger.info(f"Processing preauthorization for amount: {transaction.preauthorize_amount}")
|
|
|
|
merchantAuth = apicontractsv1.merchantAuthenticationType()
|
|
merchantAuth.name = API_LOGIN_ID
|
|
merchantAuth.transactionKey = TRANSACTION_KEY
|
|
|
|
creditCard = apicontractsv1.creditCardType()
|
|
creditCard.cardNumber = transaction.card_number
|
|
creditCard.expirationDate = transaction.expiration_date
|
|
creditCard.cardCode = transaction.cvv
|
|
|
|
payment = apicontractsv1.paymentType()
|
|
payment.creditCard = creditCard
|
|
|
|
transactionRequest = apicontractsv1.transactionRequestType()
|
|
transactionRequest.transactionType = "authOnlyTransaction"
|
|
transactionRequest.amount = transaction.preauthorize_amount # ✅ Fixed: Use preauthorize_amount
|
|
transactionRequest.payment = payment
|
|
|
|
createtransactionrequest = apicontractsv1.createTransactionRequest()
|
|
createtransactionrequest.merchantAuthentication = merchantAuth
|
|
createtransactionrequest.refId = "ref_id"
|
|
createtransactionrequest.transactionRequest = transactionRequest
|
|
|
|
controller = createTransactionController(createtransactionrequest)
|
|
controller.execute()
|
|
|
|
response = controller.getresponse()
|
|
|
|
# Extract rejection reason if payment failed
|
|
rejection_reason = None
|
|
if response is not None and response.messages is not None:
|
|
logger.info(f"Preauthorization response: {response.messages.resultCode}")
|
|
|
|
# If payment was declined (resultCode is "Error"), extract error details
|
|
if response.messages.resultCode == "Error":
|
|
rejection_reason = "Authorize.Net Error"
|
|
if hasattr(response.messages, 'message'):
|
|
try:
|
|
if len(response.messages.message) > 0:
|
|
for msg in response.messages.message:
|
|
if hasattr(msg, 'code') and hasattr(msg, 'text'):
|
|
# Convert lxml StringElement objects to Python strings
|
|
code_str = msg.code.text if msg.code else "Unknown"
|
|
text_str = msg.text.text if msg.text else "No details available"
|
|
rejection_reason = f"{code_str}: {text_str}"
|
|
break # Use the first error message
|
|
elif hasattr(msg, 'text'):
|
|
# Convert lxml StringElement to Python string
|
|
text_str = msg.text.text if msg.text else "No details available"
|
|
rejection_reason = f"Error: {text_str}"
|
|
break
|
|
else:
|
|
rejection_reason = "Payment declined - no specific error details available"
|
|
except Exception as e:
|
|
rejection_reason = f"Payment declined - error details could not be parsed: {str(e)}"
|
|
else:
|
|
rejection_reason = "Payment declined - no error message available"
|
|
|
|
if hasattr(response.messages, 'message') and len(response.messages.message) > 0:
|
|
for msg in response.messages.message:
|
|
logger.info(f"Message: {msg.text.text if msg.text else 'No message text'}")
|
|
else:
|
|
logger.error("No response from Authorize.net for preauthorization")
|
|
rejection_reason = "No response received from Authorize.Net"
|
|
|
|
# Attach rejection reason to response for the router to use
|
|
if response is not None:
|
|
response._rejection_reason = rejection_reason
|
|
|
|
return response
|
|
|
|
def capture_authorized_transaction(transaction: schemas.TransactionCapture):
|
|
merchantAuth = apicontractsv1.merchantAuthenticationType()
|
|
merchantAuth.name = API_LOGIN_ID
|
|
merchantAuth.transactionKey = TRANSACTION_KEY
|
|
|
|
transactionRequest = apicontractsv1.transactionRequestType()
|
|
transactionRequest.transactionType = "priorAuthCaptureTransaction"
|
|
transactionRequest.amount = transaction.charge_amount # ✅ Fixed: Use charge_amount
|
|
transactionRequest.refTransId = transaction.auth_net_transaction_id
|
|
|
|
createtransactionrequest = apicontractsv1.createTransactionRequest()
|
|
createtransactionrequest.merchantAuthentication = merchantAuth
|
|
createtransactionrequest.refId = "ref_id"
|
|
createtransactionrequest.transactionRequest = transactionRequest
|
|
|
|
controller = createTransactionController(createtransactionrequest)
|
|
controller.execute()
|
|
|
|
response = controller.getresponse()
|
|
|
|
# Extract rejection reason if capture failed
|
|
rejection_reason = None
|
|
if response is not None and response.messages is not None:
|
|
logger.info(f"Capture response: {response.messages.resultCode}")
|
|
|
|
# If capture was declined (resultCode is "Error"), extract error details
|
|
if response.messages.resultCode == "Error":
|
|
rejection_reason = "Authorize.Net Capture Error"
|
|
if hasattr(response.messages, 'message'):
|
|
try:
|
|
if len(response.messages.message) > 0:
|
|
for msg in response.messages.message:
|
|
if hasattr(msg, 'code') and hasattr(msg, 'text'):
|
|
# Convert lxml StringElement objects to Python strings
|
|
code_str = msg.code.text if msg.code else "Unknown"
|
|
text_str = msg.text.text if msg.text else "No details available"
|
|
rejection_reason = f"{code_str}: {text_str}"
|
|
break # Use the first error message
|
|
elif hasattr(msg, 'text'):
|
|
# Convert lxml StringElement to Python string
|
|
text_str = msg.text.text if msg.text else "No details available"
|
|
rejection_reason = f"Error: {text_str}"
|
|
break
|
|
else:
|
|
rejection_reason = "Capture declined - no specific error details available"
|
|
except Exception as e:
|
|
rejection_reason = f"Capture declined - error details could not be parsed: {str(e)}"
|
|
else:
|
|
rejection_reason = "Capture declined - no error message available"
|
|
|
|
if hasattr(response.messages, 'message') and len(response.messages.message) > 0:
|
|
for msg in response.messages.message:
|
|
logger.info(f"Message: {msg.text.text if msg.text else 'No message text'}")
|
|
else:
|
|
logger.error("No response from Authorize.net for capture")
|
|
rejection_reason = "No response received from Authorize.Net for capture"
|
|
|
|
# Attach rejection reason to response for the router to use
|
|
if response is not None:
|
|
response._rejection_reason = rejection_reason
|
|
|
|
return response
|