Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
99f9955
feat: dynamic payer name & email from form fields
batonac Oct 2, 2023
e1d37a3
fix: remove whitespace
batonac Oct 2, 2023
7878942
fix: remove "Button Label" & "Button Help" fields
batonac Oct 2, 2023
f6a790e
Add line in override to set payment gateway.
batonac Dec 6, 2023
af62692
Undo unneccessary changes to .gitignore and pyproject.toml made durin…
batonac Dec 7, 2023
4b54933
refactor: re-instate make_custom_fields (for ERPNext)
batonac Mar 19, 2024
0f5a90a
refactor: re-enable make_custom_fields hook
batonac Mar 19, 2024
7461c40
refactor: restore utils __init__.py
batonac Mar 19, 2024
36bf21f
refactor: restore delete_custom_fields
batonac Mar 19, 2024
71c34ea
Merge branch 'frappe:develop' into develop
batonac Mar 19, 2024
6510e8f
refactor: relative stripe URL
batonac May 29, 2024
09d5598
Merge branch 'frappe:develop' into develop
batonac Dec 11, 2024
b3ada19
fmt: black & prettier
batonac Dec 11, 2024
c61182a
Merge pull request #1 from batonac/feat_dynamic_webform
batonac Dec 11, 2024
134048d
fix: add payment_gateway to payment_details
batonac Dec 30, 2024
98feda4
chore: fix broken CI
ruthra-kumar May 6, 2025
8c815f4
test(mpesa-settings): add opening entry for new POS profile
Bhavan23 May 9, 2025
f0045ba
chore: extend supported currencies list for Razorpay.
Karuppasamy923 May 6, 2025
d9fd414
chore: Update formatings
Karuppasamy923 May 6, 2025
7047bfe
ci: trigger
ruthra-kumar Jun 19, 2025
c5c76aa
chore: include setuptools
ruthra-kumar Jun 19, 2025
f7abffb
fix: make Stripe checkout page responsive
ravibharathi656 Jun 19, 2025
210961b
fix(Razorpay): create order if not provided for payment link
NagariaHussain Jul 31, 2025
93ff8de
fix: flaky mpesa tests
ruthra-kumar Aug 1, 2025
b055e72
fix: image condition (#162)
barredterra Sep 15, 2025
e3e1fa2
chore: bump actions/cache to v4 (#131)
barredterra Sep 15, 2025
8211471
refactor: delete custom fields (#161)
barredterra Sep 15, 2025
14c82ae
ci: bump github actions to supported versions (#169) (#171)
mergify[bot] Sep 15, 2025
77499fe
refactor: inline immediately returned variable (#163)
barredterra Sep 15, 2025
0c09eb0
fix(Web Form): extend instead of overriding (#164)
barredterra Sep 15, 2025
9cec88c
chore: Merge branch 'develop' into feat_payer_from_web_form
batonac Sep 15, 2025
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
2 changes: 1 addition & 1 deletion payments/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# page_js = {"page" : "public/js/file.js"}

# include js in doctype views
# doctype_js = {"doctype" : "public/js/doctype.js"}
doctype_js = {"Web Form": "public/js/web_form.js"}
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
# doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"}
Expand Down
19 changes: 17 additions & 2 deletions payments/overrides/payment_webform.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ def get_payment_gateway_url(self, doc):
controller = get_payment_gateway_controller(self.payment_gateway)

title = f"Payment for {doc.doctype} {doc.name}"

# Set Amount
amount = self.amount
if self.amount_based_on_field:
amount = doc.get(self.amount_field)
Expand All @@ -36,14 +38,25 @@ def get_payment_gateway_url(self, doc):
if amount is None or Decimal(amount) <= 0:
return frappe.utils.get_url(self.success_url or self.route)

# Set Payer Name
payer_name = frappe.utils.get_fullname(frappe.session.user)
if self.payer_name_based_on_field:
payer_name = doc.get(self.payer_name_field)

# Set Payer Email
payer_email = frappe.session.user
if self.payer_email_based_on_field:
payer_email = doc.get(self.payer_email_field)

payment_details = {
"amount": amount,
"title": title,
"description": title,
"reference_doctype": doc.doctype,
"reference_docname": doc.name,
"payer_email": frappe.session.user,
"payer_name": frappe.utils.get_fullname(frappe.session.user),
"payment_gateway": self.payment_gateway,
"payer_email": payer_email,
"payer_name": payer_name,
"order_id": doc.name,
"currency": self.currency,
"redirect_to": frappe.utils.get_url(self.success_url or self.route),
Expand Down Expand Up @@ -101,6 +114,8 @@ def accept(web_form, data, docname=None, for_payment=False):
if for_payment:
web_form.validate_mandatory(doc)
doc.run_method("validate_payment")
# set payment gateway
doc.set("payment_gateway", web_form.payment_gateway)

if doc.name:
if web_form.has_web_form_permission(doc.doctype, doc.name, "write"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def validate_minimum_transaction_amount(self, currency, amount):
)

def get_payment_url(self, **kwargs):
return get_url(f"./stripe_checkout?{urlencode(kwargs)}")
return f"/stripe_checkout?{urlencode(kwargs)}"

def create_request(self, data):
import stripe
Expand Down Expand Up @@ -244,12 +244,13 @@ def finalize_request(self):
redirect_to = self.data.get("redirect_to") or None
redirect_message = self.data.get("redirect_message") or None
status = self.integration_request.status
custom_redirect_to_override = None

if self.flags.status_changed_to == "Completed":
if self.data.reference_doctype and self.data.reference_docname:
custom_redirect_to = None
try:
custom_redirect_to = frappe.get_doc(
custom_redirect_to, custom_redirect_to_override = frappe.get_doc(
self.data.reference_doctype, self.data.reference_docname
).run_method("on_payment_authorized", self.flags.status_changed_to)
except Exception:
Expand All @@ -273,6 +274,8 @@ def finalize_request(self):

if redirect_message:
redirect_url += "&" + urlencode({"redirect_message": redirect_message})
if custom_redirect_to_override:
redirect_url = redirect_to

return {"redirect_to": redirect_url, "status": status}

Expand Down
120 changes: 120 additions & 0 deletions payments/payments/custom/web_form.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{
"custom_fields": [
{
"dt": "Web Form",
"fieldname": "payments_tab",
"fieldtype": "Tab Break",
"label": "Payments",
"insert_after": "custom_css",
"is_system_generated": 1
},
{
"default": "0",
"dt": "Web Form",
"fieldname": "accept_payment",
"fieldtype": "Check",
"label": "Accept Payments",
"insert_after": "payments_tab",
"is_system_generated": 1
},
{
"depends_on": "accept_payment",
"dt": "Web Form",
"fieldname": "payments_sb",
"fieldtype": "Section Break",
"insert_after": "accept_payment",
"is_system_generated": 1
},
{
"depends_on": "eval:!doc.amount_based_on_field",
"dt": "Web Form",
"fieldname": "amount",
"fieldtype": "Currency",
"label": "Amount",
"insert_after": "payments_sb",
"is_system_generated": 1
},
{
"dt": "Web Form",
"fieldname": "currency",
"fieldtype": "Link",
"label": "Currency",
"options": "Currency",
"insert_after": "amount",
"is_system_generated": 1
},
{
"dt": "Web Form",
"fieldname": "payment_gateway",
"fieldtype": "Link",
"label": "Payment Gateway",
"options": "Payment Gateway",
"insert_after": "currency",
"is_system_generated": 1
},
{
"dt": "Web Form",
"fieldname": "payments_cb",
"fieldtype": "Column Break",
"insert_after": "payment_gateway",
"is_system_generated": 1
},
{
"default": "0",
"dt": "Web Form",
"fieldname": "amount_based_on_field",
"fieldtype": "Check",
"label": "Amount Based On Field",
"insert_after": "payments_cb",
"is_system_generated": 1
},
{
"depends_on": "eval:doc.amount_based_on_field",
"dt": "Web Form",
"fieldname": "amount_field",
"fieldtype": "Select",
"label": "Amount Field",
"insert_after": "amount_based_on_field",
"is_system_generated": 1
},
{
"default": "0",
"dt": "Web Form",
"fieldname": "payer_name_based_on_field",
"fieldtype": "Check",
"label": "Payer Name Based On Field",
"insert_after": "amount_field",
"is_system_generated": 1
},
{
"depends_on": "eval:doc.payer_name_based_on_field",
"dt": "Web Form",
"fieldname": "payer_name_field",
"fieldtype": "Select",
"label": "Payer Name Field",
"insert_after": "payer_name_based_on_field",
"is_system_generated": 1
},
{
"default": "0",
"dt": "Web Form",
"fieldname": "payer_email_based_on_field",
"fieldtype": "Check",
"label": "Payer Email Based On Field",
"insert_after": "payer_name_field",
"is_system_generated": 1
},
{
"depends_on": "eval:doc.payer_email_based_on_field",
"dt": "Web Form",
"fieldname": "payer_email_field",
"fieldtype": "Select",
"label": "Payer Email Field",
"insert_after": "payer_email_based_on_field",
"is_system_generated": 1
}
],
"property_setters": [],
"sync_on_migrate": 1,
"doctype": "Web Form"
}
90 changes: 90 additions & 0 deletions payments/public/js/web_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
frappe.ui.form.on("Web Form", {
set_fields(frm) {
let doc = frm.doc;

let update_options = (options) => {
[
frm.fields_dict.web_form_fields.grid,
frm.fields_dict.list_columns.grid,
].forEach((obj) => {
obj.update_docfield_property("fieldname", "options", options);
});
};

if (!doc.doc_type) {
update_options([]);
frm.set_df_property("amount_field", "options", []);
frm.set_df_property("payer_name_field", "options", []);
frm.set_df_property("payer_email_field", "options", []);
return;
}

update_options([`Fetching fields from ${doc.doc_type}...`]);

new Promise((resolve) =>
frappe.model.with_doctype(doc.doc_type, resolve)
).then(() => {
const fields = frappe.meta.get_docfields(doc.doc_type).filter((df) => {
return (
(frappe.model.is_value_type(df.fieldtype) &&
!["lft", "rgt"].includes(df.fieldname)) ||
["Table", "Table Multiselect"].includes(df.fieldtype) ||
frappe.model.layout_fields.includes(df.fieldtype)
);
});

let as_select_option = (df) => ({
label: df.label,
value: df.fieldname,
});

// Update main field options
update_options(fields.map(as_select_option));

// Amount Field Options
let currency_fields = fields
.filter((df) => ["Currency", "Float"].includes(df.fieldtype))
.map(as_select_option);
if (!currency_fields.length) {
currency_fields = [
{
label: `No currency fields in ${doc.doc_type}`,
value: "",
disabled: true,
},
];
}
frm.set_df_property("amount_field", "options", currency_fields);

// Payer Name Field Options
let payer_name_fields = fields
.filter((df) => ["Data", "Link"].includes(df.fieldtype))
.map(as_select_option);
if (!payer_name_fields.length) {
payer_name_fields = [
{
label: `No data or link fields in ${doc.doc_type}`,
value: "",
disabled: true,
},
];
}
frm.set_df_property("payer_name_field", "options", payer_name_fields);

// Payer Email Field Options
let payer_email_fields = fields
.filter((df) => ["Email"].includes(df.options))
.map(as_select_option);
if (!payer_email_fields.length) {
payer_email_fields = [
{
label: `No email fields in ${doc.doc_type}`,
value: "",
disabled: true,
},
];
}
frm.set_df_property("payer_email_field", "options", payer_email_fields);
});
},
});
Loading
Loading