[enhancement] modify payment request to get payment url (#6097)
* [enhancement] modify payment request to get payment url
* redirect to payment url if payment initiated via shopping cart
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 30d043f..84a8e82 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -6,10 +6,11 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import flt, get_url, nowdate
+from frappe.utils import flt, nowdate, get_url
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry, get_company_defaults
+from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller
class PaymentRequest(Document):
def validate(self):
@@ -25,7 +26,7 @@
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if self.payment_account and ref_doc.currency != frappe.db.get_value("Account", self.payment_account, "account_currency"):
frappe.throw(_("Transaction currency must be same as Payment Gateway currency"))
-
+
def on_submit(self):
send_mail = True
self.make_communication_entry()
@@ -35,17 +36,13 @@
send_mail = False
if send_mail and not self.flags.mute_email:
- self.send_payment_request()
+ self.set_payment_request_url()
self.send_email()
def on_cancel(self):
+ self.check_if_payment_entry_exists()
self.set_as_cancelled()
- def get_payment_url(self):
- """ This is blanck method to trigger hooks call from individual payment gateway app
- which will return respective payment gateway"""
- pass
-
def make_invoice(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
@@ -54,20 +51,39 @@
si = si.insert(ignore_permissions=True)
si.submit()
- def send_payment_request(self):
+ def set_payment_request_url(self):
if self.payment_account:
- self.payment_url = get_url("/api/method/erpnext.accounts.doctype.payment_request.payment_request.generate_payment_request?name={0}".format(self.name))
+ self.payment_url = self.get_payment_url()
if self.payment_url:
self.db_set('payment_url', self.payment_url)
if self.payment_url or not self.payment_gateway_account:
self.db_set('status', 'Initiated')
+
+ def get_payment_url(self):
+ data = frappe.db.get_value(self.reference_doctype, self.reference_name,
+ ["company", "customer_name"], as_dict=1)
+
+ controller = get_integration_controller(self.payment_gateway, setup=False)
+ controller.validate_transaction_currency(self.currency)
+
+ return controller.get_payment_url(**{
+ "amount": self.grand_total,
+ "title": data.company,
+ "description": self.subject,
+ "reference_doctype": "Payment Request",
+ "reference_docname": self.name,
+ "payer_email": self.email_to or frappe.session.user,
+ "payer_name": data.customer_name,
+ "order_id": self.name,
+ "currency": self.currency
+ })
def set_as_paid(self):
if frappe.session.user == "Guest":
frappe.set_user("Administrator")
-
+
payment_entry = self.create_payment_entry()
self.make_invoice()
@@ -141,6 +157,13 @@
def set_as_cancelled(self):
self.db_set("status", "Cancelled")
+
+ def check_if_payment_entry_exists(self):
+ if self.status == "Paid":
+ payment_entry = frappe.db.sql_list("""select parent from `tabPayment Entry Reference`
+ where reference_name=%s""", self.reference_name)
+ if payment_entry:
+ frappe.throw(_("Payment Entry already exists"), title=_('Error'))
def make_communication_entry(self):
"""Make communication entry"""
@@ -156,7 +179,33 @@
def get_payment_success_url(self):
return self.payment_success_url
+
+ def on_payment_authorized(self, status=None):
+ if not status:
+ return
+
+ shopping_cart_settings = frappe.get_doc("Shopping Cart Settings")
+ if status in ["Authorized", "Completed"]:
+ redirect_to = None
+ self.run_method("set_as_paid")
+
+ # if shopping cart enabled and in session
+ if (shopping_cart_settings.enabled and hasattr(frappe.local, "session")
+ and frappe.local.session.user != "Guest"):
+
+ success_url = shopping_cart_settings.payment_success_url
+ if success_url:
+ redirect_to = ({
+ "Orders": "orders",
+ "Invoices": "invoices",
+ "My Account": "me"
+ }).get(success_url, "me")
+ else:
+ redirect_to = get_url("/orders/{0}".format(self.reference_name))
+
+ return redirect_to
+
@frappe.whitelist(allow_guest=True)
def make_payment_request(**args):
"""Make payment request"""
@@ -201,8 +250,9 @@
pr.submit()
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
- generate_payment_request(pr.name)
frappe.db.commit()
+ frappe.local.response["type"] = "redirect"
+ frappe.local.response["location"] = pr.get_payment_url()
if not args.cart:
return pr
@@ -256,10 +306,6 @@
}
@frappe.whitelist(allow_guest=True)
-def generate_payment_request(name):
- frappe.get_doc("Payment Request", name).run_method("get_payment_url")
-
-@frappe.whitelist(allow_guest=True)
def resend_payment_email(docname):
return frappe.get_doc("Payment Request", docname).send_email()
@@ -278,6 +324,7 @@
doc = frappe.get_doc("Payment Request", payment_request_name)
if doc.status != "Paid":
doc.db_set('status', 'Paid')
+ frappe.db.commit()
def get_dummy_message(use_dummy_message=True):
return """
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 6efb288..9b28cbd 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -570,3 +570,58 @@
each["balance_in_account_currency"] = flt(get_balance_on(each.get("value")))
return acc
+
+def create_payment_gateway_and_account(gateway):
+ create_payment_gateway(gateway)
+ create_payment_gateway_account(gateway)
+
+def create_payment_gateway(gateway):
+ # NOTE: we don't translate Payment Gateway name because it is an internal doctype
+ if not frappe.db.exists("Payment Gateway", gateway):
+ payment_gateway = frappe.get_doc({
+ "doctype": "Payment Gateway",
+ "gateway": gateway
+ })
+ payment_gateway.insert(ignore_permissions=True)
+
+def create_payment_gateway_account(gateway):
+ from erpnext.setup.setup_wizard.setup_wizard import create_bank_account
+
+ company = frappe.db.get_value("Global Defaults", None, "default_company")
+ if not company:
+ return
+
+ # NOTE: we translate Payment Gateway account name because that is going to be used by the end user
+ bank_account = frappe.db.get_value("Account", {"account_name": _(gateway), "company": company},
+ ["name", 'account_currency'], as_dict=1)
+
+ if not bank_account:
+ # check for untranslated one
+ bank_account = frappe.db.get_value("Account", {"account_name": gateway, "company": company},
+ ["name", 'account_currency'], as_dict=1)
+
+ if not bank_account:
+ # try creating one
+ bank_account = create_bank_account({"company_name": company, "bank_account": _(gateway)})
+
+ if not bank_account:
+ frappe.msgprint(_("Payment Gateway Account not created, please create one manually."))
+ return
+
+ # if payment gateway account exists, return
+ if frappe.db.exists("Payment Gateway Account",
+ {"payment_gateway": gateway, "currency": bank_account.account_currency}):
+ return
+
+ try:
+ frappe.get_doc({
+ "doctype": "Payment Gateway Account",
+ "is_default": 1,
+ "payment_gateway": gateway,
+ "payment_account": bank_account.name,
+ "currency": bank_account.account_currency
+ }).insert(ignore_permissions=True)
+
+ except frappe.DuplicateEntryError:
+ # already exists, due to a reinstall?
+ pass