Invoice updates

This commit is contained in:
AllenTJ7 2025-06-25 18:02:34 +05:30
parent 5c886ebe42
commit 9c4cd0fc43
5 changed files with 229 additions and 218 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -142,7 +142,7 @@ export const phonePeWebhook = onRequest({
} else if (paymentId) {
await processServicePayment(payload, orderData, paymentId);
} 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}`);
@ -639,6 +639,18 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
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({
status: 'ACCEPTED',
paymentDetails: {
@ -651,9 +663,8 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
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;
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}`);
const discountPercentage = orderData.metaInfo?.discount || 0;
@ -703,7 +714,7 @@ async function processServicePayment(payload: any, orderData: any, paymentId: st
businessName: gymName,
address: gymAddress,
gstNumber: orderData.metaInfo?.gstNumber,
customerName: orderData.metaInfo?.customerName || serviceData?.customerName || '',
customerName: orderData.metaInfo?.customerName || serviceData?.normalizedName || '',
phoneNumber: orderData.metaInfo?.customerPhone || serviceData?.phoneNumber || '',
email: orderData.metaInfo?.customerEmail || serviceData?.email || '',
planName: orderData.metaInfo?.serviceName || serviceData?.serviceName || 'Service',