// Create a Helcim subscription import { getHelcimPlanId, requiresPayment, getContributionTierByValue, getTierAmount } from '../../config/contributions.js' import Member from '../../models/member.js' import { connectDB } from '../../utils/mongoose.js' import { getSlackService } from '../../utils/slack.ts' import { requireAuth } from '../../utils/auth.js' import { createHelcimSubscription, generateIdempotencyKey } from '../../utils/helcim.js' import { sendWelcomeEmail } from '../../utils/resend.js' // Function to invite member to Slack async function inviteToSlack(member) { try { const slackService = getSlackService() if (!slackService) { console.warn('Slack service not configured, skipping invitation') return } console.log(`Processing Slack invitation for ${member.email}...`) const inviteResult = await slackService.inviteUserToSlack( member.email, member.name ) if (inviteResult.success) { const update = {} if (inviteResult.status === 'existing_user_added_to_channel' || inviteResult.status === 'user_already_in_channel' || inviteResult.status === 'new_user_invited_to_workspace') { update.slackInviteStatus = 'sent' update.slackUserId = inviteResult.userId update.slackInvited = true } else { update.slackInviteStatus = 'pending' update.slackInvited = false } await Member.findByIdAndUpdate( member._id, { $set: update }, { runValidators: false } ) // Send notification to vetting channel await slackService.notifyNewMember( member.name, member.email, member.circle, member.contributionTier, inviteResult.status ) console.log(`Successfully processed Slack invitation for ${member.email}: ${inviteResult.status}`) } else { await Member.findByIdAndUpdate( member._id, { $set: { slackInviteStatus: 'failed' } }, { runValidators: false } ) console.error(`Failed to process Slack invitation for ${member.email}: ${inviteResult.error}`) // Don't throw error - subscription creation should still succeed } } catch (error) { console.error('Error during Slack invitation process:', error) try { await Member.findByIdAndUpdate( member._id, { $set: { slackInviteStatus: 'failed' } }, { runValidators: false } ) } catch (saveError) { console.error('Failed to update member Slack status:', saveError) } // Don't throw error - subscription creation should still succeed } } export default defineEventHandler(async (event) => { try { await requireAuth(event) await connectDB() const body = await validateBody(event, helcimSubscriptionSchema) // Only send welcome email when a member transitions from pending_payment // to active for the first time — not on tier upgrades (active → active). const priorMember = await Member.findOne( { helcimCustomerId: body.customerId }, { status: 1 } ) const isFirstActivation = priorMember?.status === 'pending_payment' // Check if payment is required if (!requiresPayment(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 } ) logActivity(member._id, 'subscription_created', { tier: body.contributionTier }) await inviteToSlack(member) if (isFirstActivation) await sendWelcomeEmail(member) return { success: true, subscription: null, member: { id: member._id, email: member.email, name: member.name, circle: member.circle, contributionTier: member.contributionTier, status: member.status } } } // Validate card token is provided if (!body.cardToken) { throw createError({ statusCode: 400, statusMessage: 'Payment information is required for this contribution tier' }) } const tierInfo = getContributionTierByValue(body.contributionTier) const cadence = body.cadence const paymentPlanId = getHelcimPlanId(cadence) if (!paymentPlanId) { throw createError({ statusCode: 500, statusMessage: cadence === 'annual' ? 'Annual plan id not configured' : 'Monthly plan id not configured', }) } const idempotencyKey = generateIdempotencyKey() const subscriptionPayload = { dateActivated: new Date().toISOString().split('T')[0], paymentPlanId: parseInt(paymentPlanId), customerCode: body.customerCode, recurringAmount: getTierAmount(tierInfo, cadence), paymentMethod: 'card', } const subscriptionData = await createHelcimSubscription(subscriptionPayload, idempotencyKey) // Extract the first subscription from the response array const subscription = subscriptionData.data?.[0] if (!subscription) { throw createError({ statusCode: 500, statusMessage: 'Subscription creation failed' }) } // Update member in database const member = await Member.findOneAndUpdate( { helcimCustomerId: body.customerId }, { $set: { contributionTier: body.contributionTier, helcimSubscriptionId: subscription.id, helcimCustomerId: body.customerId, paymentMethod: 'card', billingCadence: cadence, status: 'active', } }, { new: true, runValidators: false } ) logActivity(member._id, 'subscription_created', { tier: body.contributionTier }) await inviteToSlack(member) if (isFirstActivation) await sendWelcomeEmail(member) return { success: true, subscription: { subscriptionId: subscription.id, status: subscription.status, nextBillingDate: subscription.nextBillingDate }, member: { id: member._id, email: member.email, name: member.name, circle: member.circle, contributionTier: member.contributionTier, status: member.status } } } catch (error) { if (error.statusCode) throw error console.error('Error creating Helcim subscription:', error) throw createError({ statusCode: 500, statusMessage: 'An unexpected error occurred' }) } })