From b372d81ff04e4ec0b1e92da0db3c1895f315d1b4 Mon Sep 17 00:00:00 2001 From: Allen T J Date: Tue, 20 May 2025 09:04:45 +0000 Subject: [PATCH] phonepe (#33) Co-authored-by: AllenTJ7 <163137620+AllenTJ7@users.noreply.github.com> Reviewed-on: https://git.cosqnet.com/cosqnet/fitlien-services/pulls/33 --- functions/package-lock.json | 210 ++++++++++++ functions/package.json | 2 + .../phonepe/invoice/invoiceService.ts | 298 +++++------------- 3 files changed, 299 insertions(+), 211 deletions(-) diff --git a/functions/package-lock.json b/functions/package-lock.json index 9feb2ad..e5b23b4 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -18,6 +18,8 @@ "form-data": "^4.0.1", "functions": "file:", "html-to-text": "^9.0.5", + "jspdf": "^3.0.1", + "jspdf-autotable": "^5.0.2", "long": "^5.3.2", "mailgun.js": "^10.4.0", "node-fetch": "^2.7.0", @@ -1101,6 +1103,14 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", @@ -2859,6 +2869,12 @@ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "optional": true + }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", @@ -2924,6 +2940,12 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "optional": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -3066,6 +3088,17 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3248,6 +3281,15 @@ "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3379,6 +3421,17 @@ "node-int64": "^0.4.0" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -3494,6 +3547,25 @@ ], "peer": true }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3659,6 +3731,17 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-js": { + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.42.0.tgz", + "integrity": "sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -3713,6 +3796,15 @@ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -3888,6 +3980,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", @@ -4269,6 +4370,11 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4833,6 +4939,19 @@ "node": ">=14" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "optional": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", @@ -6047,6 +6166,31 @@ "node": ">=10" } }, + "node_modules/jspdf": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz", + "integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==", + "dependencies": { + "@babel/runtime": "^7.26.7", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf-autotable": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.2.tgz", + "integrity": "sha512-YNKeB7qmx3pxOLcNeoqAv3qTS7KuvVwkFe5AduCawpop3NOkBUtqDToxNc225MlNecxT4kP2Zy3z/y/yvGdXUQ==", + "peerDependencies": { + "jspdf": "^2 || ^3" + } + }, "node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -6765,6 +6909,12 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6960,6 +7110,15 @@ "node": ">=0.4.x" } }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -7003,6 +7162,12 @@ "node": ">= 6" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "optional": true + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -7108,6 +7273,15 @@ "node": ">=14" } }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7431,6 +7605,15 @@ "node": ">=10" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -7579,6 +7762,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/teeny-request": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", @@ -7671,6 +7863,15 @@ "node": ">=8" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -7931,6 +8132,15 @@ "node": ">= 0.4.0" } }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", diff --git a/functions/package.json b/functions/package.json index bbfdd3f..b9f1c89 100644 --- a/functions/package.json +++ b/functions/package.json @@ -25,6 +25,8 @@ "form-data": "^4.0.1", "functions": "file:", "html-to-text": "^9.0.5", + "jspdf": "^3.0.1", + "jspdf-autotable": "^5.0.2", "long": "^5.3.2", "mailgun.js": "^10.4.0", "node-fetch": "^2.7.0", diff --git a/functions/src/payments/phonepe/invoice/invoiceService.ts b/functions/src/payments/phonepe/invoice/invoiceService.ts index 59bb0fd..95ffa7d 100644 --- a/functions/src/payments/phonepe/invoice/invoiceService.ts +++ b/functions/src/payments/phonepe/invoice/invoiceService.ts @@ -4,12 +4,8 @@ import * as os from 'os'; import * as path from 'path'; import { format } from 'date-fns'; import { sendEmailWithAttachmentUtil } from "../../../utils/emailService"; - -const pdfMake = require('pdfmake/build/pdfmake'); -const pdfFonts = require('pdfmake/build/vfs_fonts'); -pdfMake.vfs = pdfFonts.pdfMake ? pdfFonts.pdfMake.vfs : pdfFonts.vfs; - - +import { jsPDF } from "jspdf"; +import 'jspdf-autotable'; const admin = getAdmin(); const logger = getLogger(); @@ -54,216 +50,96 @@ export class InvoiceService { const cgst = hasGst ? baseAmount * 0.09 : 0; const formattedDate = format(data.paymentDate, 'dd/MM/yyyy'); + const doc = new jsPDF(); - const docDefinition: any = { - content: [ - { - columns: [ - [ - { text: data.businessName, style: 'businessName' }, - { text: data.address, style: 'businessAddress' }, - hasGst ? { text: `GSTIN: ${data.gstNumber}`, style: 'businessDetails' } : {} - ], - [ - { text: 'RECEIPT', style: 'invoiceTitle', alignment: 'right' }, - { text: `Receipt #: ${data.invoiceNumber}`, style: 'invoiceDetails', alignment: 'right' }, - { text: `Date: ${formattedDate}`, style: 'invoiceDetails', alignment: 'right' } - ] - ] - }, - { canvas: [{ type: 'line', x1: 0, y1: 5, x2: 515, y2: 5, lineWidth: 0.5 }] }, - { text: '', margin: [0, 10] }, - - { - style: 'customerBox', - table: { - widths: ['*'], - body: [ - [ - { - stack: [ - { text: 'Receipt To:', style: 'customerTitle' }, - { text: data.customerName, style: 'customerDetails' }, - { text: `Phone: ${data.phoneNumber}`, style: 'customerDetails' }, - { text: `Email: ${data.email}`, style: 'customerDetails' } - ], - margin: [10, 10] - } - ] - ] - }, - layout: 'lightHorizontalLines' - }, - { text: '', margin: [0, 10] }, - - { - table: { - headerRows: 1, - widths: [30, '*', 80, 100], - body: [ - [ - { text: 'No.', style: 'tableHeader', alignment: 'center' }, - { text: 'Description', style: 'tableHeader' }, - { text: 'HSN/SAC', style: 'tableHeader', alignment: 'center' }, - { text: 'Amount (INR)', style: 'tableHeader', alignment: 'right' } - ], - [ - { text: '1', alignment: 'center' }, - { text: `${data.planName} Subscription` }, - { text: '999723', alignment: 'center' }, - { text: baseAmount.toFixed(2), alignment: 'right' } - ] - ] - } - }, - { text: '', margin: [0, 10] }, - - { - columns: [ - { width: '*', text: '' }, - { - width: 'auto', - table: { - widths: [100, 100], - body: hasGst ? [ - [ - { text: 'Taxable Amount:', alignment: 'right' }, - { text: `${baseAmount.toFixed(2)} INR`, alignment: 'right' } - ], - [ - { text: 'SGST (9%):', alignment: 'right' }, - { text: `${sgst.toFixed(2)} INR`, alignment: 'right' } - ], - [ - { text: 'CGST (9%):', alignment: 'right' }, - { text: `${cgst.toFixed(2)} INR`, alignment: 'right' } - ], - [ - { text: 'Total Amount:', style: 'totalAmount', alignment: 'right' }, - { text: `${data.amount.toFixed(2)} INR`, style: 'totalAmount', alignment: 'right' } - ] - ] : [ - [ - { text: 'Total Amount:', style: 'totalAmount', alignment: 'right' }, - { text: `${data.amount.toFixed(2)} INR`, style: 'totalAmount', alignment: 'right' } - ] - ] - }, - layout: { - hLineWidth: function(i: number, node: any) { - return (i === node.table.body.length - 1) ? 0.5 : 0; - }, - vLineWidth: function() { return 0; } - } - } - ] - }, - { text: '', margin: [0, 20] }, - - { - style: 'paymentBox', - table: { - widths: ['*'], - body: [ - [ - { - stack: [ - { text: 'Payment Information:', style: 'paymentTitle' }, - { text: `Transaction ID: ${data.transactionId}`, style: 'paymentDetails' }, - { text: `Payment Method: ${data.paymentMethod}`, style: 'paymentDetails' }, - { text: `Payment Date: ${formattedDate}`, style: 'paymentDetails' } - ], - margin: [10, 10] - } - ] - ] - }, - layout: 'lightHorizontalLines' - }, - { text: '', margin: [0, 20] }, - - { text: 'Thank you for your business!', style: 'footer', alignment: 'center' }, - { text: 'This is a computer-generated receipt and does not require a signature.', style: 'disclaimer', alignment: 'center' } + doc.setFontSize(20); + doc.setFont('helvetica', 'bold'); + doc.text(data.businessName, 20, 20); + + doc.setFontSize(12); + doc.setFont('helvetica', 'normal'); + doc.text(data.address, 20, 30); + + if (hasGst) { + doc.text(`GSTIN: ${data.gstNumber}`, 20, 40); + } + + doc.setFontSize(24); + doc.setFont('helvetica', 'bold'); + doc.text('RECEIPT', 190, 20, { align: 'right' }); + + doc.setFontSize(12); + doc.text(`Receipt #: ${data.invoiceNumber}`, 190, 30, { align: 'right' }); + doc.text(`Date: ${formattedDate}`, 190, 40, { align: 'right' }); + + doc.line(20, 45, 190, 45); + + doc.setFontSize(12); + doc.setFont('helvetica', 'bold'); + doc.text('Receipt To:', 20, 60); + + doc.setFont('helvetica', 'normal'); + doc.text(data.customerName, 20, 70); + doc.text(`Phone: ${data.phoneNumber}`, 20, 80); + doc.text(`Email: ${data.email}`, 20, 90); + + (doc as any).autoTable({ + startY: 110, + head: [['No.', 'Description', 'HSN/SAC', 'Amount (INR)']], + body: [ + ['1', `${data.planName} Subscription`, '999723', baseAmount.toFixed(2)] ], - styles: { - businessName: { - fontSize: 20, - bold: true, - margin: [0, 0, 0, 5] - }, - businessAddress: { - fontSize: 12, - margin: [0, 0, 0, 5] - }, - businessDetails: { - fontSize: 12 - }, - invoiceTitle: { - fontSize: 24, - bold: true - }, - invoiceDetails: { - fontSize: 12, - margin: [0, 5, 0, 0] - }, - customerBox: { - margin: [0, 10, 0, 10] - }, - customerTitle: { - fontSize: 12, - bold: true, - margin: [0, 0, 0, 5] - }, - customerDetails: { - fontSize: 12, - margin: [0, 2, 0, 0] - }, - tableHeader: { - fontSize: 12, - bold: true, - margin: [0, 5, 0, 5] - }, - totalAmount: { - fontSize: 12, - bold: true - }, - paymentBox: { - margin: [0, 10, 0, 10] - }, - paymentTitle: { - fontSize: 12, - bold: true, - margin: [0, 0, 0, 5] - }, - paymentDetails: { - fontSize: 12, - margin: [0, 2, 0, 0] - }, - footer: { - fontSize: 12, - italics: true, - margin: [0, 0, 0, 5] - }, - disclaimer: { - fontSize: 10 - } - }, - defaultStyle: { - font: 'Helvetica' + headStyles: { fillColor: [220, 220, 220], textColor: [0, 0, 0], fontStyle: 'bold' }, + styles: { halign: 'center' }, + columnStyles: { + 0: { halign: 'center', cellWidth: 20 }, + 1: { halign: 'left' }, + 2: { halign: 'center', cellWidth: 40 }, + 3: { halign: 'right', cellWidth: 40 } } - }; - - const pdfDoc = pdfMake.createPdf(docDefinition); - - await new Promise((resolve, reject) => { - pdfDoc.getBuffer((buffer: Buffer) => { - fs.writeFile(tempFilePath, buffer, (err) => { - if (err) reject(err); - else resolve(); - }); - }); }); + const finalY = (doc as any).lastAutoTable.finalY + 20; + + if (hasGst) { + doc.text('Taxable Amount:', 150, finalY, { align: 'right' }); + doc.text(`${baseAmount.toFixed(2)} INR`, 190, finalY, { align: 'right' }); + + doc.text('SGST (9%):', 150, finalY + 10, { align: 'right' }); + doc.text(`${sgst.toFixed(2)} INR`, 190, finalY + 10, { align: 'right' }); + + doc.text('CGST (9%):', 150, finalY + 20, { align: 'right' }); + doc.text(`${cgst.toFixed(2)} INR`, 190, finalY + 20, { align: 'right' }); + + doc.setFont('helvetica', 'bold'); + doc.text('Total Amount:', 150, finalY + 30, { align: 'right' }); + doc.text(`${data.amount.toFixed(2)} INR`, 190, finalY + 30, { align: 'right' }); + } else { + doc.setFont('helvetica', 'bold'); + doc.text('Total Amount:', 150, finalY, { align: 'right' }); + doc.text(`${data.amount.toFixed(2)} INR`, 190, finalY, { align: 'right' }); + } + + const paymentY = hasGst ? finalY + 50 : finalY + 20; + + doc.setFont('helvetica', 'bold'); + doc.text('Payment Information:', 20, paymentY); + + doc.setFont('helvetica', 'normal'); + doc.text(`Transaction ID: ${data.transactionId}`, 20, paymentY + 10); + doc.text(`Payment Method: ${data.paymentMethod}`, 20, paymentY + 20); + doc.text(`Payment Date: ${formattedDate}`, 20, paymentY + 30); + + doc.setFontSize(12); + doc.setFont('helvetica', 'italic'); + doc.text('Thank you for your business!', 105, 270, { align: 'center' }); + + doc.setFontSize(10); + doc.setFont('helvetica', 'normal'); + doc.text('This is a computer-generated receipt and does not require a signature.', 105, 280, { align: 'center' }); + + fs.writeFileSync(tempFilePath, Buffer.from(doc.output('arraybuffer'))); + const invoicePath = `invoices/${data.invoiceNumber}.pdf`; const bucket = admin.storage().bucket(); await bucket.upload(tempFilePath, {