ci: add precommit and semantic commit workflowpull/5/head
@@ -0,0 +1,74 @@ | |||
[flake8] | |||
ignore = | |||
B001, | |||
B007, | |||
B009, | |||
B010, | |||
B950, | |||
E101, | |||
E111, | |||
E114, | |||
E116, | |||
E117, | |||
E121, | |||
E122, | |||
E123, | |||
E124, | |||
E125, | |||
E126, | |||
E127, | |||
E128, | |||
E131, | |||
E201, | |||
E202, | |||
E203, | |||
E211, | |||
E221, | |||
E222, | |||
E223, | |||
E224, | |||
E225, | |||
E226, | |||
E228, | |||
E231, | |||
E241, | |||
E242, | |||
E251, | |||
E261, | |||
E262, | |||
E265, | |||
E266, | |||
E271, | |||
E272, | |||
E273, | |||
E274, | |||
E301, | |||
E302, | |||
E303, | |||
E305, | |||
E306, | |||
E402, | |||
E501, | |||
E502, | |||
E701, | |||
E702, | |||
E703, | |||
E741, | |||
F401, | |||
F403, | |||
F405, | |||
W191, | |||
W291, | |||
W292, | |||
W293, | |||
W391, | |||
W503, | |||
W504, | |||
E711, | |||
E129, | |||
F841, | |||
E713, | |||
E712, | |||
max-line-length = 200 |
@@ -1,5 +1,5 @@ | |||
name: CI | |||
name: Server | |||
on: | |||
push: | |||
@@ -0,0 +1,54 @@ | |||
name: Linter | |||
on: | |||
pull_request: | |||
workflow_dispatch: | |||
push: | |||
branches: [ develop ] | |||
permissions: | |||
contents: read | |||
concurrency: | |||
group: commitcheck-frappe-${{ github.event.number }} | |||
cancel-in-progress: true | |||
jobs: | |||
commit-lint: | |||
name: 'Semantic Commits' | |||
runs-on: ubuntu-latest | |||
if: github.event_name == 'pull_request' | |||
steps: | |||
- uses: actions/checkout@v3 | |||
with: | |||
fetch-depth: 200 | |||
- uses: actions/setup-node@v3 | |||
with: | |||
node-version: 16 | |||
check-latest: true | |||
- name: Check commit titles | |||
run: | | |||
npm install @commitlint/cli @commitlint/config-conventional | |||
npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} | |||
linter: | |||
name: 'Frappe Linter' | |||
runs-on: ubuntu-latest | |||
if: github.event_name == 'pull_request' | |||
steps: | |||
- uses: actions/checkout@v3 | |||
- uses: actions/setup-python@v4 | |||
with: | |||
python-version: '3.10' | |||
- uses: pre-commit/action@v3.0.0 | |||
- name: Download Semgrep rules | |||
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules | |||
- name: Run Semgrep rules | |||
run: | | |||
pip install semgrep==0.97.0 | |||
semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness |
@@ -0,0 +1,45 @@ | |||
exclude: 'node_modules|.git' | |||
default_stages: [commit] | |||
fail_fast: false | |||
repos: | |||
- repo: https://github.com/pre-commit/pre-commit-hooks | |||
rev: v4.3.0 | |||
hooks: | |||
- id: trailing-whitespace | |||
files: "payments.*" | |||
exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" | |||
- id: check-yaml | |||
- id: no-commit-to-branch | |||
args: ['--branch', 'develop'] | |||
- id: check-merge-conflict | |||
- id: check-ast | |||
- id: check-json | |||
- id: check-toml | |||
- id: check-yaml | |||
- id: debug-statements | |||
- repo: https://github.com/asottile/pyupgrade | |||
rev: v2.34.0 | |||
hooks: | |||
- id: pyupgrade | |||
args: ['--py310-plus'] | |||
- repo: https://github.com/adityahase/black | |||
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119 | |||
hooks: | |||
- id: black | |||
additional_dependencies: ['click==8.0.4'] | |||
- repo: https://gitlab.com/pycqa/flake8 | |||
rev: 3.9.2 | |||
hooks: | |||
- id: flake8 | |||
additional_dependencies: ['flake8-bugbear',] | |||
args: ['--config', '.github/helper/flake8.conf'] | |||
ci: | |||
autoupdate_schedule: weekly | |||
skip: [] | |||
submodules: false |
@@ -0,0 +1,25 @@ | |||
module.exports = { | |||
parserPreset: 'conventional-changelog-conventionalcommits', | |||
rules: { | |||
'subject-empty': [2, 'never'], | |||
'type-case': [2, 'always', 'lower-case'], | |||
'type-empty': [2, 'never'], | |||
'type-enum': [ | |||
2, | |||
'always', | |||
[ | |||
'build', | |||
'chore', | |||
'ci', | |||
'docs', | |||
'feat', | |||
'fix', | |||
'perf', | |||
'refactor', | |||
'revert', | |||
'style', | |||
'test', | |||
], | |||
], | |||
}, | |||
}; |
@@ -1,10 +1,5 @@ | |||
from frappe import _ | |||
def get_data(): | |||
return [ | |||
{ | |||
"module_name": "Payments", | |||
"type": "module", | |||
"label": _("Payments") | |||
} | |||
] | |||
return [{"module_name": "Payments", "type": "module", "label": _("Payments")}] |
@@ -6,5 +6,6 @@ Configuration for docs | |||
# headline = "App that does everything" | |||
# sub_heading = "Yes, you got that right the first time, everything" | |||
def get_context(context): | |||
context.brand_html = "Payments" |
@@ -42,7 +42,7 @@ app_license = "MIT" | |||
# website user home page (by Role) | |||
# role_home_page = { | |||
# "Role": "home_page" | |||
# "Role": "home_page" | |||
# } | |||
# Generators | |||
@@ -107,7 +107,7 @@ override_doctype_class = { | |||
# "on_update": "method", | |||
# "on_cancel": "method", | |||
# "on_trash": "method" | |||
# } | |||
# } | |||
# } | |||
# Scheduled Tasks | |||
@@ -25,12 +25,13 @@ class PaymentWebForm(WebForm): | |||
if getattr(self, "accept_payment", False): | |||
controller = get_payment_gateway_controller(self.payment_gateway) | |||
title = "Payment for {0} {1}".format(doc.doctype, doc.name) | |||
title = f"Payment for {doc.doctype} {doc.name}" | |||
amount = self.amount | |||
if self.amount_based_on_field: | |||
amount = doc.get(self.amount_field) | |||
from decimal import Decimal | |||
if amount is None or Decimal(amount) <= 0: | |||
return frappe.utils.get_url(self.success_url or self.route) | |||
@@ -44,14 +45,13 @@ class PaymentWebForm(WebForm): | |||
"payer_name": frappe.utils.get_fullname(frappe.session.user), | |||
"order_id": doc.name, | |||
"currency": self.currency, | |||
"redirect_to": frappe.utils.get_url(self.success_url or self.route) | |||
"redirect_to": frappe.utils.get_url(self.success_url or self.route), | |||
} | |||
# Redirect the user to this url | |||
return controller.get_payment_url(**payment_details) | |||
@frappe.whitelist(allow_guest=True) | |||
@rate_limit(key="web_form", limit=5, seconds=60, methods=["POST"]) | |||
def accept(web_form, data, docname=None, for_payment=False): | |||
@@ -65,7 +65,7 @@ def accept(web_form, data, docname=None, for_payment=False): | |||
web_form = frappe.get_doc("Web Form", web_form) | |||
if data.name and not web_form.allow_edit: | |||
frappe.throw(_("You are not allowed to update this Web Form Document")) | |||
frappe.throw(frappe._("You are not allowed to update this Web Form Document")) | |||
frappe.flags.in_web_form = True | |||
meta = frappe.get_meta(data.doctype) | |||
@@ -109,7 +109,7 @@ def accept(web_form, data, docname=None, for_payment=False): | |||
else: | |||
# insert | |||
if web_form.login_required and frappe.session.user == "Guest": | |||
frappe.throw(_("You must login to submit this form")) | |||
frappe.throw(frappe._("You must login to submit this form")) | |||
ignore_mandatory = True if files else False | |||
@@ -4,7 +4,6 @@ | |||
from urllib.parse import urlencode | |||
import braintree | |||
import frappe | |||
from frappe import _ | |||
from frappe.integrations.utils import create_request_log | |||
@@ -225,7 +224,9 @@ class BraintreeSettings(Document): | |||
if result.is_success: | |||
self.integration_request.db_set("status", "Completed", update_modified=False) | |||
self.flags.status_changed_to = "Completed" | |||
self.integration_request.db_set("output", result.transaction.status, update_modified=False) | |||
self.integration_request.db_set( | |||
"output", result.transaction.status, update_modified=False | |||
) | |||
elif result.transaction: | |||
self.integration_request.db_set("status", "Failed", update_modified=False) | |||
@@ -65,9 +65,8 @@ More Details: | |||
import json | |||
from urllib.parse import urlencode | |||
import pytz | |||
import frappe | |||
import pytz | |||
from frappe import _ | |||
from frappe.integrations.utils import create_request_log, make_post_request | |||
from frappe.model.document import Document | |||
@@ -75,7 +74,9 @@ from frappe.utils import call_hook_method, cint, get_datetime, get_url | |||
from payments.utils import create_payment_gateway | |||
api_path = "/api/method/payments.payment_gateways.doctype.paypal_settings.paypal_settings" | |||
api_path = ( | |||
"/api/method/payments.payment_gateways.doctype.paypal_settings.paypal_settings" | |||
) | |||
class PayPalSettings(Document): | |||
@@ -176,7 +177,9 @@ class PayPalSettings(Document): | |||
response = self.execute_set_express_checkout(**kwargs) | |||
if self.paypal_sandbox or self.use_sandbox: | |||
return_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" | |||
return_url = ( | |||
"https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" | |||
) | |||
else: | |||
return_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" | |||
@@ -212,7 +215,9 @@ class PayPalSettings(Document): | |||
response = make_post_request(url, data=params.encode("utf-8")) | |||
if response.get("ACK")[0] != "Success": | |||
frappe.throw(_("Looks like something is wrong with this site's Paypal configuration.")) | |||
frappe.throw( | |||
_("Looks like something is wrong with this site's Paypal configuration.") | |||
) | |||
return response | |||
@@ -294,7 +299,9 @@ def get_express_checkout_details(token): | |||
) | |||
frappe.local.response["type"] = "redirect" | |||
frappe.local.response["location"] = get_redirect_uri(doc, token, response.get("PAYERID")[0]) | |||
frappe.local.response["location"] = get_redirect_uri( | |||
doc, token, response.get("PAYERID")[0] | |||
) | |||
except Exception: | |||
frappe.log_error(frappe.get_traceback()) | |||
@@ -360,7 +367,9 @@ def create_recurring_profile(token, payerid): | |||
if data.get("subscription_id"): | |||
if addons: | |||
updating = True | |||
manage_recurring_payment_profile_status(data["subscription_id"], "Cancel", params, url) | |||
manage_recurring_payment_profile_status( | |||
data["subscription_id"], "Cancel", params, url | |||
) | |||
params.update( | |||
{ | |||
@@ -382,10 +391,12 @@ def create_recurring_profile(token, payerid): | |||
"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( | |||
pytz.utc | |||
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(pytz.utc) | |||
# "PROFILESTARTDATE": datetime.utcfromtimestamp(get_timestamp(starts_at)).isoformat() | |||
params.update({"PROFILESTARTDATE": starts_at.isoformat()}) | |||
@@ -4,15 +4,21 @@ | |||
import json | |||
from urllib.parse import urlencode | |||
import requests | |||
from paytmchecksum import generateSignature, verifySignature | |||
import frappe | |||
import requests | |||
from frappe import _ | |||
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 import ( | |||
call_hook_method, | |||
cint, | |||
cstr, | |||
flt, | |||
get_request_site_address, | |||
get_url, | |||
) | |||
from frappe.utils.password import get_decrypted_password | |||
from paytmchecksum import generateSignature, verifySignature | |||
from payments.utils import create_payment_gateway | |||
@@ -46,7 +52,11 @@ def get_paytm_config(): | |||
paytm_config = frappe.db.get_singles_dict("Paytm Settings") | |||
paytm_config.update( | |||
dict(merchant_key=get_decrypted_password("Paytm Settings", "Paytm Settings", "merchant_key")) | |||
dict( | |||
merchant_key=get_decrypted_password( | |||
"Paytm Settings", "Paytm Settings", "merchant_key" | |||
) | |||
) | |||
) | |||
if cint(paytm_config.staging): | |||
@@ -110,7 +120,9 @@ def verify_transaction(**paytm_params): | |||
if paytm_params and paytm_config and paytm_checksum: | |||
# Verify checksum | |||
is_valid_checksum = verifySignature(paytm_params, paytm_config.merchant_key, paytm_checksum) | |||
is_valid_checksum = verifySignature( | |||
paytm_params, paytm_config.merchant_key, paytm_checksum | |||
) | |||
if is_valid_checksum and paytm_params.get("RESPCODE") == "01": | |||
verify_transaction_status(paytm_config, paytm_params["ORDERID"]) | |||
@@ -136,7 +148,9 @@ def verify_transaction_status(paytm_config, order_id): | |||
post_data = json.dumps(paytm_params) | |||
url = paytm_config.transaction_status_url | |||
response = requests.post(url, data=post_data, headers={"Content-type": "application/json"}).json() | |||
response = requests.post( | |||
url, data=post_data, headers={"Content-type": "application/json"} | |||
).json() | |||
finalize_request(order_id, response) | |||
@@ -8,7 +8,7 @@ from frappe import _ | |||
from frappe.utils import flt | |||
from payments.payment_gateways.doctype.braintree_settings.braintree_settings import ( | |||
get_client_token, | |||
get_client_token, | |||
get_gateway_controller, | |||
) | |||
@@ -18,7 +18,9 @@ def get_context(context): | |||
try: | |||
doc = frappe.get_doc("Integration Request", frappe.form_dict["order_id"]) | |||
context.payment_details = get_paytm_params(json.loads(doc.data), doc.name, paytm_config) | |||
context.payment_details = get_paytm_params( | |||
json.loads(doc.data), doc.name, paytm_config | |||
) | |||
context.url = paytm_config.url | |||
@@ -59,7 +59,9 @@ def get_api_key(): | |||
@frappe.whitelist(allow_guest=True) | |||
def make_payment(razorpay_payment_id, options, reference_doctype, reference_docname, token): | |||
def make_payment( | |||
razorpay_payment_id, options, reference_doctype, reference_docname, token | |||
): | |||
data = {} | |||
if isinstance(options, str): | |||
@@ -6,7 +6,9 @@ import frappe | |||
from frappe import _ | |||
from frappe.utils import cint, fmt_money | |||
from payments.payment_gateways.doctype.stripe_settings.stripe_settings import get_gateway_controller | |||
from payments.payment_gateways.doctype.stripe_settings.stripe_settings import ( | |||
get_gateway_controller, | |||
) | |||
no_cache = 1 | |||
@@ -31,7 +33,9 @@ def get_context(context): | |||
for key in expected_keys: | |||
context[key] = frappe.form_dict[key] | |||
gateway_controller = get_gateway_controller(context.reference_doctype, context.reference_docname) | |||
gateway_controller = get_gateway_controller( | |||
context.reference_doctype, context.reference_docname | |||
) | |||
context.publishable_key = get_api_key(context.reference_docname, gateway_controller) | |||
context.image = get_header_image(context.reference_docname, gateway_controller) | |||
@@ -48,14 +52,18 @@ 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 | |||
def get_api_key(doc, gateway_controller): | |||
publishable_key = frappe.db.get_value("Stripe Settings", gateway_controller, "publishable_key") | |||
publishable_key = frappe.db.get_value( | |||
"Stripe Settings", gateway_controller, "publishable_key" | |||
) | |||
if cint(frappe.form_dict.get("use_sandbox")): | |||
publishable_key = frappe.conf.sandbox_publishable_key | |||
@@ -1,7 +1,7 @@ | |||
from payments.utils.utils import ( | |||
before_install, | |||
create_payment_gateway, | |||
delete_custom_fields, | |||
get_payment_gateway_controller, | |||
make_custom_fields, | |||
before_install, | |||
create_payment_gateway, | |||
delete_custom_fields, | |||
get_payment_gateway_controller, | |||
make_custom_fields, | |||
) |
@@ -1,5 +1,4 @@ | |||
import click | |||
import frappe | |||
from frappe import _ | |||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields | |||
@@ -57,81 +56,83 @@ def make_custom_fields(): | |||
if not frappe.get_meta("Web Form").has_field("payments_tab"): | |||
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" | |||
} | |||
] | |||
}) | |||
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", | |||
}, | |||
] | |||
} | |||
) | |||
frappe.clear_cache(doctype="Web Form") | |||
@@ -150,14 +151,11 @@ def delete_custom_fields(): | |||
"amount_field", | |||
"amount_based_on_field", | |||
"amount", | |||
"currency" | |||
"currency", | |||
) | |||
for fieldname in fieldnames: | |||
frappe.db.delete( | |||
"Custom Field", | |||
{"name": "Web Form-" + fieldname} | |||
) | |||
frappe.db.delete("Custom Field", {"name": "Web Form-" + fieldname}) | |||
frappe.clear_cache(doctype="Web Form") | |||