diff --git a/lms/hooks.py b/lms/hooks.py
index 1bb0f1f0..f8796e7e 100644
--- a/lms/hooks.py
+++ b/lms/hooks.py
@@ -188,6 +188,10 @@ website_route_rules = [
"from_route": "/quiz-submission//",
"to_route": "quiz_submission/quiz_submission",
},
+ {
+ "from_route": "/billing/",
+ "to_route": "billing/billing",
+ },
]
website_redirects = [
diff --git a/lms/lms/doctype/lms_batch_membership/lms_batch_membership.json b/lms/lms/doctype/lms_batch_membership/lms_batch_membership.json
index 903f66ce..6aba9d5b 100644
--- a/lms/lms/doctype/lms_batch_membership/lms_batch_membership.json
+++ b/lms/lms/doctype/lms_batch_membership/lms_batch_membership.json
@@ -12,6 +12,12 @@
"member",
"member_name",
"member_username",
+ "billing_information_section",
+ "address",
+ "payment_received",
+ "column_break_rvzn",
+ "order_id",
+ "payment_id",
"section_break_8",
"cohort",
"subgroup",
@@ -112,11 +118,42 @@
{
"fieldname": "section_break_8",
"fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "billing_information_section",
+ "fieldtype": "Section Break",
+ "label": "Billing Information"
+ },
+ {
+ "fieldname": "address",
+ "fieldtype": "Link",
+ "label": "Address",
+ "options": "Address"
+ },
+ {
+ "default": "0",
+ "fieldname": "payment_received",
+ "fieldtype": "Check",
+ "label": "Payment Received"
+ },
+ {
+ "fieldname": "column_break_rvzn",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "order_id",
+ "fieldtype": "Data",
+ "label": "Order ID"
+ },
+ {
+ "fieldname": "payment_id",
+ "fieldtype": "Data",
+ "label": "Payment ID"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2022-10-10 12:38:17.839526",
+ "modified": "2023-08-11 15:39:50.194348",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Batch Membership",
@@ -141,4 +178,4 @@
"sort_order": "DESC",
"states": [],
"title_field": "member_name"
-}
+}
\ No newline at end of file
diff --git a/lms/lms/doctype/lms_course/lms_course.py b/lms/lms/doctype/lms_course/lms_course.py
index 967d8e93..7cc082a4 100644
--- a/lms/lms/doctype/lms_course/lms_course.py
+++ b/lms/lms/doctype/lms_course/lms_course.py
@@ -359,14 +359,12 @@ def reorder_chapter(chapter_array):
@frappe.whitelist()
-def get_payment_options(course):
+def get_payment_options(course, phone):
course_details = frappe.db.get_value(
"LMS Course", course, ["name", "title", "currency", "course_price"], as_dict=True
)
razorpay_key = frappe.db.get_single_value("LMS Settings", "razorpay_key")
- razorpay_secret = frappe.db.get_single_value("LMS Settings", "razorpay_secret")
-
- client = get_client(razorpay_key, razorpay_secret)
+ client = get_client()
order = create_order(client, course_details)
options = {
@@ -379,15 +377,32 @@ def get_payment_options(course):
"prefill": {
"name": frappe.db.get_value("User", frappe.session.user, "full_name"),
"email": frappe.session.user,
+ "contact": phone,
},
- "callback_url": frappe.utils.get_url(
- "/api/method/lms.lms.doctype.lms_course.lms_course.verify_payment"
- ),
}
return options
-def get_client(razorpay_key, razorpay_secret):
+def save_address(address):
+ address = json.loads(address)
+ address.update(
+ {
+ "address_title": frappe.db.get_value("User", frappe.session.user, "full_name"),
+ "address_type": "Billing",
+ "is_primary_address": 1,
+ "email_id": frappe.session.user,
+ }
+ )
+ doc = frappe.new_doc("Address")
+ doc.update(address)
+ doc.save(ignore_permissions=True)
+ return doc.name
+
+
+def get_client():
+ razorpay_key = frappe.db.get_single_value("LMS Settings", "razorpay_key")
+ razorpay_secret = frappe.db.get_single_value("LMS Settings", "razorpay_secret")
+
if not razorpay_key and not razorpay_secret:
frappe.throw(
_(
@@ -399,9 +414,48 @@ def get_client(razorpay_key, razorpay_secret):
def create_order(client, course_details):
- return client.order.create(
+ try:
+ return client.order.create(
+ {
+ "amount": course_details.course_price * 100,
+ "currency": course_details.currency,
+ }
+ )
+ except Exception as e:
+ frappe.throw(
+ _("Error during payment: {0}. Please contact the Administrator.").format(e)
+ )
+
+
+@frappe.whitelist()
+def verify_payment(response, course, address, order_id):
+ response = json.loads(response)
+ client = get_client()
+ client.utility.verify_payment_signature(
{
- "amount": course_details.course_price * 100,
- "currency": course_details.currency,
+ "razorpay_order_id": order_id,
+ "razorpay_payment_id": response["razorpay_payment_id"],
+ "razorpay_signature": response["razorpay_signature"],
}
)
+
+ return create_membership(address, response, course)
+
+
+def create_membership(address, response, course):
+ address_name = save_address(address)
+ membership = frappe.new_doc("LMS Batch Membership")
+
+ membership.update(
+ {
+ "member": frappe.session.user,
+ "course": course,
+ "address": address_name,
+ "payment_received": 1,
+ "order_id": response["razorpay_order_id"],
+ "payment_id": response["razorpay_payment_id"],
+ }
+ )
+ membership.save(ignore_permissions=True)
+
+ return f"/courses/{course}/learn/1.1"
diff --git a/lms/public/css/style.css b/lms/public/css/style.css
index 6b885abe..c3768a6c 100644
--- a/lms/public/css/style.css
+++ b/lms/public/css/style.css
@@ -2285,4 +2285,15 @@ select {
position: absolute;
top: 0;
left: 0;
-}
\ No newline at end of file
+}
+
+.form-section .section-head {
+ margin-bottom: var(--margin-sm);
+ font-weight: 700;
+ color: var(--heading-color);
+}
+
+.form-column:first-child {
+ padding-right: 1rem !important;
+}
+
diff --git a/lms/public/js/common_functions.js b/lms/public/js/common_functions.js
index 9605629f..124c401f 100644
--- a/lms/public/js/common_functions.js
+++ b/lms/public/js/common_functions.js
@@ -201,7 +201,7 @@ const expand_the_first_chapter = () => {
const expand_the_active_chapter = () => {
let selector = $(".course-home-headings.title");
- console.log(selector);
+
if (selector.length && $(".course-details-page").length) {
expand_for_course_details(selector);
} else if ($(".active-lesson").length) {
diff --git a/lms/www/billing/billing.html b/lms/www/billing/billing.html
new file mode 100644
index 00000000..373461d9
--- /dev/null
+++ b/lms/www/billing/billing.html
@@ -0,0 +1,75 @@
+{% extends "lms/templates/lms_base.html" %}
+{% block title %}
+ {{ course.title if course.title else _("New Course") }}
+{% endblock %}
+
+
+{% block page_content %}
+
+
+ {{ Header() }}
+ {{ CourseDetails() }}
+ {{ BillingDetails() }}
+
+
+{% endblock %}
+
+{% macro Header() %}
+
+
+ {{ _("Order Details") }}
+
+
+ {{ _("Enter the billing information and complete the payment to purchase this course.") }}
+
+
+{% endmacro %}
+
+{% macro CourseDetails() %}
+
+
+
+ {{ _("Course:") }}
+
+
+ {{ course.title }}
+
+
+
+
+
+
+
+ {{ _("Total Price:") }}
+
+
+
+ {{ frappe.utils.fmt_money(course.course_price, 2, course.currency) }}
+
+
+
+{% endmacro %}
+
+{% macro BillingDetails() %}
+
+
+ {{ _("Billing Details") }}
+
+
+
+
+{% endmacro %}
+
+{%- block script %}
+{{ super() }}
+
+
+{% endblock %}
diff --git a/lms/www/billing/billing.js b/lms/www/billing/billing.js
new file mode 100644
index 00000000..51f57cef
--- /dev/null
+++ b/lms/www/billing/billing.js
@@ -0,0 +1,105 @@
+frappe.ready(() => {
+ if ($("#billing-form").length) {
+ setup_billing();
+ }
+
+ $(".btn-pay").click((e) => {
+ generate_payment_link(e);
+ });
+});
+
+const setup_billing = () => {
+ this.billing = new frappe.ui.FieldGroup({
+ fields: [
+ {
+ fieldtype: "Data",
+ label: __("Address Line 1"),
+ fieldname: "address_line1",
+ reqd: 1,
+ },
+ {
+ fieldtype: "Data",
+ label: __("Address Line 2"),
+ fieldname: "address_line2",
+ },
+ {
+ fieldtype: "Data",
+ label: __("City/Town"),
+ fieldname: "city",
+ reqd: 1,
+ },
+ {
+ fieldtype: "Data",
+ label: __("State/Province"),
+ fieldname: "state",
+ },
+ {
+ fieldtype: "Column Break",
+ },
+ {
+ fieldtype: "Link",
+ label: __("Country"),
+ fieldname: "country",
+ options: "Country",
+ reqd: 1,
+ },
+ {
+ fieldtype: "Data",
+ label: __("Postal Code"),
+ fieldname: "pincode",
+ reqd: 1,
+ },
+ {
+ fieldtype: "Data",
+ label: __("Phone Number"),
+ fieldname: "phone",
+ reqd: 1,
+ },
+ ],
+ body: $("#billing-form").get(0),
+ });
+ this.billing.make();
+ $("#billing-form .form-section:last").removeClass("empty-section");
+ $("#billing-form .frappe-control").removeClass("hide-control");
+ $("#billing-form .form-column").addClass("p-0");
+};
+
+const generate_payment_link = (e) => {
+ address = this.billing.get_values();
+ let course = decodeURIComponent($(e.currentTarget).attr("data-course"));
+
+ frappe.call({
+ method: "lms.lms.doctype.lms_course.lms_course.get_payment_options",
+ args: {
+ course: course,
+ phone: address.phone,
+ },
+ callback: (data) => {
+ data.message.handler = (response) => {
+ handle_success(
+ response,
+ course,
+ address,
+ data.message.order_id
+ );
+ };
+ let rzp1 = new Razorpay(data.message);
+ rzp1.open();
+ },
+ });
+};
+
+const handle_success = (response, course, address, order_id) => {
+ frappe.call({
+ method: "lms.lms.doctype.lms_course.lms_course.verify_payment",
+ args: {
+ response: response,
+ course: course,
+ address: address,
+ order_id: order_id,
+ },
+ callback: (data) => {
+ window.location.href = data.message;
+ },
+ });
+};
diff --git a/lms/www/billing/billing.py b/lms/www/billing/billing.py
new file mode 100644
index 00000000..562e6969
--- /dev/null
+++ b/lms/www/billing/billing.py
@@ -0,0 +1,23 @@
+import frappe
+from frappe import _
+
+
+def get_context(context):
+ course_name = frappe.form_dict.course
+
+ if not course_name:
+ raise ValueError(_("Course is required."))
+
+ if frappe.session.user == "Guest":
+ raise frappe.PermissionError(_("You are not allowed to access this page."))
+
+ membership = frappe.db.exists(
+ "LMS Batch Membership", {"member": frappe.session.user, "course": course_name}
+ )
+
+ if membership:
+ raise frappe.PermissionError(_("You are already enrolled for this course"))
+
+ context.course = frappe.db.get_value(
+ "LMS Course", course_name, ["title", "name", "course_price", "currency"], as_dict=True
+ )
diff --git a/lms/www/courses/course.html b/lms/www/courses/course.html
index ad584ec0..450cf640 100644
--- a/lms/www/courses/course.html
+++ b/lms/www/courses/course.html
@@ -222,9 +222,9 @@
{% elif course.paid_course %}
-
+
{% elif show_start_learing_cta(course, membership) %}
@@ -257,9 +257,6 @@
{% endif %}
-
-
-
{% endmacro %}
@@ -280,9 +277,4 @@
{{ _("You have exceeded the maximum number of attempts allowed to appear for evaluations of this course.") }}
{% endif %}
-{% endmacro %}
-
-{%- block script %}
-{{ super() }}
-
-{% endblock %}
\ No newline at end of file
+{% endmacro %}
\ No newline at end of file
diff --git a/lms/www/courses/course.js b/lms/www/courses/course.js
index 4f26b877..a3f86b84 100644
--- a/lms/www/courses/course.js
+++ b/lms/www/courses/course.js
@@ -20,10 +20,6 @@ frappe.ready(() => {
$("#submit-for-review").click((e) => {
submit_for_review(e);
});
-
- $("#buy-course").click((e) => {
- generate_checkout_link(e);
- });
});
const hide_wrapped_mentor_cards = () => {
@@ -146,24 +142,3 @@ const submit_for_review = (e) => {
},
});
};
-
-generate_checkout_link = (e) => {
- e.preventDefault();
- let course = decodeURIComponent($(e.currentTarget).attr("data-course"));
-
- if (frappe.session.user == "Guest") {
- window.location.href = `/login?redirect-to=/courses/${course}`;
- return;
- }
-
- frappe.call({
- method: "lms.lms.doctype.lms_course.lms_course.get_payment_options",
- args: {
- course: course,
- },
- callback: (data) => {
- let rzp1 = new Razorpay(data.message);
- rzp1.open();
- },
- });
-};