Compare commits

...

49 Commits

Author SHA1 Message Date
AllenTJ7
3e1d74196d Merge branch 'dev' into phonepe 2025-06-25 19:57:49 +05:30
AllenTJ7
9c4cd0fc43 Invoice updates 2025-06-25 18:02:34 +05:30
AllenTJ7
5c886ebe42 updated 2025-06-24 18:13:44 +05:30
AllenTJ7
1c0951b47b Update webhook.ts 2025-06-24 17:21:58 +05:30
AllenTJ7
e670f8cab6 Merge branch 'dev' into phonepe 2025-06-24 14:25:29 +05:30
AllenTJ7
63fc973064 Update webhook.ts 2025-06-24 14:24:30 +05:30
AllenTJ7
6fbda2b35b Update webhook.ts 2025-06-24 14:09:20 +05:30
AllenTJ7
375937ed5e Merge branch 'dev' into phonepe 2025-06-12 12:50:25 +05:30
AllenTJ7
168e909c77 Update invoiceService.ts 2025-06-02 15:24:16 +05:30
AllenTJ7
fd5f5f55d2 Merge branch 'dev' into phonepe 2025-05-29 14:55:10 +05:30
AllenTJ7
fb68d82024 Update webhook.ts 2025-05-29 14:51:37 +05:30
AllenTJ7
26a03c193c Merge branch 'dev' into phonepe 2025-05-28 16:49:27 +05:30
AllenTJ7
62f1130d3e Update webhook.ts 2025-05-28 16:43:40 +05:30
AllenTJ7
7f4ca23cb8 Update webhook.ts 2025-05-28 00:43:18 +05:30
AllenTJ7
bb720e6f78 Update webhook.ts 2025-05-28 00:29:28 +05:30
AllenTJ7
c271e73c3c Merge branch 'dev' into phonepe 2025-05-28 00:15:49 +05:30
AllenTJ7
f0f2cd3411 Update webhook.ts 2025-05-28 00:14:17 +05:30
AllenTJ7
4817424c71 Update emailService.ts 2025-05-27 23:47:57 +05:30
AllenTJ7
2fb0280e87 Update emailService.ts 2025-05-27 23:36:48 +05:30
AllenTJ7
bd601d896d Merge branch 'dev' into phonepe 2025-05-27 23:18:27 +05:30
AllenTJ7
0402e307df Merge branch 'dev' into phonepe 2025-05-23 15:00:40 +05:30
AllenTJ7
5fe4d30502 Update invoiceService.ts 2025-05-23 15:00:25 +05:30
AllenTJ7
d0b2d29925 Merge branch 'dev' into phonepe 2025-05-23 14:40:43 +05:30
AllenTJ7
8ad307f3a9 Update invoiceService.ts 2025-05-23 14:40:04 +05:30
AllenTJ7
083485d7de Update invoiceService.ts 2025-05-23 14:37:01 +05:30
AllenTJ7
4ed6790de3 Merge branch 'dev' into phonepe 2025-05-23 14:29:49 +05:30
AllenTJ7
1a40513cc4 Update invoiceService.ts 2025-05-23 14:27:02 +05:30
AllenTJ7
ed33fe3c46 changed layout 2025-05-23 13:43:08 +05:30
AllenTJ7
e8523b9cdb Merge branch 'dev' into phonepe 2025-05-23 13:22:43 +05:30
AllenTJ7
354881d5bf Update webhook.ts 2025-05-23 13:19:15 +05:30
AllenTJ7
df404e405c Update webhook.ts 2025-05-23 13:04:45 +05:30
AllenTJ7
63044e517e Merge branch 'dev' into phonepe 2025-05-23 12:44:43 +05:30
AllenTJ7
89b9ba897a changes to invoice layout 2025-05-21 23:23:22 +05:30
AllenTJ7
5824e1984a Merge branch 'dev' into phonepe 2025-05-21 17:00:47 +05:30
AllenTJ7
18afada52a changes 2025-05-21 17:00:19 +05:30
AllenTJ7
d73f02726e changed pdf layout 2025-05-21 16:55:54 +05:30
AllenTJ7
dd060fb963 changed import and collection name 2025-05-21 15:37:57 +05:30
AllenTJ7
5a17380f66 Merge branch 'dev' into phonepe 2025-05-21 14:12:56 +05:30
AllenTJ7
623ebc84df added dependency 2025-05-21 14:12:33 +05:30
AllenTJ7
3d64e11a05 changes for deploying 2025-05-20 17:05:30 +05:30
AllenTJ7
af8617a4a9 Merge branch 'dev' into phonepe 2025-05-20 16:28:28 +05:30
AllenTJ7
c252ba8620 timeout changes 2025-05-20 16:27:05 +05:30
AllenTJ7
2cca695f2f Merge branch 'dev' into phonepe 2025-05-20 16:22:57 +05:30
AllenTJ7
5d138e5937 Update index.ts 2025-05-20 16:21:38 +05:30
AllenTJ7
85e8dea5f1 Merge branch 'dev' into phonepe 2025-05-20 16:16:40 +05:30
AllenTJ7
66a65aed38 Update firebase.json 2025-05-20 16:16:21 +05:30
AllenTJ7
4ce107c4af Update index.ts 2025-05-20 16:16:18 +05:30
AllenTJ7
95492d304f Update webhook.ts 2025-05-20 16:08:33 +05:30
AllenTJ7
de9ea73dcd Update firebase.json 2025-05-20 15:45:20 +05:30
6 changed files with 232 additions and 230 deletions

