phonepe (#23)
All checks were successful
Deploy FitLien services to Dev / Deploy to Dev (push) Successful in 4m15s

Co-authored-by: AllenTJ7 <163137620+AllenTJ7@users.noreply.github.com>
Reviewed-on: #23
Co-authored-by: Allen T J <allentj@cosq.net>
Co-committed-by: Allen T J <allentj@cosq.net>
This commit is contained in:
Allen T J 2025-05-05 14:09:08 +00:00 committed by Benoy Bose
parent 72ad257e07
commit 9efa31b6cc
12 changed files with 518 additions and 132 deletions

View File

@ -22,17 +22,17 @@
},
"emulators": {
"functions": {
"port": 5001
"port": 5005
},
"firestore": {
"port": 8079
"port": 8085
},
"storage": {
"port": 9199
},
"ui": {
"enabled": true,
"port": 4000
"port": 4008
},
"auth": {
"port": 9099

View File

@ -11,6 +11,11 @@ GOOGLE_MAPS_API_KEY=#{GOOGLE_MAPS_API_KEY}#
CASHFREE_URL=#{CASHFREE_URL}#
CASHFREE_LINK_URL=#{CASHFREE_LINK_URL}#
CASHFREE_LINK_NOTIFY_URL=#{CASHFREE_LINK_NOTIFY_URL}#
PHONEPE_CLIENT_ID=#{PHONEPE_CLIENT_ID}#
PHONEPE_CLIENT_SECRET=#{PHONEPE_CLIENT_SECRET}#
PHONEPE_API_URL=#{PHONEPE_API_URL}#
PHONEPE_WEBHOOK_USERNAME=#{PHONEPE_WEBHOOK_USERNAME}#
PHONEPE_WEBHOOK_PASSWORD=#{PHONEPE_WEBHOOK_PASSWORD}#
SES_FROM_EMAIL=#{SES_FROM_EMAIL}#
SES_REPLY_TO_EMAIL=#{SES_REPLY_TO_EMAIL}#

View File

@ -8,7 +8,7 @@
"name": "functions",
"version": "0.0.0",
"dependencies": {
"@aws-sdk/client-ses": "^3.796.0",
"@aws-sdk/client-ses": "^3.798.0",
"@types/node-fetch": "^2.6.12",
"aws-sdk": "^2.1692.0",
"axios": "^1.8.4",
@ -165,44 +165,44 @@
}
},
"node_modules/@aws-sdk/client-ses": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.796.0.tgz",
"integrity": "sha512-gg1z0uXtm6v3wCMrGM5eCh69aWT+VOzYvNdbSMA5EPLfW9qsOCH1Zz/q9XzUZjWN25OnGvfm1LzypzQgRypSeQ==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.798.0.tgz",
"integrity": "sha512-s12ntssTgau7ySHoOXVAliVM1a1JyjsFYwauKh/7gCriJ+lIBlo25BFgCjZAHxxBn2FfCete6BVQFEbN/Ishkg==",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.796.0",
"@aws-sdk/credential-provider-node": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/credential-provider-node": "3.798.0",
"@aws-sdk/middleware-host-header": "3.775.0",
"@aws-sdk/middleware-logger": "3.775.0",
"@aws-sdk/middleware-recursion-detection": "3.775.0",
"@aws-sdk/middleware-user-agent": "3.796.0",
"@aws-sdk/middleware-user-agent": "3.798.0",
"@aws-sdk/region-config-resolver": "3.775.0",
"@aws-sdk/types": "3.775.0",
"@aws-sdk/util-endpoints": "3.787.0",
"@aws-sdk/util-user-agent-browser": "3.775.0",
"@aws-sdk/util-user-agent-node": "3.796.0",
"@aws-sdk/util-user-agent-node": "3.798.0",
"@smithy/config-resolver": "^4.1.0",
"@smithy/core": "^3.2.0",
"@smithy/core": "^3.3.0",
"@smithy/fetch-http-handler": "^5.0.2",
"@smithy/hash-node": "^4.0.2",
"@smithy/invalid-dependency": "^4.0.2",
"@smithy/middleware-content-length": "^4.0.2",
"@smithy/middleware-endpoint": "^4.1.0",
"@smithy/middleware-retry": "^4.1.0",
"@smithy/middleware-endpoint": "^4.1.1",
"@smithy/middleware-retry": "^4.1.1",
"@smithy/middleware-serde": "^4.0.3",
"@smithy/middleware-stack": "^4.0.2",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/node-http-handler": "^4.0.4",
"@smithy/protocol-http": "^5.1.0",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"@smithy/url-parser": "^4.0.2",
"@smithy/util-base64": "^4.0.0",
"@smithy/util-body-length-browser": "^4.0.0",
"@smithy/util-body-length-node": "^4.0.0",
"@smithy/util-defaults-mode-browser": "^4.0.8",
"@smithy/util-defaults-mode-node": "^4.0.8",
"@smithy/util-defaults-mode-browser": "^4.0.9",
"@smithy/util-defaults-mode-node": "^4.0.9",
"@smithy/util-endpoints": "^3.0.2",
"@smithy/util-middleware": "^4.0.2",
"@smithy/util-retry": "^4.0.2",
@ -215,43 +215,43 @@
}
},
"node_modules/@aws-sdk/client-sso": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.796.0.tgz",
"integrity": "sha512-EJExg8mbwqP0VG+RNFV4ZPuUo7QsDsUfTnuFQY51V8iXrbOdV+PDLRr4psXj2fxvrLxc9AlGUMNqd/j4VZtQzA==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.798.0.tgz",
"integrity": "sha512-Si4W7kFflNXC48lr05n2Fc5nrD6whbfgR7c5/7hYSXP52DOqy2kMle+bZx5EkmQ/e/5nAPW0DS4ABeLprVSghw==",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/middleware-host-header": "3.775.0",
"@aws-sdk/middleware-logger": "3.775.0",
"@aws-sdk/middleware-recursion-detection": "3.775.0",
"@aws-sdk/middleware-user-agent": "3.796.0",
"@aws-sdk/middleware-user-agent": "3.798.0",
"@aws-sdk/region-config-resolver": "3.775.0",
"@aws-sdk/types": "3.775.0",
"@aws-sdk/util-endpoints": "3.787.0",
"@aws-sdk/util-user-agent-browser": "3.775.0",
"@aws-sdk/util-user-agent-node": "3.796.0",
"@aws-sdk/util-user-agent-node": "3.798.0",
"@smithy/config-resolver": "^4.1.0",
"@smithy/core": "^3.2.0",
"@smithy/core": "^3.3.0",
"@smithy/fetch-http-handler": "^5.0.2",
"@smithy/hash-node": "^4.0.2",
"@smithy/invalid-dependency": "^4.0.2",
"@smithy/middleware-content-length": "^4.0.2",
"@smithy/middleware-endpoint": "^4.1.0",
"@smithy/middleware-retry": "^4.1.0",
"@smithy/middleware-endpoint": "^4.1.1",
"@smithy/middleware-retry": "^4.1.1",
"@smithy/middleware-serde": "^4.0.3",
"@smithy/middleware-stack": "^4.0.2",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/node-http-handler": "^4.0.4",
"@smithy/protocol-http": "^5.1.0",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"@smithy/url-parser": "^4.0.2",
"@smithy/util-base64": "^4.0.0",
"@smithy/util-body-length-browser": "^4.0.0",
"@smithy/util-body-length-node": "^4.0.0",
"@smithy/util-defaults-mode-browser": "^4.0.8",
"@smithy/util-defaults-mode-node": "^4.0.8",
"@smithy/util-defaults-mode-browser": "^4.0.9",
"@smithy/util-defaults-mode-node": "^4.0.9",
"@smithy/util-endpoints": "^3.0.2",
"@smithy/util-middleware": "^4.0.2",
"@smithy/util-retry": "^4.0.2",
@ -263,17 +263,17 @@
}
},
"node_modules/@aws-sdk/core": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.796.0.tgz",
"integrity": "sha512-tH8Sp7lCxISVoLnkyv4AouuXs2CDlMhTuesWa0lq2NX1f+DXsMwSBtN37ttZdpFMw3F8mWdsJt27X9h2Oq868A==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.798.0.tgz",
"integrity": "sha512-hITxDE4pVkeJqz0LXjQRDgR+noxJ5oOxG38fgmQXjPXsdwVKnNIiMJ5S2WFMVSszU7ebGSyHdPHENQKu6TReVA==",
"dependencies": {
"@aws-sdk/types": "3.775.0",
"@smithy/core": "^3.2.0",
"@smithy/core": "^3.3.0",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/property-provider": "^4.0.2",
"@smithy/protocol-http": "^5.1.0",
"@smithy/signature-v4": "^5.1.0",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"@smithy/util-middleware": "^4.0.2",
"fast-xml-parser": "4.4.1",
@ -305,11 +305,11 @@
}
},
"node_modules/@aws-sdk/credential-provider-env": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.796.0.tgz",
"integrity": "sha512-kQzGKm4IOYYO6vUrai2JocNwhJm4Aml2BsAV+tBhFhhkutE7khf9PUucoVjB78b0J48nF+kdSacqzY+gB81/Uw==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.798.0.tgz",
"integrity": "sha512-EsfzTEeoaHY1E+g3S6AmC3bF6euZN5SrLcLh5Oxhx5q2qjWUsKEK0fwek+jlt2GH7zB3F9IArV4z+8CsDQdKYw==",
"dependencies": {
"@aws-sdk/core": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/property-provider": "^4.0.2",
"@smithy/types": "^4.2.0",
@ -320,17 +320,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-http": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.796.0.tgz",
"integrity": "sha512-wWOT6VAHIKOuHdKFGm1iyKvx7f6+Kc/YTzFWJPuT+l+CPlXR6ylP1UMIDsHHLKpMzsrh3CH77QDsjkhQrnKkfg==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.798.0.tgz",
"integrity": "sha512-bw5TmcJqpBVQlXzkL63545iHQ9mxwQeXTS/rgUQ5rmNNS3yiGDekVZOLXo/Gs4wmt2/59UN/sWIRFxvxDpMQEg==",
"dependencies": {
"@aws-sdk/core": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/fetch-http-handler": "^5.0.2",
"@smithy/node-http-handler": "^4.0.4",
"@smithy/property-provider": "^4.0.2",
"@smithy/protocol-http": "^5.1.0",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"@smithy/util-stream": "^4.2.0",
"tslib": "^2.6.2"
@ -340,17 +340,17 @@
}
},
"node_modules/@aws-sdk/credential-provider-ini": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.796.0.tgz",
"integrity": "sha512-qGWBDn9aO8avFfYU7daps7Sy6OglF1x0q0w48slt0KMXbHd2/LvKVIiYwyofYCXed0yzcEOF2IYm9FjXdcn+ug==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.798.0.tgz",
"integrity": "sha512-zqWwKhhdf5CVRL6+4vNNTZVHWH9OiiwUWA3ka44jJaAMBRbbryjRedzwkWbgDaL1EbfTbcBZTYzE7N/vK7UUVA==",
"dependencies": {
"@aws-sdk/core": "3.796.0",
"@aws-sdk/credential-provider-env": "3.796.0",
"@aws-sdk/credential-provider-http": "3.796.0",
"@aws-sdk/credential-provider-process": "3.796.0",
"@aws-sdk/credential-provider-sso": "3.796.0",
"@aws-sdk/credential-provider-web-identity": "3.796.0",
"@aws-sdk/nested-clients": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/credential-provider-env": "3.798.0",
"@aws-sdk/credential-provider-http": "3.798.0",
"@aws-sdk/credential-provider-process": "3.798.0",
"@aws-sdk/credential-provider-sso": "3.798.0",
"@aws-sdk/credential-provider-web-identity": "3.798.0",
"@aws-sdk/nested-clients": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/credential-provider-imds": "^4.0.2",
"@smithy/property-provider": "^4.0.2",
@ -363,16 +363,16 @@
}
},
"node_modules/@aws-sdk/credential-provider-node": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.796.0.tgz",
"integrity": "sha512-WeNK7OWPrsOvhO3DAgpUO0FtmVghMaZ/IpPJHJ4Y0nBIsWOBXLrbZ2Y1mdT8N2bGGUaM91tJaV8Yf8COc3gvmA==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.798.0.tgz",
"integrity": "sha512-Mrhl4wS4lMpuw2NCga5/rtQehNfyRs8NUHfvrLK5bZvJbjanrh8QtdRVhrAjw71OwFh3GK49QMByGkUssALJ+g==",
"dependencies": {
"@aws-sdk/credential-provider-env": "3.796.0",
"@aws-sdk/credential-provider-http": "3.796.0",
"@aws-sdk/credential-provider-ini": "3.796.0",
"@aws-sdk/credential-provider-process": "3.796.0",
"@aws-sdk/credential-provider-sso": "3.796.0",
"@aws-sdk/credential-provider-web-identity": "3.796.0",
"@aws-sdk/credential-provider-env": "3.798.0",
"@aws-sdk/credential-provider-http": "3.798.0",
"@aws-sdk/credential-provider-ini": "3.798.0",
"@aws-sdk/credential-provider-process": "3.798.0",
"@aws-sdk/credential-provider-sso": "3.798.0",
"@aws-sdk/credential-provider-web-identity": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/credential-provider-imds": "^4.0.2",
"@smithy/property-provider": "^4.0.2",
@ -385,11 +385,11 @@
}
},
"node_modules/@aws-sdk/credential-provider-process": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.796.0.tgz",
"integrity": "sha512-r4e8/4AdKn/qQbRVocW7oXkpoiuXdTv0qty8AASNLnbQnT1vjD1bvmP6kp4fbHPWgwY8I9h0Dqjp49uy9Bqyuw==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.798.0.tgz",
"integrity": "sha512-BbRq8bhCHC94OTRIg5edgGTaWUzBH0h/IZJZ0vERle8A9nfl+5jUplvC8cvh3/8cNgHIRXj5HzlDjeSVe9dySg==",
"dependencies": {
"@aws-sdk/core": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/property-provider": "^4.0.2",
"@smithy/shared-ini-file-loader": "^4.0.2",
@ -401,13 +401,13 @@
}
},
"node_modules/@aws-sdk/credential-provider-sso": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.796.0.tgz",
"integrity": "sha512-RUYsQ1t6UdzkpZ7pocUt1l/9l9GCYCaopIhv0DU6CipA8rkWtoweKsLHKdv+8wE4p6gqDfDIHGam1ivswiCIzg==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.798.0.tgz",
"integrity": "sha512-MLpQRb7xkqI9w0slEA76QiHGzM0PDMcpVcQG0wFHrpLKkQYjYlD9H3VfxdYGUh+FPOaR1fFpRZb18Gz9MR/2eQ==",
"dependencies": {
"@aws-sdk/client-sso": "3.796.0",
"@aws-sdk/core": "3.796.0",
"@aws-sdk/token-providers": "3.796.0",
"@aws-sdk/client-sso": "3.798.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/token-providers": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/property-provider": "^4.0.2",
"@smithy/shared-ini-file-loader": "^4.0.2",
@ -419,12 +419,12 @@
}
},
"node_modules/@aws-sdk/credential-provider-web-identity": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.796.0.tgz",
"integrity": "sha512-dpmFJT4IyjT09vruvMu/rWQQjVreqdxAe8pLPpGhoeKyA1O6+PS73b+VNXKvD31rQT8e4g6dVpA6KMxNW63aag==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.798.0.tgz",
"integrity": "sha512-OWBDy/ZiC0pxLzp1Nhah5jxDZ/onLTjouIVGPyc9E8/KzUJxqQbR6fk43VqhpYdVp/S7yDDbaOpO072RRZJQrw==",
"dependencies": {
"@aws-sdk/core": "3.796.0",
"@aws-sdk/nested-clients": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/nested-clients": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/property-provider": "^4.0.2",
"@smithy/types": "^4.2.0",
@ -476,14 +476,14 @@
}
},
"node_modules/@aws-sdk/middleware-user-agent": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.796.0.tgz",
"integrity": "sha512-IeNg+3jNWT37J45opi5Jx89hGF0lOnZjiNwlMp3rKq7PlOqy8kWq5J1Gxk0W3tIkPpuf68CtBs/QFrRXWOjsZw==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.798.0.tgz",
"integrity": "sha512-nb3YvLokpu/2meKVH5hGVLNg+hz3IyFCESEJW+SpK7bW/SfaKpukGY1lqwqbf+edl+s20MRXeK/by1rvBChixQ==",
"dependencies": {
"@aws-sdk/core": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@aws-sdk/util-endpoints": "3.787.0",
"@smithy/core": "^3.2.0",
"@smithy/core": "^3.3.0",
"@smithy/protocol-http": "^5.1.0",
"@smithy/types": "^4.2.0",
"tslib": "^2.6.2"
@ -493,43 +493,43 @@
}
},
"node_modules/@aws-sdk/nested-clients": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.796.0.tgz",
"integrity": "sha512-jJ8a0ldWtXh/ice7nldUjTqja7KYlSYk1pwfIIvJLIqEn2SvQHK/pyCINTmmOmFAWXMKBQBeWUMxo1pPYNytzQ==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.798.0.tgz",
"integrity": "sha512-14iBJgg2Qqf74IeUY+z1nP5GIJIBZj8lv9mdpXrHlK8k+FcMXjpHg/B+JguSMhb2sbLeb5N0H8HLJGIRNALVWw==",
"dependencies": {
"@aws-crypto/sha256-browser": "5.2.0",
"@aws-crypto/sha256-js": "5.2.0",
"@aws-sdk/core": "3.796.0",
"@aws-sdk/core": "3.798.0",
"@aws-sdk/middleware-host-header": "3.775.0",
"@aws-sdk/middleware-logger": "3.775.0",
"@aws-sdk/middleware-recursion-detection": "3.775.0",
"@aws-sdk/middleware-user-agent": "3.796.0",
"@aws-sdk/middleware-user-agent": "3.798.0",
"@aws-sdk/region-config-resolver": "3.775.0",
"@aws-sdk/types": "3.775.0",
"@aws-sdk/util-endpoints": "3.787.0",
"@aws-sdk/util-user-agent-browser": "3.775.0",
"@aws-sdk/util-user-agent-node": "3.796.0",
"@aws-sdk/util-user-agent-node": "3.798.0",
"@smithy/config-resolver": "^4.1.0",
"@smithy/core": "^3.2.0",
"@smithy/core": "^3.3.0",
"@smithy/fetch-http-handler": "^5.0.2",
"@smithy/hash-node": "^4.0.2",
"@smithy/invalid-dependency": "^4.0.2",
"@smithy/middleware-content-length": "^4.0.2",
"@smithy/middleware-endpoint": "^4.1.0",
"@smithy/middleware-retry": "^4.1.0",
"@smithy/middleware-endpoint": "^4.1.1",
"@smithy/middleware-retry": "^4.1.1",
"@smithy/middleware-serde": "^4.0.3",
"@smithy/middleware-stack": "^4.0.2",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/node-http-handler": "^4.0.4",
"@smithy/protocol-http": "^5.1.0",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"@smithy/url-parser": "^4.0.2",
"@smithy/util-base64": "^4.0.0",
"@smithy/util-body-length-browser": "^4.0.0",
"@smithy/util-body-length-node": "^4.0.0",
"@smithy/util-defaults-mode-browser": "^4.0.8",
"@smithy/util-defaults-mode-node": "^4.0.8",
"@smithy/util-defaults-mode-browser": "^4.0.9",
"@smithy/util-defaults-mode-node": "^4.0.9",
"@smithy/util-endpoints": "^3.0.2",
"@smithy/util-middleware": "^4.0.2",
"@smithy/util-retry": "^4.0.2",
@ -557,11 +557,11 @@
}
},
"node_modules/@aws-sdk/token-providers": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.796.0.tgz",
"integrity": "sha512-Sxr/EqJBxOwLsXHv8C91N/Aao8Rgjn5bcpzplrTZ7wrfDrzqQfSCvjh7apCxdLYMKPBV+an75blCAd7JD4/bAg==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.798.0.tgz",
"integrity": "sha512-iYhNmHXfWLUwcMP9ldb/H+RMRLHZbBUWBgsoQqfb7sl6z24nH0qBJyL+oXHTCVBUYLP20CvUrVkcwlejDzyoRw==",
"dependencies": {
"@aws-sdk/nested-clients": "3.796.0",
"@aws-sdk/nested-clients": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/property-provider": "^4.0.2",
"@smithy/shared-ini-file-loader": "^4.0.2",
@ -621,11 +621,11 @@
}
},
"node_modules/@aws-sdk/util-user-agent-node": {
"version": "3.796.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.796.0.tgz",
"integrity": "sha512-9fQpNcHgVFitf1tbTT8V1xGRoRHSmOAWjrhevo6Tc0WoINMAKz+4JNqfVGWRE5Tmtpq0oHKo1RmvxXQQtJYciA==",
"version": "3.798.0",
"resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.798.0.tgz",
"integrity": "sha512-yncgNd2inI+y5kdfn2i0oBwgCxwdtcVShNNVQ+5b/nuC1Lgjgcb+hmHAeTFMge7vhDP2Md8I+ih6bPMpK79lQQ==",
"dependencies": {
"@aws-sdk/middleware-user-agent": "3.796.0",
"@aws-sdk/middleware-user-agent": "3.798.0",
"@aws-sdk/types": "3.775.0",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/types": "^4.2.0",
@ -2062,9 +2062,9 @@
}
},
"node_modules/@smithy/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.2.0.tgz",
"integrity": "sha512-k17bgQhVZ7YmUvA8at4af1TDpl0NDMBuBKJl8Yg0nrefwmValU+CnA5l/AriVdQNthU/33H3nK71HrLgqOPr1Q==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.3.0.tgz",
"integrity": "sha512-r6gvs5OfRq/w+9unPm7B3po4rmWaGh0CIL/OwHntGGux7+RhOOZLGuurbeMgWV6W55ZuyMTypJLeH0vn/ZRaWQ==",
"dependencies": {
"@smithy/middleware-serde": "^4.0.3",
"@smithy/protocol-http": "^5.1.0",
@ -2160,11 +2160,11 @@
}
},
"node_modules/@smithy/middleware-endpoint": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.0.tgz",
"integrity": "sha512-xhLimgNCbCzsUppRTGXWkZywksuTThxaIB0HwbpsVLY5sceac4e1TZ/WKYqufQLaUy+gUSJGNdwD2jo3cXL0iA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.1.tgz",
"integrity": "sha512-z5RmcHxjvScL+LwEDU2mTNCOhgUs4lu5PGdF1K36IPRmUHhNFxNxgenSB7smyDiYD4vdKQ7CAZtG5cUErqib9w==",
"dependencies": {
"@smithy/core": "^3.2.0",
"@smithy/core": "^3.3.0",
"@smithy/middleware-serde": "^4.0.3",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/shared-ini-file-loader": "^4.0.2",
@ -2178,14 +2178,14 @@
}
},
"node_modules/@smithy/middleware-retry": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.0.tgz",
"integrity": "sha512-2zAagd1s6hAaI/ap6SXi5T3dDwBOczOMCSkkYzktqN1+tzbk1GAsHNAdo/1uzxz3Ky02jvZQwbi/vmDA6z4Oyg==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.1.tgz",
"integrity": "sha512-mBJOxn9aUYwcBUPQpKv9ifzrCn4EbhPUFguEZv3jB57YOMh0caS4P8HoLvUeNUI1nx4bIVH2SIbogbDfFI9DUA==",
"dependencies": {
"@smithy/node-config-provider": "^4.0.2",
"@smithy/protocol-http": "^5.1.0",
"@smithy/service-error-classification": "^4.0.2",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"@smithy/util-middleware": "^4.0.2",
"@smithy/util-retry": "^4.0.2",
@ -2352,12 +2352,12 @@
}
},
"node_modules/@smithy/smithy-client": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.0.tgz",
"integrity": "sha512-Qs65/w30pWV7LSFAez9DKy0Koaoh3iHhpcpCCJ4waj/iqwsuSzJna2+vYwq46yBaqO5ZbP9TjUsATUNxrKeBdw==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.1.tgz",
"integrity": "sha512-fbniZef60QdsBc4ZY0iyI8xbFHIiC/QRtPi66iE4ufjiE/aaz7AfUXzcWMkpO8r+QhLeNRIfmPchIG+3/QDZ6g==",
"dependencies": {
"@smithy/core": "^3.2.0",
"@smithy/middleware-endpoint": "^4.1.0",
"@smithy/core": "^3.3.0",
"@smithy/middleware-endpoint": "^4.1.1",
"@smithy/middleware-stack": "^4.0.2",
"@smithy/protocol-http": "^5.1.0",
"@smithy/types": "^4.2.0",
@ -2451,12 +2451,12 @@
}
},
"node_modules/@smithy/util-defaults-mode-browser": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.8.tgz",
"integrity": "sha512-ZTypzBra+lI/LfTYZeop9UjoJhhGRTg3pxrNpfSTQLd3AJ37r2z4AXTKpq1rFXiiUIJsYyFgNJdjWRGP/cbBaQ==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.9.tgz",
"integrity": "sha512-B8j0XsElvyhv6+5hlFf6vFV/uCSyLKcInpeXOGnOImX2mGXshE01RvPoGipTlRpIk53e6UfYj7WdDdgbVfXDZw==",
"dependencies": {
"@smithy/property-provider": "^4.0.2",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"bowser": "^2.11.0",
"tslib": "^2.6.2"
@ -2466,15 +2466,15 @@
}
},
"node_modules/@smithy/util-defaults-mode-node": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.8.tgz",
"integrity": "sha512-Rgk0Jc/UDfRTzVthye/k2dDsz5Xxs9LZaKCNPgJTRyoyBoeiNCnHsYGOyu1PKN+sDyPnJzMOz22JbwxzBp9NNA==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.9.tgz",
"integrity": "sha512-wTDU8P/zdIf9DOpV5qm64HVgGRXvqjqB/fJZTEQbrz3s79JHM/E7XkMm/876Oq+ZLHJQgnXM9QHDo29dlM62eA==",
"dependencies": {
"@smithy/config-resolver": "^4.1.0",
"@smithy/credential-provider-imds": "^4.0.2",
"@smithy/node-config-provider": "^4.0.2",
"@smithy/property-provider": "^4.0.2",
"@smithy/smithy-client": "^4.2.0",
"@smithy/smithy-client": "^4.2.1",
"@smithy/types": "^4.2.0",
"tslib": "^2.6.2"
},

