// Create a Helcim subscription import { getHelcimPlanId, requiresPayment } from '../../config/contributions.js' import Member from '../../models/member.js' import { connectDB } from '../../utils/mongoose.js' const HELCIM_API_BASE = 'https://api.helcim.com/v2' export default defineEventHandler(async (event) => { try { await connectDB() const config = useRuntimeConfig(event) const body = await readBody(event) // Validate required fields if (!body.customerId || !body.contributionTier) { throw createError({ statusCode: 400, statusMessage: 'Customer ID and contribution tier are required' }) } if (!body.customerCode) { throw createError({ statusCode: 400, statusMessage: 'Customer code is required for subscription creation' }) } console.log('Subscription request body:', body) // Check if payment is required if (!requiresPayment(body.contributionTier)) { console.log('No payment required for tier:', body.contributionTier) // For free tier, just update member status const member = await Member.findOneAndUpdate( { helcimCustomerId: body.customerId }, { status: 'active', contributionTier: body.contributionTier, subscriptionStartDate: new Date() }, { new: true } ) console.log('Updated member for free tier:', member) return { success: true, subscription: null, member } } console.log('Payment required for tier:', body.contributionTier) // Get the Helcim plan ID const planId = getHelcimPlanId(body.contributionTier) console.log('Plan ID for tier:', planId) // Validate card token is provided if (!body.cardToken) { throw createError({ statusCode: 400, statusMessage: 'Payment information is required for this contribution tier' }) } // Check if we have a configured plan for this tier if (!planId) { console.log('No Helcim plan configured for tier:', body.contributionTier) const member = await Member.findOneAndUpdate( { helcimCustomerId: body.customerId }, { status: 'active', contributionTier: body.contributionTier, subscriptionStartDate: new Date(), paymentMethod: 'card', cardToken: body.cardToken, notes: `Payment successful but no Helcim plan configured for tier ${body.contributionTier}` }, { new: true } ) return { success: true, subscription: { subscriptionId: 'manual-' + Date.now(), status: 'needs_plan_setup', nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) }, member, warning: `Payment successful but recurring plan needs to be set up in Helcim for the ${body.contributionTier} tier` } } // Try to create subscription in Helcim const helcimToken = config.public.helcimToken || process.env.NUXT_PUBLIC_HELCIM_TOKEN console.log('Attempting to create Helcim subscription with plan ID:', planId) // Generate a proper alphanumeric idempotency key (exactly 25 characters) const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' let idempotencyKey = '' for (let i = 0; i < 25; i++) { idempotencyKey += chars.charAt(Math.floor(Math.random() * chars.length)) } // Get contribution tier details to set recurring amount const { getContributionTierByValue } = await import('../../config/contributions.js') const tierInfo = getContributionTierByValue(body.contributionTier) const requestBody = { subscriptions: [{ dateActivated: new Date().toISOString().split('T')[0], // Today in YYYY-MM-DD format paymentPlanId: parseInt(planId), customerCode: body.customerCode, recurringAmount: parseFloat(tierInfo.amount), paymentMethod: 'card' }] } const requestHeaders = { 'accept': 'application/json', 'content-type': 'application/json', 'api-token': helcimToken, 'idempotency-key': idempotencyKey } console.log('Subscription request body:', requestBody) console.log('Request headers:', requestHeaders) console.log('Request URL:', `${HELCIM_API_BASE}/subscriptions`) try { const subscriptionResponse = await fetch(`${HELCIM_API_BASE}/subscriptions`, { method: 'POST', headers: requestHeaders, body: JSON.stringify(requestBody) }) if (!subscriptionResponse.ok) { const errorText = await subscriptionResponse.text() console.error('Subscription creation failed:') console.error('Status:', subscriptionResponse.status) console.error('Status Text:', subscriptionResponse.statusText) console.error('Headers:', Object.fromEntries(subscriptionResponse.headers.entries())) console.error('Response Body:', errorText) console.error('Request was:', { url: `${HELCIM_API_BASE}/subscriptions`, method: 'POST', body: requestBody, headers: { 'accept': 'application/json', 'content-type': 'application/json', 'api-token': helcimToken ? 'present' : 'missing' } }) // If it's a validation error, let's try to get more info about available plans if (subscriptionResponse.status === 400 || subscriptionResponse.status === 404) { console.log('Plan might not exist. Trying to get list of available payment plans...') // Try to fetch available payment plans try { const plansResponse = await fetch(`${HELCIM_API_BASE}/payment-plans`, { method: 'GET', headers: { 'accept': 'application/json', 'api-token': helcimToken } }) if (plansResponse.ok) { const plansData = await plansResponse.json() console.log('Available payment plans:', JSON.stringify(plansData, null, 2)) } else { console.log('Could not fetch payment plans:', plansResponse.status, plansResponse.statusText) } } catch (planError) { console.log('Error fetching payment plans:', planError.message) } // For now, just update member status and let user know we need to configure plans const member = await Member.findOneAndUpdate( { helcimCustomerId: body.customerId }, { status: 'active', contributionTier: body.contributionTier, subscriptionStartDate: new Date(), paymentMethod: 'card', cardToken: body.cardToken, notes: `Payment successful but subscription creation failed: ${errorText}` }, { new: true } ) return { success: true, subscription: { subscriptionId: 'manual-' + Date.now(), status: 'needs_setup', error: errorText, nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) }, member, warning: 'Payment successful but recurring subscription needs manual setup' } } throw createError({ statusCode: subscriptionResponse.status, statusMessage: `Failed to create subscription: ${errorText}` }) } const subscriptionData = await subscriptionResponse.json() console.log('Subscription created successfully:', subscriptionData) // Extract the first subscription from the response array const subscription = subscriptionData.data?.[0] if (!subscription) { throw new Error('No subscription returned in response') } // Update member in database const member = await Member.findOneAndUpdate( { helcimCustomerId: body.customerId }, { status: 'active', contributionTier: body.contributionTier, helcimSubscriptionId: subscription.id, subscriptionStartDate: new Date(), paymentMethod: 'card' }, { new: true } ) return { success: true, subscription: { subscriptionId: subscription.id, status: subscription.status, nextBillingDate: subscription.nextBillingDate }, member } } catch (fetchError) { console.error('Error during subscription creation:', fetchError) // Still mark member as active since payment was successful const member = await Member.findOneAndUpdate( { helcimCustomerId: body.customerId }, { status: 'active', contributionTier: body.contributionTier, subscriptionStartDate: new Date(), paymentMethod: 'card', cardToken: body.cardToken, notes: `Payment successful but subscription creation failed: ${fetchError.message}` }, { new: true } ) return { success: true, subscription: { subscriptionId: 'manual-' + Date.now(), status: 'needs_setup', error: fetchError.message, nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) }, member, warning: 'Payment successful but recurring subscription needs manual setup' } } } catch (error) { console.error('Error creating Helcim subscription:', error) throw createError({ statusCode: error.statusCode || 500, statusMessage: error.message || 'Failed to create subscription' }) } })