View File

@ -1,62 +1,62 @@
import { onRequest } from "firebase-functions/v2/https"; // import { onRequest } from "firebase-functions/v2/https";
import { Request } from "firebase-functions/v2/https"; // import { Request } from "firebase-functions/v2/https";
import { getCorsHandler } from "../../../shared/middleware"; // import { getCorsHandler } from "../../../shared/middleware";
import { getAdmin, getLogger } from "../../../shared/config"; // import { getAdmin, getLogger } from "../../../shared/config";
import { InvoiceService } from "./invoiceService"; // import { InvoiceService } from "./invoiceService";
const admin = getAdmin(); // const admin = getAdmin();
const logger = getLogger(); // const logger = getLogger();
const corsHandler = getCorsHandler(); // const corsHandler = getCorsHandler();
const invoiceService = new InvoiceService(); // const invoiceService = new InvoiceService();
export const getInvoiceUrl = onRequest({ // export const getInvoiceUrl = onRequest({
region: '#{SERVICES_RGN}#' // region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => { // }, async (request: Request, response) => {
return corsHandler(request, response, async () => { // return corsHandler(request, response, async () => {
try { // try {
const authHeader = request.headers.authorization || ''; // const authHeader = request.headers.authorization || '';
if (!authHeader || !authHeader.startsWith('Bearer ')) { // if (!authHeader || !authHeader.startsWith('Bearer ')) {
response.status(401).json({ error: 'Unauthorized' }); // response.status(401).json({ error: 'Unauthorized' });
return; // return;
} // }
const idToken = authHeader.split('Bearer ')[1]; // const idToken = authHeader.split('Bearer ')[1];
try { // try {
await admin.auth().verifyIdToken(idToken); // await admin.auth().verifyIdToken(idToken);
const { invoicePath } = request.query; // const { invoicePath } = request.query;
if (!invoicePath) { // if (!invoicePath) {
response.status(400).json({ // response.status(400).json({
success: false, // success: false,
error: 'Missing invoice path' // error: 'Missing invoice path'
}); // });
return; // return;
} // }
const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath as string); // const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath as string);
response.json({ // response.json({
success: true, // success: true,
downloadUrl // downloadUrl
}); // });
} catch (authError: any) { // } catch (authError: any) {
logger.error('Authentication error:', authError); // logger.error('Authentication error:', authError);
response.status(401).json({ // response.status(401).json({
success: false, // success: false,
error: 'Invalid authentication token', // error: 'Invalid authentication token',
details: authError.message // details: authError.message
}); // });
} // }
} catch (error: any) { // } catch (error: any) {
logger.error('Error getting invoice URL:', error); // logger.error('Error getting invoice URL:', error);
response.status(500).json({ // response.status(500).json({
success: false, // success: false,
error: 'Failed to get invoice URL', // error: 'Failed to get invoice URL',
details: error.message // details: error.message
}); // });
} // }
}); // });
}); // });

