From e98417f7b52dea0e174131b26333e9549f4f24ad Mon Sep 17 00:00:00 2001 From: Win Date: Tue, 3 Dec 2024 16:22:01 +0700 Subject: [PATCH] [1.2.0] Custom application (cont.) --- processor/package-lock.json | 531 +++++-- processor/package.json | 10 +- .../src/commercetools/action.commercetools.ts | 50 + processor/src/index.ts | 10 + processor/src/mollie/payment.mollie.ts | 40 +- processor/src/service/payment.service.ts | 237 ++- processor/src/types/commercetools.types.ts | 23 + processor/src/utils/app.utils.ts | 22 + processor/src/utils/config.utils.ts | 3 +- processor/src/utils/constant.utils.ts | 9 + processor/src/utils/map.utils.ts | 109 +- processor/src/utils/mollie.utils.ts | 7 +- processor/src/validators/env.validators.ts | 26 +- .../action.commercetools.spec.ts | 49 +- .../tests/service/payment.service.spec.ts | 862 +++++++++- processor/tests/utils/app.utils.spec.ts | 50 + processor/tests/utils/constant.utils.spec.ts | 18 + processor/tests/utils/map.utils.spec.ts | 1401 +++++++++++------ processor/tests/utils/mollie.utils.spec.ts | 17 + 19 files changed, 2829 insertions(+), 645 deletions(-) diff --git a/processor/package-lock.json b/processor/package-lock.json index f2ccdd0..a4b3130 100644 --- a/processor/package-lock.json +++ b/processor/package-lock.json @@ -1,16 +1,17 @@ { "name": "shopmacher-mollie-processor", - "version": "1.0.3", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "shopmacher-mollie-processor", - "version": "1.0.3", + "version": "1.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@commercetools-backend/loggers": "^22.35.0", + "@commercetools-backend/express": "^22.32.2", + "@commercetools-backend/loggers": "^21.25.2", "@commercetools/connect-payments-sdk": "^0.10.0", "@commercetools/platform-sdk": "^4.11.0", "@commercetools/sdk-client-v2": "^2.5.0", @@ -62,9 +63,9 @@ "dev": true }, "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "engines": { "node": ">=0.1.90" } @@ -128,6 +129,19 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "node_modules/@sentry/core": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.43.0.tgz", + "integrity": "sha512-zvMZgEi7ptLBwDnd+xR/u4zdSe5UzS4S3ZhoemdQrn1PxsaVySD/ptyzLoGSZEABqlRxGHnQrZ78MU1hUDvKuQ==", + "dependencies": { + "@sentry/types": "7.43.0", + "@sentry/utils": "7.43.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -372,6 +386,14 @@ "node": ">=4" } }, + "node_modules/@commercetools-backend/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/istanbul-reports": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", @@ -533,20 +555,34 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@sentry/node": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.43.0.tgz", + "integrity": "sha512-oXaTBq6Bk8Qwsd46hhRU2MLEnjYqWI41nPJmXyAWkDSYQTP7sUe1qM8bCUdsRpPwQh955Vq9qCRfgMbN4lEoAQ==", + "dependencies": { + "@sentry/core": "7.43.0", + "@sentry/types": "7.43.0", + "@sentry/utils": "7.43.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/logform": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", - "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.5.1.tgz", + "integrity": "sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==", "dependencies": { - "@colors/colors": "1.6.0", + "@colors/colors": "1.5.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" } }, "node_modules/rxjs": { @@ -576,9 +612,9 @@ } }, "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz", + "integrity": "sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==" }, "node_modules/touch": { "version": "3.1.1", @@ -908,6 +944,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@commercetools/connect-payments-sdk/node_modules/winston": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", + "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -1017,6 +1074,18 @@ } } }, + "node_modules/@sentry/utils": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.43.0.tgz", + "integrity": "sha512-f78YfMLcgNU7+suyWFCuQhQlneXXMS+egb0EFZh7iU7kANUPRX5T4b+0C+fwaPm5gA6XfGYskr4ZnzQJLOlSqg==", + "dependencies": { + "@sentry/types": "7.43.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1210,6 +1279,14 @@ "node": ">= 0.4" } }, + "node_modules/jwks-rsa/node_modules/@types/jsonwebtoken": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz", + "integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -1699,6 +1776,22 @@ "node": ">=4" } }, + "node_modules/@commercetools/connect-payments-sdk/node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/synckit/node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -2505,9 +2598,9 @@ } }, "node_modules/@commercetools/connect-payments-sdk/node_modules/@commercetools-backend/loggers": { - "version": "22.32.2", - "resolved": "https://registry.npmjs.org/@commercetools-backend/loggers/-/loggers-22.32.2.tgz", - "integrity": "sha512-z2AwV1PStfAUGnYNfJHIkV2NekSSHjIDWEVj44x2yDFIJDjgj7l3E094QKA95aZmDb6zEGEFdfJO3n+24iMHtg==", + "version": "22.34.0", + "resolved": "https://registry.npmjs.org/@commercetools-backend/loggers/-/loggers-22.34.0.tgz", + "integrity": "sha512-UdXXDdBVK+S8LW3AGAjCJoNkFo53w38itRR2NNPTT7rawfgQPEMPrzwLj6BAfhGKuF3ycEQToRCNOxSbJFJwSA==", "dependencies": { "@babel/runtime": "^7.22.15", "@babel/runtime-corejs3": "^7.22.15", @@ -2641,19 +2734,19 @@ "optional": true }, "node_modules/jwks-rsa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", - "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-2.1.5.tgz", + "integrity": "sha512-IODtn1SwEm7n6GQZnQLY0oxKDrMh7n/jRH1MzE8mlxWMrh2NnMyOsXTebu8vJ1qCpmuTJcL4DdiE0E4h8jnwsA==", "dependencies": { - "@types/express": "^4.17.17", - "@types/jsonwebtoken": "^9.0.2", + "@types/express": "^4.17.14", + "@types/jsonwebtoken": "^8.5.9", "debug": "^4.3.4", - "jose": "^4.14.6", + "jose": "^2.0.6", "limiter": "^1.1.5", - "lru-memoizer": "^2.2.0" + "lru-memoizer": "^2.1.4" }, "engines": { - "node": ">=14" + "node": ">=10 < 13 || >=14" } }, "node_modules/express-winston/node_modules/escape-string-regexp": { @@ -2720,6 +2813,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@commercetools/connect-payments-sdk/node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -2771,6 +2872,11 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3048,6 +3154,20 @@ "node": ">=8" } }, + "node_modules/@commercetools-backend/express/node_modules/serve-static": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz", + "integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3193,6 +3313,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@commercetools-backend/express/node_modules/finalhandler/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/object.fromentries": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", @@ -3211,6 +3339,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@commercetools/connect-payments-sdk/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -3243,6 +3379,14 @@ "semver": "bin/semver.js" } }, + "node_modules/winston-transport/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3659,6 +3803,14 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@commercetools-backend/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3730,6 +3882,11 @@ "node": ">=0.6" } }, + "node_modules/@commercetools-backend/express/node_modules/serve-static/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3889,6 +4046,11 @@ "node": ">=0.10.0" } }, + "node_modules/express-unless": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", + "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==" + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3928,9 +4090,9 @@ "dev": true }, "node_modules/@mollie/api-client/node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" @@ -4113,9 +4275,9 @@ } }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "engines": { "node": ">= 0.6" } @@ -4331,6 +4493,18 @@ "node": ">=0.10.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -4367,6 +4541,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@commercetools-backend/express/node_modules/serve-static/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@commercetools/connect-payments-sdk/node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "node_modules/shell-quote": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", @@ -5334,15 +5521,12 @@ } }, "node_modules/@commercetools/ts-client": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@commercetools/ts-client/-/ts-client-2.0.5.tgz", - "integrity": "sha512-v1hdwMdyGV/ja+pdgTvnA/qLiZ67Zepbaj1WzucHa/ub9V78ZuezZTExTFKhSMFm5c+mMzLwq0UpacAYlu4nmA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@commercetools/ts-client/-/ts-client-2.1.0.tgz", + "integrity": "sha512-xsZRMxSik24CcWuUclE4bjIArSkepGGcWkHbkp4HgORxstLPROeMo+1+vSSTigShQAm9CZeJvXe8cQc5zUtGhw==", "dependencies": { "abort-controller": "3.0.0", - "async-mutex": "^0.5.0", - "buffer": "^6.0.3", - "node-fetch": "^2.6.1", - "uuid": "10.0.0" + "node-fetch": "^2.6.1" }, "engines": { "node": ">=14" @@ -5413,12 +5597,9 @@ } }, "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "engines": { - "node": ">= 14.0.0" - } + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "node_modules/lru-memoizer": { "version": "2.3.0", @@ -5602,6 +5783,14 @@ "node": ">=6.5" } }, + "node_modules/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5762,20 +5951,20 @@ } }, "node_modules/@commercetools-backend/loggers": { - "version": "22.35.0", - "resolved": "https://registry.npmjs.org/@commercetools-backend/loggers/-/loggers-22.35.0.tgz", - "integrity": "sha512-J+3f3m3ORJ2i0pFc7KxJfYNrjUZT3EV9YqEhsTNNg0LEUxoTF3H8+HisRprC9PeIRlbJRFtLiT3/XnbFUufHEQ==", - "dependencies": { - "@babel/runtime": "^7.22.15", - "@babel/runtime-corejs3": "^7.22.15", - "@types/lodash": "^4.14.198", - "@types/triple-beam": "1.3.5", + "version": "21.25.2", + "resolved": "https://registry.npmjs.org/@commercetools-backend/loggers/-/loggers-21.25.2.tgz", + "integrity": "sha512-+aw9ai7bNzgBBRn9bgCAKCApNavsZCpKqhuOC5KWBngaN/eao8kvuLs8Rs17MGlqkZWD+Smtt67K44LV5nZDpA==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@babel/runtime-corejs3": "^7.20.13", + "@sentry/node": "7.43.0", + "@types/triple-beam": "1.3.2", "express-winston": "4.2.0", "fast-safe-stringify": "2.1.1", "lodash": "4.17.21", - "logform": "2.6.0", - "triple-beam": "1.4.1", - "winston": "3.13.0" + "logform": "2.5.1", + "triple-beam": "1.3.0", + "winston": "3.8.2" } }, "node_modules/react-is": { @@ -6197,11 +6386,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/async-mutex/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, "node_modules/@babel/runtime": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", @@ -6267,6 +6451,17 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -6787,9 +6982,15 @@ } }, "node_modules/jose": { - "version": "4.15.9", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", - "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/jose/-/jose-2.0.7.tgz", + "integrity": "sha512-5hFWIigKqC+e/lRyQhfnirrAqUdIPMB7SJRqflJaO29dW7q5DFvH1XCSTmv6PQ6pb++0k6MJlLRoS0Wv4s38Wg==", + "dependencies": { + "@panva/asn1.js": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0 < 13 || >=13.7.0" + }, "funding": { "url": "https://github.com/sponsors/panva" } @@ -6855,6 +7056,14 @@ "node": ">=14" } }, + "node_modules/@sentry/types": { + "version": "7.43.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.43.0.tgz", + "integrity": "sha512-5XxCWqYWJNoS+P6Ie2ZpUDxLRCt7FTEzmlQkCdjW6MFWOX26hAbF/wEuOTYAFKZXMIXOz0Egofik1e8v1Cg6/A==", + "engines": { + "node": ">=8" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -6948,11 +7157,11 @@ } }, "node_modules/winston": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.0.tgz", - "integrity": "sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.2.tgz", + "integrity": "sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew==", "dependencies": { - "@colors/colors": "^1.6.0", + "@colors/colors": "1.5.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", @@ -6962,12 +7171,20 @@ "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", - "winston-transport": "^4.7.0" + "winston-transport": "^4.5.0" }, "engines": { "node": ">= 12.0.0" } }, + "node_modules/@commercetools/connect-payments-sdk/node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -7078,12 +7295,12 @@ } }, "node_modules/@commercetools/connect-payments-sdk": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@commercetools/connect-payments-sdk/-/connect-payments-sdk-0.10.0.tgz", - "integrity": "sha512-WLOF3nK1ppipiYkNXdKOAL8eS5WHMkgVBrEocKpZR4JlQyTkXXApVUb1La/evB7MlDuba1tt/YBhQwwRhyJSQw==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@commercetools/connect-payments-sdk/-/connect-payments-sdk-0.10.1.tgz", + "integrity": "sha512-xhHr+zNwaS4LM6vWyJz5Q1206Et8j1EKUrC3VCO2ve5R03m8byPeT3KOgCfE8DexURuQTaPUo1+qf1y2K8EV9Q==", "dependencies": { - "@commercetools-backend/loggers": "22.32.2", - "@commercetools/platform-sdk": "7.14.0", + "@commercetools-backend/loggers": "22.34.0", + "@commercetools/platform-sdk": "7.17.0", "@commercetools/sdk-client-v2": "2.5.0", "jsonwebtoken": "9.0.2", "jwks-rsa": "3.1.0", @@ -7121,6 +7338,47 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/@commercetools-backend/express/node_modules/express": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz", + "integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==", + "dependencies": { + "type-is": "~1.6.18", + "safe-buffer": "5.2.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "body-parser": "1.20.3", + "content-type": "~1.0.4", + "send": "0.19.0", + "cookie": "0.6.0", + "methods": "~1.1.2", + "proxy-addr": "~2.0.7", + "accepts": "~1.3.8", + "range-parser": "~1.2.1", + "on-finished": "2.4.1", + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "etag": "~1.8.1", + "path-to-regexp": "0.1.10", + "statuses": "2.0.1", + "parseurl": "~1.3.3", + "setprototypeof": "1.2.0", + "merge-descriptors": "1.0.3", + "vary": "~1.1.2", + "serve-static": "1.16.0", + "content-disposition": "0.5.4", + "escape-html": "~1.0.3", + "http-errors": "2.0.0", + "cookie-signature": "1.0.6", + "utils-merge": "1.0.1", + "array-flatten": "1.1.1", + "depd": "2.0.0", + "qs": "6.11.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -7321,9 +7579,9 @@ } }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "type-is": "~1.6.18", "safe-buffer": "5.2.1", @@ -7332,7 +7590,7 @@ "body-parser": "1.20.3", "content-type": "~1.0.4", "send": "0.19.0", - "cookie": "0.7.1", + "cookie": "0.6.0", "methods": "~1.1.2", "proxy-addr": "~2.0.7", "accepts": "~1.3.8", @@ -7504,6 +7762,29 @@ "balanced-match": "^1.0.0" } }, + "node_modules/@commercetools-backend/express/node_modules/serve-static/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/array.prototype.flat": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", @@ -7563,6 +7844,20 @@ "node": ">=6.9.0" } }, + "node_modules/@commercetools-backend/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -8002,6 +8297,19 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/express-jwt": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.4.1.tgz", + "integrity": "sha512-IZoZiDv2yZJAb3QrbaSATVtTCYT11OcqgFGoTN4iKVyN6NBkBkhtVIixww5fmakF0Upt5HfOxJuS6ZmJVeOtTQ==", + "dependencies": { + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -8320,6 +8628,37 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/@commercetools-backend/express": { + "version": "22.32.2", + "resolved": "https://registry.npmjs.org/@commercetools-backend/express/-/express-22.32.2.tgz", + "integrity": "sha512-+N2gY19BLdGSFraDjtXkzIJp6jE9W1qMR2KgOsXw9Mt1idOZI2Ed4o9rDuuWDZodvR35q2yPlgbHKiHZUwn+jw==", + "dependencies": { + "@babel/runtime": "^7.22.15", + "@babel/runtime-corejs3": "^7.22.15", + "@types/express": "^4.17.17", + "@types/node": "^18.17.14", + "express": "4.20.0", + "express-jwt": "8.4.1", + "jwks-rsa": "2.1.5" + } + }, + "node_modules/@commercetools-backend/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -8374,15 +8713,15 @@ } }, "node_modules/@commercetools/connect-payments-sdk/node_modules/@commercetools/platform-sdk": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@commercetools/platform-sdk/-/platform-sdk-7.14.0.tgz", - "integrity": "sha512-08/7yAxZFa6s5iKWw+67x17wa4pqiG8WHAW3dECWhkMpTME65qTHz5eyDfcCq8Tayo1rp46g67/2mWxrqapRAA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@commercetools/platform-sdk/-/platform-sdk-7.17.0.tgz", + "integrity": "sha512-8W7STCwn1z+sJM0xraaRxA6SeQnHbfxOsVwBpKfQfWTEw1mK92WST+vVbLuZmRvsXNgvjuR8VwiI2n5wb/x7jw==", "dependencies": { "@commercetools/sdk-client-v2": "^2.5.0", "@commercetools/sdk-middleware-auth": "^7.0.0", "@commercetools/sdk-middleware-http": "^7.0.0", "@commercetools/sdk-middleware-logger": "^3.0.0", - "@commercetools/ts-client": "^2.0.5" + "@commercetools/ts-client": "^2.1.0" }, "engines": { "node": ">=14" @@ -8634,6 +8973,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -8743,6 +9087,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@panva/asn1.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz", + "integrity": "sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@babel/core": { "version": "7.24.9", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", @@ -8773,6 +9125,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@commercetools-backend/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", @@ -8937,14 +9294,6 @@ } } }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", diff --git a/processor/package.json b/processor/package.json index 85bc755..c0c0c2d 100644 --- a/processor/package.json +++ b/processor/package.json @@ -1,7 +1,7 @@ { "name": "shopmacher-mollie-processor", "description": "Integration between commercetools and mollie payment service provider", - "version": "1.0.3", + "version": "1.2.0", "main": "index.js", "private": true, "scripts": { @@ -32,9 +32,6 @@ "engines": { "node": ">=18.0.0 <=20.9.0" }, - "resolutions": { - "axios": "1.7.4" - }, "devDependencies": { "@tsconfig/recommended": "^1.0.7", "@types/express": "^4.17.21", @@ -63,9 +60,10 @@ "typescript": "^5.5.3" }, "dependencies": { - "@commercetools-backend/loggers": "^22.35.0", - "@commercetools/connect-payments-sdk": "^0.10.0", + "@commercetools-backend/express": "^22.32.2", + "@commercetools-backend/loggers": "^21.25.2", "@commercetools/platform-sdk": "^4.11.0", + "@commercetools/connect-payments-sdk": "^0.10.0", "@commercetools/sdk-client-v2": "^2.5.0", "@mollie/api-client": "^3.7.0", "@types/uuid": "^10.0.0", diff --git a/processor/src/commercetools/action.commercetools.ts b/processor/src/commercetools/action.commercetools.ts index 1904b3a..eaff2d2 100644 --- a/processor/src/commercetools/action.commercetools.ts +++ b/processor/src/commercetools/action.commercetools.ts @@ -2,6 +2,13 @@ import { v4 as uuid } from 'uuid'; import { createDateNowString } from '../utils/app.utils'; import { CustomFields } from '../utils/constant.utils'; import { CTTransactionState, CreateInterfaceInteractionParams } from '../types/commercetools.types'; +import { + CartAddCustomLineItemAction, + CartRemoveCustomLineItemAction, + LocalizedString, + TaxCategoryResourceIdentifier, + _Money, +} from '@commercetools/platform-sdk'; /** * A function that sets custom fields with the given field name and field value. @@ -98,3 +105,46 @@ export const setTransactionCustomType = (transactionId: string, key: string, fie transactionId, }; }; + +export const removeCustomLineItem = (customLineItemId: string): CartRemoveCustomLineItemAction => { + return { + action: 'removeCustomLineItem', + customLineItemId: customLineItemId, + }; +}; + +export const addCustomLineItem = ( + name: LocalizedString, + quantity: number, + money: _Money, + slug: string, + taxCategory?: TaxCategoryResourceIdentifier, +): CartAddCustomLineItemAction => { + if (!taxCategory) { + return { + action: 'addCustomLineItem', + name, + quantity, + money, + slug, + }; + } + + return { + action: 'addCustomLineItem', + name, + quantity, + money, + slug, + taxCategory, + }; +}; + +export const setTransactionCustomField = (name: string, value: string, transactionId: string) => { + return { + action: 'setTransactionCustomField', + name, + value, + transactionId, + }; +}; diff --git a/processor/src/index.ts b/processor/src/index.ts index aca96d1..161d246 100644 --- a/processor/src/index.ts +++ b/processor/src/index.ts @@ -7,12 +7,14 @@ import bodyParser from 'body-parser'; // Import routes import ProcessorRoutes from './routes/processor.route'; import WebhookRoutes from './routes/webhook.route'; +import ApplicationRoutes from './routes/application.route'; // Import logger import { logger } from './utils/logger.utils'; import { readConfiguration } from './utils/config.utils'; import { errorMiddleware } from './middleware/error.middleware'; +import { createSessionMiddleware } from '@commercetools-backend/express'; // Read env variables readConfiguration(); @@ -30,9 +32,17 @@ app.use(bodyParser.urlencoded({ extended: true })); // Define routes app.use('/processor', ProcessorRoutes); app.use('/webhook', WebhookRoutes); +app.use('/application', ApplicationRoutes); // Global error handler app.use(errorMiddleware); +app.use( + ['/application/*'], + createSessionMiddleware({ + audience: readConfiguration().commerceTools.sessionAudience, + issuer: readConfiguration().commerceTools.sessionIssuer, + }), +); // Listen the application const server = app.listen(PORT, () => { diff --git a/processor/src/mollie/payment.mollie.ts b/processor/src/mollie/payment.mollie.ts index 3beea2f..10289a4 100644 --- a/processor/src/mollie/payment.mollie.ts +++ b/processor/src/mollie/payment.mollie.ts @@ -16,6 +16,12 @@ import { getApiKey } from '../utils/config.utils'; import { MOLLIE_VERSION_STRINGS } from '../utils/constant.utils'; import fetch from 'node-fetch'; +const HEADER = { + 'Content-Type': 'application/json', + Authorization: `Bearer ${getApiKey()}`, + versionStrings: MOLLIE_VERSION_STRINGS, +}; + /** * Creates a Mollie payment using the provided payment parameters. * @@ -108,15 +114,9 @@ export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreate let errorMessage; try { - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${getApiKey()}`, - versionStrings: MOLLIE_VERSION_STRINGS, - }; - const response = await fetch('https://api.mollie.com/v2/payments', { method: 'POST', - headers, + headers: HEADER, body: JSON.stringify(paymentParams), }); @@ -149,6 +149,32 @@ export const createPaymentWithCustomMethod = async (paymentParams: PaymentCreate } }; +export const getAllPaymentMethods = async (options: MethodsListParams): Promise> => { + let errorMessage; + + try { + const queryParams = new URLSearchParams(options as any).toString(); + const url = `https://api.mollie.com/v2/methods/all?${queryParams}`; + + const response = await fetch(url, { + method: 'GET', + headers: HEADER, + }); + + const data = await response.json(); + return data; + } catch (error: unknown) { + if (!errorMessage) { + errorMessage = 'SCTM - getAllPaymentMethods - Failed to get all payment methods with unknown errors'; + logger.error(errorMessage, { + error, + }); + } + + throw new CustomError(400, errorMessage); + } +}; + export const getApplePaySession = async (options: ApplePaySessionRequest): Promise => { try { return await initMollieClientForApplePaySession().applePay.requestPaymentSession(options); diff --git a/processor/src/service/payment.service.ts b/processor/src/service/payment.service.ts index ec605b8..f59b381 100644 --- a/processor/src/service/payment.service.ts +++ b/processor/src/service/payment.service.ts @@ -3,10 +3,18 @@ import { CancelStatusText, ConnectorActions, CustomFields, PAY_LATER_ENUMS } fro import { List, Method, Payment as MPayment, PaymentMethod, PaymentStatus, Refund } from '@mollie/api-client'; import { logger } from '../utils/logger.utils'; import { + createCartUpdateActions, createMollieCreatePaymentParams, mapCommercetoolsPaymentCustomFieldsToMollieListParams, } from '../utils/map.utils'; -import { CentPrecisionMoney, Extension, Payment, UpdateAction } from '@commercetools/platform-sdk'; +import { + CartUpdateAction, + CentPrecisionMoney, + CustomObject, + Extension, + Payment, + UpdateAction, +} from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; import { cancelPayment, @@ -22,8 +30,10 @@ import { CTTransaction, CTTransactionState, CTTransactionType, + CustomMethod, molliePaymentToCTStatusMap, mollieRefundToCTStatusMap, + PricingConstraintItem, UpdateActionKey, } from '../types/commercetools.types'; import { @@ -44,6 +54,7 @@ import { changeTransactionState, changeTransactionTimestamp, setCustomFields, + setTransactionCustomField, setTransactionCustomType, } from '../commercetools/action.commercetools'; import { readConfiguration } from '../utils/config.utils'; @@ -56,8 +67,127 @@ import { getPaymentExtension } from '../commercetools/extensions.commercetools'; import { HttpDestination } from '@commercetools/platform-sdk/dist/declarations/src/generated/models/extension'; import { cancelPaymentRefund, createPaymentRefund, getPaymentRefund } from '../mollie/refund.mollie'; import { ApplePaySessionRequest, CustomPayment, SupportedPaymentMethods } from '../types/mollie.types'; -import { parseStringToJsonObject } from '../utils/app.utils'; +import { + calculateTotalSurchargeAmount, + convertCentToEUR, + parseStringToJsonObject, + roundSurchargeAmountToCent, +} from '../utils/app.utils'; import ApplePaySession from '@mollie/api-client/dist/types/src/data/applePaySession/ApplePaySession'; +import { getMethodConfigObjects, getSingleMethodConfigObject } from '../commercetools/customObjects.commercetools'; +import { getCartFromPayment, updateCart } from '../commercetools/cart.commercetools'; +import { removeCartMollieCustomLineItem } from './cart.service'; + +/** + * Validates and sorts the payment methods. + * + * @param {CustomMethod[]} methods - The list of payment methods. + * @param {CustomObject[]} configObjects - The configuration objects. + * @return {CustomMethod[]} - The validated and sorted payment methods. + */ +const validateAndSortMethods = (methods: CustomMethod[], configObjects: CustomObject[]): CustomMethod[] => { + if (!configObjects.length) { + return methods.filter( + (method: CustomMethod) => SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods], + ); + } + + return methods + .filter((method) => isValidMethod(method, configObjects)) + .map((method) => mapMethodToCustomMethod(method, configObjects)) + .sort((a, b) => b.order - a.order); // Descending order sort +}; + +/** + * Checks if a method is valid based on the configuration objects. + * + * @param {CustomMethod} method - The payment method. + * @param {CustomObject[]} configObjects - The configuration objects. + * @return {boolean} - True if the method is valid, false otherwise. + */ +const isValidMethod = (method: CustomMethod, configObjects: CustomObject[]): boolean => { + return ( + !!configObjects.find((config) => config.key === method.id && config.value.status === 'Active') && + !!SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods] + ); +}; + +/** + * Maps a payment method to a custom method. + * + * @param {CustomMethod} method - The payment method. + * @param {CustomObject[]} configObjects - The configuration objects. + * @return {CustomMethod} - The custom method. + */ +const mapMethodToCustomMethod = (method: CustomMethod, configObjects: CustomObject[]): CustomMethod => { + const config = configObjects.find((config) => config.key === method.id); + + return { + id: method.id, + name: config?.value?.name, + description: config?.value?.description, + image: config?.value?.imageUrl, + order: config?.value?.displayOrder || 0, + }; +}; + +/** + * Determines if the card component should be enabled. + * + * @param {CustomMethod[]} validatedMethods - The validated payment methods. + * @return {boolean} - True if the card component should be enabled, false otherwise. + */ +const shouldEnableCardComponent = (validatedMethods: CustomMethod[]): boolean => { + return ( + toBoolean(readConfiguration().mollie.cardComponent, true) && + validatedMethods.some((method) => method.id === PaymentMethod.creditcard) + ); +}; + +const mapMollieMethodToCustomMethod = (method: Method) => ({ + id: method.id, + name: { 'en-GB': method.description }, + description: { 'en-GB': '' }, + image: method.image.svg, + order: 0, +}); + +const getBillingCountry = (ctPayment: Payment): string | undefined => { + const requestField = ctPayment.custom?.fields[CustomFields.payment.request]; + return requestField ? JSON.parse(requestField).billingCountry : undefined; +}; + +const filterMethodsByPricingConstraints = ( + methods: CustomMethod[], + configObjects: CustomObject[], + ctPayment: Payment, + billingCountry: string, +) => { + const currencyCode = ctPayment.amountPlanned.currencyCode; + const amount = convertCentToEUR(ctPayment.amountPlanned.centAmount, ctPayment.amountPlanned.fractionDigits); + + configObjects.forEach((item: CustomObject) => { + const pricingConstraint = item.value.pricingConstraints?.find((constraint: PricingConstraintItem) => { + return constraint.countryCode === billingCountry && constraint.currencyCode === currencyCode; + }) as PricingConstraintItem; + + if (pricingConstraint) { + const surchargeAmount = calculateTotalSurchargeAmount(ctPayment, pricingConstraint.surchargeCost); + const amountIncludedSurcharge = amount + surchargeAmount; + + if ( + (pricingConstraint.minAmount && amount < pricingConstraint.minAmount) || + (pricingConstraint.maxAmount && amount > pricingConstraint.maxAmount) || + (pricingConstraint.maxAmount && amountIncludedSurcharge > pricingConstraint.maxAmount) + ) { + const index = methods.findIndex((method) => method.id === item.value.id); + if (index !== -1) { + methods.splice(index, 1); + } + } + } + }); +}; /** * Handles listing payment methods by payment. @@ -70,29 +200,36 @@ export const handleListPaymentMethodsByPayment = async (ctPayment: Payment): Pro try { const mollieOptions = await mapCommercetoolsPaymentCustomFieldsToMollieListParams(ctPayment); const methods: List = await listPaymentMethods(mollieOptions); - const enableCardComponent = - toBoolean(readConfiguration().mollie.cardComponent, true) && - methods.filter((method: Method) => method.id === PaymentMethod.creditcard).length > 0; - const ctUpdateActions: UpdateAction[] = []; - - if (enableCardComponent) { - methods.splice( - methods.findIndex((method: Method) => method.id === PaymentMethod.creditcard), - 1, - ); + const configObjects: CustomObject[] = await getMethodConfigObjects(); + + const billingCountry = getBillingCountry(ctPayment); + + if (!billingCountry) { + logger.error(`SCTM - listPaymentMethodsByPayment - billingCountry is not provided.`, { + commerceToolsPaymentId: ctPayment.id, + }); + throw new CustomError(400, 'billingCountry is not provided.'); + } + + const customMethods = methods.map(mapMollieMethodToCustomMethod); + + const validatedMethods = validateAndSortMethods(customMethods, configObjects); + + const enableCardComponent = shouldEnableCardComponent(validatedMethods); + + if (billingCountry) { + filterMethodsByPricingConstraints(validatedMethods, configObjects, ctPayment, billingCountry); } const availableMethods = JSON.stringify({ - count: methods.length, - methods: methods.length - ? methods.filter((method: Method) => SupportedPaymentMethods[method.id.toString() as SupportedPaymentMethods]) - : [], + count: validatedMethods.length, + methods: validatedMethods.length ? validatedMethods : [], }); - ctUpdateActions.push( + const ctUpdateActions: UpdateAction[] = [ setCustomFields(CustomFields.payment.profileId, enableCardComponent ? readConfiguration().mollie.profileId : ''), - ); - ctUpdateActions.push(setCustomFields(CustomFields.payment.response, availableMethods)); + setCustomFields(CustomFields.payment.response, availableMethods), + ]; return { statusCode: 200, @@ -107,7 +244,7 @@ export const handleListPaymentMethodsByPayment = async (ctPayment: Payment): Pro }, ); if (error instanceof CustomError) { - Promise.reject(error); + return Promise.reject(error); } return { statusCode: 200, actions: [] }; @@ -166,6 +303,10 @@ export const handlePaymentWebhook = async (paymentId: string): Promise await updatePayment(ctPayment, action as PaymentUpdateAction[]); + if (molliePayment.status === PaymentStatus.canceled) { + await removeCartMollieCustomLineItem(ctPayment); + } + return true; }; @@ -250,7 +391,29 @@ export const getPaymentStatusUpdateAction = ( export const handleCreatePayment = async (ctPayment: Payment): Promise => { const extensionUrl = (((await getPaymentExtension()) as Extension)?.destination as HttpDestination).url; - const paymentParams = createMollieCreatePaymentParams(ctPayment, extensionUrl); + const cart = await getCartFromPayment(ctPayment.id); + + const [method] = ctPayment?.paymentMethodInfo?.method?.split(',') ?? [null, null]; + + logger.debug(`SCTM - handleCreatePayment - Getting customized configuration for payment method: ${method}`); + const paymentMethodConfig = await getSingleMethodConfigObject(method as string); + const billingCountry = getBillingCountry(ctPayment); + + const pricingConstraint = paymentMethodConfig.value.pricingConstraints?.find((constraint: PricingConstraintItem) => { + return ( + constraint.countryCode === billingCountry && constraint.currencyCode === ctPayment.amountPlanned.currencyCode + ); + }) as PricingConstraintItem; + + logger.debug(`SCTM - handleCreatePayment - Calculating total surcharge amount`); + const surchargeAmountInCent = pricingConstraint + ? roundSurchargeAmountToCent( + calculateTotalSurchargeAmount(ctPayment, pricingConstraint.surchargeCost), + ctPayment.amountPlanned.fractionDigits, + ) + : 0; + + const paymentParams = createMollieCreatePaymentParams(ctPayment, extensionUrl, surchargeAmountInCent, cart); let molliePayment; if (PaymentMethod[paymentParams.method as PaymentMethod]) { @@ -265,7 +428,12 @@ export const handleCreatePayment = async (ctPayment: Payment): Promise 0) { + await updateCart(cart, cartUpdateActions as CartUpdateAction[]); + } + + const ctActions = await getCreatePaymentUpdateAction(molliePayment, ctPayment, surchargeAmountInCent); return { statusCode: 201, @@ -281,7 +449,11 @@ export const handleCreatePayment = async (ctPayment: Payment): Promise} A promise that resolves to an array of update actions. * @throws {Error} If the original transaction is not found. */ -export const getCreatePaymentUpdateAction = async (molliePayment: MPayment | CustomPayment, CTPayment: Payment) => { +export const getCreatePaymentUpdateAction = async ( + molliePayment: MPayment | CustomPayment, + CTPayment: Payment, + surchargeAmountInCent: number, +) => { try { // Find the original transaction which triggered create order const originalTransaction = CTPayment.transactions?.find((transaction) => { @@ -317,7 +489,7 @@ export const getCreatePaymentUpdateAction = async (molliePayment: MPayment | Cus sctm_created_at: molliePayment.createdAt, }; - return Promise.resolve([ + const actions: UpdateAction[] = [ // Add interface interaction addInterfaceInteraction(interfaceInteractionParams), // Update transaction interactionId @@ -326,7 +498,20 @@ export const getCreatePaymentUpdateAction = async (molliePayment: MPayment | Cus changeTransactionTimestamp(originalTransaction.id, molliePayment.createdAt), // Update transaction state changeTransactionState(originalTransaction.id, CTTransactionState.Pending), - ]); + ]; + + if (surchargeAmountInCent > 0) { + // Add surcharge amount to the custom field of the transaction + actions.push( + setTransactionCustomField( + CustomFields.transactionSurchargeCost, + JSON.stringify({ surchargeAmountInCent }), + originalTransaction.id, + ), + ); + } + + return Promise.resolve(actions); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { return Promise.reject(error); @@ -540,6 +725,8 @@ export const handleCancelPayment = async (ctPayment: Payment): Promise; + description: Record; + image: string; + order: number; + pricingConstraints?: PricingConstraintItem[]; +}; + +export type PricingConstraintItem = { + id?: number; + countryCode: string; + currencyCode: string; + minAmount: number; + maxAmount?: number; + surchargeCost?: SurchargeCost; +}; + +export type SurchargeCost = { + percentageAmount: number; + fixedAmount: number; +}; diff --git a/processor/src/utils/app.utils.ts b/processor/src/utils/app.utils.ts index 23cdc3e..5c30a59 100644 --- a/processor/src/utils/app.utils.ts +++ b/processor/src/utils/app.utils.ts @@ -1,3 +1,5 @@ +import { SurchargeCost } from './../types/commercetools.types'; +import { Payment } from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; import { logger } from './logger.utils'; /** @@ -79,3 +81,23 @@ export function validateEmail(email: string): boolean { return emailRegex.test(email); } + +export const convertCentToEUR = (amount: number, fractionDigits: number): number => { + return amount / Math.pow(10, fractionDigits); +}; + +export const calculateTotalSurchargeAmount = (ctPayment: Payment, surcharges?: SurchargeCost): number => { + if (!surcharges) { + return 0; + } + const amount = convertCentToEUR(ctPayment.amountPlanned.centAmount, ctPayment.amountPlanned.fractionDigits); + + const percentageAmount = surcharges.percentageAmount ?? 0; + const fixedAmount = surcharges.fixedAmount ?? 0; + + return (amount * percentageAmount) / 100 + fixedAmount; +}; + +export const roundSurchargeAmountToCent = (surchargeAmountInEur: number, fractionDigits: number): number => { + return Math.round(surchargeAmountInEur * Math.pow(10, fractionDigits)); +}; diff --git a/processor/src/utils/config.utils.ts b/processor/src/utils/config.utils.ts index 1d2b6d0..65d0e76 100644 --- a/processor/src/utils/config.utils.ts +++ b/processor/src/utils/config.utils.ts @@ -16,8 +16,9 @@ export const readConfiguration = () => { projectKey: process.env.CTP_PROJECT_KEY as string, scope: process.env.CTP_SCOPE as string, region: process.env.CTP_REGION as string, - authUrl: process.env.CTP_AUTH_URL as string, authMode: process.env.AUTHENTICATION_MODE as string, + sessionAudience: (process.env.CTP_SESSION_AUDIENCE as string) || 'https://mc.europe-west1.gcp.commercetools.com', + sessionIssuer: (process.env.CTP_SESSION_ISSUER as string) || 'gcp-eu', }, mollie: { testApiKey: process.env.MOLLIE_API_TEST_KEY as string, diff --git a/processor/src/utils/constant.utils.ts b/processor/src/utils/constant.utils.ts index 9cd855e..3e39cee 100644 --- a/processor/src/utils/constant.utils.ts +++ b/processor/src/utils/constant.utils.ts @@ -38,6 +38,7 @@ export const CustomFields = { response: 'sctm_apple_pay_session_response', }, }, + transactionSurchargeCost: 'sctm_transaction_surcharge_cost', }; export enum ConnectorActions { @@ -61,3 +62,11 @@ export const CancelStatusText = 'Cancelled from shop side'; export const DUE_DATE_PATTERN = /^(\d+)d$/; export const DEFAULT_DUE_DATE = 14; + +export const CUSTOM_OBJECT_CONTAINER_NAME = 'sctm-app-methods'; + +export const MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM = 'mollie-surcharge-line-item'; + +export const MOLLIE_SURCHARGE_LINE_DESCRIPTION = 'Total surcharge amount'; + +export const MOLLIE_SHIPPING_LINE_DESCRIPTION = 'Shipping amount'; diff --git a/processor/src/utils/map.utils.ts b/processor/src/utils/map.utils.ts index f7f3570..e372d42 100644 --- a/processor/src/utils/map.utils.ts +++ b/processor/src/utils/map.utils.ts @@ -1,12 +1,18 @@ -import { CustomFields } from './constant.utils'; +import { + CustomFields, + MOLLIE_SHIPPING_LINE_DESCRIPTION, + MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + MOLLIE_SURCHARGE_LINE_DESCRIPTION, +} from './constant.utils'; import { logger } from './logger.utils'; import { calculateDueDate, makeMollieAmount } from './mollie.utils'; import { CustomPaymentMethod, ParsedMethodsRequestType } from '../types/mollie.types'; -import { Payment } from '@commercetools/platform-sdk'; +import { Cart, CartUpdateAction, Payment, TaxCategoryResourceIdentifier } from '@commercetools/platform-sdk'; import CustomError from '../errors/custom.error'; import { MethodsListParams, PaymentCreateParams, PaymentMethod } from '@mollie/api-client'; -import { parseStringToJsonObject, removeEmptyProperties } from './app.utils'; +import { convertCentToEUR, parseStringToJsonObject, removeEmptyProperties } from './app.utils'; import { readConfiguration } from './config.utils'; +import { addCustomLineItem, removeCustomLineItem } from '../commercetools/action.commercetools'; const extractMethodsRequest = (ctPayment: Payment): ParsedMethodsRequestType | undefined => { return parseStringToJsonObject( @@ -104,7 +110,12 @@ const getSpecificPaymentParams = (method: PaymentMethod | CustomPaymentMethod, p } }; -export const createMollieCreatePaymentParams = (payment: Payment, extensionUrl: string): PaymentCreateParams => { +export const createMollieCreatePaymentParams = ( + payment: Payment, + extensionUrl: string, + surchargeAmountInCent: number, + cart: Cart, +): PaymentCreateParams => { const { amountPlanned, paymentMethodInfo } = payment; const [method, issuer] = paymentMethodInfo?.method?.split(',') ?? [null, null]; @@ -115,10 +126,36 @@ export const createMollieCreatePaymentParams = (payment: Payment, extensionUrl: payment.id, ); + const mollieLines = paymentRequest.lines ?? []; + + // Add another line for creating Mollie payment request if surcharge exists + if (surchargeAmountInCent > 0) { + mollieLines.push( + createMollieLineForAdditionalAmount( + MOLLIE_SURCHARGE_LINE_DESCRIPTION, + surchargeAmountInCent, + amountPlanned.fractionDigits, + amountPlanned.currencyCode, + ), + ); + } + + // Add another line for creating Mollie payment request if shipping cost exists + if (cart?.shippingInfo?.price) { + mollieLines.push( + createMollieLineForAdditionalAmount( + MOLLIE_SHIPPING_LINE_DESCRIPTION, + cart.shippingInfo.price.centAmount, + cart.shippingInfo.price.fractionDigits, + cart.shippingInfo.price.currencyCode, + ), + ); + } + const defaultWebhookEndpoint = new URL(extensionUrl).origin + '/webhook'; const createPaymentParams = { - amount: makeMollieAmount(amountPlanned), + amount: makeMollieAmount(amountPlanned, surchargeAmountInCent), description: paymentRequest.description ?? '', redirectUrl: paymentRequest.redirectUrl ?? null, webhookUrl: defaultWebhookEndpoint, @@ -132,8 +169,70 @@ export const createMollieCreatePaymentParams = (payment: Payment, extensionUrl: applicationFee: paymentRequest.applicationFee ?? {}, include: paymentRequest.include ?? '', captureMode: paymentRequest.captureMode ?? '', + lines: mollieLines, ...getSpecificPaymentParams(method as PaymentMethod, paymentRequest), }; return removeEmptyProperties(createPaymentParams) as PaymentCreateParams; }; + +export const createCartUpdateActions = ( + cart: Cart, + ctPayment: Payment, + surchargeAmountInCent: number, +): CartUpdateAction[] => { + const mollieSurchargeCustomLine = cart.customLineItems.find((item) => { + return item.key === MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM; + }); + + const updateActions: CartUpdateAction[] = []; + + if (mollieSurchargeCustomLine) { + updateActions.push(removeCustomLineItem(mollieSurchargeCustomLine.id)); + } + + if (surchargeAmountInCent > 0) { + const name = { + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }; + + const money = { + centAmount: surchargeAmountInCent, + currencyCode: ctPayment.amountPlanned.currencyCode, + }; + + const slug = MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM; + const taxCategory = cart.shippingInfo?.taxCategory?.id + ? ({ + id: cart.shippingInfo.taxCategory?.id, + } as TaxCategoryResourceIdentifier) + : undefined; + + updateActions.push(addCustomLineItem(name, 1, money, slug, taxCategory)); + } + + return updateActions; +}; + +export const createMollieLineForAdditionalAmount = ( + description: string, + amountInCent: number, + fractionDigits: number, + currency: string, + quantity: number = 1, + quantityUnit: string = 'pcs', +) => { + const unitPrice = { + currency, + value: convertCentToEUR(amountInCent, fractionDigits).toFixed(2), + }; + + return { + description, + quantity, + quantityUnit, + unitPrice: unitPrice, + totalAmount: unitPrice, + }; +}; diff --git a/processor/src/utils/mollie.utils.ts b/processor/src/utils/mollie.utils.ts index 9f3cf89..afcc058 100644 --- a/processor/src/utils/mollie.utils.ts +++ b/processor/src/utils/mollie.utils.ts @@ -11,9 +11,12 @@ const convertCTToMollieAmountValue = (ctValue: number, fractionDigits = 2): stri return (ctValue / divider).toFixed(fractionDigits); }; -export const makeMollieAmount = ({ centAmount, fractionDigits, currencyCode }: CentPrecisionMoney): Amount => { +export const makeMollieAmount = ( + { centAmount, fractionDigits, currencyCode }: CentPrecisionMoney, + surchargeAmountInCent: number = 0, +): Amount => { return { - value: convertCTToMollieAmountValue(centAmount, fractionDigits), + value: convertCTToMollieAmountValue(centAmount + surchargeAmountInCent, fractionDigits), currency: currencyCode, }; }; diff --git a/processor/src/validators/env.validators.ts b/processor/src/validators/env.validators.ts index 243bfab..fba7ebe 100644 --- a/processor/src/validators/env.validators.ts +++ b/processor/src/validators/env.validators.ts @@ -1,4 +1,4 @@ -import { optional, standardKey, standardString, region, standardDueDate, standardUrl } from './helpers.validators'; +import { optional, standardKey, standardString, region, standardDueDate } from './helpers.validators'; /** * Create here your own validators @@ -46,11 +46,25 @@ const envValidators = [ referencedBy: 'environmentVariables', }), - standardUrl(['commerceTools', 'authUrl'], { - code: 'InvalidAuthUrl', - message: 'Not a valid url.', - referencedBy: 'environmentVariables', - }), + standardString( + ['commerceTools', 'sessionAudience'], + { + code: 'InvalidSessionAudience', + message: 'Not a valid sessionAudience.', + referencedBy: 'environmentVariables', + }, + { min: 1, max: undefined }, + ), + + standardString( + ['commerceTools', 'sessionIssuer'], + { + code: 'InvalidSessionIssuer', + message: 'Not a valid sessionIssuer.', + referencedBy: 'environmentVariables', + }, + { min: 1, max: undefined }, + ), standardKey(['mollie', 'testApiKey'], { code: 'InvalidMollieTestApiKey', diff --git a/processor/tests/commercetools/action.commercetools.spec.ts b/processor/tests/commercetools/action.commercetools.spec.ts index 5675b35..3035ef1 100644 --- a/processor/tests/commercetools/action.commercetools.spec.ts +++ b/processor/tests/commercetools/action.commercetools.spec.ts @@ -1,11 +1,14 @@ -import { ConnectorActions } from '../../src/utils/constant.utils'; +import { ConnectorActions, CustomFields, MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM } from '../../src/utils/constant.utils'; import { describe, test, expect, jest } from '@jest/globals'; import { + addCustomLineItem, addInterfaceInteraction, changeTransactionInteractionId, changeTransactionState, changeTransactionTimestamp, + removeCustomLineItem, setCustomFields, + setTransactionCustomField, setTransactionCustomType, } from '../../src/commercetools/action.commercetools'; import { CTTransactionState, CreateInterfaceInteractionParams } from '../../src/types/commercetools.types'; @@ -151,4 +154,48 @@ describe('Test actions.utils.ts', () => { transactionId, }); }); + + test('should be able to return the correct removeCustomLineItem action', () => { + const customId = 'custom-id'; + expect(removeCustomLineItem(customId)).toStrictEqual({ + action: 'removeCustomLineItem', + customLineItemId: customId, + }); + }); + + test('should be able to return the correct addCustomLineItem action', () => { + const name = { + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }; + const quantity = 1; + const money = { + centAmount: 100, + currencyCode: 'EUR', + }; + const slug = MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM; + + expect(addCustomLineItem(name, quantity, money, slug)).toStrictEqual({ + action: 'addCustomLineItem', + name, + quantity, + money, + slug, + }); + }); + + test('should be able to return the correct setTransactionCustomField action', () => { + const name = CustomFields.transactionSurchargeCost; + const surchargeInCentAmount = { + surchargeInCentAmount: 12345, + }; + const transactionId = 'test'; + + expect(setTransactionCustomField(name, JSON.stringify(surchargeInCentAmount), transactionId)).toStrictEqual({ + action: 'setTransactionCustomField', + name, + value: JSON.stringify(surchargeInCentAmount), + transactionId, + }); + }); }); diff --git a/processor/tests/service/payment.service.spec.ts b/processor/tests/service/payment.service.spec.ts index 43dc30a..27a5147 100644 --- a/processor/tests/service/payment.service.spec.ts +++ b/processor/tests/service/payment.service.spec.ts @@ -1,5 +1,9 @@ +import { + getMethodConfigObjects, + getSingleMethodConfigObject, +} from './../../src/commercetools/customObjects.commercetools'; import { afterEach, beforeEach, describe, expect, it, jest, test } from '@jest/globals'; -import { CustomFields, Payment } from '@commercetools/platform-sdk'; +import { Cart, CustomFields, Payment } from '@commercetools/platform-sdk'; import { getCreatePaymentUpdateAction, getPaymentCancelActions, @@ -13,7 +17,12 @@ import { handlePaymentWebhook, } from '../../src/service/payment.service'; import { ControllerResponseType } from '../../src/types/controller.types'; -import { CancelStatusText, ConnectorActions, CustomFields as CustomFieldName } from '../../src/utils/constant.utils'; +import { + CancelStatusText, + ConnectorActions, + CustomFields as CustomFieldName, + MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, +} from '../../src/utils/constant.utils'; import { Payment as molliePayment, PaymentStatus, Refund, RefundStatus } from '@mollie/api-client'; import { ChangeTransactionState, @@ -36,10 +45,13 @@ import { logger } from '../../src/utils/logger.utils'; import { getPaymentByMolliePaymentId, updatePayment } from '../../src/commercetools/payment.commercetools'; import { CreateParameters } from '@mollie/api-client/dist/types/src/binders/payments/refunds/parameters'; import { getPaymentExtension } from '../../src/commercetools/extensions.commercetools'; -import { createMollieCreatePaymentParams } from '../../src/utils/map.utils'; +import { createCartUpdateActions, createMollieCreatePaymentParams } from '../../src/utils/map.utils'; import { CustomPayment } from '../../src/types/mollie.types'; import { changeTransactionState } from '../../src/commercetools/action.commercetools'; import { makeCTMoney, shouldRefundStatusUpdate } from '../../src/utils/mollie.utils'; +import { getCartFromPayment, updateCart } from '../../src/commercetools/cart.commercetools'; +import { calculateTotalSurchargeAmount } from '../../src/utils/app.utils'; +import { removeCartMollieCustomLineItem } from '../../src/service/cart.service'; const uuid = '5c8b0375-305a-4f19-ae8e-07806b101999'; jest.mock('uuid', () => ({ @@ -55,6 +67,16 @@ jest.mock('../../src/commercetools/payment.commercetools', () => ({ updatePayment: jest.fn(), })); +jest.mock('../../src/commercetools/cart.commercetools', () => ({ + getCartFromPayment: jest.fn(), + updateCart: jest.fn(), +})); + +jest.mock('../../src/commercetools/customObjects.commercetools', () => ({ + getMethodConfigObjects: jest.fn(), + getSingleMethodConfigObject: jest.fn(), +})); + jest.mock('../../src/service/payment.service.ts', () => ({ ...(jest.requireActual('../../src/service/payment.service.ts') as object), getCreatePaymentUpdateAction: jest.fn(), @@ -144,6 +166,142 @@ describe('Test listPaymentMethodsByPayment', () => { }, }, }, + { + resource: 'method', + id: 'bancontact', + description: 'Bancontact', + minimumAmount: { value: '0.01', currency: 'EUR' }, + maximumAmount: null, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/paypal.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/paypal%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/paypal.svg', + }, + status: 'activated', + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/paypal', + type: 'application/hal+json', + }, + }, + }, + { + resource: 'method', + id: 'banktransfer', + description: 'banktransfer', + minimumAmount: { value: '0.01', currency: 'EUR' }, + maximumAmount: null, + image: { + size1x: 'https://www.mollie.com/external/icons/payment-methods/paypal.png', + size2x: 'https://www.mollie.com/external/icons/payment-methods/paypal%402x.png', + svg: 'https://www.mollie.com/external/icons/payment-methods/paypal.svg', + }, + status: 'activated', + _links: { + self: { + href: 'https://api.mollie.com/v2/methods/paypal', + type: 'application/hal+json', + }, + }, + }, + ]); + + (getMethodConfigObjects as jest.Mock).mockReturnValueOnce([ + { + id: 'e561ae8b-5d55-4659-b4a7-3bf13a177eaf', + version: 2, + versionModifiedAt: '2024-10-15T10:04:28.910Z', + createdAt: '2024-10-15T04:32:39.858Z', + lastModifiedAt: '2024-10-15T10:04:28.910Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'paypal', + value: { + id: 'paypal', + name: { + 'en-GB': 'PayPal', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/applepay.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 3000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: 'a8cfe2c6-c40e-4611-900d-faf3a0fc517c', + version: 1, + versionModifiedAt: '2024-10-15T04:32:39.962Z', + createdAt: '2024-10-15T04:32:39.962Z', + lastModifiedAt: '2024-10-15T04:32:39.962Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'bancontact', + value: { + id: 'bancontact', + name: { + 'en-GB': 'bancontact', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 44000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: '5ca6f09f-2e10-4d0e-8397-467925a28568', + version: 3, + versionModifiedAt: '2024-10-15T10:05:33.573Z', + createdAt: '2024-10-15T04:32:39.923Z', + lastModifiedAt: '2024-10-15T10:05:33.573Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'banktransfer', + value: { + id: 'banktransfer', + name: { + 'en-GB': 'Bank Transfer', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + surchargeCost: '2%', + }, + ], + }, + }, ]); mockResource = { @@ -155,13 +313,14 @@ describe('Test listPaymentMethodsByPayment', () => { amountPlanned: { type: 'centPrecision', currencyCode: 'EUR', - centAmount: 1000, + centAmount: 500000, fractionDigits: 2, }, custom: { fields: { sctm_payment_methods_request: JSON.stringify({ locale: 'de_DE', + billingCountry: 'DE', }), }, } as unknown as CustomFields, @@ -172,11 +331,139 @@ describe('Test listPaymentMethodsByPayment', () => { expect(response.statusCode).toBe(200); expect(response?.actions?.length).toBeGreaterThan(0); expect(response?.actions?.[0]?.action).toBe('setCustomField'); + expect((response?.actions?.[1] as any)?.value).toBe( + JSON.stringify({ + count: 2, + methods: [ + { + id: 'bancontact', + name: { + 'en-GB': 'bancontact', + }, + description: { + 'en-GB': '', + }, + image: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + order: 0, + }, + { + id: 'banktransfer', + name: { + 'en-GB': 'Bank Transfer', + }, + description: { + 'en-GB': '', + }, + image: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + order: 0, + }, + ], + }), + ); }); test('call listPaymentMethodsByPayment with no object reference', async () => { (listPaymentMethods as jest.Mock).mockReturnValueOnce([]); + (getMethodConfigObjects as jest.Mock).mockReturnValueOnce([ + { + id: 'e561ae8b-5d55-4659-b4a7-3bf13a177eaf', + version: 2, + versionModifiedAt: '2024-10-15T10:04:28.910Z', + createdAt: '2024-10-15T04:32:39.858Z', + lastModifiedAt: '2024-10-15T10:04:28.910Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'paypal', + value: { + id: 'paypal', + name: { + 'en-GB': 'PayPal', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/applepay.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 3000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: 'a8cfe2c6-c40e-4611-900d-faf3a0fc517c', + version: 1, + versionModifiedAt: '2024-10-15T04:32:39.962Z', + createdAt: '2024-10-15T04:32:39.962Z', + lastModifiedAt: '2024-10-15T04:32:39.962Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'bancontact', + value: { + id: 'bancontact', + name: { + 'en-GB': 'bancontact', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 44000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: '5ca6f09f-2e10-4d0e-8397-467925a28568', + version: 3, + versionModifiedAt: '2024-10-15T10:05:33.573Z', + createdAt: '2024-10-15T04:32:39.923Z', + lastModifiedAt: '2024-10-15T10:05:33.573Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'banktransfer', + value: { + id: 'banktransfer', + name: { + 'en-GB': 'Bank Transfer', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 300000, + surchargeCost: '2%', + }, + ], + }, + }, + ]); + mockResource = { typeId: 'payment', paymentMethodInfo: { @@ -189,6 +476,14 @@ describe('Test listPaymentMethodsByPayment', () => { centAmount: 1000, fractionDigits: 2, }, + custom: { + fields: { + sctm_payment_methods_request: JSON.stringify({ + locale: 'de_DE', + billingCountry: 'DE', + }), + }, + } as unknown as CustomFields, } as unknown as Payment; const response: ControllerResponseType = await handleListPaymentMethodsByPayment(mockResource); @@ -216,6 +511,7 @@ describe('Test listPaymentMethodsByPayment', () => { fields: { sctm_payment_methods_request: JSON.stringify({ locale: 'de_DE', + billingCountry: 'DE', }), }, } as unknown as CustomFields, @@ -230,6 +526,37 @@ describe('Test listPaymentMethodsByPayment', () => { expect(JSON.stringify(response)).not.toContain('count'); }); + test('call listPaymentMethodsByPayment with billingCountry', async () => { + mockResource = { + id: 'RANDOMID_12345', + paymentMethodInfo: { + paymentInterface: 'mollie', + method: 'card', + }, + amountPlanned: { + type: 'centPrecision', + currencyCode: 'VND', + centAmount: 1000, + fractionDigits: 2, + }, + custom: { + fields: { + sctm_payment_methods_request: JSON.stringify({ + locale: 'de_DE', + }), + }, + } as unknown as CustomFields, + } as unknown as Payment; + + try { + await handleListPaymentMethodsByPayment(mockResource); + } catch (error: unknown) { + expect(error).toBeInstanceOf(CustomError); + expect((error as CustomError).message).toBe('billingCountry is not provided.'); + expect((error as CustomError).statusCode).toBe(400); + } + }); + test('call listPaymentMethodsByPayment with cardComponent deactivated', async () => { (listPaymentMethods as jest.Mock).mockReturnValueOnce([ { @@ -272,6 +599,112 @@ describe('Test listPaymentMethodsByPayment', () => { }, ]); + (getMethodConfigObjects as jest.Mock).mockReturnValueOnce([ + { + id: 'e561ae8b-5d55-4659-b4a7-3bf13a177eaf', + version: 2, + versionModifiedAt: '2024-10-15T10:04:28.910Z', + createdAt: '2024-10-15T04:32:39.858Z', + lastModifiedAt: '2024-10-15T10:04:28.910Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'paypal', + value: { + id: 'paypal', + name: { + 'en-GB': 'PayPal', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/applepay.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 3000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: 'a8cfe2c6-c40e-4611-900d-faf3a0fc517c', + version: 1, + versionModifiedAt: '2024-10-15T04:32:39.962Z', + createdAt: '2024-10-15T04:32:39.962Z', + lastModifiedAt: '2024-10-15T04:32:39.962Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'creditcard', + value: { + id: 'creditcard', + name: { + 'en-GB': 'creditcard', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 0, + maxAmount: 100000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: '5ca6f09f-2e10-4d0e-8397-467925a28568', + version: 3, + versionModifiedAt: '2024-10-15T10:05:33.573Z', + createdAt: '2024-10-15T04:32:39.923Z', + lastModifiedAt: '2024-10-15T10:05:33.573Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'banktransfer', + value: { + id: 'banktransfer', + name: { + 'en-GB': 'Bank Transfer', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 300000, + surchargeCost: '2%', + }, + ], + }, + }, + ]); + + const cart = { + id: 'cart-id', + country: 'DE', + }; + + (getCartFromPayment as jest.Mock).mockReturnValue(cart); + mockResource = { id: 'RANDOMID_12345', paymentMethodInfo: { @@ -281,13 +714,14 @@ describe('Test listPaymentMethodsByPayment', () => { amountPlanned: { type: 'centPrecision', currencyCode: 'EUR', - centAmount: 1000, + centAmount: 200000, fractionDigits: 2, }, custom: { fields: { sctm_payment_methods_request: JSON.stringify({ locale: 'de_DE', + billingCountry: 'DE', }), }, } as unknown as CustomFields, @@ -348,6 +782,110 @@ describe('Test listPaymentMethodsByPayment', () => { }, ]); + (getMethodConfigObjects as jest.Mock).mockReturnValueOnce([ + { + id: 'e561ae8b-5d55-4659-b4a7-3bf13a177eaf', + version: 2, + versionModifiedAt: '2024-10-15T10:04:28.910Z', + createdAt: '2024-10-15T04:32:39.858Z', + lastModifiedAt: '2024-10-15T10:04:28.910Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'creditcard', + value: { + id: 'creditcard', + name: { + 'en-GB': 'creditcard', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/applepay.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 0, + maxAmount: 3000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: 'a8cfe2c6-c40e-4611-900d-faf3a0fc517c', + version: 1, + versionModifiedAt: '2024-10-15T04:32:39.962Z', + createdAt: '2024-10-15T04:32:39.962Z', + lastModifiedAt: '2024-10-15T04:32:39.962Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'bancontact', + value: { + id: 'bancontact', + name: { + 'en-GB': 'bancontact', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/bancontact.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 44000, + surchargeCost: '2%', + }, + ], + }, + }, + { + id: '5ca6f09f-2e10-4d0e-8397-467925a28568', + version: 3, + versionModifiedAt: '2024-10-15T10:05:33.573Z', + createdAt: '2024-10-15T04:32:39.923Z', + lastModifiedAt: '2024-10-15T10:05:33.573Z', + lastModifiedBy: '', + createdBy: '', + container: 'sctm-app-methods', + key: 'banktransfer', + value: { + id: 'banktransfer', + name: { + 'en-GB': 'Bank Transfer', + }, + description: { + 'en-GB': '', + }, + imageUrl: 'https://www.mollie.com/external/icons/payment-methods/banktransfer.svg', + status: 'Active', + displayOrder: 0, + pricingConstraints: [ + { + currencyCode: 'EUR', + countryCode: 'DE', + minAmount: 1000, + maxAmount: 300000, + surchargeCost: '2%', + }, + ], + }, + }, + ]); + + (getCartFromPayment as jest.Mock).mockReturnValue({ + id: 'cart-id', + country: 'DE', + } as Cart); + mockResource = { id: 'RANDOMID_12345', paymentMethodInfo: { @@ -364,6 +902,7 @@ describe('Test listPaymentMethodsByPayment', () => { fields: { sctm_payment_methods_request: JSON.stringify({ locale: 'de_DE', + billingCountry: 'DE', }), }, } as unknown as CustomFields, @@ -381,7 +920,7 @@ describe('Test listPaymentMethodsByPayment', () => { name: 'sctm_mollie_profile_id', value: process.env.MOLLIE_PROFILE_ID, }); - expect(JSON.stringify(response)).not.toContain('creditcard'); + expect(JSON.stringify(response)).toContain('creditcard'); }); }); @@ -444,11 +983,11 @@ describe('Test getCreatePaymentUpdateAction', () => { (getCreatePaymentUpdateAction as jest.Mock).mockImplementationOnce(() => { const paymentService = jest.requireActual( '../../src/service/payment.service.ts', - ) as typeof import('../../src/service/payment.service.ts'); - return paymentService.getCreatePaymentUpdateAction(molliePayment, CTPayment); + ) as typeof import('../../src/service/payment.service'); + return paymentService.getCreatePaymentUpdateAction(molliePayment, CTPayment, 0); }); - getCreatePaymentUpdateAction(molliePayment, CTPayment).catch((error) => { + getCreatePaymentUpdateAction(molliePayment, CTPayment, 0).catch((error) => { expect(error).toEqual({ status: 400, title: 'Cannot find original transaction', @@ -509,8 +1048,8 @@ describe('Test getCreatePaymentUpdateAction', () => { (getCreatePaymentUpdateAction as jest.Mock).mockImplementationOnce(() => { const paymentService = jest.requireActual( '../../src/service/payment.service.ts', - ) as typeof import('../../src/service/payment.service.ts'); - return paymentService.getCreatePaymentUpdateAction(molliePayment, CTPayment); + ) as typeof import('../../src/service/payment.service'); + return paymentService.getCreatePaymentUpdateAction(molliePayment, CTPayment, 0); }); (changeTransactionState as jest.Mock).mockReturnValueOnce({ @@ -519,7 +1058,7 @@ describe('Test getCreatePaymentUpdateAction', () => { transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', }); - const actual = await getCreatePaymentUpdateAction(molliePayment, CTPayment); + const actual = await getCreatePaymentUpdateAction(molliePayment, CTPayment, 0); expect(actual).toHaveLength(4); expect(actual[0]).toEqual({ @@ -561,6 +1100,120 @@ describe('Test getCreatePaymentUpdateAction', () => { state: CTTransactionState.Pending, }); }); + + test('should return an array of actions included setTransactionCustomField when surcharge amount is not 0', async () => { + const CTPayment: Payment = { + id: '5c8b0375-305a-4f19-ae8e-07806b101999', + version: 1, + createdAt: '2024-07-04T14:07:35.625Z', + lastModifiedAt: '2024-07-04T14:07:35.625Z', + amountPlanned: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + paymentStatus: {}, + transactions: [ + { + id: '5c8b0375-305a-4f19-ae8e-07806b101999', + type: 'Authorization', + amount: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 1000, + fractionDigits: 2, + }, + state: 'Initial', + }, + ], + interfaceInteractions: [], + paymentMethodInfo: { + method: 'creditcard', + }, + }; + + const molliePayment: molliePayment = { + resource: 'payment', + id: 'tr_7UhSN1zuXS', + amount: { currency: 'USD', value: '10.00' }, + createdAt: '2024-07-05T04:24:12+00:00', + _links: { + checkout: { + href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', + type: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', + }, + documentation: { + href: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', + type: 'https://api.mollie.com/v2/payments/tr_7UhSN1zuXS', + }, + }, + } as molliePayment; + + (getCreatePaymentUpdateAction as jest.Mock).mockImplementationOnce(() => { + const paymentService = jest.requireActual( + '../../src/service/payment.service.ts', + ) as typeof import('../../src/service/payment.service'); + return paymentService.getCreatePaymentUpdateAction(molliePayment, CTPayment, 1000); + }); + + (changeTransactionState as jest.Mock).mockReturnValueOnce({ + action: 'changeTransactionState', + state: 'Pending', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + }); + + const actual = await getCreatePaymentUpdateAction(molliePayment, CTPayment, 1000); + expect(actual).toHaveLength(5); + + expect(actual[0]).toEqual({ + action: 'addInterfaceInteraction', + type: { + key: 'sctm_interface_interaction_type', + }, + fields: { + sctm_id: uuid, + sctm_action_type: ConnectorActions.CreatePayment, + sctm_created_at: molliePayment.createdAt, + sctm_request: JSON.stringify({ + transactionId: CTPayment.transactions[0].id, + paymentMethod: CTPayment.paymentMethodInfo.method, + }), + sctm_response: JSON.stringify({ + molliePaymentId: molliePayment.id, + checkoutUrl: molliePayment._links.checkout?.href, + transactionId: CTPayment.transactions[0].id, + }), + }, + }); + + expect(actual[1]).toEqual({ + action: 'changeTransactionInteractionId', + transactionId: CTPayment.transactions[0].id, + interactionId: molliePayment.id, + }); + + expect(actual[2]).toEqual({ + action: 'changeTransactionTimestamp', + transactionId: CTPayment.transactions[0].id, + timestamp: molliePayment.createdAt, + }); + + expect(actual[3]).toEqual({ + action: 'changeTransactionState', + transactionId: CTPayment.transactions[0].id, + state: CTTransactionState.Pending, + }); + + expect(actual[4]).toEqual({ + action: 'setTransactionCustomField', + name: CustomFieldName.transactionSurchargeCost, + value: JSON.stringify({ + surchargeAmountInCent: 1000, + }), + transactionId: CTPayment.transactions[0].id, + }); + }); }); describe('Test handleCreatePayment', () => { @@ -593,6 +1246,17 @@ describe('Test handleCreatePayment', () => { paymentMethodInfo: { method: 'creditcard', }, + custom: { + type: { + typeId: 'type', + id: 'test', + }, + fields: { + sctm_payment_methods_request: JSON.stringify({ + billingCountry: 'DE', + }), + }, + }, }; beforeEach(() => { @@ -636,6 +1300,41 @@ describe('Test handleCreatePayment', () => { }, } as molliePayment; + const customLineItem = { + id: 'custom-line', + key: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }; + + const mockedCart = { + id: 'mocked-cart', + customLineItems: [customLineItem], + } as Cart; + + const methodConfig = { + value: { + pricingConstraints: [ + { + currencyCode: CTPayment.amountPlanned.currencyCode, + countryCode: JSON.parse(CTPayment.custom?.fields?.sctm_payment_methods_request).billingCountry, + surchargeCost: { + percentageAmount: 2, + fixedAmount: 10, + }, + }, + ], + }, + }; + + const appUtils = require('../../src/utils/app.utils'); + + jest.spyOn(appUtils, 'calculateTotalSurchargeAmount'); + + const mapUtils = require('../../src/utils/map.utils'); + + jest.spyOn(mapUtils, 'createCartUpdateActions'); + + (getCartFromPayment as jest.Mock).mockReturnValue(mockedCart); + (getSingleMethodConfigObject as jest.Mock).mockReturnValueOnce(methodConfig); (createMolliePayment as jest.Mock).mockReturnValueOnce(molliePayment); (getPaymentExtension as jest.Mock).mockReturnValueOnce({ destination: { @@ -653,8 +1352,46 @@ describe('Test handleCreatePayment', () => { transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', }); + (updateCart as jest.Mock).mockReturnValue(mockedCart); + + const totalSurchargeAmount = 1020; + const actual = await handleCreatePayment(CTPayment); + const expectedCartUpdateActions = [ + { + action: 'removeCustomLineItem', + customLineItemId: customLineItem.id, + }, + { + action: 'addCustomLineItem', + name: { + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }, + quantity: 1, + money: { + centAmount: totalSurchargeAmount, + currencyCode: CTPayment.amountPlanned.currencyCode, + }, + slug: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }, + ]; + + expect(getSingleMethodConfigObject).toHaveBeenCalledWith(CTPayment.paymentMethodInfo.method); + expect(calculateTotalSurchargeAmount).toHaveBeenCalledTimes(1); + expect(calculateTotalSurchargeAmount).toHaveBeenCalledWith( + CTPayment, + methodConfig.value.pricingConstraints[0].surchargeCost, + ); + expect(calculateTotalSurchargeAmount).toHaveReturnedWith( + totalSurchargeAmount / Math.pow(10, CTPayment.amountPlanned.fractionDigits), + ); + + expect(createCartUpdateActions).toHaveBeenCalledTimes(1); + expect(createCartUpdateActions).toHaveBeenCalledWith(mockedCart, CTPayment, totalSurchargeAmount); + expect(createCartUpdateActions).toHaveReturnedWith(expectedCartUpdateActions); + const ctActions = [ { action: 'addInterfaceInteraction', @@ -683,6 +1420,14 @@ describe('Test handleCreatePayment', () => { transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', state: 'Pending', }, + { + action: 'setTransactionCustomField', + name: 'sctm_transaction_surcharge_cost', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + value: JSON.stringify({ + surchargeAmountInCent: totalSurchargeAmount, + }), + }, ]; expect(actual).toEqual({ @@ -725,6 +1470,41 @@ describe('Test handleCreatePayment', () => { }, } as CustomPayment; + const customLineItem = { + id: 'custom-line', + key: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }; + + const cart = { + customLineItems: [customLineItem], + } as Cart; + + const methodConfig = { + value: { + pricingConstraints: [ + { + currencyCode: CTPayment.amountPlanned.currencyCode, + countryCode: JSON.parse(CTPayment.custom?.fields?.sctm_payment_methods_request).billingCountry, + surchargeCost: { + percentageAmount: 2, + fixedAmount: 10, + }, + }, + ], + }, + }; + + const appUtils = require('../../src/utils/app.utils'); + + jest.spyOn(appUtils, 'calculateTotalSurchargeAmount'); + + const mapUtils = require('../../src/utils/map.utils'); + + jest.spyOn(mapUtils, 'createCartUpdateActions'); + + (getCartFromPayment as jest.Mock).mockReturnValueOnce(cart); + (getSingleMethodConfigObject as jest.Mock).mockReturnValueOnce(methodConfig); + (createPaymentWithCustomMethod as jest.Mock).mockReturnValueOnce(molliePayment); (getPaymentExtension as jest.Mock).mockReturnValueOnce({ destination: { @@ -742,8 +1522,45 @@ describe('Test handleCreatePayment', () => { transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', }); + (updateCart as jest.Mock).mockReturnValueOnce(cart); + + const totalSurchargeAmount = 1020; + const actual = await handleCreatePayment(CTPayment); + const expectedCartUpdateActions = [ + { + action: 'removeCustomLineItem', + customLineItemId: customLineItem.id, + }, + { + action: 'addCustomLineItem', + name: { + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }, + quantity: 1, + money: { + centAmount: totalSurchargeAmount, + currencyCode: CTPayment.amountPlanned.currencyCode, + }, + slug: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + }, + ]; + + expect(calculateTotalSurchargeAmount).toHaveBeenCalledTimes(1); + expect(calculateTotalSurchargeAmount).toHaveBeenCalledWith( + CTPayment, + methodConfig.value.pricingConstraints[0].surchargeCost, + ); + expect(calculateTotalSurchargeAmount).toHaveReturnedWith( + totalSurchargeAmount / Math.pow(10, CTPayment.amountPlanned.fractionDigits), + ); + + expect(createCartUpdateActions).toHaveBeenCalledTimes(1); + expect(createCartUpdateActions).toHaveBeenCalledWith(cart, CTPayment, totalSurchargeAmount); + expect(createCartUpdateActions).toHaveReturnedWith(expectedCartUpdateActions); + const ctActions = [ { action: 'addInterfaceInteraction', @@ -772,6 +1589,14 @@ describe('Test handleCreatePayment', () => { transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', state: 'Pending', }, + { + action: 'setTransactionCustomField', + name: 'sctm_transaction_surcharge_cost', + transactionId: '5c8b0375-305a-4f19-ae8e-07806b101999', + value: JSON.stringify({ + surchargeAmountInCent: totalSurchargeAmount, + }), + }, ]; expect(createPaymentWithCustomMethod).toBeCalledTimes(1); @@ -1231,6 +2056,10 @@ describe('Test handlePaymentWebhook', () => { }); it('should return true and perform update with specific actions if the targeted status is canceled', async () => { + const cartService = require('../../src/service/cart.service'); + + jest.spyOn(cartService, 'removeCartMollieCustomLineItem'); + const fakePaymentId = 'tr_XXXX'; (getPaymentById as jest.Mock).mockReturnValue({ id: fakePaymentId, @@ -1299,6 +2128,8 @@ describe('Test handlePaymentWebhook', () => { expect(updatePayment).toBeCalledTimes(1); expect(updatePayment).toBeCalledWith(ctPayment, actions); + + expect(removeCartMollieCustomLineItem).toBeCalledTimes(1); }); it('should handle for manual capture payment', async () => { @@ -1532,6 +2363,10 @@ describe('Test handleCancelPayment', () => { }); it('should return status code and array of actions', async () => { + const cartService = require('../../src/service/cart.service'); + + jest.spyOn(cartService, 'removeCartMollieCustomLineItem'); + const molliePayment: molliePayment = { resource: 'payment', id: 'tr_7UhSN1zuXS', @@ -1569,12 +2404,15 @@ describe('Test handleCancelPayment', () => { expect(cancelPayment).toBeCalledTimes(1); expect(cancelPayment).toBeCalledWith(molliePayment.id); + expect(removeCartMollieCustomLineItem).toBeCalledTimes(1); + expect(actual).toEqual({ statusCode: 200, actions: [], }); }); }); + describe('Test handleGetApplePaySession', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/processor/tests/utils/app.utils.spec.ts b/processor/tests/utils/app.utils.spec.ts index 4f653a0..60eda64 100644 --- a/processor/tests/utils/app.utils.spec.ts +++ b/processor/tests/utils/app.utils.spec.ts @@ -1,12 +1,17 @@ import { describe, test, expect, it, jest } from '@jest/globals'; import { + calculateTotalSurchargeAmount, + convertCentToEUR, createDateNowString, parseStringToJsonObject, removeEmptyProperties, + roundSurchargeAmountToCent, validateEmail, } from '../../src/utils/app.utils'; import { logger } from '../../src/utils/logger.utils'; import CustomError from '../../src/errors/custom.error'; +import { Payment } from '@commercetools/platform-sdk'; +import { SurchargeCost } from '../../src/types/commercetools.types'; describe('Test createDateNowString', () => { test('should return correct time', async () => { @@ -95,3 +100,48 @@ describe('Test validateEmail', () => { expect(validateEmail('n.tran@shopmacher.de')).toBe(true); }); }); + +describe('Test convertCentToEUR', () => { + it('should return correct result', () => { + expect(convertCentToEUR(100, 2)).toBe(1); + }); +}); + +describe('Test calculateTotalSurchargeAmount', () => { + it('should return correct surcharge amount', () => { + const payment = { + amountPlanned: { + centAmount: 2000, + fractionDigits: 2, + }, + } as Payment; + + const surcharge = { + percentageAmount: 10, + fixedAmount: 5, + } as SurchargeCost; + + expect(calculateTotalSurchargeAmount(payment, surcharge)).toBe(7); + }); + + it('should return 0 if surcharge param is not defined', () => { + const payment = { + amountPlanned: { + centAmount: 2000, + fractionDigits: 2, + }, + } as Payment; + + expect(calculateTotalSurchargeAmount(payment, undefined)).toBe(0); + }); +}); + +describe('Test roundSurchargeAmountToCent', () => { + it('should return correct surcharge amount in cent', () => { + const surchargeAmountInEur = 300.998; + + const fractionDigits = 2; + + expect(roundSurchargeAmountToCent(surchargeAmountInEur, fractionDigits)).toBe(30100); + }); +}); diff --git a/processor/tests/utils/constant.utils.spec.ts b/processor/tests/utils/constant.utils.spec.ts index e5bfac0..a9397f2 100644 --- a/processor/tests/utils/constant.utils.spec.ts +++ b/processor/tests/utils/constant.utils.spec.ts @@ -9,6 +9,9 @@ import { PAY_LATER_ENUMS, DUE_DATE_PATTERN, DEFAULT_DUE_DATE, + MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + MOLLIE_SURCHARGE_LINE_DESCRIPTION, + MOLLIE_SHIPPING_LINE_DESCRIPTION, } from '../../src/utils/constant.utils'; import { version } from '../../package.json'; @@ -78,4 +81,19 @@ describe('Test constant.utils.ts', () => { expect(DEFAULT_DUE_DATE).toBeDefined(); expect(DEFAULT_DUE_DATE).toBe(14); }); + + test('should return correct {MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM} pattern', () => { + expect(MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM).toBeDefined(); + expect(MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM).toBe('mollie-surcharge-line-item'); + }); + + test('should return correct {MOLLIE_SURCHARGE_LINE_DESCRIPTION} pattern', () => { + expect(MOLLIE_SURCHARGE_LINE_DESCRIPTION).toBeDefined(); + expect(MOLLIE_SURCHARGE_LINE_DESCRIPTION).toBe('Total surcharge amount'); + }); + + test('should return correct {MOLLIE_SHIPPING_LINE_DESCRIPTION} pattern', () => { + expect(MOLLIE_SHIPPING_LINE_DESCRIPTION).toBeDefined(); + expect(MOLLIE_SHIPPING_LINE_DESCRIPTION).toBe('Shipping amount'); + }); }); diff --git a/processor/tests/utils/map.utils.spec.ts b/processor/tests/utils/map.utils.spec.ts index 5d2b17a..bc748c8 100644 --- a/processor/tests/utils/map.utils.spec.ts +++ b/processor/tests/utils/map.utils.spec.ts @@ -1,12 +1,17 @@ import { describe, test, expect, it, jest } from '@jest/globals'; import { + createCartUpdateActions, createMollieCreatePaymentParams, + createMollieLineForAdditionalAmount, mapCommercetoolsPaymentCustomFieldsToMollieListParams, } from '../../src/utils/map.utils'; -import { Payment } from '@commercetools/platform-sdk'; +import { Cart, Payment } from '@commercetools/platform-sdk'; import { MethodsListParams, PaymentCreateParams, PaymentMethod } from '@mollie/api-client'; -import { calculateDueDate, makeMollieAmount } from '../../src/utils/mollie.utils'; -import { CustomPaymentMethod } from '../../src/types/mollie.types'; +import { + MOLLIE_SHIPPING_LINE_DESCRIPTION, + MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + MOLLIE_SURCHARGE_LINE_DESCRIPTION, +} from '../../src/utils/constant.utils'; jest.mock('../../src/utils/mollie.utils.ts', () => ({ // @ts-expect-error ignore type error @@ -62,384 +67,837 @@ describe('Test map.utils.ts', () => { }); describe('createMollieCreatePaymentParams', () => { - it('should able to create a mollie payment params from CommerceTools payment object for with method as creditcard', async () => { - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - }; - const extensionUrl = 'https://example.com/webhook'; + // it('should able to create a mollie payment params from CommerceTools payment object for with method as creditcard', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; - const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - const mollieAmount = makeMollieAmount(CTPayment.amountPlanned); + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: 'creditcard', + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; - expect(mollieCreatePaymentParams).toEqual({ - method: CTPayment.paymentMethodInfo.method, - amount: { - currency: mollieAmount.currency, - value: mollieAmount.value, - }, - webhookUrl: extensionUrl, - }); - }); + // const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // const mollieAmount = makeMollieAmount(CTPayment.amountPlanned); - it('should able to create a mollie payment params from CommerceTools payment object with method as creditcard which has custom field', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - cardToken: 'card_token_12345', - }; + // expect(mollieCreatePaymentParams).toEqual({ + // method: CTPayment.paymentMethodInfo.method, + // amount: { + // currency: mollieAmount.currency, + // value: mollieAmount.value, + // }, + // webhookUrl: extensionUrl, + // lines: [], + // }); + // }); - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: 'creditcard', - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', - }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), - }, - }, - }; - const extensionUrl = 'https://example.com/webhook'; + // it('should able to create a mollie payment params from CommerceTools payment object for with method as creditcard and surcharge amount is not 0', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; - const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: 'creditcard', + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; - expect(mollieCreatePaymentParams).toEqual({ - method: 'creditcard', - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, // Always use our default webhook endpoint - description: customFieldObject.description, - cardToken: customFieldObject.cardToken, - }); - }); + // const surchargeAmountInCent = 1000; - it('should able to create a mollie payment params from CommerceTools payment object with method as ideal', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - include: { - 'details.qrCode': { - width: 100, - height: 100, - src: 'qr_code_url', - }, - }, - }; + // const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, surchargeAmountInCent, cart); + // const mollieAmount = { + // currency: CTPayment.amountPlanned.currencyCode, + // value: '20.00', + // }; - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.ideal + ',ideal_TEST', - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', - }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), - }, - }, - }; - const extensionUrl = 'https://example.com/webhook'; + // expect(mollieCreatePaymentParams).toEqual({ + // method: CTPayment.paymentMethodInfo.method, + // amount: mollieAmount, + // webhookUrl: extensionUrl, + // lines: [ + // { + // description: MOLLIE_SURCHARGE_LINE_DESCRIPTION, + // quantity: 1, + // quantityUnit: 'pcs', + // unitPrice: { + // currency: CTPayment.amountPlanned.currencyCode, + // value: '10.00', + // }, + // totalAmount: { + // currency: CTPayment.amountPlanned.currencyCode, + // value: '10.00', + // }, + // }, + // ], + // }); + // }); - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.ideal, - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - issuer: 'ideal_TEST', - include: customFieldObject.include, - }); - }); + // it('should able to create a mollie payment params from CommerceTools payment object with method as creditcard which has custom field', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; - it('should able to create a mollie payment params from CommerceTools payment object with method as bancontact', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - include: { - 'details.qrCode': { - width: 100, - height: 100, - src: 'qr_code_url', - }, - }, - }; + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // cardToken: 'card_token_12345', + // }; - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.bancontact, - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', - }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), - }, - }, - }; - const extensionUrl = 'https://example.com/webhook'; + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: 'creditcard', + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.bancontact, - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - include: customFieldObject.include, - }); - }); + // const mollieCreatePaymentParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); - it('should able to create a mollie payment params from CommerceTools payment object with method as banktransfer', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - billingAddress: { - email: 'test@mollie.com', - }, - }; + // expect(mollieCreatePaymentParams).toEqual({ + // method: 'creditcard', + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, // Always use our default webhook endpoint + // description: customFieldObject.description, + // cardToken: customFieldObject.cardToken, + // lines: [], + // }); + // }); - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.banktransfer, - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', - }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), - }, - }, - }; - const extensionUrl = 'https://example.com/webhook'; + // it('should able to create a mollie payment params from CommerceTools payment object with method as ideal', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; - const dueDate = '2024-01-01'; - (calculateDueDate as jest.Mock).mockReturnValueOnce(dueDate); + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // include: { + // 'details.qrCode': { + // width: 100, + // height: 100, + // src: 'qr_code_url', + // }, + // }, + // }; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.ideal + ',ideal_TEST', + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.banktransfer, - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - billingAddress: customFieldObject.billingAddress, - dueDate, - }); - }); + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.ideal, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // issuer: 'ideal_TEST', + // include: customFieldObject.include, + // lines: [], + // }); + // }); - it('should able to create a mollie payment params from CommerceTools payment object with method as przelewy24', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - billingEmail: 'test@mollie.com', - }; + // it('should able to create a mollie payment params from CommerceTools payment object with method as bancontact', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.przelewy24, - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', - }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), - }, - }, - }; - const extensionUrl = 'https://example.com/webhook'; + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // include: { + // 'details.qrCode': { + // width: 100, + // height: 100, + // src: 'qr_code_url', + // }, + // }, + // }; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.przelewy24, - amount: { - currency: 'EUR', - value: '10.00', + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.bancontact, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.bancontact, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // include: customFieldObject.include, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as banktransfer', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // billingAddress: { + // email: 'test@mollie.com', + // }, + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.banktransfer, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const dueDate = '2024-01-01'; + // (calculateDueDate as jest.Mock).mockReturnValueOnce(dueDate); + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.banktransfer, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // billingAddress: customFieldObject.billingAddress, + // dueDate, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as przelewy24', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // billingEmail: 'test@mollie.com', + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.przelewy24, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.przelewy24, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // billingEmail: customFieldObject.billingEmail, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as kbc', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.kbc, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.kbc, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as blik', () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // billingEmail: 'n.tran@shopmacher.de', + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: CustomPaymentMethod.blik, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: CustomPaymentMethod.blik, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: customFieldObject.webhookUrl, + // description: customFieldObject.description, + // billingEmail: customFieldObject.billingEmail, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as applepay', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // applePayPaymentToken: '{"paymentData": {"version": "EC_v1", "data": "vK3BbrCbI/...."}}', + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.applepay, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.applepay, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // applePayPaymentToken: JSON.stringify(customFieldObject.applePayPaymentToken), + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as paypal', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // sessionId: '12345', + // digitalGoods: true, + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.paypal, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.paypal, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // sessionId: customFieldObject.sessionId, + // digitalGoods: customFieldObject.digitalGoods, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object with method as giftcard', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // voucherNumber: '12345', + // voucherPin: '9999', + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 1000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.giftcard, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.giftcard, + // amount: { + // currency: 'EUR', + // value: '10.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // voucherNumber: customFieldObject.voucherNumber, + // voucherPin: customFieldObject.voucherPin, + // lines: [], + // }); + // }); + + // it('should able to create a mollie payment params from CommerceTools payment object including lineItems', async () => { + // const cart = { + // id: 'cart-test-id' + // } as Cart; + + // const customFieldObject = { + // description: 'Test payment', + // locale: 'en_GB', + // redirectUrl: 'https://example.com/success', + // webhookUrl: 'https://example.com/webhook', + // lines: [ + // { + // description: 'Item 1', + // quantity: 1, + // quantityUnit: 'pcs', + // unitPrice: { currency: 'EUR', value: '10.00' }, + // totalAmount: { currency: 'EUR', value: '10.00' }, + // sku: 'TEST1', + // imageUrl: 'https://example.com/image1.jpg', + // productUrl: 'https://example.com/product1', + // }, + // { + // description: 'Item 2', + // quantity: 1, + // quantityUnit: 'pcs', + // unitPrice: { currency: 'EUR', value: '10.00' }, + // totalAmount: { currency: 'EUR', value: '10.00' }, + // sku: 'TEST2', + // imageUrl: 'https://example.com/image2.jpg', + // productUrl: 'https://example.com/product2', + // }, + // ], + // }; + + // const CTPayment: Payment = { + // id: '5c8b0375-305a-4f19-ae8e-07806b101999', + // version: 1, + // createdAt: '2024-07-04T14:07:35.625Z', + // lastModifiedAt: '2024-07-04T14:07:35.625Z', + // amountPlanned: { + // type: 'centPrecision', + // currencyCode: 'EUR', + // centAmount: 2000, + // fractionDigits: 2, + // }, + // paymentStatus: {}, + // transactions: [], + // interfaceInteractions: [], + // paymentMethodInfo: { + // method: PaymentMethod.paypal, + // }, + // custom: { + // type: { + // typeId: 'type', + // id: 'sctm_payment', + // }, + // fields: { + // sctm_create_payment_request: JSON.stringify(customFieldObject), + // }, + // }, + // }; + // const extensionUrl = 'https://example.com/webhook'; + + // const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl, 0, cart); + // expect(mollieCreatePaymentParams).toEqual({ + // method: PaymentMethod.paypal, + // amount: { + // currency: 'EUR', + // value: '20.00', + // }, + // locale: customFieldObject.locale, + // redirectUrl: customFieldObject.redirectUrl, + // webhookUrl: extensionUrl, + // description: customFieldObject.description, + // lines: customFieldObject.lines, + // }); + // }); + + it('should able to create a mollie payment params from CommerceTools payment object including a line item for shipping amount', async () => { + const cart = { + id: 'cart-test-id', + shippingInfo: { + price: { + type: 'centPrecision', + currencyCode: 'EUR', + centAmount: 5000, + fractionDigits: 2, + }, }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - billingEmail: customFieldObject.billingEmail, - }); - }); + } as Cart; - it('should able to create a mollie payment params from CommerceTools payment object with method as kbc', async () => { const customFieldObject = { description: 'Test payment', locale: 'en_GB', redirectUrl: 'https://example.com/success', webhookUrl: 'https://example.com/webhook', - }; - - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.kbc, - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', + lines: [ + { + description: 'Item 1', + quantity: 1, + quantityUnit: 'pcs', + unitPrice: { currency: 'EUR', value: '10.00' }, + totalAmount: { currency: 'EUR', value: '10.00' }, + sku: 'TEST1', + imageUrl: 'https://example.com/image1.jpg', + productUrl: 'https://example.com/product1', }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), + { + description: 'Item 2', + quantity: 1, + quantityUnit: 'pcs', + unitPrice: { currency: 'EUR', value: '10.00' }, + totalAmount: { currency: 'EUR', value: '10.00' }, + sku: 'TEST2', + imageUrl: 'https://example.com/image2.jpg', + productUrl: 'https://example.com/product2', }, - }, + ], }; - const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.kbc, - amount: { + const mollieLines = Object.assign([], customFieldObject.lines as Array); + mollieLines.push({ + description: MOLLIE_SHIPPING_LINE_DESCRIPTION, + quantity: 1, + quantityUnit: 'pcs', + unitPrice: { currency: 'EUR', - value: '10.00', + value: '50.00', + }, + totalAmount: { + currency: 'EUR', + value: '50.00', }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, }); - }); - - it('should able to create a mollie payment params from CommerceTools payment object with method as blik', () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - billingEmail: 'n.tran@shopmacher.de', - }; const CTPayment: Payment = { id: '5c8b0375-305a-4f19-ae8e-07806b101999', @@ -449,14 +907,14 @@ describe('createMollieCreatePaymentParams', () => { amountPlanned: { type: 'centPrecision', currencyCode: 'EUR', - centAmount: 1000, + centAmount: 2000, fractionDigits: 2, }, paymentStatus: {}, transactions: [], interfaceInteractions: [], paymentMethodInfo: { - method: CustomPaymentMethod.blik, + method: PaymentMethod.paypal, }, custom: { type: { @@ -471,181 +929,146 @@ describe('createMollieCreatePaymentParams', () => { const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); + const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams( + CTPayment, + extensionUrl, + 0, + cart, + ); expect(mollieCreatePaymentParams).toEqual({ - method: CustomPaymentMethod.blik, + method: PaymentMethod.paypal, amount: { currency: 'EUR', - value: '10.00', + value: '20.00', }, locale: customFieldObject.locale, redirectUrl: customFieldObject.redirectUrl, - webhookUrl: customFieldObject.webhookUrl, + webhookUrl: extensionUrl, description: customFieldObject.description, - billingEmail: customFieldObject.billingEmail, + lines: mollieLines, }); }); +}); - it('should able to create a mollie payment params from CommerceTools payment object with method as applepay', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - applePayPaymentToken: '{"paymentData": {"version": "EC_v1", "data": "vK3BbrCbI/...."}}', - }; - - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', +describe('Test createCartUpdateActions', () => { + it('should able to create cart update actions including only insert new custom line item', async () => { + const cart = { + customLineItems: [], + shippingInfo: { + taxCategory: { + id: '123', + }, + }, + } as unknown as Cart; + const ctPayment = { amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, + centAmount: 10000, fractionDigits: 2, + currencyCode: 'EUR', }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.applepay, - }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', + } as Payment; + const surchargeAmountInCent = 10; + + const expectedResult = [ + { + action: 'addCustomLineItem', + name: { + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), + quantity: 1, + slug: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + money: { + centAmount: surchargeAmountInCent, + currencyCode: ctPayment.amountPlanned.currencyCode, + }, + taxCategory: { + id: cart.shippingInfo?.taxCategory?.id, }, }, - }; - const extensionUrl = 'https://example.com/webhook'; + ]; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.applepay, - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - applePayPaymentToken: JSON.stringify(customFieldObject.applePayPaymentToken), - }); + expect(createCartUpdateActions(cart, ctPayment, surchargeAmountInCent)).toEqual(expectedResult); }); - it('should able to create a mollie payment params from CommerceTools payment object with method as paypal', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - sessionId: '12345', - digitalGoods: true, + it('should able to create cart update actions including remove the existing custom line item and insert new custom line item', async () => { + const mollieSurchargeCustomLine = { + id: '5c8b0375-305a-4f19-ae8e-07806b101999', + key: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, }; - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', + const cart = { + customLineItems: [mollieSurchargeCustomLine], + shippingInfo: { + taxCategory: { + id: '123', + }, + }, + } as unknown as Cart; + const ctPayment = { amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, + centAmount: 10000, fractionDigits: 2, + currencyCode: 'EUR', }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.paypal, + } as Payment; + const surchargeAmountInCent = 10; + + const expectedResult = [ + { + action: 'removeCustomLineItem', + customLineItemId: mollieSurchargeCustomLine.id, }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', + { + action: 'addCustomLineItem', + name: { + en: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + de: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), + quantity: 1, + slug: MOLLIE_SURCHARGE_CUSTOM_LINE_ITEM, + money: { + centAmount: surchargeAmountInCent, + // centAmount: surchargeAmount, + currencyCode: ctPayment.amountPlanned.currencyCode, + }, + taxCategory: { + id: cart.shippingInfo?.taxCategory?.id, }, }, - }; - const extensionUrl = 'https://example.com/webhook'; + ]; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.paypal, - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - sessionId: customFieldObject.sessionId, - digitalGoods: customFieldObject.digitalGoods, - }); + expect(createCartUpdateActions(cart, ctPayment, surchargeAmountInCent)).toEqual(expectedResult); }); +}); - it('should able to create a mollie payment params from CommerceTools payment object with method as giftcard', async () => { - const customFieldObject = { - description: 'Test payment', - locale: 'en_GB', - redirectUrl: 'https://example.com/success', - webhookUrl: 'https://example.com/webhook', - voucherNumber: '12345', - voucherPin: '9999', - }; +describe('Test createMollieLineForAdditionalAmount', () => { + it('should return a Mollie line for the surcharge amount', () => { + const surchargeAmountInCent = 1020; + const fractionDigits = 2; + const currency = 'EUR'; - const CTPayment: Payment = { - id: '5c8b0375-305a-4f19-ae8e-07806b101999', - version: 1, - createdAt: '2024-07-04T14:07:35.625Z', - lastModifiedAt: '2024-07-04T14:07:35.625Z', - amountPlanned: { - type: 'centPrecision', - currencyCode: 'EUR', - centAmount: 1000, - fractionDigits: 2, - }, - paymentStatus: {}, - transactions: [], - interfaceInteractions: [], - paymentMethodInfo: { - method: PaymentMethod.giftcard, + const expected = { + description: MOLLIE_SURCHARGE_LINE_DESCRIPTION, + quantity: 1, + quantityUnit: 'pcs', + unitPrice: { + currency, + value: '10.20', }, - custom: { - type: { - typeId: 'type', - id: 'sctm_payment', - }, - fields: { - sctm_create_payment_request: JSON.stringify(customFieldObject), - }, + totalAmount: { + currency, + value: '10.20', }, }; - const extensionUrl = 'https://example.com/webhook'; - const mollieCreatePaymentParams: PaymentCreateParams = createMollieCreatePaymentParams(CTPayment, extensionUrl); - expect(mollieCreatePaymentParams).toEqual({ - method: PaymentMethod.giftcard, - amount: { - currency: 'EUR', - value: '10.00', - }, - locale: customFieldObject.locale, - redirectUrl: customFieldObject.redirectUrl, - webhookUrl: extensionUrl, - description: customFieldObject.description, - voucherNumber: customFieldObject.voucherNumber, - voucherPin: customFieldObject.voucherPin, - }); + expect( + createMollieLineForAdditionalAmount( + MOLLIE_SURCHARGE_LINE_DESCRIPTION, + surchargeAmountInCent, + fractionDigits, + currency, + ), + ).toStrictEqual(expected); }); }); diff --git a/processor/tests/utils/mollie.utils.spec.ts b/processor/tests/utils/mollie.utils.spec.ts index 7863481..c016a39 100644 --- a/processor/tests/utils/mollie.utils.spec.ts +++ b/processor/tests/utils/mollie.utils.spec.ts @@ -45,6 +45,23 @@ describe('Test mollie.utils.ts', () => { expect(makeMollieAmount(centPrecisionMoney)).toEqual(expected); }); + + it('should create a Mollie Amount from CentPrecisionMoney with surcharge amount is not 0', () => { + const centPrecisionMoney: CentPrecisionMoney = { + centAmount: 1234, + fractionDigits: 2, + currencyCode: 'EUR', + } as CentPrecisionMoney; + + const surchargeAmountInCent = 20; + + const expected: Amount = { + value: '12.54', + currency: 'EUR', + }; + + expect(makeMollieAmount(centPrecisionMoney, surchargeAmountInCent)).toEqual(expected); + }); }); describe('makeCTMoney', () => {