import requests import frappe # Checks to see if customer has Autopay on @frappe.whitelist() def check_autopay(customer): cust = frappe.get_doc("Customer", customer) return { "autopay_enabled": bool(cust.auto_pay), "autopay_id": cust.auto_pay_id if cust.auto_pay else None } # Creates payload to send through API, checks for success, then makes a call to create a payment entry. @frappe.whitelist() def run_autopay_payment(invoice, autopay_id, amount): payload = { "autopay_id": autopay_id, "amount": amount, "invoice": invoice } response = call_payment_api(payload) # Handle gateway failure clearly if not response.get("success"): frappe.throw(response.get("error", "Payment failed")) # Create ERPNext payment record payment_entry = create_payment_entry( invoice=invoice, amount=amount, transaction_id=response.get("transaction_id") ) return { "message": "AutoPay payment successful", "payment_entry": payment_entry } # Call's Crystal CLear's API and Runs an AutoPay transaction using NMI / Crystal Clear using customer_vault_id (autopay_id) def call_payment_api(payload): url = "https://crystalclear.transactiongateway.com/api/transact.php" # Store these in site_config.json or environment variables api_username = frappe.conf.get("nmi_username") api_password = frappe.conf.get("nmi_password") if not api_username or not api_password: frappe.throw("Payment gateway credentials not configured") data = { "username": api_username, "password": api_password, "type": "sale", "customer_vault_id": payload["autopay_id"], "amount": payload["amount"], "orderid": payload["invoice"], "response": "json" } try: response = requests.post(url, data=data, timeout=30) response.raise_for_status() result = response.json() except Exception as e: frappe.log_error(frappe.get_traceback(), "NMI Payment API Error") frappe.throw("Payment processor unreachable") # NMI success condition if result.get("response") == "1": return { "success": True, "transaction_id": result.get("transactionid") } # Failure case return { "success": False, "error": result.get("responsetext", "Payment failed") } # Auto creates payment entry in ERP Next def create_payment_entry(invoice, amount, transaction_id=None): inv = frappe.get_doc("Sales Invoice", invoice) pe = frappe.new_doc("Payment Entry") pe.payment_type = "Receive" pe.party_type = "Customer" pe.party = inv.customer pe.paid_amount = amount pe.received_amount = amount pe.reference_no = transaction_id pe.reference_date = frappe.utils.nowdate() pe.append("references", { "reference_doctype": "Sales Invoice", "reference_name": invoice, "allocated_amount": amount }) pe.insert() pe.submit() return pe.name