View File

@ -1,13 +1,13 @@
import { getInvoiceUrl } from './getInvoiceUrl'; // import { getInvoiceUrl } from './getInvoiceUrl';
import { InvoiceService } from './invoiceService'; import { InvoiceService } from './invoiceService';
import { processInvoice } from './processInvoice'; // import { processInvoice } from './processInvoice';
import { sendInvoiceEmail } from './sendInvoiceEmail'; // import { sendInvoiceEmail } from './sendInvoiceEmail';
import { directGenerateInvoice } from './directInvoice'; import { directGenerateInvoice } from './directInvoice';
export { export {
getInvoiceUrl, // getInvoiceUrl,
InvoiceService, InvoiceService,
processInvoice, // processInvoice,
sendInvoiceEmail, // sendInvoiceEmail,
directGenerateInvoice, directGenerateInvoice,
}; };

View File

@ -1,83 +1,83 @@
import { onRequest } from "firebase-functions/v2/https"; // import { onRequest } from "firebase-functions/v2/https";
import { Request } from "firebase-functions/v2/https"; // import { Request } from "firebase-functions/v2/https";
import { getCorsHandler } from "../../../shared/middleware"; // import { getCorsHandler } from "../../../shared/middleware";
import { getAdmin, getLogger } from "../../../shared/config"; // import { getAdmin, getLogger } from "../../../shared/config";
import { InvoiceService } from "./invoiceService"; // import { InvoiceService } from "./invoiceService";
const admin = getAdmin(); // const admin = getAdmin();
const logger = getLogger(); // const logger = getLogger();
const corsHandler = getCorsHandler(); // const corsHandler = getCorsHandler();
const invoiceService = new InvoiceService(); // const invoiceService = new InvoiceService();
export const processInvoice = onRequest({ // export const processInvoice = onRequest({
region: '#{SERVICES_RGN}#' // region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => { // }, async (request: Request, response) => {
return corsHandler(request, response, async () => { // return corsHandler(request, response, async () => {
try { // try {
const authHeader = request.headers.authorization || ''; // const authHeader = request.headers.authorization || '';
if (!authHeader || !authHeader.startsWith('Bearer ')) { // if (!authHeader || !authHeader.startsWith('Bearer ')) {
response.status(401).json({ error: 'Unauthorized' }); // response.status(401).json({ error: 'Unauthorized' });
return; // return;
} // }
const idToken = authHeader.split('Bearer ')[1]; // const idToken = authHeader.split('Bearer ')[1];
try { // try {
await admin.auth().verifyIdToken(idToken); // await admin.auth().verifyIdToken(idToken);
const { // const {
membershipId, // membershipId,
paymentId, // paymentId,
invoiceData, // invoiceData,
emailOptions // emailOptions
} = request.body; // } = request.body;
if (!membershipId || !paymentId || !invoiceData) { // if (!membershipId || !paymentId || !invoiceData) {
response.status(400).json({ // response.status(400).json({
success: false, // success: false,
error: 'Missing required fields' // error: 'Missing required fields'
}); // });
return; // return;
} // }
const result = await invoiceService.processInvoice( // const result = await invoiceService.processInvoice(
membershipId, // membershipId,
paymentId, // paymentId,
invoiceData, // invoiceData,
emailOptions // emailOptions
); // );
if (!result.success) { // if (!result.success) {
response.status(400).json({ // response.status(400).json({
success: false, // success: false,
error: result.error || 'Failed to process invoice' // error: result.error || 'Failed to process invoice'
}); // });
return; // return;
} // }
response.json({ // response.json({
success: true, // success: true,
message: 'Invoice processed successfully', // message: 'Invoice processed successfully',
invoicePath: result.invoicePath, // invoicePath: result.invoicePath,
downloadUrl: result.downloadUrl, // downloadUrl: result.downloadUrl,
emailSent: result.emailSent // emailSent: result.emailSent
}); // });
} catch (authError: any) { // } catch (authError: any) {
logger.error('Authentication error:', authError); // logger.error('Authentication error:', authError);
response.status(401).json({ // response.status(401).json({
success: false, // success: false,
error: 'Invalid authentication token', // error: 'Invalid authentication token',
details: authError.message // details: authError.message
}); // });
} // }
} catch (error: any) { // } catch (error: any) {
logger.error('Error processing invoice:', error); // logger.error('Error processing invoice:', error);
response.status(500).json({ // response.status(500).json({
success: false, // success: false,
error: 'Failed to process invoice', // error: 'Failed to process invoice',
details: error.message // details: error.message
}); // });
} // }
}); // });
}); // });

