interface MauticToken { access_token: string; expires_in: number; token_type: string; } let cachedToken: MauticToken | null = null; let tokenExpiresAt: number = 0; async function getMauticToken(): Promise { if (cachedToken && Date.now() < tokenExpiresAt - 60000) { return cachedToken.access_token; } const clientId = process.env.MAUTIC_CLIENT_ID; const clientSecret = process.env.MAUTIC_CLIENT_SECRET; const apiUrl = process.env.MAUTIC_API_URL || "https://mautic.nodecrew.me"; if (!clientId || !clientSecret) { throw new Error("Mautic credentials not configured"); } const response = await fetch(`${apiUrl}/oauth/v2/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "client_credentials", client_id: clientId, client_secret: clientSecret, }), }); if (!response.ok) { const errorText = await response.text(); console.error("Mautic token error:", response.status, errorText); throw new Error(`Failed to get Mautic token: ${response.status} - ${errorText}`); } const token: MauticToken = await response.json(); cachedToken = token; tokenExpiresAt = Date.now() + token.expires_in * 1000; return token.access_token; } export async function createMauticContact( email: string, tags: string[], additionalData?: { firstName?: string; lastName?: string; country?: string; city?: string; phone?: string; website?: string; preferredLocale?: string; ipAddress?: string; utmSource?: string; utmMedium?: string; utmCampaign?: string; utmContent?: string; pageUrl?: string; } ): Promise<{ success: boolean; alreadyExists?: boolean; contactId?: number }> { try { const token = await getMauticToken(); const apiUrl = process.env.MAUTIC_API_URL || "https://mautic.nodecrew.me"; const payload: any = { email, tags: tags.join(","), }; if (additionalData) { if (additionalData.firstName) payload.firstname = additionalData.firstName; if (additionalData.lastName) payload.lastname = additionalData.lastName; if (additionalData.country) payload.country = additionalData.country; if (additionalData.city) payload.city = additionalData.city; if (additionalData.phone) payload.phone = additionalData.phone; if (additionalData.preferredLocale) payload.preferred_locale = additionalData.preferredLocale; if (additionalData.utmSource) payload.utm_source = additionalData.utmSource; if (additionalData.utmMedium) payload.utm_medium = additionalData.utmMedium; if (additionalData.utmCampaign) payload.utm_campaign = additionalData.utmCampaign; if (additionalData.utmContent) payload.utm_content = additionalData.utmContent; if (additionalData.pageUrl) payload.page_url = additionalData.pageUrl; } const response = await fetch(`${apiUrl}/api/contacts/new`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}`, }, body: JSON.stringify(payload), }); if (response.status === 409) { return { success: true, alreadyExists: true }; } if (!response.ok) { const errorText = await response.text(); console.error("Mautic API error:", response.status, errorText); throw new Error(`Mautic API error: ${response.status} - ${errorText}`); } const responseData = await response.json(); console.log("Mautic API success:", responseData); return { success: true, contactId: responseData.contact?.id }; } catch (error) { console.error("Mautic contact creation failed:", error); throw error; } }