282 lines
No EOL
9.7 KiB
JavaScript
282 lines
No EOL
9.7 KiB
JavaScript
// 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'
|
|
})
|
|
}
|
|
}) |