View File

@ -1,91 +1,91 @@
import { onRequest } from "firebase-functions/v2/https"; // import { onRequest } from "firebase-functions/v2/https";
import { Request } from "firebase-functions/v2/https"; // import { Request } from "firebase-functions/v2/https";
import { getCorsHandler } from "../../../shared/middleware"; // import { getCorsHandler } from "../../../shared/middleware";
import { getAdmin, getLogger } from "../../../shared/config"; // import { getAdmin, getLogger } from "../../../shared/config";
import { InvoiceService, EmailOptions } from "./invoiceService"; // import { InvoiceService, EmailOptions } from "./invoiceService";
const admin = getAdmin(); // const admin = getAdmin();
const logger = getLogger(); // const logger = getLogger();
const corsHandler = getCorsHandler(); // const corsHandler = getCorsHandler();
const invoiceService = new InvoiceService(); // const invoiceService = new InvoiceService();
export const sendInvoiceEmail = onRequest({ // export const sendInvoiceEmail = onRequest({
region: '#{SERVICES_RGN}#' // region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => { // }, async (request: Request, response) => {
return corsHandler(request, response, async () => { // return corsHandler(request, response, async () => {
try { // try {
const authHeader = request.headers.authorization || ''; // const authHeader = request.headers.authorization || '';
if (!authHeader || !authHeader.startsWith('Bearer ')) { // if (!authHeader || !authHeader.startsWith('Bearer ')) {
response.status(401).json({ error: 'Unauthorized' }); // response.status(401).json({ error: 'Unauthorized' });
return; // return;
} // }
const idToken = authHeader.split('Bearer ')[1]; // const idToken = authHeader.split('Bearer ')[1];
try { // try {
await admin.auth().verifyIdToken(idToken); // await admin.auth().verifyIdToken(idToken);
const { // const {
invoicePath, // invoicePath,
recipientEmail, // recipientEmail,
recipientName, // recipientName,
subject, // subject,
customHtml, // customHtml,
gymName, // gymName,
planName, // planName,
amount, // amount,
transactionId, // transactionId,
paymentDate, // paymentDate,
paymentMethod // paymentMethod
} = request.body; // } = request.body;
if (!invoicePath || !recipientEmail) { // if (!invoicePath || !recipientEmail) {
response.status(400).json({ // response.status(400).json({
success: false, // success: false,
error: 'Missing required fields' // error: 'Missing required fields'
}); // });
return; // return;
} // }
const emailOptions: EmailOptions = { // const emailOptions: EmailOptions = {
recipientEmail, // recipientEmail,
recipientName, // recipientName,
subject, // subject,
customHtml, // customHtml,
additionalData: { // additionalData: {
gymName, // gymName,
planName, // planName,
amount, // amount,
transactionId, // transactionId,
paymentDate: paymentDate ? new Date(paymentDate) : undefined, // paymentDate: paymentDate ? new Date(paymentDate) : undefined,
paymentMethod // paymentMethod
} // }
}; // };
const emailSent = await invoiceService.sendInvoiceEmail(invoicePath, emailOptions); // const emailSent = await invoiceService.sendInvoiceEmail(invoicePath, emailOptions);
const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath); // const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath);
response.json({ // response.json({
success: true, // success: true,
message: emailSent ? 'Invoice email sent successfully' : 'Failed to send email but URL generated', // message: emailSent ? 'Invoice email sent successfully' : 'Failed to send email but URL generated',
downloadUrl // downloadUrl
}); // });
} catch (authError: any) { // } catch (authError: any) {
logger.error('Authentication error:', authError); // logger.error('Authentication error:', authError);
response.status(401).json({ // response.status(401).json({
success: false, // success: false,
error: 'Invalid authentication token', // error: 'Invalid authentication token',
details: authError.message // details: authError.message
}); // });
} // }
} catch (error: any) { // } catch (error: any) {
logger.error('Error sending invoice email:', error); // logger.error('Error sending invoice email:', error);
response.status(500).json({ // response.status(500).json({
success: false, // success: false,
error: 'Failed to send invoice email', // error: 'Failed to send invoice email',
details: error.message // details: error.message
}); // });
} // }
}); // });
}); // });

