diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e58aeba --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd diff --git a/__pycache__/config.cpython-39.pyc b/__pycache__/config.cpython-39.pyc deleted file mode 100644 index 3614ff2..0000000 Binary files a/__pycache__/config.cpython-39.pyc and /dev/null differ diff --git a/__pycache__/settings_dev.cpython-39.pyc b/__pycache__/settings_dev.cpython-39.pyc deleted file mode 100644 index 3f6484c..0000000 Binary files a/__pycache__/settings_dev.cpython-39.pyc and /dev/null differ diff --git a/app/__pycache__/__init__.cpython-39.pyc b/app/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 78a8abf..0000000 Binary files a/app/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/app/__pycache__/crud.cpython-39.pyc b/app/__pycache__/crud.cpython-39.pyc deleted file mode 100644 index 3cfbd43..0000000 Binary files a/app/__pycache__/crud.cpython-39.pyc and /dev/null differ diff --git a/app/__pycache__/database.cpython-39.pyc b/app/__pycache__/database.cpython-39.pyc deleted file mode 100644 index 5caf837..0000000 Binary files a/app/__pycache__/database.cpython-39.pyc and /dev/null differ diff --git a/app/__pycache__/main.cpython-39.pyc b/app/__pycache__/main.cpython-39.pyc deleted file mode 100644 index 013728c..0000000 Binary files a/app/__pycache__/main.cpython-39.pyc and /dev/null differ diff --git a/app/__pycache__/models.cpython-39.pyc b/app/__pycache__/models.cpython-39.pyc deleted file mode 100644 index fde198b..0000000 Binary files a/app/__pycache__/models.cpython-39.pyc and /dev/null differ diff --git a/app/__pycache__/schemas.cpython-39.pyc b/app/__pycache__/schemas.cpython-39.pyc deleted file mode 100644 index 38031ce..0000000 Binary files a/app/__pycache__/schemas.cpython-39.pyc and /dev/null differ diff --git a/app/crud.py b/app/crud.py index 06069e1..4a6afcb 100644 --- a/app/crud.py +++ b/app/crud.py @@ -12,17 +12,17 @@ def get_customers(db: Session, skip: int = 0, limit: int = 100): def create_transaction(db: Session, transaction: schemas.TransactionBase, customer_id: int, status: int, auth_net_transaction_id: str = None): db_transaction = models.Transaction( - preauthorize_amount=transaction.preauthorize_amount, - charge_amount=transaction.charge_amount, + preauthorize_amount=transaction.preauthorize_amount if status == 0 else 0, # Only save pre-auth amount if approved + charge_amount=transaction.charge_amount if transaction.transaction_type != 0 or status == 0 else 0, # Only save charge amount for charges if approved transaction_type=transaction.transaction_type, customer_id=customer_id, status=status, auth_net_transaction_id=auth_net_transaction_id, - service_id=transaction.service_id, - delivery_id=transaction.delivery_id, - card_id=transaction.card_id, + service_id=transaction.service_id, + delivery_id=transaction.delivery_id, + card_id=transaction.card_id, payment_gateway=transaction.payment_gateway, - rejection_reason=transaction.rejection_reason + rejection_reason=transaction.rejection_reason ) db.add(db_transaction) db.commit() @@ -50,7 +50,8 @@ def update_transaction_for_capture(db: Session, auth_net_transaction_id: str, ch if not transaction: return None - transaction.charge_amount = charge_amount + # Set charge_amount only if approved (status == 0), otherwise set to 0 + transaction.charge_amount = charge_amount if status == 0 else 0 transaction.transaction_type = 3 # capture transaction.status = status if rejection_reason: @@ -58,4 +59,4 @@ def update_transaction_for_capture(db: Session, auth_net_transaction_id: str, ch db.commit() db.refresh(transaction) - return transaction \ No newline at end of file + return transaction diff --git a/app/routers/__pycache__/__init__.cpython-39.pyc b/app/routers/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 6dba933..0000000 Binary files a/app/routers/__pycache__/__init__.cpython-39.pyc and /dev/null differ diff --git a/app/routers/__pycache__/payment.cpython-39.pyc b/app/routers/__pycache__/payment.cpython-39.pyc deleted file mode 100644 index 1886df4..0000000 Binary files a/app/routers/__pycache__/payment.cpython-39.pyc and /dev/null differ diff --git a/app/routers/payment.py b/app/routers/payment.py index 678d473..b523536 100644 --- a/app/routers/payment.py +++ b/app/routers/payment.py @@ -34,19 +34,39 @@ def _parse_authnet_response(response: Optional[AuthNetResponse]) -> Tuple[Transa Parses the response from the Authorize.Net service. (This is the same helper from before, it's a good change to keep) """ - if response and hasattr(response, 'messages') and response.messages.resultCode == "Ok": + if response is not None and hasattr(response, 'messages') and response.messages.resultCode == "Ok": status = TransactionStatus.APPROVED auth_net_transaction_id = str(response.transactionResponse.transId) if hasattr(response, 'transactionResponse') else None rejection_reason = None else: status = TransactionStatus.DECLINED auth_net_transaction_id = None - if hasattr(response, '_rejection_reason'): + + # Improved rejection reason extraction + rejection_reason = "Payment declined by gateway." + if hasattr(response, '_rejection_reason') and response._rejection_reason: rejection_reason = str(response._rejection_reason) + elif response is not None: + # Check transaction response for errors + if hasattr(response, 'transactionResponse') and response.transactionResponse: + tr = response.transactionResponse + if hasattr(tr, 'errors') and tr.errors: + for error in tr.errors: + if hasattr(error, 'errorCode') and hasattr(error, 'errorText'): + error_code = error.errorCode.text if error.errorCode else "Unknown" + error_text = error.errorText.text if error.errorText else "No error details" + rejection_reason = f"{error_code}: {error_text}" + break + elif hasattr(tr, 'messages') and tr.messages: + if len(tr.messages) > 0: + msg = tr.messages[0] + if hasattr(msg, 'code') and hasattr(msg, 'description'): + code = msg.code.text if msg.code else "Unknown" + desc = msg.description.text if msg.description else "No description" + rejection_reason = f"{code}: {desc}" elif response is None: rejection_reason = "No response received from payment gateway." - else: - rejection_reason = "Payment declined by gateway." + return status, auth_net_transaction_id, rejection_reason @@ -74,9 +94,6 @@ def authorize_card(customer_id: int, transaction: schemas.TransactionAuthorize, @router.post("/charge/{customer_id}", response_model=schemas.Transaction) def charge_card(customer_id: int, transaction: schemas.TransactionCreate, db: Session = Depends(database.get_db)): - # Add debug logging - print(f"DEBUG: Received charge request for customer_id: {customer_id}") - print(f"DEBUG: Transaction data: {transaction.dict() if hasattr(transaction, 'dict') else transaction}") try: auth_net_response = payment_service.charge_credit_card(transaction) @@ -90,7 +107,6 @@ def charge_card(customer_id: int, transaction: schemas.TransactionCreate, db: Se card_id=transaction.card_id, rejection_reason=rejection_reason ) - print(f"DEBUG: Transaction data to create: {transaction_data.dict()}") result = crud.create_transaction( db=db, @@ -99,7 +115,6 @@ def charge_card(customer_id: int, transaction: schemas.TransactionCreate, db: Se status=status, auth_net_transaction_id=auth_net_transaction_id ) - print(f"DEBUG: Created transaction: {result.dict()}") return result except Exception as e: print(f"DEBUG: Exception in charge_card: {str(e)}") diff --git a/app/services/__pycache__/payment_service.cpython-39.pyc b/app/services/__pycache__/payment_service.cpython-39.pyc deleted file mode 100644 index 466d3e2..0000000 Binary files a/app/services/__pycache__/payment_service.cpython-39.pyc and /dev/null differ diff --git a/app/services/payment_service.py b/app/services/payment_service.py index b67aa80..3779325 100644 --- a/app/services/payment_service.py +++ b/app/services/payment_service.py @@ -55,46 +55,15 @@ def charge_credit_card(transaction: schemas.TransactionCreate): response = controller.getresponse() - # Extract rejection reason if payment failed - rejection_reason = None + # Log response status if payment failed 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 @@ -128,46 +97,15 @@ def authorize_credit_card(transaction: schemas.TransactionAuthorize): response = controller.getresponse() - # Extract rejection reason if payment failed - rejection_reason = None + # Log response status 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 @@ -191,45 +129,14 @@ def capture_authorized_transaction(transaction: schemas.TransactionCapture): response = controller.getresponse() - # Extract rejection reason if capture failed - rejection_reason = None + # Log response status 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