Gocardless integration (#13008)
* GoCardless integration * Addition of a method for determining if the email should be sent or not * Correction for Tests * Codacy fix * Documents moved to ERPNext * Codacy fix * Codacy fixes * Remove method where not necessary and replace with hasattr
This commit is contained in:
parent
3fecbb98c5
commit
bc7a549fdb
@ -34,7 +34,7 @@ class PaymentRequest(Document):
|
|||||||
frappe.throw(_("Transaction currency must be same as Payment Gateway currency"))
|
frappe.throw(_("Transaction currency must be same as Payment Gateway currency"))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
send_mail = True
|
send_mail = self.payment_gateway_validation()
|
||||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
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") \
|
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
||||||
@ -58,6 +58,16 @@ class PaymentRequest(Document):
|
|||||||
si = si.insert(ignore_permissions=True)
|
si = si.insert(ignore_permissions=True)
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
|
def payment_gateway_validation(self):
|
||||||
|
try:
|
||||||
|
controller = get_payment_gateway_controller(self.payment_gateway)
|
||||||
|
if hasattr(controller, 'on_payment_request_submission'):
|
||||||
|
return controller.on_payment_request_submission(self)
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def set_payment_request_url(self):
|
def set_payment_request_url(self):
|
||||||
if self.payment_account:
|
if self.payment_account:
|
||||||
self.payment_url = self.get_payment_url()
|
self.payment_url = self.get_payment_url()
|
||||||
|
22
erpnext/config/erpnext_integrations.py
Normal file
22
erpnext/config/erpnext_integrations.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"label": _("Payments"),
|
||||||
|
"icon": "fa fa-star",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "GoCardless Settings",
|
||||||
|
"description": _("GoCardless payment gateway settings"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "GoCardless Mandate",
|
||||||
|
"description": _("GoCardless SEPA Mandate"),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
BIN
erpnext/docs/assets/img/setup/integrations/gocardless_coa.png
Normal file
BIN
erpnext/docs/assets/img/setup/integrations/gocardless_coa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
@ -0,0 +1,44 @@
|
|||||||
|
# Setting up GoCardless
|
||||||
|
|
||||||
|
To setup GoCardless, go to `Explore > Integrations > GoCardless Settings`
|
||||||
|
|
||||||
|
## Setup GoCardless
|
||||||
|
|
||||||
|
To enable GoCardless in your ERPNext account, you need to configure the following parameters and Access Token and optionally (but highly recommended), a Webhooks Secret key.
|
||||||
|
|
||||||
|
|
||||||
|
You can setup several GoCardless payment gateways if needed. The choice of payment gateway account will determine which GoCardless account is used for the payment.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
On enabling service, the system will create a Payment Gateway record and an Account head in chart of account with account type as Bank.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It will also create a payment gateway account. You can change the default bank account if needed and create a template for the payment request.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
After configuring the Payment Gateway Account, your system is able to accept online payments through GoCardless.
|
||||||
|
|
||||||
|
## SEPA Payments Flow
|
||||||
|
|
||||||
|
When a new payment SEPA payment in initiated, the customer is asked to enter his IBAN (or local account number) and to validate a SEPA mandate.
|
||||||
|
|
||||||
|
Upon validation of the mandate, a payment request is sent to GoCardless and processed.
|
||||||
|
|
||||||
|
If the customer has already a valid SEPA mandate, when instead of sending a payment request to the customer, the payment request is directly sent to GoCardless without the need for the customer to validate it.
|
||||||
|
The customer will only receive a confirmation email from GoCardless informing him that a payment has been processed.
|
||||||
|
|
||||||
|
|
||||||
|
## Mandate cancellation
|
||||||
|
|
||||||
|
You can setup a Webhook in GoCardless to automatically disabled cancelled or expired mandates in ERPNext.
|
||||||
|
|
||||||
|
The Endpoint URL of your webhook should be: https://yoursite.com/api/method/erpnext.erpnext_integrations.doctype.gocardless_settings.webhooks
|
||||||
|
|
||||||
|
In this case do not forget to configure your Webhooks Secret Key in your GoCardless account settings in ERPNext.
|
||||||
|
|
||||||
|
|
||||||
|
## Supported transaction currencies
|
||||||
|
"EUR", "DKK", "GBP", "SEK"
|
0
erpnext/erpnext_integrations/doctype/__init__.py
Normal file
0
erpnext/erpnext_integrations/doctype/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('GoCardless Mandate', {
|
||||||
|
});
|
@ -0,0 +1,184 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"autoname": "field:mandate",
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2018-02-08 11:33:15.721919",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Disabled",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Customer",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Customer",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "mandate",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Mandate",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "gocardless_customer",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "GoCardless Customer",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 0,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2018-02-11 12:28:03.183095",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "ERPNext Integrations",
|
||||||
|
"name": "GoCardless Mandate",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1,
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2018, Frappe Technologies and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class GoCardlessMandate(Document):
|
||||||
|
pass
|
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// rename this file from _test_[name] to test_[name] to activate
|
||||||
|
// and remove above this line
|
||||||
|
|
||||||
|
QUnit.test("test: GoCardless Mandate", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// number of asserts
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
// insert a new GoCardless Mandate
|
||||||
|
() => frappe.tests.make('GoCardless Mandate', [
|
||||||
|
// values to be set
|
||||||
|
{key: 'value'}
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
assert.equal(cur_frm.doc.key, 'value');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2018, Frappe Technologies and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestGoCardlessMandate(unittest.TestCase):
|
||||||
|
pass
|
@ -0,0 +1,70 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2018, Frappe Technologies and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
import json
|
||||||
|
import hmac
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
@frappe.whitelist(allow_guest=True)
|
||||||
|
def webhooks():
|
||||||
|
r = frappe.request
|
||||||
|
if not r:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not authenticate_signature(r):
|
||||||
|
raise frappe.AuthenticationError
|
||||||
|
|
||||||
|
gocardless_events = json.loads(r.get_data()) or []
|
||||||
|
for event in gocardless_events["events"]:
|
||||||
|
set_status(event)
|
||||||
|
|
||||||
|
return 200
|
||||||
|
def set_status(event):
|
||||||
|
resource_type = event.get("resource_type", {})
|
||||||
|
|
||||||
|
if resource_type == "mandates":
|
||||||
|
set_mandate_status(event)
|
||||||
|
|
||||||
|
def set_mandate_status(event):
|
||||||
|
mandates = []
|
||||||
|
if isinstance(event["links"], (list,)):
|
||||||
|
for link in event["links"]:
|
||||||
|
mandates.append(link["mandate"])
|
||||||
|
else:
|
||||||
|
mandates.append(event["links"]["mandate"])
|
||||||
|
|
||||||
|
if event["action"] == "pending_customer_approval" or event["action"] == "pending_submission" or event["action"] == "submitted" or event["action"] == "active":
|
||||||
|
disabled = 0
|
||||||
|
else:
|
||||||
|
disabled = 1
|
||||||
|
|
||||||
|
for mandate in mandates:
|
||||||
|
frappe.db.set_value("GoCardless Mandate", mandate, "disabled", disabled)
|
||||||
|
|
||||||
|
def authenticate_signature(r):
|
||||||
|
"""Returns True if the received signature matches the generated signature"""
|
||||||
|
received_signature = frappe.get_request_header("Webhook-Signature")
|
||||||
|
|
||||||
|
if not received_signature:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for key in get_webhook_keys():
|
||||||
|
computed_signature = hmac.new(key.encode("utf-8"), r.get_data(), hashlib.sha256).hexdigest()
|
||||||
|
if hmac.compare_digest(str(received_signature), computed_signature):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_webhook_keys():
|
||||||
|
def _get_webhook_keys():
|
||||||
|
webhook_keys = [d.webhooks_secret for d in frappe.get_all("GoCardless Settings", fields=["webhooks_secret"],) if d.webhooks_secret]
|
||||||
|
|
||||||
|
return webhook_keys
|
||||||
|
|
||||||
|
return frappe.cache().get_value("gocardless_webhooks_secret", _get_webhook_keys)
|
||||||
|
|
||||||
|
def clear_cache():
|
||||||
|
frappe.cache().delete_value("gocardless_webhooks_secret")
|
@ -0,0 +1,5 @@
|
|||||||
|
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('GoCardless Settings', {
|
||||||
|
});
|
@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"autoname": "field:gateway_name",
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2018-02-06 16:11:10.028249",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "gateway_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Payment Gateway Name",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "section_break_2",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "access_token",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Access Token",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "webhooks_secret",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Webhooks Secret",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "use_sandbox",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Use Sandbox",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 0,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2018-02-12 14:18:47.209114",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "ERPNext Integrations",
|
||||||
|
"name": "GoCardless Settings",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1,
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2018, Frappe Technologies and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
import gocardless_pro
|
||||||
|
from frappe import _
|
||||||
|
from six.moves.urllib.parse import urlencode
|
||||||
|
from frappe.utils import get_url, call_hook_method, flt, cint
|
||||||
|
from frappe.integrations.utils import create_request_log, create_payment_gateway
|
||||||
|
|
||||||
|
class GoCardlessSettings(Document):
|
||||||
|
supported_currencies = ["EUR", "DKK", "GBP", "SEK"]
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self.initialize_client()
|
||||||
|
|
||||||
|
def initialize_client(self):
|
||||||
|
self.environment = self.get_environment()
|
||||||
|
try:
|
||||||
|
self.client = gocardless_pro.Client(
|
||||||
|
access_token=self.access_token,
|
||||||
|
environment=self.environment
|
||||||
|
)
|
||||||
|
return self.client
|
||||||
|
except Exception as e:
|
||||||
|
frappe.throw(e)
|
||||||
|
|
||||||
|
def on_update(self):
|
||||||
|
create_payment_gateway('GoCardless-' + self.gateway_name, settings='GoCardLess Settings', controller=self.gateway_name)
|
||||||
|
call_hook_method('payment_gateway_enabled', gateway='GoCardless-' + self.gateway_name)
|
||||||
|
|
||||||
|
def on_payment_request_submission(self, data):
|
||||||
|
if data.reference_doctype != "Fees":
|
||||||
|
customer_data = frappe.db.get_value(data.reference_doctype, data.reference_name, ["company", "customer_name"], as_dict=1)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"amount": flt(data.grand_total, data.precision("grand_total")),
|
||||||
|
"title": customer_data.company.encode("utf-8"),
|
||||||
|
"description": data.subject.encode("utf-8"),
|
||||||
|
"reference_doctype": data.doctype,
|
||||||
|
"reference_docname": data.name,
|
||||||
|
"payer_email": data.email_to or frappe.session.user,
|
||||||
|
"payer_name": customer_data.customer_name,
|
||||||
|
"order_id": data.name,
|
||||||
|
"currency": data.currency
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_mandate = self.check_mandate_validity(data)
|
||||||
|
if valid_mandate is not None:
|
||||||
|
data.update(valid_mandate)
|
||||||
|
|
||||||
|
self.create_payment_request(data)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def check_mandate_validity(self, data):
|
||||||
|
|
||||||
|
if frappe.db.exists("GoCardless Mandate", dict(customer=data.get('payer_name'), disabled=0)):
|
||||||
|
registered_mandate = frappe.db.get_value("GoCardless Mandate", dict(customer=data.get('payer_name'), disabled=0), 'mandate')
|
||||||
|
self.initialize_client()
|
||||||
|
mandate = self.client.mandates.get(registered_mandate)
|
||||||
|
|
||||||
|
if mandate.status=="pending_customer_approval" or mandate.status=="pending_submission" or mandate.status=="submitted" or mandate.status=="active":
|
||||||
|
return {"mandate": registered_mandate}
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_environment(self):
|
||||||
|
if self.use_sandbox:
|
||||||
|
return 'sandbox'
|
||||||
|
else:
|
||||||
|
return 'live'
|
||||||
|
|
||||||
|
def validate_transaction_currency(self, currency):
|
||||||
|
if currency not in self.supported_currencies:
|
||||||
|
frappe.throw(_("Please select another payment method. Stripe does not support transactions in currency '{0}'").format(currency))
|
||||||
|
|
||||||
|
def get_payment_url(self, **kwargs):
|
||||||
|
return get_url("./integrations/gocardless_checkout?{0}".format(urlencode(kwargs)))
|
||||||
|
|
||||||
|
def create_payment_request(self, data):
|
||||||
|
self.data = frappe._dict(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.integration_request = create_request_log(self.data, "Host", "GoCardless")
|
||||||
|
return self.create_charge_on_gocardless()
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
frappe.log_error(frappe.get_traceback())
|
||||||
|
return{
|
||||||
|
"redirect_to": frappe.redirect_to_message(_('Server Error'), _("There seems to be an issue with the server's GoCardless configuration. Don't worry, in case of failure, the amount will get refunded to your account.")),
|
||||||
|
"status": 401
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_charge_on_gocardless(self):
|
||||||
|
redirect_to = self.data.get('redirect_to') or None
|
||||||
|
redirect_message = self.data.get('redirect_message') or None
|
||||||
|
|
||||||
|
reference_doc = frappe.get_doc(self.data.get('reference_doctype'), self.data.get('reference_docname'))
|
||||||
|
self.initialize_client()
|
||||||
|
|
||||||
|
try:
|
||||||
|
payment = self.client.payments.create(
|
||||||
|
params={
|
||||||
|
"amount" : cint(reference_doc.grand_total * 100),
|
||||||
|
"currency" : reference_doc.currency,
|
||||||
|
"links" : {
|
||||||
|
"mandate": self.data.get('mandate')
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"reference_doctype": reference_doc.doctype,
|
||||||
|
"reference_document": reference_doc.name
|
||||||
|
}
|
||||||
|
}, headers={
|
||||||
|
'Idempotency-Key' : self.data.get('reference_docname'),
|
||||||
|
})
|
||||||
|
|
||||||
|
if payment.status=="pending_submission" or payment.status=="pending_customer_approval" or payment.status=="submitted":
|
||||||
|
self.integration_request.db_set('status', 'Authorized', update_modified=False)
|
||||||
|
self.flags.status_changed_to = "Completed"
|
||||||
|
self.integration_request.db_set('output', payment.status, update_modified=False)
|
||||||
|
|
||||||
|
elif payment.status=="confirmed" or payment.status=="paid_out":
|
||||||
|
self.integration_request.db_set('status', 'Completed', update_modified=False)
|
||||||
|
self.flags.status_changed_to = "Completed"
|
||||||
|
self.integration_request.db_set('output', payment.status, update_modified=False)
|
||||||
|
|
||||||
|
elif payment.status=="cancelled" or payment.status=="customer_approval_denied" or payment.status=="charged_back":
|
||||||
|
self.integration_request.db_set('status', 'Cancelled', update_modified=False)
|
||||||
|
frappe.log_error(_("Payment Cancelled. Please check your GoCardless Account for more details"), "GoCardless Payment Error")
|
||||||
|
self.integration_request.db_set('error', payment.status, update_modified=False)
|
||||||
|
else:
|
||||||
|
self.integration_request.db_set('status', 'Failed', update_modified=False)
|
||||||
|
frappe.log_error(_("Payment Failed. Please check your GoCardless Account for more details"), "GoCardless Payment Error")
|
||||||
|
self.integration_request.db_set('error', payment.status, update_modified=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(e, "GoCardless Payment Error")
|
||||||
|
|
||||||
|
if self.flags.status_changed_to == "Completed":
|
||||||
|
status = 'Completed'
|
||||||
|
if self.data.reference_doctype and self.data.reference_docname:
|
||||||
|
custom_redirect_to = None
|
||||||
|
try:
|
||||||
|
custom_redirect_to = frappe.get_doc(self.data.reference_doctype,
|
||||||
|
self.data.reference_docname).run_method("on_payment_authorized", self.flags.status_changed_to)
|
||||||
|
except Exception:
|
||||||
|
frappe.log_error(frappe.get_traceback())
|
||||||
|
|
||||||
|
if custom_redirect_to:
|
||||||
|
redirect_to = custom_redirect_to
|
||||||
|
|
||||||
|
redirect_url = redirect_to
|
||||||
|
else:
|
||||||
|
status = 'Error'
|
||||||
|
redirect_url = 'payment-failed'
|
||||||
|
|
||||||
|
if redirect_message:
|
||||||
|
redirect_url += '&' + urlencode({'redirect_message': redirect_message})
|
||||||
|
|
||||||
|
redirect_url = get_url(redirect_url)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"redirect_to": redirect_url,
|
||||||
|
"status": status
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_gateway_controller(doc):
|
||||||
|
payment_request = frappe.get_doc("Payment Request", doc)
|
||||||
|
gateway_controller = frappe.db.get_value("Payment Gateway", payment_request.payment_gateway, "gateway_controller")
|
||||||
|
return gateway_controller
|
||||||
|
|
||||||
|
def gocardless_initialization(doc):
|
||||||
|
gateway_controller = get_gateway_controller(doc)
|
||||||
|
settings = frappe.get_doc("GoCardless Settings", gateway_controller)
|
||||||
|
client = settings.initialize_client()
|
||||||
|
return client
|
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// rename this file from _test_[name] to test_[name] to activate
|
||||||
|
// and remove above this line
|
||||||
|
|
||||||
|
QUnit.test("test: GoCardless Settings", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// number of asserts
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
// insert a new GoCardless Settings
|
||||||
|
() => frappe.tests.make('GoCardless Settings', [
|
||||||
|
// values to be set
|
||||||
|
{key: 'value'}
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
assert.equal(cur_frm.doc.key, 'value');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2018, Frappe Technologies and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestGoCardlessSettings(unittest.TestCase):
|
||||||
|
pass
|
@ -0,0 +1,24 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
var data = {{ frappe.form_dict | json }};
|
||||||
|
var doctype = "{{ reference_doctype }}"
|
||||||
|
var docname = "{{ reference_docname }}"
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.templates.pages.integrations.gocardless_checkout.check_mandate",
|
||||||
|
freeze: true,
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
"data": JSON.stringify(data),
|
||||||
|
"reference_doctype": doctype,
|
||||||
|
"reference_docname": docname
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
window.location.href = r.message.redirect_to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -0,0 +1,24 @@
|
|||||||
|
$(document).ready(function() {
|
||||||
|
var redirect_flow_id = "{{ redirect_flow_id }}";
|
||||||
|
var doctype = "{{ reference_doctype }}";
|
||||||
|
var docname = "{{ reference_docname }}";
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.templates.pages.integrations.gocardless_confirmation.confirm_payment",
|
||||||
|
freeze: true,
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
"redirect_flow_id": redirect_flow_id,
|
||||||
|
"reference_doctype": doctype,
|
||||||
|
"reference_docname": docname
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
window.location.href = r.message.redirect_to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
0
erpnext/templates/pages/integrations/__init__.py
Normal file
0
erpnext/templates/pages/integrations/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% extends "templates/web.html" %}
|
||||||
|
|
||||||
|
{% block title %} Payment {% endblock %}
|
||||||
|
|
||||||
|
{%- block header -%}{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>{% include "templates/includes/integrations/gocardless_checkout.js" %}</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{%- block page_content -%}
|
||||||
|
<p class='lead text-center centered'>
|
||||||
|
<span class='gocardless-loading'>{{ _("Loading Payment System") }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% endblock %}
|
76
erpnext/templates/pages/integrations/gocardless_checkout.py
Normal file
76
erpnext/templates/pages/integrations/gocardless_checkout.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import flt
|
||||||
|
import json
|
||||||
|
from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import gocardless_initialization, get_gateway_controller
|
||||||
|
from frappe.utils import get_url
|
||||||
|
|
||||||
|
no_cache = 1
|
||||||
|
no_sitemap = 1
|
||||||
|
|
||||||
|
expected_keys = ('amount', 'title', 'description', 'reference_doctype', 'reference_docname',
|
||||||
|
'payer_name', 'payer_email', 'order_id', 'currency')
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
context.no_cache = 1
|
||||||
|
|
||||||
|
# all these keys exist in form_dict
|
||||||
|
if not (set(expected_keys) - set(frappe.form_dict.keys())):
|
||||||
|
for key in expected_keys:
|
||||||
|
context[key] = frappe.form_dict[key]
|
||||||
|
|
||||||
|
context['amount'] = flt(context['amount'])
|
||||||
|
|
||||||
|
gateway_controller = get_gateway_controller(context.reference_docname)
|
||||||
|
context['header_img'] = frappe.db.get_value("GoCardless Settings", gateway_controller, "header_img")
|
||||||
|
|
||||||
|
else:
|
||||||
|
frappe.redirect_to_message(_('Some information is missing'),
|
||||||
|
_('Looks like someone sent you to an incomplete URL. Please ask them to look into it.'))
|
||||||
|
frappe.local.flags.redirect_location = frappe.local.response.location
|
||||||
|
raise frappe.Redirect
|
||||||
|
|
||||||
|
@frappe.whitelist(allow_guest=True)
|
||||||
|
def check_mandate(data, reference_doctype, reference_docname):
|
||||||
|
data = json.loads(data)
|
||||||
|
|
||||||
|
client = gocardless_initialization(reference_docname)
|
||||||
|
|
||||||
|
payer = frappe.get_doc("Customer", data["payer_name"])
|
||||||
|
|
||||||
|
if payer.customer_type == "Individual" and payer.customer_primary_contact is not None:
|
||||||
|
primary_contact = frappe.get_doc("Contact", payer.customer_primary_contact)
|
||||||
|
prefilled_customer = {
|
||||||
|
"company_name": payer.name,
|
||||||
|
"given_name": primary_contact.first_name,
|
||||||
|
"family_name": primary_contact.last_name,
|
||||||
|
}
|
||||||
|
if primary_contact.email_id is not None:
|
||||||
|
prefilled_customer.update({"email": primary_contact.email_id})
|
||||||
|
else:
|
||||||
|
prefilled_customer.update({"email": frappe.session.user})
|
||||||
|
|
||||||
|
else:
|
||||||
|
prefilled_customer = {
|
||||||
|
"company_name": payer.name,
|
||||||
|
"email": frappe.session.user
|
||||||
|
}
|
||||||
|
|
||||||
|
success_url = get_url("./integrations/gocardless_confirmation?reference_doctype=" + reference_doctype + "&reference_docname=" + reference_docname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
redirect_flow = client.redirect_flows.create(params={
|
||||||
|
"description": _("Pay {0} {1}".format(data['amount'], data['currency'])),
|
||||||
|
"session_token": frappe.session.user,
|
||||||
|
"success_redirect_url": success_url,
|
||||||
|
"prefilled_customer": prefilled_customer
|
||||||
|
})
|
||||||
|
|
||||||
|
return {"redirect_to": redirect_flow.redirect_url}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(e, "GoCardless Payment Error")
|
||||||
|
return {"redirect_to": '/integrations/payment-failed'}
|
@ -0,0 +1,16 @@
|
|||||||
|
{% extends "templates/web.html" %}
|
||||||
|
|
||||||
|
{% block title %} Payment {% endblock %}
|
||||||
|
|
||||||
|
{%- block header -%}{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>{% include "templates/includes/integrations/gocardless_confirmation.js" %}</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{%- block page_content -%}
|
||||||
|
<p class='lead text-center centered'>
|
||||||
|
<span class='gocardless-loading'>{{ _("Payment Confirmation") }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -0,0 +1,85 @@
|
|||||||
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from erpnext.erpnext_integrations.doctype.gocardless_settings.gocardless_settings import gocardless_initialization, get_gateway_controller
|
||||||
|
|
||||||
|
no_cache = 1
|
||||||
|
no_sitemap = 1
|
||||||
|
|
||||||
|
expected_keys = ('redirect_flow_id', 'reference_doctype', 'reference_docname')
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
context.no_cache = 1
|
||||||
|
|
||||||
|
# all these keys exist in form_dict
|
||||||
|
if not (set(expected_keys) - set(frappe.form_dict.keys())):
|
||||||
|
for key in expected_keys:
|
||||||
|
context[key] = frappe.form_dict[key]
|
||||||
|
|
||||||
|
else:
|
||||||
|
frappe.redirect_to_message(_('Some information is missing'),
|
||||||
|
_('Looks like someone sent you to an incomplete URL. Please ask them to look into it.'))
|
||||||
|
frappe.local.flags.redirect_location = frappe.local.response.location
|
||||||
|
raise frappe.Redirect
|
||||||
|
|
||||||
|
@frappe.whitelist(allow_guest=True)
|
||||||
|
def confirm_payment(redirect_flow_id, reference_doctype, reference_docname):
|
||||||
|
|
||||||
|
client = gocardless_initialization(reference_docname)
|
||||||
|
|
||||||
|
try:
|
||||||
|
redirect_flow = client.redirect_flows.complete(
|
||||||
|
redirect_flow_id,
|
||||||
|
params={
|
||||||
|
"session_token": frappe.session.user
|
||||||
|
})
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"mandate": redirect_flow.links.mandate,
|
||||||
|
"customer": redirect_flow.links.customer,
|
||||||
|
"redirect_to": redirect_flow.confirmation_url,
|
||||||
|
"redirect_message": "Mandate successfully created",
|
||||||
|
"reference_doctype": reference_doctype,
|
||||||
|
"reference_docname": reference_docname
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
create_mandate(data)
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(e, "GoCardless Mandate Registration Error")
|
||||||
|
|
||||||
|
gateway_controller = get_gateway_controller(reference_docname)
|
||||||
|
frappe.get_doc("GoCardless Settings", gateway_controller).create_payment_request(data)
|
||||||
|
|
||||||
|
return {"redirect_to": redirect_flow.confirmation_url}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(e, "GoCardless Payment Error")
|
||||||
|
return {"redirect_to": '/integrations/payment-failed'}
|
||||||
|
|
||||||
|
|
||||||
|
def create_mandate(data):
|
||||||
|
data = frappe._dict(data)
|
||||||
|
frappe.logger().debug(data)
|
||||||
|
|
||||||
|
mandate = data.get('mandate')
|
||||||
|
|
||||||
|
if frappe.db.exists("GoCardless Mandate", mandate):
|
||||||
|
return
|
||||||
|
|
||||||
|
else:
|
||||||
|
reference_doc = frappe.db.get_value(data.get('reference_doctype'), data.get('reference_docname'), ["reference_doctype", "reference_name"], as_dict=1)
|
||||||
|
erpnext_customer = frappe.db.get_value(reference_doc.reference_doctype, reference_doc.reference_name, ["customer_name"], as_dict=1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
frappe.get_doc({
|
||||||
|
"doctype": "GoCardless Mandate",
|
||||||
|
"mandate": mandate,
|
||||||
|
"customer": erpnext_customer.customer_name,
|
||||||
|
"gocardless_customer": data.get('customer')
|
||||||
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
frappe.log_error(frappe.get_traceback())
|
@ -4,3 +4,4 @@ pygithub
|
|||||||
googlemaps
|
googlemaps
|
||||||
python-stdnum
|
python-stdnum
|
||||||
braintree
|
braintree
|
||||||
|
gocardless_pro
|
||||||
|
Loading…
x
Reference in New Issue
Block a user