phonepe #35

Merged
allentj merged 19 commits from phonepe into dev 2025-05-20 10:10:58 +00:00
5 changed files with 359 additions and 215 deletions

View File

@ -7,6 +7,7 @@
{ {
"source": "functions", "source": "functions",
"codebase": "default", "codebase": "default",
"timeoutSeconds": 300,
"ignore": [ "ignore": [
"node_modules", "node_modules",
".git", ".git",

View File

@ -18,6 +18,8 @@
"form-data": "^4.0.1", "form-data": "^4.0.1",
"functions": "file:", "functions": "file:",
"html-to-text": "^9.0.5", "html-to-text": "^9.0.5",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"long": "^5.3.2", "long": "^5.3.2",
"mailgun.js": "^10.4.0", "mailgun.js": "^10.4.0",
"node-fetch": "^2.7.0", "node-fetch": "^2.7.0",
@ -1101,6 +1103,14 @@
"@babel/core": "^7.0.0-0" "@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": { "node_modules/@babel/template": {
"version": "7.27.0", "version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", "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", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" "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": { "node_modules/@types/range-parser": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
@ -2924,6 +2940,12 @@
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"optional": true "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": { "node_modules/@types/yargs": {
"version": "17.0.33", "version": "17.0.33",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "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", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "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": { "node_modules/available-typed-arrays": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "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", "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" "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": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@ -3379,6 +3421,17 @@
"node-int64": "^0.4.0" "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": { "node_modules/buffer": {
"version": "4.9.2", "version": "4.9.2",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
@ -3494,6 +3547,25 @@
], ],
"peer": true "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": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "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", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" "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": { "node_modules/cors": {
"version": "2.8.5", "version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "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", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" "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": { "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",
@ -3888,6 +3980,15 @@
"url": "https://github.com/fb55/domhandler?sponsor=1" "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": { "node_modules/domutils": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
@ -4269,6 +4370,11 @@
"bser": "2.1.1" "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": { "node_modules/fill-range": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -4833,6 +4939,19 @@
"node": ">=14" "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": { "node_modules/htmlparser2": {
"version": "8.0.2", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
@ -6047,6 +6166,31 @@
"node": ">=10" "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": { "node_modules/jwa": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
@ -6765,6 +6909,12 @@
"url": "https://ko-fi.com/killymxi" "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": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -6960,6 +7110,15 @@
"node": ">=0.4.x" "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": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@ -7003,6 +7162,12 @@
"node": ">= 6" "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": { "node_modules/regexp.prototype.flags": {
"version": "1.5.4", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@ -7108,6 +7273,15 @@
"node": ">=14" "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": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -7431,6 +7605,15 @@
"node": ">=10" "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": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@ -7579,6 +7762,15 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/teeny-request": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
@ -7671,6 +7863,15 @@
"node": ">=8" "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": { "node_modules/tiny-inflate": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
@ -7931,6 +8132,15 @@
"node": ">= 0.4.0" "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": { "node_modules/uuid": {
"version": "10.0.0", "version": "10.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",

View File

@ -25,6 +25,8 @@
"form-data": "^4.0.1", "form-data": "^4.0.1",
"functions": "file:", "functions": "file:",
"html-to-text": "^9.0.5", "html-to-text": "^9.0.5",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"long": "^5.3.2", "long": "^5.3.2",
"mailgun.js": "^10.4.0", "mailgun.js": "^10.4.0",
"node-fetch": "^2.7.0", "node-fetch": "^2.7.0",

View File

@ -4,12 +4,8 @@ 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 { jsPDF } from "jspdf";
const pdfMake = require('pdfmake/build/pdfmake'); import 'jspdf-autotable';
const pdfFonts = require('pdfmake/build/vfs_fonts');
pdfMake.vfs = pdfFonts.pdfMake ? pdfFonts.pdfMake.vfs : pdfFonts.vfs;
const admin = getAdmin(); const admin = getAdmin();
const logger = getLogger(); const logger = getLogger();
@ -54,216 +50,96 @@ export class InvoiceService {
const cgst = hasGst ? baseAmount * 0.09 : 0; const cgst = hasGst ? baseAmount * 0.09 : 0;
const formattedDate = format(data.paymentDate, 'dd/MM/yyyy'); const formattedDate = format(data.paymentDate, 'dd/MM/yyyy');
const doc = new jsPDF();
const docDefinition: any = { doc.setFontSize(20);
content: [ doc.setFont('helvetica', 'bold');
{ doc.text(data.businessName, 20, 20);
columns: [
[ doc.setFontSize(12);
{ text: data.businessName, style: 'businessName' }, doc.setFont('helvetica', 'normal');
{ text: data.address, style: 'businessAddress' }, doc.text(data.address, 20, 30);
hasGst ? { text: `GSTIN: ${data.gstNumber}`, style: 'businessDetails' } : {}
], if (hasGst) {
[ doc.text(`GSTIN: ${data.gstNumber}`, 20, 40);
{ text: 'RECEIPT', style: 'invoiceTitle', alignment: 'right' }, }
{ text: `Receipt #: ${data.invoiceNumber}`, style: 'invoiceDetails', alignment: 'right' },
{ text: `Date: ${formattedDate}`, style: 'invoiceDetails', alignment: 'right' } doc.setFontSize(24);
] doc.setFont('helvetica', 'bold');
] doc.text('RECEIPT', 190, 20, { align: 'right' });
},
{ canvas: [{ type: 'line', x1: 0, y1: 5, x2: 515, y2: 5, lineWidth: 0.5 }] }, doc.setFontSize(12);
{ text: '', margin: [0, 10] }, doc.text(`Receipt #: ${data.invoiceNumber}`, 190, 30, { align: 'right' });
doc.text(`Date: ${formattedDate}`, 190, 40, { align: 'right' });
{
style: 'customerBox', doc.line(20, 45, 190, 45);
table: {
widths: ['*'], doc.setFontSize(12);
body: [ doc.setFont('helvetica', 'bold');
[ doc.text('Receipt To:', 20, 60);
{
stack: [ doc.setFont('helvetica', 'normal');
{ text: 'Receipt To:', style: 'customerTitle' }, doc.text(data.customerName, 20, 70);
{ text: data.customerName, style: 'customerDetails' }, doc.text(`Phone: ${data.phoneNumber}`, 20, 80);
{ text: `Phone: ${data.phoneNumber}`, style: 'customerDetails' }, doc.text(`Email: ${data.email}`, 20, 90);
{ text: `Email: ${data.email}`, style: 'customerDetails' }
], (doc as any).autoTable({
margin: [10, 10] startY: 110,
} head: [['No.', 'Description', 'HSN/SAC', 'Amount (INR)']],
] body: [
] ['1', `${data.planName} Subscription`, '999723', baseAmount.toFixed(2)]
},
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' }
], ],
styles: { headStyles: { fillColor: [220, 220, 220], textColor: [0, 0, 0], fontStyle: 'bold' },
businessName: { styles: { halign: 'center' },
fontSize: 20, columnStyles: {
bold: true, 0: { halign: 'center', cellWidth: 20 },
margin: [0, 0, 0, 5] 1: { halign: 'left' },
}, 2: { halign: 'center', cellWidth: 40 },
businessAddress: { 3: { halign: 'right', cellWidth: 40 }
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'
} }
};
const pdfDoc = pdfMake.createPdf(docDefinition);
await new Promise<void>((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 invoicePath = `invoices/${data.invoiceNumber}.pdf`;
const bucket = admin.storage().bucket(); const bucket = admin.storage().bucket();
await bucket.upload(tempFilePath, { await bucket.upload(tempFilePath, {

View File

@ -21,7 +21,7 @@ export const phonePeWebhook = onRequest({
body: request.body, body: request.body,
method: request.method method: request.method
}); });
const authHeader = request.headers['authorization'] as string; const authHeader = request.headers['authorization'] as string;
const username = process.env.PHONEPE_WEBHOOK_USERNAME; const username = process.env.PHONEPE_WEBHOOK_USERNAME;
const password = process.env.PHONEPE_WEBHOOK_PASSWORD; const password = process.env.PHONEPE_WEBHOOK_PASSWORD;
@ -37,7 +37,7 @@ export const phonePeWebhook = onRequest({
.createHash('sha256') .createHash('sha256')
.update(credentialString) .update(credentialString)
.digest('hex'); .digest('hex');
const receivedAuth = authHeader.replace(/^SHA256\s+/i, ''); const receivedAuth = authHeader.replace(/^SHA256\s+/i, '');
if (receivedAuth.toLowerCase() !== expectedAuth.toLowerCase()) { if (receivedAuth.toLowerCase() !== expectedAuth.toLowerCase()) {
@ -107,34 +107,53 @@ export const phonePeWebhook = onRequest({
if (payload.state === 'COMPLETED') { if (payload.state === 'COMPLETED') {
try { try {
logger.info(`Starting payment update process for merchantOrderId: ${payload.merchantOrderId}`);
const paymentUpdateSuccess = await updatePaymentDataAfterSuccess( const paymentUpdateSuccess = await updatePaymentDataAfterSuccess(
payload.merchantOrderId, payload.merchantOrderId,
payload.orderId, payload.orderId,
payload payload
); );
logger.info(`Payment update result for merchantOrderId: ${payload.merchantOrderId}`, {
success: paymentUpdateSuccess,
orderId: payload.orderId
});
if (paymentUpdateSuccess) { if (paymentUpdateSuccess) {
const orderData = orderDoc.data(); const orderData = orderDoc.data();
const membershipId = orderData.metaInfo?.membershipId; const membershipId = orderData.metaInfo?.membershipId;
logger.info(`Processing invoice for completed payment`, {
merchantOrderId: payload.merchantOrderId,
orderId: payload.orderId,
membershipId: membershipId || 'not-provided'
});
if (membershipId) { if (membershipId) {
try { try {
logger.info(`Fetching membership data for membershipId: ${membershipId}`);
const membershipDoc = await admin.firestore() const membershipDoc = await admin.firestore()
.collection('memberships') .collection('memberships')
.doc(membershipId) .doc(membershipId)
.get(); .get();
if (membershipDoc.exists) { if (membershipDoc.exists) {
logger.info(`Membership data retrieved successfully for membershipId: ${membershipId}`);
const membershipData = membershipDoc.data(); const membershipData = membershipDoc.data();
const userId = membershipData?.userId; const userId = membershipData?.userId;
logger.info(`Fetching user data for userId: ${userId}`);
const userDoc = await admin.firestore() const userDoc = await admin.firestore()
.collection('users') .collection('users')
.doc(userId) .doc(userId)
.get(); .get();
if (userDoc.exists) { if (userDoc.exists) {
logger.info(`User data retrieved successfully for userId: ${userId}`);
logger.info(`Starting invoice generation process for payment: ${payload.merchantOrderId}`);
const userData = userDoc.data(); const userData = userDoc.data();
const gymId = orderData.metaInfo?.gymId || membershipData?.gymId; const gymId = orderData.metaInfo?.gymId || membershipData?.gymId;
@ -202,6 +221,14 @@ export const phonePeWebhook = onRequest({
} }
const invoiceNumber = `INV-${payload.merchantOrderId.substring(0, 8)}`; const invoiceNumber = `INV-${payload.merchantOrderId.substring(0, 8)}`;
logger.info(`Generated invoice number: ${invoiceNumber}`);
logger.info(`Preparing invoice data for generation`, {
invoiceNumber,
merchantOrderId: payload.merchantOrderId,
gymName: gymName
});
const invoiceData = { const invoiceData = {
invoiceNumber, invoiceNumber,
businessName: gymName, businessName: gymName,
@ -218,6 +245,12 @@ export const phonePeWebhook = onRequest({
}; };
const invoicePath = await invoiceService.generateInvoice(invoiceData); const invoicePath = await invoiceService.generateInvoice(invoiceData);
logger.info(`Invoice generated successfully at path: ${invoicePath}`);
logger.info(`Updating membership payment with invoice path`, {
membershipId,
invoicePath
});
await admin.firestore() await admin.firestore()
.collection('membership_payments') .collection('membership_payments')
@ -225,29 +258,50 @@ export const phonePeWebhook = onRequest({
.get() .get()
.then(async (doc) => { .then(async (doc) => {
if (doc.exists) { if (doc.exists) {
logger.info(`Found membership payment document for membershipId: ${membershipId}`);
const paymentsData = doc.data()?.payments || []; const paymentsData = doc.data()?.payments || [];
let paymentFound = false;
for (let i = 0; i < paymentsData.length; i++) { for (let i = 0; i < paymentsData.length; i++) {
if (paymentsData[i].referenceNumber === payload.merchantOrderId || if (paymentsData[i].referenceNumber === payload.merchantOrderId ||
paymentsData[i].transactionId === payload.orderId) { paymentsData[i].transactionId === payload.orderId) {
paymentsData[i].invoicePath = invoicePath; paymentsData[i].invoicePath = invoicePath;
paymentFound = true;
break; break;
} }
} }
logger.info(`Payment record ${paymentFound ? 'found' : 'not found'} in membership payments`, {
membershipId,
merchantOrderId: payload.merchantOrderId,
orderId: payload.orderId
});
await doc.ref.update({ await doc.ref.update({
'payments': paymentsData, 'payments': paymentsData,
'updatedAt': admin.firestore.FieldValue.serverTimestamp(), 'updatedAt': admin.firestore.FieldValue.serverTimestamp(),
}); });
logger.info(`Successfully updated membership payment with invoice path`, {
membershipId,
invoicePath
});
} else {
logger.warn(`No membership payment document found for membershipId: ${membershipId}`);
} }
}); });
logger.info(`Generated invoice for payment: ${payload.merchantOrderId}, path: ${invoicePath}`); logger.info(`Generated invoice for payment: ${payload.merchantOrderId}, path: ${invoicePath}`);
logger.info(`Getting download URL for invoice: ${invoicePath}`);
const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath); const downloadUrl = await invoiceService.getInvoiceDownloadUrl(invoicePath);
logger.info(`Generated download URL for invoice: ${invoicePath}`);
const formattedDate = format(new Date(), 'dd/MM/yyyy'); const formattedDate = format(new Date(), 'dd/MM/yyyy');
if (membershipData?.fields?.['email']) { if (membershipData?.fields?.['email']) {
logger.info(`Preparing to send invoice email to customer: ${membershipData?.fields?.['email']}`);
try { try {
const emailSubject = isFreeplan const emailSubject = isFreeplan
? `Free Plan Assigned - ${gymName}` ? `Free Plan Assigned - ${gymName}`
@ -294,6 +348,7 @@ export const phonePeWebhook = onRequest({
} }
if (gymOwnerEmail) { if (gymOwnerEmail) {
logger.info(`Preparing to send invoice email to gym owner: ${gymOwnerEmail}`);
try { try {
const ownerEmailSubject = isFreeplan const ownerEmailSubject = isFreeplan
? `Free Plan Assigned${paymentType === 'Gym Membership with Personal Training' ? ' with Personal Training' : ''} - ${gymName}` ? `Free Plan Assigned${paymentType === 'Gym Membership with Personal Training' ? ' with Personal Training' : ''} - ${gymName}`