* did a bit of cleanup via pre-commit * added custom field creation/deletion via hooks * add scheduler event for razorpaypull/2/head
@@ -1,3 +1 @@ | |||
__version__ = '0.0.1' | |||
__version__ = "0.0.1" |
@@ -64,12 +64,12 @@ app_license = "MIT" | |||
# ------------ | |||
# before_install = "pay.install.before_install" | |||
# after_install = "pay.install.after_install" | |||
after_install = "payments.utils.make_custom_fields" | |||
# Uninstallation | |||
# ------------ | |||
# before_uninstall = "pay.uninstall.before_uninstall" | |||
before_uninstall = "payments.utils.delete_custom_fields" | |||
# after_uninstall = "pay.uninstall.after_uninstall" | |||
# Desk Notifications | |||
@@ -94,9 +94,9 @@ app_license = "MIT" | |||
# --------------- | |||
# Override standard doctype classes | |||
# override_doctype_class = { | |||
# "ToDo": "custom_app.overrides.CustomToDo" | |||
# } | |||
override_doctype_class = { | |||
"Web Form": "payments.overrides.paymentwebform.PaymentWebForm" | |||
} | |||
# Document Events | |||
# --------------- | |||
@@ -113,23 +113,11 @@ app_license = "MIT" | |||
# Scheduled Tasks | |||
# --------------- | |||
# scheduler_events = { | |||
# "all": [ | |||
# "pay.tasks.all" | |||
# ], | |||
# "daily": [ | |||
# "pay.tasks.daily" | |||
# ], | |||
# "hourly": [ | |||
# "pay.tasks.hourly" | |||
# ], | |||
# "weekly": [ | |||
# "pay.tasks.weekly" | |||
# ], | |||
# "monthly": [ | |||
# "pay.tasks.monthly" | |||
# ], | |||
# } | |||
scheduler_events = { | |||
"all": [ | |||
"payments.payment_gateways.doctype.razorpay_settings.razorpay_settings.capture_payment", | |||
], | |||
} | |||
# Testing | |||
# ------- | |||
@@ -7,10 +7,12 @@ import braintree | |||
import frappe | |||
from frappe import _ | |||
from frappe.integrations.utils import create_payment_gateway, create_request_log | |||
from frappe.integrations.utils import create_request_log | |||
from frappe.model.document import Document | |||
from frappe.utils import call_hook_method, get_url | |||
from payments.utils import create_payment_gateway | |||
class BraintreeSettings(Document): | |||
supported_currencies = [ | |||
@@ -157,7 +159,9 @@ class BraintreeSettings(Document): | |||
def on_update(self): | |||
create_payment_gateway( | |||
"Braintree-" + self.gateway_name, settings="Braintree Settings", controller=self.gateway_name | |||
"Braintree-" + self.gateway_name, | |||
settings="Braintree Settings", | |||
controller=self.gateway_name, | |||
) | |||
call_hook_method("payment_gateway_enabled", gateway="Braintree-" + self.gateway_name) | |||
@@ -237,7 +241,8 @@ class BraintreeSettings(Document): | |||
self.integration_request.db_set("status", "Failed", update_modified=False) | |||
for error in result.errors.deep_errors: | |||
error_log = frappe.log_error( | |||
"code: " + str(error.code) + " | message: " + str(error.message), "Braintree Payment Error" | |||
"code: " + str(error.code) + " | message: " + str(error.message), | |||
"Braintree Payment Error", | |||
) | |||
self.integration_request.db_set("error", error_log.error, update_modified=False) | |||
@@ -8,7 +8,7 @@ | |||
Example: | |||
from frappe.integrations.utils import get_payment_gateway_controller | |||
from payments.utils import get_payment_gateway_controller | |||
controller = get_payment_gateway_controller("PayPal") | |||
controller().validate_transaction_currency(currency) | |||
@@ -69,10 +69,12 @@ import pytz | |||
import frappe | |||
from frappe import _ | |||
from frappe.integrations.utils import create_payment_gateway, create_request_log, make_post_request | |||
from frappe.integrations.utils import create_request_log, make_post_request | |||
from frappe.model.document import Document | |||
from frappe.utils import call_hook_method, cint, get_datetime, get_url | |||
from payments.utils import create_payment_gateway | |||
api_path = "/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings" | |||
@@ -179,7 +181,10 @@ class PayPalSettings(Document): | |||
return_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" | |||
kwargs.update( | |||
{"token": response.get("TOKEN")[0], "correlation_id": response.get("CORRELATIONID")[0]} | |||
{ | |||
"token": response.get("TOKEN")[0], | |||
"correlation_id": response.get("CORRELATIONID")[0], | |||
} | |||
) | |||
create_request_log(kwargs, service_name="PayPal", name=kwargs["token"]) | |||
@@ -373,7 +378,9 @@ def create_recurring_profile(token, payerid): | |||
} | |||
) | |||
status_changed_to = "Completed" if data.get("starting_immediately") or updating else "Verified" | |||
status_changed_to = ( | |||
"Completed" if data.get("starting_immediately") or updating else "Verified" | |||
) | |||
starts_at = get_datetime(subscription_details.get("start_date")) or frappe.utils.now_datetime() | |||
starts_at = starts_at.replace(tzinfo=pytz.timezone(frappe.utils.get_time_zone())).astimezone( | |||
@@ -433,7 +440,11 @@ def get_redirect_uri(doc, token, payerid): | |||
def manage_recurring_payment_profile_status(profile_id, action, args, url): | |||
args.update( | |||
{"METHOD": "ManageRecurringPaymentsProfileStatus", "PROFILEID": profile_id, "ACTION": action} | |||
{ | |||
"METHOD": "ManageRecurringPaymentsProfileStatus", | |||
"PROFILEID": profile_id, | |||
"ACTION": action, | |||
} | |||
) | |||
response = make_post_request(url, data=args) | |||
@@ -442,7 +453,9 @@ def manage_recurring_payment_profile_status(profile_id, action, args, url): | |||
# thus could not cancel the subscription. | |||
# thus raise an exception only if the error code is not equal to 11556 | |||
if response.get("ACK")[0] != "Success" and response.get("L_ERRORCODE0", [])[0] != "11556": | |||
if ( | |||
response.get("ACK")[0] != "Success" and response.get("L_ERRORCODE0", [])[0] != "11556" | |||
): | |||
frappe.throw(_("Failed while amending subscription")) | |||
@@ -491,7 +504,10 @@ def validate_ipn_request(data): | |||
params, url = doc.get_paypal_params_and_url() | |||
params.update( | |||
{"METHOD": "GetRecurringPaymentsProfileDetails", "PROFILEID": data.get("recurring_payment_id")} | |||
{ | |||
"METHOD": "GetRecurringPaymentsProfileDetails", | |||
"PROFILEID": data.get("recurring_payment_id"), | |||
} | |||
) | |||
params = urlencode(params) | |||
@@ -6,14 +6,15 @@ from urllib.parse import urlencode | |||
import requests | |||
from paytmchecksum import generateSignature, verifySignature | |||
import frappe | |||
from frappe import _ | |||
from frappe.integrations.utils import create_payment_gateway, create_request_log | |||
from frappe.integrations.utils import create_request_log | |||
from frappe.model.document import Document | |||
from frappe.utils import call_hook_method, cint, cstr, flt, get_request_site_address, get_url | |||
from frappe.utils.password import get_decrypted_password | |||
from payments.utils import create_payment_gateway | |||
class PaytmSettings(Document): | |||
supported_currencies = ["INR"] | |||
@@ -8,7 +8,7 @@ | |||
Example: | |||
from frappe.integrations.utils import get_payment_gateway_controller | |||
from payments.utils import get_payment_gateway_controller | |||
controller = get_payment_gateway_controller("Razorpay") | |||
controller().validate_transaction_currency(currency) | |||
@@ -64,19 +64,16 @@ import hmac | |||
import json | |||
from urllib.parse import urlencode | |||
import razorpay | |||
import frappe | |||
import razorpay | |||
from frappe import _ | |||
from frappe.integrations.utils import ( | |||
create_payment_gateway, | |||
create_request_log, | |||
make_get_request, | |||
make_post_request, | |||
) | |||
from frappe.integrations.utils import (create_request_log, make_get_request, | |||
make_post_request) | |||
from frappe.model.document import Document | |||
from frappe.utils import call_hook_method, cint, get_timestamp, get_url | |||
from payments.utils import create_payment_gateway | |||
class RazorpaySettings(Document): | |||
supported_currencies = ["INR"] | |||
@@ -97,7 +94,10 @@ class RazorpaySettings(Document): | |||
try: | |||
make_get_request( | |||
url="https://api.razorpay.com/v1/payments", | |||
auth=(self.api_key, self.get_password(fieldname="api_secret", raise_exception=False)), | |||
auth=( | |||
self.api_key, | |||
self.get_password(fieldname="api_secret", raise_exception=False), | |||
), | |||
) | |||
except Exception: | |||
frappe.throw(_("Seems API Key or API Secret is wrong !!!")) | |||
@@ -123,7 +123,9 @@ class RazorpaySettings(Document): | |||
"quantity": 1 (The total amount is calculated as item.amount * quantity) | |||
} | |||
""" | |||
url = "https://api.razorpay.com/v1/subscriptions/{}/addons".format(kwargs.get("subscription_id")) | |||
url = "https://api.razorpay.com/v1/subscriptions/{}/addons".format( | |||
kwargs.get("subscription_id") | |||
) | |||
try: | |||
if not frappe.conf.converted_rupee_to_paisa: | |||
@@ -137,7 +139,9 @@ class RazorpaySettings(Document): | |||
headers={"content-type": "application/json"}, | |||
) | |||
if not resp.get("id"): | |||
frappe.log_error(message=str(resp), title="Razorpay Failed while creating subscription") | |||
frappe.log_error( | |||
message=str(resp), title="Razorpay Failed while creating subscription" | |||
) | |||
except Exception: | |||
frappe.log_error() | |||
# failed | |||
@@ -176,7 +180,9 @@ class RazorpaySettings(Document): | |||
frappe.flags.status = "created" | |||
return kwargs | |||
else: | |||
frappe.log_error(message=str(resp), title="Razorpay Failed while creating subscription") | |||
frappe.log_error( | |||
message=str(resp), title="Razorpay Failed while creating subscription" | |||
) | |||
except Exception: | |||
frappe.log_error() | |||
@@ -214,7 +220,10 @@ class RazorpaySettings(Document): | |||
try: | |||
order = make_post_request( | |||
"https://api.razorpay.com/v1/orders", | |||
auth=(self.api_key, self.get_password(fieldname="api_secret", raise_exception=False)), | |||
auth=( | |||
self.api_key, | |||
self.get_password(fieldname="api_secret", raise_exception=False), | |||
), | |||
data=payment_options, | |||
) | |||
order["integration_request"] = integration_request.name | |||
@@ -387,7 +396,9 @@ def capture_payment(is_sandbox=False, sanbox_response=None): | |||
if resp.get("status") == "authorized": | |||
resp = make_post_request( | |||
"https://api.razorpay.com/v1/payments/{}/capture".format(data.get("razorpay_payment_id")), | |||
"https://api.razorpay.com/v1/payments/{}/capture".format( | |||
data.get("razorpay_payment_id") | |||
), | |||
auth=(settings.api_key, settings.api_secret), | |||
data={"amount": data.get("amount")}, | |||
) | |||
@@ -417,7 +428,9 @@ def get_order(doctype, docname): | |||
# Do not use run_method here as it fails silently | |||
return doc.get_razorpay_order() | |||
except AttributeError: | |||
frappe.log_error(frappe.get_traceback(), _("Controller method get_razorpay_order missing")) | |||
frappe.log_error( | |||
frappe.get_traceback(), _("Controller method get_razorpay_order missing") | |||
) | |||
frappe.throw(_("Could not create Razorpay order. Please contact Administrator")) | |||
@@ -5,15 +5,12 @@ from urllib.parse import urlencode | |||
import frappe | |||
from frappe import _ | |||
from frappe.integrations.utils import ( | |||
create_payment_gateway, | |||
create_request_log, | |||
make_get_request, | |||
make_post_request, | |||
) | |||
from frappe.integrations.utils import create_request_log, make_get_request | |||
from frappe.model.document import Document | |||
from frappe.utils import call_hook_method, cint, flt, get_url | |||
from payments.utils import create_payment_gateway | |||
class StripeSettings(Document): | |||
supported_currencies = [ | |||
@@ -153,7 +150,9 @@ class StripeSettings(Document): | |||
def on_update(self): | |||
create_payment_gateway( | |||
"Stripe-" + self.gateway_name, settings="Stripe Settings", controller=self.gateway_name | |||
"Stripe-" + self.gateway_name, | |||
settings="Stripe Settings", | |||
controller=self.gateway_name, | |||
) | |||
call_hook_method("payment_gateway_enabled", gateway="Stripe-" + self.gateway_name) | |||
if not self.flags.ignore_mandatory: | |||
@@ -6,7 +6,7 @@ import json | |||
import frappe | |||
from frappe import _ | |||
from frappe.integrations.doctype.braintree_settings.braintree_settings import ( | |||
get_client_token, | |||
get_client_token, | |||
get_gateway_controller, | |||
) | |||
from frappe.utils import flt | |||
@@ -46,7 +46,9 @@ def get_context(context): | |||
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."), | |||
_( | |||
"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 | |||
@@ -59,6 +61,8 @@ def make_payment(payload_nonce, data, reference_doctype, reference_docname): | |||
data.update({"payload_nonce": payload_nonce}) | |||
gateway_controller = get_gateway_controller(reference_docname) | |||
data = frappe.get_doc("Braintree Settings", gateway_controller).create_payment_request(data) | |||
data = frappe.get_doc("Braintree Settings", gateway_controller).create_payment_request( | |||
data | |||
) | |||
frappe.db.commit() | |||
return data |
@@ -0,0 +1,6 @@ | |||
from utils import ( | |||
get_payment_gateway_controller, | |||
create_payment_gateway, | |||
make_custom_fields, | |||
delete_custom_fields, | |||
) |
@@ -0,0 +1,158 @@ | |||
import click | |||
import frappe | |||
from frappe import _ | |||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields | |||
def get_payment_gateway_controller(payment_gateway): | |||
"""Return payment gateway controller""" | |||
gateway = frappe.get_doc("Payment Gateway", payment_gateway) | |||
if gateway.gateway_controller is None: | |||
try: | |||
return frappe.get_doc(f"{payment_gateway} Settings") | |||
except Exception: | |||
frappe.throw(_("{0} Settings not found").format(payment_gateway)) | |||
else: | |||
try: | |||
return frappe.get_doc(gateway.gateway_settings, gateway.gateway_controller) | |||
except Exception: | |||
frappe.throw(_("{0} Settings not found").format(payment_gateway)) | |||
@frappe.whitelist(allow_guest=True, xss_safe=True) | |||
def get_checkout_url(**kwargs): | |||
try: | |||
if kwargs.get("payment_gateway"): | |||
doc = frappe.get_doc("{} Settings".format(kwargs.get("payment_gateway"))) | |||
return doc.get_payment_url(**kwargs) | |||
else: | |||
raise Exception | |||
except Exception: | |||
frappe.respond_as_web_page( | |||
_("Something went wrong"), | |||
_( | |||
"Looks like something is wrong with this site's payment gateway configuration. No payment has been made." | |||
), | |||
indicator_color="red", | |||
http_status_code=frappe.ValidationError.http_status_code, | |||
) | |||
def create_payment_gateway(gateway, settings=None, controller=None): | |||
# 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, | |||
"gateway_settings": settings, | |||
"gateway_controller": controller, | |||
} | |||
) | |||
payment_gateway.insert(ignore_permissions=True) | |||
def make_custom_fields(): | |||
if not frappe.get_meta("Web Form").has_field("payments"): | |||
click.secho("* Installing Payment Custom Fields in Web Form") | |||
create_custom_fields({ | |||
'Web Form': [ | |||
{ | |||
"fieldname": "payments_tab", | |||
"fieldtype": "Tab Break", | |||
"label": "Payments", | |||
"insert_after": "custom_css" | |||
}, | |||
{ | |||
"default": "0", | |||
"fieldname": "accept_payment", | |||
"fieldtype": "Check", | |||
"label": "Accept Payment", | |||
"insert_after": "payments" | |||
}, | |||
{ | |||
"depends_on": "accept_payment", | |||
"fieldname": "payment_gateway", | |||
"fieldtype": "Link", | |||
"label": "Payment Gateway", | |||
"options": "Payment Gateway", | |||
"insert_after": "accept_payment" | |||
}, | |||
{ | |||
"default": "Buy Now", | |||
"depends_on": "accept_payment", | |||
"fieldname": "payment_button_label", | |||
"fieldtype": "Data", | |||
"label": "Button Label", | |||
"insert_after": "payment_gateway" | |||
}, | |||
{ | |||
"depends_on": "accept_payment", | |||
"fieldname": "payment_button_help", | |||
"fieldtype": "Text", | |||
"label": "Button Help", | |||
"insert_after": "payment_button_label" | |||
}, | |||
{ | |||
"fieldname": "payments_cb", | |||
"fieldtype": "Column Break", | |||
"insert_after": "payment_button_help" | |||
}, | |||
{ | |||
"default": "0", | |||
"depends_on": "accept_payment", | |||
"fieldname": "amount_based_on_field", | |||
"fieldtype": "Check", | |||
"label": "Amount Based On Field", | |||
"insert_after": "payments_cb" | |||
}, | |||
{ | |||
"depends_on": "eval:doc.accept_payment && doc.amount_based_on_field", | |||
"fieldname": "amount_field", | |||
"fieldtype": "Select", | |||
"label": "Amount Field", | |||
"insert_after": "amount_based_on_field" | |||
}, | |||
{ | |||
"depends_on": "eval:doc.accept_payment && !doc.amount_based_on_field", | |||
"fieldname": "amount", | |||
"fieldtype": "Currency", | |||
"label": "Amount", | |||
"insert_after": "amount_field" | |||
}, | |||
{ | |||
"depends_on": "accept_payment", | |||
"fieldname": "currency", | |||
"fieldtype": "Link", | |||
"label": "Currency", | |||
"options": "Currency", | |||
"insert_after": "amount" | |||
} | |||
] | |||
}) | |||
def delete_custom_fields(): | |||
if frappe.get_meta("Web Form").has_field("payments_tab"): | |||
click.secho("* Uninstalling Payment Custom Fields from Web Form") | |||
fieldnames = ( | |||
"payments_tab", | |||
"accept_payment", | |||
"payment_gateway", | |||
"payment_button_label", | |||
"payment_button_help", | |||
"payments_cb", | |||
"amount_field", | |||
"amount_based_on_field", | |||
"amount", | |||
"currency" | |||
) | |||
for fieldname in fieldnames: | |||
frappe.db.delete( | |||
"Custom Field", | |||
{"name": "Web Form-" + fieldname} | |||
) |
@@ -1,4 +1,4 @@ | |||
from setuptools import setup, find_packages | |||
from setuptools import find_packages, setup | |||
with open("requirements.txt") as f: | |||
install_requires = f.read().strip().split("\n") | |||
@@ -15,5 +15,5 @@ setup( | |||
packages=find_packages(), | |||
zip_safe=False, | |||
include_package_data=True, | |||
install_requires=install_requires | |||
install_requires=install_requires, | |||
) |