From 36315db110b709beebe9aa1fbb09f51fffff2f40 Mon Sep 17 00:00:00 2001 From: rehanrehman389 Date: Tue, 27 Jan 2026 18:02:20 +0000 Subject: [PATCH 01/32] feat: UPI/QR payment support (manual verification) --- buzz/api.py | 25 +++- .../events/doctype/buzz_event/buzz_event.json | 35 ++++++ buzz/events/doctype/buzz_event/buzz_event.py | 7 +- .../doctype/event_booking/event_booking.js | 30 ++++- .../doctype/event_booking/event_booking.json | 18 ++- .../doctype/event_booking/event_booking.py | 50 +++++++- dashboard/components.d.ts | 1 + dashboard/src/components/BookingForm.vue | 40 ++++++ dashboard/src/components/UPIPaymentDialog.vue | 116 ++++++++++++++++++ dashboard/src/pages/BookTickets.vue | 6 + dashboard/src/pages/BookingDetails.vue | 49 +++++++- 11 files changed, 368 insertions(+), 9 deletions(-) create mode 100644 dashboard/src/components/UPIPaymentDialog.vue diff --git a/buzz/api.py b/buzz/api.py index 7f4f22d3..c3b8853e 100644 --- a/buzz/api.py +++ b/buzz/api.py @@ -307,6 +307,15 @@ def get_event_booking_data(event_route: str) -> dict: # Payment Gateways data.payment_gateways = get_payment_gateways_for_event(event_doc.name) + # UPI Payment Settings + data.upi_payment_enabled = event_doc.enable_upi_payment + if event_doc.enable_upi_payment: + data.upi_settings = { + "upi_id": event_doc.upi_id, + "qr_code": event_doc.upi_qr_code, + "instructions": event_doc.upi_instructions + } + return data @@ -421,6 +430,21 @@ def process_booking( booking.submit() return {"booking_name": booking.name} + # Check if UPI payment is enabled and no payment gateway is provided + event_doc = frappe.get_cached_doc("Buzz Event", event) + if event_doc.enable_upi_payment and not payment_gateway: + # For UPI payments, submit booking directly without payment gateway + # Mark this as UPI payment in additional fields + booking.append("additional_fields", { + "fieldname": "payment_method", + "value": "UPI", + "label": "Payment Method", + "fieldtype": "Data" + }) + booking.flags.ignore_permissions = True + booking.submit() + return {"booking_name": booking.name, "upi_payment": True} + return { "payment_link": get_payment_link_for_booking( booking.name, @@ -1193,7 +1217,6 @@ def validate_coupon(coupon_code: str, event: str, user_email: str | None = None) "free_add_ons": [a.add_on for a in coupon.free_add_ons], } - @frappe.whitelist() def get_campaign_details(campaign: str): """Get campaign details for the register interest page.""" diff --git a/buzz/events/doctype/buzz_event/buzz_event.json b/buzz/events/doctype/buzz_event/buzz_event.json index eb750a7b..26b6bb62 100644 --- a/buzz/events/doctype/buzz_event/buzz_event.json +++ b/buzz/events/doctype/buzz_event/buzz_event.json @@ -44,6 +44,11 @@ "payments_tab", "section_break_owtc", "payment_gateways", + "upi_payment_section", + "enable_upi_payment", + "upi_id", + "upi_qr_code", + "upi_instructions", "tax_settings_section", "apply_tax", "tax_label", @@ -375,6 +380,36 @@ "fieldtype": "Section Break", "label": "Tax Settings" }, + { + "description": "Copy UPI ID or scan QR and upload screenshot for verification", + "fieldname": "upi_payment_section", + "fieldtype": "Section Break", + "label": "UPI Payment via QR / UPI ID" + }, + { + "default": "0", + "fieldname": "enable_upi_payment", + "fieldtype": "Check", + "label": "Allow Manual UPI Payment" + }, + { + "depends_on": "eval:doc.enable_upi_payment==1", + "fieldname": "upi_id", + "fieldtype": "Data", + "label": "Merchant UPI ID" + }, + { + "depends_on": "eval:doc.enable_upi_payment==1", + "fieldname": "upi_qr_code", + "fieldtype": "Attach Image", + "label": "UPI QR Code" + }, + { + "depends_on": "eval:doc.enable_upi_payment==1", + "fieldname": "upi_instructions", + "fieldtype": "Small Text", + "label": "Payment Instructions" + }, { "default": "0", "fieldname": "apply_tax", diff --git a/buzz/events/doctype/buzz_event/buzz_event.py b/buzz/events/doctype/buzz_event/buzz_event.py index 134bcdda..2c90c6cb 100644 --- a/buzz/events/doctype/buzz_event/buzz_event.py +++ b/buzz/events/doctype/buzz_event/buzz_event.py @@ -16,12 +16,11 @@ class BuzzEvent(Document): from typing import TYPE_CHECKING if TYPE_CHECKING: - from frappe.types import DF - from buzz.events.doctype.event_featured_speaker.event_featured_speaker import EventFeaturedSpeaker from buzz.events.doctype.event_payment_gateway.event_payment_gateway import EventPaymentGateway from buzz.events.doctype.schedule_item.schedule_item import ScheduleItem from buzz.proposals.doctype.sponsorship_deck_item.sponsorship_deck_item import SponsorshipDeckItem + from frappe.types import DF about: DF.TextEditor | None allow_editing_talks_after_acceptance: DF.Check @@ -34,6 +33,7 @@ class BuzzEvent(Document): card_image: DF.AttachImage | None category: DF.Link default_ticket_type: DF.Link | None + enable_upi_payment: DF.Check end_date: DF.Date | None end_time: DF.Time external_registration_page: DF.Check @@ -65,6 +65,9 @@ class BuzzEvent(Document): ticket_print_format: DF.Link | None time_zone: DF.Autocomplete | None title: DF.Data + upi_id: DF.Data | None + upi_instructions: DF.SmallText | None + upi_qr_code: DF.AttachImage | None venue: DF.Link | None # end: auto-generated types diff --git a/buzz/ticketing/doctype/event_booking/event_booking.js b/buzz/ticketing/doctype/event_booking/event_booking.js index f062d6f1..945b65f1 100644 --- a/buzz/ticketing/doctype/event_booking/event_booking.js +++ b/buzz/ticketing/doctype/event_booking/event_booking.js @@ -10,5 +10,33 @@ frappe.ui.form.on("Event Booking", { }, }; }); + + // Check if this is a UPI payment + let isUpiPayment = false; + let isVerified = false; + + if (frm.doc.additional_fields) { + for (let field of frm.doc.additional_fields) { + if (field.fieldname === 'payment_method' && field.value === 'UPI') { + isUpiPayment = true; + } + if (field.fieldname === 'payment_verified' && field.value === 'Yes') { + isVerified = true; + } + } + } + + // Hide both buttons first + frm.toggle_display('upi_payment_verification', false); + frm.toggle_display('upi_payment_unverify', false); + + // Show appropriate button based on status + if (isUpiPayment && frappe.user.has_role('Event Manager')) { + if (isVerified) { + frm.toggle_display('upi_payment_unverify', true); + } else { + frm.toggle_display('upi_payment_verification', true); + } + } }, -}); +}); \ No newline at end of file diff --git a/buzz/ticketing/doctype/event_booking/event_booking.json b/buzz/ticketing/doctype/event_booking/event_booking.json index 916c1015..4a5df9b6 100644 --- a/buzz/ticketing/doctype/event_booking/event_booking.json +++ b/buzz/ticketing/doctype/event_booking/event_booking.json @@ -15,6 +15,8 @@ "section_break_suav", "additional_fields", "section_break_tsys", + "upi_payment_verification", + "upi_payment_unverify", "net_amount", "tax_percentage", "tax_label", @@ -78,6 +80,20 @@ "fieldname": "section_break_tsys", "fieldtype": "Section Break" }, + { + "depends_on": "eval:doc.additional_fields && doc.additional_fields.find(f => f.fieldname === 'payment_method' && f.value === 'UPI') && !doc.additional_fields.find(f => f.fieldname === 'payment_verified' && f.value === 'Yes')", + "fieldname": "upi_payment_verification", + "fieldtype": "Button", + "label": "Verify UPI Payment", + "options": "verify_upi_payment" + }, + { + "depends_on": "eval:doc.additional_fields && doc.additional_fields.find(f => f.fieldname === 'payment_verified' && f.value === 'Yes')", + "fieldname": "upi_payment_unverify", + "fieldtype": "Button", + "label": "Mark as Unverified", + "options": "unverify_upi_payment" + }, { "default": "0", "fieldname": "total_amount", @@ -182,7 +198,7 @@ "link_fieldname": "reference_docname" } ], - "modified": "2026-01-03 17:29:18.102192", + "modified": "2026-01-27 22:42:24.144504", "modified_by": "Administrator", "module": "Ticketing", "name": "Event Booking", diff --git a/buzz/ticketing/doctype/event_booking/event_booking.py b/buzz/ticketing/doctype/event_booking/event_booking.py index 696be6bf..416666bf 100644 --- a/buzz/ticketing/doctype/event_booking/event_booking.py +++ b/buzz/ticketing/doctype/event_booking/event_booking.py @@ -16,11 +16,10 @@ class EventBooking(Document): from typing import TYPE_CHECKING if TYPE_CHECKING: - from frappe.types import DF - from buzz.events.doctype.utm_parameter.utm_parameter import UTMParameter from buzz.ticketing.doctype.additional_field.additional_field import AdditionalField from buzz.ticketing.doctype.event_booking_attendee.event_booking_attendee import EventBookingAttendee + from frappe.types import DF additional_fields: DF.Table[AdditionalField] amended_from: DF.Link | None @@ -222,6 +221,51 @@ def cancel_all_tickets(self): for ticket in tickets: frappe.get_cached_doc("Event Ticket", ticket).cancel() + @frappe.whitelist() + def verify_upi_payment(self): + """Verify UPI payment for this booking.""" + frappe.only_for("Event Manager") + + # Check if already verified + existing = frappe.db.exists("Additional Field", { + "parent": self.name, + "fieldname": "payment_verified", + "value": "Yes" + }) + if existing: + frappe.msgprint("Payment is already verified!") + return + + # Add payment verified field using direct SQL + frappe.db.sql( + """INSERT INTO `tabAdditional Field` + (name, parent, parenttype, parentfield, fieldname, value, label, fieldtype) + VALUES (%(name)s, %(parent)s, 'Event Booking', 'additional_fields', %(fieldname)s, %(value)s, %(label)s, %(fieldtype)s)""", + { + "name": frappe.generate_hash(length=10), + "parent": self.name, + "fieldname": "payment_verified", + "value": "Yes", + "label": "Payment Verified", + "fieldtype": "Data" + } + ) + frappe.db.commit() + frappe.msgprint("UPI Payment verified successfully!") + + @frappe.whitelist() + def unverify_upi_payment(self): + """Remove UPI payment verification.""" + frappe.only_for("Event Manager") + + # Remove payment verified field using direct SQL + frappe.db.sql( + "DELETE FROM `tabAdditional Field` WHERE parent = %s AND fieldname = 'payment_verified'", + (self.name,) + ) + frappe.db.commit() + frappe.msgprint("UPI Payment marked as unverified!") + def apply_coupon_if_applicable(self): self.discount_amount = 0 @@ -287,4 +331,4 @@ def apply_coupon_if_applicable(self): if discounted == 0: frappe.throw(_("No attendees with eligible ticket type for this coupon")) - self.total_amount = self.net_amount - self.discount_amount + self.total_amount = self.net_amount - self.discount_amount \ No newline at end of file diff --git a/dashboard/components.d.ts b/dashboard/components.d.ts index 5708600e..9598e1ad 100644 --- a/dashboard/components.d.ts +++ b/dashboard/components.d.ts @@ -42,5 +42,6 @@ declare module 'vue' { TicketsSection: typeof import('./src/components/TicketsSection.vue')['default'] TicketTransferDialog: typeof import('./src/components/TicketTransferDialog.vue')['default'] TransferTicketDialog: typeof import('./src/components/TransferTicketDialog.vue')['default'] + UPIPaymentDialog: typeof import('./src/components/UPIPaymentDialog.vue')['default'] } } diff --git a/dashboard/src/components/BookingForm.vue b/dashboard/src/components/BookingForm.vue index 9b0b2b05..1e4f3da1 100644 --- a/dashboard/src/components/BookingForm.vue +++ b/dashboard/src/components/BookingForm.vue @@ -89,6 +89,18 @@
+ + + +
@@ -370,6 +382,7 @@ import BookingSummary from "./BookingSummary.vue"; import EventDetailsHeader from "./EventDetailsHeader.vue"; import CustomFieldsSection from "./CustomFieldsSection.vue"; import PaymentGatewayDialog from "./PaymentGatewayDialog.vue"; +import UPIPaymentDialog from "./UPIPaymentDialog.vue"; import { createResource, toast, FormControl } from "frappe-ui"; import { formatPriceOrFree, formatCurrency } from "../utils/currency.js"; import { useBookingFormStorage } from "../composables/useBookingFormStorage.js"; @@ -434,6 +447,13 @@ const props = defineProps({ isGuestMode: { type: Boolean, default: false, + upiPaymentEnabled: { + type: Boolean, + default: false + }, + upiSettings: { + type: Object, + default: () => ({}) }, }); @@ -453,6 +473,7 @@ const bookingCustomFieldsData = storedBookingCustomFields; // Payment gateway dialog state const showGatewayDialog = ref(false); +const showUPIDialog = ref(false); const pendingPayload = ref(null); const selectedGateway = ref(null); @@ -1073,6 +1094,13 @@ async function submit() { pendingBookingPayload.value = final_payload; if (finalTotal.value > 0 && props.paymentGateways.length > 1) { + // Check if we need to show UPI dialog or gateway selection dialog + if (finalTotal.value > 0) { + if (props.upiPaymentEnabled && (!props.paymentGateways.length || confirm(__("Use UPI Payment instead of gateway?")))) { + pendingPayload.value = final_payload; + showUPIDialog.value = true; + return; + } else if (props.paymentGateways.length > 1) { pendingPayload.value = final_payload; showGatewayDialog.value = true; return; @@ -1117,6 +1145,9 @@ function submitBooking(payload, paymentGateway, { isOtpFlow = false } = {}) { } else if (props.isGuestMode) { bookingSuccess.value = true; successBookingName.value = data.booking_name; + } else if (data.upi_payment) { + // UPI payment submitted - redirect to booking details with UPI flag + router.replace(`/bookings/${data.booking_name}?success=true&upi=true`); } else { // free event router.replace(`/bookings/${data.booking_name}?success=true`); @@ -1142,6 +1173,15 @@ function submitBooking(payload, paymentGateway, { isOtpFlow = false } = {}) { ); } +function onUPIPaymentSubmit(paymentProof) { + if (pendingPayload.value) { + // For UPI payments, submit without payment gateway + submitBooking(pendingPayload.value, null); + pendingPayload.value = null; + showUPIDialog.value = false; + } +} + function onGatewaySelected(gateway) { if (props.isGuestMode) { selectedGateway.value = gateway; diff --git a/dashboard/src/components/UPIPaymentDialog.vue b/dashboard/src/components/UPIPaymentDialog.vue new file mode 100644 index 00000000..07da115f --- /dev/null +++ b/dashboard/src/components/UPIPaymentDialog.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/dashboard/src/pages/BookTickets.vue b/dashboard/src/pages/BookTickets.vue index 713e6399..ce28bcb6 100644 --- a/dashboard/src/pages/BookTickets.vue +++ b/dashboard/src/pages/BookTickets.vue @@ -30,6 +30,8 @@ :eventRoute="eventRoute" :paymentGateways="eventBookingData.paymentGateways" :isGuestMode="isGuest" + :upiPaymentEnabled="eventBookingData.upiPaymentEnabled" + :upiSettings="eventBookingData.upiSettings" />
@@ -49,6 +51,8 @@ const eventBookingData = reactive({ eventDetails: null, customFields: null, paymentGateways: [], + upiPaymentEnabled: false, + upiSettings: {}, }); const props = defineProps({ @@ -81,6 +85,8 @@ const eventBookingResource = createResource({ eventBookingData.eventDetails = data.event_details || {}; eventBookingData.customFields = data.custom_fields || []; eventBookingData.paymentGateways = data.payment_gateways || []; + eventBookingData.upiPaymentEnabled = data.upi_payment_enabled || false; + eventBookingData.upiSettings = data.upi_settings || {}; }, onError: (error) => { if (error.message.includes("DoesNotExistError")) { diff --git a/dashboard/src/pages/BookingDetails.vue b/dashboard/src/pages/BookingDetails.vue index eb38f690..a5fcc31a 100644 --- a/dashboard/src/pages/BookingDetails.vue +++ b/dashboard/src/pages/BookingDetails.vue @@ -6,6 +6,25 @@
+ +
+
+
+
+ +
+
+

+ {{ __("Payment Confirmation Pending") }} +

+

+ {{ __("Your booking is confirmed subject to verifying the UPI/QR code payment details. You will be notified once payment is verified.") }} +

+
+
+
+
+
@@ -39,7 +58,7 @@ { + if (!bookingDetails.data?.doc?.additional_fields) return false; + const paymentMethod = bookingDetails.data.doc.additional_fields.find( + field => field.fieldname === 'payment_method' + ); + const paymentStatus = bookingDetails.data.doc.additional_fields.find( + field => field.fieldname === 'payment_verified' + ); + // Show message only if it's UPI payment and not yet verified + return paymentMethod?.value === 'UPI' && paymentStatus?.value !== 'Yes'; +}); + +// Check if UPI payment is verified +const isUpiPaymentVerified = computed(() => { + if (!bookingDetails.data?.doc?.additional_fields) return true; // Non-UPI payments are considered verified + const paymentMethod = bookingDetails.data.doc.additional_fields.find( + field => field.fieldname === 'payment_method' + ); + const paymentStatus = bookingDetails.data.doc.additional_fields.find( + field => field.fieldname === 'payment_verified' + ); + // If it's UPI payment, check if verified; otherwise return true + return paymentMethod?.value !== 'UPI' || paymentStatus?.value === 'Yes'; +}); + // Check if this is a successful payment redirect (check URL immediately) const isPaymentSuccess = route.query.success === "true"; From e242a26291419211d6b762fc3746f655e715cfa3 Mon Sep 17 00:00:00 2001 From: rehanrehman389 Date: Tue, 3 Feb 2026 16:27:59 +0000 Subject: [PATCH 02/32] chore: replace UPI with generic off-platform payments --- buzz/api.py | 26 ++++++++-------- .../events/doctype/buzz_event/buzz_event.json | 26 ++++++++-------- .../doctype/event_booking/event_booking.js | 18 +++++------ .../doctype/event_booking/event_booking.py | 12 ++++---- dashboard/src/components/BookingForm.vue | 30 ++++++++++++------- ...ialog.vue => OffPlatformPaymentDialog.vue} | 14 ++++----- dashboard/src/pages/BookTickets.vue | 10 ++++--- dashboard/src/pages/BookingDetails.vue | 28 ++++++++--------- 8 files changed, 87 insertions(+), 77 deletions(-) rename dashboard/src/components/{UPIPaymentDialog.vue => OffPlatformPaymentDialog.vue} (86%) diff --git a/buzz/api.py b/buzz/api.py index c3b8853e..0276dc0f 100644 --- a/buzz/api.py +++ b/buzz/api.py @@ -307,13 +307,13 @@ def get_event_booking_data(event_route: str) -> dict: # Payment Gateways data.payment_gateways = get_payment_gateways_for_event(event_doc.name) - # UPI Payment Settings - data.upi_payment_enabled = event_doc.enable_upi_payment - if event_doc.enable_upi_payment: - data.upi_settings = { - "upi_id": event_doc.upi_id, - "qr_code": event_doc.upi_qr_code, - "instructions": event_doc.upi_instructions + # Off-platform Payment Settings + data.off_platform_payment_enabled = event_doc.enable_off_platform_payment + if event_doc.enable_off_platform_payment: + data.off_platform_settings = { + "payment_id": event_doc.off_platform_payment_id, + "qr_code": event_doc.off_platform_qr_code, + "instructions": event_doc.off_platform_instructions } return data @@ -430,20 +430,20 @@ def process_booking( booking.submit() return {"booking_name": booking.name} - # Check if UPI payment is enabled and no payment gateway is provided + # Check if off-platform payment is enabled and no payment gateway is provided event_doc = frappe.get_cached_doc("Buzz Event", event) - if event_doc.enable_upi_payment and not payment_gateway: - # For UPI payments, submit booking directly without payment gateway - # Mark this as UPI payment in additional fields + if event_doc.enable_off_platform_payment and not payment_gateway: + # For off-platform payments, submit booking directly without payment gateway + # Mark this as off-platform payment in additional fields booking.append("additional_fields", { "fieldname": "payment_method", - "value": "UPI", + "value": "Off-platform", "label": "Payment Method", "fieldtype": "Data" }) booking.flags.ignore_permissions = True booking.submit() - return {"booking_name": booking.name, "upi_payment": True} + return {"booking_name": booking.name, "off_platform_payment": True} return { "payment_link": get_payment_link_for_booking( diff --git a/buzz/events/doctype/buzz_event/buzz_event.json b/buzz/events/doctype/buzz_event/buzz_event.json index 26b6bb62..6204953c 100644 --- a/buzz/events/doctype/buzz_event/buzz_event.json +++ b/buzz/events/doctype/buzz_event/buzz_event.json @@ -381,32 +381,32 @@ "label": "Tax Settings" }, { - "description": "Copy UPI ID or scan QR and upload screenshot for verification", - "fieldname": "upi_payment_section", + "description": "Copy payment details or scan QR and upload proof for verification", + "fieldname": "off_platform_payment_section", "fieldtype": "Section Break", - "label": "UPI Payment via QR / UPI ID" + "label": "Off-platform Payment Settings" }, { "default": "0", - "fieldname": "enable_upi_payment", + "fieldname": "enable_off_platform_payment", "fieldtype": "Check", - "label": "Allow Manual UPI Payment" + "label": "Enable Off-platform Payments" }, { - "depends_on": "eval:doc.enable_upi_payment==1", - "fieldname": "upi_id", + "depends_on": "eval:doc.enable_off_platform_payment==1", + "fieldname": "off_platform_payment_id", "fieldtype": "Data", - "label": "Merchant UPI ID" + "label": "Payment ID (UPI ID, Account Number, etc.)" }, { - "depends_on": "eval:doc.enable_upi_payment==1", - "fieldname": "upi_qr_code", + "depends_on": "eval:doc.enable_off_platform_payment==1", + "fieldname": "off_platform_qr_code", "fieldtype": "Attach Image", - "label": "UPI QR Code" + "label": "Payment QR Code" }, { - "depends_on": "eval:doc.enable_upi_payment==1", - "fieldname": "upi_instructions", + "depends_on": "eval:doc.enable_off_platform_payment==1", + "fieldname": "off_platform_instructions", "fieldtype": "Small Text", "label": "Payment Instructions" }, diff --git a/buzz/ticketing/doctype/event_booking/event_booking.js b/buzz/ticketing/doctype/event_booking/event_booking.js index 945b65f1..d0b52b90 100644 --- a/buzz/ticketing/doctype/event_booking/event_booking.js +++ b/buzz/ticketing/doctype/event_booking/event_booking.js @@ -11,14 +11,14 @@ frappe.ui.form.on("Event Booking", { }; }); - // Check if this is a UPI payment - let isUpiPayment = false; + // Check if this is an off-platform payment + let isOffPlatformPayment = false; let isVerified = false; if (frm.doc.additional_fields) { for (let field of frm.doc.additional_fields) { - if (field.fieldname === 'payment_method' && field.value === 'UPI') { - isUpiPayment = true; + if (field.fieldname === 'payment_method' && field.value === 'Off-platform') { + isOffPlatformPayment = true; } if (field.fieldname === 'payment_verified' && field.value === 'Yes') { isVerified = true; @@ -27,15 +27,15 @@ frappe.ui.form.on("Event Booking", { } // Hide both buttons first - frm.toggle_display('upi_payment_verification', false); - frm.toggle_display('upi_payment_unverify', false); + frm.toggle_display('off_platform_payment_verification', false); + frm.toggle_display('off_platform_payment_unverify', false); // Show appropriate button based on status - if (isUpiPayment && frappe.user.has_role('Event Manager')) { + if (isOffPlatformPayment && frappe.user.has_role('Event Manager')) { if (isVerified) { - frm.toggle_display('upi_payment_unverify', true); + frm.toggle_display('off_platform_payment_unverify', true); } else { - frm.toggle_display('upi_payment_verification', true); + frm.toggle_display('off_platform_payment_verification', true); } } }, diff --git a/buzz/ticketing/doctype/event_booking/event_booking.py b/buzz/ticketing/doctype/event_booking/event_booking.py index 416666bf..a2bc4cc4 100644 --- a/buzz/ticketing/doctype/event_booking/event_booking.py +++ b/buzz/ticketing/doctype/event_booking/event_booking.py @@ -222,8 +222,8 @@ def cancel_all_tickets(self): frappe.get_cached_doc("Event Ticket", ticket).cancel() @frappe.whitelist() - def verify_upi_payment(self): - """Verify UPI payment for this booking.""" + def verify_off_platform_payment(self): + """Verify off-platform payment for this booking.""" frappe.only_for("Event Manager") # Check if already verified @@ -251,11 +251,11 @@ def verify_upi_payment(self): } ) frappe.db.commit() - frappe.msgprint("UPI Payment verified successfully!") + frappe.msgprint("Off-platform payment verified successfully!") @frappe.whitelist() - def unverify_upi_payment(self): - """Remove UPI payment verification.""" + def unverify_off_platform_payment(self): + """Remove off-platform payment verification.""" frappe.only_for("Event Manager") # Remove payment verified field using direct SQL @@ -264,7 +264,7 @@ def unverify_upi_payment(self): (self.name,) ) frappe.db.commit() - frappe.msgprint("UPI Payment marked as unverified!") + frappe.msgprint("Off-platform payment marked as unverified!") def apply_coupon_if_applicable(self): self.discount_amount = 0 diff --git a/dashboard/src/components/BookingForm.vue b/dashboard/src/components/BookingForm.vue index 1e4f3da1..d654c48e 100644 --- a/dashboard/src/components/BookingForm.vue +++ b/dashboard/src/components/BookingForm.vue @@ -92,12 +92,15 @@ + @@ -382,7 +385,7 @@ import BookingSummary from "./BookingSummary.vue"; import EventDetailsHeader from "./EventDetailsHeader.vue"; import CustomFieldsSection from "./CustomFieldsSection.vue"; import PaymentGatewayDialog from "./PaymentGatewayDialog.vue"; -import UPIPaymentDialog from "./UPIPaymentDialog.vue"; +import OffPlatformPaymentDialog from "./OffPlatformPaymentDialog.vue"; import { createResource, toast, FormControl } from "frappe-ui"; import { formatPriceOrFree, formatCurrency } from "../utils/currency.js"; import { useBookingFormStorage } from "../composables/useBookingFormStorage.js"; @@ -448,10 +451,11 @@ const props = defineProps({ type: Boolean, default: false, upiPaymentEnabled: { + offPlatformPaymentEnabled: { type: Boolean, default: false }, - upiSettings: { + offPlatformSettings: { type: Object, default: () => ({}) }, @@ -473,7 +477,7 @@ const bookingCustomFieldsData = storedBookingCustomFields; // Payment gateway dialog state const showGatewayDialog = ref(false); -const showUPIDialog = ref(false); +const showOffPlatformDialog = ref(false); const pendingPayload = ref(null); const selectedGateway = ref(null); @@ -1095,10 +1099,11 @@ async function submit() { if (finalTotal.value > 0 && props.paymentGateways.length > 1) { // Check if we need to show UPI dialog or gateway selection dialog + // Check if we need to show off-platform dialog or gateway selection dialog if (finalTotal.value > 0) { - if (props.upiPaymentEnabled && (!props.paymentGateways.length || confirm(__("Use UPI Payment instead of gateway?")))) { + if (props.offPlatformPaymentEnabled && (!props.paymentGateways.length || confirm(__("Use Off-platform Payment instead of gateway?")))) { pendingPayload.value = final_payload; - showUPIDialog.value = true; + showOffPlatformDialog.value = true; return; } else if (props.paymentGateways.length > 1) { pendingPayload.value = final_payload; @@ -1148,6 +1153,9 @@ function submitBooking(payload, paymentGateway, { isOtpFlow = false } = {}) { } else if (data.upi_payment) { // UPI payment submitted - redirect to booking details with UPI flag router.replace(`/bookings/${data.booking_name}?success=true&upi=true`); + } else if (data.off_platform_payment) { + // Off-platform payment submitted - redirect to booking details with off-platform flag + router.replace(`/bookings/${data.booking_name}?success=true&off_platform=true`); } else { // free event router.replace(`/bookings/${data.booking_name}?success=true`); @@ -1173,12 +1181,12 @@ function submitBooking(payload, paymentGateway, { isOtpFlow = false } = {}) { ); } -function onUPIPaymentSubmit(paymentProof) { +function onOffPlatformPaymentSubmit(paymentProof) { if (pendingPayload.value) { - // For UPI payments, submit without payment gateway + // For off-platform payments, submit without payment gateway submitBooking(pendingPayload.value, null); pendingPayload.value = null; - showUPIDialog.value = false; + showOffPlatformDialog.value = false; } } diff --git a/dashboard/src/components/UPIPaymentDialog.vue b/dashboard/src/components/OffPlatformPaymentDialog.vue similarity index 86% rename from dashboard/src/components/UPIPaymentDialog.vue rename to dashboard/src/components/OffPlatformPaymentDialog.vue index 07da115f..81e1c58e 100644 --- a/dashboard/src/components/UPIPaymentDialog.vue +++ b/dashboard/src/components/OffPlatformPaymentDialog.vue @@ -2,7 +2,7 @@