diff --git a/server/api/helcim/customer-code.get.js b/server/api/helcim/customer-code.get.js index df2253e..943f028 100644 --- a/server/api/helcim/customer-code.get.js +++ b/server/api/helcim/customer-code.get.js @@ -1,42 +1,10 @@ // Get customer code for an existing Helcim customer -import jwt from 'jsonwebtoken' -import Member from '../../models/member.js' -import { connectDB } from '../../utils/mongoose.js' - -const HELCIM_API_BASE = 'https://api.helcim.com/v2' +import { requireAuth } from '../../utils/auth.js' +import { getHelcimCustomer } from '../../utils/helcim.js' export default defineEventHandler(async (event) => { try { - await connectDB() - const config = useRuntimeConfig(event) - const token = getCookie(event, 'auth-token') - - if (!token) { - throw createError({ - statusCode: 401, - statusMessage: 'Not authenticated' - }) - } - - // Decode JWT token - let decoded - try { - decoded = jwt.verify(token, useRuntimeConfig().jwtSecret) - } catch (err) { - throw createError({ - statusCode: 401, - statusMessage: 'Invalid or expired token' - }) - } - - // Get member - const member = await Member.findById(decoded.memberId) - if (!member) { - throw createError({ - statusCode: 404, - statusMessage: 'Member not found' - }) - } + const member = await requireAuth(event) if (!member.helcimCustomerId) { throw createError({ @@ -45,27 +13,7 @@ export default defineEventHandler(async (event) => { }) } - const helcimToken = config.helcimApiToken - - const response = await fetch( - `${HELCIM_API_BASE}/customers/${member.helcimCustomerId}`, - { - headers: { - 'accept': 'application/json', - 'api-token': helcimToken - } - } - ) - - if (!response.ok) { - const errorText = await response.text() - throw createError({ - statusCode: response.status, - statusMessage: 'Customer lookup failed' - }) - } - - const customerData = await response.json() + const customerData = await getHelcimCustomer(member.helcimCustomerId) return { success: true, diff --git a/server/api/helcim/customer.post.js b/server/api/helcim/customer.post.js index fced502..2120053 100644 --- a/server/api/helcim/customer.post.js +++ b/server/api/helcim/customer.post.js @@ -2,8 +2,7 @@ import jwt from 'jsonwebtoken' import Member from '../../models/member.js' import { connectDB } from '../../utils/mongoose.js' - -const HELCIM_API_BASE = 'https://api.helcim.com/v2' +import { createHelcimCustomer } from '../../utils/helcim.js' export default defineEventHandler(async (event) => { try { @@ -20,65 +19,12 @@ export default defineEventHandler(async (event) => { }) } - // Get token directly from environment if not in config - const helcimToken = config.helcimApiToken - - if (!helcimToken) { - throw createError({ - statusCode: 500, - statusMessage: 'Helcim API token not configured' - }) - } - - - // Test the connection first with native fetch - try { - const testResponse = await fetch('https://api.helcim.com/v2/connection-test', { - method: 'GET', - headers: { - 'accept': 'application/json', - 'api-token': helcimToken - } - }) - - if (!testResponse.ok) { - throw new Error(`HTTP ${testResponse.status}: ${testResponse.statusText}`) - } - - await testResponse.json() - } catch (testError) { - console.error('Connection test failed:', testError) - throw createError({ - statusCode: 401, - statusMessage: 'Payment service unavailable' - }) - } - - // Create customer in Helcim using native fetch - const customerResponse = await fetch(`${HELCIM_API_BASE}/customers`, { - method: 'POST', - headers: { - 'accept': 'application/json', - 'content-type': 'application/json', - 'api-token': helcimToken - }, - body: JSON.stringify({ - customerType: 'PERSON', - contactName: body.name, - email: body.email - }) + // Create customer in Helcim + const customerData = await createHelcimCustomer({ + customerType: 'PERSON', + contactName: body.name, + email: body.email }) - - if (!customerResponse.ok) { - const errorText = await customerResponse.text() - console.error('Customer creation failed:', customerResponse.status, errorText) - throw createError({ - statusCode: customerResponse.status, - statusMessage: 'Customer creation failed' - }) - } - - const customerData = await customerResponse.json() // Create member in database const member = await Member.create({ @@ -92,10 +38,10 @@ export default defineEventHandler(async (event) => { // Generate JWT token for the session const token = jwt.sign( - { + { memberId: member._id, email: body.email, - helcimCustomerId: customerData.id + helcimCustomerId: customerData.id }, config.jwtSecret, { expiresIn: '7d' } @@ -132,4 +78,4 @@ export default defineEventHandler(async (event) => { statusMessage: 'An unexpected error occurred' }) } -}) \ No newline at end of file +}) diff --git a/server/api/helcim/get-or-create-customer.post.js b/server/api/helcim/get-or-create-customer.post.js index 34baf28..7264246 100644 --- a/server/api/helcim/get-or-create-customer.post.js +++ b/server/api/helcim/get-or-create-customer.post.js @@ -1,112 +1,43 @@ // Get existing or create new Helcim customer (for upgrading members) -import jwt from 'jsonwebtoken' -import Member from '../../models/member.js' -import { connectDB } from '../../utils/mongoose.js' - -const HELCIM_API_BASE = 'https://api.helcim.com/v2' +import { requireAuth } from '../../utils/auth.js' +import { findHelcimCustomerByEmail, createHelcimCustomer } from '../../utils/helcim.js' export default defineEventHandler(async (event) => { try { - await connectDB() - const config = useRuntimeConfig(event) - const token = getCookie(event, 'auth-token') + const member = await requireAuth(event) - if (!token) { - throw createError({ - statusCode: 401, - statusMessage: 'Not authenticated' - }) - } - - // Decode JWT token - let decoded + // First, try to find an existing customer + let existingCustomer = null try { - decoded = jwt.verify(token, useRuntimeConfig().jwtSecret) - } catch (err) { - throw createError({ - statusCode: 401, - statusMessage: 'Invalid or expired token' - }) - } - - // Get member - const member = await Member.findById(decoded.memberId) - if (!member) { - throw createError({ - statusCode: 404, - statusMessage: 'Member not found' - }) - } - - const helcimToken = config.helcimApiToken - - // First, search for existing customer - try { - const searchResponse = await fetch( - `${HELCIM_API_BASE}/customers?search=${encodeURIComponent(member.email)}`, - { - headers: { - 'accept': 'application/json', - 'api-token': helcimToken - } - } - ) - - if (searchResponse.ok) { - const searchData = await searchResponse.json() - - if (searchData.customers && searchData.customers.length > 0) { - const existingCustomer = searchData.customers.find(c => c.email === member.email) - - if (existingCustomer) { - - // Update member record with customer ID if not already set - if (!member.helcimCustomerId) { - member.helcimCustomerId = existingCustomer.id - await member.save() - } - - return { - success: true, - customerId: existingCustomer.id, - customerCode: existingCustomer.customerCode, - existing: true - } - } - } + const searchData = await findHelcimCustomerByEmail(member.email) + if (searchData.customers && searchData.customers.length > 0) { + existingCustomer = searchData.customers.find(c => c.email === member.email) || null } } catch (searchError) { console.error('Error searching for customer:', searchError) - // Continue to create new customer + // Fall through to create } - // No existing customer found, create new one - const createResponse = await fetch(`${HELCIM_API_BASE}/customers`, { - method: 'POST', - headers: { - 'accept': 'application/json', - 'content-type': 'application/json', - 'api-token': helcimToken - }, - body: JSON.stringify({ - contactName: member.name, - businessName: member.name, - email: member.email - }) + if (existingCustomer) { + if (!member.helcimCustomerId) { + member.helcimCustomerId = existingCustomer.id + await member.save() + } + return { + success: true, + customerId: existingCustomer.id, + customerCode: existingCustomer.customerCode, + existing: true + } + } + + // No existing customer found — create one + const customerData = await createHelcimCustomer({ + contactName: member.name, + businessName: member.name, + email: member.email }) - if (!createResponse.ok) { - const errorText = await createResponse.text() - console.error('Failed to create Helcim customer:', createResponse.status, errorText) - throw createError({ - statusCode: createResponse.status, - statusMessage: 'Customer creation failed' - }) - } - - const customerData = await createResponse.json() - - // Update member record with customer ID member.helcimCustomerId = customerData.id await member.save() diff --git a/server/api/helcim/initialize-payment.post.js b/server/api/helcim/initialize-payment.post.js index f14c6ea..26f9e94 100644 --- a/server/api/helcim/initialize-payment.post.js +++ b/server/api/helcim/initialize-payment.post.js @@ -1,86 +1,59 @@ // Initialize HelcimPay.js session -import { requireAuth } from "../../utils/auth.js"; - -const HELCIM_API_BASE = "https://api.helcim.com/v2"; +import { requireAuth } from '../../utils/auth.js' +import { initializeHelcimPaySession } from '../../utils/helcim.js' export default defineEventHandler(async (event) => { try { - const config = useRuntimeConfig(event); - const body = await validateBody(event, helcimInitializePaymentSchema); + const body = await validateBody(event, helcimInitializePaymentSchema) // Event ticket purchases can be made without authentication - const isEventTicket = body.metadata?.type === "event_ticket"; + const isEventTicket = body.metadata?.type === 'event_ticket' if (!isEventTicket) { - await requireAuth(event); + await requireAuth(event) } - const helcimToken = config.helcimApiToken; - const amount = body.amount || 0; + const amount = body.amount || 0 // For event tickets with amount > 0, we do a purchase // For subscriptions or card verification, we do verify - const paymentType = isEventTicket && amount > 0 ? "purchase" : "verify"; + const paymentType = isEventTicket && amount > 0 ? 'purchase' : 'verify' const requestBody = { paymentType, - amount: paymentType === "purchase" ? amount : 0, - currency: "CAD", - paymentMethod: "cc", - }; + amount: paymentType === 'purchase' ? amount : 0, + currency: 'CAD', + paymentMethod: 'cc' + } // For subscription setup (verify mode), include customer code if provided // For one-time purchases (event tickets), don't include customer code // as the customer may not exist in Helcim yet - if (body.customerCode && paymentType === "verify") { - requestBody.customerCode = body.customerCode; + if (body.customerCode && paymentType === 'verify') { + requestBody.customerCode = body.customerCode } // Add product/event information for better display in Helcim modal if (body.metadata?.eventTitle) { // Some Helcim accounts don't support invoice numbers in initialization // Try multiple fields that might display in the modal - requestBody.description = body.metadata.eventTitle; - requestBody.notes = body.metadata.eventTitle; - requestBody.orderNumber = `${body.metadata.eventId}`; + requestBody.description = body.metadata.eventTitle + requestBody.notes = body.metadata.eventTitle + requestBody.orderNumber = `${body.metadata.eventId}` } - // Initialize HelcimPay.js session - const response = await fetch(`${HELCIM_API_BASE}/helcim-pay/initialize`, { - method: "POST", - headers: { - accept: "application/json", - "content-type": "application/json", - "api-token": helcimToken, - }, - body: JSON.stringify(requestBody), - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error( - "HelcimPay initialization failed:", - response.status, - errorText, - ); - throw createError({ - statusCode: response.status, - statusMessage: 'Payment initialization failed', - }); - } - - const paymentData = await response.json(); + const paymentData = await initializeHelcimPaySession(requestBody) return { success: true, checkoutToken: paymentData.checkoutToken, - secretToken: paymentData.secretToken, - }; + secretToken: paymentData.secretToken + } } catch (error) { - if (error.statusCode) throw error; - console.error("Error initializing HelcimPay:", error); + if (error.statusCode) throw error + console.error('Error initializing HelcimPay:', error) throw createError({ statusCode: 500, - statusMessage: "An unexpected error occurred", - }); + statusMessage: 'An unexpected error occurred' + }) } -}); +}) diff --git a/server/api/helcim/verify-payment.post.js b/server/api/helcim/verify-payment.post.js index 1d0741c..5f3238a 100644 --- a/server/api/helcim/verify-payment.post.js +++ b/server/api/helcim/verify-payment.post.js @@ -2,43 +2,15 @@ import { requireAuth } from '../../utils/auth.js' import { validateBody } from '../../utils/validateBody.js' import { paymentVerifySchema } from '../../utils/schemas.js' - -const HELCIM_API_BASE = 'https://api.helcim.com/v2' +import { listHelcimCustomerCards } from '../../utils/helcim.js' export default defineEventHandler(async (event) => { try { await requireAuth(event) - const config = useRuntimeConfig(event) const body = await validateBody(event, paymentVerifySchema) - const helcimToken = config.helcimApiToken - - if (!helcimToken) { - throw createError({ - statusCode: 500, - statusMessage: 'Helcim API token not configured' - }) - } - // Verify the card token by fetching the customer's cards from Helcim - const response = await fetch(`${HELCIM_API_BASE}/customers/${body.customerId}/cards`, { - method: 'GET', - headers: { - 'accept': 'application/json', - 'api-token': helcimToken - } - }) - - if (!response.ok) { - const errorText = await response.text() - console.error('Payment verification failed:', response.status, errorText) - throw createError({ - statusCode: 502, - statusMessage: 'Payment verification failed with Helcim' - }) - } - - const cards = await response.json() + const cards = await listHelcimCustomerCards(body.customerId) // Verify the card token exists for this customer const cardExists = Array.isArray(cards) && cards.some(card =>