View File

@ -15,7 +15,7 @@
},
"main": "lib/index.js",
"dependencies": {
"@aws-sdk/client-ses": "^3.796.0",
"@aws-sdk/client-ses": "^3.798.0",
"@types/node-fetch": "^2.6.12",
"aws-sdk": "^2.1692.0",
"axios": "^1.8.4",

View File

@ -3,6 +3,6 @@ export { sendEmailMessage, sendEmailWithAttachment, sendEmailSES } from './email
export { sendSMSMessage } from './sms';
export { accessFile } from './storage';
export { processNotificationOnCreate } from './notifications';
export { createCashfreeLink, createCashfreeOrder, verifyCashfreePayment } from './payments';
export { createCashfreeLink, createCashfreeOrder, verifyCashfreePayment, checkPhonePePaymentStatus, createPhonePeOrder, phonePeWebhook } from './payments';
export { getPlaceDetails, getPlacesAutocomplete } from './places';
export { registerClient } from './clientRegistration';

View File

@ -1 +1,2 @@
export * from './cashfree';
export * from './phonepe';

View File

@ -0,0 +1,139 @@
import axios from "axios";
import { onRequest } from "firebase-functions/v2/https";
import { Request} from "firebase-functions/v2/https";
import { getCorsHandler } from "../../shared/middleware";
import { getAdmin, getLogger } from "../../shared/config";
const admin = getAdmin();
const logger = getLogger();
const corsHandler = getCorsHandler();
export const checkPhonePePaymentStatus = onRequest({
region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => {
return corsHandler(request, response, async () => {
try {
const authHeader = request.headers.authorization || '';
if (!authHeader || !authHeader.startsWith('Bearer ')) {
response.status(401).json({ error: 'Unauthorized' });
return;
}
const idToken = authHeader.split('Bearer ')[1];
try {
await admin.auth().verifyIdToken(idToken);
const merchantOrderId = request.query.merchantOrderId as string;
if (!merchantOrderId) {
response.status(400).json({ error: 'Missing merchant order ID' });
return;
}
const details = request.query.details === 'true';
const errorContext = request.query.errorContext === 'true';
const clientId = process.env.PHONEPE_CLIENT_ID;
const clientSecret = process.env.PHONEPE_CLIENT_SECRET;
const apiUrl = process.env.PHONEPE_API_URL;
const clientVersion = process.env.PHONEPE_CLIENT_VERSION || '1';
if (!clientId || !clientSecret || !apiUrl) {
logger.error('PhonePe credentials not configured');
response.status(500).json({ error: 'Payment gateway configuration error' });
return;
}
const tokenResponse = await axios.post(
`${apiUrl}/v1/oauth/token`,
{
client_id: clientId,
client_version: clientVersion,
client_secret: clientSecret,
grant_type: 'client_credentials',
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
const accessToken = tokenResponse.data.access_token;
const queryParams = new URLSearchParams();
if (details) queryParams.append('details', 'true');
if (errorContext) queryParams.append('errorContext', 'true');
const queryString = queryParams.toString() ? `?${queryParams.toString()}` : '';
const statusResponse = await axios.get(
`${apiUrl}/checkout/v2/order/${merchantOrderId}/status${queryString}`,
{
headers: {
'Content-Type': 'application/json',
'Authorization': `O-Bearer ${accessToken}`,
},
}
);
const orderQuery = await admin.firestore()
.collection('payment_orders')
.where('merchantOrderId', '==', merchantOrderId)
.limit(1)
.get();
if (orderQuery.empty) {
logger.error(`No payment order found with PhonePe orderId: ${merchantOrderId}`);
response.status(404).json({
success: false,
error: 'Payment order not found',
message: `No record found for PhonePe order ID: ${merchantOrderId}`
});
return;
}
const orderDoc = orderQuery.docs[0];
await orderDoc.ref.update({
orderStatus: statusResponse.data.state || 'UNKNOWN',
lastChecked: new Date(),
statusResponse: statusResponse.data
});
logger.info('PhonePe status response data:', JSON.stringify(statusResponse.data));
response.json({
success: true,
state: statusResponse.data.state,
data: statusResponse.data
});
} catch (authError: any) {
logger.error('Authentication error:', authError);
if (authError.response) {
logger.error('API error details:', {
status: authError.response.status,
data: authError.response.data
});
response.status(authError.response.status).json({
success: false,
error: 'API error',
details: authError.response.data
});
} else {
response.status(401).json({
success: false,
error: 'Invalid authentication token or API error',
message: authError.message
});
}
}
} catch (error: any) {
logger.error('PhonePe payment status check error:', error);
response.status(500).json({
success: false,
error: 'Failed to check payment status',
details: error.message
});
}
});
});

View File

@ -0,0 +1,133 @@
import { onRequest } from "firebase-functions/v2/https";
import { Request} from "firebase-functions/v2/https";
import { getCorsHandler } from "../../shared/middleware";
import { getAdmin, getLogger } from "../../shared/config";
import axios from "axios";
const admin = getAdmin();
const logger = getLogger();
const corsHandler = getCorsHandler();
export const createPhonePeOrder = onRequest({
region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => {
return corsHandler(request, response, async () => {
try {
const authHeader = request.headers.authorization || '';
if (!authHeader || !authHeader.startsWith('Bearer ')) {
response.status(401).json({ error: 'Unauthorized' });
return;
}
const idToken = authHeader.split('Bearer ')[1];
try {
const decodedToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedToken.uid;
const {
merchantOrderId,
amount,
expireAfter,
metaInfo,
paymentFlow
} = request.body;
if (!merchantOrderId || !amount || !paymentFlow || !expireAfter) {
response.status(400).json({ error: 'Missing required fields' });
return;
}
if (!paymentFlow.type || !paymentFlow.merchantUrls || !paymentFlow.merchantUrls.redirectUrl) {
response.status(400).json({ error: 'Invalid payment flow configuration' });
return;
}
const clientId = process.env.PHONEPE_CLIENT_ID;
const clientSecret = process.env.PHONEPE_CLIENT_SECRET;
const apiUrl = process.env.PHONEPE_API_URL;
const clientVersion = process.env.PHONEPE_CLIENT_VERSION || '1';
if (!clientId || !clientSecret || !apiUrl) {
logger.error('PhonePe credentials not configured');
response.status(500).json({ error: 'Payment gateway configuration error' });
return;
}
try {
const tokenResponse = await axios.post(
`${apiUrl}/v1/oauth/token`,
{
client_id: clientId,
client_version: clientVersion,
client_secret: clientSecret,
grant_type: 'client_credentials',
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}
);
const accessToken = tokenResponse.data.access_token;
const paymentResponse = await axios.post(
`${apiUrl}/checkout/v2/pay`,
request.body,
{
headers: {
'Content-Type': 'application/json',
'Authorization': `O-Bearer ${accessToken}`,
},
}
);
try {
await admin.firestore().collection('payment_orders').doc(merchantOrderId).set({
userId: uid,
amount: amount / 100,
orderStatus: paymentResponse.data.state || 'PENDING',
paymentGateway: 'PhonePe',
createdAt: new Date(),
merchantOrderId: merchantOrderId,
paymentUrl: paymentResponse.data.redirectUrl,
orderId: paymentResponse.data.orderId,
expireAt: new Date(paymentResponse.data.expireAt),
// rawResponse: paymentResponse.data,
metaInfo: metaInfo || {}
});
} catch (firestoreError) {
logger.error('Error storing order in Firestore:', firestoreError);
}
response.json({
...paymentResponse.data,
merchantOrderId: merchantOrderId
});
logger.info(`PhonePe order created: ${merchantOrderId}`);
} catch (apiError: any) {
logger.error('PhonePe API error:', apiError);
response.status(apiError.response?.status || 500).json({
success: false,
error: 'Payment gateway error',
details: apiError.response?.data || apiError.message,
code: apiError.code
});
}
} catch (authError) {
logger.error('Authentication error:', authError);
response.status(401).json({
success: false,
error: 'Invalid authentication token'
});
}
} catch (error: any) {
logger.error('PhonePe order creation error:', error);
response.status(500).json({
success: false,
error: 'Failed to create payment order',
details: error.message
});
}
});
});

View File

@ -0,0 +1,3 @@
export { createPhonePeOrder } from './createPhonepeOrder';
export { checkPhonePePaymentStatus } from './checkStatus';
export { phonePeWebhook } from './webhook';

View File

@ -0,0 +1,106 @@
import { onRequest } from "firebase-functions/v2/https";
import { Request } from "firebase-functions/v2/https";
import { getAdmin, getLogger } from "../../shared/config";
import crypto from "crypto";
const admin = getAdmin();
const logger = getLogger();
export const phonePeWebhook = onRequest({
region: '#{SERVICES_RGN}#'
}, async (request: Request, response) => {
try {
const authHeader = request.headers['authorization'] as string;
const username = process.env.PHONEPE_WEBHOOK_USERNAME;
const password = process.env.PHONEPE_WEBHOOK_PASSWORD;
if (!authHeader || !username || !password) {
logger.error('Missing authorization header or webhook credentials');
response.status(401).json({ error: 'Unauthorized' });
return;
}
// Calculate expected authorization value
const credentialString = `${username}:${password}`;
const expectedAuth = crypto
.createHash('sha256')
.update(credentialString)
.digest('hex');
// PhonePe may send the header with a prefix like "SHA256 " or just the hash
const receivedAuth = authHeader.replace(/^SHA256\s+/i, '');
if (receivedAuth.toLowerCase() !== expectedAuth.toLowerCase()) {
logger.error('Invalid webhook authorization');
response.status(401).json({ error: 'Invalid authorization' });
return;
}
const { event, payload } = request.body;
if (!event || !payload || !payload.merchantOrderId || !payload.orderId) {
logger.error('Invalid webhook payload', request.body);
response.status(400).json({ error: 'Invalid payload' });
return;
}
logger.info(`Received PhonePe webhook: ${event}`, {
merchantOrderId: payload.merchantOrderId,
orderId: payload.orderId,
state: payload.state
});
const orderQuery = await admin.firestore()
.collection('payment_orders')
.where('orderId', '==', payload.orderId)
.limit(1)
.get();
if (orderQuery.empty) {
const merchantOrderQuery = await admin.firestore()
.collection('payment_orders')
.where('merchantOrderId', '==', payload.merchantOrderId)
.limit(1)
.get();
if (merchantOrderQuery.empty) {
logger.error(`No payment order found for PhonePe orderId: ${payload.orderId} or merchantOrderId: ${payload.merchantOrderId}`);
response.status(404).json({
success: false,
error: 'Payment order not found'
});
return;
}
const orderDoc = merchantOrderQuery.docs[0];
await orderDoc.ref.update({
orderStatus: payload.state || 'UNKNOWN',
lastUpdated: new Date(),
webhookEvent: event,
webhookData: payload
});
logger.info(`Updated order status via webhook for merchantOrderId: ${payload.merchantOrderId} to ${payload.state}`);
} else {
const orderDoc = orderQuery.docs[0];
await orderDoc.ref.update({
orderStatus: payload.state || 'UNKNOWN',
lastUpdated: new Date(),
webhookEvent: event,
webhookData: payload
});
logger.info(`Updated order status via webhook for orderId: ${payload.orderId} to ${payload.state}`);
}
response.status(200).json({ success: true });
} catch (error: any) {
logger.error('PhonePe webhook processing error:', error);
response.status(500).json({
success: false,
error: 'Failed to process webhook',
details: error.message
});
}
});

View File

@ -9,7 +9,9 @@
"outDir": "lib",
"sourceMap": true,
"strict": true,
"target": "es2017"
"target": "es2017",
"types": [],
"skipLibCheck": true
},
"compileOnSave": true,
"include": [

View File

@ -2,8 +2,5 @@
"dependencies": {
"@types/busboy": "^1.5.4",
"busboy": "^1.6.0"
},
"devDependencies": {
"@types/long": "^5.0.0"
}
}