View File

@ -142,7 +142,7 @@ export const phonePeWebhook = onRequest({
} else if (paymentId) { } else if (paymentId) {
await processServicePayment(payload, orderData, paymentId); await processServicePayment(payload, orderData, paymentId);
} else { } else {
logger.error(`No membershipId, bookingId, or serviceId found in metaInfo for order: ${payload.merchantOrderId}`); logger.error(`No membershipId, bookingId, or paymentId found in metaInfo for order: ${payload.merchantOrderId}`);
} }
logger.info(`Payment data updated for completed payment: ${payload.merchantOrderId}`); logger.info(`Payment data updated for completed payment: ${payload.merchantOrderId}`);
@ -639,6 +639,18 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
return; return;
} }
const serviceData = serviceDoc.data();
if (serviceData?.status === 'ACCEPTED' && serviceData?.paymentDetails?.merchantOrderId) {
logger.warn(`Service payment already processed for serviceId: ${paymentId}, merchantOrderId: ${serviceData.paymentDetails.merchantOrderId}`);
return;
}
if (serviceData?.invoicePath && serviceData?.invoiceNumber) {
logger.warn(`Invoice already exists for serviceId: ${paymentId}, invoicePath: ${serviceData.invoicePath}`);
return;
}
await serviceRef.update({ await serviceRef.update({
status: 'ACCEPTED', status: 'ACCEPTED',
paymentDetails: { paymentDetails: {
@ -651,9 +663,8 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
updatedAt: admin.firestore.FieldValue.serverTimestamp() updatedAt: admin.firestore.FieldValue.serverTimestamp()
}); });
logger.info(`Updated service booking status to 'CONFIRMED' for serviceId: ${paymentId}`); logger.info(`Updated service booking status to 'CONFIRMED' for paymentId: ${paymentId}`);
const serviceData = serviceDoc.data();
const gymId = orderData.metaInfo?.gymId || serviceData?.gymId; const gymId = orderData.metaInfo?.gymId || serviceData?.gymId;
if (gymId) { if (gymId) {
@ -681,7 +692,7 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
} }
} }
const invoiceNumber = `SRV-${payload.merchantOrderId.substring(0, 8)}`; const invoiceNumber = `INV-${payload.merchantOrderId.substring(0, 8)}`;
logger.info(`Generated invoice number for service: ${invoiceNumber}`); logger.info(`Generated invoice number for service: ${invoiceNumber}`);
const discountPercentage = orderData.metaInfo?.discount || 0; const discountPercentage = orderData.metaInfo?.discount || 0;
@ -703,7 +714,7 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
businessName: gymName, businessName: gymName,
address: gymAddress, address: gymAddress,
gstNumber: orderData.metaInfo?.gstNumber, gstNumber: orderData.metaInfo?.gstNumber,
customerName: orderData.metaInfo?.customerName || serviceData?.customerName || '', customerName: orderData.metaInfo?.customerName || serviceData?.normalizedName || '',
phoneNumber: orderData.metaInfo?.customerPhone || serviceData?.phoneNumber || '', phoneNumber: orderData.metaInfo?.customerPhone || serviceData?.phoneNumber || '',
email: orderData.metaInfo?.customerEmail || serviceData?.email || '', email: orderData.metaInfo?.customerEmail || serviceData?.email || '',
planName: orderData.metaInfo?.serviceName || serviceData?.serviceName || 'Service', planName: orderData.metaInfo?.serviceName || serviceData?.serviceName || 'Service',

View File

