phonepe #61
@ -120,6 +120,7 @@ export const phonePeWebhook = onRequest({
|
|||||||
const orderData = orderDoc.data();
|
const orderData = orderDoc.data();
|
||||||
const membershipId = orderData.metaInfo?.membershipId;
|
const membershipId = orderData.metaInfo?.membershipId;
|
||||||
const bookingId = orderData.metaInfo?.bookingId;
|
const bookingId = orderData.metaInfo?.bookingId;
|
||||||
|
const serviceId = orderData.metaInfo?.serviceId;
|
||||||
|
|
||||||
if (bookingId) {
|
if (bookingId) {
|
||||||
await processDayPassBooking(payload, orderData, bookingId);
|
await processDayPassBooking(payload, orderData, bookingId);
|
||||||
@ -138,8 +139,10 @@ export const phonePeWebhook = onRequest({
|
|||||||
if (paymentUpdateSuccess) {
|
if (paymentUpdateSuccess) {
|
||||||
await processMembershipPayment(payload, orderData, membershipId);
|
await processMembershipPayment(payload, orderData, membershipId);
|
||||||
}
|
}
|
||||||
|
} else if (serviceId) {
|
||||||
|
await processServicePayment(payload, orderData, serviceId);
|
||||||
} else {
|
} else {
|
||||||
logger.error(`No membershipId or bookingId found in metaInfo for order: ${payload.merchantOrderId}`);
|
logger.error(`No membershipId, bookingId, or serviceId 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}`);
|
||||||
@ -623,3 +626,206 @@ async function processMembershipPayment(payload: any, orderData: any, membership
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function processServicePayment(payload: any, orderData: any, serviceId: string) {
|
||||||
|
try {
|
||||||
|
logger.info(`Processing service payment for serviceId: ${serviceId}`);
|
||||||
|
|
||||||
|
const serviceRef = admin.firestore().collection('service_payments').doc(serviceId);
|
||||||
|
const serviceDoc = await serviceRef.get();
|
||||||
|
|
||||||
|
if (!serviceDoc.exists) {
|
||||||
|
logger.error(`Service booking not found for serviceId: ${serviceId}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await serviceRef.update({
|
||||||
|
status: 'ACCEPTED',
|
||||||
|
paymentDetails: {
|
||||||
|
transactionId: payload.orderId,
|
||||||
|
merchantOrderId: payload.merchantOrderId,
|
||||||
|
amount: orderData.amount,
|
||||||
|
paymentDate: new Date(),
|
||||||
|
paymentMethod: 'PhonePe'
|
||||||
|
},
|
||||||
|
updatedAt: admin.firestore.FieldValue.serverTimestamp()
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Updated service booking status to 'CONFIRMED' for serviceId: ${serviceId}`);
|
||||||
|
|
||||||
|
const serviceData = serviceDoc.data();
|
||||||
|
const gymId = orderData.metaInfo?.gymId || serviceData?.gymId;
|
||||||
|
|
||||||
|
if (gymId) {
|
||||||
|
try {
|
||||||
|
const gymDoc = await admin.firestore().collection('gyms').doc(gymId).get();
|
||||||
|
let gymName = 'Fitlien';
|
||||||
|
let gymAddress = '';
|
||||||
|
let gymOwnerEmail = '';
|
||||||
|
|
||||||
|
if (gymDoc.exists) {
|
||||||
|
const gymData = gymDoc.data();
|
||||||
|
gymName = gymData?.name || 'Fitlien';
|
||||||
|
gymAddress = gymData?.address || '';
|
||||||
|
|
||||||
|
if (gymData?.userId) {
|
||||||
|
const gymOwnerDoc = await admin.firestore()
|
||||||
|
.collection('users')
|
||||||
|
.doc(gymData.userId)
|
||||||
|
.get();
|
||||||
|
|
||||||
|
if (gymOwnerDoc.exists) {
|
||||||
|
const gymOwnerData = gymOwnerDoc.data();
|
||||||
|
gymOwnerEmail = gymOwnerData?.email || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoiceNumber = `SRV-${payload.merchantOrderId.substring(0, 8)}`;
|
||||||
|
logger.info(`Generated invoice number for service: ${invoiceNumber}`);
|
||||||
|
|
||||||
|
const discountPercentage = orderData.metaInfo?.discount || 0;
|
||||||
|
const hasDiscount = discountPercentage > 0;
|
||||||
|
const isFreeService = discountPercentage === 100;
|
||||||
|
const originalAmount = hasDiscount ?
|
||||||
|
orderData.amount / (1 - discountPercentage / 100) :
|
||||||
|
orderData.amount;
|
||||||
|
const discountText = isFreeService ?
|
||||||
|
" (Free Service)" :
|
||||||
|
hasDiscount ? ` (${discountPercentage.toFixed(0)}% discount applied)` :
|
||||||
|
'';
|
||||||
|
const amountSaved = hasDiscount ?
|
||||||
|
originalAmount - orderData.amount :
|
||||||
|
0;
|
||||||
|
|
||||||
|
const invoiceData = {
|
||||||
|
invoiceNumber,
|
||||||
|
businessName: gymName,
|
||||||
|
address: gymAddress,
|
||||||
|
gstNumber: orderData.metaInfo?.gstNumber,
|
||||||
|
customerName: orderData.metaInfo?.customerName || serviceData?.customerName || '',
|
||||||
|
phoneNumber: orderData.metaInfo?.customerPhone || serviceData?.phoneNumber || '',
|
||||||
|
email: orderData.metaInfo?.customerEmail || serviceData?.email || '',
|
||||||
|
planName: orderData.metaInfo?.serviceName || serviceData?.serviceName || 'Service',
|
||||||
|
amount: orderData.amount,
|
||||||
|
transactionId: payload.orderId,
|
||||||
|
paymentDate: new Date(),
|
||||||
|
paymentMethod: 'Online'
|
||||||
|
};
|
||||||
|
|
||||||
|
const invoicePath = await invoiceService.generateInvoice(invoiceData);
|
||||||
|
logger.info(`Service invoice generated successfully at path: ${invoicePath}`);
|
||||||
|
|
||||||
|
await serviceRef.update({
|
||||||
|
invoicePath: invoicePath,
|
||||||
|
invoiceNumber: invoiceNumber
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Updated service booking with invoice path: ${invoicePath}`);
|
||||||
|
|
||||||
|
const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath);
|
||||||
|
const formattedDate = format(new Date(), 'dd/MM/yyyy');
|
||||||
|
|
||||||
|
if (invoiceData.email) {
|
||||||
|
logger.info(`Preparing to send service invoice email to customer: ${invoiceData.email}`);
|
||||||
|
try {
|
||||||
|
const emailSubject = isFreeService
|
||||||
|
? `Free Service Confirmed - ${gymName}`
|
||||||
|
: `Service Booking Confirmed - ${gymName}`;
|
||||||
|
|
||||||
|
const customerEmailHtml = `
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h2>${isFreeService ? 'Free Service Confirmed' : 'Service Booking Confirmed'}</h2>
|
||||||
|
<p>Dear ${invoiceData.customerName},</p>
|
||||||
|
<p>${isFreeService ? 'Your free service has been successfully confirmed.' : 'Thank you for your payment. Your service booking has been confirmed.'}</p>
|
||||||
|
<p>Please find attached your invoice for the service.</p>
|
||||||
|
<p>Service Details:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Gym: ${gymName}</li>
|
||||||
|
<li>Service: ${invoiceData.planName}</li>
|
||||||
|
${hasDiscount ? `<li>Original Price: ₹${originalAmount.toFixed(2)}</li>` : ''}
|
||||||
|
${hasDiscount ? `<li>Discount: ${discountPercentage.toFixed(1)}%</li>` : ''}
|
||||||
|
${hasDiscount ? `<li>You Save: ₹${amountSaved.toFixed(2)}</li>` : ''}
|
||||||
|
<li>Amount: ₹${orderData.amount.toFixed(2)}${discountText}</li>
|
||||||
|
<li>Transaction ID: ${payload.merchantOrderId}</li>
|
||||||
|
<li>Date: ${formattedDate}</li>
|
||||||
|
</ul>
|
||||||
|
<p>If you have any questions, please contact us.</p>
|
||||||
|
<p>Regards,<br>Fitlien Team</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
await sendEmailWithAttachmentUtil(
|
||||||
|
invoiceData.email,
|
||||||
|
emailSubject,
|
||||||
|
customerEmailHtml,
|
||||||
|
downloadUrl,
|
||||||
|
`Invoice_${path.basename(invoicePath)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`Service invoice email sent to customer (${invoiceData.email}) for payment: ${payload.merchantOrderId}`);
|
||||||
|
} catch (emailError) {
|
||||||
|
logger.error('Error sending customer service invoice email:', emailError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gymOwnerEmail) {
|
||||||
|
logger.info(`Preparing to send service invoice email to gym owner: ${gymOwnerEmail}`);
|
||||||
|
try {
|
||||||
|
const ownerEmailSubject = isFreeService
|
||||||
|
? `Free Service Assigned - ${gymName}`
|
||||||
|
: `New Service Booking - ${gymName}`;
|
||||||
|
|
||||||
|
const gymOwnerEmailHtml = `
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h2>${isFreeService ? 'Free Service Assigned' : 'New Service Booking Received'}</h2>
|
||||||
|
<p>Dear Gym Owner,</p>
|
||||||
|
<p>${isFreeService ? 'A free service has been assigned' : 'A new service booking has been received'} for your gym.</p>
|
||||||
|
<p>Customer Details:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Name: ${invoiceData.customerName}</li>
|
||||||
|
<li>Phone: ${invoiceData.phoneNumber}</li>
|
||||||
|
<li>Email: ${invoiceData.email}</li>
|
||||||
|
</ul>
|
||||||
|
<p>Service Details:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Service: ${invoiceData.planName}</li>
|
||||||
|
${hasDiscount ? `<li>Original Price: ₹${originalAmount.toFixed(2)}</li>` : ''}
|
||||||
|
${hasDiscount ? `<li>Discount: ${discountPercentage.toFixed(1)}%</li>` : ''}
|
||||||
|
${hasDiscount ? `<li>Amount Saved by Customer: ₹${amountSaved.toFixed(2)}</li>` : ''}
|
||||||
|
<li>Amount: ₹${orderData.amount.toFixed(2)}${discountText}</li>
|
||||||
|
<li>Transaction ID: ${payload.merchantOrderId}</li>
|
||||||
|
<li>Date: ${formattedDate}</li>
|
||||||
|
</ul>
|
||||||
|
<p>Please find the invoice attached.</p>
|
||||||
|
<p>Regards,<br>Fitlien Team</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
await sendEmailWithAttachmentUtil(
|
||||||
|
gymOwnerEmail,
|
||||||
|
ownerEmailSubject,
|
||||||
|
gymOwnerEmailHtml,
|
||||||
|
downloadUrl,
|
||||||
|
`Invoice_${path.basename(invoicePath)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`Service invoice email sent to gym owner (${gymOwnerEmail}) for payment: ${payload.merchantOrderId}`);
|
||||||
|
} catch (ownerEmailError) {
|
||||||
|
logger.error('Error sending gym owner service invoice email:', ownerEmailError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (invoiceError) {
|
||||||
|
logger.error('Error generating service invoice:', invoiceError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error processing service payment:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user