diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index de36119e8..9b4440753 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,30 +1,30 @@ -* @prafulkoppalkar @arun-mi +* @prafulkoppalkar @akash-kamble-juspay @ArushKapoorJuspay docs/ @akash-kamble-juspay LICENSE @akash-kamble-juspay README.md @akash-kamble-juspay SECURITY.md @akash-kamble-juspay -.gitignore @arun-mi @prafulkoppalkar +.gitignore @prafulkoppalkar @ArushKapoorJuspay Hyperswitch-React-Demo-App/ @prafulkoppalkar @JeevaRamu0104 Hyperswitch-React-Demo-App/Dockerfile @JeevaRamu0104 @prafulkoppalkar Hyperswitch-React-Demo-App/Makefile @JeevaRamu0104 @prafulkoppalkar -Hyperswitch-React-Demo-App/promptScript.js @prafulkoppalkar -Hyperswitch-React-Demo-App/webpack.common.js @arun-mi @prafulkoppalkar -Hyperswitch-React-Demo-App/webpack.dev.js @arun-mi @prafulkoppalkar +Hyperswitch-React-Demo-App/promptScript.js @prafulkoppalkar @ArushKapoorJuspay +Hyperswitch-React-Demo-App/webpack.common.js @prafulkoppalkar @ArushKapoorJuspay +Hyperswitch-React-Demo-App/webpack.dev.js @prafulkoppalkar @ArushKapoorJuspay .husky/ @harsh-Juspay @prafulkoppalkar .github/ @harsh-Juspay @prafulkoppalkar -aws/ @arun-mi @prafulkoppalkar +aws/ @prafulkoppalkar -webpack.dev.js @arun-mi @prafulkoppalkar -webpack.common.js @arun-mi @prafulkoppalkar +webpack.dev.js @prafulkoppalkar @ArushKapoorJuspay +webpack.common.js @prafulkoppalkar @ArushKapoorJuspay -src/orca-log-catcher @vsrivatsa-juspay @arun-mi -src/orca-loader @arun-mi @prafulkoppalkar -src/LoaderConroller.res @arun-mi @prafulkoppalkar -src/Utilities @arun-mi @prafulkoppalkar -src/libraries @arun-mi @prafulkoppalkar -src/Hooks @arun-mi @prafulkoppalkar -src/Payment.res @arun-mi @prafulkoppalkar \ No newline at end of file +src/orca-log-catcher @vsrivatsa-juspay @prafulkoppalkar +src/orca-loader @prafulkoppalkar +src/LoaderConroller.res @prafulkoppalkar +src/Utilities @prafulkoppalkar +src/libraries @prafulkoppalkar +src/Hooks @prafulkoppalkar +src/Payment.res @prafulkoppalkar diff --git a/CHANGELOG.md b/CHANGELOG.md index c16a4cd29..3edc06c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,166 @@ +# [0.25.0](https://github.com/juspay/hyperswitch-web/compare/v0.24.0...v0.25.0) (2024-02-19) + + +### Features + +* HS-132: Language Support for Error Messages ([#173](https://github.com/juspay/hyperswitch-web/issues/173)) ([281279c](https://github.com/juspay/hyperswitch-web/commit/281279c773f757e3c361f8e068b4d24a24028e42)) +* moved Issues from Jira to Github ([#138](https://github.com/juspay/hyperswitch-web/issues/138)) ([5b42603](https://github.com/juspay/hyperswitch-web/commit/5b426034ca13a75eafb282e4de09a402b5c91636)) + +# [0.24.0](https://github.com/juspay/hyperswitch-web/compare/v0.23.12...v0.24.0) (2024-02-19) + + +### Features + +* Logging framework revamped ([#167](https://github.com/juspay/hyperswitch-web/issues/167)) ([6ecbadd](https://github.com/juspay/hyperswitch-web/commit/6ecbaddad644b50d6225516f4dd5df5403e5d9ba)) + +## [0.23.12](https://github.com/juspay/hyperswitch-web/compare/v0.23.11...v0.23.12) (2024-02-19) + + +### Bug Fixes + +* Handling error cases for payment methods list ([#164](https://github.com/juspay/hyperswitch-web/issues/164)) ([8df24df](https://github.com/juspay/hyperswitch-web/commit/8df24df92e9fa488ad81fb27709cbffe12a6bef6)) + +## [0.23.11](https://github.com/juspay/hyperswitch-web/compare/v0.23.10...v0.23.11) (2024-02-16) + + +### Bug Fixes + +* HS-222: Address line2 optional in case of isUseBillingAddress ([#174](https://github.com/juspay/hyperswitch-web/issues/174)) ([d2fa10e](https://github.com/juspay/hyperswitch-web/commit/d2fa10e3c9e48bb8c9488cc3fcf044b7c05dd301)) + +## [0.23.10](https://github.com/juspay/hyperswitch-web/compare/v0.23.9...v0.23.10) (2024-02-16) + + +### Bug Fixes + +* HS-148: Added Currency with surcharge amount ([#146](https://github.com/juspay/hyperswitch-web/issues/146)) ([ceadf3e](https://github.com/juspay/hyperswitch-web/commit/ceadf3e8bb1845058504884940c5d5573bda2dbf)) + +## [0.23.9](https://github.com/juspay/hyperswitch-web/compare/v0.23.8...v0.23.9) (2024-02-16) + + +### Bug Fixes + +* HS-132: Error handling for Address Fields ([#171](https://github.com/juspay/hyperswitch-web/issues/171)) ([38f3a79](https://github.com/juspay/hyperswitch-web/commit/38f3a79552f10e6712d2402475d44a70aba46e41)) + +## [0.23.8](https://github.com/juspay/hyperswitch-web/compare/v0.23.7...v0.23.8) (2024-02-15) + + +### Bug Fixes + +* HS-111: Added billing name to address element ([#145](https://github.com/juspay/hyperswitch-web/issues/145)) ([426415a](https://github.com/juspay/hyperswitch-web/commit/426415a8c88e56bd0e6b84da56982c1e40082c67)) + +## [0.23.7](https://github.com/juspay/hyperswitch-web/compare/v0.23.6...v0.23.7) (2024-02-13) + + +### Bug Fixes + +* Payment Dropdown multiple render fix ([#166](https://github.com/juspay/hyperswitch-web/issues/166)) ([56e8cb3](https://github.com/juspay/hyperswitch-web/commit/56e8cb311301fb26d415e87af639b58cfa090080)) + +## [0.23.6](https://github.com/juspay/hyperswitch-web/compare/v0.23.5...v0.23.6) (2024-02-13) + + +### Bug Fixes + +* HS-2222: Validation failing for Billing details ([#165](https://github.com/juspay/hyperswitch-web/issues/165)) ([2c1754d](https://github.com/juspay/hyperswitch-web/commit/2c1754df5cf90b37c897bf03de11b79d6eb3bb32)) + +## [0.23.5](https://github.com/juspay/hyperswitch-web/compare/v0.23.4...v0.23.5) (2024-02-13) + + +### Bug Fixes + +* HS-202: ApplePay Dynamic Fields Error Handling ([#159](https://github.com/juspay/hyperswitch-web/issues/159)) ([ad97f2d](https://github.com/juspay/hyperswitch-web/commit/ad97f2da1bd83132f1a97590154f650a283f8aea)) + +## [0.23.4](https://github.com/juspay/hyperswitch-web/compare/v0.23.3...v0.23.4) (2024-02-13) + + +### Bug Fixes + +* HS-1223: updated codeowners ([#142](https://github.com/juspay/hyperswitch-web/issues/142)) ([ff29a20](https://github.com/juspay/hyperswitch-web/commit/ff29a209958df1c27f4384652500c4e52cefb97c)) +* HS-234: Added support for few more countries ([#160](https://github.com/juspay/hyperswitch-web/issues/160)) ([4bac3aa](https://github.com/juspay/hyperswitch-web/commit/4bac3aa238f5d986b730801cc117e8980c42e66f)) + +## [0.23.3](https://github.com/juspay/hyperswitch-web/compare/v0.23.2...v0.23.3) (2024-02-13) + + +### Bug Fixes + +* HS-167: Changed name and icon for Open Banking Uk ([#156](https://github.com/juspay/hyperswitch-web/issues/156)) ([4b33231](https://github.com/juspay/hyperswitch-web/commit/4b33231c0ee1689f77da0c5e5e20f88f0ade5c37)) +* HS-187: Improve UI qr code display for PIX ([#149](https://github.com/juspay/hyperswitch-web/issues/149)) ([1cf448b](https://github.com/juspay/hyperswitch-web/commit/1cf448b5e0e4d0124ff34aed818f44dfb75e9cf5)) + +## [0.23.2](https://github.com/juspay/hyperswitch-web/compare/v0.23.1...v0.23.2) (2024-02-12) + + +### Bug Fixes + +* HS-1111: fixed addres state ([#151](https://github.com/juspay/hyperswitch-web/issues/151)) ([a91cfd9](https://github.com/juspay/hyperswitch-web/commit/a91cfd9ab2331760032221d732e92b02a4b40750)) + +## [0.23.1](https://github.com/juspay/hyperswitch-web/compare/v0.23.0...v0.23.1) (2024-02-12) + + +### Bug Fixes + +* HS-2222: Missing Address state ([#150](https://github.com/juspay/hyperswitch-web/issues/150)) ([fd10795](https://github.com/juspay/hyperswitch-web/commit/fd107951beedc82d83a0688a1ae7df86c4bf7c12)) + +# [0.23.0](https://github.com/juspay/hyperswitch-web/compare/v0.22.5...v0.23.0) (2024-02-11) + + +### Features + +* Added masked payload for confirm calls ([#148](https://github.com/juspay/hyperswitch-web/issues/148)) ([e3fe10d](https://github.com/juspay/hyperswitch-web/commit/e3fe10d1d8b63d1b1ce2f4cdc8901384ba6cdb91)) + +## [0.22.5](https://github.com/juspay/hyperswitch-web/compare/v0.22.4...v0.22.5) (2024-02-07) + + +### Bug Fixes + +* HS-132: Fixed multiple re render ([#144](https://github.com/juspay/hyperswitch-web/issues/144)) ([8ffa095](https://github.com/juspay/hyperswitch-web/commit/8ffa095b52e45fd3804b4b416f58e4706746cb45)) + +## [0.22.4](https://github.com/juspay/hyperswitch-web/compare/v0.22.3...v0.22.4) (2024-02-05) + + +### Bug Fixes + +* HS-1111: invalid country options ([#141](https://github.com/juspay/hyperswitch-web/issues/141)) ([4198ace](https://github.com/juspay/hyperswitch-web/commit/4198ace098b32bfecb810e99db966f62833b2428)) + +## [0.22.3](https://github.com/juspay/hyperswitch-web/compare/v0.22.2...v0.22.3) (2024-02-05) + + +### Bug Fixes + +* HS-111: Fix: Empty/Invalid Country Varient /confirm call ([#137](https://github.com/juspay/hyperswitch-web/issues/137)) ([ad0ef72](https://github.com/juspay/hyperswitch-web/commit/ad0ef72f3652375d1e255f44427e317ad1ea84e4)) +* HS-132: Dynamic Fields fix for email ([#139](https://github.com/juspay/hyperswitch-web/issues/139)) ([10dc77a](https://github.com/juspay/hyperswitch-web/commit/10dc77a8eb72c437703ccfbdf60c8e99ec0c8df1)) + +## [0.22.2](https://github.com/juspay/hyperswitch-web/compare/v0.22.1...v0.22.2) (2024-02-02) + + +### Bug Fixes + +* HS-111: Fix: Card cvc for saved cards empty (blocking confirm) ([#135](https://github.com/juspay/hyperswitch-web/issues/135)) ([d710cd7](https://github.com/juspay/hyperswitch-web/commit/d710cd787bc33b3233d7dcdae29cef32b8cba47f)) + +## [0.22.1](https://github.com/juspay/hyperswitch-web/compare/v0.22.0...v0.22.1) (2024-02-01) + + +### Bug Fixes + +* HS-187: Added pix svg icon ([#134](https://github.com/juspay/hyperswitch-web/issues/134)) ([7b2e4c5](https://github.com/juspay/hyperswitch-web/commit/7b2e4c511ba831c9a15fa76092b7a431274d0bec)) + +# [0.22.0](https://github.com/juspay/hyperswitch-web/compare/v0.21.0...v0.22.0) (2024-02-01) + + +### Features + +* HS-187: Added Pix Bank Transfer ([#129](https://github.com/juspay/hyperswitch-web/issues/129)) ([3ea2e03](https://github.com/juspay/hyperswitch-web/commit/3ea2e0387fd34029308368ea8f068cf9d336a716)) + +# [0.21.0](https://github.com/juspay/hyperswitch-web/compare/v0.20.5...v0.21.0) (2024-02-01) + + +### Bug Fixes + +* HS-12344: Email fields empty for saved cards flow ([#127](https://github.com/juspay/hyperswitch-web/issues/127)) ([710f705](https://github.com/juspay/hyperswitch-web/commit/710f705f738a0c805847a3b596e83d449486d704)) + + +### Features + +* HS-2222: Dynamic fields support for IDeal, Sofort and Eps ([#125](https://github.com/juspay/hyperswitch-web/issues/125)) ([789ecb9](https://github.com/juspay/hyperswitch-web/commit/789ecb99ffff4c9ee9ffe55af501ba8953c667b7)) + ## [0.20.5](https://github.com/juspay/hyperswitch-web/compare/v0.20.4...v0.20.5) (2024-01-24) diff --git a/package-lock.json b/package-lock.json index 4ac305949..e1ceb9b96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "orca-payment-page", - "version": "0.20.5", + "version": "0.25.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2663,76 +2663,6 @@ "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", "dev": true }, - "@digitalroute/cz-conventional-changelog-for-jira": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@digitalroute/cz-conventional-changelog-for-jira/-/cz-conventional-changelog-for-jira-7.5.1.tgz", - "integrity": "sha512-LB/cglMCftFbNCaVcXE4CgZp7qD3ZgOzXkia6535Yb55BHpaxbREslTFRz+fJFSU2fPBfVseaUvdaOCTCp0WQQ==", - "dev": true, - "requires": { - "@commitlint/load": ">6.1.1", - "@ryansonshine/commitizen": "^4.2.8", - "@ryansonshine/cz-conventional-changelog": "^3.3.4", - "boxen": "^5.1.2", - "chalk": "^2.4.1", - "inquirer": "^8.2.4", - "lodash.map": "^4.5.1", - "longest": "^2.0.1", - "right-pad": "^1.0.1", - "word-wrap": "^1.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -3608,142 +3538,6 @@ "integrity": "sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA==", "dev": true }, - "@ryansonshine/commitizen": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@ryansonshine/commitizen/-/commitizen-4.2.8.tgz", - "integrity": "sha512-umjePm6tYbki25Mp34ijQSGqMBqj5R1e4BTwTGD2zaZA5k3k0lx4MQJhh0g7QyIhzvkuoZvD0f4MG0gj8ABjBg==", - "dev": true, - "requires": { - "@ryansonshine/cz-conventional-changelog": "^3.3.4", - "cachedir": "^2.3.0", - "dedent": "^0.7.0", - "detect-indent": "^6.1.0", - "find-node-modules": "^2.1.2", - "find-root": "^1.1.0", - "fs-extra": "^9.1.0", - "glob": "^7.2.0", - "inquirer": "^8.2.2", - "is-utf8": "^0.2.1", - "lodash": "^4.17.21", - "minimist": "^1.2.6", - "strip-bom": "^4.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "@ryansonshine/cz-conventional-changelog": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@ryansonshine/cz-conventional-changelog/-/cz-conventional-changelog-3.3.4.tgz", - "integrity": "sha512-t9DRpWeUXPs4YpQt0Yu50qlvOLax/OKBQwpygr00hjIXPG7HAiGS9BYc+V8W7riri3cIE5D4bmt7V9Drk1jp8Q==", - "dev": true, - "requires": { - "@commitlint/load": ">6.1.1", - "@ryansonshine/commitizen": "^4.2.6", - "chalk": "^2.4.1", - "conventional-commit-types": "^3.0.0", - "lodash.map": "^4.5.1", - "longest": "^2.0.1", - "word-wrap": "^1.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "@ryyppy/rescript-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@ryyppy/rescript-promise/-/rescript-promise-2.1.0.tgz", @@ -5629,15 +5423,6 @@ "fast-deep-equal": "^3.1.3" } }, - "ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "requires": { - "string-width": "^4.1.0" - } - }, "ansi-colors": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", @@ -6286,6 +6071,16 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -6411,36 +6206,6 @@ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" }, - "boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -6548,12 +6313,6 @@ "unset-value": "^1.0.0" } }, - "cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", - "dev": true - }, "call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -6737,12 +6496,6 @@ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6934,31 +6687,6 @@ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true }, - "commitlint-config-jira": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/commitlint-config-jira/-/commitlint-config-jira-1.6.4.tgz", - "integrity": "sha512-DNVOzQZgNPZuFYU61mVRssm0t+4IR7eF5VQA3RCGYdz45usauekjguOXJmV3uxEHIoqVDctKN2UrUlhbKCymrQ==", - "dev": true, - "requires": { - "commitlint-jira-utils": "^1.6.4", - "opencollective-postinstall": "^2.0.2" - } - }, - "commitlint-jira-utils": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/commitlint-jira-utils/-/commitlint-jira-utils-1.6.4.tgz", - "integrity": "sha512-1Mp5b0Q31XBreH3EKrDy8sD6vZmcXFm1x6OXeskaiiOKm5C1qQ0ObOiF66RKozuUAixaOGgHje6N/T+nfd5GBA==", - "dev": true - }, - "commitlint-plugin-jira-rules": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/commitlint-plugin-jira-rules/-/commitlint-plugin-jira-rules-1.6.4.tgz", - "integrity": "sha512-e+5pfp6emYp02Qm0OCs8rvPApKpTK7+HpndA2R9RJzNY0kg2i2/CGHuWd+StigMZe1Wmeo/d3H8Cwwlz3biWxA==", - "dev": true, - "requires": { - "commitlint-jira-utils": "^1.6.4" - } - }, "common-path-prefix": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", @@ -9800,6 +9528,13 @@ } } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -11027,42 +10762,6 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "dependencies": { - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, "internal-ip": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", @@ -13603,6 +13302,13 @@ "thenify-all": "^1.0.0" } }, + "nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "dev": true, + "optional": true + }, "nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -15770,12 +15476,6 @@ "is-wsl": "^2.2.0" } }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "dev": true - }, "opener": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", @@ -18254,12 +17954,6 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, - "right-pad": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/right-pad/-/right-pad-1.0.1.tgz", - "integrity": "sha512-bYBjgxmkvTAfgIYy328fmkwhp39v8lwVgWhhrzxPV3yHtcSqyYKe9/XOhvW48UFjATg3VuJbpsp5822ACNvkmw==", - "dev": true - }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -21096,7 +20790,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob": { "version": "7.2.3", @@ -21767,15 +21465,6 @@ "has-tostringtag": "^1.0.0" } }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, "wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", diff --git a/package.json b/package.json index ca1d5ee7f..f7d923e86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orca-payment-page", - "version": "0.20.5", + "version": "0.25.0", "main": "index.js", "private": true, "dependencies": { @@ -61,15 +61,12 @@ "devDependencies": { "@commitlint/cli": "^17.0.3", "@commitlint/config-conventional": "^17.0.3", - "@digitalroute/cz-conventional-changelog-for-jira": "^7.4.2", "@semantic-release/changelog": "^6.0.1", "@semantic-release/commit-analyzer": "^9.0.2", "@semantic-release/git": "^10.0.1", "@semantic-release/npm": "^9.0.1", "@semantic-release/release-notes-generator": "^10.0.3", "autoprefixer": "^10.4.8", - "commitlint-config-jira": "^1.6.4", - "commitlint-plugin-jira-rules": "^1.6.4", "concurrently": "4.1.2", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.1", @@ -90,42 +87,39 @@ }, "config": { "commitizen": { - "path": "./node_modules/@digitalroute/cz-conventional-changelog-for-jira", - "jiraPrefix": "HS", - "jiraLocation": "post-type", - "jiraAppend": ":" + "path": "./node_modules/cz-conventional-changelog", + "types": { + "feat": { + "description": "A new feature", + "title": "Features" + }, + "fix": { + "description": "A bug fix", + "title": "Bug Fixes" + }, + "refactor": { + "description": "A code change that neither fixes a bug nor adds a feature", + "title": "Code Refactoring" + }, + "chore": { + "description": "Other changes that don't modify src or test files", + "title": "Chores" + }, + "docs": { + "description": "Documentation only changes", + "title": "Documentation" + }, + "revert": { + "description": "Reverts a previous commit", + "title": "Reverts" + } + } } }, "commitlint": { - "plugins": [ - "commitlint-plugin-jira-rules" - ], "extends": [ - "jira" - ], - "rules": { - "jira-task-id-max-length": [ - 0 - ], - "jira-commit-message-separator": [ - 0 - ], - "jira-commit-status-case": [ - 0 - ], - "jira-task-id-project-key": [ - 0 - ], - "jira-task-id-separator": [ - 0 - ], - "jira-task-id-case": [ - 0 - ], - "jira-task-id-min-length": [ - 0 - ] - } + "@commitlint/config-conventional" + ] }, "release": { "branches": [ diff --git a/public/icons/orca.svg b/public/icons/orca.svg index 1b22cfd3c..6016bc4b1 100644 --- a/public/icons/orca.svg +++ b/public/icons/orca.svg @@ -1197,4 +1197,16 @@ License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL d="M10.44 14.25c-.01-1.011.827-1.499.865-1.522-.47-.689-1.202-.783-1.463-.794-.623-.062-1.216.367-1.532.367-.315 0-.804-.357-1.32-.347-.68.01-1.305.394-1.655 1.002-.705 1.225-.18 3.038.507 4.031.336.485.737 1.032 1.263 1.012.507-.02.698-.327 1.31-.327.613 0 .785.327 1.321.317.545-.01.891-.495 1.225-.982.385-.564.544-1.11.553-1.139-.012-.004-1.063-.407-1.074-1.618zM9.85 10c-.403.016-.89.269-1.179.606-.259.3-.486.779-.424 1.238.449.035.907-.228 1.187-.566.28-.339.467-.81.416-1.278z" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Components/AddressPaymentInput.res b/src/Components/AddressPaymentInput.res index 75b5af853..5b9dc389a 100644 --- a/src/Components/AddressPaymentInput.res +++ b/src/Components/AddressPaymentInput.res @@ -32,30 +32,6 @@ let showField = (val: PaymentType.addressType, type_: addressType) => { } } -let checkPostalValidity = ( - postal: RecoilAtomTypes.field, - setPostal: ( - . OrcaPaymentPage.RecoilAtomTypes.field => OrcaPaymentPage.RecoilAtomTypes.field, - ) => unit, - regex, -) => { - if Js.Re.test_(regex->Js.Re.fromString, postal.value) && postal.value !== "" && regex !== "" { - setPostal(.prev => { - ...prev, - isValid: Some(true), - errorString: "", - }) - } else if ( - regex !== "" && !Js.Re.test_(regex->Js.Re.fromString, postal.value) && postal.value !== "" - ) { - setPostal(.prev => { - ...prev, - isValid: Some(false), - errorString: "Invalid postal code", - }) - } -} - @react.component let make = (~paymentType, ~className="") => { let {localeString, themeObj} = Recoil.useRecoilValueFromAtom(configAtom) @@ -89,6 +65,30 @@ let make = (~paymentType, ~className="") => { let countryNames = getCountryNames(Country.country) + let checkPostalValidity = ( + postal: RecoilAtomTypes.field, + setPostal: ( + . OrcaPaymentPage.RecoilAtomTypes.field => OrcaPaymentPage.RecoilAtomTypes.field, + ) => unit, + regex, + ) => { + if Js.Re.test_(regex->Js.Re.fromString, postal.value) && postal.value !== "" && regex !== "" { + setPostal(.prev => { + ...prev, + isValid: Some(true), + errorString: "", + }) + } else if ( + regex !== "" && !Js.Re.test_(regex->Js.Re.fromString, postal.value) && postal.value !== "" + ) { + setPostal(.prev => { + ...prev, + isValid: Some(false), + errorString: localeString.postalCodeInvalidText, + }) + } + } + React.useEffect0(() => { open Promise // Dynamically import/download Postal codes and states JSON @@ -147,7 +147,7 @@ let make = (~paymentType, ~className="") => { setPostalCode(.prev => { ...prev, isValid: Some(false), - errorString: "Invalid postal code", + errorString: localeString.postalCodeInvalidText, }) } } @@ -173,31 +173,31 @@ let make = (~paymentType, ~className="") => { if line1.value == "" { setLine1(.prev => { ...prev, - errorString: "Address line 1 cannot be empty", + errorString: localeString.line1EmptyText, }) } if line2.value == "" { setLine2(.prev => { ...prev, - errorString: "Address line 2 cannot be empty", + errorString: localeString.line2EmptyText, }) } if state.value == "" { setState(.prev => { ...prev, - errorString: "State cannot be empty", + errorString: localeString.stateEmptyText, }) } if postalCode.value == "" { setPostalCode(.prev => { ...prev, - errorString: "Postal code cannot be empty", + errorString: localeString.postalCodeEmptyText, }) } if city.value == "" { setCity(.prev => { ...prev, - errorString: "City cannot be empty", + errorString: localeString.cityEmptyText, }) } } diff --git a/src/Components/BillingNamePaymentInput.res b/src/Components/BillingNamePaymentInput.res index 238fc0796..a9cb52a33 100644 --- a/src/Components/BillingNamePaymentInput.res +++ b/src/Components/BillingNamePaymentInput.res @@ -19,9 +19,16 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None, ( let changeName = ev => { let val: string = ReactEvent.Form.target(ev)["value"] setBillingName(.prev => { - ...prev, value: val, - errorString: "", + isValid: Some(val !== ""), + errorString: val !== "" ? "" : prev.errorString, + }) + } + let onBlur = ev => { + let val: string = ReactEvent.Focus.target(ev)["value"] + setBillingName(.prev => { + ...prev, + isValid: Some(val !== ""), }) } let (placeholder, fieldName) = switch customFieldName { @@ -37,7 +44,7 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None, ( if billingName.value == "" { setBillingName(.prev => { ...prev, - errorString: `Please provide your ${fieldName}`, + errorString: fieldName->localeString.nameEmptyText, }) } else { switch optionalRequiredFields { @@ -45,7 +52,7 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None, ( if !DynamicFieldsUtils.checkIfNameIsValid(requiredFields, BillingName, billingName) { setBillingName(.prev => { ...prev, - errorString: `Please provide your complete ${fieldName}`, + errorString: fieldName->localeString.completeNameEmptyText, }) } | None => () @@ -58,9 +65,11 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None, ( // - let paymentMethodTypes = + let paymentMethodTypes = React.useMemo3(() => { PaymentMethodsRecord.getPaymentMethodTypeFromList( ~list, ~paymentMethod, - ~paymentMethodType, + ~paymentMethodType=PaymentUtils.getPaymentMethodName( + ~paymentMethodType=paymentMethod, + ~paymentMethodName=paymentMethodType, + ), )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType) + }, (list, paymentMethod, paymentMethodType)) - let requiredFieldsWithBillingDetails = if paymentMethod === "card" { - paymentMethodTypes.required_fields - } else if ( - PaymentMethodsRecord.dynamicFieldsEnabledPaymentMethods->Js.Array2.includes(paymentMethodType) - ) { - paymentMethodTypes.required_fields - } else { - [] - } + let requiredFieldsWithBillingDetails = React.useMemo3(() => { + if paymentMethod === "card" { + paymentMethodTypes.required_fields + } else if ( + PaymentMethodsRecord.dynamicFieldsEnabledPaymentMethods->Js.Array2.includes(paymentMethodType) + ) { + paymentMethodTypes.required_fields + } else { + [] + } + }, (paymentMethod, paymentMethodTypes.required_fields, paymentMethodType)) - let requiredFields = - requiredFieldsWithBillingDetails->DynamicFieldsUtils.removeBillingDetailsIfUseBillingAddress + let requiredFields = React.useMemo1(() => { + requiredFieldsWithBillingDetails->DynamicFieldsUtils.removeBillingDetailsIfUseBillingAddress( + billingAddress, + ) + }, [requiredFieldsWithBillingDetails]) let isAllStoredCardsHaveName = React.useMemo1(() => { PaymentType.getIsAllStoredCardsHaveName(savedCards) }, [savedCards]) //<...>// - let fieldsArr = + let fieldsArr = React.useMemo3(() => { PaymentMethodsRecord.getPaymentMethodFields( paymentMethodType, requiredFields, @@ -54,9 +63,10 @@ let make = ( ~isAllStoredCardsHaveName, (), ) - ->DynamicFieldsUtils.updateDynamicFields() + ->DynamicFieldsUtils.updateDynamicFields(billingAddress, ()) ->Belt.SortArray.stableSortBy(PaymentMethodsRecord.sortPaymentMethodFields) - //<...>// + //<...>// + }, (requiredFields, isAllStoredCardsHaveName, isSavedCardFlow)) let {config, themeObj, localeString} = Recoil.useRecoilValueFromAtom(configAtom) @@ -186,29 +196,17 @@ let make = ( let onPostalChange = ev => { let val = ReactEvent.Form.target(ev)["value"] - setPostalCode(.prev => { - ...prev, - value: val, - errorString: "", - }) - if regex !== "" && Js.Re.test_(regex->Js.Re.fromString, val) { - CardUtils.blurRef(postalRef) - } - } - - let onPostalBlur = ev => { - let val = ReactEvent.Focus.target(ev)["value"] - if regex !== "" && Js.Re.test_(regex->Js.Re.fromString, val) && val !== "" { - setPostalCode(.prev => { - ...prev, + if val !== "" { + setPostalCode(._ => { isValid: Some(true), + value: val, errorString: "", }) - } else if regex !== "" && !Js.Re.test_(regex->Js.Re.fromString, val) && val !== "" { - setPostalCode(.prev => { - ...prev, + } else { + setPostalCode(._ => { isValid: Some(false), - errorString: "Invalid postal code", + value: val, + errorString: "", }) } } @@ -244,6 +242,9 @@ let make = ( ~setRequiredFieldsBody, ) + let submitCallback = DynamicFieldsUtils.useSubmitCallback() + Utils.submitPaymentData(submitCallback) + let bottomElement = let getCustomFieldName = (item: PaymentMethodsRecord.paymentMethodsFields) => { @@ -261,24 +262,30 @@ let make = ( } } - let dynamicFieldsToRenderOutsideBilling = + let dynamicFieldsToRenderOutsideBilling = React.useMemo1(() => { fieldsArr->Js.Array2.filter(field => field->DynamicFieldsUtils.isFieldTypeToRenderOutsideBilling ) + }, [fieldsArr]) - let dynamicFieldsToRenderInsideBilling = + let dynamicFieldsToRenderInsideBilling = React.useMemo1(() => { fieldsArr->Js.Array2.filter(field => !(field->DynamicFieldsUtils.isFieldTypeToRenderOutsideBilling) ) + }, [fieldsArr]) - let isInfoElementPresent = dynamicFieldsToRenderInsideBilling->Js.Array2.includes(InfoElement) + let isInfoElementPresent = React.useMemo1(() => { + dynamicFieldsToRenderInsideBilling->Js.Array2.includes(InfoElement) + }, [dynamicFieldsToRenderInsideBilling]) - let isOnlyInfoElementPresent = + let isOnlyInfoElementPresent = React.useMemo2(() => { dynamicFieldsToRenderInsideBilling->Js.Array2.length === 1 && isInfoElementPresent + }, (dynamicFieldsToRenderInsideBilling, isInfoElementPresent)) - let isRenderDynamicFieldsInsideBilling = + let isRenderDynamicFieldsInsideBilling = React.useMemo2(() => { dynamicFieldsToRenderInsideBilling->Js.Array2.length > 0 && (dynamicFieldsToRenderInsideBilling->Js.Array2.length > 1 || !isOnlyInfoElementPresent) + }, (dynamicFieldsToRenderInsideBilling, isOnlyInfoElementPresent)) { fieldsArr->Js.Array2.length > 0 @@ -403,8 +410,13 @@ let make = ( disabled=false options=currencyArr /> + | FullName => + getCustomFieldName} + optionalRequiredFields={Some(requiredFields)} + /> | Email - | FullName | InfoElement | Country | Bank @@ -434,7 +446,7 @@ let make = ( ~margin=`10px 0`, (), )}> - {React.string("Billing Details")} + {React.string(localeString.billingDetailsText)}
{dynamicFieldsToRenderInsideBilling ->Js.Array2.mapi((item, index) => { @@ -442,17 +454,9 @@ let make = ( key={`inside-billing-${index->Js.Int.toString}`} className="flex flex-col w-full place-content-between"> {switch item { - | FullName => - getCustomFieldName} - optionalRequiredFields={Some(requiredFields)} - /> | BillingName => getCustomFieldName} - optionalRequiredFields={Some(requiredFields)} + paymentType optionalRequiredFields={Some(requiredFields)} /> | Email => | PhoneNumber => @@ -463,9 +467,18 @@ let make = ( setValue={setCity} value=city onChange={ev => { + let value = ReactEvent.Form.target(ev)["value"] + setCity(.prev => { + isValid: value !== "" ? Some(true) : Some(false), + value, + errorString: value !== "" ? "" : prev.errorString, + }) + }} + onBlur={ev => { + let value = ReactEvent.Focus.target(ev)["value"] setCity(.prev => { ...prev, - value: ReactEvent.Form.target(ev)["value"], + isValid: Some(value !== ""), }) }} paymentType @@ -503,7 +516,13 @@ let make = ( fieldName=localeString.postalCodeLabel setValue={setPostalCode} value=postalCode - onBlur=onPostalBlur + onBlur={ev => { + let value = ReactEvent.Focus.target(ev)["value"] + setPostalCode(.prev => { + ...prev, + isValid: Some(value !== ""), + }) + }} onChange=onPostalChange paymentType type_="tel" @@ -518,9 +537,18 @@ let make = ( setValue={setLine1} value=line1 onChange={ev => { + let value = ReactEvent.Form.target(ev)["value"] + setLine1(.prev => { + isValid: value !== "" ? Some(true) : Some(false), + value, + errorString: value !== "" ? "" : prev.errorString, + }) + }} + onBlur={ev => { + let value = ReactEvent.Focus.target(ev)["value"] setLine1(.prev => { ...prev, - value: ReactEvent.Form.target(ev)["value"], + isValid: Some(value !== ""), }) }} paymentType @@ -535,9 +563,18 @@ let make = ( setValue={setLine2} value=line2 onChange={ev => { + let value = ReactEvent.Form.target(ev)["value"] + setLine2(.prev => { + isValid: value !== "" ? Some(true) : Some(false), + value, + errorString: value !== "" ? "" : prev.errorString, + }) + }} + onBlur={ev => { + let value = ReactEvent.Focus.target(ev)["value"] setLine2(.prev => { ...prev, - value: ReactEvent.Form.target(ev)["value"], + isValid: Some(value !== ""), }) }} paymentType @@ -552,9 +589,18 @@ let make = ( setValue={setCity} value=city onChange={ev => { + let value = ReactEvent.Form.target(ev)["value"] + setCity(.prev => { + isValid: value !== "" ? Some(true) : Some(false), + value, + errorString: value !== "" ? "" : prev.errorString, + }) + }} + onBlur={ev => { + let value = ReactEvent.Focus.target(ev)["value"] setCity(.prev => { ...prev, - value: ReactEvent.Form.target(ev)["value"], + isValid: Some(value !== ""), }) }} paymentType @@ -583,7 +629,13 @@ let make = ( fieldName=localeString.postalCodeLabel setValue={setPostalCode} value=postalCode - onBlur=onPostalBlur + onBlur={ev => { + let value = ReactEvent.Focus.target(ev)["value"] + setPostalCode(.prev => { + ...prev, + isValid: Some(value !== ""), + }) + }} onChange=onPostalChange paymentType type_="tel" @@ -636,6 +688,7 @@ let make = ( | CardCvc | CardExpiryAndCvc | Currency(_) + | FullName | None => React.null }}
diff --git a/src/Components/EmailPaymentInput.res b/src/Components/EmailPaymentInput.res index bd4bf42ca..a207be735 100644 --- a/src/Components/EmailPaymentInput.res +++ b/src/Components/EmailPaymentInput.res @@ -16,19 +16,26 @@ let make = (~paymentType) => { let changeEmail = ev => { let val: string = ReactEvent.Form.target(ev)["value"] setEmail(.prev => { - ...prev, value: val, + isValid: val->Utils.isEmailValid, + errorString: val->Utils.isEmailValid->Belt.Option.getWithDefault(false) + ? "" + : prev.errorString, }) } - let onBlur = _ => { - Utils.checkEmailValid(email, setEmail) + let onBlur = ev => { + let val = ReactEvent.Focus.target(ev)["value"] + setEmail(.prev => { + ...prev, + isValid: val->Utils.isEmailValid, + }) } React.useEffect1(() => { setEmail(.prev => { ...prev, errorString: switch prev.isValid { - | Some(val) => val ? "" : "Invalid email address" + | Some(val) => val ? "" : localeString.emailInvalidText | None => "" }, }) @@ -42,7 +49,7 @@ let make = (~paymentType) => { if email.value == "" { setEmail(.prev => { ...prev, - errorString: "Email cannot be empty", + errorString: localeString.emailEmptyText, }) } } diff --git a/src/Components/FullNamePaymentInput.res b/src/Components/FullNamePaymentInput.res index 34c221abc..adea5cb76 100644 --- a/src/Components/FullNamePaymentInput.res +++ b/src/Components/FullNamePaymentInput.res @@ -15,11 +15,20 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None) = let changeName = ev => { let val: string = ReactEvent.Form.target(ev)["value"] setFullName(.prev => { - ...prev, value: val, - errorString: "", + isValid: Some(val !== ""), + errorString: val !== "" ? "" : prev.errorString, }) } + + let onBlur = ev => { + let val: string = ReactEvent.Focus.target(ev)["value"] + setFullName(.prev => { + ...prev, + isValid: Some(val !== ""), + }) + } + let (placeholder, fieldName) = switch customFieldName { | Some(val) => (val, val) | None => (localeString.fullNamePlaceholder, localeString.fullNameLabel) @@ -33,7 +42,7 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None) = if fullName.value == "" { setFullName(.prev => { ...prev, - errorString: `Please provide your ${fieldName}`, + errorString: fieldName->localeString.nameEmptyText, }) } else { switch optionalRequiredFields { @@ -41,7 +50,7 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None) = if !DynamicFieldsUtils.checkIfNameIsValid(requiredFields, FullName, fullName) { setFullName(.prev => { ...prev, - errorString: `Please provide your complete ${fieldName}`, + errorString: fieldName->localeString.completeNameEmptyText, }) } | None => () @@ -54,9 +63,11 @@ let make = (~paymentType, ~customFieldName=None, ~optionalRequiredFields=None) = { - if value.value === "" { + React.useEffect1(() => { + let initialValue = options->Belt.Array.get(0)->Belt.Option.getWithDefault("") + if ( + value.value === "" || + value.value === initialValue || + options->Js.Array2.includes(value.value)->not + ) { setValue(.prev => { ...prev, - value: options->Belt.Array.get(0)->Belt.Option.getWithDefault(""), + isValid: Some(true), + value: initialValue, }) } None - }) + }, [options->Belt.Array.get(0)->Belt.Option.getWithDefault("")]) let handleFocus = _ => { setInputFocused(_ => true) - setValue(.prev => { - ...prev, - isValid: None, - errorString: "", - }) + // setValue(.prev => { + // ...prev, + // isValid: None, + // errorString: "", + // }) Utils.handleOnFocusPostMessage(~targetOrigin=parentURL, ()) } let focusClass = if inputFocused || value.value->Js.String2.length > 0 { @@ -58,9 +64,9 @@ let make = ( let handleChange = ev => { let target = ev->ReactEvent.Form.target let value = target["value"] - setValue(.prev => { - ...prev, - value: value, + setValue(._ => { + isValid: Some(true), + value, errorString: "", }) } diff --git a/src/Components/Surcharge.res b/src/Components/Surcharge.res index 1a88b2712..f8821d4bf 100644 --- a/src/Components/Surcharge.res +++ b/src/Components/Surcharge.res @@ -10,7 +10,10 @@ let make = ( PaymentMethodsRecord.getPaymentMethodTypeFromList( ~list, ~paymentMethod, - ~paymentMethodType, + ~paymentMethodType=PaymentUtils.getPaymentMethodName( + ~paymentMethodType=paymentMethod, + ~paymentMethodName=paymentMethodType, + ), )->Belt.Option.getWithDefault(PaymentMethodsRecord.defaultPaymentMethodType) } @@ -21,7 +24,7 @@ let make = ( SurchargeUtils.getOneClickWalletsMessage(~list) } else { switch paymentMethodTypes.surcharge_details { - | Some(surchargeDetails) => SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails) + | Some(surchargeDetails) => SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails, ~list) | None => if paymentMethod === "card" { let creditPaymentMethodTypes = getPaymentMethodTypes("credit") @@ -41,13 +44,13 @@ let make = ( let debitCardSurcharge = debitSurchargeDetails.displayTotalSurchargeAmount if creditCardSurcharge >= debitCardSurcharge { - SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails={creditSurchargeDetails}) + SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails={creditSurchargeDetails}, ~list) } else { - SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails={debitSurchargeDetails}) + SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails={debitSurchargeDetails}, ~list) } | (None, Some(surchargeDetails)) | (Some(surchargeDetails), None) => - SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails) + SurchargeUtils.getMessage(~paymentMethod, ~surchargeDetails, ~list) | (None, None) => None } } else { diff --git a/src/Components/SurchargeUtils.res b/src/Components/SurchargeUtils.res index c136f4d52..cc077d976 100644 --- a/src/Components/SurchargeUtils.res +++ b/src/Components/SurchargeUtils.res @@ -48,12 +48,18 @@ let getSurchargeDetailsForOneClickWallets = (~list) => { }, []) } -let getMessage = (~surchargeDetails: PaymentMethodsRecord.surchargeDetails, ~paymentMethod) => { +let getMessage = ( + ~surchargeDetails: PaymentMethodsRecord.surchargeDetails, + ~paymentMethod, + ~list: PaymentMethodsRecord.list, +) => { let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) let surchargeValue = surchargeDetails.displayTotalSurchargeAmount->Js.Float.toString let getLocaleStrForSurcharge = (cardLocale, altPaymentLocale) => { - paymentMethod === "card" ? cardLocale(surchargeValue) : altPaymentLocale(surchargeValue) + paymentMethod === "card" + ? cardLocale(list.currency, surchargeValue) + : altPaymentLocale(list.currency, surchargeValue) } Some( @@ -74,7 +80,7 @@ let getOneClickWalletsMessage = (~list) => { let amount = wallet.surchargeDetails.displayTotalSurchargeAmount->Js.Float.toString let myMsg = <> - {React.string(amount)} + {React.string(`${list.currency} ${amount}`)} {React.string(`${Utils.nbsp}${localeString.on} ${wallet.name}`)} let msgToConcat = if index === 0 { diff --git a/src/Country.res b/src/Country.res index a50de08bf..59a3dc1f3 100644 --- a/src/Country.res +++ b/src/Country.res @@ -186,6 +186,234 @@ let country = [ countryName: "Cambodia", isoAlpha2: "KH", }, + { + isoAlpha3: "BHS", + timeZones: ["America/Nassau"], + countryName: "Bahamas", + isoAlpha2: "BS", + }, + { + isoAlpha3: "BEN", + timeZones: ["Africa/Porto-Novo"], + countryName: "Benin", + isoAlpha2: "BJ", + }, + { + isoAlpha3: "BDI", + timeZones: ["Africa/Bujumbura"], + countryName: "Burundi", + isoAlpha2: "BI", + }, + { + isoAlpha3: "CPV", + timeZones: ["Atlantic/Cape_Verde"], + countryName: "Cabo Verde", + isoAlpha2: "CV", + }, + { + isoAlpha3: "CYM", + timeZones: ["America/Cayman"], + countryName: "Cayman Islands", + isoAlpha2: "KY", + }, + { + isoAlpha3: "CAF", + timeZones: ["Africa/Bangui"], + countryName: "Central African Republic", + isoAlpha2: "CF", + }, + { + isoAlpha3: "TCD", + timeZones: ["Africa/Ndjamena"], + countryName: "Chad", + isoAlpha2: "TD", + }, + { + isoAlpha3: "COM", + timeZones: ["Indian/Comoro"], + countryName: "Comoros", + isoAlpha2: "KM", + }, + { + isoAlpha3: "COG", + timeZones: ["Africa/Brazzaville"], + countryName: "Congo", + isoAlpha2: "CG", + }, + { + isoAlpha3: "GAB", + timeZones: ["Africa/Libreville"], + countryName: "Gabon", + isoAlpha2: "GA", + }, + { + isoAlpha3: "GMB", + timeZones: ["Africa/Banjul"], + countryName: "Gambia", + isoAlpha2: "GM", + }, + { + isoAlpha3: "GHA", + timeZones: ["Africa/Accra"], + countryName: "Ghana", + isoAlpha2: "GH", + }, + { + isoAlpha3: "GIN", + timeZones: ["Africa/Conakry"], + countryName: "Guinea", + isoAlpha2: "GN", + }, + { + isoAlpha3: "GNB", + timeZones: ["Africa/Bissau"], + countryName: "Guinea-Bissau", + isoAlpha2: "GW", + }, + { + isoAlpha3: "GUY", + timeZones: ["America/Guyana"], + countryName: "Guyana", + isoAlpha2: "GY", + }, + { + isoAlpha3: "HMD", + timeZones: ["Indian/Kerguelen"], + countryName: "Heard Island and McDonald Islands", + isoAlpha2: "HM", + }, + { + isoAlpha3: "PRK", + timeZones: ["Asia/Pyongyang"], + countryName: "Korea (Democratic People's Republic of)", + isoAlpha2: "KP", + }, + { + isoAlpha3: "MDG", + timeZones: ["Indian/Antananarivo"], + countryName: "Madagascar", + isoAlpha2: "MG", + }, + { + isoAlpha3: "MWI", + timeZones: ["Africa/Blantyre"], + countryName: "Malawi", + isoAlpha2: "MW", + }, + { + isoAlpha3: "MRT", + timeZones: ["Africa/Nouakchott"], + countryName: "Mauritania", + isoAlpha2: "MR", + }, + { + isoAlpha3: "MUS", + timeZones: ["Indian/Mauritius"], + countryName: "Mauritius", + isoAlpha2: "MU", + }, + { + isoAlpha3: "MOZ", + timeZones: ["Africa/Maputo"], + countryName: "Mozambique", + isoAlpha2: "MZ", + }, + { + isoAlpha3: "NAM", + timeZones: ["Africa/Windhoek"], + countryName: "Namibia", + isoAlpha2: "NA", + }, + { + isoAlpha3: "PSE", + timeZones: ["Asia/Gaza", "Asia/Hebron"], + countryName: "Palestine, State of", + isoAlpha2: "PS", + }, + { + isoAlpha3: "PNG", + timeZones: ["Pacific/Port_Moresby"], + countryName: "Papua New Guinea", + isoAlpha2: "PG", + }, + { + isoAlpha3: "SHN", + timeZones: ["Atlantic/St_Helena"], + countryName: "Saint Helena, Ascension and Tristan da Cunha", + isoAlpha2: "SH", + }, + { + isoAlpha3: "SPM", + timeZones: ["America/Miquelon"], + countryName: "Saint Pierre and Miquelon", + isoAlpha2: "PM", + }, + { + isoAlpha3: "SYC", + timeZones: ["Indian/Mahe"], + countryName: "Seychelles", + isoAlpha2: "SC", + }, + { + isoAlpha3: "SLE", + timeZones: ["Africa/Freetown"], + countryName: "Sierra Leone", + isoAlpha2: "SL", + }, + { + isoAlpha3: "SLB", + timeZones: ["Pacific/Guadalcanal"], + countryName: "Solomon Islands", + isoAlpha2: "SB", + }, + { + isoAlpha3: "SSD", + timeZones: ["Africa/Juba"], + countryName: "South Sudan", + isoAlpha2: "SS", + }, + { + isoAlpha3: "SDN", + timeZones: ["Africa/Khartoum"], + countryName: "Sudan", + isoAlpha2: "SD", + }, + { + isoAlpha3: "SUR", + timeZones: ["America/Paramaribo"], + countryName: "Suriname", + isoAlpha2: "SR", + }, + { + isoAlpha3: "SWZ", + timeZones: ["Africa/Mbabane"], + countryName: "Eswatini", + isoAlpha2: "SZ", + }, + { + isoAlpha3: "TZA", + timeZones: ["Africa/Dar_es_Salaam"], + countryName: "Tanzania, United Republic of", + isoAlpha2: "TZ", + }, + { + isoAlpha3: "TGO", + timeZones: ["Africa/Lome"], + countryName: "Togo", + isoAlpha2: "TG", + }, + { + isoAlpha3: "UGA", + timeZones: ["Africa/Kampala"], + countryName: "Uganda", + isoAlpha2: "UG", + }, + { + isoAlpha3: "ZMB", + timeZones: ["Africa/Lusaka"], + countryName: "Zambia", + isoAlpha2: "ZM", + }, { isoAlpha3: "CMR", timeZones: ["Africa/Douala"], @@ -653,6 +881,261 @@ let country = [ countryName: "New Zealand", isoAlpha2: "NZ", }, + {isoAlpha3: "AND", timeZones: ["Europe/Andorra"], countryName: "Andorra", isoAlpha2: "AD"}, + { + isoAlpha3: "ATG", + timeZones: ["America/Antigua"], + countryName: "Antigua and Barbuda", + isoAlpha2: "AG", + }, + {isoAlpha3: "AIA", timeZones: ["America/Anguilla"], countryName: "Anguilla", isoAlpha2: "AI"}, + {isoAlpha3: "AGO", timeZones: ["Africa/Luanda"], countryName: "Angola", isoAlpha2: "AO"}, + {isoAlpha3: "ATA", timeZones: ["Antarctica/Palmer"], countryName: "Antarctica", isoAlpha2: "AQ"}, + { + isoAlpha3: "ASM", + timeZones: ["Pacific/Pago_Pago"], + countryName: "American Samoa", + isoAlpha2: "AS", + }, + {isoAlpha3: "ABW", timeZones: ["America/Aruba"], countryName: "Aruba", isoAlpha2: "AW"}, + { + isoAlpha3: "ALA", + timeZones: ["Europe/Mariehamn"], + countryName: "Åland Islands", + isoAlpha2: "AX", + }, + {isoAlpha3: "BRB", timeZones: ["America/Barbados"], countryName: "Barbados", isoAlpha2: "BB"}, + { + isoAlpha3: "BFA", + timeZones: ["Africa/Ouagadougou"], + countryName: "Burkina Faso", + isoAlpha2: "BF", + }, + { + isoAlpha3: "BLM", + timeZones: ["America/St_Barthelemy"], + countryName: "Saint Barthélemy", + isoAlpha2: "BL", + }, + {isoAlpha3: "BMU", timeZones: ["Atlantic/Bermuda"], countryName: "Bermuda", isoAlpha2: "BM"}, + { + isoAlpha3: "BES", + timeZones: ["America/Kralendijk"], + countryName: "Bonaire, Sint Eustatius and Saba", + isoAlpha2: "BQ", + }, + {isoAlpha3: "BTN", timeZones: ["Asia/Thimphu"], countryName: "Bhutan", isoAlpha2: "BT"}, + { + isoAlpha3: "BVT", + timeZones: ["Africa/Porto-Novo"], + countryName: "Bouvet Island", + isoAlpha2: "BV", + }, + { + isoAlpha3: "CCK", + timeZones: ["Indian/Cocos"], + countryName: "Cocos (Keeling) Islands", + isoAlpha2: "CC", + }, + { + isoAlpha3: "COK", + timeZones: ["Pacific/Rarotonga"], + countryName: "Cook Islands", + isoAlpha2: "CK", + }, + {isoAlpha3: "CUW", timeZones: ["America/Curacao"], countryName: "Curaçao", isoAlpha2: "CW"}, + { + isoAlpha3: "CXR", + timeZones: ["Indian/Christmas"], + countryName: "Christmas Island", + isoAlpha2: "CX", + }, + {isoAlpha3: "CYP", timeZones: ["Asia/Nicosia"], countryName: "Cyprus", isoAlpha2: "CY"}, + {isoAlpha3: "DMA", timeZones: ["America/Dominica"], countryName: "Dominica", isoAlpha2: "DM"}, + { + isoAlpha3: "ESH", + timeZones: ["Africa/El_Aaiun"], + countryName: "Western Sahara", + isoAlpha2: "EH", + }, + {isoAlpha3: "FJI", timeZones: ["Pacific/Fiji"], countryName: "Fiji", isoAlpha2: "FJ"}, + { + isoAlpha3: "FLK", + timeZones: ["Atlantic/Stanley"], + countryName: "Falkland Islands (Malvinas)", + isoAlpha2: "FK", + }, + { + isoAlpha3: "FSM", + timeZones: ["Pacific/Chuuk", "Pacific/Pohnpei", "Pacific/Kosrae"], + countryName: "Micronesia, Federated States of", + isoAlpha2: "FM", + }, + {isoAlpha3: "GRD", timeZones: ["America/Grenada"], countryName: "Grenada", isoAlpha2: "GD"}, + {isoAlpha3: "GUF", timeZones: ["America/Cayenne"], countryName: "French Guiana", isoAlpha2: "GF"}, + {isoAlpha3: "GGY", timeZones: ["Europe/Guernsey"], countryName: "Guernsey", isoAlpha2: "GG"}, + {isoAlpha3: "GIB", timeZones: ["Europe/Gibraltar"], countryName: "Gibraltar", isoAlpha2: "GI"}, + {isoAlpha3: "GLP", timeZones: ["America/Guadeloupe"], countryName: "Guadeloupe", isoAlpha2: "GP"}, + { + isoAlpha3: "GNQ", + timeZones: ["Africa/Malabo"], + countryName: "Equatorial Guinea", + isoAlpha2: "GQ", + }, + { + isoAlpha3: "SGS", + timeZones: ["Atlantic/South_Georgia"], + countryName: "South Georgia and the South Sandwich Islands", + isoAlpha2: "GS", + }, + {isoAlpha3: "GUM", timeZones: ["Pacific/Guam"], countryName: "Guam", isoAlpha2: "GU"}, + { + isoAlpha3: "IMN", + timeZones: ["Europe/Isle_of_Man"], + countryName: "Isle of Man", + isoAlpha2: "IM", + }, + { + isoAlpha3: "IOT", + timeZones: ["Indian/Chagos"], + countryName: "British Indian Ocean Territory", + isoAlpha2: "IO", + }, + {isoAlpha3: "JEY", timeZones: ["Europe/Jersey"], countryName: "Jersey", isoAlpha2: "JE"}, + {isoAlpha3: "KIR", timeZones: ["Pacific/Tarawa"], countryName: "Kiribati", isoAlpha2: "KI"}, + { + isoAlpha3: "KNA", + timeZones: ["America/St_Kitts"], + countryName: "Saint Kitts and Nevis", + isoAlpha2: "KN", + }, + {isoAlpha3: "LCA", timeZones: ["America/St_Lucia"], countryName: "Saint Lucia", isoAlpha2: "LC"}, + {isoAlpha3: "LBR", timeZones: ["Africa/Monrovia"], countryName: "Liberia", isoAlpha2: "LR"}, + {isoAlpha3: "LSO", timeZones: ["Africa/Maseru"], countryName: "Lesotho", isoAlpha2: "LS"}, + { + isoAlpha3: "MAF", + timeZones: ["America/Marigot"], + countryName: "Saint Martin (French part)", + isoAlpha2: "MF", + }, + { + isoAlpha3: "MHL", + timeZones: ["Pacific/Majuro"], + countryName: "Marshall Islands", + isoAlpha2: "MH", + }, + { + isoAlpha3: "MNP", + timeZones: ["Pacific/Saipan"], + countryName: "Northern Mariana Islands", + isoAlpha2: "MP", + }, + {isoAlpha3: "MTQ", timeZones: ["America/Martinique"], countryName: "Martinique", isoAlpha2: "MQ"}, + {isoAlpha3: "MSR", timeZones: ["America/Montserrat"], countryName: "Montserrat", isoAlpha2: "MS"}, + {isoAlpha3: "NCL", timeZones: ["Pacific/Noumea"], countryName: "New Caledonia", isoAlpha2: "NC"}, + {isoAlpha3: "NER", timeZones: ["Africa/Niamey"], countryName: "Niger", isoAlpha2: "NE"}, + { + isoAlpha3: "NFK", + timeZones: ["Pacific/Norfolk"], + countryName: "Norfolk Island", + isoAlpha2: "NF", + }, + {isoAlpha3: "NRU", timeZones: ["Pacific/Nauru"], countryName: "Nauru", isoAlpha2: "NR"}, + {isoAlpha3: "NIU", timeZones: ["Pacific/Niue"], countryName: "Niue", isoAlpha2: "NU"}, + {isoAlpha3: "PCN", timeZones: ["Pacific/Pitcairn"], countryName: "Pitcairn", isoAlpha2: "PN"}, + { + isoAlpha3: "PYF", + timeZones: ["Pacific/Tahiti", "Pacific/Marquesas", "Pacific/Gambier"], + countryName: "French Polynesia", + isoAlpha2: "PF", + }, + {isoAlpha3: "PLW", timeZones: ["Pacific/Palau"], countryName: "Palau", isoAlpha2: "PW"}, + { + isoAlpha3: "SJM", + timeZones: ["Arctic/Longyearbyen"], + countryName: "Svalbard and Jan Mayen", + isoAlpha2: "SJ", + }, + { + isoAlpha3: "BLM", + timeZones: ["America/St_Barthelemy"], + countryName: "Saint Barthélemy", + isoAlpha2: "BL", + }, + { + isoAlpha3: "MAF", + timeZones: ["America/Marigot"], + countryName: "Saint Martin (French part)", + isoAlpha2: "MF", + }, + {isoAlpha3: "SMR", timeZones: ["Europe/San_Marino"], countryName: "San Marino", isoAlpha2: "SM"}, + { + isoAlpha3: "STP", + timeZones: ["Africa/Sao_Tome"], + countryName: "Sao Tome and Principe", + isoAlpha2: "ST", + }, + { + isoAlpha3: "SXM", + timeZones: ["America/Lower_Princes"], + countryName: "Sint Maarten (Dutch part)", + isoAlpha2: "SX", + }, + { + isoAlpha3: "TCA", + timeZones: ["America/Grand_Turk"], + countryName: "Turks and Caicos Islands", + isoAlpha2: "TC", + }, + { + isoAlpha3: "ATF", + timeZones: ["Indian/Kerguelen"], + countryName: "French Southern Territories", + isoAlpha2: "TF", + }, + {isoAlpha3: "TKL", timeZones: ["Pacific/Fakaofo"], countryName: "Tokelau", isoAlpha2: "TK"}, + {isoAlpha3: "TLS", timeZones: ["Asia/Dili"], countryName: "Timor-Leste", isoAlpha2: "TL"}, + {isoAlpha3: "TON", timeZones: ["Pacific/Tongatapu"], countryName: "Tonga", isoAlpha2: "TO"}, + {isoAlpha3: "TUV", timeZones: ["Pacific/Funafuti"], countryName: "Tuvalu", isoAlpha2: "TV"}, + { + isoAlpha3: "UMI", + timeZones: ["Pacific/Midway", "Pacific/Wake"], + countryName: "United States Minor Outlying Islands", + isoAlpha2: "UM", + }, + { + isoAlpha3: "VAT", + timeZones: ["Europe/Vatican"], + countryName: "Holy See (Vatican City State)", + isoAlpha2: "VA", + }, + { + isoAlpha3: "VCT", + timeZones: ["America/St_Vincent"], + countryName: "Saint Vincent and the Grenadines", + isoAlpha2: "VC", + }, + { + isoAlpha3: "VGB", + timeZones: ["America/Tortola"], + countryName: "Virgin Islands, British", + isoAlpha2: "VG", + }, + { + isoAlpha3: "VIR", + timeZones: ["America/St_Thomas"], + countryName: "Virgin Islands, U.S.", + isoAlpha2: "VI", + }, + {isoAlpha3: "VUT", timeZones: ["Pacific/Efate"], countryName: "Vanuatu", isoAlpha2: "VU"}, + { + isoAlpha3: "WLF", + timeZones: ["Pacific/Wallis"], + countryName: "Wallis and Futuna", + isoAlpha2: "WF", + }, + {isoAlpha3: "WSM", timeZones: ["Pacific/Apia"], countryName: "Samoa", isoAlpha2: "WS"}, + {isoAlpha3: "YT", timeZones: ["Indian/Mayotte"], countryName: "Mayotte", isoAlpha2: "YT"}, { isoAlpha3: "NIC", timeZones: ["America/Managua"], diff --git a/src/GlobalVars.res b/src/GlobalVars.res index 6320c4e18..0580a11fb 100644 --- a/src/GlobalVars.res +++ b/src/GlobalVars.res @@ -9,6 +9,7 @@ @val external sentryScriptUrl: string = "sentryScriptUrl" @val external enableLogging: bool = "enableLogging" @val external loggingLevelStr: string = "loggingLevel" +@val external maxLogsPushedPerEventName: int = "maxLogsPushedPerEventName" let targetOrigin: string = "*" let isInteg = sdkUrl === "https://dev.hyperswitch.io" let isSandbox = sdkUrl === "https://beta.hyperswitch.io" diff --git a/src/Hooks/CommonHooks.res b/src/Hooks/CommonHooks.res index 99ce82640..b5fd1f564 100644 --- a/src/Hooks/CommonHooks.res +++ b/src/Hooks/CommonHooks.res @@ -14,6 +14,7 @@ type keys = { iframeId: string, parentURL: string, sdkHandleConfirmPayment: bool, + sdkHandleOneClickConfirmPayment: bool, } @val @scope("document") external querySelector: string => Js.Nullable.t = "querySelector" @@ -67,7 +68,7 @@ let useScript = (src: string) => { let updateKeys = (dict, keyPair, setKeys) => { let (key, value) = keyPair let valueStr = value->Js.Json.decodeString->Belt.Option.getWithDefault("") - let valueBool = value->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + let valueBool = default => value->Js.Json.decodeBoolean->Belt.Option.getWithDefault(default) if dict->Utils.getDictIsSome(key) { switch key { | "iframeId" => @@ -88,7 +89,12 @@ let updateKeys = (dict, keyPair, setKeys) => { | "sdkHandleConfirmPayment" => setKeys(.prev => { ...prev, - sdkHandleConfirmPayment: dict->Utils.getBool(key, valueBool), + sdkHandleConfirmPayment: dict->Utils.getBool(key, valueBool(false)), + }) + | "sdkHandleOneClickConfirmPayment" => + setKeys(.prev => { + ...prev, + sdkHandleOneClickConfirmPayment: dict->Utils.getBool(key, valueBool(true)), }) | _ => () } @@ -100,4 +106,5 @@ let defaultkeys = { iframeId: "", parentURL: "*", sdkHandleConfirmPayment: false, + sdkHandleOneClickConfirmPayment: true, } diff --git a/src/LoaderController.res b/src/LoaderController.res index 955211b86..efe963d22 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -125,6 +125,14 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { handlePostMessage([("iframeMounted", true->Js.Json.boolean)]) handlePostMessage([("applePayMounted", true->Js.Json.boolean)]) logger.setLogInitiated() + switch paymentlist { + | Loaded(_) + | LoadError => () + | _ => + setList(._ => + showCardFormByDefault && Utils.checkPriorityList(paymentMethodOrder) ? SemiLoaded : Loading + ) + } Window.addEventListener("click", ev => handleOnClickPostMessage(~targetOrigin=keys.parentURL, ev) ) @@ -246,12 +254,13 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { } } - logger.setLogInfo(~value="paymentElementCreate", ~eventName=APP_RENDERED, ()) + logger.setLogInfo(~value=Window.href, ~eventName=APP_RENDERED, ()) [ ("iframeId", "no-element"->Js.Json.string), ("publishableKey", ""->Js.Json.string), ("parentURL", "*"->Js.Json.string), ("sdkHandleConfirmPayment", false->Js.Json.boolean), + ("sdkHandleOneClickConfirmPayment", true->Js.Json.boolean), ]->Js.Array2.forEach(keyPair => { dict->CommonHooks.updateKeys(keyPair, setKeys) }) @@ -332,10 +341,16 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { if dict->getDictIsSome("paymentMethodList") { let list = dict->getJsonObjectFromDict("paymentMethodList") list == Js.Dict.empty()->Js.Json.object_ - ? setList(._ => LoadError(Js.Dict.empty()->Js.Json.object_)) + ? setList(._ => LoadError) : switch list->Utils.getDictFromJson->Js.Dict.get("error") { - | Some(err) => setList(._ => LoadError(err)) - | None => setList(._ => Loaded(list)) + | Some(_) => setList(._ => LoadError) + | None => + let isNonEmptyPaymentMethodList = + list + ->Utils.getDictFromJson + ->Utils.getArray("payment_methods") + ->Js.Array2.length > 0 + setList(._ => isNonEmptyPaymentMethodList ? Loaded(list) : LoadError) } } if dict->getDictIsSome("customerPaymentMethods") { @@ -358,18 +373,6 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { handleMessage(handleFun, "Error in parsing sent Data") }, (showCardFormByDefault, paymentMethodOrder)) - React.useEffect1(() => { - switch paymentlist { - | Loaded(_) - | LoadError(_) => () - | _ => - setList(._ => - showCardFormByDefault && Utils.checkPriorityList(paymentMethodOrder) ? SemiLoaded : Loading - ) - } - None - }, [paymentlist]) - let observer = ResizeObserver.newResizerObserver(entries => { entries ->Js.Array2.map(item => { diff --git a/src/LocaleString.res b/src/LocaleString.res index c3719894f..c6b03c901 100644 --- a/src/LocaleString.res +++ b/src/LocaleString.res @@ -11,15 +11,23 @@ type localeStrings = { sortCodeText: string, cvcTextLabel: string, emailLabel: string, + emailEmptyText: string, + emailInvalidText: string, accountNumberText: string, fullNameLabel: string, line1Label: string, line1Placeholder: string, + line1EmptyText: string, line2Label: string, line2Placeholder: string, + line2EmptyText: string, cityLabel: string, + cityEmptyText: string, postalCodeLabel: string, + postalCodeEmptyText: string, + postalCodeInvalidText: string, stateLabel: string, + stateEmptyText: string, fullNamePlaceholder: string, countryLabel: string, currencyLabel: string, @@ -42,14 +50,17 @@ type localeStrings = { enterFieldsText: string, enterValidDetailsText: string, card: string, - surchargeMsgAmount: string => React.element, - surchargeMsgAmountForCard: string => React.element, + surchargeMsgAmount: (string, string) => React.element, + surchargeMsgAmountForCard: (string, string) => React.element, surchargeMsgAmountForOneClickWallets: string, billingNameLabel: string, billingNamePlaceholder: string, cardHolderName: string, on: string, \"and": string, + nameEmptyText: string => string, + completeNameEmptyText: string => string, + billingDetailsText: string, } let defaultLocale = { @@ -66,13 +77,21 @@ let defaultLocale = { accountNumberText: "Account Number", cvcTextLabel: "CVC", emailLabel: "Email", + emailEmptyText: "Email cannot be empty", + emailInvalidText: "Invalid email address", line1Label: "Address line 1", line1Placeholder: "Street address", + line1EmptyText: "Address line 1 cannot be empty", line2Label: "Address line 2", line2Placeholder: "Apt., unit number, etc (optional)", + line2EmptyText: "Address line 2 cannot be empty", cityLabel: "City", + cityEmptyText: "City cannot be empty", postalCodeLabel: "Postal Code", + postalCodeEmptyText: "Postal code cannot be empty", + postalCodeInvalidText: "Invalid postal code", stateLabel: "State", + stateEmptyText: "State cannot be empty", fullNameLabel: "Full name", fullNamePlaceholder: "First and last name", countryLabel: "Country", @@ -99,14 +118,14 @@ let defaultLocale = { enterFieldsText: "Please enter all fields", enterValidDetailsText: "Please enter valid details", card: "Card", - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`A surcharge amount of${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string({`${Utils.nbsp}will be applied for this transaction`})} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`A surcharge amount of upto${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}will be applied for this transaction`)} , surchargeMsgAmountForOneClickWallets: "Additional fee applicable", @@ -115,6 +134,9 @@ let defaultLocale = { cardHolderName: "Card Holder Name", on: "on", \"and": "and", + nameEmptyText: str => `Please provide your ${str}`, + completeNameEmptyText: str => `Please provide your complete ${str}`, + billingDetailsText: "Billing Details", } type locale = {localeStrings: array} @@ -133,13 +155,21 @@ let localeStrings = [ cvcTextLabel: "CVC", line1Label: "Address line 1", line1Placeholder: "Street address", + line1EmptyText: "Address line 1 cannot be empty", line2Label: "Address line 2", line2Placeholder: "Apt., unit number, etc (optional)", + line2EmptyText: "Address line 2 cannot be empty", cityLabel: "City", + cityEmptyText: "City cannot be empty", postalCodeLabel: "Postal Code", + postalCodeEmptyText: "Postal code cannot be empty", + postalCodeInvalidText: "Invalid postal code", stateLabel: "State", + stateEmptyText: "State cannot be empty", accountNumberText: "Account Number", emailLabel: "Email", + emailEmptyText: "Email cannot be empty", + emailInvalidText: "Invalid email address", fullNameLabel: "Full name", fullNamePlaceholder: "First and last name", countryLabel: "Country", @@ -166,14 +196,14 @@ let localeStrings = [ enterFieldsText: "Please enter all fields", enterValidDetailsText: "Please enter valid details", card: "Card", - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`A surcharge amount of${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string({`${Utils.nbsp}will be applied for this transaction`})} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`A surcharge amount of upto${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}will be applied for this transaction`)} , surchargeMsgAmountForOneClickWallets: "Additional fee applicable", @@ -182,6 +212,9 @@ let localeStrings = [ cardHolderName: "Card Holder Name", on: "on", \"and": "and", + nameEmptyText: str => `Please provide your ${str}`, + completeNameEmptyText: str => `Please provide your complete ${str}`, + billingDetailsText: "Billing Details", }, { locale: "he", @@ -197,13 +230,21 @@ let localeStrings = [ cvcTextLabel: `קוד בגב הכרטיס`, line1Label: `כתובת - שורה 1`, line1Placeholder: `כתובת רחוב`, + line1EmptyText: `שורת כתובת 1 לא יכולה להיות ריקה`, line2Label: `כתובת - שורה 2`, line2Placeholder: `דירה, יחידה, וכדומה (אופציונלי)`, + line2EmptyText: `שורת כתובת 2 לא יכולה להיות ריקה`, cityLabel: `עיר`, + cityEmptyText: `עיר לא יכולה להיות ריקה`, postalCodeLabel: `מיקוד`, + postalCodeEmptyText: `מיקוד לא יכול להיות ריק`, + postalCodeInvalidText: `מיקוד לא חוקי`, stateLabel: `מדינה`, + stateEmptyText: `המדינה לא יכולה להיות ריקה`, accountNumberText: `מספר חשבון`, emailLabel: `אימייל`, + emailEmptyText: `אימייל לא יכול להיות ריק`, + emailInvalidText: `כתובת אימייל לא חוקית`, fullNameLabel: `שם מלא`, fullNamePlaceholder: `שם פרטי ושם משפחה`, countryLabel: `מדינה`, @@ -230,14 +271,14 @@ let localeStrings = [ enterFieldsText: `יש להזין את כל השדות`, enterValidDetailsText: `יש להזין פרטים תקינים`, card: `כרטיס`, - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`סכום היטל של${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}יוחל עבור עסקה זו`)} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`סכום היטל של עד${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}יחול עבור עסקה זו`)} , surchargeMsgAmountForOneClickWallets: `תשלום נוסף חל`, @@ -246,6 +287,9 @@ let localeStrings = [ cardHolderName: `שם בעל הכרטיס`, on: `עַל`, \"and": `ו`, + nameEmptyText: str => `אנא ספק את שלך ${str}`, + completeNameEmptyText: str => `אנא ספק את המלא שלך ${str}`, + billingDetailsText: `פרטי תשלום`, }, { locale: `fr`, @@ -261,13 +305,21 @@ let localeStrings = [ cvcTextLabel: `Code CVC`, line1Label: `Adresse - Ligne 1`, line1Placeholder: `Adresse de rue`, + line1EmptyText: `La ligne d'adresse 1 ne peut pas être vide`, line2Label: `Adresse - Ligne 2`, line2Placeholder: `Appartement, numéro d'unité, etc (facultatif)`, + line2EmptyText: `La ligne d'adresse 2 ne peut pas être vide`, cityLabel: `Ville`, + cityEmptyText: `La ville ne peut pas être vide`, postalCodeLabel: `Code postal`, + postalCodeEmptyText: `Le code postal ne peut pas être vide`, + postalCodeInvalidText: `Code postal invalide`, stateLabel: `État`, + stateEmptyText: `L'état ne peut pas être vide`, accountNumberText: `Numéro de compte`, emailLabel: `E-mail`, + emailEmptyText: `L'e-mail ne peut pas être vide`, + emailInvalidText: "Adresse e-mail invalide", fullNameLabel: `Nom complet`, fullNamePlaceholder: `Prénom et nom de famille`, countryLabel: `Pays`, @@ -294,14 +346,14 @@ let localeStrings = [ enterFieldsText: `Veuillez saisir tous les champs`, enterValidDetailsText: `Veuillez saisir des informations valides`, card: `Carte`, - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`Un montant supplémentaire d'${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}sera appliqué pour cette transaction`)} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`Un montant supplémentaire allant jusqu'à${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}sera appliqué pour cette transaction.`)} , surchargeMsgAmountForOneClickWallets: `Frais supplémentaires applicables`, @@ -310,6 +362,9 @@ let localeStrings = [ cardHolderName: `Nom du titulaire`, on: `sur`, \"and": `et`, + nameEmptyText: str => `Veuillez fournir votre ${str}`, + completeNameEmptyText: str => `Veuillez fournir votre complet ${str}`, + billingDetailsText: "Détails de la facturation", }, { locale: "en-GB", @@ -324,13 +379,21 @@ let localeStrings = [ sortCodeText: "Sort Code", cvcTextLabel: "CVC", emailLabel: "Email", + emailEmptyText: "Email cannot be empty", + emailInvalidText: "Invalid email address", line1Label: "Address line 1", line1Placeholder: "Street address", + line1EmptyText: "Address line 1 cannot be empty", line2Label: "Address line 2", line2Placeholder: "Apt., unit number, etc (optional)", + line2EmptyText: "Address line 2 cannot be empty", cityLabel: "City", + cityEmptyText: "City cannot be empty", postalCodeLabel: "Postal Code", + postalCodeEmptyText: "Postal code cannot be empty", + postalCodeInvalidText: "Invalid postal code", stateLabel: "State", + stateEmptyText: "State cannot be empty", accountNumberText: "Account Number", fullNameLabel: "Full name", fullNamePlaceholder: "First and last name", @@ -358,14 +421,14 @@ let localeStrings = [ enterFieldsText: "Please enter all fields", enterValidDetailsText: "Please enter valid details", card: "Card", - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`A surcharge amount of${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}will be applied for this transaction`)} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`A surcharge amount of upto${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}will be applied for this transaction`)} , surchargeMsgAmountForOneClickWallets: "Additional fee applicable", @@ -374,6 +437,9 @@ let localeStrings = [ cardHolderName: "Card Holder Name", on: "on", \"and": "and", + nameEmptyText: str => `Please provide your ${str}`, + completeNameEmptyText: str => `Please provide your complete ${str}`, + billingDetailsText: "Billing Details", }, { locale: "ar", @@ -389,14 +455,22 @@ let localeStrings = [ accountNumberText: `رقم حساب`, cvcTextLabel: `رمز الحماية`, emailLabel: `البريد الإلكتروني`, + emailEmptyText: `لا يمكن أن يكون البريد الإلكتروني فارغًا`, + emailInvalidText: `عنوان البريد الإلكتروني غير صالح`, fullNameLabel: `الاسم الكامل`, line1Label: `العنوان سطر 1`, line1Placeholder: `.عنوان الشارع`, + line1EmptyText: `لا يمكن أن يكون سطر العنوان 1 فارغًا`, line2Label: `سطر العنوان 2`, line2Placeholder: `مناسب ، رقم الوحدة ، إلخ (اختياري)`, + line2EmptyText: `لا يمكن أن يكون سطر العنوان 2 فارغًا`, postalCodeLabel: `رمز بريدي`, + postalCodeEmptyText: `لا يمكن أن يكون الرمز البريدي فارغًا`, + postalCodeInvalidText: `الرمز البريدي غير صالح`, stateLabel: `ولاية`, + stateEmptyText: `لا يمكن أن تكون الحالة فارغة`, cityLabel: `مدينة`, + cityEmptyText: `لا يمكن أن تكون المدينة فارغة`, fullNamePlaceholder: `الاسم الأول والاسم الأخير`, countryLabel: `دولة`, currencyLabel: `عملة`, @@ -422,14 +496,14 @@ let localeStrings = [ enterFieldsText: `الرجاء إدخال كافة الحقول`, enterValidDetailsText: `الرجاء إدخال تفاصيل صالحة`, card: `بطاقة`, - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`سيتم تطبيق مبلغ إضافي من${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}على هذه المعاملة`)} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`سيتم تطبيق مبلغ إضافي يصل إلى${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}على هذه المعاملة`)} , surchargeMsgAmountForOneClickWallets: `رسوم إضافية قابلة للتطبيق`, @@ -438,6 +512,9 @@ let localeStrings = [ cardHolderName: `إسم صاحب البطاقة`, on: `على`, \"and": `و`, + nameEmptyText: str => `يرجى تقديم الخاص بك ${str}`, + completeNameEmptyText: str => `يرجى تقديم كامل الخاص بك ${str}`, + billingDetailsText: `تفاصيل الفاتورة`, }, { locale: "ja", @@ -453,16 +530,24 @@ let localeStrings = [ cvcTextLabel: `セキュリティコード`, accountNumberText: `口座番号`, emailLabel: `Eメール`, + emailEmptyText: `電子メールを空にすることはできません`, + emailInvalidText: `無効なメールアドレス`, fullNameLabel: `フルネーム`, fullNamePlaceholder: `名前と苗字`, line1Label: `住所1`, line1Placeholder: `住所`, + line1EmptyText: `住所行 1 を空にすることはできません`, line2Label: `住所2`, postalCodeLabel: `郵便番号`, + postalCodeEmptyText: `郵便番号を空白にすることはできません`, + postalCodeInvalidText: `郵便番号が無効です`, stateLabel: `州`, + stateEmptyText: `状態を空にすることはできません`, cityLabel: `街`, line2Placeholder: `アパート、ユニット番号など(任意)`, + line2EmptyText: `住所行 2 を空にすることはできません`, countryLabel: `国`, + cityEmptyText: `都市を空にすることはできません`, currencyLabel: "通貨", bankLabel: `バンクを選択`, redirectText: `注文を送信すると、安全に購入を完了するためにリダイレクトされます。`, @@ -486,14 +571,14 @@ let localeStrings = [ enterFieldsText: `すべてのフィールドに入力してください`, enterValidDetailsText: `有効な詳細を入力してください`, card: `カード`, - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`この取引には${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}の追加料金が適用されます`)} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`この取引には${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}までの追加料金が適用されます`)} , surchargeMsgAmountForOneClickWallets: `追加料金が適用されます`, @@ -502,6 +587,9 @@ let localeStrings = [ cardHolderName: `クレジットカード名義人氏名`, on: `の上`, \"and": `そして`, + nameEmptyText: str => `あなたの情報を提供してください ${str}`, + completeNameEmptyText: str => `完全な情報を提供してください ${str}`, + billingDetailsText: `支払明細`, }, { locale: "de", @@ -517,13 +605,21 @@ let localeStrings = [ cvcTextLabel: `CVC`, line1Label: `Adresszeile 1`, line1Placeholder: `Adresse`, + line1EmptyText: "Adresszeile 1 darf nicht leer sein", line2Label: `Adresszeile 2`, line2Placeholder: `Wohnung, Einheitennummer usw. (optional)`, + line2EmptyText: `Adresszeile 2 darf nicht leer sein`, cityLabel: `Stadt`, + cityEmptyText: `Die Stadt darf nicht leer sein`, postalCodeLabel: `Postleitzahl`, + postalCodeEmptyText: `Die Postleitzahl darf nicht leer sein`, + postalCodeInvalidText: `Ungültige Postleitzahl`, stateLabel: `Zustand`, + stateEmptyText: `Der Status darf nicht leer sein`, accountNumberText: `Accountnummer`, emailLabel: `Email`, + emailEmptyText: `E-Mail darf nicht leer sein`, + emailInvalidText: `Ungültige E-Mail-Adresse`, fullNameLabel: `Vollständiger Name`, fullNamePlaceholder: `Vor-und Nachname`, countryLabel: `Land`, @@ -550,14 +646,14 @@ let localeStrings = [ enterFieldsText: `Bitte füllen Sie alle Felder aus`, enterValidDetailsText: `Bitte geben Sie gültige Daten ein`, card: `Karte`, - surchargeMsgAmount: str => <> + surchargeMsgAmount: (currency, str) => <> {React.string(`Für diese Transaktion wird ein Zuschlag in Höhe von${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}erhoben`)} , - surchargeMsgAmountForCard: str => <> + surchargeMsgAmountForCard: (currency, str) => <> {React.string(`Für diese Transaktion wird ein Zuschlagsbetrag von bis zu${Utils.nbsp}`)} - {React.string(str)} + {React.string(`${currency} ${str}`)} {React.string(`${Utils.nbsp}erhoben`)} , surchargeMsgAmountForOneClickWallets: `Es fällt eine zusätzliche Gebühr an`, @@ -566,5 +662,8 @@ let localeStrings = [ cardHolderName: `Name des Karteninhabers`, on: `An`, \"and": `Und`, + nameEmptyText: str => `Bitte geben Sie Ihre an ${str}`, + completeNameEmptyText: str => `Bitte geben Sie Ihr vollständiges Formular an ${str}`, + billingDetailsText: `Rechnungsdetails`, }, ] diff --git a/src/PaymentElement.res b/src/PaymentElement.res index 3d189bccf..3580221d0 100644 --- a/src/PaymentElement.res +++ b/src/PaymentElement.res @@ -20,7 +20,6 @@ let make = ( ) let isApplePayReady = Recoil.useRecoilValueFromAtom(isApplePayReady) let isGooglePayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady) - let pmList = Recoil.useRecoilValueFromAtom(list) let methodslist = Recoil.useRecoilValueFromAtom(list) let paymentOrder = paymentMethodOrder->Utils.getOptionalArr->Utils.removeDuplicate let (sessions, setSessions) = React.useState(_ => Js.Dict.empty()->Js.Json.object_) @@ -91,6 +90,7 @@ let make = ( ) } : () + | LoadError | SemiLoaded => setPaymentOptions(_ => showCardFormByDefault && Utils.checkPriorityList(paymentMethodOrder) ? ["card"] : [] @@ -143,7 +143,7 @@ let make = ( ? "" : switch methodslist { | SemiLoaded - | LoadError(_) => + | LoadError => showCardFormByDefault && Utils.checkPriorityList(paymentMethodOrder) ? "card" : "" | Loaded(_) => paymentOptions->Js.Array2.includes(selectedOption) && showCardFormByDefault @@ -319,8 +319,8 @@ let make = (
- {switch pmList { - | LoadError(_) => React.null + {switch methodslist { + | LoadError => React.null | _ => Js.Array2.length == 0 && walletOptions->Js.Array2.length == 0}> diff --git a/src/Payments/ApplePay.res b/src/Payments/ApplePay.res index ed1f5a008..ed310c8ad 100644 --- a/src/Payments/ApplePay.res +++ b/src/Payments/ApplePay.res @@ -7,7 +7,9 @@ let make = ( ~walletOptions: array, ) => { let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) - let {publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) + let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom( + RecoilAtoms.keys, + ) let isApplePayReady = Recoil.useRecoilValueFromAtom(RecoilAtoms.isApplePayReady) let setIsShowOrPayUsing = Recoil.useSetRecoilState(RecoilAtoms.isShowOrPayUsing) let (showApplePay, setShowApplePay) = React.useState(() => false) @@ -234,33 +236,43 @@ let make = ( (), ) setApplePayClicked(_ => true) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + ->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + if isInvokeSDKFlow { + let isDelayedSessionToken = + sessionObj + ->Belt.Option.getWithDefault(Js.Json.null) + ->Js.Json.decodeObject + ->Belt.Option.getWithDefault(Js.Dict.empty()) + ->Js.Dict.get("delayed_session_token") + ->Belt.Option.getWithDefault(Js.Json.null) + ->Js.Json.decodeBoolean + ->Belt.Option.getWithDefault(false) - if isInvokeSDKFlow { - let isDelayedSessionToken = - sessionObj - ->Belt.Option.getWithDefault(Js.Json.null) - ->Js.Json.decodeObject - ->Belt.Option.getWithDefault(Js.Dict.empty()) - ->Js.Dict.get("delayed_session_token") - ->Belt.Option.getWithDefault(Js.Json.null) - ->Js.Json.decodeBoolean - ->Belt.Option.getWithDefault(false) - - if isDelayedSessionToken { - setShowApplePayLoader(_ => true) - let bodyDict = PaymentBody.applePayThirdPartySdkBody(~connectors) - processPayment(bodyDict) + if isDelayedSessionToken { + setShowApplePayLoader(_ => true) + let bodyDict = PaymentBody.applePayThirdPartySdkBody(~connectors) + processPayment(bodyDict) + } else { + let message = [("applePayButtonClicked", true->Js.Json.boolean)] + Utils.handlePostMessage(message) + } + } else { + let bodyDict = PaymentBody.applePayRedirectBody(~connectors) + processPayment(bodyDict) + } } else { - let message = [("applePayButtonClicked", true->Js.Json.boolean)] - Utils.handlePostMessage(message) + setApplePayClicked(_ => false) } - } else { - let bodyDict = PaymentBody.applePayRedirectBody(~connectors) - processPayment(bodyDict) - } + resolve() + }) + ->ignore } - React.useEffect2(() => { + React.useEffect3(() => { let handleApplePayMessages = (ev: Window.event) => { let json = try { ev.data->Js.Json.parseExn @@ -279,6 +291,9 @@ let make = ( processPayment(bodyDict) } else if dict->Js.Dict.get("showApplePayButton")->Belt.Option.isSome { setApplePayClicked(_ => false) + if !isWallet { + postFailedSubmitResponse(~errortype="server_error", ~message="Something went wrong") + } } else if dict->Js.Dict.get("applePaySyncPayment")->Belt.Option.isSome { syncPayment() } @@ -293,7 +308,7 @@ let make = ( Window.removeEventListener("message", handleApplePayMessages) }, ) - }, (isInvokeSDKFlow, requiredFieldsBody)) + }, (isInvokeSDKFlow, requiredFieldsBody, isWallet)) React.useEffect4(() => { if ( diff --git a/src/Payments/BankDebitModal.res b/src/Payments/BankDebitModal.res index 702f4fd6d..732de6225 100644 --- a/src/Payments/BankDebitModal.res +++ b/src/Payments/BankDebitModal.res @@ -91,7 +91,8 @@ module AccountNumberCard = {
{React.string("000123456789")}
-
{React.string("1234")}
+ +
{React.string("1234")}
@@ -112,7 +113,7 @@ let make = (~setModalData) => { let (sortCode, setSortCode) = React.useState(_ => "") let (isRoutingValid, setIsRoutingValid) = React.useState(_ => None) let (routingError, setRoutingError) = React.useState(_ => "") - let {themeObj, config} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) + let {themeObj, config, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) let (accountType, setAccountType) = React.useState(() => "Savings") let (openModal, setOpenModal) = React.useState(_ => false) @@ -188,7 +189,7 @@ let make = (~setModalData) => {
- {React.string("Billing Details")} + {React.string(localeString.billingDetailsText)}
{ active=submitActive onclick={_ => { setModalData(_ => Some({ - routingNumber: routingNumber, + routingNumber, accountNumber: accountNum, - accountHolderName: accountHolderName, + accountHolderName, accountType: accountType->Js.String2.toLowerCase, - iban: iban, - sortCode: sortCode, + iban, + sortCode, })) Modal.close(setOpenModal) }} diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res index 8dfef2d89..a129c3551 100644 --- a/src/Payments/CardPayment.res +++ b/src/Payments/CardPayment.res @@ -147,7 +147,7 @@ let make = ( ~isCvcValidValue, ) - let submitCallback = React.useCallback((ev: Window.event) => { + let submitCallback = React.useCallback5((ev: Window.event) => { let json = ev.data->Js.Json.parseExn let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper let (month, year) = CardUtils.getExpiryDates(cardExpiry) @@ -224,7 +224,7 @@ let make = ( } } } - }) + }, (areRequiredFieldsValid, requiredFieldsBody, empty, complete, isSaveCardsChecked)) submitPaymentData(submitCallback) let paymentMethod = isBancontact ? "bank_redirect" : "card" diff --git a/src/Payments/GPay.res b/src/Payments/GPay.res index a7219f339..d69fea639 100644 --- a/src/Payments/GPay.res +++ b/src/Payments/GPay.res @@ -14,7 +14,7 @@ let make = ( let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Js.Dict.empty()) let (loggerState, _setLoggerState) = Recoil.useRecoilState(loggerAtom) let {iframeId} = Recoil.useRecoilValueFromAtom(keys) - let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) + let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) let {localeString} = Recoil.useRecoilValueFromAtom(configAtom) let options = Recoil.useRecoilValueFromAtom(optionAtom) let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Gpay) @@ -137,28 +137,35 @@ let make = ( ~paymentMethod="GOOGLE_PAY", (), ) - if isInvokeSDKFlow { - if isDelayedSessionToken { - let bodyDict = PaymentBody.gPayThirdPartySdkBody(~connectors) - processPayment(bodyDict) - } else { - handlePostMessage([ - ("fullscreen", true->Js.Json.boolean), - ("param", "paymentloader"->Js.Json.string), - ("iframeId", iframeId->Js.Json.string), - ]) - options.readOnly ? () : handlePostMessage([("GpayClicked", true->Js.Json.boolean)]) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment)->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + if isInvokeSDKFlow { + if isDelayedSessionToken { + let bodyDict = PaymentBody.gPayThirdPartySdkBody(~connectors) + processPayment(bodyDict) + } else { + handlePostMessage([ + ("fullscreen", true->Js.Json.boolean), + ("param", "paymentloader"->Js.Json.string), + ("iframeId", iframeId->Js.Json.string), + ]) + options.readOnly ? () : handlePostMessage([("GpayClicked", true->Js.Json.boolean)]) + } + } else { + let bodyDict = PaymentBody.gpayRedirectBody(~connectors) + processPayment(bodyDict) + } + loggerState.setLogInfo( + ~value="", + ~eventName=PAYMENT_DATA_FILLED, + ~paymentMethod="GOOGLE_PAY", + (), + ) } - } else { - let bodyDict = PaymentBody.gpayRedirectBody(~connectors) - processPayment(bodyDict) - } - loggerState.setLogInfo( - ~value="", - ~eventName=PAYMENT_DATA_FILLED, - ~paymentMethod="GOOGLE_PAY", - (), - ) + resolve() + }) } let buttonStyle = { diff --git a/src/Payments/PayPal.res b/src/Payments/PayPal.res index ffe0abbb6..041f7b00f 100644 --- a/src/Payments/PayPal.res +++ b/src/Payments/PayPal.res @@ -14,7 +14,7 @@ let payPalIcon = let make = (~list: PaymentMethodsRecord.list) => { let loggerState = Recoil.useRecoilValueFromAtom(loggerAtom) let (paypalClicked, setPaypalClicked) = React.useState(_ => false) - let {publishableKey} = Recoil.useRecoilValueFromAtom(keys) + let {publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom(keys) let options = Recoil.useRecoilValueFromAtom(optionAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered) let (_, _, labelType) = options.wallets.style.type_ @@ -38,17 +38,28 @@ let make = (~list: PaymentMethodsRecord.list) => { (), ) setPaypalClicked(_ => true) - let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(Redirect))) - let body = PaymentBody.paypalRedirectionBody(~connectors) - intent( - ~bodyArr=body, - ~confirmParam={ - return_url: options.wallets.walletReturnUrl, - publishableKey, - }, - ~handleUserError=true, - (), - ) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + ->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(Redirect))) + let body = PaymentBody.paypalRedirectionBody(~connectors) + intent( + ~bodyArr=body, + ~confirmParam={ + return_url: options.wallets.walletReturnUrl, + publishableKey, + }, + ~handleUserError=true, + (), + ) + } else { + setPaypalClicked(_ => false) + } + resolve() + }) + ->ignore } React.useEffect0(() => { diff --git a/src/Payments/PaymentMethodsRecord.res b/src/Payments/PaymentMethodsRecord.res index e8c85c8c5..6d6104f40 100644 --- a/src/Payments/PaymentMethodsRecord.res +++ b/src/Payments/PaymentMethodsRecord.res @@ -187,7 +187,7 @@ let paymentMethodsFields = [ { paymentMethodName: "sofort", icon: Some(icon("sofort", ~size=19)), - fields: [FullName, Email, Country, InfoElement], + fields: [InfoElement], displayName: "Sofort", miniIcon: None, }, @@ -230,7 +230,7 @@ let paymentMethodsFields = [ paymentMethodName: "eps", icon: Some(icon("eps", ~size=19, ~width=25)), displayName: "EPS", - fields: [Bank, FullName, InfoElement], + fields: [InfoElement], miniIcon: None, }, { @@ -321,7 +321,7 @@ let paymentMethodsFields = [ paymentMethodName: "ideal", icon: Some(icon("ideal", ~size=19, ~width=25)), displayName: "iDEAL", - fields: [Bank, FullName, InfoElement], + fields: [InfoElement], miniIcon: None, }, { @@ -466,10 +466,10 @@ let paymentMethodsFields = [ }, { paymentMethodName: "open_banking_uk", - icon: Some(icon("open_banking", ~size=19, ~width=50)), - displayName: "Open Banking", + icon: Some(icon("bank", ~size=19)), + displayName: "Pay by Bank", fields: [InfoElement], - miniIcon: Some(icon("open_banking", ~size=19)), + miniIcon: Some(icon("bank", ~size=19)), }, { paymentMethodName: "evoucher", @@ -478,6 +478,13 @@ let paymentMethodsFields = [ fields: [InfoElement], miniIcon: Some(icon("cashtocode", ~size=19)), }, + { + paymentMethodName: "pix_transfer", + fields: [InfoElement], + icon: Some(icon("pix", ~size=26, ~width=40)), + displayName: "Pix", + miniIcon: None, + }, ] type required_fields = { @@ -522,7 +529,12 @@ let getPaymentMethodsFieldTypeFromDict = dict => { switch options->Belt.Array.get(0)->Belt.Option.getWithDefault("") { | "" => None | "ALL" => AddressCountry(Country.country->Js.Array2.map(item => item.countryName)) - | _ => AddressCountry(options) + | _ => + AddressCountry( + Country.country + ->Js.Array2.filter(item => options->Js.Array2.includes(item.isoAlpha2)) + ->Js.Array2.map(item => item.countryName), + ) } } | _ => None @@ -557,6 +569,10 @@ let dynamicFieldsEnabledPaymentMethods = [ "apple_pay", "bancontact_card", "open_banking_uk", + "eps", + "ideal", + "sofort", + "pix_transfer", ] let getIsBillingField = requiredFieldType => { @@ -686,6 +702,7 @@ type mandate = { type list = { redirect_url: string, + currency: string, payment_methods: array, mandate_payment: option, payment_type: string, @@ -706,6 +723,7 @@ let defaultPaymentMethodType = { let defaultList = { redirect_url: "", + currency: "", payment_methods: [], mandate_payment: None, payment_type: "", @@ -904,6 +922,7 @@ let getMandate = (dict, str) => { let itemToObjMapper = dict => { { redirect_url: getString(dict, "redirect_url", ""), + currency: getString(dict, "currency", ""), payment_methods: getMethodsArr(dict, "payment_methods"), mandate_payment: getMandate(dict, "mandate_payment"), payment_type: getString(dict, "payment_type", ""), @@ -968,3 +987,33 @@ let getCardNetwork = (~paymentMethodType, ~cardBrand) => { ->Belt.Array.get(0) ->Belt.Option.getWithDefault(defaultCardNetworks) } + +let paymentMethodFieldToStrMapper = (field: paymentMethodsFields) => { + switch field { + | Email => "Email" + | FullName => "FullName" + | InfoElement => "InfoElement" + | Country => "Country" + | Bank => "Bank" + | SpecialField(_) => "SpecialField" + | None => "None" + | BillingName => "BillingName" + | PhoneNumber => "PhoneNumber" + | AddressLine1 => "AddressLine1" + | AddressLine2 => "AddressLine2" + | AddressCity => "AddressCity" + | StateAndCity => "StateAndCity" + | CountryAndPincode(_) => "CountryAndPincode" + | AddressPincode => "AddressPincode" + | AddressState => "AddressState" + | AddressCountry(_) => "AddressCountry" + | BlikCode => "BlikCode" + | Currency(_) => "Currency" + | CardNumber => "CardNumber" + | CardExpiryMonth => "CardExpiryMonth" + | CardExpiryYear => "CardExpiryYear" + | CardExpiryMonthAndYear => "CardExpiryMonthAndYear" + | CardCvc => "CardCvc" + | CardExpiryAndCvc => "CardExpiryAndCvc" + } +} diff --git a/src/Payments/PaymentMethodsWrapper.res b/src/Payments/PaymentMethodsWrapper.res index 2deb9ae76..a1e42a527 100644 --- a/src/Payments/PaymentMethodsWrapper.res +++ b/src/Payments/PaymentMethodsWrapper.res @@ -17,7 +17,10 @@ let make = ( let optionPaymentMethodDetails = list ->PaymentMethodsRecord.buildFromPaymentList - ->Js.Array2.find(x => x.paymentMethodName === paymentMethodName) + ->Js.Array2.find(x => + x.paymentMethodName === + PaymentUtils.getPaymentMethodName(~paymentMethodType=x.methodType, ~paymentMethodName) + ) let paymentMethodDetails = optionPaymentMethodDetails->Belt.Option.getWithDefault( PaymentMethodsRecord.defaultPaymentMethodContent, @@ -120,7 +123,7 @@ let make = ( paymentType list paymentMethod=paymentMethodDetails.methodType - paymentMethodType=paymentMethodDetails.paymentMethodName + paymentMethodType=paymentMethodName setRequiredFieldsBody />
diff --git a/src/Payments/PaypalSDK.res b/src/Payments/PaypalSDK.res index b80a1457d..727aab4a0 100644 --- a/src/Payments/PaypalSDK.res +++ b/src/Payments/PaypalSDK.res @@ -1,6 +1,8 @@ @react.component let make = (~sessionObj: SessionsType.token, ~list: PaymentMethodsRecord.list) => { - let {iframeId, publishableKey} = Recoil.useRecoilValueFromAtom(RecoilAtoms.keys) + let {iframeId, publishableKey, sdkHandleOneClickConfirmPayment} = Recoil.useRecoilValueFromAtom( + RecoilAtoms.keys, + ) let (loggerState, _setLoggerState) = Recoil.useRecoilState(RecoilAtoms.loggerAtom) let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered) @@ -42,77 +44,90 @@ let make = (~sessionObj: SessionsType.token, ~list: PaymentMethodsRecord.list) = ~paymentMethod="PAYPAL", (), ) - braintree.client.create(.{authorization: token}, (clientErr, clientInstance) => { - if clientErr { - Js.Console.error2("Error creating client", clientErr) + open Promise + OrcaUtils.makeOneClickHandlerPromise(sdkHandleOneClickConfirmPayment) + ->then(result => { + let result = result->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) + if result { + braintree.client.create(.{authorization: token}, (clientErr, clientInstance) => { + if clientErr { + Js.Console.error2("Error creating client", clientErr) + } + braintree.paypalCheckout.create(. + {client: clientInstance}, + (paypalCheckoutErr, paypalCheckoutInstance) => { + switch paypalCheckoutErr->Js.Nullable.toOption { + | Some(val) => Js.Console.warn(`INTEGRATION ERROR: ${val.message}`) + | None => () + } + paypalCheckoutInstance.loadPayPalSDK(. + {vault: true}, + () => { + let paypalWrapper = GooglePayType.getElementById(Utils.document, "paypal-button") + paypalWrapper.innerHTML = "" + paypal["Buttons"](. { + style: buttonStyle, + fundingSource: paypal["FUNDING"]["PAYPAL"], + createBillingAgreement: () => { + //Paypal Clicked + Utils.handlePostMessage([ + ("fullscreen", true->Js.Json.boolean), + ("param", "paymentloader"->Js.Json.string), + ("iframeId", iframeId->Js.Json.string), + ]) + options.readOnly + ? () + : paypalCheckoutInstance.createPayment(. { + ...defaultOrderDetails, + flow: "vault", + }) + }, + onApprove: (. data, _actions) => { + options.readOnly + ? () + : paypalCheckoutInstance.tokenizePayment(. + data, + (. _err, payload) => { + let (connectors, _) = + list->PaymentUtils.getConnectors(Wallets(Paypal(SDK))) + let body = PaymentBody.paypalSdkBody( + ~token=payload.nonce, + ~connectors, + ) + intent( + ~bodyArr=body, + ~confirmParam={ + return_url: options.wallets.walletReturnUrl, + publishableKey, + }, + ~handleUserError=true, + (), + ) + }, + ) + }, + onCancel: (. _data) => { + handleCloseLoader() + }, + onError: (. _err) => { + handleCloseLoader() + }, + }).render(. "#paypal-button") + areOneClickWalletsRendered(. + prev => { + ...prev, + isPaypal: true, + }, + ) + }, + ) + }, + ) + })->ignore } - braintree.paypalCheckout.create(.{client: clientInstance}, ( - paypalCheckoutErr, - paypalCheckoutInstance, - ) => { - switch paypalCheckoutErr->Js.Nullable.toOption { - | Some(val) => Js.Console.warn(`INTEGRATION ERROR: ${val.message}`) - | None => () - } - paypalCheckoutInstance.loadPayPalSDK(. - {vault: true}, - () => { - let paypalWrapper = GooglePayType.getElementById(Utils.document, "paypal-button") - paypalWrapper.innerHTML = "" - paypal["Buttons"](. { - style: buttonStyle, - fundingSource: paypal["FUNDING"]["PAYPAL"], - createBillingAgreement: () => { - //Paypal Clicked - Utils.handlePostMessage([ - ("fullscreen", true->Js.Json.boolean), - ("param", "paymentloader"->Js.Json.string), - ("iframeId", iframeId->Js.Json.string), - ]) - options.readOnly - ? () - : paypalCheckoutInstance.createPayment(. { - ...defaultOrderDetails, - flow: "vault", - }) - }, - onApprove: (. data, _actions) => { - options.readOnly - ? () - : paypalCheckoutInstance.tokenizePayment(. - data, - (. _err, payload) => { - let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(SDK))) - let body = PaymentBody.paypalSdkBody(~token=payload.nonce, ~connectors) - intent( - ~bodyArr=body, - ~confirmParam={ - return_url: options.wallets.walletReturnUrl, - publishableKey, - }, - ~handleUserError=true, - (), - ) - }, - ) - }, - onCancel: (. _data) => { - handleCloseLoader() - }, - onError: (. _err) => { - handleCloseLoader() - }, - }).render(. "#paypal-button") - areOneClickWalletsRendered(. - prev => { - ...prev, - isPaypal: true, - }, - ) - }, - ) - }) - })->ignore + resolve() + }) + ->ignore } React.useEffect0(() => { if true { diff --git a/src/Payments/QRCodeDisplay.res b/src/Payments/QRCodeDisplay.res index 73aa916e3..bee54faca 100644 --- a/src/Payments/QRCodeDisplay.res +++ b/src/Payments/QRCodeDisplay.res @@ -44,7 +44,15 @@ let make = () => { let (x, val) = entries Js.Dict.set(headers, x, val->Js.Json.decodeString->Belt.Option.getWithDefault("")) }) - let _timeExpiry = metaDataDict->getString("expiryTime", "") + let expiryTime = + metaDataDict + ->getString("expiryTime", "") + ->Belt.Float.fromString + ->Belt.Option.getWithDefault(0.0) + let timeExpiry = expiryTime -. Js.Date.now() + if timeExpiry > 0.0 && timeExpiry < 900000.0 { + setExpiryTime(_ => timeExpiry) + } open Promise setHeaders(_ => headers->Js.Dict.entries) PaymentHelpers.pollRetrievePaymentIntent( @@ -92,11 +100,16 @@ let make = () => { if status === "succeeded" { postSubmitResponse(~jsonData=json, ~url=return_url) - } else { + } else if status === "failed" { postFailedSubmitResponse( ~errortype="confirm_payment_failed", ~message="Payment failed. Try again!", ) + } else { + postFailedSubmitResponse( + ~errortype="sync_payment_failed", + ~message="Payment is processing. Try again later!", + ) } resolve(json) }) @@ -125,7 +138,7 @@ let make = () => { className=" flex flex-row w-full justify-center items-start mb-8 font-medium text-2xl font-semibold text-[#151A1F] opacity-50"> {expiryString->React.string} - +
{React.string( diff --git a/src/Types/PaymentConfirmTypes.res b/src/Types/PaymentConfirmTypes.res index ffc56e75e..8be8a2015 100644 --- a/src/Types/PaymentConfirmTypes.res +++ b/src/Types/PaymentConfirmTypes.res @@ -34,6 +34,7 @@ type nextAction = { bank_transfer_steps_and_charges_details: option, session_token: option, image_data_url: option, + display_to_timestamp: option, } type intent = { nextAction: nextAction, @@ -56,6 +57,7 @@ let defaultNextAction = { bank_transfer_steps_and_charges_details: None, session_token: None, image_data_url: None, + display_to_timestamp: None, } let defaultIntent = { nextAction: defaultNextAction, @@ -123,6 +125,12 @@ let getNextAction = (dict, str) => { getJsonObjFromDict(json, "session_token", Js.Dict.empty())->Js.Json.object_, ), image_data_url: Some(json->getString("image_data_url", "")), + display_to_timestamp: Some( + json + ->Js.Dict.get("display_to_timestamp") + ->Belt.Option.flatMap(Js.Json.decodeNumber) + ->Belt.Option.getWithDefault(0.0), + ), } }) ->Belt.Option.getWithDefault(defaultNextAction) diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res index a1fbe2f87..bbbd5bbe9 100644 --- a/src/Types/PaymentType.res +++ b/src/Types/PaymentType.res @@ -298,7 +298,7 @@ let getAddress = (dict, str, logger) => { line2: getWarningString(json, "line2", "", ~logger), city: getWarningString(json, "city", "", ~logger), state: getWarningString(json, "state", "", ~logger), - country: country, + country, postal_code: getWarningString(json, "postal_code", "", ~logger), } }) @@ -866,6 +866,8 @@ let itemToObjMapper = (dict, logger) => { "wallets", "showCardFormByDefault", "disableSaveCards", + "sdkHandleOneClickConfirmPayment", + "showCardFormByDefault", ], dict, "options", @@ -893,7 +895,7 @@ let itemToObjMapper = (dict, logger) => { } } -type loadType = Loading | Loaded(Js.Json.t) | SemiLoaded | LoadError(Js.Json.t) +type loadType = Loading | Loaded(Js.Json.t) | SemiLoaded | LoadError let getIsAllStoredCardsHaveName = (savedCards: array) => { savedCards diff --git a/src/Utilities/DynamicFieldsUtils.res b/src/Utilities/DynamicFieldsUtils.res index be003fbcd..c21995fbf 100644 --- a/src/Utilities/DynamicFieldsUtils.res +++ b/src/Utilities/DynamicFieldsUtils.res @@ -15,6 +15,7 @@ let getName = (item: PaymentMethodsRecord.required_fields, field: RecoilAtomType let countryNames = Utils.getCountryNames(Country.country) let billingAddressFields: array = [ + BillingName, AddressLine1, AddressLine2, AddressCity, @@ -25,6 +26,7 @@ let billingAddressFields: array = [ let isBillingAddressFieldType = (fieldType: PaymentMethodsRecord.paymentMethodsFields) => { switch fieldType { + | BillingName | AddressLine1 | AddressLine2 | AddressCity @@ -49,9 +51,8 @@ let getBillingAddressPathFromFieldType = (fieldType: PaymentMethodsRecord.paymen let removeBillingDetailsIfUseBillingAddress = ( requiredFields: Js.Array2.t, + billingAddress: PaymentType.billingAddress, ) => { - let {billingAddress} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) - if billingAddress.isUseBillingAddress { requiredFields->Js.Array2.filter(requiredField => { !(requiredField.field_type->isBillingAddressFieldType) @@ -61,9 +62,10 @@ let removeBillingDetailsIfUseBillingAddress = ( } } -let addBillingAddressIfUseBillingAddress = fieldsArr => { - let {billingAddress} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) - +let addBillingAddressIfUseBillingAddress = ( + fieldsArr, + billingAddress: PaymentType.billingAddress, +) => { if billingAddress.isUseBillingAddress { fieldsArr->Js.Array2.concat(billingAddressFields) } else { @@ -119,8 +121,9 @@ let useRequiredFieldsEmptyAndValid = ( let currency = Recoil.useRecoilValueFromAtom(RecoilAtoms.userCurrency) let setAreRequiredFieldsValid = Recoil.useSetRecoilState(RecoilAtoms.areRequiredFieldsValid) let setAreRequiredFieldsEmpty = Recoil.useSetRecoilState(RecoilAtoms.areRequiredFieldsEmpty) + let {billingAddress} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) - let fieldsArrWithBillingAddress = fieldsArr->addBillingAddressIfUseBillingAddress + let fieldsArrWithBillingAddress = fieldsArr->addBillingAddressIfUseBillingAddress(billingAddress) React.useEffect7(() => { let areRequiredFieldsValid = fieldsArr->Js.Array2.reduce((acc, paymentMethodFields) => { @@ -132,7 +135,7 @@ let useRequiredFieldsEmptyAndValid = ( | AddressCountry(countryArr) => country !== "" || countryArr->Belt.Array.length === 0 | BillingName => checkIfNameIsValid(requiredFields, paymentMethodFields, billingName) | AddressLine1 => line1.value !== "" - | AddressLine2 => line2.value !== "" + | AddressLine2 => billingAddress.isUseBillingAddress ? true : line2.value !== "" | Bank => selectedBank !== "" || bankNames->Belt.Array.length === 0 | PhoneNumber => phone.value !== "" | StateAndCity => state.value !== "" && city.value !== "" @@ -169,7 +172,7 @@ let useRequiredFieldsEmptyAndValid = ( | AddressCountry(countryArr) => country === "" && countryArr->Belt.Array.length > 0 | BillingName => billingName.value === "" | AddressLine1 => line1.value === "" - | AddressLine2 => line2.value === "" + | AddressLine2 => billingAddress.isUseBillingAddress ? false : line2.value === "" | Bank => selectedBank === "" && bankNames->Belt.Array.length > 0 | StateAndCity => city.value === "" || state.value === "" | CountryAndPincode(countryArr) => @@ -272,7 +275,7 @@ let useSetInitialRequiredFields = ( logger, ) - React.useEffect0(() => { + React.useEffect1(() => { let getNameValue = (item: PaymentMethodsRecord.required_fields) => { requiredFields ->Js.Array2.filter(requiredFields => requiredFields.field_type === item.field_type) @@ -379,7 +382,7 @@ let useSetInitialRequiredFields = ( } }) None - }) + }, [requiredFields]) } let useRequiredFieldsBody = ( @@ -428,6 +431,7 @@ let useRequiredFieldsBody = ( ->Belt.Option.getWithDefault(Country.defaultTimeZone) countryCode.isoAlpha2 } + | BillingName => billingName.value | CardNumber => cardNumber->CardUtils.clearSpaces | CardExpiryMonth => let (month, _) = CardUtils.getExpiryDates(cardExpiry) @@ -443,7 +447,6 @@ let useRequiredFieldsBody = ( | CardExpiryMonthAndYear | CardExpiryAndCvc | FullName - | BillingName | None => "" } } @@ -452,8 +455,20 @@ let useRequiredFieldsBody = ( if billingAddress.isUseBillingAddress { billingAddressFields->Js.Array2.reduce((acc, item) => { let value = item->getFieldValueFromFieldType - let path = item->getBillingAddressPathFromFieldType - acc->Js.Dict.set(path, value->Js.Json.string) + if item === BillingName { + let arr = value->Js.String2.split(" ") + acc->Js.Dict.set( + "billing.address.first_name", + arr->Belt.Array.get(0)->Belt.Option.getWithDefault("")->Js.Json.string, + ) + acc->Js.Dict.set( + "billing.address.last_name", + arr->Belt.Array.get(1)->Belt.Option.getWithDefault("")->Js.Json.string, + ) + } else { + let path = item->getBillingAddressPathFromFieldType + acc->Js.Dict.set(path, value->Js.Json.string) + } acc }, requiredFieldsBody) } else { @@ -513,6 +528,7 @@ let useRequiredFieldsBody = ( let isFieldTypeToRenderOutsideBilling = (fieldType: PaymentMethodsRecord.paymentMethodsFields) => { switch fieldType { + | FullName | CardNumber | CardExpiryMonth | CardExpiryYear @@ -559,10 +575,10 @@ let combineCountryAndPostal = arr => { acc->Js.Array2.concat( switch item { | AddressCountry(val) => val - | _ => [""] + | _ => [] }, ) - }, [""]) + }, []) if hasCountryAndPostal { arr->Js.Array2.push(CountryAndPincode(options))->ignore @@ -614,13 +630,70 @@ let combineCardExpiryAndCvc = arr => { } } -let updateDynamicFields = (arr: Js.Array2.t, ()) => { +let updateDynamicFields = ( + arr: Js.Array2.t, + billingAddress, + (), +) => { arr ->Utils.removeDuplicate ->Js.Array2.filter(item => item !== None) - ->addBillingAddressIfUseBillingAddress + ->addBillingAddressIfUseBillingAddress(billingAddress) ->combineStateAndCity ->combineCountryAndPostal ->combineCardExpiryMonthAndYear ->combineCardExpiryAndCvc } + +let useSubmitCallback = () => { + let logger = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) + let (line1, setLine1) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressline1, "line1", logger) + let (line2, setLine2) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressline2, "line2", logger) + let (state, setState) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressState, "state", logger) + let (postalCode, setPostalCode) = Recoil.useLoggedRecoilState( + RecoilAtoms.userAddressPincode, + "postal_code", + logger, + ) + let (city, setCity) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressCity, "city", logger) + let {billingAddress} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) + + let {localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) + + React.useCallback5((ev: Window.event) => { + let json = ev.data->Js.Json.parseExn + let confirm = json->Utils.getDictFromJson->ConfirmType.itemToObjMapper + if confirm.doSubmit { + if line1.value == "" { + setLine1(.prev => { + ...prev, + errorString: localeString.line1EmptyText, + }) + } + if line2.value == "" { + setLine2(.prev => { + ...prev, + errorString: billingAddress.isUseBillingAddress ? "" : localeString.line2EmptyText, + }) + } + if state.value == "" { + setState(.prev => { + ...prev, + errorString: localeString.stateEmptyText, + }) + } + if postalCode.value == "" { + setPostalCode(.prev => { + ...prev, + errorString: localeString.postalCodeEmptyText, + }) + } + if city.value == "" { + setCity(.prev => { + ...prev, + errorString: localeString.cityEmptyText, + }) + } + } + }, (line1, line2, state, city, postalCode)) +} diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res index 69e60785d..1c2cca1ff 100644 --- a/src/Utilities/PaymentBody.res +++ b/src/Utilities/PaymentBody.res @@ -1450,6 +1450,24 @@ let openBankingUKBody = () => { ] } +let pixTransferBody = () => { + [ + ("payment_method", "bank_transfer"->Js.Json.string), + ("payment_method_type", "pix"->Js.Json.string), + ( + "payment_method_data", + [ + ( + "bank_transfer", + [("pix", Js.Dict.empty()->Js.Json.object_)]->Js.Dict.fromArray->Js.Json.object_, + ), + ] + ->Js.Dict.fromArray + ->Js.Json.object_, + ), + ] +} + let getPaymentBody = ( ~paymentMethod, ~fullName, @@ -1513,6 +1531,7 @@ let getPaymentBody = ( | "card_redirect" => cardRedirectBody() | "open_banking_uk" => openBankingUKBody() | "evoucher" => rewardBody(~paymentMethodType=paymentMethod) + | "pix_transfer" => pixTransferBody() | _ => [] } } diff --git a/src/Utilities/PaymentHelpers.res b/src/Utilities/PaymentHelpers.res index 04b7fb3e9..df0989b87 100644 --- a/src/Utilities/PaymentHelpers.res +++ b/src/Utilities/PaymentHelpers.res @@ -253,17 +253,20 @@ let intentCall = ( ]) } else if intent.nextAction.type_ === "qr_code_information" { let qrData = intent.nextAction.image_data_url->Belt.Option.getWithDefault("") + let expiryTime = intent.nextAction.display_to_timestamp->Belt.Option.getWithDefault(0.0) let headerObj = Js.Dict.empty() - headers->Js.Array2.forEach(entries => { - let (x, val) = entries - Js.Dict.set(headerObj, x, val->Js.Json.string) - }) + headers->Js.Array2.forEach( + entries => { + let (x, val) = entries + Js.Dict.set(headerObj, x, val->Js.Json.string) + }, + ) let metaData = [ ("qrData", qrData->Js.Json.string), ("paymentIntentId", clientSecret->Js.Json.string), ("headers", headerObj->Js.Json.object_), - ("expiryTime", Js.Json.null), + ("expiryTime", expiryTime->Belt.Float.toString->Js.Json.string), ("url", url.href->Js.Json.string), ]->Js.Dict.fromArray handleLogging( @@ -333,7 +336,11 @@ let intentCall = ( handlePostMessage(message) } else { switch paymentType { - | Card => postSubmitResponse(~jsonData=data, ~url=url.href) + | Card + | Gpay + | Applepay + | Paypal => + postSubmitResponse(~jsonData=data, ~url=url.href) | _ => openUrl(url.href) } } @@ -356,10 +363,14 @@ let intentCall = ( ) } if intent.status === "failed" { - setIsManualRetryEnabled(._ => intent.manualRetryAllowed) + setIsManualRetryEnabled(. _ => intent.manualRetryAllowed) } switch paymentType { - | Card => postSubmitResponse(~jsonData=data, ~url=url.href) + | Card + | Gpay + | Applepay + | Paypal => + postSubmitResponse(~jsonData=data, ~url=url.href) | _ => openUrl(url.href) } } else { @@ -433,11 +444,11 @@ let usePaymentSync = (optLogger: option, paymentType: pay | Loaded(_) => paymentSync() | SemiLoaded | Loading - | LoadError(_) => () + | LoadError => () } | None => postFailedSubmitResponse( - ~errortype="SYNC_PAYMENT_FAILED", + ~errortype="sync_payment_failed", ~message="Sync Payment Failed. Try Again!", ) } @@ -450,7 +461,15 @@ let rec maskPayload = payloadDict => { keys ->Js.Array2.map(key => { let value = payloadDict->Js.Dict.get(key)->Belt.Option.getWithDefault(Js.Json.null) - if value->Js.Json.decodeObject->Belt.Option.isSome { + if value->Js.Json.decodeArray->Belt.Option.isSome { + let arr = value->Js.Json.decodeArray->Belt.Option.getWithDefault([]) + arr->Js.Array2.forEachi((element, index) => { + maskedPayload->Js.Dict.set( + key ++ "[" ++ index->Belt.Int.toString ++ "]", + element->Utils.getDictFromJson->maskPayload->Js.Json.string, + ) + }) + } else if value->Js.Json.decodeObject->Belt.Option.isSome { let valueDict = value->Utils.getDictFromJson maskedPayload->Js.Dict.set(key, valueDict->maskPayload->Js.Json.string) } else { @@ -503,9 +522,31 @@ let usePaymentIntent = (optLogger: option, paymentType: p ) let uri = `${endpoint}/payments/${paymentIntentID}/confirm` let fetchMethod = Fetch.Post - let loggerPayload = body->Js.Dict.fromArray->maskPayload let callIntent = body => { + let maskedPayload = + body + ->OrcaUtils.safeParseOpt + ->Belt.Option.getWithDefault(Js.Json.null) + ->Utils.getDictFromJson + ->maskPayload + let loggerPayload = + [ + ("payload", maskedPayload->Js.Json.string), + ( + "headers", + headers + ->Js.Array2.map(header => { + let (key, value) = header + (key, value->Js.Json.string) + }) + ->Js.Dict.fromArray + ->Js.Json.object_, + ), + ] + ->Js.Dict.fromArray + ->Js.Json.object_ + ->Js.Json.stringify switch paymentType { | Card => handleLogging( @@ -596,7 +637,7 @@ let usePaymentIntent = (optLogger: option, paymentType: p } | SemiLoaded => intentWithoutMandate() | Loading - | LoadError(_) => () + | LoadError => () } | None => postFailedSubmitResponse( @@ -842,4 +883,4 @@ let useCustomerDetails = ( ) Js.Json.null->resolve }) -} \ No newline at end of file +} diff --git a/src/Utilities/PaymentUtils.res b/src/Utilities/PaymentUtils.res index 456781432..f4ef8f1e1 100644 --- a/src/Utilities/PaymentUtils.res +++ b/src/Utilities/PaymentUtils.res @@ -230,3 +230,13 @@ let getDisplayNameAndIcon = ( | None => (defaultName, defaultIcon) } } + +let getPaymentMethodName = (~paymentMethodType, ~paymentMethodName) => { + if paymentMethodType == "bank_debit" { + paymentMethodName->Js.String2.replace("_debit", "") + } else if paymentMethodType == "bank_transfer" { + paymentMethodName->Js.String2.replace("_transfer", "") + } else { + paymentMethodName + } +} diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res index b43f88a23..167f2a12d 100644 --- a/src/Utilities/Utils.res +++ b/src/Utilities/Utils.res @@ -37,8 +37,9 @@ let handleOnClickPostMessage = (~targetOrigin="*", ev) => { ~targetOrigin, ) } -let handleOnConfirmPostMessage = (~targetOrigin="*", ()) => { - handlePostMessage([("confirmTriggered", true->Js.Json.boolean)], ~targetOrigin) +let handleOnConfirmPostMessage = (~targetOrigin="*", ~isOneClick=false, ()) => { + let message = isOneClick ? "oneClickConfirmTriggered" : "confirmTriggered" + handlePostMessage([(message, true->Js.Json.boolean)], ~targetOrigin) } let getOptionString = (dict, key) => { dict->Js.Dict.get(key)->Belt.Option.flatMap(Js.Json.decodeString) @@ -353,6 +354,17 @@ let removeDuplicate = arr => { }) } +let isEmailValid = email => { + switch email->Js.String2.match_( + %re( + "/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/" + ), + ) { + | Some(_match) => Some(true) + | None => email->Js.String2.length > 0 ? Some(false) : None + } +} + let checkEmailValid = ( email: RecoilAtomTypes.field, fn: (. RecoilAtomTypes.field => RecoilAtomTypes.field) => unit, diff --git a/src/Window.res b/src/Window.res index 805d63fba..1bf126025 100644 --- a/src/Window.res +++ b/src/Window.res @@ -37,6 +37,7 @@ type eventData = { focus: bool, blur: bool, confirmTriggered: bool, + oneClickConfirmTriggered: bool, } type loaderEvent = {key: string, data: eventData} @set external innerHTML: (Dom.element, string) => unit = "innerHTML" @@ -113,7 +114,11 @@ external userAgent: string = "userAgent" @val @scope("navigator") external sendBeacon: (string, string) => unit = "sendBeacon" -@val @scope(("window", "location")) external hostname: string = "hostname" +@val @scope(("window", "location")) +external hostname: string = "hostname" + +@val @scope(("window", "location")) +external href: string = "href" let isSandbox = hostname === "beta.hyperswitch.io" diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index 2be4a3774..1361a6509 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -145,6 +145,8 @@ let make = ( } let msg = [("paymentMethodList", json)]->Js.Dict.fromArray mountedIframeRef->Window.iframePostMessage(msg) + let maskedPayload = json->getDictFromJson->PaymentHelpers.maskPayload + logger.setLogInfo(~value=maskedPayload, ~eventName=PAYMENT_METHODS_RESPONSE, ()) } } let msg = [("sendPaymentMethodsResponse", true->Js.Json.boolean)]->Js.Dict.fromArray @@ -241,6 +243,7 @@ let make = ( mountedIframeRef, selectorString, sdkHandleConfirmPayment, + sdkHandleOneClickConfirmPayment, disableSaveCards, ) => { open Promise @@ -270,6 +273,7 @@ let make = ( ("blockConfirm", blockConfirm->Js.Json.boolean), ("switchToCustomPod", switchToCustomPod->Js.Json.boolean), ("endpoint", endpoint->Js.Json.string), + ("sdkHandleOneClickConfirmPayment", sdkHandleOneClickConfirmPayment->Js.Json.boolean), ("parentURL", "*"->Js.Json.string), ("analyticsMetadata", analyticsMetadata), ]->Js.Dict.fromArray diff --git a/src/orca-loader/Hyper.res b/src/orca-loader/Hyper.res index 9a5688375..3ed07f432 100644 --- a/src/orca-loader/Hyper.res +++ b/src/orca-loader/Hyper.res @@ -113,12 +113,7 @@ let make = (publishableKey, options: option, analyticsInfo: option { logger.setMerchantId(publishableKey) logger.setSessionId(sessionID) - logger.setLogInfo( - ~value="orca-sdk initiated", - ~eventName=APP_INITIATED, - ~timestamp=sdkTimestamp, - (), - ) + logger.setLogInfo(~value=Window.href, ~eventName=APP_INITIATED, ~timestamp=sdkTimestamp, ()) } }->Sentry.sentryLogger switch Window.getHyper->Js.Nullable.toOption { @@ -245,7 +240,8 @@ let make = (publishableKey, options: option, analyticsInfo: optionJs.Dict.fromArray->Js.Json.object_->Promise.resolve }) } - let confirmPayment = payload => { + + let confirmPaymentWrapper = (payload, isOneClick, result) => { let confirmParams = payload ->Js.Json.decodeObject @@ -267,24 +263,6 @@ let make = (publishableKey, options: option, analyticsInfo: optionBelt.Option.getWithDefault("") Js.Promise.make((~resolve, ~reject as _) => { - iframeRef.contents->Js.Array2.forEach(ifR => { - ifR->Window.iframePostMessage( - [ - ("doSubmit", true->Js.Json.boolean), - ("clientSecret", clientSecret.contents->Js.Json.string), - ( - "confirmParams", - [ - ("return_url", url->Js.Json.string), - ("publishableKey", publishableKey->Js.Json.string), - ] - ->Js.Dict.fromArray - ->Js.Json.object_, - ), - ]->Js.Dict.fromArray, - ) - }) - let handleMessage = (event: Types.event) => { let json = event.data->eventToJson let dict = json->getDictFromJson @@ -308,6 +286,16 @@ let make = (publishableKey, options: option, analyticsInfo: optionJs.Dict.get("url") ->Belt.Option.flatMap(Js.Json.decodeString) ->Belt.Option.getWithDefault(url) + + if isOneClick { + iframeRef.contents->Js.Array2.forEach(ifR => { + // to unset one click button loader + ifR->Window.iframePostMessage( + [("oneClickDoSubmit", false->Js.Json.boolean)]->Js.Dict.fromArray, + ) + }) + } + if ( val->Js.Json.decodeBoolean->Belt.Option.getWithDefault(false) && redirect === "always" @@ -321,12 +309,38 @@ let make = (publishableKey, options: option, analyticsInfo: option () } } - + let message = isOneClick + ? [("oneClickDoSubmit", result->Js.Json.boolean)]->Js.Dict.fromArray + : [ + ("doSubmit", true->Js.Json.boolean), + ("clientSecret", clientSecret.contents->Js.Json.string), + ( + "confirmParams", + [ + ("return_url", url->Js.Json.string), + ("publishableKey", publishableKey->Js.Json.string), + ] + ->Js.Dict.fromArray + ->Js.Json.object_, + ), + ]->Js.Dict.fromArray addSmartEventListener("message", handleMessage, "onSubmit") + iframeRef.contents->Js.Array2.forEach(ifR => { + ifR->Window.iframePostMessage(message) + }) }) } + + let confirmPayment = payload => { + confirmPaymentWrapper(payload, false, true) + } + + let confirmOneClickPayment = (payload, result: bool) => { + confirmPaymentWrapper(payload, true, result) + } + let elements = elementsOptions => { - logger.setLogInfo(~value="orca.elements called", ~eventName=ORCA_ELEMENTS_CALLED, ()) + open Promise let clientSecretId = elementsOptions ->Js.Json.decodeObject @@ -334,8 +348,15 @@ let make = (publishableKey, options: option, analyticsInfo: optionBelt.Option.flatMap(Js.Json.decodeString) ->Belt.Option.getWithDefault("") clientSecret := clientSecretId - - logger.setClientSecret(clientSecretId) + Js.Promise.make((~resolve, ~reject as _) => { + logger.setClientSecret(clientSecretId) + resolve(. Js.Json.null) + }) + ->then(_ => { + logger.setLogInfo(~value=Window.href, ~eventName=ORCA_ELEMENTS_CALLED, ()) + resolve() + }) + ->ignore Elements.make( elementsOptions, @@ -466,7 +487,9 @@ let make = (publishableKey, options: option, analyticsInfo: optionJs.Json.object_ Window.paymentRequest(methodData, details, optionsForPaymentRequest) } + let returnObject = { + confirmOneClickPayment, confirmPayment, elements, widgets: elements, diff --git a/src/orca-loader/LoaderPaymentElement.res b/src/orca-loader/LoaderPaymentElement.res index ad3fd4f0e..df198ccf0 100644 --- a/src/orca-loader/LoaderPaymentElement.res +++ b/src/orca-loader/LoaderPaymentElement.res @@ -25,6 +25,14 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = ->Belt.Option.flatMap(x => x->Js.Dict.get("sdkHandleConfirmPayment")) ->Belt.Option.flatMap(Js.Json.decodeBoolean) ->Belt.Option.getWithDefault(false) + + let sdkHandleOneClickConfirmPayment = + options + ->Js.Json.decodeObject + ->Belt.Option.flatMap(x => x->Js.Dict.get("sdkHandleOneClickConfirmPayment")) + ->Belt.Option.flatMap(Js.Json.decodeBoolean) + ->Belt.Option.getWithDefault(true) + let disableSaveCards = options ->Js.Json.decodeObject @@ -65,6 +73,13 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = ConfirmPayment, "onHelpConfirmPayment", ) + | OneClickConfirmPayment => + eventHandlerFunc( + ev => ev.data.oneClickConfirmTriggered, + eventHandler, + OneClickConfirmPayment, + "onHelpOneClickConfirmPayment", + ) | _ => () } } @@ -292,6 +307,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) = Window.querySelector(`#orca-payment-element-iframeRef-${localSelectorString}`), localSelectorString, sdkHandleConfirmPayment, + sdkHandleOneClickConfirmPayment, disableSaveCards, ) } diff --git a/src/orca-loader/OrcaUtils.res b/src/orca-loader/OrcaUtils.res index aac452edf..9e5b68880 100644 --- a/src/orca-loader/OrcaUtils.res +++ b/src/orca-loader/OrcaUtils.res @@ -1,5 +1,6 @@ open Utils external toNullable: Js.Json.t => Js.Nullable.t = "%identity" +external eventToJson: Types.eventData => Js.Json.t = "%identity" let safeParseOpt = st => { try { Js.Json.parseExn(st)->Some @@ -132,10 +133,12 @@ let rec flatten = (obj, addIndicatorForObject) => { let flattenedSubObj = flatten(item, addIndicatorForObject) flattenedSubObj ->Js.Dict.entries - ->Js.Array2.forEach(subEntry => { - let (subKey, subValue) = subEntry - Js.Dict.set(newDict, `${key}[${index->string_of_int}].${subKey}`, subValue) - }) + ->Js.Array2.forEach( + subEntry => { + let (subKey, subValue) = subEntry + Js.Dict.set(newDict, `${key}[${index->string_of_int}].${subKey}`, subValue) + }, + ) } | _ => @@ -243,6 +246,7 @@ let eventHandlerFunc = ( | Ready | Focus | ConfirmPayment + | OneClickConfirmPayment | Blur => switch eventHandler { | Some(eH) => eH(Some(ev.data)) @@ -330,3 +334,27 @@ let getArrayOfTupleFromDict = dict => { ->Js.Dict.keys ->Belt.Array.map(key => (key, Js.Dict.get(dict, key)->Belt.Option.getWithDefault(Js.Json.null))) } + +let makeOneClickHandlerPromise = sdkHandleOneClickConfirmPayment => { + open EventListenerManager + Js.Promise.make((~resolve, ~reject as _) => { + if sdkHandleOneClickConfirmPayment { + resolve(. Js.Json.boolean(true)) + } else { + let handleMessage = (event: Types.event) => { + let json = event.data->eventToJson->getStringfromjson("")->safeParse + + let dict = json->Utils.getDictFromJson + if dict->Js.Dict.get("oneClickDoSubmit")->Belt.Option.isSome { + resolve(. + dict + ->Js.Dict.get("oneClickDoSubmit") + ->Belt.Option.getWithDefault(true->Js.Json.boolean), + ) + } + } + addSmartEventListener("message", handleMessage, "onOneClickHandlerPaymentConfirm") + Utils.handleOnConfirmPostMessage(~targetOrigin="*", ~isOneClick=true, ()) + } + }) +} diff --git a/src/orca-loader/Types.res b/src/orca-loader/Types.res index 2fe5eea3e..a37ab20b0 100644 --- a/src/orca-loader/Types.res +++ b/src/orca-loader/Types.res @@ -8,6 +8,7 @@ type eventData = { classChange: bool, newClassType: string, confirmTriggered: bool, + oneClickConfirmTriggered: bool, } type event = {key: string, data: eventData} type eventParam = Event(event) | EventData(eventData) | Empty @@ -46,6 +47,7 @@ type confirmPaymentParams = { } type hyperInstance = { + confirmOneClickPayment: (Js.Json.t, bool) => Js.Promise.t, confirmPayment: Js.Json.t => Js.Promise.t, elements: Js.Json.t => element, confirmCardPayment: Js_OO.Callback.arity4< @@ -56,6 +58,10 @@ type hyperInstance = { paymentRequest: Js.Json.t => Js.Json.t, } +let oneClickConfirmPaymentFn = (_, _) => { + Js.Promise.resolve(Js.Dict.empty()->Js.Json.object_) +} + let confirmPaymentFn = (_elements: Js.Json.t) => { Js.Promise.resolve(Js.Dict.empty()->Js.Json.object_) } @@ -103,13 +109,14 @@ let create = (_componentType, _options) => { } let defaultElement = { - getElement: getElement, - update: update, - fetchUpdates: fetchUpdates, - create: create, + getElement, + update, + fetchUpdates, + create, } let defaultHyperInstance = { + confirmOneClickPayment: oneClickConfirmPaymentFn, confirmPayment: confirmPaymentFn, confirmCardPayment: confirmCardPaymentFn, retrievePaymentIntent: retrievePaymentIntentFn, @@ -118,7 +125,8 @@ let defaultHyperInstance = { paymentRequest: _ev => Js.Json.null, } -type eventType = Escape | Change | Click | Ready | Focus | Blur | ConfirmPayment | None +type eventType = + Escape | Change | Click | Ready | Focus | Blur | ConfirmPayment | OneClickConfirmPayment | None let eventTypeMapper = event => { switch event { @@ -129,6 +137,7 @@ let eventTypeMapper = event => { | "focus" => Focus | "blur" => Blur | "confirmTriggered" => ConfirmPayment + | "oneClickConfirmTriggered" => OneClickConfirmPayment | _ => None } } diff --git a/src/orca-log-catcher/ErrorBoundary.res b/src/orca-log-catcher/ErrorBoundary.res index f8aa14567..a636fab58 100644 --- a/src/orca-log-catcher/ErrorBoundary.res +++ b/src/orca-log-catcher/ErrorBoundary.res @@ -98,11 +98,7 @@ module ErrorCard = { let make = (~error: Sentry.ErrorBoundary.fallbackArg, ~level) => { let beaconApiCall = data => { if data->Js.Array2.length > 0 { - let logData = - [("data", data->Js.Array2.map(OrcaLogger.logFileToObj)->Js.Json.array)] - ->Js.Dict.fromArray - ->Js.Json.object_ - ->Js.Json.stringify + let logData = data->Js.Array2.map(OrcaLogger.logFileToObj)->Js.Json.array->Js.Json.stringify Window.sendBeacon(GlobalVars.logEndpoint, logData) } } @@ -131,7 +127,7 @@ module ErrorCard = { platform: Window.platform, userAgent: Window.userAgent, appId: "", - eventName: "SDK_CRASH", + eventName: SDK_CRASH, latency: "", paymentMethod: "", firstEvent: false, diff --git a/src/orca-log-catcher/OrcaLogger.res b/src/orca-log-catcher/OrcaLogger.res index 3307ba403..a123e362c 100644 --- a/src/orca-log-catcher/OrcaLogger.res +++ b/src/orca-log-catcher/OrcaLogger.res @@ -27,6 +27,7 @@ type eventName = | PAYPAL_SDK_FLOW | APP_INITIATED | APP_REINITIATED + | LOG_INITIATED | LOADER_CALLED | ORCA_ELEMENTS_CALLED | PAYMENT_OPTIONS_PROVIDED @@ -52,6 +53,7 @@ type eventName = | REDIRECTING_USER | DISPLAY_BANK_TRANSFER_INFO_PAGE | DISPLAY_QR_CODE_INFO_PAGE + | PAYMENT_METHODS_RESPONSE let eventNameToStrMapper = eventName => { switch eventName { @@ -80,6 +82,7 @@ let eventNameToStrMapper = eventName => { | PAYPAL_SDK_FLOW => "PAYPAL_SDK_FLOW" | APP_INITIATED => "APP_INITIATED" | APP_REINITIATED => "APP_REINITIATED" + | LOG_INITIATED => "LOG_INITIATED" | LOADER_CALLED => "LOADER_CALLED" | ORCA_ELEMENTS_CALLED => "ORCA_ELEMENTS_CALLED" | PAYMENT_OPTIONS_PROVIDED => "PAYMENT_OPTIONS_PROVIDED" @@ -105,6 +108,7 @@ let eventNameToStrMapper = eventName => { | REDIRECTING_USER => "REDIRECTING_USER" | DISPLAY_BANK_TRANSFER_INFO_PAGE => "DISPLAY_BANK_TRANSFER_INFO_PAGE" | DISPLAY_QR_CODE_INFO_PAGE => "DISPLAY_QR_CODE_INFO_PAGE" + | PAYMENT_METHODS_RESPONSE => "PAYMENT_METHODS_RESPONSE" } } @@ -134,7 +138,7 @@ type logFile = { browserName: string, browserVersion: string, userAgent: string, - eventName: string, + eventName: eventName, latency: string, firstEvent: bool, paymentMethod: string, @@ -253,7 +257,7 @@ let logFileToObj = logFile => { ("app_id", logFile.appId->Js.Json.string), ("platform", logFile.platform->convertToScreamingSnakeCase->Js.Json.string), ("user_agent", logFile.userAgent->Js.Json.string), - ("event_name", logFile.eventName->Js.Json.string), + ("event_name", logFile.eventName->eventNameToStrMapper->Js.Json.string), ("browser_name", logFile.browserName->convertToScreamingSnakeCase->Js.Json.string), ("browser_version", logFile.browserVersion->Js.Json.string), ("latency", logFile.latency->Js.Json.string), @@ -404,6 +408,9 @@ let make = ( | None => GlobalVars.repoName } + let events = ref(Js.Dict.empty()) + let eventsCounter = ref(Js.Dict.empty()) + let timeOut = ref(None) let merchantId = getRefFromOption(merchantId) @@ -417,8 +424,25 @@ let make = ( metadata := value } + let calculateAndUpdateCounterHook = eventName => { + let updatedCounter = switch eventsCounter.contents->Js.Dict.get(eventName) { + | Some(num) => num + 1 + | None => 1 + } + eventsCounter.contents->Js.Dict.set(eventName, updatedCounter) + updatedCounter + } + let conditionalLogPush = (log: logFile) => { - if GlobalVars.enableLogging { + let maxLogsPushedPerEventName = GlobalVars.maxLogsPushedPerEventName + let conditionalEventName = switch log.eventName { + | INPUT_FIELD_CHANGED => log.value // to enforce rate limiting for each input field independently + | _ => "" + } + let eventName = log.eventName->eventNameToStrMapper ++ conditionalEventName + + let counter = eventName->calculateAndUpdateCounterHook + if GlobalVars.enableLogging && counter <= maxLogsPushedPerEventName { switch loggingLevel { | DEBUG => log->Js.Array2.push(mainLogFile, _)->ignore | INFO => @@ -438,11 +462,7 @@ let make = ( let beaconApiCall = data => { if data->Js.Array2.length > 0 { - let logData = - [("data", data->Js.Array2.map(logFileToObj)->Js.Json.array)] - ->Js.Dict.fromArray - ->Js.Json.object_ - ->Js.Json.stringify + let logData = data->Js.Array2.map(logFileToObj)->Js.Json.array->Js.Json.stringify Window.sendBeacon(GlobalVars.logEndpoint, logData) } } @@ -452,8 +472,6 @@ let make = ( clientSecret := value } - let events = ref(Js.Dict.empty()) - let rec sendLogs = () => { switch timeOut.contents { | Some(val) => { @@ -471,16 +489,16 @@ let make = ( let checkForPriorityEvents = (arrayOfLogs: array) => { let priorityEventNames = [ - "APP_RENDERED", - "ORCA_ELEMENTS_CALLED", - "PAYMENT_DATA_FILLED", - "PAYMENT_ATTEMPT", - "CONFIRM_CALL", - "SDK_CRASH", - "REDIRECTING_USER", - "DISPLAY_BANK_TRANSFER_INFO_PAGE", - "DISPLAY_QR_CODE_INFO_PAGE", - "SESSIONS_CALL", + APP_RENDERED, + ORCA_ELEMENTS_CALLED, + PAYMENT_DATA_FILLED, + PAYMENT_ATTEMPT, + CONFIRM_CALL, + SDK_CRASH, + REDIRECTING_USER, + DISPLAY_BANK_TRANSFER_INFO_PAGE, + DISPLAY_QR_CODE_INFO_PAGE, + SESSIONS_CALL, ] arrayOfLogs ->Js.Array2.find(log => { @@ -507,19 +525,20 @@ let make = ( let calculateLatencyHook = (~eventName, ~type_="", ()) => { let currentTimestamp = Js.Date.now() let latency = switch eventName { - | "PAYMENT_ATTEMPT" => { - let appRenderedTimestamp = events.contents->Js.Dict.get("APP_RENDERED") + | PAYMENT_ATTEMPT => { + let appRenderedTimestamp = events.contents->Js.Dict.get(APP_RENDERED->eventNameToStrMapper) switch appRenderedTimestamp { | Some(float) => currentTimestamp -. float | _ => -1. } } - | "RETRIEVE_CALL" - | "CONFIRM_CALL" - | "SESSIONS_CALL" - | "PAYMENT_METHODS_CALL" - | "CUSTOMER_PAYMENT_METHODS_CALL" => { - let logRequestTimestamp = events.contents->Js.Dict.get(eventName ++ "_INIT") + | RETRIEVE_CALL + | CONFIRM_CALL + | SESSIONS_CALL + | PAYMENT_METHODS_CALL + | CUSTOMER_PAYMENT_METHODS_CALL => { + let logRequestTimestamp = + events.contents->Js.Dict.get(eventName->eventNameToStrMapper ++ "_INIT") switch (logRequestTimestamp, type_) { | (Some(_), "request") => 0. | (Some(float), _) => currentTimestamp -. float @@ -543,7 +562,7 @@ let make = ( ) => { let eventNameStr = eventName->eventNameToStrMapper let firstEvent = events.contents->Js.Dict.get(eventNameStr)->Belt.Option.isNone - let latency = calculateLatencyHook(~eventName=eventNameStr, ()) + let latency = calculateLatencyHook(~eventName, ()) let localTimestamp = timestamp->Belt.Option.getWithDefault(Js.Date.now()->Belt.Float.toString) let localTimestampFloat = localTimestamp->Belt.Float.fromString->Belt.Option.getWithDefault(Js.Date.now()) @@ -565,7 +584,7 @@ let make = ( platform: Window.platform, userAgent: Window.userAgent, appId: "", - eventName: eventNameStr, + eventName, latency, paymentMethod, firstEvent, @@ -596,7 +615,7 @@ let make = ( ) => { let eventNameStr = eventName->eventNameToStrMapper let firstEvent = events.contents->Js.Dict.get(eventNameStr)->Belt.Option.isNone - let latency = calculateLatencyHook(~eventName=eventNameStr, ~type_, ()) + let latency = calculateLatencyHook(~eventName, ~type_, ()) let localTimestamp = timestamp->Belt.Option.getWithDefault(Js.Date.now()->Belt.Float.toString) let localTimestampFloat = localTimestamp->Belt.Float.fromString->Belt.Option.getWithDefault(Js.Date.now()) @@ -624,7 +643,7 @@ let make = ( platform: Window.platform, userAgent: Window.userAgent, appId: "", - eventName: eventNameStr, + eventName, latency, paymentMethod, firstEvent, @@ -648,7 +667,7 @@ let make = ( ) => { let eventNameStr = eventName->eventNameToStrMapper let firstEvent = events.contents->Js.Dict.get(eventNameStr)->Belt.Option.isNone - let latency = calculateLatencyHook(~eventName=eventNameStr, ()) + let latency = calculateLatencyHook(~eventName, ()) let localTimestamp = timestamp->Belt.Option.getWithDefault(Js.Date.now()->Belt.Float.toString) let localTimestampFloat = localTimestamp->Belt.Float.fromString->Belt.Option.getWithDefault(Js.Date.now()) @@ -670,7 +689,7 @@ let make = ( platform: Window.platform, userAgent: Window.userAgent, appId: "", - eventName: eventNameStr, + eventName, latency, paymentMethod, firstEvent, @@ -683,8 +702,9 @@ let make = ( } let setLogInitiated = () => { - let eventName = "LOG_INITIATED" - let firstEvent = events.contents->Js.Dict.get(eventName)->Belt.Option.isNone + let eventName: eventName = LOG_INITIATED + let eventNameStr = eventName->eventNameToStrMapper + let firstEvent = events.contents->Js.Dict.get(eventNameStr)->Belt.Option.isNone let latency = calculateLatencyHook(~eventName, ()) { logType: INFO, @@ -713,7 +733,7 @@ let make = ( ->conditionalLogPush ->ignore checkLogSizeAndSendData() - events.contents->Js.Dict.set(eventName, Js.Date.now()) + events.contents->Js.Dict.set(eventNameStr, Js.Date.now()) } let handleBeforeUnload = _event => { diff --git a/webpack.common.js b/webpack.common.js index d9c8df82f..17aeac1b8 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -65,8 +65,8 @@ if (envBackendUrl === undefined) { let logEndpoint = sdkEnv === "prod" - ? "https://api.hyperswitch.io/sdk-logs" - : "https://sandbox.juspay.io/godel/analytics"; + ? "https://api.hyperswitch.io/logs/sdk" + : "https://sandbox.hyperswitch.io/logs/sdk"; // Set this to true to enable logging let enableLogging = true; @@ -74,6 +74,9 @@ let enableLogging = true; // Choose from DEBUG, INFO, WARNING, ERROR, SILENT let loggingLevel = "DEBUG"; +// Maximum logs emitted for a particular event, to rate limit logs +let maxLogsPushedPerEventName = 100; + module.exports = (publicPath = "auto") => { let entries = { app: "./index.js", @@ -123,6 +126,7 @@ module.exports = (publicPath = "auto") => { sentryScriptUrl: JSON.stringify(process.env.SENTRY_SCRIPT_URL), enableLogging: JSON.stringify(enableLogging), loggingLevel: JSON.stringify(loggingLevel), + maxLogsPushedPerEventName: JSON.stringify(maxLogsPushedPerEventName), }), new HtmlWebpackPlugin({ inject: false,