feat: payment flow with payments app
This commit is contained in:
@@ -38,7 +38,7 @@
|
|||||||
v-if="orderSummary.data.gst_applied"
|
v-if="orderSummary.data.gst_applied"
|
||||||
class="flex items-center justify-between mt-2"
|
class="flex items-center justify-between mt-2"
|
||||||
>
|
>
|
||||||
<div>
|
<div class="text-sm text-gray-600 font-medium">
|
||||||
{{ __('GST Amount') }}
|
{{ __('GST Amount') }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -171,7 +171,7 @@ import { Input, Button, createResource, call } from 'frappe-ui'
|
|||||||
import { reactive, inject, onMounted, ref } from 'vue'
|
import { reactive, inject, onMounted, ref } from 'vue'
|
||||||
import Link from '@/components/Controls/Link.vue'
|
import Link from '@/components/Controls/Link.vue'
|
||||||
import NotPermitted from '@/components/NotPermitted.vue'
|
import NotPermitted from '@/components/NotPermitted.vue'
|
||||||
import { createToast } from '@/utils/'
|
import { showToast } from '@/utils/'
|
||||||
|
|
||||||
const user = inject('$user')
|
const user = inject('$user')
|
||||||
|
|
||||||
@@ -224,73 +224,45 @@ const orderSummary = createResource({
|
|||||||
const billingDetails = reactive({})
|
const billingDetails = reactive({})
|
||||||
|
|
||||||
const setBillingDetails = (data) => {
|
const setBillingDetails = (data) => {
|
||||||
billingDetails.billing_name = data.billing_name || ''
|
billingDetails.billing_name = data?.billing_name || ''
|
||||||
billingDetails.address_line1 = data.address_line1 || ''
|
billingDetails.address_line1 = data?.address_line1 || ''
|
||||||
billingDetails.address_line2 = data.address_line2 || ''
|
billingDetails.address_line2 = data?.address_line2 || ''
|
||||||
billingDetails.city = data.city || ''
|
billingDetails.city = data?.city || ''
|
||||||
billingDetails.state = data.state || ''
|
billingDetails.state = data?.state || ''
|
||||||
billingDetails.country = data.country || ''
|
billingDetails.country = data?.country || ''
|
||||||
billingDetails.pincode = data.pincode || ''
|
billingDetails.pincode = data?.pincode || ''
|
||||||
billingDetails.phone = data.phone || ''
|
billingDetails.phone = data?.phone || ''
|
||||||
billingDetails.source = data.source || ''
|
billingDetails.source = data?.source || ''
|
||||||
billingDetails.gstin = data.gstin || ''
|
billingDetails.gstin = data?.gstin || ''
|
||||||
billingDetails.pan = data.pan || ''
|
billingDetails.pan = data?.pan || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentOptions = createResource({
|
const paymentLink = createResource({
|
||||||
url: 'lms.lms.utils.get_payment_options',
|
url: 'lms.lms.payments.get_payment_link',
|
||||||
makeParams(values) {
|
makeParams(values) {
|
||||||
return {
|
return {
|
||||||
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
||||||
docname: props.name,
|
docname: props.name,
|
||||||
phone: billingDetails.phone,
|
amount: orderSummary.data.original_amount,
|
||||||
country: billingDetails.country,
|
total_amount: orderSummary.data.amount,
|
||||||
|
currency: orderSummary.data.currency,
|
||||||
|
address: billingDetails,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const generatePaymentLink = () => {
|
const generatePaymentLink = () => {
|
||||||
call('lms.lms.payments.get_payment_link', {
|
paymentLink.submit(
|
||||||
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
{},
|
||||||
docname: props.name,
|
|
||||||
amount: orderSummary.data.amount,
|
|
||||||
currency: orderSummary.data.currency,
|
|
||||||
billing_name: billingDetails.billing_name,
|
|
||||||
}).then((data) => {
|
|
||||||
window.location.href = data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const paymentResource = createResource({
|
|
||||||
url: 'lms.lms.utils.verify_payment',
|
|
||||||
makeParams(values) {
|
|
||||||
return {
|
|
||||||
response: values.response,
|
|
||||||
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
|
|
||||||
docname: props.name,
|
|
||||||
address: billingDetails,
|
|
||||||
order_id: values.orderId,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleSuccess = (response, doctype, docname, orderId) => {
|
|
||||||
paymentResource.submit(
|
|
||||||
{
|
|
||||||
response: response,
|
|
||||||
orderId: orderId,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
|
validate() {
|
||||||
|
return validateAddress()
|
||||||
|
},
|
||||||
onSuccess(data) {
|
onSuccess(data) {
|
||||||
createToast({
|
window.location.href = data
|
||||||
title: 'Success',
|
},
|
||||||
text: 'Payment Successful',
|
onError(err) {
|
||||||
icon: 'check',
|
showToast(__('Error'), err.messages?.[0] || err, 'x')
|
||||||
iconClasses: 'bg-green-600 text-white rounded-md p-px',
|
|
||||||
})
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = data
|
|
||||||
}, 3000)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -165,25 +165,64 @@ class LMSBatch(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def on_payment_authorized(self, payment_status):
|
def on_payment_authorized(self, payment_status):
|
||||||
print(payment_status)
|
if payment_status == "Authorized":
|
||||||
|
self.update_payment_record()
|
||||||
|
|
||||||
|
def update_payment_record(self):
|
||||||
|
request = frappe.get_all(
|
||||||
|
"Integration Request",
|
||||||
|
{
|
||||||
|
"reference_doctype": self.doctype,
|
||||||
|
"reference_docname": self.name,
|
||||||
|
"owner": frappe.session.user,
|
||||||
|
},
|
||||||
|
order_by="creation desc",
|
||||||
|
limit=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(request):
|
||||||
|
data = frappe.db.get_value("Integration Request", request[0].name, "data")
|
||||||
|
data = frappe._dict(json.loads(data))
|
||||||
|
|
||||||
|
payment_gateway = data.get("payment_gateway")
|
||||||
|
frappe.db.set_value(
|
||||||
|
"LMS Payment",
|
||||||
|
data.payment,
|
||||||
|
{
|
||||||
|
"payment_received": 1,
|
||||||
|
"payment_id": data[f"{payment_gateway.lower()}_payment_id"],
|
||||||
|
"order_id": data["order_id"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
enroll_in_batch(data.payment, self)
|
||||||
|
except Exception as e:
|
||||||
|
frappe.log_error(frappe.get_traceback(), _("Enrollment Failed"))
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
def enroll_in_batch(payment_name, batch):
|
||||||
def remove_student(student, batch_name):
|
if not frappe.db.exists(
|
||||||
frappe.only_for("Moderator")
|
"Batch Student", {"parent": batch.name, "student": frappe.session.user}
|
||||||
frappe.db.delete("Batch Student", {"student": student, "parent": batch_name})
|
):
|
||||||
|
student = frappe.new_doc("Batch Student")
|
||||||
|
current_count = frappe.db.count("Batch Student", {"parent": batch.name})
|
||||||
|
payment = frappe.db.get_value(
|
||||||
|
"LMS Payment", payment_name, ["name", "source"], as_dict=True
|
||||||
|
)
|
||||||
|
|
||||||
|
student.update(
|
||||||
@frappe.whitelist()
|
{
|
||||||
def remove_course(course, parent):
|
"student": frappe.session.user,
|
||||||
frappe.only_for("Moderator")
|
"payment": payment.name,
|
||||||
frappe.db.delete("Batch Course", {"course": course, "parent": parent})
|
"source": payment.source,
|
||||||
|
"parent": batch.name,
|
||||||
|
"parenttype": "LMS Batch",
|
||||||
@frappe.whitelist()
|
"parentfield": "students",
|
||||||
def remove_assessment(assessment, parent):
|
"idx": current_count + 1,
|
||||||
frappe.only_for("Moderator")
|
}
|
||||||
frappe.db.delete("LMS Assessment", {"assessment_name": assessment, "parent": parent})
|
)
|
||||||
|
student.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
"mentor_request_status_update",
|
"mentor_request_status_update",
|
||||||
"payment_settings_tab",
|
"payment_settings_tab",
|
||||||
"payment_section",
|
"payment_section",
|
||||||
|
"payment_gateway",
|
||||||
"razorpay_key",
|
"razorpay_key",
|
||||||
"razorpay_secret",
|
"razorpay_secret",
|
||||||
"apply_gst",
|
"apply_gst",
|
||||||
@@ -331,12 +332,18 @@
|
|||||||
"fieldname": "custom_signup_content",
|
"fieldname": "custom_signup_content",
|
||||||
"fieldtype": "HTML Editor",
|
"fieldtype": "HTML Editor",
|
||||||
"label": "Custom Signup Content"
|
"label": "Custom Signup Content"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "payment_gateway",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Payment Gateway",
|
||||||
|
"options": "Razorpay\nMpesa\nPaytm\nBraintree\nStripe\nPaypal\nGoCardless"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-09-23 17:57:01.350020",
|
"modified": "2024-09-24 18:09:25.366651",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Settings",
|
"name": "LMS Settings",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from payments.utils import get_payment_gateway_controller
|
|||||||
|
|
||||||
|
|
||||||
def get_payment_gateway():
|
def get_payment_gateway():
|
||||||
return "Razorpay"
|
return frappe.db.get_single_value("LMS Settings", "payment_gateway")
|
||||||
|
|
||||||
|
|
||||||
def get_controller(payment_gateway):
|
def get_controller(payment_gateway):
|
||||||
@@ -16,21 +16,73 @@ def validate_currency(payment_gateway, currency):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_link(doctype, docname, amount, currency, billing_name):
|
def get_payment_link(doctype, docname, amount, total_amount, currency, address):
|
||||||
payment_gateway = get_payment_gateway()
|
payment_gateway = get_payment_gateway()
|
||||||
|
address = frappe._dict(address)
|
||||||
|
amount_with_gst = total_amount if total_amount != amount else 0
|
||||||
|
|
||||||
|
payment = record_payment(address, doctype, docname, amount, currency, amount_with_gst)
|
||||||
|
|
||||||
payment_details = {
|
payment_details = {
|
||||||
"amount": amount,
|
"amount": total_amount,
|
||||||
"title": f"Payment for {doctype} {docname}",
|
"title": f"Payment for {doctype} {docname}",
|
||||||
"description": f"{billing_name}'s payment for {doctype} {docname}",
|
"description": f"{address.billing_name}'s payment for {doctype} {docname}",
|
||||||
"reference_doctype": doctype,
|
"reference_doctype": doctype,
|
||||||
"reference_docname": docname,
|
"reference_docname": docname,
|
||||||
"payer_email": frappe.session.user,
|
"payer_email": frappe.session.user,
|
||||||
"payer_name": billing_name,
|
"payer_name": address.billing_name,
|
||||||
"order_id": docname,
|
"order_id": docname,
|
||||||
"currency": currency,
|
"currency": currency,
|
||||||
"payment_gateway": payment_gateway,
|
"payment_gateway": payment_gateway,
|
||||||
|
"redirect_to": f"/lms/batches/{docname}",
|
||||||
|
"payment": payment.name,
|
||||||
}
|
}
|
||||||
controller = get_controller(payment_gateway)
|
controller = get_controller(payment_gateway)
|
||||||
url = controller().get_payment_url(**payment_details)
|
url = controller.get_payment_url(**payment_details)
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
def record_payment(address, doctype, docname, amount, currency, amount_with_gst=0):
|
||||||
|
address = frappe._dict(address)
|
||||||
|
address_name = save_address(address)
|
||||||
|
|
||||||
|
payment_doc = frappe.new_doc("LMS Payment")
|
||||||
|
payment_doc.update(
|
||||||
|
{
|
||||||
|
"member": frappe.session.user,
|
||||||
|
"billing_name": address.billing_name,
|
||||||
|
"address": address_name,
|
||||||
|
"amount": amount,
|
||||||
|
"currency": currency,
|
||||||
|
"amount_with_gst": amount_with_gst,
|
||||||
|
"gstin": address.gstin,
|
||||||
|
"pan": address.pan,
|
||||||
|
"source": address.source,
|
||||||
|
"payment_for_document_type": doctype,
|
||||||
|
"payment_for_document": docname,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
payment_doc.save(ignore_permissions=True)
|
||||||
|
return payment_doc
|
||||||
|
|
||||||
|
|
||||||
|
def save_address(address):
|
||||||
|
filters = {"email_id": frappe.session.user}
|
||||||
|
exists = frappe.db.exists("Address", filters)
|
||||||
|
if exists:
|
||||||
|
address_doc = frappe.get_last_doc("Address", filters=filters)
|
||||||
|
else:
|
||||||
|
address_doc = frappe.new_doc("Address")
|
||||||
|
|
||||||
|
address_doc.update(address)
|
||||||
|
address_doc.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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
address_doc.save(ignore_permissions=True)
|
||||||
|
return address_doc.name
|
||||||
|
|||||||
@@ -1021,27 +1021,6 @@ def get_details(doctype, docname):
|
|||||||
return details
|
return details
|
||||||
|
|
||||||
|
|
||||||
def save_address(address):
|
|
||||||
filters = {"email_id": frappe.session.user}
|
|
||||||
exists = frappe.db.exists("Address", filters)
|
|
||||||
if exists:
|
|
||||||
address_doc = frappe.get_last_doc("Address", filters=filters)
|
|
||||||
else:
|
|
||||||
address_doc = frappe.new_doc("Address")
|
|
||||||
|
|
||||||
address_doc.update(address)
|
|
||||||
address_doc.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,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
address_doc.save(ignore_permissions=True)
|
|
||||||
return address_doc.name
|
|
||||||
|
|
||||||
|
|
||||||
def get_client():
|
def get_client():
|
||||||
settings = frappe.get_single("LMS Settings")
|
settings = frappe.get_single("LMS Settings")
|
||||||
razorpay_key = settings.razorpay_key
|
razorpay_key = settings.razorpay_key
|
||||||
@@ -1073,52 +1052,6 @@ def create_order(client, amount, currency):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def verify_payment(response, doctype, docname, address, order_id):
|
|
||||||
client = get_client()
|
|
||||||
client.utility.verify_payment_signature(
|
|
||||||
{
|
|
||||||
"razorpay_order_id": order_id,
|
|
||||||
"razorpay_payment_id": response["razorpay_payment_id"],
|
|
||||||
"razorpay_signature": response["razorpay_signature"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
payment = record_payment(address, response, client, doctype, docname)
|
|
||||||
if doctype == "LMS Course":
|
|
||||||
return create_membership(docname, payment)
|
|
||||||
else:
|
|
||||||
return add_student_to_batch(docname, payment)
|
|
||||||
|
|
||||||
|
|
||||||
def record_payment(address, response, client, doctype, docname):
|
|
||||||
address = frappe._dict(address)
|
|
||||||
address_name = save_address(address)
|
|
||||||
|
|
||||||
payment_details = get_payment_details(doctype, docname, address)
|
|
||||||
payment_doc = frappe.new_doc("LMS Payment")
|
|
||||||
payment_doc.update(
|
|
||||||
{
|
|
||||||
"member": frappe.session.user,
|
|
||||||
"billing_name": address.billing_name,
|
|
||||||
"address": address_name,
|
|
||||||
"payment_received": 1,
|
|
||||||
"order_id": response["razorpay_order_id"],
|
|
||||||
"payment_id": response["razorpay_payment_id"],
|
|
||||||
"amount": payment_details["amount"],
|
|
||||||
"currency": payment_details["currency"],
|
|
||||||
"amount_with_gst": payment_details["amount_with_gst"],
|
|
||||||
"gstin": address.gstin,
|
|
||||||
"pan": address.pan,
|
|
||||||
"source": address.source,
|
|
||||||
"payment_for_document_type": doctype,
|
|
||||||
"payment_for_document": docname,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
payment_doc.save(ignore_permissions=True)
|
|
||||||
return payment_doc
|
|
||||||
|
|
||||||
|
|
||||||
def get_payment_details(doctype, docname, address):
|
def get_payment_details(doctype, docname, address):
|
||||||
amount_field = "course_price" if doctype == "LMS Course" else "amount"
|
amount_field = "course_price" if doctype == "LMS Course" else "amount"
|
||||||
amount = frappe.db.get_value(doctype, docname, amount_field)
|
amount = frappe.db.get_value(doctype, docname, amount_field)
|
||||||
@@ -1146,24 +1079,6 @@ def create_membership(course, payment):
|
|||||||
return f"/lms/courses/{course}/learn/1-1"
|
return f"/lms/courses/{course}/learn/1-1"
|
||||||
|
|
||||||
|
|
||||||
def add_student_to_batch(batchname, payment):
|
|
||||||
student = frappe.new_doc("Batch Student")
|
|
||||||
current_count = frappe.db.count("Batch Student", {"parent": batchname})
|
|
||||||
student.update(
|
|
||||||
{
|
|
||||||
"student": frappe.session.user,
|
|
||||||
"payment": payment.name,
|
|
||||||
"source": payment.source,
|
|
||||||
"parent": batchname,
|
|
||||||
"parenttype": "LMS Batch",
|
|
||||||
"parentfield": "students",
|
|
||||||
"idx": current_count + 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
student.save(ignore_permissions=True)
|
|
||||||
return f"/batches/{batchname}"
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_exchange_rate(source, target="USD"):
|
def get_current_exchange_rate(source, target="USD"):
|
||||||
url = f"https://api.frankfurter.app/latest?from={source}&to={target}"
|
url = f"https://api.frankfurter.app/latest?from={source}&to={target}"
|
||||||
|
|
||||||
@@ -1765,6 +1680,7 @@ def get_order_summary(doctype, docname, country=None):
|
|||||||
details.amount, details.currency = check_multicurrency(
|
details.amount, details.currency = check_multicurrency(
|
||||||
details.amount, details.currency, country, details.amount_usd
|
details.amount, details.currency, country, details.amount_usd
|
||||||
)
|
)
|
||||||
|
details.original_amount = details.amount
|
||||||
details.original_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
details.original_amount_formatted = fmt_money(details.amount, 0, details.currency)
|
||||||
|
|
||||||
if details.currency == "INR":
|
if details.currency == "INR":
|
||||||
|
|||||||
Reference in New Issue
Block a user