diff --git a/ns_app/api/payments.py b/ns_app/api/payments.py index 6b27073..020233a 100644 --- a/ns_app/api/payments.py +++ b/ns_app/api/payments.py @@ -115,23 +115,49 @@ def call_payment_api(payload): "success": False, "error": message } - + @frappe.whitelist() -def get_collect_checkout_url(invoice): +def run_token_payment(invoice, token): inv = frappe.get_doc("Sales Invoice", invoice) - security_key = frappe.conf.get("nmi_security_key") + url = "https://secure.nmi.com/api/transact.php" - if not security_key: - frappe.throw("NMI security key not configured") + # Get and parse customer name. + customer_name = inv.customer_name or inv.customer or "Customer" + parts = customer_name.split(" ", 1) + first_name = parts[0] + last_name = parts[1] if len(parts) > 1 else "." - return ( - "https://crystalclear.transactiongateway.com/collect/checkout" - f"?security_key={security_key}" - f"&amount={inv.outstanding_amount}" - f"&orderid={inv.name}" - ) + data = { + "security_key": frappe.conf.get("nmi_security_key"), + "type": "sale", + "payment_token": token, + "amount": inv.outstanding_amount, + "orderid": inv.name, + + "first_name": first_name, + "last_name": last_name, + "email": inv.contact_email or "", + } + + response = requests.post(url, data=data) + result = urllib.parse.parse_qs(response.text) + frappe.logger("payments").info(f"NMI RESPONSE: {response.text}") + success = result.get("response", ["0"])[0] + transaction_id = 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" + ) + + return {"success": True} + + return {"success": False, "error": result.get("responsetext", ["Error"])[0]} @frappe.whitelist(allow_guest=True) diff --git a/ns_app/public/js/sales_invoice.js b/ns_app/public/js/sales_invoice.js index dc76cf4..037097a 100644 --- a/ns_app/public/js/sales_invoice.js +++ b/ns_app/public/js/sales_invoice.js @@ -124,66 +124,118 @@ function open_manual_payment_form(frm) { fields: [ { fieldtype: "HTML", - fieldname: "loader", + fieldname: "payment_form", options: ` -
- Loading secure checkout… +
+
+
+
+ +
` - }, - { - fieldtype: "HTML", - fieldname: "payment_form", - options: `
` } ], primary_action_label: "Close", primary_action() { dialog.hide(); - frm.reload_doc(); } }); dialog.show(); - frappe.call({ - method: "ns_app.api.payments.get_collect_checkout_url", - args: { invoice: frm.doc.name }, - callback(r) { - if (!r.message) { - frappe.msgprint("Unable to start payment"); - return; - } + // Load NMI Collect.js + const script = document.createElement("script"); + script.src = "https://secure.nmi.com/token/Collect.js"; - const iframe_html = ` - - `; + script.setAttribute( + "data-tokenization-key", + "HKx4XR-G549wT-8bZ2YJ-3kbG28" +); - document.getElementById("iframe_container").innerHTML = iframe_html; - } - }); + script.onload = () => { + console.log("CollectJS loaded"); - // Poll invoice every 5 seconds - const poller = setInterval(() => { - frappe.call({ - method: "frappe.client.get", - args: { - doctype: "Sales Invoice", - name: frm.doc.name + CollectJS.configure({ + variant: "inline", + styleSniffer: true, + + fields: { + ccnumber: { + selector: "#cc_number", + placeholder: "Card Number" + }, + ccexp: { + selector: "#cc_exp", + placeholder: "MM / YY" + }, + cvv: { + selector: "#cc_cvv", + placeholder: "CVV" + } }, - callback(r) { - if (r.message && r.message.outstanding_amount <= 0) { - clearInterval(poller); - dialog.hide(); - frm.reload_doc(); - frappe.msgprint("Payment successful!"); + + + callback: function (response) { + console.log("Token response:", response); + + if (response.token) { + run_token_payment(frm, response.token, dialog); + } else { + frappe.msgprint("Payment failed to tokenize"); } } }); - }, 5000); + + console.log("Fields:", document.querySelector("#cc_number")); + + setTimeout(() => { + const btn = dialog.$wrapper.find("#pay_btn")[0]; + + if (!btn) { + console.error("Pay button not found"); + return; + } + + btn.onclick = function () { + console.log("Pay clicked"); + + frappe.show_alert({ + message: "Processing payment...", + indicator: "blue" + }); + + CollectJS.startPaymentRequest(); + }; + }, 300); + + }; + + document.body.appendChild(script); +} + + +function run_token_payment(frm, token, dialog) { + frappe.call({ + method: "ns_app.api.payments.run_token_payment", + args: { + invoice: frm.doc.name, + token: token + }, + callback(r) { + if (r.message?.success) { + dialog.hide(); + frm.reload_doc(); + + frappe.show_alert({ + message: "Payment successful", + indicator: "green" + }); + } else { + frappe.msgprint(r.message?.error || "Payment failed"); + } + } + }); }