diff --git a/ns_app/api/payments.py b/ns_app/api/payments.py index 7f00b4f..9865c82 100644 --- a/ns_app/api/payments.py +++ b/ns_app/api/payments.py @@ -118,8 +118,10 @@ def call_payment_api(payload): @frappe.whitelist() -def run_token_payment(invoice, token, cardholder_name=None, billing_zip=None): +def run_token_payment(invoice, token, cardholder_name=None, billing_zip=None, save_autopay=0): + inv = frappe.get_doc("Sales Invoice", invoice) + customer = frappe.get_doc("Customer", inv.customer) url = "https://secure.nmi.com/api/transact.php" @@ -138,12 +140,13 @@ def run_token_payment(invoice, token, cardholder_name=None, billing_zip=None): or "" ) - # Name Split + # Name split parts = customer_name.strip().split(" ", 1) first_name = parts[0] last_name = parts[1] if len(parts) > 1 else "." - data = { + + sale_data = { "security_key": frappe.conf.get("nmi_security_key"), "type": "sale", "payment_token": token, @@ -153,72 +156,110 @@ def run_token_payment(invoice, token, cardholder_name=None, billing_zip=None): "first_name": first_name, "last_name": last_name, "email": inv.contact_email or "", - "zip": billing_zip, } - response = requests.post(url, data=data) - result = urllib.parse.parse_qs(response.text) + sale_response = requests.post(url, data=sale_data) + sale_result = urllib.parse.parse_qs(sale_response.text) - frappe.logger("payments").info(f"NMI RESPONSE: {response.text}") + frappe.logger("payments").info(f"NMI SALE RESPONSE: {sale_response.text}") - success = result.get("response", ["0"])[0] - transaction_id = result.get("transactionid", [""])[0] + success = sale_result.get("response", ["0"])[0] + transaction_id = sale_result.get("transactionid", [""])[0] - if success == "1": - create_payment_entry( - invoice=invoice, - amount=inv.outstanding_amount, - transaction_id=transaction_id, - mode_of_payment="Credit Card" - ) + if success != "1": + return { + "success": False, + "error": sale_result.get("responsetext", ["Error"])[0] + } - return {"success": True} + # Create payment entry + create_payment_entry( + invoice=invoice, + amount=inv.outstanding_amount, + transaction_id=transaction_id, + mode_of_payment="Credit Card" + ) + + vault_id = None + + # Vault (if checked) + if cint(save_autopay): + vault_data = { + "security_key": frappe.conf.get("nmi_security_key"), + "type": "add_customer", + "payment_token": token, + + "first_name": first_name, + "last_name": last_name, + "zip": billing_zip, + + "customer_vault": "add_customer" + } + + try: + vault_response = requests.post(url, data=vault_data, timeout=30) + vault_result = urllib.parse.parse_qs(vault_response.text) + + frappe.logger("payments").info(f"NMI VAULT RESPONSE: {vault_response.text}") + + vault_success = vault_result.get("response", ["0"])[0] + vault_id = vault_result.get("customer_vault_id", [""])[0] + + if vault_success == "1" and vault_id: + customer.custom_auto_pay_id = vault_id + customer.custom_auto_pay_status = 1 + customer.save(ignore_permissions=True) + + except Exception: + frappe.log_error(frappe.get_traceback(), "Vault Creation Failed") return { - "success": False, - "error": result.get("responsetext", ["Error"])[0] + "success": True, + "vault_id": vault_id } @frappe.whitelist() def save_to_autopay(customer, token, cardholder_name=None, billing_zip=None): - import requests - import urllib.parse - inv_customer = frappe.get_doc("Customer", customer) + cust = frappe.get_doc("Customer", customer) - # Fallback logic - customer_name = ( + # Priority: Form input → Customer record → fallback + final_name = ( cardholder_name - or inv_customer.customer_name + or cust.customer_name + or cust.name or "Customer" ).strip() - billing_zip = ( + # ZIP fallback chain + final_zip = ( billing_zip - or inv_customer.get("billing_zip") - or inv_customer.get("pincode") + or cust.get("billing_zip") + or cust.get("pincode") or "" ) - # name split - if " " in customer_name: - first_name, last_name = customer_name.split(" ", 1) - else: - first_name = customer_name - last_name = "." + # Optional but recommended fields + email = cust.get("email_id") or "" + + # Safe name split + parts = final_name.split(" ", 1) + first_name = parts[0] + last_name = parts[1] if len(parts) > 1 else "." data = { "security_key": frappe.conf.get("nmi_security_key"), "type": "add_customer", + "payment_token": token, + "customer_vault": "add_customer", "first_name": first_name, "last_name": last_name, + "email": email, - "zip": billing_zip, - - "customer_vault": "add_customer" + "zip": final_zip, } try: @@ -230,7 +271,10 @@ def save_to_autopay(customer, token, cardholder_name=None, billing_zip=None): result = urllib.parse.parse_qs(response.text) - frappe.logger("payments").info(f"NMI VAULT RESPONSE: {response.text}") + frappe.logger("payments").info({ + "vault_request": data, + "vault_response": response.text + }) success = result.get("response", ["0"])[0] vault_id = result.get("customer_vault_id", [""])[0] @@ -241,14 +285,25 @@ def save_to_autopay(customer, token, cardholder_name=None, billing_zip=None): return {"success": False, "error": "Vault request failed"} if success == "1" and vault_id: - # Save to customer - inv_customer.custom_auto_pay_id = vault_id - inv_customer.custom_auto_pay_status = 1 - inv_customer.save(ignore_permissions=True) - return {"success": True} + # Save vault ID to ERP Customer + cust.custom_auto_pay_id = vault_id + cust.custom_auto_pay_status = 1 - return {"success": False, "error": message} + cust.custom_auto_pay_name = final_name + cust.custom_auto_pay_zip = final_zip + + cust.save(ignore_permissions=True) + + return { + "success": True, + "vault_id": vault_id + } + + return { + "success": False, + "error": message + } @frappe.whitelist(allow_guest=True) diff --git a/ns_app/public/js/sales_invoice.js b/ns_app/public/js/sales_invoice.js index 4562ee2..7d29c2e 100644 --- a/ns_app/public/js/sales_invoice.js +++ b/ns_app/public/js/sales_invoice.js @@ -141,6 +141,13 @@ function open_manual_payment_form(frm) { +