feat: payment flow with payments app

This commit is contained in:
Jannat Patel
2024-09-24 18:14:34 +05:30
parent d8ab88be28
commit e0f569c382
5 changed files with 150 additions and 164 deletions

View File

@@ -38,7 +38,7 @@
v-if="orderSummary.data.gst_applied"
class="flex items-center justify-between mt-2"
>
<div>
<div class="text-sm text-gray-600 font-medium">
{{ __('GST Amount') }}
</div>
<div>
@@ -171,7 +171,7 @@ import { Input, Button, createResource, call } from 'frappe-ui'
import { reactive, inject, onMounted, ref } from 'vue'
import Link from '@/components/Controls/Link.vue'
import NotPermitted from '@/components/NotPermitted.vue'
import { createToast } from '@/utils/'
import { showToast } from '@/utils/'
const user = inject('$user')
@@ -224,73 +224,45 @@ const orderSummary = createResource({
const billingDetails = reactive({})
const setBillingDetails = (data) => {
billingDetails.billing_name = data.billing_name || ''
billingDetails.address_line1 = data.address_line1 || ''
billingDetails.address_line2 = data.address_line2 || ''
billingDetails.city = data.city || ''
billingDetails.state = data.state || ''
billingDetails.country = data.country || ''
billingDetails.pincode = data.pincode || ''
billingDetails.phone = data.phone || ''
billingDetails.source = data.source || ''
billingDetails.gstin = data.gstin || ''
billingDetails.pan = data.pan || ''
billingDetails.billing_name = data?.billing_name || ''
billingDetails.address_line1 = data?.address_line1 || ''
billingDetails.address_line2 = data?.address_line2 || ''
billingDetails.city = data?.city || ''
billingDetails.state = data?.state || ''
billingDetails.country = data?.country || ''
billingDetails.pincode = data?.pincode || ''
billingDetails.phone = data?.phone || ''
billingDetails.source = data?.source || ''
billingDetails.gstin = data?.gstin || ''
billingDetails.pan = data?.pan || ''
}
const paymentOptions = createResource({
url: 'lms.lms.utils.get_payment_options',
const paymentLink = createResource({
url: 'lms.lms.payments.get_payment_link',
makeParams(values) {
return {
doctype: props.type == 'course' ? 'LMS Course' : 'LMS Batch',
docname: props.name,
phone: billingDetails.phone,
country: billingDetails.country,
amount: orderSummary.data.original_amount,
total_amount: orderSummary.data.amount,
currency: orderSummary.data.currency,
address: billingDetails,
}
},
})
const generatePaymentLink = () => {
call('lms.lms.payments.get_payment_link', {
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,
},
paymentLink.submit(
{},
{
validate() {
return validateAddress()
},
onSuccess(data) {
createToast({
title: 'Success',
text: 'Payment Successful',
icon: 'check',
iconClasses: 'bg-green-600 text-white rounded-md p-px',
})
setTimeout(() => {
window.location.href = data
}, 3000)
window.location.href = data
},
onError(err) {
showToast(__('Error'), err.messages?.[0] || err, 'x')
},
}
)

View File

@@ -165,25 +165,64 @@ class LMSBatch(Document):
)
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 remove_student(student, batch_name):
frappe.only_for("Moderator")
frappe.db.delete("Batch Student", {"student": student, "parent": batch_name})
def enroll_in_batch(payment_name, batch):
if not frappe.db.exists(
"Batch Student", {"parent": batch.name, "student": frappe.session.user}
):
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
)
@frappe.whitelist()
def remove_course(course, parent):
frappe.only_for("Moderator")
frappe.db.delete("Batch Course", {"course": course, "parent": parent})
@frappe.whitelist()
def remove_assessment(assessment, parent):
frappe.only_for("Moderator")
frappe.db.delete("LMS Assessment", {"assessment_name": assessment, "parent": parent})
student.update(
{
"student": frappe.session.user,
"payment": payment.name,
"source": payment.source,
"parent": batch.name,
"parenttype": "LMS Batch",
"parentfield": "students",
"idx": current_count + 1,
}
)
student.save(ignore_permissions=True)
@frappe.whitelist()

View File

@@ -42,6 +42,7 @@
"mentor_request_status_update",
"payment_settings_tab",
"payment_section",
"payment_gateway",
"razorpay_key",
"razorpay_secret",
"apply_gst",
@@ -331,12 +332,18 @@
"fieldname": "custom_signup_content",
"fieldtype": "HTML Editor",
"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,
"issingle": 1,
"links": [],
"modified": "2024-09-23 17:57:01.350020",
"modified": "2024-09-24 18:09:25.366651",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Settings",

View File

@@ -3,7 +3,7 @@ from payments.utils import get_payment_gateway_controller
def get_payment_gateway():
return "Razorpay"
return frappe.db.get_single_value("LMS Settings", "payment_gateway")
def get_controller(payment_gateway):
@@ -16,21 +16,73 @@ def validate_currency(payment_gateway, currency):
@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()
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 = {
"amount": amount,
"amount": total_amount,
"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_docname": docname,
"payer_email": frappe.session.user,
"payer_name": billing_name,
"payer_name": address.billing_name,
"order_id": docname,
"currency": currency,
"payment_gateway": payment_gateway,
"redirect_to": f"/lms/batches/{docname}",
"payment": payment.name,
}
controller = get_controller(payment_gateway)
url = controller().get_payment_url(**payment_details)
url = controller.get_payment_url(**payment_details)
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

View File

@@ -1021,27 +1021,6 @@ def get_details(doctype, docname):
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():
settings = frappe.get_single("LMS Settings")
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):
amount_field = "course_price" if doctype == "LMS Course" else "amount"
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"
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"):
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, country, details.amount_usd
)
details.original_amount = details.amount
details.original_amount_formatted = fmt_money(details.amount, 0, details.currency)
if details.currency == "INR":