import Member from '../../models/member.js' import Series from '../../models/series.js' import { loadPublicEvent } from '../../utils/loadEvent.js' import { calculateTicketPrice, calculateSeriesTicketPrice, hasMemberAccess } from '../../utils/tickets.js' import { requireAuth, getOptionalMember, getPaymentBridgeMember } from '../../utils/auth.js' import { initializeHelcimPaySession } from '../../utils/helcim.js' export default defineEventHandler(async (event) => { try { const body = await validateBody(event, helcimInitializePaymentSchema) const metaType = body.metadata?.type const isEventTicket = metaType === 'event_ticket' const isSeriesTicket = metaType === 'series_ticket' const isTicket = isEventTicket || isSeriesTicket const isMembershipSignup = metaType === 'membership_signup' if (!isTicket) { if (isMembershipSignup) { const bridgeMember = await getPaymentBridgeMember(event) if (!bridgeMember) { await requireAuth(event) } } else { await requireAuth(event) } } let amount = 0 let description = body.metadata?.eventTitle if (isTicket) { // Re-derive price server-side; never trust body.amount for ticket flows. const caller = await getOptionalMember(event).catch(() => null) const lookupEmail = body.metadata?.email?.toLowerCase() const member = caller || (lookupEmail ? await Member.findOne({ email: lookupEmail }) : null) const accessMember = hasMemberAccess(member) ? member : null if (isEventTicket) { const eventId = body.metadata?.eventId if (!eventId) { throw createError({ statusCode: 400, statusMessage: 'metadata.eventId is required for event_ticket' }) } const eventDoc = await loadPublicEvent(event, eventId) const ticketInfo = calculateTicketPrice(eventDoc, accessMember) if (!ticketInfo) { throw createError({ statusCode: 403, statusMessage: 'No tickets available for your membership status' }) } amount = ticketInfo.price description = description || eventDoc.title } else { const seriesId = body.metadata?.seriesId if (!seriesId) { throw createError({ statusCode: 400, statusMessage: 'metadata.seriesId is required for series_ticket' }) } const isObjectId = /^[0-9a-fA-F]{24}$/.test(seriesId) const seriesQuery = isObjectId ? { $or: [{ _id: seriesId }, { id: seriesId }, { slug: seriesId }] } : { $or: [{ id: seriesId }, { slug: seriesId }] } const series = await Series.findOne(seriesQuery) if (!series) { throw createError({ statusCode: 404, statusMessage: 'Series not found' }) } const ticketInfo = calculateSeriesTicketPrice(series, accessMember) if (!ticketInfo) { throw createError({ statusCode: 403, statusMessage: 'No series passes available for your membership status' }) } amount = ticketInfo.price description = description || series.title } } else { amount = body.amount || 0 } const paymentType = isTicket && amount > 0 ? 'purchase' : 'verify' const requestBody = { paymentType, amount: paymentType === 'purchase' ? amount : 0, currency: 'CAD', paymentMethod: 'cc' } if (body.customerCode && paymentType === 'verify') { requestBody.customerCode = body.customerCode } if (description) { requestBody.description = description requestBody.notes = description const orderId = body.metadata?.eventId || body.metadata?.seriesId if (orderId) requestBody.orderNumber = `${orderId}` } const paymentData = await initializeHelcimPaySession(requestBody) return { success: true, checkoutToken: paymentData.checkoutToken, secretToken: paymentData.secretToken, // Echo derived amount so the client can sanity-check before opening modal. amount } } catch (error) { if (error.statusCode) throw error console.error('Error initializing HelcimPay:', error) throw createError({ statusCode: 500, statusMessage: 'An unexpected error occurred' }) } })