import Event from "../../../../models/event.js"; import Member from "../../../../models/member.js"; import { connectDB } from "../../../../utils/mongoose.js"; import { validateTicketPurchase, calculateTicketPrice, completeTicketPurchase, } from "../../../../utils/tickets.js"; import { sendEventRegistrationEmail } from "../../../../utils/resend.js"; import mongoose from "mongoose"; /** * POST /api/events/[id]/tickets/purchase * Purchase a ticket for an event * Body: { name, email, paymentToken? } */ export default defineEventHandler(async (event) => { try { await connectDB(); const identifier = getRouterParam(event, "id"); const body = await readBody(event); if (!identifier) { throw createError({ statusCode: 400, statusMessage: "Event identifier is required", }); } // Validate required fields if (!body.name || !body.email) { throw createError({ statusCode: 400, statusMessage: "Name and email are required", }); } // Fetch the event let eventData; if (mongoose.Types.ObjectId.isValid(identifier)) { eventData = await Event.findById(identifier); } if (!eventData) { eventData = await Event.findOne({ slug: identifier }); } if (!eventData) { throw createError({ statusCode: 404, statusMessage: "Event not found", }); } // Check if user is a member const member = await Member.findOne({ email: body.email.toLowerCase() }); // Validate ticket purchase const validation = validateTicketPurchase(eventData, { email: body.email, name: body.name, member, }); if (!validation.valid) { throw createError({ statusCode: 400, statusMessage: validation.reason, data: { waitlistAvailable: validation.waitlistAvailable, }, }); } const { ticketInfo } = validation; const requiresPayment = ticketInfo.price > 0; // Handle payment if required let transactionId = null; if (requiresPayment) { // For HelcimPay.js with purchase type, the transaction is already completed // We just need to verify we received the transaction ID if (!body.transactionId) { throw createError({ statusCode: 400, statusMessage: "Transaction ID is required. Payment must be completed first.", }); } transactionId = body.transactionId; // Optional: Verify the transaction with Helcim API // This adds extra security to ensure the transaction is legitimate // For now, we trust the transaction ID from HelcimPay.js console.log("Payment completed with transaction ID:", transactionId); } // Create registration const registration = { memberId: member ? member._id : null, name: body.name, email: body.email.toLowerCase(), membershipLevel: member ? `${member.circle}-${member.contributionTier}` : "non-member", isMember: !!member, ticketType: ticketInfo.ticketType, ticketPrice: ticketInfo.price, paymentStatus: requiresPayment ? "completed" : "not_required", paymentId: transactionId, amountPaid: ticketInfo.price, registeredAt: new Date(), }; // Add registration to event eventData.registrations.push(registration); // Complete ticket purchase (updates sold/reserved counts) await completeTicketPurchase(eventData, ticketInfo.ticketType); // Save event with registration await eventData.save(); // Send confirmation email try { await sendEventRegistrationEmail(registration, eventData); } catch (emailError) { console.error("Failed to send confirmation email:", emailError); // Don't fail the registration if email fails } return { success: true, message: "Ticket purchased successfully!", registration: { id: eventData.registrations[eventData.registrations.length - 1]._id, name: registration.name, email: registration.email, ticketType: registration.ticketType, amountPaid: registration.amountPaid, }, payment: transactionId ? { transactionId: transactionId, amount: ticketInfo.price, currency: ticketInfo.currency, } : null, }; } catch (error) { console.error("Error purchasing ticket:", error); if (error.statusCode) { throw error; } throw createError({ statusCode: 500, statusMessage: "Failed to purchase ticket. Please try again.", }); } });