phonepe #29
							
								
								
									
										286
									
								
								functions/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										286
									
								
								functions/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -22,12 +22,14 @@ | |||||||
|         "mailgun.js": "^10.4.0", |         "mailgun.js": "^10.4.0", | ||||||
|         "node-fetch": "^2.7.0", |         "node-fetch": "^2.7.0", | ||||||
|         "pdfjs-dist": "^5.0.375", |         "pdfjs-dist": "^5.0.375", | ||||||
|  |         "pdfmake": "^0.2.20", | ||||||
|         "twilio": "^5.4.0" |         "twilio": "^5.4.0" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
|         "@types/long": "^5.0.0", |         "@types/long": "^5.0.0", | ||||||
|         "@types/mime-types": "^2.1.4", |         "@types/mime-types": "^2.1.4", | ||||||
|         "@types/node": "^22.13.14", |         "@types/node": "^22.13.14", | ||||||
|  |         "@types/pdfmake": "^0.2.11", | ||||||
|         "firebase-functions-test": "^3.1.0", |         "firebase-functions-test": "^3.1.0", | ||||||
|         "typescript": "^5.8.2" |         "typescript": "^5.8.2" | ||||||
|       }, |       }, | ||||||
| @ -1260,6 +1262,52 @@ | |||||||
|         "tslib": "^2.1.0" |         "tslib": "^2.1.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@foliojs-fork/fontkit": { | ||||||
|  |       "version": "1.9.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz", | ||||||
|  |       "integrity": "sha512-IfB5EiIb+GZk+77TRB86AHroVaqfq8JRFlUbz0WEwsInyCG0epX2tCPOy+UfaWPju30DeVoUAXfzWXmhn753KA==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@foliojs-fork/restructure": "^2.0.2", | ||||||
|  |         "brotli": "^1.2.0", | ||||||
|  |         "clone": "^1.0.4", | ||||||
|  |         "deep-equal": "^1.0.0", | ||||||
|  |         "dfa": "^1.2.0", | ||||||
|  |         "tiny-inflate": "^1.0.2", | ||||||
|  |         "unicode-properties": "^1.2.2", | ||||||
|  |         "unicode-trie": "^2.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@foliojs-fork/linebreak": { | ||||||
|  |       "version": "1.1.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@foliojs-fork/linebreak/-/linebreak-1.1.2.tgz", | ||||||
|  |       "integrity": "sha512-ZPohpxxbuKNE0l/5iBJnOAfUaMACwvUIKCvqtWGKIMv1lPYoNjYXRfhi9FeeV9McBkBLxsMFWTVVhHJA8cyzvg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "base64-js": "1.3.1", | ||||||
|  |         "unicode-trie": "^2.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@foliojs-fork/linebreak/node_modules/base64-js": { | ||||||
|  |       "version": "1.3.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", | ||||||
|  |       "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" | ||||||
|  |     }, | ||||||
|  |     "node_modules/@foliojs-fork/pdfkit": { | ||||||
|  |       "version": "0.15.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@foliojs-fork/pdfkit/-/pdfkit-0.15.3.tgz", | ||||||
|  |       "integrity": "sha512-Obc0Wmy3bm7BINFVvPhcl2rnSSK61DQrlHU8aXnAqDk9LCjWdUOPwhgD8Ywz5VtuFjRxmVOM/kQ/XLIBjDvltw==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@foliojs-fork/fontkit": "^1.9.2", | ||||||
|  |         "@foliojs-fork/linebreak": "^1.1.1", | ||||||
|  |         "crypto-js": "^4.2.0", | ||||||
|  |         "jpeg-exif": "^1.1.4", | ||||||
|  |         "png-js": "^1.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@foliojs-fork/restructure": { | ||||||
|  |       "version": "2.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@foliojs-fork/restructure/-/restructure-2.0.2.tgz", | ||||||
|  |       "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==" | ||||||
|  |     }, | ||||||
|     "node_modules/@google-cloud/firestore": { |     "node_modules/@google-cloud/firestore": { | ||||||
|       "version": "7.11.0", |       "version": "7.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", |       "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-7.11.0.tgz", | ||||||
| @ -2787,6 +2835,25 @@ | |||||||
|         "form-data": "^4.0.0" |         "form-data": "^4.0.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/pdfkit": { | ||||||
|  |       "version": "0.13.9", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.13.9.tgz", | ||||||
|  |       "integrity": "sha512-RDG8Yb1zT7I01FfpwK7nMSA433XWpblMqSCtA5vJlSyavWZb303HUYPCel6JTiDDFqwGLvtAnYbH8N/e0Cb89g==", | ||||||
|  |       "dev": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "@types/node": "*" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@types/pdfmake": { | ||||||
|  |       "version": "0.2.11", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/pdfmake/-/pdfmake-0.2.11.tgz", | ||||||
|  |       "integrity": "sha512-gglgMQhnG6C2kco13DJlvokqTxL+XKxHwCejElH8fSCNF9ZCkRK6Mzo011jQ0zuug+YlIgn6BpcpZrARyWdW3Q==", | ||||||
|  |       "dev": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "@types/node": "*", | ||||||
|  |         "@types/pdfkit": "*" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/@types/qs": { |     "node_modules/@types/qs": { | ||||||
|       "version": "6.9.18", |       "version": "6.9.18", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", |       "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", | ||||||
| @ -3261,6 +3328,14 @@ | |||||||
|         "node": ">=8" |         "node": ">=8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/brotli": { | ||||||
|  |       "version": "1.3.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", | ||||||
|  |       "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "base64-js": "^1.1.2" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/browserslist": { |     "node_modules/browserslist": { | ||||||
|       "version": "4.24.4", |       "version": "4.24.4", | ||||||
|       "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", |       "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", | ||||||
| @ -3483,6 +3558,14 @@ | |||||||
|         "node": ">=12" |         "node": ">=12" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/clone": { | ||||||
|  |       "version": "1.0.4", | ||||||
|  |       "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", | ||||||
|  |       "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=0.8" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/co": { |     "node_modules/co": { | ||||||
|       "version": "4.6.0", |       "version": "4.6.0", | ||||||
|       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", |       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", | ||||||
| @ -3625,6 +3708,11 @@ | |||||||
|         "node": ">= 8" |         "node": ">= 8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/crypto-js": { | ||||||
|  |       "version": "4.2.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", | ||||||
|  |       "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" | ||||||
|  |     }, | ||||||
|     "node_modules/dayjs": { |     "node_modules/dayjs": { | ||||||
|       "version": "1.11.13", |       "version": "1.11.13", | ||||||
|       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", |       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", | ||||||
| @ -3653,6 +3741,25 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/deep-equal": { | ||||||
|  |       "version": "1.1.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", | ||||||
|  |       "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "is-arguments": "^1.1.1", | ||||||
|  |         "is-date-object": "^1.0.5", | ||||||
|  |         "is-regex": "^1.1.4", | ||||||
|  |         "object-is": "^1.1.5", | ||||||
|  |         "object-keys": "^1.1.1", | ||||||
|  |         "regexp.prototype.flags": "^1.5.1" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/deepmerge": { |     "node_modules/deepmerge": { | ||||||
|       "version": "4.3.1", |       "version": "4.3.1", | ||||||
|       "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", |       "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", | ||||||
| @ -3677,6 +3784,22 @@ | |||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/define-properties": { | ||||||
|  |       "version": "1.2.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", | ||||||
|  |       "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "define-data-property": "^1.0.1", | ||||||
|  |         "has-property-descriptors": "^1.0.0", | ||||||
|  |         "object-keys": "^1.1.1" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/delayed-stream": { |     "node_modules/delayed-stream": { | ||||||
|       "version": "1.0.0", |       "version": "1.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", |       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | ||||||
| @ -3712,6 +3835,11 @@ | |||||||
|         "node": ">=8" |         "node": ">=8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/dfa": { | ||||||
|  |       "version": "1.2.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", | ||||||
|  |       "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" | ||||||
|  |     }, | ||||||
|     "node_modules/diff-sequences": { |     "node_modules/diff-sequences": { | ||||||
|       "version": "29.6.3", |       "version": "29.6.3", | ||||||
|       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", |       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", | ||||||
| @ -4351,6 +4479,14 @@ | |||||||
|       "resolved": "", |       "resolved": "", | ||||||
|       "link": true |       "link": true | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/functions-have-names": { | ||||||
|  |       "version": "1.2.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", | ||||||
|  |       "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/gaxios": { |     "node_modules/gaxios": { | ||||||
|       "version": "6.7.1", |       "version": "6.7.1", | ||||||
|       "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", |       "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", | ||||||
| @ -4950,6 +5086,21 @@ | |||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/is-date-object": { | ||||||
|  |       "version": "1.1.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", | ||||||
|  |       "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "call-bound": "^1.0.2", | ||||||
|  |         "has-tostringtag": "^1.0.2" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/is-fullwidth-code-point": { |     "node_modules/is-fullwidth-code-point": { | ||||||
|       "version": "3.0.0", |       "version": "3.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", |       "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | ||||||
| @ -5772,6 +5923,11 @@ | |||||||
|         "url": "https://github.com/sponsors/panva" |         "url": "https://github.com/sponsors/panva" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/jpeg-exif": { | ||||||
|  |       "version": "1.1.4", | ||||||
|  |       "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", | ||||||
|  |       "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==" | ||||||
|  |     }, | ||||||
|     "node_modules/js-tokens": { |     "node_modules/js-tokens": { | ||||||
|       "version": "4.0.0", |       "version": "4.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", |       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", | ||||||
| @ -6366,6 +6522,29 @@ | |||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/object-is": { | ||||||
|  |       "version": "1.1.6", | ||||||
|  |       "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", | ||||||
|  |       "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "call-bind": "^1.0.7", | ||||||
|  |         "define-properties": "^1.2.1" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/object-keys": { | ||||||
|  |       "version": "1.1.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", | ||||||
|  |       "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/on-finished": { |     "node_modules/on-finished": { | ||||||
|       "version": "2.4.1", |       "version": "2.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", |       "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", | ||||||
| @ -6456,6 +6635,11 @@ | |||||||
|         "node": ">=6" |         "node": ">=6" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/pako": { | ||||||
|  |       "version": "0.2.9", | ||||||
|  |       "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", | ||||||
|  |       "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" | ||||||
|  |     }, | ||||||
|     "node_modules/parse-json": { |     "node_modules/parse-json": { | ||||||
|       "version": "5.2.0", |       "version": "5.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", |       "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", | ||||||
| @ -6548,6 +6732,31 @@ | |||||||
|         "@napi-rs/canvas": "^0.1.67" |         "@napi-rs/canvas": "^0.1.67" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/pdfmake": { | ||||||
|  |       "version": "0.2.20", | ||||||
|  |       "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.20.tgz", | ||||||
|  |       "integrity": "sha512-bGbxbGFP5p8PWNT3Phsu1ZcRLnRfF6jmnuKTkgmt6i5PZzSdX6JaB+NeTz9q+aocfW8SE9GUjL3o/5GroBqGcQ==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@foliojs-fork/linebreak": "^1.1.2", | ||||||
|  |         "@foliojs-fork/pdfkit": "^0.15.3", | ||||||
|  |         "iconv-lite": "^0.6.3", | ||||||
|  |         "xmldoc": "^2.0.1" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=18" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/pdfmake/node_modules/iconv-lite": { | ||||||
|  |       "version": "0.6.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", | ||||||
|  |       "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "safer-buffer": ">= 2.1.2 < 3.0.0" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=0.10.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/peberminta": { |     "node_modules/peberminta": { | ||||||
|       "version": "0.9.0", |       "version": "0.9.0", | ||||||
|       "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", |       "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", | ||||||
| @ -6599,6 +6808,11 @@ | |||||||
|         "node": ">=8" |         "node": ">=8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/png-js": { | ||||||
|  |       "version": "1.0.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", | ||||||
|  |       "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" | ||||||
|  |     }, | ||||||
|     "node_modules/possible-typed-array-names": { |     "node_modules/possible-typed-array-names": { | ||||||
|       "version": "1.1.0", |       "version": "1.1.0", | ||||||
|       "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", |       "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", | ||||||
| @ -6789,6 +7003,25 @@ | |||||||
|         "node": ">= 6" |         "node": ">= 6" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/regexp.prototype.flags": { | ||||||
|  |       "version": "1.5.4", | ||||||
|  |       "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", | ||||||
|  |       "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "call-bind": "^1.0.8", | ||||||
|  |         "define-properties": "^1.2.1", | ||||||
|  |         "es-errors": "^1.3.0", | ||||||
|  |         "get-proto": "^1.0.1", | ||||||
|  |         "gopd": "^1.2.0", | ||||||
|  |         "set-function-name": "^2.0.2" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/require-directory": { |     "node_modules/require-directory": { | ||||||
|       "version": "2.1.1", |       "version": "2.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", |       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", | ||||||
| @ -7023,6 +7256,20 @@ | |||||||
|         "node": ">= 0.4" |         "node": ">= 0.4" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/set-function-name": { | ||||||
|  |       "version": "2.0.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", | ||||||
|  |       "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "define-data-property": "^1.1.4", | ||||||
|  |         "es-errors": "^1.3.0", | ||||||
|  |         "functions-have-names": "^1.2.3", | ||||||
|  |         "has-property-descriptors": "^1.0.2" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/setprototypeof": { |     "node_modules/setprototypeof": { | ||||||
|       "version": "1.2.0", |       "version": "1.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", |       "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", | ||||||
| @ -7424,6 +7671,11 @@ | |||||||
|         "node": ">=8" |         "node": ">=8" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/tiny-inflate": { | ||||||
|  |       "version": "1.0.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", | ||||||
|  |       "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" | ||||||
|  |     }, | ||||||
|     "node_modules/tmpl": { |     "node_modules/tmpl": { | ||||||
|       "version": "1.0.5", |       "version": "1.0.5", | ||||||
|       "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", |       "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", | ||||||
| @ -7582,6 +7834,24 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", |       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", | ||||||
|       "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" |       "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/unicode-properties": { | ||||||
|  |       "version": "1.4.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", | ||||||
|  |       "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "base64-js": "^1.3.0", | ||||||
|  |         "unicode-trie": "^2.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/unicode-trie": { | ||||||
|  |       "version": "2.0.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", | ||||||
|  |       "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "pako": "^0.2.5", | ||||||
|  |         "tiny-inflate": "^1.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/unpipe": { |     "node_modules/unpipe": { | ||||||
|       "version": "1.0.0", |       "version": "1.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", |       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", | ||||||
| @ -7842,6 +8112,22 @@ | |||||||
|         "node": ">=6.0" |         "node": ">=6.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/xmldoc": { | ||||||
|  |       "version": "2.0.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-2.0.1.tgz", | ||||||
|  |       "integrity": "sha512-sOOqgsjl3PU6iBw+fBUGAkTCE+JFK+sBaOL3pnZgzqk2/yvOD7RlFmZtDRJAEBzdpOYxSXyOQH4mjubdfs3MSg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "sax": "^1.2.4" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">=12.0.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/xmldoc/node_modules/sax": { | ||||||
|  |       "version": "1.4.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", | ||||||
|  |       "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" | ||||||
|  |     }, | ||||||
|     "node_modules/y18n": { |     "node_modules/y18n": { | ||||||
|       "version": "5.0.8", |       "version": "5.0.8", | ||||||
|       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", |       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", | ||||||
|  | |||||||
| @ -29,12 +29,14 @@ | |||||||
|     "mailgun.js": "^10.4.0", |     "mailgun.js": "^10.4.0", | ||||||
|     "node-fetch": "^2.7.0", |     "node-fetch": "^2.7.0", | ||||||
|     "pdfjs-dist": "^5.0.375", |     "pdfjs-dist": "^5.0.375", | ||||||
|  |     "pdfmake": "^0.2.20", | ||||||
|     "twilio": "^5.4.0" |     "twilio": "^5.4.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@types/long": "^5.0.0", |     "@types/long": "^5.0.0", | ||||||
|     "@types/mime-types": "^2.1.4", |     "@types/mime-types": "^2.1.4", | ||||||
|     "@types/node": "^22.13.14", |     "@types/node": "^22.13.14", | ||||||
|  |     "@types/pdfmake": "^0.2.11", | ||||||
|     "firebase-functions-test": "^3.1.0", |     "firebase-functions-test": "^3.1.0", | ||||||
|     "typescript": "^5.8.2" |     "typescript": "^5.8.2" | ||||||
|   }, |   }, | ||||||
|  | |||||||
| @ -1,10 +1,13 @@ | |||||||
| import { getAdmin, getLogger } from "../../../shared/config"; | import { getAdmin, getLogger } from "../../../shared/config"; | ||||||
| import PDFDocument from 'pdfkit'; |  | ||||||
| import * as fs from 'fs'; | import * as fs from 'fs'; | ||||||
| import * as os from 'os'; | import * as os from 'os'; | ||||||
| import * as path from 'path'; | import * as path from 'path'; | ||||||
| import { format } from 'date-fns'; | import { format } from 'date-fns'; | ||||||
| import { sendEmailWithAttachmentUtil } from "../../../utils/emailService"; | import { sendEmailWithAttachmentUtil } from "../../../utils/emailService"; | ||||||
|  | import * as pdfMake from 'pdfmake/build/pdfmake'; | ||||||
|  | import * as pdfFonts from 'pdfmake/build/vfs_fonts'; | ||||||
|  | 
 | ||||||
|  | (pdfMake as any).vfs = pdfFonts.vfs; | ||||||
| 
 | 
 | ||||||
| const admin = getAdmin(); | const admin = getAdmin(); | ||||||
| const logger = getLogger(); | const logger = getLogger(); | ||||||
| @ -43,99 +46,223 @@ export class InvoiceService { | |||||||
|   async generateInvoice(data: InvoiceData): Promise<string> { |   async generateInvoice(data: InvoiceData): Promise<string> { | ||||||
|     try { |     try { | ||||||
|       const tempFilePath = path.join(os.tmpdir(), `invoice_${data.invoiceNumber}.pdf`); |       const tempFilePath = path.join(os.tmpdir(), `invoice_${data.invoiceNumber}.pdf`); | ||||||
|       const doc = new PDFDocument({ margin: 50 }); |  | ||||||
|        |        | ||||||
|       // Create a write stream to the temporary file
 |  | ||||||
|       const writeStream = fs.createWriteStream(tempFilePath); |  | ||||||
|       doc.pipe(writeStream); |  | ||||||
|        |  | ||||||
|       // Check if GST is applicable
 |  | ||||||
|       const hasGst = data.gstNumber && data.gstNumber.length > 0; |       const hasGst = data.gstNumber && data.gstNumber.length > 0; | ||||||
|       const baseAmount = hasGst ? data.amount / 1.18 : data.amount; |       const baseAmount = hasGst ? data.amount / 1.18 : data.amount; | ||||||
|       const sgst = hasGst ? baseAmount * 0.09 : 0; |       const sgst = hasGst ? baseAmount * 0.09 : 0; | ||||||
|       const cgst = hasGst ? baseAmount * 0.09 : 0; |       const cgst = hasGst ? baseAmount * 0.09 : 0; | ||||||
|        |        | ||||||
|       // Add business details
 |       const formattedDate = format(data.paymentDate, 'dd/MM/yyyy'); | ||||||
|       doc.fontSize(20).font('Helvetica-Bold').text(data.businessName, 50, 50); |        | ||||||
|       doc.fontSize(12).font('Helvetica').text(data.address, 50, 75, { width: 250 }); |       const docDefinition: any = { | ||||||
|       if (hasGst) { |         content: [ | ||||||
|         doc.text(`GSTIN: ${data.gstNumber}`, 50, 115); |           { | ||||||
|  |             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] }, | ||||||
|            |            | ||||||
|       // Add invoice title and details
 |           { | ||||||
|       doc.fontSize(24).font('Helvetica-Bold').text('RECEIPT', 400, 50, { align: 'right' }); |             table: { | ||||||
|       doc.fontSize(12).font('Helvetica').text(`Receipt #: ${data.invoiceNumber}`, 400, 80, { align: 'right' }); |               headerRows: 1, | ||||||
|       doc.text(`Date: ${format(data.paymentDate, 'dd/MM/yyyy')}`, 400, 95, { align: 'right' }); |               widths: [30, '*', 80, 100], | ||||||
|        |               body: [ | ||||||
|       // Add customer details
 |                 [ | ||||||
|       doc.rect(50, 150, 500, 80).stroke(); |                   { text: 'No.', style: 'tableHeader', alignment: 'center' }, | ||||||
|       doc.fontSize(12).font('Helvetica-Bold').text('Receipt To:', 60, 160); |                   { text: 'Description', style: 'tableHeader' }, | ||||||
|       doc.fontSize(12).font('Helvetica').text(data.customerName, 60, 175); |                   { text: 'HSN/SAC', style: 'tableHeader', alignment: 'center' }, | ||||||
|       doc.text(`Phone: ${data.phoneNumber}`, 60, 190); |                   { text: 'Amount (INR)', style: 'tableHeader', alignment: 'right' } | ||||||
|       doc.text(`Email: ${data.email}`, 60, 205); |                 ], | ||||||
|        |                 [ | ||||||
|       // Add table header
 |                   { text: '1', alignment: 'center' }, | ||||||
|       const tableTop = 260; |                   { text: `${data.planName} Subscription` }, | ||||||
|       doc.rect(50, tableTop, 500, 30).stroke(); |                   { text: '999723', alignment: 'center' }, | ||||||
|       doc.fontSize(12).font('Helvetica-Bold').text('No.', 60, tableTop + 10, { width: 30, align: 'center' }); |                   { text: baseAmount.toFixed(2), alignment: 'right' } | ||||||
|       doc.text('Description', 100, tableTop + 10, { width: 250 }); |                 ] | ||||||
|       doc.text('HSN/SAC', 350, tableTop + 10, { width: 80, align: 'center' }); |               ] | ||||||
|       doc.text('Amount (INR)', 430, tableTop + 10, { width: 100, align: 'right' }); |  | ||||||
|        |  | ||||||
|       // Add table row
 |  | ||||||
|       const rowTop = tableTop + 30; |  | ||||||
|       doc.rect(50, rowTop, 500, 30).stroke(); |  | ||||||
|       doc.fontSize(12).font('Helvetica').text('1', 60, rowTop + 10, { width: 30, align: 'center' }); |  | ||||||
|       doc.text(`${data.planName} Subscription`, 100, rowTop + 10, { width: 250 }); |  | ||||||
|       doc.text('999723', 350, rowTop + 10, { width: 80, align: 'center' }); |  | ||||||
|       doc.text(baseAmount.toFixed(2), 430, rowTop + 10, { width: 100, align: 'right' }); |  | ||||||
|        |  | ||||||
|       // Add totals
 |  | ||||||
|       let currentY = rowTop + 50; |  | ||||||
|        |  | ||||||
|       if (hasGst) { |  | ||||||
|         doc.fontSize(12).font('Helvetica').text('Taxable Amount:', 350, currentY, { width: 100, align: 'right' }); |  | ||||||
|         doc.text(`${baseAmount.toFixed(2)} INR`, 450, currentY, { width: 100, align: 'right' }); |  | ||||||
|         currentY += 20; |  | ||||||
|          |  | ||||||
|         doc.text('SGST (9%):', 350, currentY, { width: 100, align: 'right' }); |  | ||||||
|         doc.text(`${sgst.toFixed(2)} INR`, 450, currentY, { width: 100, align: 'right' }); |  | ||||||
|         currentY += 20; |  | ||||||
|          |  | ||||||
|         doc.text('CGST (9%):', 350, currentY, { width: 100, align: 'right' }); |  | ||||||
|         doc.text(`${cgst.toFixed(2)} INR`, 450, currentY, { width: 100, align: 'right' }); |  | ||||||
|         currentY += 20; |  | ||||||
|             } |             } | ||||||
|  |           }, | ||||||
|  |           { text: '', margin: [0, 10] }, | ||||||
|            |            | ||||||
|       doc.moveTo(350, currentY).lineTo(550, currentY).stroke(); |           { | ||||||
|       currentY += 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] }, | ||||||
|            |            | ||||||
|       doc.fontSize(12).font('Helvetica-Bold').text('Total Amount:', 350, currentY, { width: 100, align: 'right' }); |           { | ||||||
|       doc.text(`${data.amount.toFixed(2)} INR`, 450, currentY, { width: 100, align: 'right' }); |             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] }, | ||||||
|            |            | ||||||
|       // Add payment information
 |           { text: 'Thank you for your business!', style: 'footer', alignment: 'center' }, | ||||||
|       currentY += 40; |           { text: 'This is a computer-generated receipt and does not require a signature.', style: 'disclaimer', alignment: 'center' } | ||||||
|       doc.rect(50, currentY, 500, 80).stroke(); |         ], | ||||||
|       doc.fontSize(12).font('Helvetica-Bold').text('Payment Information:', 60, currentY + 10); |         styles: { | ||||||
|       doc.fontSize(12).font('Helvetica').text(`Transaction ID: ${data.transactionId}`, 60, currentY + 30); |           businessName: { | ||||||
|       doc.text(`Payment Method: ${data.paymentMethod}`, 60, currentY + 45); |             fontSize: 20, | ||||||
|       doc.text(`Payment Date: ${format(data.paymentDate, 'dd/MM/yyyy')}`, 60, currentY + 60); |             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' | ||||||
|  |         } | ||||||
|  |       }; | ||||||
|        |        | ||||||
|       // Add footer
 |       const pdfDoc = pdfMake.createPdf(docDefinition); | ||||||
|       currentY += 100; |  | ||||||
|       doc.fontSize(12).font('Helvetica-Oblique').text('Thank you for your business!', 50, currentY, { align: 'center' }); |  | ||||||
|       doc.fontSize(10).font('Helvetica').text('This is a computer-generated receipt and does not require a signature.', 50, currentY + 20, { align: 'center' }); |  | ||||||
|        |        | ||||||
|       // Finalize the PDF
 |  | ||||||
|       doc.end(); |  | ||||||
|        |  | ||||||
|       // Wait for the file to be written
 |  | ||||||
|       await new Promise<void>((resolve, reject) => { |       await new Promise<void>((resolve, reject) => { | ||||||
|         writeStream.on('finish', () => resolve()); |         pdfDoc.getBuffer((buffer) => { | ||||||
|         writeStream.on('error', reject); |           fs.writeFile(tempFilePath, buffer, (err) => { | ||||||
|  |             if (err) reject(err); | ||||||
|  |             else resolve(); | ||||||
|  |           }); | ||||||
|  |         }); | ||||||
|       }); |       }); | ||||||
|        |        | ||||||
|       // Upload to Firebase Storage using admin SDK
 |  | ||||||
|       const invoicePath = `invoices/${data.invoiceNumber}.pdf`; |       const invoicePath = `invoices/${data.invoiceNumber}.pdf`; | ||||||
|       const bucket = admin.storage().bucket(); |       const bucket = admin.storage().bucket(); | ||||||
|       await bucket.upload(tempFilePath, { |       await bucket.upload(tempFilePath, { | ||||||
| @ -145,10 +272,8 @@ export class InvoiceService { | |||||||
|         }, |         }, | ||||||
|       }); |       }); | ||||||
|        |        | ||||||
|       // Clean up the temporary file
 |  | ||||||
|       fs.unlinkSync(tempFilePath); |       fs.unlinkSync(tempFilePath); | ||||||
|        |        | ||||||
|       // Return the storage path
 |  | ||||||
|       return invoicePath; |       return invoicePath; | ||||||
|     } catch (error: any) { |     } catch (error: any) { | ||||||
|       logger.error('Error generating invoice:', error); |       logger.error('Error generating invoice:', error); | ||||||
| @ -158,11 +283,10 @@ export class InvoiceService { | |||||||
| 
 | 
 | ||||||
|   async getInvoiceDownloadUrl(invoicePath: string): Promise<string> { |   async getInvoiceDownloadUrl(invoicePath: string): Promise<string> { | ||||||
|     try { |     try { | ||||||
|       // Using admin SDK to generate a signed URL
 |  | ||||||
|       const bucket = admin.storage().bucket(); |       const bucket = admin.storage().bucket(); | ||||||
|       const file = bucket.file(invoicePath); |       const file = bucket.file(invoicePath); | ||||||
|        |        | ||||||
|       const expirationMs = 7 * 24 * 60 * 60 * 1000; // 7 days
 |       const expirationMs = 7 * 24 * 60 * 60 * 1000;  | ||||||
|        |        | ||||||
|       const [signedUrl] = await file.getSignedUrl({ |       const [signedUrl] = await file.getSignedUrl({ | ||||||
|         action: 'read', |         action: 'read', | ||||||
| @ -193,7 +317,6 @@ export class InvoiceService { | |||||||
|       const data = docSnapshot.data(); |       const data = docSnapshot.data(); | ||||||
|       const paymentsData = data?.payments || []; |       const paymentsData = data?.payments || []; | ||||||
|        |        | ||||||
|       // Find the payment by referenceNumber or transactionId
 |  | ||||||
|       let found = false; |       let found = false; | ||||||
|       for (let i = 0; i < paymentsData.length; i++) { |       for (let i = 0; i < paymentsData.length; i++) { | ||||||
|         if (paymentsData[i].referenceNumber === paymentId ||  |         if (paymentsData[i].referenceNumber === paymentId ||  | ||||||
| @ -224,15 +347,12 @@ export class InvoiceService { | |||||||
| 
 | 
 | ||||||
|   async sendInvoiceEmail(invoicePath: string, emailOptions: EmailOptions): Promise<boolean> { |   async sendInvoiceEmail(invoicePath: string, emailOptions: EmailOptions): Promise<boolean> { | ||||||
|     try { |     try { | ||||||
|       // Get the download URL for the invoice
 |  | ||||||
|       const downloadUrl = await this.getInvoiceDownloadUrl(invoicePath); |       const downloadUrl = await this.getInvoiceDownloadUrl(invoicePath); | ||||||
|        |        | ||||||
|       // Format the date
 |  | ||||||
|       const formattedDate = emailOptions.additionalData?.paymentDate  |       const formattedDate = emailOptions.additionalData?.paymentDate  | ||||||
|         ? new Date(emailOptions.additionalData.paymentDate).toLocaleDateString('en-GB') |         ? new Date(emailOptions.additionalData.paymentDate).toLocaleDateString('en-GB') | ||||||
|         : new Date().toLocaleDateString('en-GB'); |         : new Date().toLocaleDateString('en-GB'); | ||||||
|        |        | ||||||
|       // Create email HTML content if not provided
 |  | ||||||
|       const emailHtml = emailOptions.customHtml || ` |       const emailHtml = emailOptions.customHtml || ` | ||||||
|         <html> |         <html> | ||||||
|           <body> |           <body> | ||||||
| @ -255,7 +375,6 @@ export class InvoiceService { | |||||||
|         </html> |         </html> | ||||||
|       `;
 |       `;
 | ||||||
|        |        | ||||||
|       // Send the email with attachment
 |  | ||||||
|       await sendEmailWithAttachmentUtil( |       await sendEmailWithAttachmentUtil( | ||||||
|         emailOptions.recipientEmail, |         emailOptions.recipientEmail, | ||||||
|         emailOptions.subject || 'Your Fitlien Membership Invoice', |         emailOptions.subject || 'Your Fitlien Membership Invoice', | ||||||
| @ -285,10 +404,8 @@ export class InvoiceService { | |||||||
|     error?: string; |     error?: string; | ||||||
|   }> { |   }> { | ||||||
|     try { |     try { | ||||||
|       // Generate the invoice
 |  | ||||||
|       const invoicePath = await this.generateInvoice(invoiceData); |       const invoicePath = await this.generateInvoice(invoiceData); | ||||||
|        |        | ||||||
|       // Update the payment record with the invoice path
 |  | ||||||
|       const updateSuccess = await this.updateInvoicePath(membershipId, paymentId, invoicePath); |       const updateSuccess = await this.updateInvoicePath(membershipId, paymentId, invoicePath); | ||||||
|        |        | ||||||
|       if (!updateSuccess) { |       if (!updateSuccess) { | ||||||
| @ -300,10 +417,8 @@ export class InvoiceService { | |||||||
|         }; |         }; | ||||||
|       } |       } | ||||||
|        |        | ||||||
|       // Get a download URL for the invoice
 |  | ||||||
|       const downloadUrl = await this.getInvoiceDownloadUrl(invoicePath); |       const downloadUrl = await this.getInvoiceDownloadUrl(invoicePath); | ||||||
|        |        | ||||||
|       // Send email if email options are provided
 |  | ||||||
|       let emailSent = false; |       let emailSent = false; | ||||||
|       if (emailOptions && emailOptions.recipientEmail) { |       if (emailOptions && emailOptions.recipientEmail) { | ||||||
|         emailSent = await this.sendInvoiceEmail(invoicePath, emailOptions); |         emailSent = await this.sendInvoiceEmail(invoicePath, emailOptions); | ||||||
|  | |||||||
| @ -142,7 +142,6 @@ export const phonePeWebhook = onRequest({ | |||||||
|                   let gymAddress = ''; |                   let gymAddress = ''; | ||||||
|                   let subscriptionName = ''; |                   let subscriptionName = ''; | ||||||
|                   let gymOwnerEmail = ''; |                   let gymOwnerEmail = ''; | ||||||
|                   let gymPhoneNumber = ''; |  | ||||||
|                   let paymentType = orderData.metaInfo?.paymentType || 'Gym Membership'; |                   let paymentType = orderData.metaInfo?.paymentType || 'Gym Membership'; | ||||||
|                   let trainerId = orderData.metaInfo?.trainerId; |                   let trainerId = orderData.metaInfo?.trainerId; | ||||||
|                   let trainerData = null; |                   let trainerData = null; | ||||||
| @ -172,7 +171,6 @@ export const phonePeWebhook = onRequest({ | |||||||
|                       gymName = gymData?.name || 'Fitlien'; |                       gymName = gymData?.name || 'Fitlien'; | ||||||
|                       gymAddress = gymData?.address || ''; |                       gymAddress = gymData?.address || ''; | ||||||
|                       subscriptionName = gymData?.subscriptions?.name || ''; |                       subscriptionName = gymData?.subscriptions?.name || ''; | ||||||
|                       gymPhoneNumber = gymData?.phoneNumber || ''; |  | ||||||
| 
 | 
 | ||||||
|                       if (gymData?.userId) { |                       if (gymData?.userId) { | ||||||
|                         const gymOwnerDoc = await admin.firestore() |                         const gymOwnerDoc = await admin.firestore() | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user