Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set ERPNext Payment Request status from GoCardless Webhook #75

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions payments/payment_gateways/doctype/gocardless_settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ def webhooks():

def set_status(event):
resource_type = event.get("resource_type", {})
reference_doctype = event.get("resource_metadata", {}).get('reference_doctype')

if resource_type == "mandates":
set_mandate_status(event)
if resource_type == "payments" and reference_doctype == "Payment Request":
set_payment_request_status(event)


def set_mandate_status(event):
Expand All @@ -54,6 +57,22 @@ def set_mandate_status(event):
frappe.db.set_value("GoCardless Mandate", mandate, "disabled", disabled)


def set_payment_request_status(event):
event_action = event["action"]
payment_request = event["resource_metadata"]["reference_document"]
doc = frappe.get_doc("Payment Request", payment_request)
if event_action == "confirmed" and doc.status != "Paid":
doc.set_as_paid()
if event_action == "cancelled" and doc.status != "Cancelled":
doc.set_as_cancelled()
if event_action == "failed" and doc.status != "Failed":
doc.db_set("status", "Failed")
try: # failed reason is a field in ERPNext version 16+, so it may not exist in the database
doc.db_set("failed_reason", event["details"]["description"])
except:
pass


def authenticate_signature(r):
"""Returns True if the received signature matches the generated signature"""
received_signature = frappe.get_request_header("Webhook-Signature")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ def on_payment_request_submission(self, data):
"payer_name": customer_data.customer_name,
"order_id": data.name,
"currency": data.currency,
"charge_date": data.transaction_date or frappe.utils.nowdate(),
}

valid_mandate = self.check_mandate_validity(data)
valid_mandate, next_possible_charge_date = self.check_mandate_validity(data)
if valid_mandate is not None:
data.update(valid_mandate)

data["charge_date"] = max(data.get("charge_date"), next_possible_charge_date)
self.create_payment_request(data)
return False
else:
Expand All @@ -78,11 +79,11 @@ def check_mandate_validity(self, data):
or mandate.status == "submitted"
or mandate.status == "active"
):
return {"mandate": registered_mandate}
return {"mandate": registered_mandate}, mandate.next_possible_charge_date
else:
return None
return None, None
else:
return None
return None, None

def get_environment(self):
if self.use_sandbox:
Expand Down Expand Up @@ -133,6 +134,7 @@ def create_charge_on_gocardless(self):
payment = self.client.payments.create(
params={
"amount": cint(reference_doc.grand_total * 100),
"charge_date": self.data.get("charge_date"),
"currency": reference_doc.currency,
"links": {"mandate": self.data.get("mandate")},
"metadata": {
Expand All @@ -145,35 +147,32 @@ def create_charge_on_gocardless(self):
},
)

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("Gocardless payment cancelled")
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("Gocardless payment failed")
self.integration_request.db_set("error", payment.status, update_modified=False)
self.integration_request.db_set("output", payment.api_response._response._content.decode())

match payment.status:
case "pending_submission" | "pending_customer_approval" | "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)

case "confirmed" | "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)

case "cancelled" | "customer_approval_denied" | "charged_back":
self.integration_request.db_set("status", "Cancelled", update_modified=False)
frappe.log_error("Gocardless payment cancelled")
self.integration_request.db_set("error", payment.status, update_modified=False)

case _:
self.integration_request.db_set("status", "Failed", update_modified=False)
frappe.log_error("Gocardless payment failed")
self.integration_request.db_set("error", payment.status, update_modified=False)

except Exception as e:
frappe.log_error("GoCardless Payment Error")
self.integration_request.db_set("error", str(e))
frappe.log_error("GoCardless Payment Error", str(e))

if self.flags.status_changed_to == "Completed":
status = "Completed"
Expand Down
Loading