@ -21,7 +21,7 @@ interface EmailRequest {
interface Attachment { interface Attachment {
filename: string; filename: string;
content: string | Buffer; // Base64 encoded string or Buffer content: string | Buffer;
contentType?: string; contentType?: string;
} }
@ -32,7 +32,7 @@ const stripHtml = (html: string): string => {
async function sendSimpleEmail(data: EmailRequest, recipients: string[]) { async function sendSimpleEmail(data: EmailRequest, recipients: string[]) {
const ses = new SESClient({ const ses = new SESClient({
region: 'ap-south-1', region: process.env.AWS_REGION,
credentials: { credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '' secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || ''
@ -58,7 +58,7 @@ async function sendSimpleEmail(data: EmailRequest, recipients: string[]) {
async function sendEmailWithAttachments(data: EmailRequest, recipients: string[]) { async function sendEmailWithAttachments(data: EmailRequest, recipients: string[]) {
const ses = new SESClient({ const ses = new SESClient({
region: 'ap-south-1', region: process.env.AWS_REGION,
credentials: { credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '', accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '' secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || ''
@ -72,26 +72,21 @@ async function sendEmailWithAttachments(data: EmailRequest, recipients: string[]
rawMessage += `MIME-Version: 1.0\n`; rawMessage += `MIME-Version: 1.0\n`;
rawMessage += `Content-Type: multipart/mixed; boundary="${boundary}"\n\n`; rawMessage += `Content-Type: multipart/mixed; boundary="${boundary}"\n\n`;
// Add email body (multipart/alternative)
rawMessage += `--${boundary}\n`; rawMessage += `--${boundary}\n`;
rawMessage += `Content-Type: multipart/alternative; boundary="alt_${boundary}"\n\n`; rawMessage += `Content-Type: multipart/alternative; boundary="alt_${boundary}"\n\n`;
// Text part
if (data.text) { if (data.text) {
rawMessage += `--alt_${boundary}\n`; rawMessage += `--alt_${boundary}\n`;
rawMessage += `Content-Type: text/plain; charset=UTF-8\n\n`; rawMessage += `Content-Type: text/plain; charset=UTF-8\n\n`;
rawMessage += `${data.text}\n\n`; rawMessage += `${data.text}\n\n`;
} }
// HTML part
rawMessage += `--alt_${boundary}\n`; rawMessage += `--alt_${boundary}\n`;
rawMessage += `Content-Type: text/html; charset=UTF-8\n\n`; rawMessage += `Content-Type: text/html; charset=UTF-8\n\n`;
rawMessage += `${data.html}\n\n`; rawMessage += `${data.html}\n\n`;
// Close alternative part
rawMessage += `--alt_${boundary}--\n\n`; rawMessage += `--alt_${boundary}--\n\n`;
// Add attachments
for (const attachment of data.attachments || []) { for (const attachment of data.attachments || []) {
const contentType = attachment.contentType || const contentType = attachment.contentType ||
mime.lookup(attachment.filename) || mime.lookup(attachment.filename) ||
@ -109,7 +104,6 @@ async function sendEmailWithAttachments(data: EmailRequest, recipients: string[]
rawMessage += contentBuffer.toString('base64') + '\n\n'; rawMessage += contentBuffer.toString('base64') + '\n\n';
} }
// Close message
rawMessage += `--${boundary}--`; rawMessage += `--${boundary}--`;
const command = new SendRawEmailCommand({ const command = new SendRawEmailCommand({
@ -140,7 +134,6 @@ export async function sendEmailWithAttachmentUtil(
try { try {
logger.info(`Sending email with attachment to: ${toAddress}`); logger.info(`Sending email with attachment to: ${toAddress}`);
// Initialize data with basic fields
const data: EmailRequest = { const data: EmailRequest = {
to: toAddress, to: toAddress,
html: message, html: message,
@ -151,13 +144,11 @@ export async function sendEmailWithAttachmentUtil(
attachments: [] attachments: []
}; };
// Handle file URL if provided
if (fileUrl && fileName) { if (fileUrl && fileName) {
logger.info(`Downloading attachment from URL: ${fileUrl}`); logger.info(`Downloading attachment from URL: ${fileUrl}`);
try { try {
const fileContent = await downloadFileFromUrl(fileUrl); const fileContent = await downloadFileFromUrl(fileUrl);
// Add the downloaded file as an attachment
data.attachments!.push({ data.attachments!.push({
filename: fileName, filename: fileName,
content: fileContent, content: fileContent,