diff --git a/.github/secrets/connector_auth.toml.gpg b/.github/secrets/connector_auth.toml.gpg index 487e436df463..7da9189ade58 100644 Binary files a/.github/secrets/connector_auth.toml.gpg and b/.github/secrets/connector_auth.toml.gpg differ diff --git a/CHANGELOG.md b/CHANGELOG.md index e5da650def02..427fa7403e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,60 @@ All notable changes to HyperSwitch will be documented here. - - - +## 1.81.0 (2023-11-16) + +### Features + +- **connector:** + - [BANKOFAMERICA] Implement Cards for Bank of America ([#2765](https://github.com/juspay/hyperswitch/pull/2765)) ([`e8de3a7`](https://github.com/juspay/hyperswitch/commit/e8de3a710710b92f5c2351c5d67c22352c2b0a30)) + - [ProphetPay] Implement Card Redirect PaymentMethodType and flows for Authorize, CompleteAuthorize, Psync, Refund, Rsync and Void ([#2641](https://github.com/juspay/hyperswitch/pull/2641)) ([`8d4adc5`](https://github.com/juspay/hyperswitch/commit/8d4adc52af57ed0994e6efbb5b2d0d3df3fb3150)) + +### Testing + +- **postman:** Update postman collection files ([`f829197`](https://github.com/juspay/hyperswitch/commit/f8291973c38bde874c45ca15ff8d48c1f2de9781)) + +**Full Changelog:** [`v1.80.0...v1.81.0`](https://github.com/juspay/hyperswitch/compare/v1.80.0...v1.81.0) + +- - - + + +## 1.80.0 (2023-11-16) + +### Features + +- **router:** Add api to migrate card from basilisk to rust ([#2853](https://github.com/juspay/hyperswitch/pull/2853)) ([`b8b20c4`](https://github.com/juspay/hyperswitch/commit/b8b20c412df0485bf395f9aa21e6e34e90d97acd)) +- Spawn webhooks and async scheduling in background ([#2780](https://github.com/juspay/hyperswitch/pull/2780)) ([`f248fe2`](https://github.com/juspay/hyperswitch/commit/f248fe2889c9cb68af4464ab0db1735224ab5c8d)) + +### Refactors + +- **router:** Add openapi spec support for gsm apis ([#2871](https://github.com/juspay/hyperswitch/pull/2871)) ([`62c9cca`](https://github.com/juspay/hyperswitch/commit/62c9ccae6ab0d128c54962675b88739ad7797fe6)) + +**Full Changelog:** [`v1.79.0...v1.80.0`](https://github.com/juspay/hyperswitch/compare/v1.79.0...v1.80.0) + +- - - + + +## 1.79.0 (2023-11-16) + +### Features + +- Change async-bb8 fork and tokio spawn for concurrent database calls ([#2774](https://github.com/juspay/hyperswitch/pull/2774)) ([`d634fde`](https://github.com/juspay/hyperswitch/commit/d634fdeac349b92e3619234580299a6c6c38e6d4)) + +### Bug Fixes + +- **connector:** [noon] add validate psync reference ([#2886](https://github.com/juspay/hyperswitch/pull/2886)) ([`b129023`](https://github.com/juspay/hyperswitch/commit/b1290234ba13de2dd8cc4210f63bae514c2988b4)) +- **payment_link:** Render SDK for status requires_payment_method ([#2887](https://github.com/juspay/hyperswitch/pull/2887)) ([`d4d2c2c`](https://github.com/juspay/hyperswitch/commit/d4d2c2c7076a46996aa0aa74d1df827169f73155)) +- Paypal postman collection changes for surcharge feature ([#2884](https://github.com/juspay/hyperswitch/pull/2884)) ([`5956242`](https://github.com/juspay/hyperswitch/commit/5956242588ef7bdbaa1804a952d48dc47c6e15f1)) + +### Testing + +- **postman:** Update postman collection files ([`5c31365`](https://github.com/juspay/hyperswitch/commit/5c313656a129362b0e905e5fbf349dbbec57199c)) + +**Full Changelog:** [`v1.78.0...v1.79.0`](https://github.com/juspay/hyperswitch/compare/v1.78.0...v1.79.0) + +- - - + + ## 1.78.0 (2023-11-14) ### Features diff --git a/Cargo.lock b/Cargo.lock index 1574933810b3..a03340093c88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,12 +9,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" dependencies = [ "bitflags 1.3.2", - "bytes", + "bytes 1.5.0", "futures-core", "futures-sink", "memchr", "pin-project-lite", - "tokio", + "tokio 1.32.0", "tokio-util", "tracing", ] @@ -31,7 +31,7 @@ dependencies = [ "futures-util", "log", "once_cell", - "smallvec", + "smallvec 1.11.1", ] [[package]] @@ -48,7 +48,7 @@ dependencies = [ "base64 0.21.4", "bitflags 1.3.2", "brotli", - "bytes", + "bytes 1.5.0", "bytestring", "derive_more", "encoding_rs", @@ -66,8 +66,8 @@ dependencies = [ "pin-project-lite", "rand 0.8.5", "sha1", - "smallvec", - "tokio", + "smallvec 1.11.1", + "tokio 1.32.0", "tokio-util", "tracing", "zstd", @@ -92,7 +92,7 @@ dependencies = [ "actix-multipart-derive", "actix-utils", "actix-web", - "bytes", + "bytes 1.5.0", "derive_more", "futures-core", "futures-util", @@ -105,7 +105,7 @@ dependencies = [ "serde_json", "serde_plain", "tempfile", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -142,7 +142,7 @@ checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" dependencies = [ "actix-macros", "futures-core", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -156,9 +156,9 @@ dependencies = [ "actix-utils", "futures-core", "futures-util", - "mio", + "mio 0.8.8", "socket2 0.5.4", - "tokio", + "tokio 1.32.0", "tracing", ] @@ -188,7 +188,7 @@ dependencies = [ "pin-project-lite", "rustls 0.21.7", "rustls-webpki", - "tokio", + "tokio 1.32.0", "tokio-rustls", "tokio-util", "tracing", @@ -221,9 +221,9 @@ dependencies = [ "actix-utils", "actix-web-codegen", "ahash 0.7.6", - "bytes", + "bytes 1.5.0", "bytestring", - "cfg-if", + "cfg-if 1.0.0", "cookie", "derive_more", "encoding_rs", @@ -240,7 +240,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "smallvec", + "smallvec 1.11.1", "socket2 0.4.9", "time", "url", @@ -296,7 +296,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "getrandom 0.2.10", "once_cell", "version_check", @@ -475,14 +475,14 @@ dependencies = [ [[package]] name = "async-bb8-diesel" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779f1fa3defe66bf147fe5c811b23a02cfcaa528a25293e0b20d1911eac1fb05" +source = "git+https://github.com/jarnura/async-bb8-diesel?rev=53b4ab901aab7635c8215fd1c2d542c8db443094#53b4ab901aab7635c8215fd1c2d542c8db443094" dependencies = [ "async-trait", "bb8", "diesel", "thiserror", - "tokio", + "tokio 1.32.0", + "tracing", ] [[package]] @@ -506,7 +506,7 @@ dependencies = [ "futures-core", "memchr", "pin-project-lite", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -517,7 +517,7 @@ checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg", - "cfg-if", + "cfg-if 1.0.0", "concurrent-queue", "futures-lite", "log", @@ -606,8 +606,8 @@ dependencies = [ "actix-utils", "ahash 0.7.6", "base64 0.21.4", - "bytes", - "cfg-if", + "bytes 1.5.0", + "cfg-if 1.0.0", "cookie", "derive_more", "futures-core", @@ -624,7 +624,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -644,14 +644,14 @@ dependencies = [ "aws-smithy-json", "aws-smithy-types", "aws-types", - "bytes", + "bytes 1.5.0", "fastrand 1.9.0", "hex", "http", "hyper", "ring", "time", - "tokio", + "tokio 1.32.0", "tower", "tracing", "zeroize", @@ -666,7 +666,7 @@ dependencies = [ "aws-smithy-async", "aws-smithy-types", "fastrand 1.9.0", - "tokio", + "tokio 1.32.0", "tracing", "zeroize", ] @@ -695,7 +695,7 @@ dependencies = [ "aws-smithy-http", "aws-smithy-types", "aws-types", - "bytes", + "bytes 1.5.0", "http", "http-body", "lazy_static", @@ -721,7 +721,7 @@ dependencies = [ "aws-smithy-json", "aws-smithy-types", "aws-types", - "bytes", + "bytes 1.5.0", "http", "regex", "tokio-stream", @@ -750,7 +750,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "bytes", + "bytes 1.5.0", "http", "http-body", "once_cell", @@ -779,7 +779,7 @@ dependencies = [ "aws-smithy-json", "aws-smithy-types", "aws-types", - "bytes", + "bytes 1.5.0", "http", "regex", "tokio-stream", @@ -804,7 +804,7 @@ dependencies = [ "aws-smithy-json", "aws-smithy-types", "aws-types", - "bytes", + "bytes 1.5.0", "http", "regex", "tokio-stream", @@ -831,7 +831,7 @@ dependencies = [ "aws-smithy-types", "aws-smithy-xml", "aws-types", - "bytes", + "bytes 1.5.0", "http", "regex", "tower", @@ -861,7 +861,7 @@ checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", - "bytes", + "bytes 1.5.0", "form_urlencoded", "hex", "hmac", @@ -882,7 +882,7 @@ checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" dependencies = [ "futures-util", "pin-project-lite", - "tokio", + "tokio 1.32.0", "tokio-stream", ] @@ -894,7 +894,7 @@ checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" dependencies = [ "aws-smithy-http", "aws-smithy-types", - "bytes", + "bytes 1.5.0", "crc32c", "crc32fast", "hex", @@ -917,7 +917,7 @@ dependencies = [ "aws-smithy-http", "aws-smithy-http-tower", "aws-smithy-types", - "bytes", + "bytes 1.5.0", "fastrand 1.9.0", "http", "http-body", @@ -926,7 +926,7 @@ dependencies = [ "lazy_static", "pin-project-lite", "rustls 0.20.9", - "tokio", + "tokio 1.32.0", "tower", "tracing", ] @@ -938,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" dependencies = [ "aws-smithy-types", - "bytes", + "bytes 1.5.0", "crc32fast", ] @@ -950,7 +950,7 @@ checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" dependencies = [ "aws-smithy-eventstream", "aws-smithy-types", - "bytes", + "bytes 1.5.0", "bytes-utils", "futures-core", "http", @@ -960,7 +960,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "pin-utils", - "tokio", + "tokio 1.32.0", "tokio-util", "tracing", ] @@ -973,7 +973,7 @@ checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" dependencies = [ "aws-smithy-http", "aws-smithy-types", - "bytes", + "bytes 1.5.0", "http", "http-body", "pin-project-lite", @@ -1034,7 +1034,7 @@ dependencies = [ "aws-smithy-http", "aws-smithy-types", "http", - "rustc_version", + "rustc_version 0.4.0", "tracing", ] @@ -1047,7 +1047,7 @@ dependencies = [ "async-trait", "axum-core", "bitflags 1.3.2", - "bytes", + "bytes 1.5.0", "futures-util", "http", "http-body", @@ -1073,7 +1073,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", - "bytes", + "bytes 1.5.0", "futures-util", "http", "http-body", @@ -1091,7 +1091,7 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide 0.7.1", "object", @@ -1136,7 +1136,7 @@ dependencies = [ "futures-channel", "futures-util", "parking_lot 0.12.1", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -1204,7 +1204,7 @@ dependencies = [ "arrayref", "arrayvec", "cc", - "cfg-if", + "cfg-if 1.0.0", "constant_time_eq", "digest 0.10.7", ] @@ -1282,6 +1282,16 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "1.5.0" @@ -1294,7 +1304,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" dependencies = [ - "bytes", + "bytes 1.5.0", "either", ] @@ -1304,7 +1314,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" dependencies = [ - "bytes", + "bytes 1.5.0", ] [[package]] @@ -1347,7 +1357,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.19", "serde", "serde_json", ] @@ -1360,7 +1370,7 @@ checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" dependencies = [ "camino", "cargo-platform", - "semver", + "semver 1.0.19", "serde", "serde_json", "thiserror", @@ -1393,6 +1403,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -1509,6 +1525,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "color_quant" version = "1.1.0" @@ -1532,12 +1557,12 @@ name = "common_utils" version = "0.1.0" dependencies = [ "async-trait", - "bytes", + "bytes 1.5.0", "common_enums", "diesel", "error-stack", "fake", - "futures", + "futures 0.3.28", "hex", "http", "masking", @@ -1562,7 +1587,7 @@ dependencies = [ "test-case", "thiserror", "time", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -1571,7 +1596,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.8.16", ] [[package]] @@ -1674,7 +1699,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" dependencies = [ - "rustc_version", + "rustc_version 0.4.0", ] [[package]] @@ -1683,7 +1708,7 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1728,8 +1753,19 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ - "cfg-if", - "crossbeam-utils", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +dependencies = [ + "crossbeam-epoch 0.8.2", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] @@ -1738,9 +1774,24 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", + "cfg-if 1.0.0", + "crossbeam-epoch 0.9.15", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset 0.5.6", + "scopeguard", ] [[package]] @@ -1750,20 +1801,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", + "memoffset 0.9.0", "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + [[package]] name = "crossbeam-queue" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" dependencies = [ - "cfg-if", - "crossbeam-utils", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", ] [[package]] @@ -1772,7 +1845,7 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -1861,9 +1934,9 @@ version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "hashbrown 0.14.1", - "lock_api", + "lock_api 0.4.10", "once_cell", "parking_lot_core 0.9.8", ] @@ -1900,7 +1973,7 @@ dependencies = [ "deadpool-runtime", "num_cpus", "retain_mut", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -1953,7 +2026,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn 1.0.109", ] @@ -2064,7 +2137,7 @@ checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -2111,7 +2184,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "thiserror", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -2132,7 +2205,7 @@ version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -2187,7 +2260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f00447f331c7f726db5b8532ebc9163519eed03c6d7c8b73c90b3ff5646ac85" dependencies = [ "anyhow", - "rustc_version", + "rustc_version 0.4.0", "serde", ] @@ -2261,7 +2334,7 @@ dependencies = [ "router_env", "serde", "thiserror", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -2291,7 +2364,7 @@ dependencies = [ "serde", "serde_json", "time", - "tokio", + "tokio 1.32.0", "url", "webdriver", ] @@ -2375,19 +2448,19 @@ dependencies = [ "arc-swap", "arcstr", "async-trait", - "bytes", + "bytes 1.5.0", "bytes-utils", - "cfg-if", + "cfg-if 1.0.0", "float-cmp", - "futures", + "futures 0.3.28", "lazy_static", "log", "parking_lot 0.12.1", "rand 0.8.5", "redis-protocol", - "semver", + "semver 1.0.19", "sha-1 0.10.1", - "tokio", + "tokio 1.32.0", "tokio-stream", "tokio-util", "tracing", @@ -2447,6 +2520,28 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + [[package]] name = "futures" version = "0.3.28" @@ -2496,7 +2591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" dependencies = [ "futures-core", - "lock_api", + "lock_api 0.4.10", "parking_lot 0.11.2", ] @@ -2594,7 +2689,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] @@ -2605,7 +2700,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2677,7 +2772,7 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ - "bytes", + "bytes 1.5.0", "fnv", "futures-core", "futures-sink", @@ -2685,7 +2780,7 @@ dependencies = [ "http", "indexmap 1.9.3", "slab", - "tokio", + "tokio 1.32.0", "tokio-util", "tracing", ] @@ -2769,7 +2864,7 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes", + "bytes 1.5.0", "fnv", "itoa", ] @@ -2780,7 +2875,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.5.0", "http", "pin-project-lite", ] @@ -2833,7 +2928,7 @@ version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ - "bytes", + "bytes 1.5.0", "futures-channel", "futures-core", "futures-util", @@ -2845,7 +2940,7 @@ dependencies = [ "itoa", "pin-project-lite", "socket2 0.4.9", - "tokio", + "tokio 1.32.0", "tower-service", "tracing", "want", @@ -2862,7 +2957,7 @@ dependencies = [ "log", "rustls 0.20.9", "rustls-native-certs", - "tokio", + "tokio 1.32.0", "tokio-rustls", ] @@ -2874,7 +2969,7 @@ checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", "pin-project-lite", - "tokio", + "tokio 1.32.0", "tokio-io-timeout", ] @@ -2884,10 +2979,10 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.5.0", "hyper", "native-tls", - "tokio", + "tokio 1.32.0", "tokio-native-tls", ] @@ -3015,7 +3110,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -3029,6 +3124,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + [[package]] name = "ipnet" version = "2.8.0" @@ -3140,6 +3244,16 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "kgraph_utils" version = "0.1.0" @@ -3246,6 +3360,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "lock_api" version = "0.4.10" @@ -3293,7 +3416,7 @@ dependencies = [ name = "masking" version = "0.1.0" dependencies = [ - "bytes", + "bytes 1.5.0", "diesel", "serde", "serde_json", @@ -3340,13 +3463,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "md-5" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "digest 0.10.7", ] @@ -3362,6 +3491,15 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "memoffset" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -3430,6 +3568,25 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + [[package]] name = "mio" version = "0.8.8" @@ -3442,6 +3599,29 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + [[package]] name = "moka" version = "0.11.3" @@ -3451,16 +3631,16 @@ dependencies = [ "async-io", "async-lock", "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", + "crossbeam-epoch 0.9.15", + "crossbeam-utils 0.8.16", "futures-util", "once_cell", "parking_lot 0.12.1", "quanta", - "rustc_version", + "rustc_version 0.4.0", "scheduled-thread-pool", "skeptic", - "smallvec", + "smallvec 1.11.1", "tagptr", "thiserror", "triomphe", @@ -3494,6 +3674,17 @@ dependencies = [ "tempfile", ] +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + [[package]] name = "nom" version = "7.1.3" @@ -3511,7 +3702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -3626,7 +3817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ "bitflags 2.4.0", - "cfg-if", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -3680,14 +3871,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8af72d59a4484654ea8eb183fea5ae4eb6a41d7ac3e3bae5f4d2a282a3a7d3ca" dependencies = [ "async-trait", - "futures", + "futures 0.3.28", "futures-util", "http", "opentelemetry", "opentelemetry-proto", "prost", "thiserror", - "tokio", + "tokio 1.32.0", "tonic", ] @@ -3697,7 +3888,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "045f8eea8c0fa19f7d48e7bc3128a39c2e5c533d5c61298c548dfefc1064474c" dependencies = [ - "futures", + "futures 0.3.28", "futures-util", "opentelemetry", "prost", @@ -3738,7 +3929,7 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "thiserror", - "tokio", + "tokio 1.32.0", "tokio-stream", ] @@ -3770,6 +3961,17 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" +[[package]] +name = "parking_lot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.6.3", + "rustc_version 0.2.3", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -3777,7 +3979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", - "lock_api", + "lock_api 0.4.10", "parking_lot_core 0.8.6", ] @@ -3787,22 +3989,37 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "lock_api", + "lock_api 0.4.10", "parking_lot_core 0.9.8", ] +[[package]] +name = "parking_lot_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66b810a62be75176a80873726630147a5ca780cd33921e0b5709033e66b0a" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "rustc_version 0.2.3", + "smallvec 0.6.14", + "winapi 0.3.9", +] + [[package]] name = "parking_lot_core" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "instant", "libc", "redox_syscall 0.2.16", - "smallvec", - "winapi", + "smallvec 1.11.1", + "winapi 0.3.9", ] [[package]] @@ -3811,10 +4028,10 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall 0.3.5", - "smallvec", + "smallvec 1.11.1", "windows-targets", ] @@ -4061,7 +4278,7 @@ checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if", + "cfg-if 1.0.0", "concurrent-queue", "libc", "log", @@ -4143,7 +4360,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ - "bytes", + "bytes 1.5.0", "prost-derive", ] @@ -4187,14 +4404,14 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.8.16", "libc", "mach2", "once_cell", "raw-cpuid", "wasi 0.11.0+wasi-snapshot-preview1", "web-sys", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4338,8 +4555,8 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "crossbeam-deque 0.8.3", + "crossbeam-utils 0.8.16", ] [[package]] @@ -4348,7 +4565,7 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c31deddf734dc0a39d3112e73490e88b61a05e83e074d211f348404cee4d2c6" dependencies = [ - "bytes", + "bytes 1.5.0", "bytes-utils", "cookie-factory", "crc16", @@ -4363,13 +4580,19 @@ dependencies = [ "common_utils", "error-stack", "fred", - "futures", + "futures 0.3.28", "router_env", "serde", "thiserror", - "tokio", + "tokio 1.32.0", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.2.16" @@ -4463,7 +4686,7 @@ checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "async-compression", "base64 0.21.4", - "bytes", + "bytes 1.5.0", "encoding_rs", "futures-core", "futures-util", @@ -4485,7 +4708,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "system-configuration", - "tokio", + "tokio 1.32.0", "tokio-native-tls", "tokio-util", "tower-service", @@ -4514,7 +4737,7 @@ dependencies = [ "spin", "untrusted", "web-sys", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -4561,7 +4784,7 @@ dependencies = [ "bb8", "bigdecimal", "blake3", - "bytes", + "bytes 1.5.0", "cards", "clap", "common_enums", @@ -4577,7 +4800,7 @@ dependencies = [ "error-stack", "euclid", "external_services", - "futures", + "futures 0.3.28", "hex", "http", "hyper", @@ -4621,7 +4844,8 @@ dependencies = [ "test_utils", "thiserror", "time", - "tokio", + "tokio 1.32.0", + "tracing-futures", "unicode-segmentation", "url", "utoipa", @@ -4664,7 +4888,7 @@ dependencies = [ "serde_path_to_error", "strum 0.24.1", "time", - "tokio", + "tokio 1.32.0", "tracing", "tracing-actix-web", "tracing-appender", @@ -4724,7 +4948,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "ordered-multimap", ] @@ -4740,13 +4964,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.19", ] [[package]] @@ -4900,7 +5133,7 @@ dependencies = [ "diesel_models", "error-stack", "external_services", - "futures", + "futures 0.3.28", "masking", "once_cell", "rand 0.8.5", @@ -4912,7 +5145,7 @@ dependencies = [ "strum 0.24.1", "thiserror", "time", - "tokio", + "tokio 1.32.0", "uuid", ] @@ -4961,6 +5194,15 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.19" @@ -4970,6 +5212,12 @@ dependencies = [ "serde", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.188" @@ -5122,7 +5370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ "dashmap", - "futures", + "futures 0.3.28", "lazy_static", "log", "parking_lot 0.12.1", @@ -5147,7 +5395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -5159,7 +5407,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -5170,7 +5418,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -5181,7 +5429,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -5232,7 +5480,7 @@ dependencies = [ "futures-core", "libc", "signal-hook", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -5286,6 +5534,15 @@ dependencies = [ "deunicode", ] +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +dependencies = [ + "maybe-uninit", +] + [[package]] name = "smallvec" version = "1.11.1" @@ -5299,7 +5556,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -5351,9 +5608,9 @@ dependencies = [ "bigdecimal", "bitflags 1.3.2", "byteorder", - "bytes", + "bytes 1.5.0", "crc", - "crossbeam-queue", + "crossbeam-queue 0.3.8", "dirs", "dotenvy", "either", @@ -5381,7 +5638,7 @@ dependencies = [ "serde_json", "sha1", "sha2", - "smallvec", + "smallvec 1.11.1", "sqlformat", "sqlx-rt", "stringprep", @@ -5419,7 +5676,7 @@ checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" dependencies = [ "native-tls", "once_cell", - "tokio", + "tokio 1.32.0", "tokio-native-tls", ] @@ -5432,7 +5689,7 @@ dependencies = [ "async-bb8-diesel", "async-trait", "bb8", - "bytes", + "bytes 1.5.0", "common_utils", "config", "crc32fast", @@ -5441,7 +5698,7 @@ dependencies = [ "diesel_models", "dyn-clone", "error-stack", - "futures", + "futures 0.3.28", "http", "masking", "mime", @@ -5454,7 +5711,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -5606,7 +5863,7 @@ version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fastrand 2.0.1", "redox_syscall 0.3.5", "rustix 0.38.17", @@ -5650,7 +5907,7 @@ version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54c25e2cb8f5fcd7318157634e8838aa6f7e4715c96637f969fabaccd1ef5462" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "proc-macro-error", "proc-macro2", "quote", @@ -5686,7 +5943,7 @@ dependencies = [ "serial_test", "thirtyfour", "time", - "tokio", + "tokio 1.32.0", "toml 0.7.4", ] @@ -5701,7 +5958,7 @@ dependencies = [ "chrono", "cookie", "fantoccini", - "futures", + "futures 0.3.28", "http", "log", "parking_lot 0.12.1", @@ -5711,7 +5968,7 @@ dependencies = [ "stringmatch", "thirtyfour-macros", "thiserror", - "tokio", + "tokio 1.32.0", "url", "urlparse", ] @@ -5754,7 +6011,7 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", ] @@ -5821,6 +6078,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "mio 0.6.23", + "num_cpus", + "tokio-codec", + "tokio-current-thread", + "tokio-executor", + "tokio-fs", + "tokio-io", + "tokio-reactor", + "tokio-sync", + "tokio-tcp", + "tokio-threadpool", + "tokio-timer", + "tokio-udp", + "tokio-uds", +] + [[package]] name = "tokio" version = "1.32.0" @@ -5828,9 +6109,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ "backtrace", - "bytes", + "bytes 1.5.0", "libc", - "mio", + "mio 0.8.8", "num_cpus", "parking_lot 0.12.1", "pin-project-lite", @@ -5840,6 +6121,59 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "tokio-codec" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "tokio-io", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" +dependencies = [ + "futures 0.1.31", + "tokio-executor", +] + +[[package]] +name = "tokio-executor" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", +] + +[[package]] +name = "tokio-fs" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4" +dependencies = [ + "futures 0.1.31", + "tokio-io", + "tokio-threadpool", +] + +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -5847,7 +6181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ "pin-project-lite", - "tokio", + "tokio 1.32.0", ] [[package]] @@ -5868,7 +6202,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio", + "tokio 1.32.0", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "lazy_static", + "log", + "mio 0.6.23", + "num_cpus", + "parking_lot 0.9.0", + "slab", + "tokio-executor", + "tokio-io", + "tokio-sync", ] [[package]] @@ -5878,7 +6231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ "rustls 0.20.9", - "tokio", + "tokio 1.32.0", "webpki", ] @@ -5890,7 +6243,93 @@ checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", - "tokio", + "tokio 1.32.0", +] + +[[package]] +name = "tokio-sync" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" +dependencies = [ + "fnv", + "futures 0.1.31", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "iovec", + "mio 0.6.23", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" +dependencies = [ + "crossbeam-deque 0.7.4", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "lazy_static", + "log", + "num_cpus", + "slab", + "tokio-executor", +] + +[[package]] +name = "tokio-timer" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures 0.1.31", + "slab", + "tokio-executor", +] + +[[package]] +name = "tokio-udp" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", + "mio 0.6.23", + "tokio-codec", + "tokio-io", + "tokio-reactor", +] + +[[package]] +name = "tokio-uds" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "iovec", + "libc", + "log", + "mio 0.6.23", + "mio-uds", + "tokio-codec", + "tokio-io", + "tokio-reactor", ] [[package]] @@ -5899,11 +6338,11 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ - "bytes", + "bytes 1.5.0", "futures-core", "futures-sink", "pin-project-lite", - "tokio", + "tokio 1.32.0", "tracing", ] @@ -5960,7 +6399,7 @@ dependencies = [ "async-trait", "axum", "base64 0.13.1", - "bytes", + "bytes 1.5.0", "futures-core", "futures-util", "h2", @@ -5972,7 +6411,7 @@ dependencies = [ "pin-project", "prost", "prost-derive", - "tokio", + "tokio 1.32.0", "tokio-stream", "tokio-util", "tower", @@ -5995,7 +6434,7 @@ dependencies = [ "pin-project-lite", "rand 0.8.5", "slab", - "tokio", + "tokio 1.32.0", "tokio-util", "tower-layer", "tower-service", @@ -6020,7 +6459,7 @@ version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "log", "pin-project-lite", "tracing-attributes", @@ -6080,6 +6519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ "pin-project", + "tokio 0.1.22", "tracing", ] @@ -6131,7 +6571,7 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec", + "smallvec 1.11.1", "thread_local", "tracing", "tracing-core", @@ -6389,7 +6829,7 @@ checksum = "8b3c89c2c7e50f33e4d35527e5bf9c11d6d132226dbbd1753f0fbe9f19ef88c6" dependencies = [ "anyhow", "git2", - "rustc_version", + "rustc_version 0.4.0", "rustversion", "time", ] @@ -6458,7 +6898,7 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] @@ -6483,7 +6923,7 @@ version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "js-sys", "wasm-bindgen", "web-sys", @@ -6535,7 +6975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9973cb72c8587d5ad5efdb91e663d36177dc37725e6c90ca86c626b0cc45c93f" dependencies = [ "base64 0.13.1", - "bytes", + "bytes 1.5.0", "cookie", "http", "log", @@ -6582,6 +7022,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -6592,6 +7038,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -6604,7 +7056,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -6703,7 +7155,7 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "windows-sys", ] @@ -6717,7 +7169,7 @@ dependencies = [ "async-trait", "base64 0.21.4", "deadpool", - "futures", + "futures 0.3.28", "futures-timer", "http-types", "hyper", @@ -6726,7 +7178,17 @@ dependencies = [ "regex", "serde", "serde_json", - "tokio", + "tokio 1.32.0", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", ] [[package]] @@ -6775,7 +7237,7 @@ checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", - "crossbeam-utils", + "crossbeam-utils 0.8.16", "flate2", ] diff --git a/config/config.example.toml b/config/config.example.toml index f0083bb48b19..02eff1d42979 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -112,6 +112,7 @@ kms_encrypted_recon_admin_api_key = "" # Base64-encoded (KMS encrypted) ciph # like card details [locker] host = "" # Locker host +host_rs = "" # Rust Locker host mock_locker = true # Emulate a locker locally using Postgres basilisk_host = "" # Basilisk host locker_signing_key_id = "1" # Key_id to sign basilisk hs locker @@ -130,6 +131,7 @@ locker_encryption_key2 = "" # public key 2 in pem format, corresponding private locker_decryption_key1 = "" # private key 1 in pem format, corresponding public key in basilisk locker_decryption_key2 = "" # private key 2 in pem format, corresponding public key in basilisk vault_encryption_key = "" # public key in pem format, corresponding private key in basilisk-hs +rust_locker_encryption_key = "" # public key in pem format, corresponding private key in rust locker vault_private_key = "" # private key in pem format, corresponding public key in basilisk-hs @@ -415,6 +417,9 @@ ach = { currency = "USD" } [pm_filters.stripe] cashapp = {country = "US", currency = "USD"} +[pm_filters.prophetpay] +card_redirect = { currency = "USD" } + [connector_customer] connector_list = "gocardless,stax,stripe" payout_connector_list = "wise" diff --git a/config/development.toml b/config/development.toml index 63c1f045d94f..c82607a704c3 100644 --- a/config/development.toml +++ b/config/development.toml @@ -48,6 +48,7 @@ applepay_endpoint = "DOMAIN SPECIFIC ENDPOINT" [locker] host = "" +host_rs = "" mock_locker = true basilisk_host = "" @@ -59,6 +60,7 @@ locker_encryption_key2 = "" locker_decryption_key1 = "" locker_decryption_key2 = "" vault_encryption_key = "" +rust_locker_encryption_key = "" vault_private_key = "" tunnel_private_key = "" @@ -353,6 +355,9 @@ credit = { currency = "USD" } debit = { currency = "USD" } ach = { currency = "USD" } +[pm_filters.prophetpay] +card_redirect = { currency = "USD" } + [pm_filters.trustpay] credit = { not_available_flows = { capture_method = "manual" } } debit = { not_available_flows = { capture_method = "manual" } } diff --git a/config/docker_compose.toml b/config/docker_compose.toml index ddda7e7021a4..a5294546de41 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -44,6 +44,7 @@ recon_admin_api_key = "recon_test_admin" [locker] host = "" +host_rs = "" mock_locker = true basilisk_host = "" @@ -55,6 +56,7 @@ locker_encryption_key2 = "" locker_decryption_key1 = "" locker_decryption_key2 = "" vault_encryption_key = "" +rust_locker_encryption_key = "" vault_private_key = "" [redis] @@ -283,6 +285,9 @@ red_pagos = { country = "UY", currency = "UYU" } [pm_filters.stripe] cashapp = {country = "US", currency = "USD"} +[pm_filters.prophetpay] +card_redirect = { currency = "USD" } + [pm_filters.stax] credit = { currency = "USD" } debit = { currency = "USD" } diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index b27e71b9e8f5..c4e4aa90c4b8 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -76,7 +76,7 @@ pub enum Connector { Airwallex, Authorizedotnet, Bambora, - // Bankofamerica, Added as template code for future usage + Bankofamerica, Bitpay, Bluesnap, Boku, @@ -108,7 +108,7 @@ pub enum Connector { Paypal, Payu, Powertranz, - // Prophetpay, added as a template code for future usage + Prophetpay, Rapyd, Shift4, Square, @@ -196,7 +196,7 @@ pub enum RoutableConnectors { Adyen, Airwallex, Authorizedotnet, - // Bankofamerica, Added as template code for future usage + Bankofamerica, Bitpay, Bambora, Bluesnap, @@ -229,7 +229,7 @@ pub enum RoutableConnectors { Paypal, Payu, Powertranz, - // Prophetpay, added as a template code for future usage + Prophetpay, Rapyd, Shift4, Square, @@ -562,3 +562,9 @@ pub enum RetryAction { /// Denotes that the payment is requeued Requeue, } + +#[derive(Clone, Copy)] +pub enum LockerChoice { + Basilisk, + Tartarus, +} diff --git a/crates/api_models/src/events.rs b/crates/api_models/src/events.rs index ad07340615b4..0ce7638b5ed1 100644 --- a/crates/api_models/src/events.rs +++ b/crates/api_models/src/events.rs @@ -1,5 +1,6 @@ pub mod customer; pub mod gsm; +mod locker_migration; pub mod payment; #[cfg(feature = "payouts")] pub mod payouts; diff --git a/crates/api_models/src/events/gsm.rs b/crates/api_models/src/events/gsm.rs index d984ae1ff698..a653cc291d6c 100644 --- a/crates/api_models/src/events/gsm.rs +++ b/crates/api_models/src/events/gsm.rs @@ -31,3 +31,9 @@ impl ApiEventMetric for gsm::GsmDeleteResponse { Some(ApiEventsType::Gsm) } } + +impl ApiEventMetric for gsm::GsmResponse { + fn get_api_event_type(&self) -> Option { + Some(ApiEventsType::Gsm) + } +} diff --git a/crates/api_models/src/events/locker_migration.rs b/crates/api_models/src/events/locker_migration.rs new file mode 100644 index 000000000000..db76a8f760db --- /dev/null +++ b/crates/api_models/src/events/locker_migration.rs @@ -0,0 +1,9 @@ +use common_utils::events::ApiEventMetric; + +use crate::locker_migration::MigrateCardResponse; + +impl ApiEventMetric for MigrateCardResponse { + fn get_api_event_type(&self) -> Option { + Some(common_utils::events::ApiEventsType::RustLocker) + } +} diff --git a/crates/api_models/src/gsm.rs b/crates/api_models/src/gsm.rs index 6bd8fd99dd93..254981b1f8f7 100644 --- a/crates/api_models/src/gsm.rs +++ b/crates/api_models/src/gsm.rs @@ -1,8 +1,10 @@ -use crate::enums; +use utoipa::ToSchema; -#[derive(Debug, serde::Deserialize, serde::Serialize)] +use crate::enums::Connector; + +#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct GsmCreateRequest { - pub connector: enums::Connector, + pub connector: Connector, pub flow: String, pub sub_flow: String, pub code: String, @@ -13,9 +15,9 @@ pub struct GsmCreateRequest { pub step_up_possible: bool, } -#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct GsmRetrieveRequest { - pub connector: enums::Connector, + pub connector: Connector, pub flow: String, pub sub_flow: String, pub code: String, @@ -33,6 +35,7 @@ pub struct GsmRetrieveRequest { serde::Serialize, serde::Deserialize, strum::EnumString, + ToSchema, )] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] @@ -43,7 +46,7 @@ pub enum GsmDecision { DoDefault, } -#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct GsmUpdateRequest { pub connector: String, pub flow: String, @@ -56,7 +59,7 @@ pub struct GsmUpdateRequest { pub step_up_possible: Option, } -#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[derive(Debug, serde::Deserialize, serde::Serialize, ToSchema)] pub struct GsmDeleteRequest { pub connector: String, pub flow: String, @@ -65,7 +68,7 @@ pub struct GsmDeleteRequest { pub message: String, } -#[derive(Debug, serde::Serialize)] +#[derive(Debug, serde::Serialize, ToSchema)] pub struct GsmDeleteResponse { pub gsm_rule_delete: bool, pub connector: String, @@ -73,3 +76,16 @@ pub struct GsmDeleteResponse { pub sub_flow: String, pub code: String, } + +#[derive(serde::Serialize, Debug, ToSchema)] +pub struct GsmResponse { + pub connector: String, + pub flow: String, + pub sub_flow: String, + pub code: String, + pub message: String, + pub status: String, + pub router_error: Option, + pub decision: String, + pub step_up_possible: bool, +} diff --git a/crates/api_models/src/lib.rs b/crates/api_models/src/lib.rs index bcc3913ea824..40faa6b3e81d 100644 --- a/crates/api_models/src/lib.rs +++ b/crates/api_models/src/lib.rs @@ -13,6 +13,7 @@ pub mod errors; pub mod events; pub mod files; pub mod gsm; +pub mod locker_migration; pub mod mandates; pub mod organization; pub mod payment_methods; diff --git a/crates/api_models/src/locker_migration.rs b/crates/api_models/src/locker_migration.rs new file mode 100644 index 000000000000..6e2881cd463e --- /dev/null +++ b/crates/api_models/src/locker_migration.rs @@ -0,0 +1,8 @@ +#[derive(Debug, Clone, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MigrateCardResponse { + pub status_message: String, + pub status_code: String, + pub customers_moved: usize, + pub cards_moved: usize, +} diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index cf0259f26951..d924fb2e4f62 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -704,6 +704,7 @@ pub enum CardRedirectData { Knet {}, Benefit {}, MomoAtm {}, + CardRedirect {}, } #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] @@ -868,6 +869,7 @@ impl GetPaymentMethodType for CardRedirectData { Self::Knet {} => api_enums::PaymentMethodType::Knet, Self::Benefit {} => api_enums::PaymentMethodType::Benefit, Self::MomoAtm {} => api_enums::PaymentMethodType::MomoAtm, + Self::CardRedirect {} => api_enums::PaymentMethodType::CardRedirect, } } } diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 363df5389a79..47a44ea7443e 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -306,8 +306,9 @@ impl From for ast::ConnectorChoice { RoutableConnectors::Adyen => euclid_enums::Connector::Adyen, RoutableConnectors::Airwallex => euclid_enums::Connector::Airwallex, RoutableConnectors::Authorizedotnet => euclid_enums::Connector::Authorizedotnet, - RoutableConnectors::Bitpay => euclid_enums::Connector::Bitpay, RoutableConnectors::Bambora => euclid_enums::Connector::Bambora, + RoutableConnectors::Bankofamerica => euclid_enums::Connector::Bankofamerica, + RoutableConnectors::Bitpay => euclid_enums::Connector::Bitpay, RoutableConnectors::Bluesnap => euclid_enums::Connector::Bluesnap, RoutableConnectors::Boku => euclid_enums::Connector::Boku, RoutableConnectors::Braintree => euclid_enums::Connector::Braintree, @@ -336,6 +337,7 @@ impl From for ast::ConnectorChoice { RoutableConnectors::Paypal => euclid_enums::Connector::Paypal, RoutableConnectors::Payu => euclid_enums::Connector::Payu, RoutableConnectors::Powertranz => euclid_enums::Connector::Powertranz, + RoutableConnectors::Prophetpay => euclid_enums::Connector::Prophetpay, RoutableConnectors::Rapyd => euclid_enums::Connector::Rapyd, RoutableConnectors::Shift4 => euclid_enums::Connector::Shift4, RoutableConnectors::Square => euclid_enums::Connector::Square, diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index f0386fc2f42e..48b0664c16d3 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -990,6 +990,7 @@ pub enum PaymentMethodType { BcaBankTransfer, BniVa, BriVa, + CardRedirect, CimbVa, #[serde(rename = "classic")] ClassicReward, diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs index 73f736cdeefa..63abfdb3f73a 100644 --- a/crates/common_enums/src/transformers.rs +++ b/crates/common_enums/src/transformers.rs @@ -1807,6 +1807,7 @@ impl From for PaymentMethod { PaymentMethodType::Bizum => Self::BankRedirect, PaymentMethodType::Blik => Self::BankRedirect, PaymentMethodType::Alfamart => Self::Voucher, + PaymentMethodType::CardRedirect => Self::CardRedirect, PaymentMethodType::CimbVa => Self::BankTransfer, PaymentMethodType::ClassicReward => Self::Reward, PaymentMethodType::Credit => Self::Card, diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 7bc248bf8d1b..60756192d66e 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -41,3 +41,9 @@ pub const DEFAULT_PRODUCT_IMG: &str = "https://i.imgur.com/On3VtKF.png"; /// Default Merchant Logo Link pub const DEFAULT_MERCHANT_LOGO: &str = "https://i.imgur.com/RfxPFQo.png"; + +/// Redirect url for Prophetpay +pub const PROPHETPAY_REDIRECT_URL: &str = "https://ccm-thirdparty.cps.golf/hp/tokenize/"; + +/// Variable which store the card token for Prophetpay +pub const PROPHETPAY_TOKEN: &str = "cctoken"; diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index 753f1deeb676..14b8d4de1c36 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -44,6 +44,7 @@ pub enum ApiEventsType { Gsm, // TODO: This has to be removed once the corresponding apiEventTypes are created Miscellaneous, + RustLocker, } impl ApiEventMetric for serde_json::Value {} diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index b28bffe0dc90..111f0f43c0f2 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -77,7 +77,9 @@ impl Percentage { if value.contains('.') { // if string has '.' then take the decimal part and verify precision length match value.split('.').last() { - Some(decimal_part) => decimal_part.trim_end_matches('0').len() <= PRECISION.into(), + Some(decimal_part) => { + decimal_part.trim_end_matches('0').len() <= >::into(PRECISION) + } // will never be None None => false, } diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index 9521c690366f..ccef0bf4e742 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -12,7 +12,7 @@ default = ["kv_store"] kv_store = [] [dependencies] -async-bb8-diesel = "0.1.0" +async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } diesel = { version = "2.1.0", features = ["postgres", "serde_json", "time", "64-column-tables"] } error-stack = "0.3.1" frunk = "0.4.1" diff --git a/crates/drainer/Cargo.toml b/crates/drainer/Cargo.toml index 56bebdce6b86..668e8b0574fe 100644 --- a/crates/drainer/Cargo.toml +++ b/crates/drainer/Cargo.toml @@ -13,7 +13,7 @@ kms = ["external_services/kms"] vergen = ["router_env/vergen"] [dependencies] -async-bb8-diesel = "0.1.0" +async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } bb8 = "0.8" clap = { version = "4.3.2", default-features = false, features = ["std", "derive", "help", "usage"] } config = { version = "0.13.3", features = ["toml"] } diff --git a/crates/euclid/src/enums.rs b/crates/euclid/src/enums.rs index 4188860ab90f..dc6d9f66a58f 100644 --- a/crates/euclid/src/enums.rs +++ b/crates/euclid/src/enums.rs @@ -86,8 +86,9 @@ pub enum Connector { Adyen, Airwallex, Authorizedotnet, - Bitpay, Bambora, + Bankofamerica, + Bitpay, Bluesnap, Boku, Braintree, @@ -116,6 +117,7 @@ pub enum Connector { Paypal, Payu, Powertranz, + Prophetpay, Rapyd, Shift4, Square, diff --git a/crates/euclid/src/frontend/dir/enums.rs b/crates/euclid/src/frontend/dir/enums.rs index 17699940363f..f049ad35328e 100644 --- a/crates/euclid/src/frontend/dir/enums.rs +++ b/crates/euclid/src/frontend/dir/enums.rs @@ -225,6 +225,7 @@ pub enum CardRedirectType { Benefit, Knet, MomoAtm, + CardRedirect, } #[derive( diff --git a/crates/euclid/src/frontend/dir/lowering.rs b/crates/euclid/src/frontend/dir/lowering.rs index 516e10e0389e..b1f03e8dd557 100644 --- a/crates/euclid/src/frontend/dir/lowering.rs +++ b/crates/euclid/src/frontend/dir/lowering.rs @@ -134,6 +134,7 @@ impl From for global_enums::PaymentMethodType { enums::CardRedirectType::Benefit => Self::Benefit, enums::CardRedirectType::Knet => Self::Knet, enums::CardRedirectType::MomoAtm => Self::MomoAtm, + enums::CardRedirectType::CardRedirect => Self::CardRedirect, } } } diff --git a/crates/euclid/src/frontend/dir/transformers.rs b/crates/euclid/src/frontend/dir/transformers.rs index da413d380c0f..c99b39e36f46 100644 --- a/crates/euclid/src/frontend/dir/transformers.rs +++ b/crates/euclid/src/frontend/dir/transformers.rs @@ -161,6 +161,9 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet } global_enums::PaymentMethodType::MomoAtm => Ok(dirval!(CardRedirectType = MomoAtm)), global_enums::PaymentMethodType::Oxxo => Ok(dirval!(VoucherType = Oxxo)), + global_enums::PaymentMethodType::CardRedirect => { + Ok(dirval!(CardRedirectType = CardRedirect)) + } } } } diff --git a/crates/kgraph_utils/src/transformers.rs b/crates/kgraph_utils/src/transformers.rs index 3d32cce38bd8..b1636418aa17 100644 --- a/crates/kgraph_utils/src/transformers.rs +++ b/crates/kgraph_utils/src/transformers.rs @@ -280,6 +280,9 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) { } api_enums::PaymentMethodType::MomoAtm => Ok(dirval!(CardRedirectType = MomoAtm)), api_enums::PaymentMethodType::Oxxo => Ok(dirval!(VoucherType = Oxxo)), + api_enums::PaymentMethodType::CardRedirect => { + Ok(dirval!(CardRedirectType = CardRedirect)) + } } } } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 4d9c315a10b0..01595dc18cd5 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -37,8 +37,8 @@ actix-cors = "0.6.4" actix-multipart = "0.6.0" actix-rt = "2.8.0" actix-web = "4.3.1" +async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } argon2 = { version = "0.5.0", features = ["std"] } -async-bb8-diesel = "0.1.0" async-trait = "0.1.68" aws-config = { version = "0.55.3", optional = true } aws-sdk-s3 = { version = "0.28.0", optional = true } @@ -97,6 +97,7 @@ utoipa-swagger-ui = { version = "3.1.3", features = ["actix-web"] } uuid = { version = "1.3.3", features = ["serde", "v4"] } validator = "0.16.0" x509-parser = "0.15.0" +tracing-futures = { version = "0.2.5", features = ["tokio"] } # First party crates api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } diff --git a/crates/router/src/bin/router.rs b/crates/router/src/bin/router.rs index cb3a8d83b031..beb2869f998c 100644 --- a/crates/router/src/bin/router.rs +++ b/crates/router/src/bin/router.rs @@ -4,7 +4,7 @@ use router::{ logger, }; -#[actix_web::main] +#[tokio::main] async fn main() -> ApplicationResult<()> { // get commandline config before initializing config let cmd_line = ::parse(); @@ -43,7 +43,7 @@ async fn main() -> ApplicationResult<()> { logger::info!("Application started [{:?}] [{:?}]", conf.server, conf.log); #[allow(clippy::expect_used)] - let server = router::start_server(conf) + let server = Box::pin(router::start_server(conf)) .await .expect("Failed to create the server"); let _ = server.await; diff --git a/crates/router/src/bin/scheduler.rs b/crates/router/src/bin/scheduler.rs index 09f23bc3b2f3..4c19408582bc 100644 --- a/crates/router/src/bin/scheduler.rs +++ b/crates/router/src/bin/scheduler.rs @@ -40,7 +40,12 @@ async fn main() -> CustomResult<(), ProcessTrackerError> { ); // channel for listening to redis disconnect events let (redis_shutdown_signal_tx, redis_shutdown_signal_rx) = oneshot::channel(); - let state = routes::AppState::new(conf, redis_shutdown_signal_tx, api_client).await; + let state = Box::pin(routes::AppState::new( + conf, + redis_shutdown_signal_tx, + api_client, + )) + .await; // channel to shutdown scheduler gracefully let (tx, rx) = mpsc::channel(1); tokio::spawn(router::receiver_for_error( diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index 8d58037343e0..b71e2aad5b5d 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -48,6 +48,7 @@ impl Default for super::settings::Locker { fn default() -> Self { Self { host: "localhost".into(), + host_rs: "localhost".into(), mock_locker: true, basilisk_host: "localhost".into(), locker_signing_key_id: "1".into(), @@ -458,6 +459,138 @@ impl Default for super::settings::RequiredFields { common: HashMap::new(), } ), + ( + enums::Connector::Bankofamerica, + RequiredFieldFinal { + mandate: HashMap::new(), + non_mandate: HashMap::from( + [ + ( + "payment_method_data.card.card_number".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_number".to_string(), + display_name: "card_number".to_string(), + field_type: enums::FieldType::UserCardNumber, + value: None, + } + ), + ( + "payment_method_data.card.card_exp_month".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_exp_month".to_string(), + display_name: "card_exp_month".to_string(), + field_type: enums::FieldType::UserCardExpiryMonth, + value: None, + } + ), + ( + "payment_method_data.card.card_exp_year".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_exp_year".to_string(), + display_name: "card_exp_year".to_string(), + field_type: enums::FieldType::UserCardExpiryYear, + value: None, + } + ), + ( + "payment_method_data.card.card_cvc".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_cvc".to_string(), + display_name: "card_cvc".to_string(), + field_type: enums::FieldType::UserCardCvc, + value: None, + } + ), + ( + "payment_method_data.card.card_holder_name".to_string(), + RequiredFieldInfo { + required_field: "payment_method_data.card.card_holder_name".to_string(), + display_name: "card_holder_name".to_string(), + field_type: enums::FieldType::UserFullName, + value: None, + } + ), + ( + "email".to_string(), + RequiredFieldInfo { + required_field: "email".to_string(), + display_name: "email".to_string(), + field_type: enums::FieldType::UserEmailAddress, + value: None, + } + ), + ( + "billing.address.first_name".to_string(), + RequiredFieldInfo { + required_field: "billing.address.first_name".to_string(), + display_name: "billing_first_name".to_string(), + field_type: enums::FieldType::UserBillingName, + value: None, + } + ), + ( + "billing.address.last_name".to_string(), + RequiredFieldInfo { + required_field: "billing.address.last_name".to_string(), + display_name: "billing_last_name".to_string(), + field_type: enums::FieldType::UserBillingName, + value: None, + } + ), + ( + "billing.address.city".to_string(), + RequiredFieldInfo { + required_field: "billing.address.city".to_string(), + display_name: "city".to_string(), + field_type: enums::FieldType::UserAddressCity, + value: None, + } + ), + ( + "billing.address.state".to_string(), + RequiredFieldInfo { + required_field: "billing.address.state".to_string(), + display_name: "state".to_string(), + field_type: enums::FieldType::UserAddressState, + value: None, + } + ), + ( + "billing.address.zip".to_string(), + RequiredFieldInfo { + required_field: "billing.address.zip".to_string(), + display_name: "zip".to_string(), + field_type: enums::FieldType::UserAddressPincode, + value: None, + } + ), + ( + "billing.address.country".to_string(), + RequiredFieldInfo { + required_field: "billing.address.country".to_string(), + display_name: "country".to_string(), + field_type: enums::FieldType::UserAddressCountry{ + options: vec![ + "ALL".to_string(), + ] + }, + value: None, + } + ), + ( + "billing.address.line1".to_string(), + RequiredFieldInfo { + required_field: "billing.address.line1".to_string(), + display_name: "line1".to_string(), + field_type: enums::FieldType::UserAddressline1, + value: None, + } + ), + ] + ), + common: HashMap::new(), + } + ), ( enums::Connector::Bluesnap, RequiredFieldFinal { diff --git a/crates/router/src/configs/kms.rs b/crates/router/src/configs/kms.rs index 317ad0608b49..205169fa291b 100644 --- a/crates/router/src/configs/kms.rs +++ b/crates/router/src/configs/kms.rs @@ -18,6 +18,7 @@ impl KmsDecrypt for settings::Jwekey { self.locker_decryption_key1, self.locker_decryption_key2, self.vault_encryption_key, + self.rust_locker_encryption_key, self.vault_private_key, self.tunnel_private_key, ) = tokio::try_join!( @@ -26,6 +27,7 @@ impl KmsDecrypt for settings::Jwekey { kms_client.decrypt(self.locker_decryption_key1), kms_client.decrypt(self.locker_decryption_key2), kms_client.decrypt(self.vault_encryption_key), + kms_client.decrypt(self.rust_locker_encryption_key), kms_client.decrypt(self.vault_private_key), kms_client.decrypt(self.tunnel_private_key), )?; diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index c5b71c6f7341..0007e636926c 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -420,6 +420,7 @@ pub struct Secrets { #[serde(default)] pub struct Locker { pub host: String, + pub host_rs: String, pub mock_locker: bool, pub basilisk_host: String, pub locker_signing_key_id: String, @@ -448,6 +449,7 @@ pub struct Jwekey { pub locker_decryption_key1: String, pub locker_decryption_key2: String, pub vault_encryption_key: String, + pub rust_locker_encryption_key: String, pub vault_private_key: String, pub tunnel_private_key: String, } diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 8bb287812800..ec21c9baa5e9 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2201,6 +2201,12 @@ impl<'a> TryFrom<&api_models::payments::CardRedirectData> for AdyenPaymentMethod payments::CardRedirectData::Knet {} => Ok(AdyenPaymentMethod::Knet), payments::CardRedirectData::Benefit {} => Ok(AdyenPaymentMethod::Benefit), payments::CardRedirectData::MomoAtm {} => Ok(AdyenPaymentMethod::MomoAtm), + payments::CardRedirectData::CardRedirect {} => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Adyen"), + ) + .into()) + } } } } diff --git a/crates/router/src/connector/bankofamerica.rs b/crates/router/src/connector/bankofamerica.rs index 84870f7407fb..51a1d722dc51 100644 --- a/crates/router/src/connector/bankofamerica.rs +++ b/crates/router/src/connector/bankofamerica.rs @@ -2,12 +2,19 @@ pub mod transformers; use std::fmt::Debug; +use base64::Engine; +use diesel_models::enums; use error_stack::{IntoReport, ResultExt}; -use masking::ExposeInterface; +use masking::{ExposeInterface, PeekInterface}; +use ring::{digest, hmac}; +use time::OffsetDateTime; use transformers as bankofamerica; +use url::Url; use crate::{ configs::settings, + connector::{utils as connector_utils, utils::RefundsRequestData}, + consts, core::errors::{self, CustomResult}, headers, services::{ @@ -23,6 +30,8 @@ use crate::{ utils::{self, BytesExt}, }; +pub const V_C_MERCHANT_ID: &str = "v-c-merchant-id"; + #[derive(Debug, Clone)] pub struct Bankofamerica; @@ -39,6 +48,54 @@ impl api::RefundExecute for Bankofamerica {} impl api::RefundSync for Bankofamerica {} impl api::PaymentToken for Bankofamerica {} +impl Bankofamerica { + pub fn generate_digest(&self, payload: &[u8]) -> String { + let payload_digest = digest::digest(&digest::SHA256, payload); + consts::BASE64_ENGINE.encode(payload_digest) + } + + pub fn generate_signature( + &self, + auth: bankofamerica::BankOfAmericaAuthType, + host: String, + resource: &str, + payload: &String, + date: OffsetDateTime, + http_method: services::Method, + ) -> CustomResult { + let bankofamerica::BankOfAmericaAuthType { + api_key, + merchant_account, + api_secret, + } = auth; + let is_post_method = matches!(http_method, services::Method::Post); + let digest_str = if is_post_method { "digest " } else { "" }; + let headers = format!("host date (request-target) {digest_str}{V_C_MERCHANT_ID}"); + let request_target = if is_post_method { + format!("(request-target): post {resource}\ndigest: SHA-256={payload}\n") + } else { + format!("(request-target): get {resource}\n") + }; + let signature_string = format!( + "host: {host}\ndate: {date}\n{request_target}{V_C_MERCHANT_ID}: {}", + merchant_account.peek() + ); + let key_value = consts::BASE64_ENGINE + .decode(api_secret.expose()) + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value); + let signature_value = + consts::BASE64_ENGINE.encode(hmac::sign(&key, signature_string.as_bytes()).as_ref()); + let signature_header = format!( + r#"keyid="{}", algorithm="HmacSHA256", headers="{headers}", signature="{signature_value}""#, + api_key.peek() + ); + + Ok(signature_header) + } +} + impl ConnectorIntegration< api::PaymentMethodToken, @@ -56,15 +113,63 @@ where fn build_headers( &self, req: &types::RouterData, - _connectors: &settings::Connectors, - ) -> CustomResult)>, errors::ConnectorError> { - let mut header = vec![( - headers::CONTENT_TYPE.to_string(), - self.get_content_type().to_string().into(), - )]; - let mut api_key = self.get_auth_header(&req.connector_auth_type)?; - header.append(&mut api_key); - Ok(header) + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> + { + let date = OffsetDateTime::now_utc(); + let boa_req = self.get_request_body(req, connectors)?; + let http_method = self.get_http_method(); + let auth = bankofamerica::BankOfAmericaAuthType::try_from(&req.connector_auth_type)?; + let merchant_account = auth.merchant_account.clone(); + let base_url = connectors.bankofamerica.base_url.as_str(); + let boa_host = Url::parse(base_url) + .into_report() + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let host = boa_host + .host_str() + .ok_or(errors::ConnectorError::RequestEncodingFailed)?; + let path: String = self + .get_url(req, connectors)? + .chars() + .skip(base_url.len() - 1) + .collect(); + let sha256 = self.generate_digest( + boa_req + .map_or("{}".to_string(), |s| { + types::RequestBody::get_inner_value(s).expose() + }) + .as_bytes(), + ); + let signature = self.generate_signature( + auth, + host.to_string(), + path.as_str(), + &sha256, + date, + http_method, + )?; + + let mut headers = vec![ + ( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + ), + ( + headers::ACCEPT.to_string(), + "application/hal+json;charset=utf-8".to_string().into(), + ), + (V_C_MERCHANT_ID.to_string(), merchant_account.into_masked()), + ("Date".to_string(), date.to_string().into()), + ("Host".to_string(), host.to_string().into()), + ("Signature".to_string(), signature.into_masked()), + ]; + if matches!(http_method, services::Method::Post | services::Method::Put) { + headers.push(( + "Digest".to_string(), + format!("SHA-256={sha256}").into_masked(), + )); + } + Ok(headers) } } @@ -74,50 +179,77 @@ impl ConnectorCommon for Bankofamerica { } fn get_currency_unit(&self) -> api::CurrencyUnit { - api::CurrencyUnit::Minor + api::CurrencyUnit::Base } fn common_get_content_type(&self) -> &'static str { - "application/json" + "application/json;charset=utf-8" } fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { connectors.bankofamerica.base_url.as_ref() } - fn get_auth_header( - &self, - auth_type: &types::ConnectorAuthType, - ) -> CustomResult)>, errors::ConnectorError> { - let auth = bankofamerica::BankofamericaAuthType::try_from(auth_type) - .change_context(errors::ConnectorError::FailedToObtainAuthType)?; - Ok(vec![( - headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), - )]) - } - fn build_error_response( &self, res: Response, ) -> CustomResult { - let response: bankofamerica::BankofamericaErrorResponse = res + let response: bankofamerica::BankOfAmericaErrorResponse = res .response - .parse_struct("BankofamericaErrorResponse") + .parse_struct("BankOfAmerica ErrorResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let error_message = if res.status_code == 401 { + consts::CONNECTOR_UNAUTHORIZED_ERROR + } else { + consts::NO_ERROR_MESSAGE + }; + + let (code, message) = match response.error_information { + Some(ref error_info) => (error_info.reason.clone(), error_info.message.clone()), + None => ( + response + .reason + .map_or(consts::NO_ERROR_CODE.to_string(), |reason| { + reason.to_string() + }), + response + .message + .map_or(error_message.to_string(), |message| message), + ), + }; + let connector_reason = match response.details { + Some(details) => details + .iter() + .map(|det| format!("{} : {}", det.field, det.reason)) + .collect::>() + .join(", "), + None => message.clone(), + }; + Ok(ErrorResponse { status_code: res.status_code, - code: response.code, - message: response.message, - reason: response.reason, + code, + message, + reason: Some(connector_reason), attempt_status: None, }) } } impl ConnectorValidation for Bankofamerica { - //TODO: implement functions when support enabled + fn validate_capture_method( + &self, + capture_method: Option, + ) -> CustomResult<(), errors::ConnectorError> { + let capture_method = capture_method.unwrap_or_default(); + match capture_method { + enums::CaptureMethod::Automatic | enums::CaptureMethod::Manual => Ok(()), + enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err( + connector_utils::construct_not_implemented_error_report(capture_method, self.id()), + ), + } + } } impl ConnectorIntegration @@ -158,9 +290,12 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}pts/v2/payments/", + api::ConnectorCommon::base_url(self, connectors) + )) } fn get_request_body( @@ -168,20 +303,20 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { - let connector_router_data = bankofamerica::BankofamericaRouterData::try_from(( + let connector_router_data = bankofamerica::BankOfAmericaRouterData::try_from(( &self.get_currency_unit(), req.request.currency, req.request.amount, req, ))?; - let req_obj = - bankofamerica::BankofamericaPaymentsRequest::try_from(&connector_router_data)?; - let bankofamerica_req = types::RequestBody::log_and_get_request_body( - &req_obj, - utils::Encode::::encode_to_string_of_json, + let connector_request = + bankofamerica::BankOfAmericaPaymentsRequest::try_from(&connector_router_data)?; + let bankofamerica_payments_request = types::RequestBody::log_and_get_request_body( + &connector_request, + utils::Encode::::encode_to_string_of_json, ) .change_context(errors::ConnectorError::RequestEncodingFailed)?; - Ok(Some(bankofamerica_req)) + Ok(Some(bankofamerica_payments_request)) } fn build_request( @@ -211,9 +346,9 @@ impl ConnectorIntegration CustomResult { - let response: bankofamerica::BankofamericaPaymentsResponse = res + let response: bankofamerica::BankOfAmericaPaymentsResponse = res .response - .parse_struct("Bankofamerica PaymentsAuthorizeResponse") + .parse_struct("BankOfAmerica PaymentResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -245,12 +380,24 @@ impl ConnectorIntegration services::Method { + services::Method::Get + } + fn get_url( &self, - _req: &types::PaymentsSyncRouterData, - _connectors: &settings::Connectors, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_payment_id = req + .request + .connector_transaction_id + .get_connector_transaction_id() + .change_context(errors::ConnectorError::MissingConnectorTransactionID)?; + Ok(format!( + "{}tss/v2/transactions/{connector_payment_id}", + self.base_url(connectors) + )) } fn build_request( @@ -273,9 +420,9 @@ impl ConnectorIntegration CustomResult { - let response: bankofamerica::BankofamericaPaymentsResponse = res + let response: bankofamerica::BankOfAmericaTransactionResponse = res .response - .parse_struct("bankofamerica PaymentsSyncResponse") + .parse_struct("BankOfAmerica PaymentSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -309,18 +456,35 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_payment_id = req.request.connector_transaction_id.clone(); + Ok(format!( + "{}pts/v2/payments/{connector_payment_id}/captures", + self.base_url(connectors) + )) } fn get_request_body( &self, - _req: &types::PaymentsCaptureRouterData, + req: &types::PaymentsCaptureRouterData, _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + let connector_router_data = bankofamerica::BankOfAmericaRouterData::try_from(( + &self.get_currency_unit(), + req.request.currency, + req.request.amount_to_capture, + req, + ))?; + let connector_request = + bankofamerica::BankOfAmericaCaptureRequest::try_from(&connector_router_data)?; + let bankofamerica_capture_request = types::RequestBody::log_and_get_request_body( + &connector_request, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(bankofamerica_capture_request)) } fn build_request( @@ -348,9 +512,9 @@ impl ConnectorIntegration CustomResult { - let response: bankofamerica::BankofamericaPaymentsResponse = res + let response: bankofamerica::BankOfAmericaPaymentsResponse = res .response - .parse_struct("Bankofamerica PaymentsCaptureResponse") + .parse_struct("BankOfAmerica PaymentResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -370,6 +534,100 @@ impl ConnectorIntegration for Bankofamerica { + fn get_headers( + &self, + req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_url( + &self, + req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + let connector_payment_id = req.request.connector_transaction_id.clone(); + Ok(format!( + "{}pts/v2/payments/{connector_payment_id}/reversals", + self.base_url(connectors) + )) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_request_body( + &self, + req: &types::PaymentsCancelRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let connector_router_data = bankofamerica::BankOfAmericaRouterData::try_from(( + &self.get_currency_unit(), + req.request + .currency + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "Currency", + })?, + req.request + .amount + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "Amount", + })?, + req, + ))?; + let connector_request = + bankofamerica::BankOfAmericaVoidRequest::try_from(&connector_router_data)?; + + let bankofamerica_void_request = types::RequestBody::log_and_get_request_body( + &connector_request, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(bankofamerica_void_request)) + } + + fn build_request( + &self, + req: &types::PaymentsCancelRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) + .body(types::PaymentsVoidType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsCancelRouterData, + res: Response, + ) -> CustomResult { + let response: bankofamerica::BankOfAmericaPaymentsResponse = res + .response + .parse_struct("BankOfAmerica PaymentResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + ) -> CustomResult { + self.build_error_response(res) + } } impl ConnectorIntegration @@ -389,10 +647,14 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + req: &types::RefundsRouterData, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let connector_payment_id = req.request.connector_transaction_id.clone(); + Ok(format!( + "{}pts/v2/payments/{connector_payment_id}/refunds", + self.base_url(connectors) + )) } fn get_request_body( @@ -400,16 +662,16 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { - let connector_router_data = bankofamerica::BankofamericaRouterData::try_from(( + let connector_router_data = bankofamerica::BankOfAmericaRouterData::try_from(( &self.get_currency_unit(), req.request.currency, req.request.refund_amount, req, ))?; - let req_obj = bankofamerica::BankofamericaRefundRequest::try_from(&connector_router_data)?; + let req_obj = bankofamerica::BankOfAmericaRefundRequest::try_from(&connector_router_data)?; let bankofamerica_req = types::RequestBody::log_and_get_request_body( &req_obj, - utils::Encode::::encode_to_string_of_json, + utils::Encode::::encode_to_string_of_json, ) .change_context(errors::ConnectorError::RequestEncodingFailed)?; Ok(Some(bankofamerica_req)) @@ -439,7 +701,7 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult, errors::ConnectorError> { - let response: bankofamerica::RefundResponse = res + let response: bankofamerica::BankOfAmericaRefundResponse = res .response .parse_struct("bankofamerica RefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; @@ -473,12 +735,20 @@ impl ConnectorIntegration services::Method { + services::Method::Get + } + fn get_url( &self, - _req: &types::RefundSyncRouterData, - _connectors: &settings::Connectors, + req: &types::RefundSyncRouterData, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + let refund_id = req.request.get_connector_refund_id()?; + Ok(format!( + "{}tss/v2/transactions/{refund_id}", + self.base_url(connectors) + )) } fn build_request( @@ -504,7 +774,7 @@ impl ConnectorIntegration CustomResult { - let response: bankofamerica::RefundResponse = res + let response: bankofamerica::BankOfAmericaRsyncResponse = res .response .parse_struct("bankofamerica RefundSyncResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index a396c47a4ced..20b2af48b168 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -1,15 +1,51 @@ +use api_models::payments; +use common_utils::pii; use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ - connector::utils::PaymentsAuthorizeRequestData, + connector::utils::{ + self, AddressDetailsData, CardData, CardIssuer, PaymentsAuthorizeRequestData, + PaymentsSyncRequestData, RouterData, + }, + consts, core::errors, - types::{self, api, storage::enums}, + types::{ + self, + api::{self, enums as api_enums}, + storage::enums, + transformers::ForeignFrom, + }, }; -//TODO: Fill the struct with respective fields -pub struct BankofamericaRouterData { - pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. +pub struct BankOfAmericaAuthType { + pub(super) api_key: Secret, + pub(super) merchant_account: Secret, + pub(super) api_secret: Secret, +} + +impl TryFrom<&types::ConnectorAuthType> for BankOfAmericaAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + if let types::ConnectorAuthType::SignatureKey { + api_key, + key1, + api_secret, + } = auth_type + { + Ok(Self { + api_key: api_key.to_owned(), + merchant_account: key1.to_owned(), + api_secret: api_secret.to_owned(), + }) + } else { + Err(errors::ConnectorError::FailedToObtainAuthType)? + } + } +} + +pub struct BankOfAmericaRouterData { + pub amount: String, pub router_data: T, } @@ -19,18 +55,18 @@ impl types::storage::enums::Currency, i64, T, - )> for BankofamericaRouterData + )> for BankOfAmericaRouterData { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( + (currency_unit, currency, amount, item): ( &types::api::CurrencyUnit, types::storage::enums::Currency, i64, T, ), ) -> Result { - //Todo : use utils to convert the amount to the type of amount that a connector accepts + let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; Ok(Self { amount, router_data: item, @@ -38,184 +74,633 @@ impl } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct BankofamericaPaymentsRequest { - amount: i64, - card: BankofamericaCard, +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaPaymentsRequest { + processing_information: ProcessingInformation, + payment_information: PaymentInformation, + order_information: OrderInformationWithBill, + client_reference_information: ClientReferenceInformation, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct BankofamericaCard { - name: Secret, +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProcessingInformation { + capture: bool, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CaptureOptions { + capture_sequence_number: u32, + total_capture_count: u32, +} + +#[derive(Debug, Serialize)] +pub struct PaymentInformation { + card: Card, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Card { number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, + expiration_month: Secret, + expiration_year: Secret, + security_code: Secret, + #[serde(rename = "type")] + card_type: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OrderInformationWithBill { + amount_details: Amount, + bill_to: BillTo, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Amount { + total_amount: String, + currency: api_models::enums::Currency, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BillTo { + first_name: Secret, + last_name: Secret, + address1: Secret, + locality: String, + administrative_area: Secret, + postal_code: Secret, + country: api_enums::CountryAlpha2, + email: pii::Email, +} + +// for bankofamerica each item in Billing is mandatory +fn build_bill_to( + address_details: &payments::Address, + email: pii::Email, +) -> Result> { + let address = address_details + .address + .as_ref() + .ok_or_else(utils::missing_field_err("billing.address"))?; + Ok(BillTo { + first_name: address.get_first_name()?.to_owned(), + last_name: address.get_last_name()?.to_owned(), + address1: address.get_line1()?.to_owned(), + locality: address.get_city()?.to_owned(), + administrative_area: address.to_state_code()?, + postal_code: address.get_zip()?.to_owned(), + country: address.get_country()?.to_owned(), + email, + }) +} + +impl From for String { + fn from(card_issuer: CardIssuer) -> Self { + let card_type = match card_issuer { + CardIssuer::AmericanExpress => "003", + CardIssuer::Master => "002", + //"042" is the type code for Masetro Cards(International). For Maestro Cards(UK-Domestic) the mapping should be "024" + CardIssuer::Maestro => "042", + CardIssuer::Visa => "001", + CardIssuer::Discover => "004", + CardIssuer::DinersClub => "005", + CardIssuer::CarteBlanche => "006", + CardIssuer::JCB => "007", + }; + card_type.to_string() + } } -impl TryFrom<&BankofamericaRouterData<&types::PaymentsAuthorizeRouterData>> - for BankofamericaPaymentsRequest +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ClientReferenceInformation { + code: Option, +} + +impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> + for BankOfAmericaPaymentsRequest { type Error = error_stack::Report; fn try_from( - item: &BankofamericaRouterData<&types::PaymentsAuthorizeRouterData>, + item: &BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>, ) -> Result { match item.router_data.request.payment_method_data.clone() { - api::PaymentMethodData::Card(req_card) => { - let card = BankofamericaCard { - name: req_card.card_holder_name, - number: req_card.card_number, - expiry_month: req_card.card_exp_month, - expiry_year: req_card.card_exp_year, - cvc: req_card.card_cvc, - complete: item.router_data.request.is_auto_capture()?, + api::PaymentMethodData::Card(ccard) => { + let email = item.router_data.request.get_email()?; + let bill_to = build_bill_to(item.router_data.get_billing()?, email)?; + + let order_information = OrderInformationWithBill { + amount_details: Amount { + total_amount: item.amount.to_owned(), + currency: item.router_data.request.currency, + }, + bill_to, + }; + let card_issuer = ccard.get_card_issuer(); + let card_type = match card_issuer { + Ok(issuer) => Some(String::from(issuer)), + Err(_) => None, + }; + let payment_information = PaymentInformation { + card: Card { + number: ccard.card_number, + expiration_month: ccard.card_exp_month, + expiration_year: ccard.card_exp_year, + security_code: ccard.card_cvc, + card_type, + }, }; + + let processing_information = ProcessingInformation { + capture: matches!( + item.router_data.request.capture_method, + Some(enums::CaptureMethod::Automatic) | None + ), + }; + + let client_reference_information = ClientReferenceInformation { + code: Some(item.router_data.connector_request_reference_id.clone()), + }; + Ok(Self { - amount: item.amount.to_owned(), - card, + processing_information, + payment_information, + order_information, + client_reference_information, }) } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + payments::PaymentMethodData::CardRedirect(_) + | payments::PaymentMethodData::Wallet(_) + | payments::PaymentMethodData::PayLater(_) + | payments::PaymentMethodData::BankRedirect(_) + | payments::PaymentMethodData::BankDebit(_) + | payments::PaymentMethodData::BankTransfer(_) + | payments::PaymentMethodData::Crypto(_) + | payments::PaymentMethodData::MandatePayment + | payments::PaymentMethodData::Reward + | payments::PaymentMethodData::Upi(_) + | payments::PaymentMethodData::Voucher(_) + | payments::PaymentMethodData::GiftCard(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Bank of America"), + ) + .into()) + } } } } -//TODO: Fill the struct with respective fields -// Auth Struct -pub struct BankofamericaAuthType { - pub(super) api_key: Secret, +#[derive(Debug, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum BankofamericaPaymentStatus { + Authorized, + Succeeded, + Failed, + Voided, + Reversed, + Pending, + Declined, + AuthorizedPendingReview, + Transmitted, } -impl TryFrom<&types::ConnectorAuthType> for BankofamericaAuthType { +impl ForeignFrom<(BankofamericaPaymentStatus, bool)> for enums::AttemptStatus { + fn foreign_from((status, auto_capture): (BankofamericaPaymentStatus, bool)) -> Self { + match status { + BankofamericaPaymentStatus::Authorized => { + if auto_capture { + // Because BankOfAmerica will return Payment Status as Authorized even in AutoCapture Payment + Self::Pending + } else { + Self::Authorized + } + } + BankofamericaPaymentStatus::AuthorizedPendingReview => Self::Authorized, + BankofamericaPaymentStatus::Succeeded | BankofamericaPaymentStatus::Transmitted => { + Self::Charged + } + BankofamericaPaymentStatus::Voided | BankofamericaPaymentStatus::Reversed => { + Self::Voided + } + BankofamericaPaymentStatus::Failed | BankofamericaPaymentStatus::Declined => { + Self::Failure + } + BankofamericaPaymentStatus::Pending => Self::Pending, + } + } +} + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum BankOfAmericaPaymentsResponse { + ClientReferenceInformation(BankOfAmericaClientReferenceResponse), + ErrorInformation(BankOfAmericaErrorInformationResponse), +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaClientReferenceResponse { + id: String, + status: BankofamericaPaymentStatus, + client_reference_information: ClientReferenceInformation, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaErrorInformationResponse { + id: String, + error_information: BankOfAmericaErrorInformation, +} + +#[derive(Debug, Deserialize)] +pub struct BankOfAmericaErrorInformation { + reason: Option, + message: String, +} + +impl + TryFrom< + types::ResponseRouterData< + F, + BankOfAmericaPaymentsResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ type Error = error_stack::Report; - fn try_from(auth_type: &types::ConnectorAuthType) -> Result { - match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { - api_key: api_key.to_owned(), + fn try_from( + item: types::ResponseRouterData< + F, + BankOfAmericaPaymentsResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, + ) -> Result { + match item.response { + BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => Ok(Self { + status: enums::AttemptStatus::foreign_from(( + info_response.status, + item.data.request.is_auto_capture()?, + )), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + info_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some( + info_response + .client_reference_information + .code + .unwrap_or(info_response.id), + ), + }), + ..item.data + }), + BankOfAmericaPaymentsResponse::ErrorInformation(error_response) => Ok(Self { + response: Err(types::ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: error_response.error_information.message, + reason: error_response.error_information.reason, + status_code: item.http_code, + attempt_status: None, + }), + ..item.data }), - _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), } } } -// PaymentsResponse -//TODO: Append the remaining status flags -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum BankofamericaPaymentStatus { - Succeeded, - Failed, - #[default] - Processing, + +impl + TryFrom< + types::ResponseRouterData< + F, + BankOfAmericaPaymentsResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + BankOfAmericaPaymentsResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + ) -> Result { + match item.response { + BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => Ok(Self { + status: enums::AttemptStatus::foreign_from((info_response.status, true)), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + info_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some( + info_response + .client_reference_information + .code + .unwrap_or(info_response.id), + ), + }), + ..item.data + }), + BankOfAmericaPaymentsResponse::ErrorInformation(error_response) => Ok(Self { + response: Err(types::ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: error_response.error_information.message, + reason: error_response.error_information.reason, + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }), + } + } } -impl From for enums::AttemptStatus { - fn from(item: BankofamericaPaymentStatus) -> Self { - match item { - BankofamericaPaymentStatus::Succeeded => Self::Charged, - BankofamericaPaymentStatus::Failed => Self::Failure, - BankofamericaPaymentStatus::Processing => Self::Authorizing, +impl + TryFrom< + types::ResponseRouterData< + F, + BankOfAmericaPaymentsResponse, + types::PaymentsCancelData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + BankOfAmericaPaymentsResponse, + types::PaymentsCancelData, + types::PaymentsResponseData, + >, + ) -> Result { + match item.response { + BankOfAmericaPaymentsResponse::ClientReferenceInformation(info_response) => Ok(Self { + status: enums::AttemptStatus::foreign_from((info_response.status, false)), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + info_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some( + info_response + .client_reference_information + .code + .unwrap_or(info_response.id), + ), + }), + ..item.data + }), + BankOfAmericaPaymentsResponse::ErrorInformation(error_response) => Ok(Self { + response: Err(types::ErrorResponse { + code: consts::NO_ERROR_CODE.to_string(), + message: error_response.error_information.message, + reason: error_response.error_information.reason, + status_code: item.http_code, + attempt_status: None, + }), + ..item.data + }), } } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct BankofamericaPaymentsResponse { - status: BankofamericaPaymentStatus, +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum BankOfAmericaTransactionResponse { + ApplicationInformation(BankOfAmericaApplicationInfoResponse), + ErrorInformation(BankOfAmericaErrorInformationResponse), +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaApplicationInfoResponse { id: String, + application_information: ApplicationInformation, + client_reference_information: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ApplicationInformation { + status: BankofamericaPaymentStatus, } -impl +impl TryFrom< - types::ResponseRouterData, - > for types::RouterData + types::ResponseRouterData< + F, + BankOfAmericaTransactionResponse, + types::PaymentsSyncData, + types::PaymentsResponseData, + >, + > for types::RouterData { type Error = error_stack::Report; fn try_from( item: types::ResponseRouterData< F, - BankofamericaPaymentsResponse, - T, + BankOfAmericaTransactionResponse, + types::PaymentsSyncData, types::PaymentsResponseData, >, ) -> Result { - Ok(Self { - status: enums::AttemptStatus::from(item.response.status), - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), - redirection_data: None, - mandate_reference: None, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: None, + match item.response { + BankOfAmericaTransactionResponse::ApplicationInformation(app_response) => Ok(Self { + status: enums::AttemptStatus::foreign_from(( + app_response.application_information.status, + item.data.request.is_auto_capture()?, + )), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(app_response.id.clone()), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: app_response + .client_reference_information + .map(|cref| cref.code) + .unwrap_or(Some(app_response.id)), + }), + ..item.data }), - ..item.data + BankOfAmericaTransactionResponse::ErrorInformation(error_response) => Ok(Self { + status: item.data.status, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + error_response.id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some(error_response.id), + }), + ..item.data + }), + } + } +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OrderInformation { + amount_details: Amount, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaCaptureRequest { + order_information: OrderInformation, + client_reference_information: ClientReferenceInformation, +} + +impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsCaptureRouterData>> + for BankOfAmericaCaptureRequest +{ + type Error = error_stack::Report; + fn try_from( + value: &BankOfAmericaRouterData<&types::PaymentsCaptureRouterData>, + ) -> Result { + Ok(Self { + order_information: OrderInformation { + amount_details: Amount { + total_amount: value.amount.to_owned(), + currency: value.router_data.request.currency, + }, + }, + client_reference_information: ClientReferenceInformation { + code: Some(value.router_data.connector_request_reference_id.clone()), + }, }) } } -//TODO: Fill the struct with respective fields -// REFUND : -// Type definition for RefundRequest -#[derive(Default, Debug, Serialize)] -pub struct BankofamericaRefundRequest { - pub amount: i64, +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaVoidRequest { + client_reference_information: ClientReferenceInformation, + reversal_information: ReversalInformation, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ReversalInformation { + amount_details: Amount, + reason: String, } -impl TryFrom<&BankofamericaRouterData<&types::RefundsRouterData>> - for BankofamericaRefundRequest +impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsCancelRouterData>> + for BankOfAmericaVoidRequest { type Error = error_stack::Report; fn try_from( - item: &BankofamericaRouterData<&types::RefundsRouterData>, + value: &BankOfAmericaRouterData<&types::PaymentsCancelRouterData>, ) -> Result { Ok(Self { - amount: item.amount.to_owned(), + client_reference_information: ClientReferenceInformation { + code: Some(value.router_data.connector_request_reference_id.clone()), + }, + reversal_information: ReversalInformation { + amount_details: Amount { + total_amount: value.amount.to_owned(), + currency: value.router_data.request.currency.ok_or( + errors::ConnectorError::MissingRequiredField { + field_name: "Currency", + }, + )?, + }, + reason: value + .router_data + .request + .cancellation_reason + .clone() + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "Cancellation Reason", + })?, + }, }) } } -// Type definition for Refund Response +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaRefundRequest { + order_information: OrderInformation, + client_reference_information: ClientReferenceInformation, +} -#[allow(dead_code)] -#[derive(Debug, Serialize, Default, Deserialize, Clone)] -pub enum RefundStatus { - Succeeded, - Failed, - #[default] - Processing, +impl TryFrom<&BankOfAmericaRouterData<&types::RefundsRouterData>> + for BankOfAmericaRefundRequest +{ + type Error = error_stack::Report; + fn try_from( + item: &BankOfAmericaRouterData<&types::RefundsRouterData>, + ) -> Result { + Ok(Self { + order_information: OrderInformation { + amount_details: Amount { + total_amount: item.amount.clone(), + currency: item.router_data.request.currency, + }, + }, + client_reference_information: ClientReferenceInformation { + code: Some(item.router_data.request.refund_id.clone()), + }, + }) + } } -impl From for enums::RefundStatus { - fn from(item: RefundStatus) -> Self { +impl From for enums::RefundStatus { + fn from(item: BankofamericaRefundStatus) -> Self { match item { - RefundStatus::Succeeded => Self::Success, - RefundStatus::Failed => Self::Failure, - RefundStatus::Processing => Self::Pending, - //TODO: Review mapping + BankofamericaRefundStatus::Succeeded | BankofamericaRefundStatus::Transmitted => { + Self::Success + } + BankofamericaRefundStatus::Failed => Self::Failure, + BankofamericaRefundStatus::Pending => Self::Pending, } } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct RefundResponse { +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaRefundResponse { id: String, - status: RefundStatus, + status: BankofamericaRefundStatus, } -impl TryFrom> +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), + connector_refund_id: item.response.id, refund_status: enums::RefundStatus::from(item.response.status), }), ..item.data @@ -223,28 +708,86 @@ impl TryFrom> } } -impl TryFrom> +#[derive(Debug, Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum BankofamericaRefundStatus { + Succeeded, + Transmitted, + Failed, + Pending, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RsyncApplicationInformation { + status: BankofamericaRefundStatus, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaRsyncResponse { + id: String, + application_information: RsyncApplicationInformation, +} + +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + connector_refund_id: item.response.id, + refund_status: enums::RefundStatus::from( + item.response.application_information.status, + ), }), ..item.data }) } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] -pub struct BankofamericaErrorResponse { - pub status_code: u16, - pub code: String, +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BankOfAmericaErrorResponse { + pub error_information: Option, + pub status: Option, + pub message: Option, + pub reason: Option, + pub details: Option>, +} + +#[derive(Debug, Deserialize, strum::Display)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum Reason { + MissingField, + InvalidData, + DuplicateRequest, + InvalidCard, + AuthAlreadyReversed, + CardTypeNotAccepted, + InvalidMerchantConfiguration, + ProcessorUnavailable, + InvalidAmount, + InvalidCardType, + InvalidPaymentId, + NotSupported, + SystemError, + ServerTimeout, + ServiceTimeout, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Details { + pub field: String, + pub reason: String, +} + +#[derive(Debug, Default, Deserialize)] +pub struct ErrorInformation { pub message: String, - pub reason: Option, + pub reason: String, } diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs index 8737d2b30474..3670f65a2f02 100644 --- a/crates/router/src/connector/klarna.rs +++ b/crates/router/src/connector/klarna.rs @@ -323,6 +323,7 @@ impl | api_models::enums::PaymentMethodType::BcaBankTransfer | api_models::enums::PaymentMethodType::BniVa | api_models::enums::PaymentMethodType::BriVa + | api_models::enums::PaymentMethodType::CardRedirect | api_models::enums::PaymentMethodType::CimbVa | api_models::enums::PaymentMethodType::ClassicReward | api_models::enums::PaymentMethodType::Credit diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 6e371b1e1a2b..ee70d26ffbda 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -260,13 +260,13 @@ impl TryFrom for Gateway { utils::CardIssuer::Maestro => Ok(Self::Maestro), utils::CardIssuer::Discover => Ok(Self::Discover), utils::CardIssuer::Visa => Ok(Self::Visa), - utils::CardIssuer::DinersClub | utils::CardIssuer::JCB => { - Err(errors::ConnectorError::NotSupported { - message: issuer.to_string(), - connector: "Multisafe pay", - } - .into()) + utils::CardIssuer::DinersClub + | utils::CardIssuer::JCB + | utils::CardIssuer::CarteBlanche => Err(errors::ConnectorError::NotSupported { + message: issuer.to_string(), + connector: "Multisafe pay", } + .into()), } } } diff --git a/crates/router/src/connector/noon.rs b/crates/router/src/connector/noon.rs index 866f8f4c58fa..0ea73efd94bd 100644 --- a/crates/router/src/connector/noon.rs +++ b/crates/router/src/connector/noon.rs @@ -154,6 +154,14 @@ impl ConnectorValidation for Noon { ), } } + + fn validate_psync_reference_id( + &self, + _data: &types::PaymentsSyncRouterData, + ) -> CustomResult<(), errors::ConnectorError> { + // since we can make psync call with our reference_id, having connector_transaction_id is not an mandatory criteria + Ok(()) + } } impl ConnectorIntegration diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index 3a859b325300..e2e837929c41 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -69,13 +69,14 @@ impl TryFrom for PayeezyCardType { utils::CardIssuer::Discover => Ok(Self::Discover), utils::CardIssuer::Visa => Ok(Self::Visa), - utils::CardIssuer::Maestro | utils::CardIssuer::DinersClub | utils::CardIssuer::JCB => { - Err(errors::ConnectorError::NotSupported { - message: utils::SELECTED_PAYMENT_METHOD.to_string(), - connector: "Payeezy", - } - .into()) + utils::CardIssuer::Maestro + | utils::CardIssuer::DinersClub + | utils::CardIssuer::JCB + | utils::CardIssuer::CarteBlanche => Err(errors::ConnectorError::NotSupported { + message: utils::SELECTED_PAYMENT_METHOD.to_string(), + connector: "Payeezy", } + .into()), } } } diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs index 0092363523e5..5468c6bb8061 100644 --- a/crates/router/src/connector/paypal/transformers.rs +++ b/crates/router/src/connector/paypal/transformers.rs @@ -439,7 +439,8 @@ impl TryFrom<&api_models::payments::CardRedirectData> for PaypalPaymentsRequest match value { api_models::payments::CardRedirectData::Knet {} | api_models::payments::CardRedirectData::Benefit {} - | api_models::payments::CardRedirectData::MomoAtm {} => { + | api_models::payments::CardRedirectData::MomoAtm {} + | api_models::payments::CardRedirectData::CardRedirect {} => { Err(errors::ConnectorError::NotSupported { message: utils::SELECTED_PAYMENT_METHOD.to_string(), connector: "Paypal", diff --git a/crates/router/src/connector/prophetpay.rs b/crates/router/src/connector/prophetpay.rs index 417c34207e05..6765fad2653d 100644 --- a/crates/router/src/connector/prophetpay.rs +++ b/crates/router/src/connector/prophetpay.rs @@ -2,12 +2,14 @@ pub mod transformers; use std::fmt::Debug; +use base64::Engine; use error_stack::{IntoReport, ResultExt}; -use masking::ExposeInterface; +use masking::PeekInterface; use transformers as prophetpay; use crate::{ configs::settings, + consts, core::errors::{self, CustomResult}, headers, services::{ @@ -37,6 +39,7 @@ impl api::Refund for Prophetpay {} impl api::RefundExecute for Prophetpay {} impl api::RefundSync for Prophetpay {} impl api::PaymentToken for Prophetpay {} +impl api::payments::PaymentsCompleteAuthorize for Prophetpay {} impl ConnectorIntegration< @@ -73,7 +76,7 @@ impl ConnectorCommon for Prophetpay { } fn get_currency_unit(&self) -> api::CurrencyUnit { - api::CurrencyUnit::Minor + api::CurrencyUnit::Base } fn common_get_content_type(&self) -> &'static str { @@ -90,9 +93,13 @@ impl ConnectorCommon for Prophetpay { ) -> CustomResult)>, errors::ConnectorError> { let auth = prophetpay::ProphetpayAuthType::try_from(auth_type) .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + + let auth_val = format!("{}:{}", auth.user_name.peek(), auth.password.peek()); + let basic_token = format!("Basic {}", consts::BASE64_ENGINE.encode(auth_val)); + Ok(vec![( headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), + basic_token.into_masked(), )]) } @@ -107,9 +114,9 @@ impl ConnectorCommon for Prophetpay { Ok(ErrorResponse { status_code: res.status_code, - code: response.code, - message: response.message, - reason: response.reason, + code: response.status.to_string(), + message: response.title, + reason: Some(response.errors.to_string()), attempt_status: None, }) } @@ -157,9 +164,12 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}hp/api/HostedTokenize/CreateHostedTokenize", + self.base_url(connectors) + )) } fn get_request_body( @@ -173,10 +183,11 @@ impl ConnectorIntegration::encode_to_string_of_json, + utils::Encode::::encode_to_string_of_json, ) .change_context(errors::ConnectorError::RequestEncodingFailed)?; Ok(Some(prophetpay_req)) @@ -208,10 +219,114 @@ impl ConnectorIntegration CustomResult { - let response: prophetpay::ProphetpayPaymentsResponse = res + ) -> CustomResult + where + types::PaymentsResponseData: Clone, + { + let response: prophetpay::ProphetpayTokenResponse = res .response - .parse_struct("Prophetpay PaymentsAuthorizeResponse") + .parse_struct("prophetpay ProphetpayTokenResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + ) -> CustomResult { + self.build_error_response(res) + } +} + +impl + ConnectorIntegration< + api::CompleteAuthorize, + types::CompleteAuthorizeData, + types::PaymentsResponseData, + > for Prophetpay +{ + fn get_headers( + &self, + req: &types::PaymentsCompleteAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsCompleteAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!( + "{}hp/api/Transactions/ProcessTransaction", + self.base_url(connectors) + )) + } + + fn get_request_body( + &self, + req: &types::PaymentsCompleteAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let connector_router_data = prophetpay::ProphetpayRouterData::try_from(( + &self.get_currency_unit(), + req.request.currency, + req.request.amount, + req, + ))?; + let req_obj = prophetpay::ProphetpayCompleteRequest::try_from(&connector_router_data)?; + + let prophetpay_req = types::RequestBody::log_and_get_request_body( + &req_obj, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(prophetpay_req)) + } + + fn build_request( + &self, + req: &types::PaymentsCompleteAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsCompleteAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsCompleteAuthorizeType::get_headers( + self, req, connectors, + )?) + .body(types::PaymentsCompleteAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsCompleteAuthorizeRouterData, + res: Response, + ) -> CustomResult + where + types::PaymentsResponseData: Clone, + { + let response: prophetpay::ProphetpayResponse = res + .response + .parse_struct("prophetpay ProphetpayResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -246,9 +361,27 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}hp/api/Transactions/ProcessTransaction", + self.base_url(connectors) + )) + } + + fn get_request_body( + &self, + req: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let req_obj = prophetpay::ProphetpaySyncRequest::try_from(req)?; + + let prophetpay_req = types::RequestBody::log_and_get_request_body( + &req_obj, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(prophetpay_req)) } fn build_request( @@ -258,10 +391,13 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() - .method(services::Method::Get) + .method(services::Method::Post) .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -271,9 +407,9 @@ impl ConnectorIntegration CustomResult { - let response: prophetpay::ProphetpayPaymentsResponse = res + let response: prophetpay::ProphetpayResponse = res .response - .parse_struct("prophetpay PaymentsSyncResponse") + .parse_struct("prophetpay ProphetpayResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -292,10 +428,15 @@ impl ConnectorIntegration for Prophetpay +{ +} + +impl ConnectorIntegration + for Prophetpay { fn get_headers( &self, - req: &types::PaymentsCaptureRouterData, + req: &types::PaymentsCancelRouterData, connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) @@ -307,34 +448,42 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}hp/api/Transactions/ProcessTransaction", + self.base_url(connectors) + )) } fn get_request_body( &self, - _req: &types::PaymentsCaptureRouterData, + req: &types::PaymentsCancelRouterData, _connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + let req_obj = prophetpay::ProphetpayVoidRequest::try_from(req)?; + + let prophetpay_req = types::RequestBody::log_and_get_request_body( + &req_obj, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(prophetpay_req)) } fn build_request( &self, - req: &types::PaymentsCaptureRouterData, + req: &types::PaymentsCancelRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() - .method(services::Method::Post) - .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) + .method(services::Method::Get) + .url(&types::PaymentsVoidType::get_url(self, req, connectors)?) .attach_default_headers() - .headers(types::PaymentsCaptureType::get_headers( - self, req, connectors, - )?) - .body(types::PaymentsCaptureType::get_request_body( + .headers(types::PaymentsVoidType::get_headers(self, req, connectors)?) + .body(types::PaymentsVoidType::get_request_body( self, req, connectors, )?) .build(), @@ -343,12 +492,12 @@ impl ConnectorIntegration CustomResult { - let response: prophetpay::ProphetpayPaymentsResponse = res + ) -> CustomResult { + let response: prophetpay::ProphetpayResponse = res .response - .parse_struct("Prophetpay PaymentsCaptureResponse") + .parse_struct("prophetpay ProphetpayResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, @@ -365,11 +514,6 @@ impl ConnectorIntegration - for Prophetpay -{ -} - impl ConnectorIntegration for Prophetpay { @@ -388,9 +532,12 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}hp/api/Transactions/ProcessTransaction", + self.base_url(connectors) + )) } fn get_request_body( @@ -437,10 +584,11 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult, errors::ConnectorError> { - let response: prophetpay::RefundResponse = res + let response: prophetpay::ProphetpayRefundResponse = res .response - .parse_struct("prophetpay RefundResponse") + .parse_struct("prophetpay ProphetpayRefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + types::RouterData::try_from(types::ResponseRouterData { response, data: data.clone(), @@ -474,9 +622,27 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(format!( + "{}hp/api/Transactions/ProcessTransaction", + self.base_url(connectors) + )) + } + + fn get_request_body( + &self, + req: &types::RefundSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let req_obj = prophetpay::ProphetpayRefundSyncRequest::try_from(req)?; + + let prophetpay_req = types::RequestBody::log_and_get_request_body( + &req_obj, + utils::Encode::::encode_to_string_of_json, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Some(prophetpay_req)) } fn build_request( @@ -502,9 +668,9 @@ impl ConnectorIntegration CustomResult { - let response: prophetpay::RefundResponse = res + let response: prophetpay::ProphetpayRefundResponse = res .response - .parse_struct("prophetpay RefundSyncResponse") + .parse_struct("prophetpay ProphetpayRefundResponse") .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; types::RouterData::try_from(types::ResponseRouterData { response, diff --git a/crates/router/src/connector/prophetpay/transformers.rs b/crates/router/src/connector/prophetpay/transformers.rs index 1066c88df3e1..74071d5b85cb 100644 --- a/crates/router/src/connector/prophetpay/transformers.rs +++ b/crates/router/src/connector/prophetpay/transformers.rs @@ -1,14 +1,20 @@ -use masking::Secret; +use std::collections::HashMap; + +use common_utils::{consts, errors::CustomResult}; +use error_stack::{IntoReport, ResultExt}; +use masking::{PeekInterface, Secret}; use serde::{Deserialize, Serialize}; +use url::Url; use crate::{ - connector::utils::PaymentsAuthorizeRequestData, + connector::utils, core::errors, + services, types::{self, api, storage::enums}, }; pub struct ProphetpayRouterData { - pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub amount: f64, pub router_data: T, } @@ -22,13 +28,14 @@ impl { type Error = error_stack::Report; fn try_from( - (_currency_unit, _currency, amount, item): ( + (currency_unit, currency, amount, item): ( &types::api::CurrencyUnit, types::storage::enums::Currency, i64, T, ), ) -> Result { + let amount = utils::get_amount_as_f64(currency_unit, amount, currency)?; Ok(Self { amount, router_data: item, @@ -36,108 +43,370 @@ impl } } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct ProphetpayPaymentsRequest { - amount: i64, - card: ProphetpayCard, +pub struct ProphetpayAuthType { + pub(super) user_name: Secret, + pub(super) password: Secret, + pub(super) profile_id: Secret, +} + +impl TryFrom<&types::ConnectorAuthType> for ProphetpayAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + match auth_type { + types::ConnectorAuthType::SignatureKey { + api_key, + key1, + api_secret, + } => Ok(Self { + user_name: api_key.to_owned(), + password: key1.to_owned(), + profile_id: api_secret.to_owned(), + }), + _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + } + } +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct ProphetpayTokenRequest { + ref_info: String, + profile: Secret, + entry_method: i8, + token_type: i8, + card_entry_context: i8, +} + +#[derive(Debug, Clone)] +pub enum ProphetpayEntryMethod { + ManualEntry, + CardSwipe, +} + +impl ProphetpayEntryMethod { + fn get_entry_method(&self) -> i8 { + match self { + Self::ManualEntry => 1, + Self::CardSwipe => 2, + } + } +} + +#[derive(Debug, Clone)] +#[repr(i8)] +pub enum ProphetpayTokenType { + Normal, + SaleTab, + TemporarySave, } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct ProphetpayCard { - name: Secret, - number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, +impl ProphetpayTokenType { + fn get_token_type(&self) -> i8 { + match self { + Self::Normal => 0, + Self::SaleTab => 1, + Self::TemporarySave => 2, + } + } +} + +#[derive(Debug, Clone)] +#[repr(i8)] +pub enum ProphetpayCardContext { + NotApplicable, + WebConsumerInitiated, +} + +impl ProphetpayCardContext { + fn get_card_context(&self) -> i8 { + match self { + Self::NotApplicable => 0, + Self::WebConsumerInitiated => 5, + } + } } impl TryFrom<&ProphetpayRouterData<&types::PaymentsAuthorizeRouterData>> - for ProphetpayPaymentsRequest + for ProphetpayTokenRequest { type Error = error_stack::Report; fn try_from( item: &ProphetpayRouterData<&types::PaymentsAuthorizeRouterData>, ) -> Result { - match item.router_data.request.payment_method_data.clone() { - api::PaymentMethodData::Card(req_card) => { - let card = ProphetpayCard { - name: req_card.card_holder_name, - number: req_card.card_number, - expiry_month: req_card.card_exp_month, - expiry_year: req_card.card_exp_year, - cvc: req_card.card_cvc, - complete: item.router_data.request.is_auto_capture()?, - }; - Ok(Self { - amount: item.amount.to_owned(), - card, - }) + if item.router_data.request.currency == api_models::enums::Currency::USD { + match item.router_data.request.payment_method_data.clone() { + api::PaymentMethodData::CardRedirect( + api_models::payments::CardRedirectData::CardRedirect {}, + ) => { + let auth_data = + ProphetpayAuthType::try_from(&item.router_data.connector_auth_type)?; + Ok(Self { + ref_info: item.router_data.connector_request_reference_id.to_owned(), + profile: auth_data.profile_id, + entry_method: ProphetpayEntryMethod::get_entry_method( + &ProphetpayEntryMethod::ManualEntry, + ), + token_type: ProphetpayTokenType::get_token_type( + &ProphetpayTokenType::SaleTab, + ), + card_entry_context: ProphetpayCardContext::get_card_context( + &ProphetpayCardContext::WebConsumerInitiated, + ), + }) + } + _ => Err( + errors::ConnectorError::NotImplemented("Payment methods".to_string()).into(), + ), } - _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } else { + Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } } } -pub struct ProphetpayAuthType { - pub(super) api_key: Secret, +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayTokenResponse { + hosted_tokenize_id: String, } -impl TryFrom<&types::ConnectorAuthType> for ProphetpayAuthType { +impl + TryFrom< + types::ResponseRouterData< + F, + ProphetpayTokenResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, + > for types::RouterData +{ type Error = error_stack::Report; - fn try_from(auth_type: &types::ConnectorAuthType) -> Result { - match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { - api_key: api_key.to_owned(), + fn try_from( + item: types::ResponseRouterData< + F, + ProphetpayTokenResponse, + types::PaymentsAuthorizeData, + types::PaymentsResponseData, + >, + ) -> Result { + let url_data = format!( + "{}{}", + consts::PROPHETPAY_REDIRECT_URL, + item.response.hosted_tokenize_id + ); + + let redirect_url = Url::parse(url_data.as_str()) + .into_report() + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + let redirection_data = get_redirect_url_form( + redirect_url, + item.data.request.complete_authorize_url.clone(), + ) + .ok(); + + Ok(Self { + status: enums::AttemptStatus::AuthenticationPending, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::NoResponseId, + redirection_data, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, }), - _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + ..item.data + }) + } +} + +fn get_redirect_url_form( + mut redirect_url: Url, + complete_auth_url: Option, +) -> CustomResult { + let mut form_fields = std::collections::HashMap::::new(); + + form_fields.insert( + String::from("redirectUrl"), + complete_auth_url.ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "complete_auth_url", + })?, + ); + + // Do not include query params in the endpoint + redirect_url.set_query(None); + + Ok(services::RedirectForm::Form { + endpoint: redirect_url.to_string(), + method: services::Method::Get, + form_fields, + }) +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayCompleteRequest { + amount: f64, + ref_info: String, + inquiry_reference: String, + profile: Secret, + action_type: i8, + card_token: String, +} + +impl TryFrom<&ProphetpayRouterData<&types::PaymentsCompleteAuthorizeRouterData>> + for ProphetpayCompleteRequest +{ + type Error = error_stack::Report; + fn try_from( + item: &ProphetpayRouterData<&types::PaymentsCompleteAuthorizeRouterData>, + ) -> Result { + let auth_data = ProphetpayAuthType::try_from(&item.router_data.connector_auth_type)?; + let card_token = get_card_token(item.router_data.request.redirect_response.clone())?; + Ok(Self { + amount: item.amount.to_owned(), + ref_info: item.router_data.connector_request_reference_id.to_owned(), + inquiry_reference: format!( + "inquiry_{}", + item.router_data.connector_request_reference_id + ), + profile: auth_data.profile_id, + action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Charge), + card_token, + }) + } +} + +fn get_card_token( + response: Option, +) -> CustomResult { + let res = response.ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "redirect_response", + })?; + let queries_params = res + .params + .map(|param| { + let mut queries = HashMap::::new(); + let values = param.peek().split('&').collect::>(); + for value in values { + let pair = value.split('=').collect::>(); + queries.insert(pair[0].to_string(), pair[1].to_string()); + } + queries + }) + .ok_or(errors::ConnectorError::ResponseDeserializationFailed)?; + + for (key, val) in queries_params { + if key.as_str() == consts::PROPHETPAY_TOKEN { + return Ok(val); + } + } + + Err(errors::ConnectorError::MissingRequiredField { + field_name: "card_token", + }) + .into_report() +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpaySyncRequest { + transaction_id: String, + ref_info: String, + inquiry_reference: String, + profile: Secret, + action_type: i8, +} + +#[derive(Debug, Clone)] +pub enum ProphetpayActionType { + Charge, + Refund, + Inquiry, +} + +impl ProphetpayActionType { + fn get_action_type(&self) -> i8 { + match self { + Self::Charge => 1, + Self::Refund => 3, + Self::Inquiry => 7, } } } -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase")] +impl TryFrom<&types::PaymentsSyncRouterData> for ProphetpaySyncRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsSyncRouterData) -> Result { + let auth_data = ProphetpayAuthType::try_from(&item.connector_auth_type)?; + let transaction_id = item + .request + .connector_transaction_id + .get_connector_transaction_id() + .change_context(errors::ConnectorError::MissingConnectorTransactionID)?; + Ok(Self { + transaction_id, + ref_info: item.attempt_id.to_owned(), + inquiry_reference: format!("inquiry_{}", item.attempt_id), + profile: auth_data.profile_id, + action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Inquiry), + }) + } +} + +#[derive(Debug, Clone, Deserialize)] pub enum ProphetpayPaymentStatus { - Succeeded, - Failed, - #[default] - Processing, + Success, + #[serde(rename = "Transaction Approved")] + Charged, + Failure, + #[serde(rename = "Transaction Voided")] + Voided, + #[serde(rename = "Requires a card on file.")] + CardTokenNotFound, + #[serde(rename = "RefInfo and InquiryReference are duplicated")] + DuplicateValue, + #[serde(rename = "Profile is missing")] + MissingProfile, + #[serde(rename = "RefInfo is empty.")] + EmptyRef, } impl From for enums::AttemptStatus { fn from(item: ProphetpayPaymentStatus) -> Self { match item { - ProphetpayPaymentStatus::Succeeded => Self::Charged, - ProphetpayPaymentStatus::Failed => Self::Failure, - ProphetpayPaymentStatus::Processing => Self::Authorizing, + ProphetpayPaymentStatus::Success | ProphetpayPaymentStatus::Charged => Self::Charged, + ProphetpayPaymentStatus::Failure + | ProphetpayPaymentStatus::CardTokenNotFound + | ProphetpayPaymentStatus::DuplicateValue + | ProphetpayPaymentStatus::MissingProfile + | ProphetpayPaymentStatus::EmptyRef => Self::Failure, + ProphetpayPaymentStatus::Voided => Self::Voided, } } } -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ProphetpayPaymentsResponse { - status: ProphetpayPaymentStatus, - id: String, +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayResponse { + pub response_text: ProphetpayPaymentStatus, + #[serde(rename = "transactionID")] + pub transaction_id: String, } -impl - TryFrom< - types::ResponseRouterData, - > for types::RouterData +impl TryFrom> + for types::RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData< - F, - ProphetpayPaymentsResponse, - T, - types::PaymentsResponseData, - >, + item: types::ResponseRouterData, ) -> Result { Ok(Self { - status: enums::AttemptStatus::from(item.response.status), + status: enums::AttemptStatus::from(item.response.response_text), response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.transaction_id, + ), redirection_data: None, mandate_reference: None, connector_metadata: None, @@ -150,8 +419,39 @@ impl } #[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayVoidRequest { + pub transaction_id: String, + pub profile: Secret, + pub ref_info: String, + pub inquiry_reference: String, + pub action_type: i8, +} + +impl TryFrom<&types::PaymentsCancelRouterData> for ProphetpayVoidRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsCancelRouterData) -> Result { + let auth_data = ProphetpayAuthType::try_from(&item.connector_auth_type)?; + let transaction_id = item.request.connector_transaction_id.to_owned(); + Ok(Self { + transaction_id, + ref_info: item.attempt_id.to_owned(), + inquiry_reference: format!("inquiry_{}", item.attempt_id), + profile: auth_data.profile_id, + action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Inquiry), + }) + } +} + +#[derive(Default, Debug, Serialize)] +#[serde(rename_all = "camelCase")] pub struct ProphetpayRefundRequest { - pub amount: i64, + pub amount: f64, + pub transaction_id: String, + pub profile: Secret, + pub ref_info: String, + pub inquiry_reference: String, + pub action_type: i8, } impl TryFrom<&ProphetpayRouterData<&types::RefundsRouterData>> for ProphetpayRefundRequest { @@ -159,75 +459,118 @@ impl TryFrom<&ProphetpayRouterData<&types::RefundsRouterData>> for Prophet fn try_from( item: &ProphetpayRouterData<&types::RefundsRouterData>, ) -> Result { + let auth_data = ProphetpayAuthType::try_from(&item.router_data.connector_auth_type)?; + let transaction_id = item.router_data.request.connector_transaction_id.to_owned(); Ok(Self { + transaction_id, amount: item.amount.to_owned(), + profile: auth_data.profile_id, + ref_info: item.router_data.request.refund_id.to_owned(), + inquiry_reference: format!("inquiry_{}", item.router_data.request.refund_id), + action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Refund), }) } } #[allow(dead_code)] -#[derive(Debug, Serialize, Default, Deserialize, Clone)] +#[derive(Debug, Deserialize, Clone)] pub enum RefundStatus { - Succeeded, - Failed, - #[default] - Processing, + Success, + Failure, + #[serde(rename = "Transaction Voided")] + Voided, + #[serde(rename = "Requires a card on file.")] + CardTokenNotFound, + #[serde(rename = "RefInfo and InquiryReference are duplicated")] + DuplicateValue, + #[serde(rename = "Profile is missing")] + MissingProfile, + #[serde(rename = "RefInfo is empty.")] + EmptyRef, } impl From for enums::RefundStatus { fn from(item: RefundStatus) -> Self { match item { - RefundStatus::Succeeded => Self::Success, - RefundStatus::Failed => Self::Failure, - RefundStatus::Processing => Self::Pending, + RefundStatus::Success + // in retrieving refund, if it is successful, it is shown as voided + | RefundStatus::Voided => Self::Success, + RefundStatus::Failure + | RefundStatus::CardTokenNotFound + | RefundStatus::DuplicateValue + | RefundStatus::MissingProfile + | RefundStatus::EmptyRef => Self::Failure, } } } -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct RefundResponse { - id: String, - status: RefundStatus, +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayRefundResponse { + pub response_text: RefundStatus, } -impl TryFrom> +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + // no refund id is generated, rather transaction id is used for referring to status in refund also + connector_refund_id: item.data.request.connector_transaction_id.clone(), + refund_status: enums::RefundStatus::from(item.response.response_text), }), ..item.data }) } } -impl TryFrom> +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ProphetpayRefundSyncRequest { + ref_info: String, + profile: Secret, + action_type: i8, +} + +impl TryFrom<&types::RefundSyncRouterData> for ProphetpayRefundSyncRequest { + type Error = error_stack::Report; + fn try_from(item: &types::RefundSyncRouterData) -> Result { + let auth_data = ProphetpayAuthType::try_from(&item.connector_auth_type)?; + Ok(Self { + ref_info: item.attempt_id.to_owned(), + profile: auth_data.profile_id, + action_type: ProphetpayActionType::get_action_type(&ProphetpayActionType::Inquiry), + }) + } +} + +impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + connector_refund_id: item.data.request.connector_transaction_id.clone(), + refund_status: enums::RefundStatus::from(item.response.response_text), }), ..item.data }) } } +// Error Response body is yet to be confirmed with the connector #[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] pub struct ProphetpayErrorResponse { - pub status_code: u16, - pub code: String, - pub message: String, - pub reason: Option, + pub status: u16, + pub title: String, + pub trace_id: String, + pub errors: serde_json::Value, } diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index a783fd23fe19..3f0d4f543ba4 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -614,6 +614,7 @@ impl TryFrom for StripePaymentMethodType { enums::PaymentMethodType::AliPay => Ok(Self::Alipay), enums::PaymentMethodType::Przelewy24 => Ok(Self::Przelewy24), enums::PaymentMethodType::Boleto + | enums::PaymentMethodType::CardRedirect | enums::PaymentMethodType::CryptoCurrency | enums::PaymentMethodType::GooglePay | enums::PaymentMethodType::Multibanco @@ -1391,11 +1392,14 @@ fn create_stripe_payment_method( payments::PaymentMethodData::CardRedirect(cardredirect_data) => match cardredirect_data { payments::CardRedirectData::Knet {} | payments::CardRedirectData::Benefit {} - | payments::CardRedirectData::MomoAtm {} => Err(errors::ConnectorError::NotSupported { - message: connector_util::SELECTED_PAYMENT_METHOD.to_string(), - connector: "stripe", + | payments::CardRedirectData::MomoAtm {} + | payments::CardRedirectData::CardRedirect {} => { + Err(errors::ConnectorError::NotSupported { + message: connector_util::SELECTED_PAYMENT_METHOD.to_string(), + connector: "stripe", + } + .into()) } - .into()), }, payments::PaymentMethodData::Reward => Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 8600fe802195..efabbf87aeba 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -638,6 +638,7 @@ static CARD_REGEX: Lazy>> = Lazy CardIssuer::JCB, Regex::new(r"^(3(?:088|096|112|158|337|5(?:2[89]|[3-8][0-9]))\d{12})$"), ); + map.insert(CardIssuer::CarteBlanche, Regex::new(r"^389[0-9]{11}$")); map }); @@ -650,6 +651,7 @@ pub enum CardIssuer { Discover, DinersClub, JCB, + CarteBlanche, } pub trait CardData { diff --git a/crates/router/src/connector/zen/transformers.rs b/crates/router/src/connector/zen/transformers.rs index 6b0d46dec8d1..689894176b26 100644 --- a/crates/router/src/connector/zen/transformers.rs +++ b/crates/router/src/connector/zen/transformers.rs @@ -790,7 +790,8 @@ impl TryFrom<&api_models::payments::CardRedirectData> for ZenPaymentsRequest { match value { api_models::payments::CardRedirectData::Knet {} | api_models::payments::CardRedirectData::Benefit {} - | api_models::payments::CardRedirectData::MomoAtm {} => { + | api_models::payments::CardRedirectData::MomoAtm {} + | api_models::payments::CardRedirectData::CardRedirect {} => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Zen"), ) diff --git a/crates/router/src/core.rs b/crates/router/src/core.rs index b7023fe5ae46..8cc85eef60d6 100644 --- a/crates/router/src/core.rs +++ b/crates/router/src/core.rs @@ -9,6 +9,7 @@ pub mod disputes; pub mod errors; pub mod files; pub mod gsm; +pub mod locker_migration; pub mod mandate; pub mod metrics; pub mod payment_link; diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 5ccd9e964866..39b4749535b7 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1511,10 +1511,10 @@ pub(crate) fn validate_auth_and_metadata_type( authorizedotnet::transformers::AuthorizedotnetAuthType::try_from(val)?; Ok(()) } - // api_enums::Connector::Bankofamerica => { - // bankofamerica::transformers::BankofamericaAuthType::try_from(val)?; - // Ok(()) - // } Added as template code for future usage + api_enums::Connector::Bankofamerica => { + bankofamerica::transformers::BankOfAmericaAuthType::try_from(val)?; + Ok(()) + } api_enums::Connector::Bitpay => { bitpay::transformers::BitpayAuthType::try_from(val)?; Ok(()) @@ -1639,6 +1639,10 @@ pub(crate) fn validate_auth_and_metadata_type( powertranz::transformers::PowertranzAuthType::try_from(val)?; Ok(()) } + api_enums::Connector::Prophetpay => { + prophetpay::transformers::ProphetpayAuthType::try_from(val)?; + Ok(()) + } api_enums::Connector::Rapyd => { rapyd::transformers::RapydAuthType::try_from(val)?; Ok(()) diff --git a/crates/router/src/core/gsm.rs b/crates/router/src/core/gsm.rs index d25860674570..ed72275a73ab 100644 --- a/crates/router/src/core/gsm.rs +++ b/crates/router/src/core/gsm.rs @@ -10,7 +10,7 @@ use crate::{ }, db::gsm::GsmInterface, services, - types::{self, transformers::ForeignInto}, + types::transformers::ForeignInto, AppState, }; @@ -18,21 +18,21 @@ use crate::{ pub async fn create_gsm_rule( state: AppState, gsm_rule: gsm_api_types::GsmCreateRequest, -) -> RouterResponse { +) -> RouterResponse { let db = state.store.as_ref(); GsmInterface::add_gsm_rule(db, gsm_rule.foreign_into()) .await .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { message: "GSM with given key already exists in our records".to_string(), }) - .map(services::ApplicationResponse::Json) + .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into())) } #[instrument(skip_all)] pub async fn retrieve_gsm_rule( state: AppState, gsm_request: gsm_api_types::GsmRetrieveRequest, -) -> RouterResponse { +) -> RouterResponse { let db = state.store.as_ref(); let gsm_api_types::GsmRetrieveRequest { connector, @@ -46,14 +46,14 @@ pub async fn retrieve_gsm_rule( .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError { message: "GSM with given key does not exist in our records".to_string(), }) - .map(services::ApplicationResponse::Json) + .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into())) } #[instrument(skip_all)] pub async fn update_gsm_rule( state: AppState, gsm_request: gsm_api_types::GsmUpdateRequest, -) -> RouterResponse { +) -> RouterResponse { let db = state.store.as_ref(); let gsm_api_types::GsmUpdateRequest { connector, @@ -85,7 +85,7 @@ pub async fn update_gsm_rule( message: "GSM with given key does not exist in our records".to_string(), }) .attach_printable("Failed while updating Gsm rule") - .map(services::ApplicationResponse::Json) + .map(|gsm| services::ApplicationResponse::Json(gsm.foreign_into())) } #[instrument(skip_all)] diff --git a/crates/router/src/core/locker_migration.rs b/crates/router/src/core/locker_migration.rs new file mode 100644 index 000000000000..aa82b4a3a636 --- /dev/null +++ b/crates/router/src/core/locker_migration.rs @@ -0,0 +1,131 @@ +use api_models::{enums as api_enums, locker_migration::MigrateCardResponse}; +use common_utils::errors::CustomResult; +use diesel_models::PaymentMethod; +use error_stack::{FutureExt, ResultExt}; +use futures::TryFutureExt; + +use super::{errors::StorageErrorExt, payment_methods::cards}; +use crate::{ + errors, + routes::AppState, + services::{self, logger}, + types::{api, domain}, +}; + +pub async fn rust_locker_migration( + state: AppState, + merchant_id: &str, +) -> CustomResult, errors::ApiErrorResponse> { + let db = state.store.as_ref(); + + let key_store = state + .store + .get_merchant_key_store_by_merchant_id( + merchant_id, + &state.store.get_master_key().to_vec().into(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let merchant_account = db + .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let domain_customers = db + .list_customers_by_merchant_id(merchant_id, &key_store) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let mut customers_moved = 0; + let mut cards_moved = 0; + + for customer in domain_customers { + let result = db + .find_payment_method_by_customer_id_merchant_id_list(&customer.customer_id, merchant_id) + .change_context(errors::ApiErrorResponse::InternalServerError) + .and_then(|pm| { + call_to_locker( + &state, + pm, + &customer.customer_id, + merchant_id, + &merchant_account, + ) + }) + .await?; + + customers_moved += 1; + cards_moved += result; + } + + Ok(services::api::ApplicationResponse::Json( + MigrateCardResponse { + status_code: "200".to_string(), + status_message: "Card migration completed".to_string(), + customers_moved, + cards_moved, + }, + )) +} + +pub async fn call_to_locker( + state: &AppState, + payment_methods: Vec, + customer_id: &String, + merchant_id: &str, + merchant_account: &domain::MerchantAccount, +) -> CustomResult { + let mut cards_moved = 0; + + for pm in payment_methods { + let card = + cards::get_card_from_locker(state, customer_id, merchant_id, &pm.payment_method_id) + .await?; + + let card_details = api::CardDetail { + card_number: card.card_number, + card_exp_month: card.card_exp_month, + card_exp_year: card.card_exp_year, + card_holder_name: card.name_on_card, + nick_name: card.nick_name.map(masking::Secret::new), + }; + + let pm_create = api::PaymentMethodCreate { + payment_method: pm.payment_method, + payment_method_type: pm.payment_method_type, + payment_method_issuer: pm.payment_method_issuer, + payment_method_issuer_code: pm.payment_method_issuer_code, + card: Some(card_details.clone()), + metadata: pm.metadata, + customer_id: Some(pm.customer_id), + card_network: card.card_brand, + }; + + let (_add_card_rs_resp, _is_duplicate) = cards::add_card_hs( + state, + pm_create, + card_details, + customer_id.to_string(), + merchant_account, + api_enums::LockerChoice::Tartarus, + Some(&pm.payment_method_id), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable(format!( + "Card migration failed for merchant_id: {merchant_id}, customer_id: {customer_id}, payment_method_id: {} ", + pm.payment_method_id + ))?; + + cards_moved += 1; + + logger::info!( + "Card migrated for merchant_id: {merchant_id}, customer_id: {customer_id}, payment_method_id: {} ", + pm.payment_method_id + ); + } + + Ok(cards_moved) +} diff --git a/crates/router/src/core/payment_link/payment_link.html b/crates/router/src/core/payment_link/payment_link.html index e02bc16e7197..abacf0998f67 100644 --- a/crates/router/src/core/payment_link/payment_link.html +++ b/crates/router/src/core/payment_link/payment_link.html @@ -871,8 +871,8 @@ var widgets = null; var unifiedCheckout = null; - const pub_key = window.__PAYMENT_DETAILS.pub_key; - const hyper = Hyper(pub_key); + var pub_key = window.__PAYMENT_DETAILS.pub_key; + var hyper = Hyper(pub_key); function mountUnifiedCheckout(id) { if (unifiedCheckout !== null) { @@ -881,9 +881,9 @@ } async function initialize() { - const paymentDetails = window.__PAYMENT_DETAILS; + var paymentDetails = window.__PAYMENT_DETAILS; var client_secret = paymentDetails.client_secret; - const appearance = { + var appearance = { variables: { colorPrimary: paymentDetails.sdk_theme || "rgb(0, 109, 249)", fontFamily: "Work Sans, sans-serif", @@ -902,7 +902,7 @@ clientSecret: client_secret, }); - const unifiedCheckoutOptions = { + var unifiedCheckoutOptions = { layout: "tabs", sdkHandleConfirmPayment: true, branding: "never", @@ -930,16 +930,16 @@ initialize(); async function handleSubmit(e) { - const paymentDetails = window.__PAYMENT_DETAILS; - const { error, data, status } = await hyper.confirmPayment({ + var paymentDetails = window.__PAYMENT_DETAILS; + var { error, data, status } = await hyper.confirmPayment({ widgets, confirmParams: { // Make sure to change this to your payment completion page return_url: paymentDetails.return_url, }, }); - // This point will only be reached if there is an immediate error occurring while confirming the payment. Otherwise, your customer will be redirected to your `return_url`. - // For some payment flows such as Sofort, iDEAL, your customer will be redirected to an intermediate page to complete authorization of the payment, and then redirected to the `return_url`. + // This point will only be reached if there is an immediate error occurring while confirming the payment. Otherwise, your customer will be redirected to your 'return_url'. + // For some payment flows such as Sofort, iDEAL, your customer will be redirected to an intermediate page to complete authorization of the payment, and then redirected to the 'return_url'. if (error) { if (error.type === "validation_error") { @@ -951,7 +951,7 @@ // Re-initialize SDK mountUnifiedCheckout("#unified-checkout"); } else { - const { paymentIntent } = await hyper.retrievePaymentIntent( + var { paymentIntent } = await hyper.retrievePaymentIntent( paymentDetails.client_secret ); if (paymentIntent && paymentIntent.status) { @@ -966,8 +966,8 @@ // Fetches the payment status after payment submission async function checkStatus() { - const paymentDetails = window.__PAYMENT_DETAILS; - const res = { + var paymentDetails = window.__PAYMENT_DETAILS; + var res = { showSdk: true, }; @@ -975,25 +975,50 @@ "payment_intent_client_secret" ); + // If clientSecret is not found in URL params, try to fetch from window context if (!clientSecret) { clientSecret = paymentDetails.client_secret; } + // If clientSecret is not present, show status if (!clientSecret) { + res.showSdk = false; + showStatus( + Object.assign({}, paymentDetails, { + status: "", + error: { + code: "NO_CLIENT_SECRET", + message: "client_secret not found", + }, + }) + ); return res; } - const { paymentIntent } = await hyper.retrievePaymentIntent( - clientSecret - ); - - if ( - !paymentIntent || - paymentIntent.status === "requires_confirmation" - ) { + var { paymentIntent } = await hyper.retrievePaymentIntent(clientSecret); + + // If paymentIntent was not found, show status + if (!paymentIntent) { + res.showSdk = false; + showStatus( + Object.assign({}, paymentDetails, { + status: "", + error: { + code: "NOT_FOUND", + message: "PaymentIntent was not found", + }, + }) + ); return res; } + // Show SDK only if paymentIntent status has not been initiated + switch (paymentIntent.status) { + case "requires_confirmation": + case "requires_payment_method": + return res; + } + showStatus(paymentIntent); res.showSdk = false; @@ -1021,7 +1046,7 @@ } function showStatus(paymentDetails) { - const status = paymentDetails.status; + var status = paymentDetails.status; let statusDetails = { imageSource: "", message: null, @@ -1038,7 +1063,7 @@ // Status specific information switch (status) { case "succeeded": - statusDetails.imageSource = "https://i.imgur.com/5BOmYVl.img"; + statusDetails.imageSource = "https://i.imgur.com/5BOmYVl.png"; statusDetails.message = "We have successfully received your payment"; statusDetails.status = "Paid successfully"; @@ -1079,17 +1104,28 @@ statusDetails.status = "Payment under review"; break; + case "requires_capture": + statusDetails.imageSource = "https://i.imgur.com/Yb79Qt4.png"; + statusDetails.status = "Payment Pending"; + break; + + case "partially_captured": + statusDetails.imageSource = "https://i.imgur.com/Yb79Qt4.png"; + statusDetails.message = "Partial payment was captured."; + statusDetails.status = "Partial Payment Pending"; + break; + default: statusDetails.imageSource = "https://i.imgur.com/UD8CEuY.png"; statusDetails.status = "Something went wrong"; // Error details if (typeof paymentDetails.error === "object") { var errorCodeNode = createItem( - "ERROR CODE", + "Error Code", paymentDetails.error.code ); var errorMessageNode = createItem( - "ERROR MESSAGE", + "Error Message", paymentDetails.error.message ); // @ts-ignore @@ -1105,7 +1141,7 @@ paymentDetails.currency + " " + paymentDetails.amount; var merchantLogoNode = document.createElement("img"); merchantLogoNode.className = "hyper-checkout-status-merchant-logo"; - merchantLogoNode.src = ""; + merchantLogoNode.src = window.__PAYMENT_DETAILS.merchant_logo; merchantLogoNode.alt = ""; // Form content items @@ -1123,13 +1159,13 @@ // Append items statusDetails.items.map((item) => statusDetailsNode?.append(item)); - const statusHeaderNode = document.getElementById( + var statusHeaderNode = document.getElementById( "hyper-checkout-status-header" ); if (statusHeaderNode !== null) { statusHeaderNode.append(amountNode, merchantLogoNode); } - const statusContentNode = document.getElementById( + var statusContentNode = document.getElementById( "hyper-checkout-status-content" ); if (statusContentNode !== null) { @@ -1171,7 +1207,7 @@ } function renderPaymentDetails() { - const paymentDetails = window.__PAYMENT_DETAILS; + var paymentDetails = window.__PAYMENT_DETAILS; // Create price node var priceNode = document.createElement("div"); @@ -1221,14 +1257,14 @@ } function renderCart() { - const paymentDetails = window.__PAYMENT_DETAILS; - const orderDetails = paymentDetails.order_details; + var paymentDetails = window.__PAYMENT_DETAILS; + var orderDetails = paymentDetails.order_details; var cartNode = document.getElementById("hyper-checkout-cart"); var cartItemsNode = document.getElementById( "hyper-checkout-cart-items" ); - const MAX_ITEMS_VISIBLE_AFTER_COLLAPSE = + var MAX_ITEMS_VISIBLE_AFTER_COLLAPSE = paymentDetails.max_items_visible_after_collapse; // Cart items @@ -1247,7 +1283,7 @@ } // Expand / collapse button - const totalItems = orderDetails.length; + var totalItems = orderDetails.length; if (totalItems > MAX_ITEMS_VISIBLE_AFTER_COLLAPSE) { var expandButtonNode = document.createElement("div"); expandButtonNode.className = "hyper-checkout-cart-button"; @@ -1255,9 +1291,9 @@ var buttonImageNode = document.createElement("img"); var buttonTextNode = document.createElement("span"); buttonTextNode.id = "hyper-checkout-cart-button-text"; - const hiddenItemsCount = + var hiddenItemsCount = orderDetails.length - MAX_ITEMS_VISIBLE_AFTER_COLLAPSE; - buttonTextNode.innerText = `Show More (${hiddenItemsCount})`; + buttonTextNode.innerText = "Show More (" + hiddenItemsCount + ")"; expandButtonNode.append(buttonTextNode, buttonImageNode); cartNode.append(expandButtonNode); } @@ -1307,9 +1343,9 @@ } function handleCartView() { - const paymentDetails = window.__PAYMENT_DETAILS; - const orderDetails = paymentDetails.order_details; - const MAX_ITEMS_VISIBLE_AFTER_COLLAPSE = + var paymentDetails = window.__PAYMENT_DETAILS; + var orderDetails = paymentDetails.order_details; + var MAX_ITEMS_VISIBLE_AFTER_COLLAPSE = paymentDetails.max_items_visible_after_collapse; var itemsHTMLCollection = document.getElementsByClassName( "hyper-checkout-cart-item" @@ -1347,7 +1383,7 @@ cartItemsNode.style.maxHeight = "354px"; cartItemsNode.style.height = "354px"; cartItemsNode.scrollTo({ top: 0, behavior: "smooth" }); - setTimeout(() => { + setTimeout(function () { cartItems.map((item, index) => { if (index < MAX_ITEMS_VISIBLE_AFTER_COLLAPSE) { return; @@ -1361,34 +1397,35 @@ cartItemsNode.removeChild(item); }); }, 300); - setTimeout(() => { - const hiddenItemsCount = + setTimeout(function () { + var hiddenItemsCount = orderDetails.length - MAX_ITEMS_VISIBLE_AFTER_COLLAPSE; - cartButtonTextNode.innerText = `Show More (${hiddenItemsCount})`; + cartButtonTextNode.innerText = + "Show More (" + hiddenItemsCount + ")"; }, 250); } } function hideCartInMobileView() { window.history.back(); - const cartNode = document.getElementById("hyper-checkout-cart"); + var cartNode = document.getElementById("hyper-checkout-cart"); cartNode.style.animation = "slide-to-right 0.3s linear"; cartNode.style.right = "-582px"; - setTimeout(() => { + setTimeout(function () { hide("#hyper-checkout-cart"); }, 300); } function viewCartInMobileView() { window.history.pushState("view-cart", ""); - const cartNode = document.getElementById("hyper-checkout-cart"); + var cartNode = document.getElementById("hyper-checkout-cart"); cartNode.style.animation = "slide-from-right 0.3s linear"; cartNode.style.right = "0px"; show("#hyper-checkout-cart"); } function renderSDKHeader() { - const paymentDetails = window.__PAYMENT_DETAILS; + var paymentDetails = window.__PAYMENT_DETAILS; // SDK headers' items var sdkHeaderItemNode = document.createElement("div"); @@ -1443,8 +1480,8 @@ } window.addEventListener("resize", (event) => { - const currentHeight = window.innerHeight; - const currentWidth = window.innerWidth; + var currentHeight = window.innerHeight; + var currentWidth = window.innerWidth; if (currentWidth <= 1200 && window.state.prevWidth > 1200) { hide("#hyper-checkout-cart"); } else if (currentWidth > 1200 && window.state.prevWidth <= 1200) { diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 6b3cf11f5891..f9c666cbb954 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -214,12 +214,20 @@ pub async fn add_card_to_locker( metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]); request::record_operation_time( async { - add_card_hs(state, req, card, customer_id, merchant_account) - .await - .map_err(|error| { - metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); - error - }) + add_card_hs( + state, + req, + card, + customer_id, + merchant_account, + api_enums::LockerChoice::Basilisk, + None, + ) + .await + .map_err(|error| { + metrics::CARD_LOCKER_FAILURES.add(&metrics::CONTEXT, 1, &[]); + error + }) }, &metrics::CARD_ADD_TIME, &[], @@ -282,10 +290,13 @@ pub async fn add_card_hs( card: api::CardDetail, customer_id: String, merchant_account: &domain::MerchantAccount, + locker_choice: api_enums::LockerChoice, + card_reference: Option<&str>, ) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> { let payload = payment_methods::StoreLockerReq::LockerCard(payment_methods::StoreCardReq { merchant_id: &merchant_account.merchant_id, merchant_customer_id: customer_id.to_owned(), + card_reference: card_reference.map(str::to_string), card: payment_methods::Card { card_number: card.card_number.to_owned(), name_on_card: card.card_holder_name.to_owned(), @@ -296,7 +307,8 @@ pub async fn add_card_hs( nick_name: card.nick_name.as_ref().map(masking::Secret::peek).cloned(), }, }); - let store_card_payload = call_to_locker_hs(state, &payload, &customer_id).await?; + let store_card_payload = + call_to_locker_hs(state, &payload, &customer_id, locker_choice).await?; let payment_method_resp = payment_methods::mk_add_card_response_hs( card, @@ -394,6 +406,7 @@ pub async fn call_to_locker_hs<'a>( state: &routes::AppState, payload: &payment_methods::StoreLockerReq<'a>, customer_id: &str, + locker_choice: api_enums::LockerChoice, ) -> errors::CustomResult { let locker = &state.conf.locker; #[cfg(not(feature = "kms"))] @@ -402,7 +415,9 @@ pub async fn call_to_locker_hs<'a>( let jwekey = &state.kms_secrets; let db = &*state.store; let stored_card_response = if !locker.mock_locker { - let request = payment_methods::mk_add_locker_request_hs(jwekey, locker, payload).await?; + let request = + payment_methods::mk_add_locker_request_hs(jwekey, locker, payload, locker_choice) + .await?; let response = services::call_connector_api(state, request) .await .change_context(errors::VaultError::SaveCardFailed); @@ -1944,7 +1959,14 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( ) -> errors::RouterResponse { let db = state.store.as_ref(); if let Some(customer_id) = customer_id { - list_customer_payment_method(&state, merchant_account, key_store, None, customer_id).await + Box::pin(list_customer_payment_method( + &state, + merchant_account, + key_store, + None, + customer_id, + )) + .await } else { let cloned_secret = req.and_then(|r| r.client_secret.as_ref().cloned()); let payment_intent = helpers::verify_payment_intent_time_and_client_secret( @@ -1957,13 +1979,13 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( .as_ref() .and_then(|intent| intent.customer_id.to_owned()) .ok_or(errors::ApiErrorResponse::CustomerNotFound)?; - list_customer_payment_method( + Box::pin(list_customer_payment_method( &state, merchant_account, key_store, payment_intent, &customer_id, - ) + )) .await } } diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 086133ec78a5..63a0479375e8 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use api_models::enums as api_enums; use common_utils::{ext_traits::StringExt, pii::Email}; use error_stack::ResultExt; use josekit::jwe; @@ -26,6 +27,8 @@ pub enum StoreLockerReq<'a> { pub struct StoreCardReq<'a> { pub merchant_id: &'a str, pub merchant_customer_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub card_reference: Option, pub card: Card, } @@ -224,6 +227,7 @@ pub async fn mk_basilisk_req( #[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets, #[cfg(not(feature = "kms"))] jwekey: &settings::Jwekey, jws: &str, + locker_choice: api_enums::LockerChoice, ) -> CustomResult { let jws_payload: Vec<&str> = jws.split('.').collect(); @@ -241,10 +245,18 @@ pub async fn mk_basilisk_req( .change_context(errors::VaultError::SaveCardFailed)?; #[cfg(feature = "kms")] - let public_key = jwekey.jwekey.peek().vault_encryption_key.as_bytes(); + let public_key = match locker_choice { + api_enums::LockerChoice::Basilisk => jwekey.jwekey.peek().vault_encryption_key.as_bytes(), + api_enums::LockerChoice::Tartarus => { + jwekey.jwekey.peek().rust_locker_encryption_key.as_bytes() + } + }; #[cfg(not(feature = "kms"))] - let public_key = jwekey.vault_encryption_key.as_bytes(); + let public_key = match locker_choice { + api_enums::LockerChoice::Basilisk => jwekey.vault_encryption_key.as_bytes(), + api_enums::LockerChoice::Tartarus => jwekey.rust_locker_encryption_key.as_bytes(), + }; let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key) .await @@ -272,6 +284,7 @@ pub async fn mk_add_locker_request_hs<'a>( #[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets, locker: &settings::Locker, payload: &StoreLockerReq<'a>, + locker_choice: api_enums::LockerChoice, ) -> CustomResult { let payload = utils::Encode::>::encode_to_vec(&payload) .change_context(errors::VaultError::RequestEncodingFailed)?; @@ -286,11 +299,14 @@ pub async fn mk_add_locker_request_hs<'a>( .await .change_context(errors::VaultError::RequestEncodingFailed)?; - let jwe_payload = mk_basilisk_req(jwekey, &jws).await?; + let jwe_payload = mk_basilisk_req(jwekey, &jws, locker_choice).await?; let body = utils::Encode::::encode_to_value(&jwe_payload) .change_context(errors::VaultError::RequestEncodingFailed)?; - let mut url = locker.host.to_owned(); + let mut url = match locker_choice { + api_enums::LockerChoice::Basilisk => locker.host.to_owned(), + api_enums::LockerChoice::Tartarus => locker.host_rs.to_owned(), + }; url.push_str("/cards/add"); let mut request = services::Request::new(services::Method::Post, &url); request.add_header(headers::CONTENT_TYPE, "application/json".into()); @@ -432,7 +448,7 @@ pub async fn mk_get_card_request_hs( .await .change_context(errors::VaultError::RequestEncodingFailed)?; - let jwe_payload = mk_basilisk_req(jwekey, &jws).await?; + let jwe_payload = mk_basilisk_req(jwekey, &jws, api_enums::LockerChoice::Basilisk).await?; let body = utils::Encode::::encode_to_value(&jwe_payload) .change_context(errors::VaultError::RequestEncodingFailed)?; @@ -512,7 +528,7 @@ pub async fn mk_delete_card_request_hs( .await .change_context(errors::VaultError::RequestEncodingFailed)?; - let jwe_payload = mk_basilisk_req(jwekey, &jws).await?; + let jwe_payload = mk_basilisk_req(jwekey, &jws, api_enums::LockerChoice::Basilisk).await?; let body = utils::Encode::::encode_to_value(&jwe_payload) .change_context(errors::VaultError::RequestEncodingFailed)?; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index e7408cecf163..7e19b0b60571 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -193,7 +193,7 @@ where ) .await?; let operation = Box::new(PaymentResponse); - let db = &*state.store; + connector_http_status_code = router_data.connector_http_status_code; external_latency = router_data.external_latency; //add connector http status code metrics @@ -201,7 +201,7 @@ where operation .to_post_update_tracker()? .update_tracker( - db, + state, &validate_result.payment_id, payment_data, router_data, @@ -272,7 +272,6 @@ where } let operation = Box::new(PaymentResponse); - let db = &*state.store; connector_http_status_code = router_data.connector_http_status_code; external_latency = router_data.external_latency; //add connector http status code metrics @@ -280,7 +279,7 @@ where operation .to_post_update_tracker()? .update_tracker( - db, + state, &validate_result.payment_id, payment_data, router_data, @@ -323,7 +322,7 @@ where (_, payment_data) = operation .to_update_tracker()? .update_trackers( - &*state.store, + state, payment_data.clone(), customer.clone(), validate_result.storage_scheme, @@ -582,7 +581,14 @@ impl PaymentRedirectFlow for PaymentRedirectCom }), ..Default::default() }; - payments_core::( + Box::pin(payments_core::< + api::CompleteAuthorize, + api::PaymentsResponse, + _, + _, + _, + Ctx, + >( state.clone(), merchant_account, merchant_key_store, @@ -592,7 +598,7 @@ impl PaymentRedirectFlow for PaymentRedirectCom connector_action, None, HeaderPayload::default(), - ) + )) .await } @@ -678,7 +684,14 @@ impl PaymentRedirectFlow for PaymentRedirectSyn expand_attempts: None, expand_captures: None, }; - payments_core::( + Box::pin(payments_core::< + api::PSync, + api::PaymentsResponse, + _, + _, + _, + Ctx, + >( state.clone(), merchant_account, merchant_key_store, @@ -688,7 +701,7 @@ impl PaymentRedirectFlow for PaymentRedirectSyn connector_action, None, HeaderPayload::default(), - ) + )) .await } fn generate_response( @@ -889,7 +902,7 @@ where (_, *payment_data) = operation .to_update_tracker()? .update_trackers( - &*state.store, + state, payment_data.clone(), customer.clone(), merchant_account.storage_scheme, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 0b253cdc6079..46eaca26f7cc 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -168,7 +168,6 @@ default_imp_for_complete_authorize!( connector::Opennode, connector::Payeezy, connector::Payu, - connector::Prophetpay, connector::Rapyd, connector::Square, connector::Stax, diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index 24f7e05e7b9d..14b710de914a 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -25,7 +25,10 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, ) -> RouterResult { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::Approve, + types::PaymentsApproveData, + >( state, self.clone(), connector_id, @@ -33,7 +36,7 @@ impl key_store, customer, merchant_connector_account, - ) + )) .await } } diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index e27fe54c0ed0..04bd7f0b4338 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -39,7 +39,10 @@ impl types::PaymentsResponseData, >, > { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::Authorize, + types::PaymentsAuthorizeData, + >( state, self.clone(), connector_id, @@ -47,7 +50,7 @@ impl key_store, customer, merchant_connector_account, - ) + )) .await } } @@ -96,7 +99,7 @@ impl Feature for types::PaymentsAu metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics if resp.request.setup_mandate_details.clone().is_some() { - let payment_method_id = tokenization::save_payment_method( + let payment_method_id = Box::pin(tokenization::save_payment_method( state, connector, resp.to_owned(), @@ -104,7 +107,7 @@ impl Feature for types::PaymentsAu merchant_account, self.request.payment_method_type, key_store, - ) + )) .await?; Ok(mandate::mandate_procedure( state, @@ -127,7 +130,7 @@ impl Feature for types::PaymentsAu tokio::spawn(async move { logger::info!("Starting async call to save_payment_method in locker"); - let result = tokenization::save_payment_method( + let result = Box::pin(tokenization::save_payment_method( &state, &connector, response, @@ -135,7 +138,7 @@ impl Feature for types::PaymentsAu &merchant_account, self.request.payment_method_type, &key_store, - ) + )) .await; if let Err(err) = result { diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index 3a3ac1b5b0bb..5918380ee0b2 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -24,7 +24,10 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, ) -> RouterResult { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::Void, + types::PaymentsCancelData, + >( state, self.clone(), connector_id, @@ -32,7 +35,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, ) -> RouterResult { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::Capture, + types::PaymentsCaptureData, + >( state, self.clone(), connector_id, @@ -33,7 +36,7 @@ impl key_store, customer, merchant_connector_account, - ) + )) .await } } diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index 6fbbb01e1a64..44d8728fd4d2 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -35,7 +35,7 @@ impl types::PaymentsResponseData, >, > { - transformers::construct_payment_router_data::< + Box::pin(transformers::construct_payment_router_data::< api::CompleteAuthorize, types::CompleteAuthorizeData, >( @@ -46,7 +46,7 @@ impl key_store, customer, merchant_connector_account, - ) + )) .await } } diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 36d418a3ae8c..cb7a764985d1 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -28,7 +28,10 @@ impl ConstructFlowSpecificData RouterResult< types::RouterData, > { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::PSync, + types::PaymentsSyncData, + >( state, self.clone(), connector_id, @@ -36,7 +39,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, ) -> RouterResult { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::Reject, + types::PaymentsRejectData, + >( state, self.clone(), connector_id, @@ -32,7 +35,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, ) -> RouterResult { - transformers::construct_payment_router_data::( + Box::pin(transformers::construct_payment_router_data::< + api::Session, + types::PaymentsSessionData, + >( state, self.clone(), connector_id, @@ -40,7 +43,7 @@ impl key_store, customer, merchant_connector_account, - ) + )) .await } } diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index dae9ed0bf833..0c03c8ce123b 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -31,7 +31,7 @@ impl customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, ) -> RouterResult { - transformers::construct_payment_router_data::< + Box::pin(transformers::construct_payment_router_data::< api::SetupMandate, types::SetupMandateRequestData, >( @@ -42,7 +42,7 @@ impl key_store, customer, merchant_connector_account, - ) + )) .await } } @@ -75,7 +75,7 @@ impl Feature for types::Setup .await .to_setup_mandate_failed_response()?; - let pm_id = tokenization::save_payment_method( + let pm_id = Box::pin(tokenization::save_payment_method( state, connector, resp.to_owned(), @@ -83,7 +83,7 @@ impl Feature for types::Setup merchant_account, self.request.payment_method_type, key_store, - ) + )) .await?; mandate::mandate_procedure( @@ -208,7 +208,7 @@ impl types::SetupMandateRouterData { .to_setup_mandate_failed_response()?; let payment_method_type = self.request.payment_method_type; - let pm_id = tokenization::save_payment_method( + let pm_id = Box::pin(tokenization::save_payment_method( state, connector, resp.to_owned(), @@ -216,7 +216,7 @@ impl types::SetupMandateRouterData { merchant_account, payment_method_type, key_store, - ) + )) .await?; Ok(mandate::mandate_procedure( diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 4ee2fd4b94d3..b9e96ec36e11 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1817,6 +1817,7 @@ pub fn validate_payment_method_type_against_payment_method( api_enums::PaymentMethodType::Knet | api_enums::PaymentMethodType::Benefit | api_enums::PaymentMethodType::MomoAtm + | api_enums::PaymentMethodType::CardRedirect ), } } diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index ad747ac2792a..f65e65459e00 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -154,7 +154,7 @@ pub trait Domain: Send + Sync { pub trait UpdateTracker: Send { async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_data: D, customer: Option, storage_scheme: enums::MerchantStorageScheme, @@ -171,7 +171,7 @@ pub trait UpdateTracker: Send { pub trait PostUpdateTracker: Send { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, payment_data: D, response: types::RouterData, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index d5d0d2d01765..538e65e4b22e 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -336,7 +336,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, mut payment_data: PaymentData, _customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, @@ -356,6 +356,7 @@ impl updated_by: storage_scheme.to_string(), }; payment_data.payment_intent = db + .store .update_payment_intent( payment_data.payment_intent, intent_status_update, diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index f734afef7826..535edf736ca6 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -14,7 +14,6 @@ use crate::{ payment_methods::PaymentMethodRetrieve, payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, }, - db::StorageInterface, routes::AppState, services, types::{ @@ -178,7 +177,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, mut payment_data: PaymentData, _customer: Option, storage_scheme: enums::MerchantStorageScheme, @@ -207,6 +206,7 @@ impl if let Some(payment_intent_update) = intent_status_update { payment_data.payment_intent = db + .store .update_payment_intent( payment_data.payment_intent, payment_intent_update, @@ -216,17 +216,18 @@ impl .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; } - db.update_payment_attempt_with_attempt_id( - payment_data.payment_attempt.clone(), - storage::PaymentAttemptUpdate::VoidUpdate { - status: attempt_status_update, - cancellation_reason, - updated_by: storage_scheme.to_string(), - }, - storage_scheme, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; + db.store + .update_payment_attempt_with_attempt_id( + payment_data.payment_attempt.clone(), + storage::PaymentAttemptUpdate::VoidUpdate { + status: attempt_status_update, + cancellation_reason, + updated_by: storage_scheme.to_string(), + }, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; Ok((Box::new(self), payment_data)) } } diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index 6e794b1ba618..ff51a2c49d77 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -13,7 +13,6 @@ use crate::{ payment_methods::PaymentMethodRetrieve, payments::{self, helpers, operations, types::MultipleCaptureData}, }, - db::StorageInterface, routes::AppState, services, types::{ @@ -222,7 +221,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, mut payment_data: payments::PaymentData, _customer: Option, storage_scheme: enums::MerchantStorageScheme, @@ -239,6 +238,7 @@ impl { payment_data.payment_attempt = match &payment_data.multiple_capture_data { Some(multiple_capture_data) => db + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt, storage::PaymentAttemptUpdate::MultipleCaptureCountUpdate { diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 038d34ea290f..c648d95a4950 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -326,7 +326,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - _db: &dyn StorageInterface, + _state: &'b AppState, payment_data: PaymentData, _customer: Option, _storage_scheme: storage_enums::MerchantStorageScheme, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 96cd4f5c622f..afb7f110ed5d 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -11,6 +11,7 @@ use futures::FutureExt; use redis_interface::errors::RedisError; use router_derive::PaymentOperation; use router_env::{instrument, tracing}; +use tracing_futures::Instrument; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; use crate::{ @@ -65,20 +66,46 @@ impl // Stage 1 - let payment_intent_fut = db - .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme) - .map(|x| x.change_context(errors::ApiErrorResponse::PaymentNotFound)); + let store = state.clone().store; + let m_merchant_id = merchant_id.clone(); + let payment_intent_fut = tokio::spawn( + async move { + store + .find_payment_intent_by_payment_id_merchant_id( + &payment_id, + m_merchant_id.as_str(), + storage_scheme, + ) + .map(|x| x.change_context(errors::ApiErrorResponse::PaymentNotFound)) + .await + } + .in_current_span(), + ); - let mandate_details_fut = helpers::get_token_pm_type_mandate_details( - state, - request, - mandate_type.clone(), - merchant_account, - key_store, + let m_state = state.clone(); + let m_mandate_type = mandate_type.clone(); + let m_merchant_account = merchant_account.clone(); + let m_request = request.clone(); + let m_key_store = key_store.clone(); + + let mandate_details_fut = tokio::spawn( + async move { + helpers::get_token_pm_type_mandate_details( + &m_state, + &m_request, + m_mandate_type, + &m_merchant_account, + &m_key_store, + ) + .await + } + .in_current_span(), ); - let (mut payment_intent, mandate_details) = - futures::try_join!(payment_intent_fut, mandate_details_fut)?; + let (mut payment_intent, mandate_details) = tokio::try_join!( + utils::flatten_join_error(payment_intent_fut), + utils::flatten_join_error(mandate_details_fut) + )?; helpers::validate_customer_access(&payment_intent, auth_flow, request)?; @@ -112,76 +139,122 @@ impl // Stage 2 let attempt_id = payment_intent.active_attempt.get_id(); - let payment_attempt_fut = db - .find_payment_attempt_by_payment_id_merchant_id_attempt_id( - payment_intent.payment_id.as_str(), - merchant_id, - attempt_id.as_str(), - storage_scheme, - ) - .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)); - - let shipping_address_fut = helpers::create_or_find_address_for_payment_by_request( - db, - request.shipping.as_ref(), - payment_intent.shipping_address_id.as_deref(), - merchant_id, - payment_intent - .customer_id - .as_ref() - .or(customer_details.customer_id.as_ref()), - key_store, - &payment_intent.payment_id, - merchant_account.storage_scheme, + let store = state.clone().store; + let m_payment_id = payment_intent.payment_id.clone(); + let m_merchant_id = merchant_id.clone(); + + let payment_attempt_fut = tokio::spawn( + async move { + store + .find_payment_attempt_by_payment_id_merchant_id_attempt_id( + m_payment_id.as_str(), + m_merchant_id.as_str(), + attempt_id.as_str(), + storage_scheme, + ) + .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)) + .await + } + .in_current_span(), ); - let billing_address_fut = helpers::create_or_find_address_for_payment_by_request( - db, - request.billing.as_ref(), - payment_intent.billing_address_id.as_deref(), - merchant_id, - payment_intent - .customer_id - .as_ref() - .or(customer_details.customer_id.as_ref()), - key_store, - &payment_intent.payment_id, - merchant_account.storage_scheme, + let m_merchant_id = merchant_id.clone(); + let m_request_shipping = request.shipping.clone(); + let m_payment_intent_shipping_address_id = payment_intent.shipping_address_id.clone(); + let m_payment_intent_payment_id = payment_intent.payment_id.clone(); + let m_customer_details_customer_id = customer_details.customer_id.clone(); + let m_payment_intent_customer_id = payment_intent.customer_id.clone(); + let store = state.clone().store; + let m_key_store = key_store.clone(); + + let shipping_address_fut = tokio::spawn( + async move { + helpers::create_or_find_address_for_payment_by_request( + store.as_ref(), + m_request_shipping.as_ref(), + m_payment_intent_shipping_address_id.as_deref(), + m_merchant_id.as_str(), + m_payment_intent_customer_id + .as_ref() + .or(m_customer_details_customer_id.as_ref()), + &m_key_store, + m_payment_intent_payment_id.as_ref(), + storage_scheme, + ) + .await + } + .in_current_span(), ); - let config_update_fut = request - .merchant_connector_details - .to_owned() - .async_map(|mcd| async { - helpers::insert_merchant_connector_creds_to_config( - db, - merchant_account.merchant_id.as_str(), - mcd, + let m_merchant_id = merchant_id.clone(); + let m_request_billing = request.billing.clone(); + let m_customer_details_customer_id = customer_details.customer_id.clone(); + let m_payment_intent_customer_id = payment_intent.customer_id.clone(); + let m_payment_intent_billing_address_id = payment_intent.billing_address_id.clone(); + let m_payment_intent_payment_id = payment_intent.payment_id.clone(); + let store = state.clone().store; + let m_key_store = key_store.clone(); + + let billing_address_fut = tokio::spawn( + async move { + helpers::create_or_find_address_for_payment_by_request( + store.as_ref(), + m_request_billing.as_ref(), + m_payment_intent_billing_address_id.as_deref(), + m_merchant_id.as_ref(), + m_payment_intent_customer_id + .as_ref() + .or(m_customer_details_customer_id.as_ref()), + &m_key_store, + m_payment_intent_payment_id.as_ref(), + storage_scheme, ) .await - }) - .map(|x| x.transpose()); + } + .in_current_span(), + ); + + let m_merchant_id = merchant_id.clone(); + let store = state.clone().store; + let m_request_merchant_connector_details = request.merchant_connector_details.clone(); + + let config_update_fut = tokio::spawn( + async move { + m_request_merchant_connector_details + .async_map(|mcd| async { + helpers::insert_merchant_connector_creds_to_config( + store.as_ref(), + m_merchant_id.as_str(), + mcd, + ) + .await + }) + .map(|x| x.transpose()) + .await + } + .in_current_span(), + ); let (mut payment_attempt, shipping_address, billing_address) = match payment_intent.status { api_models::enums::IntentStatus::RequiresCustomerAction | api_models::enums::IntentStatus::RequiresMerchantAction | api_models::enums::IntentStatus::RequiresPaymentMethod | api_models::enums::IntentStatus::RequiresConfirmation => { - let (payment_attempt, shipping_address, billing_address, _) = futures::try_join!( - payment_attempt_fut, - shipping_address_fut, - billing_address_fut, - config_update_fut + let (payment_attempt, shipping_address, billing_address, _) = tokio::try_join!( + utils::flatten_join_error(payment_attempt_fut), + utils::flatten_join_error(shipping_address_fut), + utils::flatten_join_error(billing_address_fut), + utils::flatten_join_error(config_update_fut) )?; (payment_attempt, shipping_address, billing_address) } _ => { - let (mut payment_attempt, shipping_address, billing_address, _) = futures::try_join!( - payment_attempt_fut, - shipping_address_fut, - billing_address_fut, - config_update_fut + let (mut payment_attempt, shipping_address, billing_address, _) = tokio::try_join!( + utils::flatten_join_error(payment_attempt_fut), + utils::flatten_join_error(shipping_address_fut), + utils::flatten_join_error(billing_address_fut), + utils::flatten_join_error(config_update_fut) )?; let attempt_type = helpers::get_attempt_type( @@ -193,11 +266,10 @@ impl (payment_intent, payment_attempt) = attempt_type .modify_payment_intent_and_payment_attempt( - // 3 request, payment_intent, payment_attempt, - db, + &*state.store, storage_scheme, ) .await?; @@ -421,7 +493,27 @@ impl Domain, ) -> CustomResult<(), errors::ApiErrorResponse> { - helpers::add_domain_task_to_pt(self, state, payment_attempt, requeue, schedule_time).await + // This spawns this futures in a background thread, the exception inside this future won't affect + // the current thread and the lifecycle of spawn thread is not handled by runtime. + // So when server shutdown won't wait for this thread's completion. + let m_payment_attempt = payment_attempt.clone(); + let m_state = state.clone(); + let m_self = *self; + tokio::spawn( + async move { + helpers::add_domain_task_to_pt( + &m_self, + m_state.as_ref(), + &m_payment_attempt, + requeue, + schedule_time, + ) + .await + } + .in_current_span(), + ); + + Ok(()) } async fn get_connector<'a>( @@ -445,7 +537,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + state: &'b AppState, mut payment_data: PaymentData, customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, @@ -501,7 +593,7 @@ impl .payment_method_data .as_ref() .async_map(|payment_method_data| async { - helpers::get_additional_payment_data(payment_method_data, db).await + helpers::get_additional_payment_data(payment_method_data, &*state.store).await }) .await .as_ref() @@ -537,76 +629,131 @@ impl .as_ref() .map(|surcharge_details| surcharge_details.final_amount) .unwrap_or(payment_data.payment_attempt.amount); - let payment_attempt_fut = db - .update_payment_attempt_with_attempt_id( - payment_data.payment_attempt, - storage::PaymentAttemptUpdate::ConfirmUpdate { - amount: payment_data.amount.into(), - currency: payment_data.currency, - status: attempt_status, - payment_method, - authentication_type, - browser_info, - connector, - payment_token, - payment_method_data: additional_pm_data, - payment_method_type, - payment_experience, - business_sub_label, - straight_through_algorithm, - error_code, - error_message, - amount_capturable: Some(authorized_amount), - updated_by: storage_scheme.to_string(), - merchant_connector_id, - }, - storage_scheme, - ) - .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)); - - let payment_intent_fut = db - .update_payment_intent( - payment_data.payment_intent, - storage::PaymentIntentUpdate::Update { - amount: payment_data.amount.into(), - currency: payment_data.currency, - setup_future_usage, - status: intent_status, - customer_id, - shipping_address_id: shipping_address, - billing_address_id: billing_address, - return_url, - business_country, - business_label, - description, - statement_descriptor_name, - statement_descriptor_suffix, - order_details, - metadata, - payment_confirm_source: header_payload.payment_confirm_source, - updated_by: storage_scheme.to_string(), - }, - storage_scheme, - ) - .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)); - let customer_fut = Box::pin(async { - if let Some((updated_customer, customer)) = updated_customer.zip(customer) { - db.update_customer_by_customer_id_merchant_id( - customer.customer_id.to_owned(), - customer.merchant_id.to_owned(), - updated_customer, - key_store, + let m_payment_data_payment_attempt = payment_data.payment_attempt.clone(); + let m_browser_info = browser_info.clone(); + let m_connector = connector.clone(); + let m_payment_token = payment_token.clone(); + let m_additional_pm_data = additional_pm_data.clone(); + let m_business_sub_label = business_sub_label.clone(); + let m_straight_through_algorithm = straight_through_algorithm.clone(); + let m_error_code = error_code.clone(); + let m_error_message = error_message.clone(); + let m_db = state.clone().store; + + let payment_attempt_fut = tokio::spawn( + async move { + m_db.update_payment_attempt_with_attempt_id( + m_payment_data_payment_attempt, + storage::PaymentAttemptUpdate::ConfirmUpdate { + amount: payment_data.amount.into(), + currency: payment_data.currency, + status: attempt_status, + payment_method, + authentication_type, + browser_info: m_browser_info, + connector: m_connector, + payment_token: m_payment_token, + payment_method_data: m_additional_pm_data, + payment_method_type, + payment_experience, + business_sub_label: m_business_sub_label, + straight_through_algorithm: m_straight_through_algorithm, + error_code: m_error_code, + error_message: m_error_message, + amount_capturable: Some(authorized_amount), + updated_by: storage_scheme.to_string(), + merchant_connector_id, + }, + storage_scheme, ) + .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)) .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to update CustomerConnector in customer")?; + } + .in_current_span(), + ); + + let m_payment_data_payment_intent = payment_data.payment_intent.clone(); + let m_customer_id = customer_id.clone(); + let m_shipping_address_id = shipping_address.clone(); + let m_billing_address_id = billing_address.clone(); + let m_return_url = return_url.clone(); + let m_business_label = business_label.clone(); + let m_description = description.clone(); + let m_statement_descriptor_name = statement_descriptor_name.clone(); + let m_statement_descriptor_suffix = statement_descriptor_suffix.clone(); + let m_order_details = order_details.clone(); + let m_metadata = metadata.clone(); + let m_db = state.clone().store; + let m_storage_scheme = storage_scheme.to_string(); + + let payment_intent_fut = tokio::spawn( + async move { + m_db.update_payment_intent( + m_payment_data_payment_intent, + storage::PaymentIntentUpdate::Update { + amount: payment_data.amount.into(), + currency: payment_data.currency, + setup_future_usage, + status: intent_status, + customer_id: m_customer_id, + shipping_address_id: m_shipping_address_id, + billing_address_id: m_billing_address_id, + return_url: m_return_url, + business_country, + business_label: m_business_label, + description: m_description, + statement_descriptor_name: m_statement_descriptor_name, + statement_descriptor_suffix: m_statement_descriptor_suffix, + order_details: m_order_details, + metadata: m_metadata, + payment_confirm_source: header_payload.payment_confirm_source, + updated_by: m_storage_scheme, + }, + storage_scheme, + ) + .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)) + .await + } + .in_current_span(), + ); + + let customer_fut = + if let Some((updated_customer, customer)) = updated_customer.zip(customer) { + let m_customer_customer_id = customer.customer_id.to_owned(); + let m_customer_merchant_id = customer.merchant_id.to_owned(); + let m_key_store = key_store.clone(); + let m_updated_customer = updated_customer.clone(); + let m_db = state.clone().store; + tokio::spawn( + async move { + m_db.update_customer_by_customer_id_merchant_id( + m_customer_customer_id, + m_customer_merchant_id, + m_updated_customer, + &m_key_store, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to update CustomerConnector in customer")?; + + Ok::<_, error_stack::Report>(()) + } + .in_current_span(), + ) + } else { + tokio::spawn( + async move { Ok::<_, error_stack::Report>(()) } + .in_current_span(), + ) }; - Ok::<_, error_stack::Report>(()) - }); - let (payment_intent, payment_attempt, _) = - futures::try_join!(payment_intent_fut, payment_attempt_fut, customer_fut)?; + let (payment_intent, payment_attempt, _) = tokio::try_join!( + utils::flatten_join_error(payment_intent_fut), + utils::flatten_join_error(payment_attempt_fut), + utils::flatten_join_error(customer_fut) + )?; + payment_data.payment_intent = payment_intent; payment_data.payment_attempt = payment_attempt; diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index fad7212c61d3..974f5e6ab5b6 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -16,7 +16,7 @@ use crate::{ errors::{self, CustomResult, RouterResult, StorageErrorExt}, payment_methods::PaymentMethodRetrieve, payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, - utils::{self as core_utils}, + utils as core_utils, }, db::StorageInterface, routes::AppState, @@ -394,7 +394,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + state: &'b AppState, mut payment_data: PaymentData, _customer: Option, storage_scheme: enums::MerchantStorageScheme, @@ -443,7 +443,8 @@ impl .as_ref() .map(|surcharge_details| surcharge_details.tax_on_surcharge_amount); - payment_data.payment_attempt = db + payment_data.payment_attempt = state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt, storage::PaymentAttemptUpdate::UpdateTrackers { @@ -466,7 +467,8 @@ impl let customer_id = payment_data.payment_intent.customer_id.clone(); - payment_data.payment_intent = db + payment_data.payment_intent = state + .store .update_payment_intent( payment_data.payment_intent, storage::PaymentIntentUpdate::ReturnUrlUpdate { diff --git a/crates/router/src/core/payments/operations/payment_method_validate.rs b/crates/router/src/core/payments/operations/payment_method_validate.rs index 7e4fe0951b03..62f12cfbc90c 100644 --- a/crates/router/src/core/payments/operations/payment_method_validate.rs +++ b/crates/router/src/core/payments/operations/payment_method_validate.rs @@ -205,7 +205,7 @@ impl UpdateTracker, api: #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + state: &'b AppState, mut payment_data: PaymentData, _customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, @@ -225,7 +225,8 @@ impl UpdateTracker, api: let customer_id = payment_data.payment_intent.customer_id.clone(); - payment_data.payment_intent = db + payment_data.payment_intent = state + .store .update_payment_intent( payment_data.payment_intent, storage::PaymentIntentUpdate::ReturnUrlUpdate { diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index a6c2561aaeed..16d264c001ec 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -13,7 +13,6 @@ use crate::{ payment_methods::PaymentMethodRetrieve, payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, }, - db::StorageInterface, routes::AppState, services, types::{ @@ -164,7 +163,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + state: &'b AppState, mut payment_data: PaymentData, _customer: Option, storage_scheme: enums::MerchantStorageScheme, @@ -201,7 +200,8 @@ impl updated_by: storage_scheme.to_string(), }; - payment_data.payment_intent = db + payment_data.payment_intent = state + .store .update_payment_intent( payment_data.payment_intent, intent_status_update, @@ -210,7 +210,8 @@ impl .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - payment_data.payment_attempt = db + payment_data.payment_attempt = state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), attempt_status_update, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index d6346a512ef1..b55b0c46f6ad 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -1,10 +1,13 @@ use std::collections::HashMap; use async_trait::async_trait; +use data_models::payments::payment_attempt::PaymentAttempt; use error_stack::ResultExt; use futures::FutureExt; use router_derive; use router_env::{instrument, tracing}; +use storage_impl::DataModelExt; +use tracing_futures::Instrument; use super::{Operation, PostUpdateTracker}; use crate::{ @@ -15,8 +18,7 @@ use crate::{ payments::{types::MultipleCaptureData, PaymentData}, utils as core_utils, }, - db::StorageInterface, - routes::metrics, + routes::{metrics, AppState}, services::RedirectForm, types::{ self, api, @@ -43,7 +45,7 @@ impl PostUpdateTracker, types::PaymentsAuthorizeData { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -60,13 +62,13 @@ impl PostUpdateTracker, types::PaymentsAuthorizeData .mandate_id .or_else(|| router_data.request.mandate_id.clone()); - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -77,7 +79,7 @@ impl PostUpdateTracker, types::PaymentsAuthorizeData impl PostUpdateTracker, types::PaymentsSyncData> for PaymentResponse { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, payment_data: PaymentData, router_data: types::RouterData, @@ -86,8 +88,14 @@ impl PostUpdateTracker, types::PaymentsSyncData> for where F: 'b + Send, { - payment_response_update_tracker(db, payment_id, payment_data, router_data, storage_scheme) - .await + Box::pin(payment_response_update_tracker( + db, + payment_id, + payment_data, + router_data, + storage_scheme, + )) + .await } } @@ -97,7 +105,7 @@ impl PostUpdateTracker, types::PaymentsSessionData> { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -106,13 +114,13 @@ impl PostUpdateTracker, types::PaymentsSessionData> where F: 'b + Send, { - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -125,7 +133,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -134,13 +142,13 @@ impl PostUpdateTracker, types::PaymentsCaptureData> where F: 'b + Send, { - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -151,7 +159,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> impl PostUpdateTracker, types::PaymentsCancelData> for PaymentResponse { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -161,13 +169,13 @@ impl PostUpdateTracker, types::PaymentsCancelData> f where F: 'b + Send, { - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -180,7 +188,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -190,13 +198,13 @@ impl PostUpdateTracker, types::PaymentsApproveData> where F: 'b + Send, { - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -207,7 +215,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> impl PostUpdateTracker, types::PaymentsRejectData> for PaymentResponse { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -217,13 +225,13 @@ impl PostUpdateTracker, types::PaymentsRejectData> f where F: 'b + Send, { - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -236,7 +244,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -255,13 +263,13 @@ impl PostUpdateTracker, types::SetupMandateRequestDa // .map(api_models::payments::MandateIds::new) }); - payment_data = payment_response_update_tracker( + payment_data = Box::pin(payment_response_update_tracker( db, payment_id, payment_data, router_data, storage_scheme, - ) + )) .await?; Ok(payment_data) @@ -274,7 +282,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + db: &'b AppState, payment_id: &api::PaymentIdType, payment_data: PaymentData, response: types::RouterData, @@ -283,14 +291,20 @@ impl PostUpdateTracker, types::CompleteAuthorizeData where F: 'b + Send, { - payment_response_update_tracker(db, payment_id, payment_data, response, storage_scheme) - .await + Box::pin(payment_response_update_tracker( + db, + payment_id, + payment_data, + response, + storage_scheme, + )) + .await } } #[instrument(skip_all)] async fn payment_response_update_tracker( - db: &dyn StorageInterface, + state: &AppState, _payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -524,7 +538,8 @@ async fn payment_response_update_tracker( payment_data.multiple_capture_data = match capture_update { Some((mut multiple_capture_data, capture_updates)) => { for (capture, capture_update) in capture_updates { - let updated_capture = db + let updated_capture = state + .store .update_capture_with_capture_id(capture, capture_update, storage_scheme) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; @@ -548,17 +563,43 @@ async fn payment_response_update_tracker( let payment_attempt = payment_data.payment_attempt.clone(); - payment_data.payment_attempt = match payment_attempt_update { - Some(payment_attempt_update) => db - .update_payment_attempt_with_attempt_id( - payment_attempt, - payment_attempt_update, - storage_scheme, + let m_db = state.clone().store; + let m_payment_attempt_update = payment_attempt_update.clone(); + let m_payment_attempt = payment_attempt.clone(); + + let payment_attempt = payment_attempt_update + .map(|payment_attempt_update| { + PaymentAttempt::from_storage_model( + payment_attempt_update + .to_storage_model() + .apply_changeset(payment_attempt.clone().to_storage_model()), ) + }) + .unwrap_or_else(|| payment_attempt); + + let payment_attempt_fut = tokio::spawn( + async move { + Box::pin(async move { + Ok::<_, error_stack::Report>( + match m_payment_attempt_update { + Some(payment_attempt_update) => m_db + .update_payment_attempt_with_attempt_id( + m_payment_attempt, + payment_attempt_update, + storage_scheme, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?, + None => m_payment_attempt, + }, + ) + }) .await - .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?, - None => payment_attempt, - }; + } + .in_current_span(), + ); + + payment_data.payment_attempt = payment_attempt; let amount_captured = get_total_amount_captured( router_data.request, @@ -566,6 +607,7 @@ async fn payment_response_update_tracker( router_data.status, &payment_data, ); + let payment_intent_update = match &router_data.response { Err(_) => storage::PaymentIntentUpdate::PGStatusUpdate { status: payment_data @@ -583,25 +625,47 @@ async fn payment_response_update_tracker( }, }; - let payment_intent_fut = db - .update_payment_intent( - payment_data.payment_intent.clone(), - payment_intent_update, - storage_scheme, - ) - .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)); + let m_db = state.clone().store; + let m_payment_data_payment_intent = payment_data.payment_intent.clone(); + let m_payment_intent_update = payment_intent_update.clone(); + let payment_intent_fut = tokio::spawn( + async move { + m_db.update_payment_intent( + m_payment_data_payment_intent, + m_payment_intent_update, + storage_scheme, + ) + .map(|x| x.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)) + .await + } + .in_current_span(), + ); // When connector requires redirection for mandate creation it can update the connector mandate_id during Psync - let mandate_update_fut = mandate::update_connector_mandate_id( - db, - router_data.merchant_id, - payment_data.mandate_id.clone(), - router_data.response.clone(), + let m_db = state.clone().store; + let m_router_data_merchant_id = router_data.merchant_id.clone(); + let m_payment_data_mandate_id = payment_data.mandate_id.clone(); + let m_router_data_response = router_data.response.clone(); + let mandate_update_fut = tokio::spawn( + async move { + mandate::update_connector_mandate_id( + m_db.as_ref(), + m_router_data_merchant_id, + m_payment_data_mandate_id, + m_router_data_response, + ) + .await + } + .in_current_span(), ); - let (payment_intent, _) = futures::try_join!(payment_intent_fut, mandate_update_fut)?; - payment_data.payment_intent = payment_intent; + let (payment_intent, _, _) = futures::try_join!( + utils::flatten_join_error(payment_intent_fut), + utils::flatten_join_error(mandate_update_fut), + utils::flatten_join_error(payment_attempt_fut) + )?; + payment_data.payment_intent = payment_intent; Ok(payment_data) } diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 52677ab3cc8d..3abde60c2e9b 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -200,7 +200,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + state: &'b AppState, mut payment_data: PaymentData, _customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, @@ -217,7 +217,8 @@ impl { let metadata = payment_data.payment_intent.metadata.clone(); payment_data.payment_intent = match metadata { - Some(metadata) => db + Some(metadata) => state + .store .update_payment_intent( payment_data.payment_intent, storage::PaymentIntentUpdate::MetadataUpdate { diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 5578f6b3dc15..17f39d5150bb 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -174,7 +174,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - _db: &dyn StorageInterface, + _state: &'b AppState, payment_data: PaymentData, _customer: Option, _storage_scheme: storage_enums::MerchantStorageScheme, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 83e7131b2675..fb58aeb34e07 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -132,7 +132,7 @@ impl { async fn update_trackers<'b>( &'b self, - _db: &dyn StorageInterface, + _state: &'b AppState, payment_data: PaymentData, _customer: Option, _storage_scheme: enums::MerchantStorageScheme, @@ -157,7 +157,7 @@ impl { async fn update_trackers<'b>( &'b self, - _db: &dyn StorageInterface, + _state: &'b AppState, payment_data: PaymentData, _customer: Option, _storage_scheme: enums::MerchantStorageScheme, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 26bda6d6bee6..53a768f26810 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -422,7 +422,7 @@ impl #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &dyn StorageInterface, + state: &'b AppState, mut payment_data: PaymentData, customer: Option, storage_scheme: storage_enums::MerchantStorageScheme, @@ -456,7 +456,7 @@ impl .payment_method_data .as_ref() .async_map(|payment_method_data| async { - helpers::get_additional_payment_data(payment_method_data, db).await + helpers::get_additional_payment_data(payment_method_data, &*state.store).await }) .await .as_ref() @@ -471,6 +471,7 @@ impl let payment_experience = payment_data.payment_attempt.payment_experience; let amount_to_capture = payment_data.payment_attempt.amount_to_capture; let capture_method = payment_data.payment_attempt.capture_method; + let surcharge_amount = payment_data .surcharge_details .as_ref() @@ -479,7 +480,8 @@ impl .surcharge_details .as_ref() .map(|surcharge_details| surcharge_details.tax_on_surcharge_amount); - payment_data.payment_attempt = db + payment_data.payment_attempt = state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt, storage::PaymentAttemptUpdate::Update { @@ -540,7 +542,8 @@ impl let order_details = payment_data.payment_intent.order_details.clone(); let metadata = payment_data.payment_intent.metadata.clone(); - payment_data.payment_intent = db + payment_data.payment_intent = state + .store .update_payment_intent( payment_data.payment_intent, storage::PaymentIntentUpdate::Update { diff --git a/crates/router/src/core/payments/routing/transformers.rs b/crates/router/src/core/payments/routing/transformers.rs index de94a36248ff..5704f82f4983 100644 --- a/crates/router/src/core/payments/routing/transformers.rs +++ b/crates/router/src/core/payments/routing/transformers.rs @@ -74,8 +74,9 @@ impl ForeignFrom for dsl_enums::Connector { api_enums::RoutableConnectors::Adyen => Self::Adyen, api_enums::RoutableConnectors::Airwallex => Self::Airwallex, api_enums::RoutableConnectors::Authorizedotnet => Self::Authorizedotnet, - api_enums::RoutableConnectors::Bitpay => Self::Bitpay, api_enums::RoutableConnectors::Bambora => Self::Bambora, + api_enums::RoutableConnectors::Bankofamerica => Self::Bankofamerica, + api_enums::RoutableConnectors::Bitpay => Self::Bitpay, api_enums::RoutableConnectors::Bluesnap => Self::Bluesnap, api_enums::RoutableConnectors::Boku => Self::Boku, api_enums::RoutableConnectors::Braintree => Self::Braintree, @@ -104,6 +105,7 @@ impl ForeignFrom for dsl_enums::Connector { api_enums::RoutableConnectors::Paypal => Self::Paypal, api_enums::RoutableConnectors::Payu => Self::Payu, api_enums::RoutableConnectors::Powertranz => Self::Powertranz, + api_enums::RoutableConnectors::Prophetpay => Self::Prophetpay, api_enums::RoutableConnectors::Rapyd => Self::Rapyd, api_enums::RoutableConnectors::Shift4 => Self::Shift4, api_enums::RoutableConnectors::Square => Self::Square, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 39079ea36cd6..c1e00b9b8000 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -152,6 +152,7 @@ pub async fn save_payout_data_to_locker( card_isin: None, nick_name: None, }, + card_reference: None, }); ( payload, @@ -195,9 +196,14 @@ pub async fn save_payout_data_to_locker( } }; // Store payout method in locker - let stored_resp = cards::call_to_locker_hs(state, &locker_req, &payout_attempt.customer_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; + let stored_resp = cards::call_to_locker_hs( + state, + &locker_req, + &payout_attempt.customer_id, + api_enums::LockerChoice::Basilisk, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; // Store card_reference in payouts table let db = &*state.store; diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index ba4d7f6549e7..db53a3b56a15 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -79,29 +79,35 @@ pub async fn payments_incoming_webhook_flow< .perform_locking_action(&state, merchant_account.merchant_id.to_string()) .await?; - let response = - payments::payments_core::( - state.clone(), - merchant_account.clone(), - key_store, - payments::operations::PaymentStatus, - api::PaymentsRetrieveRequest { - resource_id: id, - merchant_id: Some(merchant_account.merchant_id.clone()), - force_sync: true, - connector: None, - param: None, - merchant_connector_details: None, - client_secret: None, - expand_attempts: None, - expand_captures: None, - }, - services::AuthFlow::Merchant, - consume_or_trigger_flow, - None, - HeaderPayload::default(), - ) - .await; + let response = Box::pin(payments::payments_core::< + api::PSync, + api::PaymentsResponse, + _, + _, + _, + Ctx, + >( + state.clone(), + merchant_account.clone(), + key_store, + payments::operations::PaymentStatus, + api::PaymentsRetrieveRequest { + resource_id: id, + merchant_id: Some(merchant_account.merchant_id.clone()), + force_sync: true, + connector: None, + param: None, + merchant_connector_details: None, + client_secret: None, + expand_attempts: None, + expand_captures: None, + }, + services::AuthFlow::Merchant, + consume_or_trigger_flow, + None, + HeaderPayload::default(), + )) + .await; lock_action .free_lock_action(&state, merchant_account.merchant_id.to_owned()) @@ -572,7 +578,14 @@ async fn bank_transfer_webhook_flow( + Box::pin(payments::payments_core::< + api::Authorize, + api::PaymentsResponse, + _, + _, + _, + Ctx, + >( state.clone(), merchant_account.to_owned(), key_store, @@ -582,7 +595,7 @@ async fn bank_transfer_webhook_flow RouterResponse { - let (application_response, _webhooks_response_tracker) = webhooks_core::( + let (application_response, _webhooks_response_tracker) = Box::pin(webhooks_core::( state, req, merchant_account, key_store, connector_name_or_mca_id, body, - ) + )) .await?; Ok(application_response) @@ -1089,18 +1102,18 @@ pub async fn webhooks_core payments_incoming_webhook_flow::( + api::WebhookFlow::Payment => Box::pin(payments_incoming_webhook_flow::( state.clone(), merchant_account, business_profile, key_store, webhook_details, source_verified, - ) + )) .await .attach_printable("Incoming webhook flow for payments failed")?, - api::WebhookFlow::Refund => refunds_incoming_webhook_flow::( + api::WebhookFlow::Refund => Box::pin(refunds_incoming_webhook_flow::( state.clone(), merchant_account, business_profile, @@ -1109,7 +1122,7 @@ pub async fn webhooks_core bank_transfer_webhook_flow::( + api::WebhookFlow::BankTransfer => Box::pin(bank_transfer_webhook_flow::( state.clone(), merchant_account, business_profile, key_store, webhook_details, source_verified, - ) + )) .await .attach_printable("Incoming bank-transfer webhook flow failed")?, diff --git a/crates/router/src/db/address.rs b/crates/router/src/db/address.rs index 9244fc022d9e..689d1f9c7891 100644 --- a/crates/router/src/db/address.rs +++ b/crates/router/src/db/address.rs @@ -339,7 +339,7 @@ mod storage { MerchantStorageScheme::RedisKv => { let key = format!("mid_{}_pid_{}", merchant_id, payment_id); let field = format!("add_{}", address_id); - db_utils::try_redis_get_else_try_database_get( + Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -350,7 +350,7 @@ mod storage { .try_into_hget() }, database_call, - ) + )) .await } }?; diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index c9b9f8ac55f5..8ac8bd106eff 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -310,7 +310,7 @@ mod storage { .await?; let key = &lookup.pk_id; - db_utils::try_redis_get_else_try_database_get( + Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -321,7 +321,7 @@ mod storage { .try_into_hget() }, database_call, - ) + )) .await } } @@ -490,7 +490,7 @@ mod storage { let pattern = db_utils::generate_hscan_pattern_for_refund(&lookup.sk_id); - db_utils::try_redis_get_else_try_database_get( + Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -501,7 +501,7 @@ mod storage { .try_into_scan() }, database_call, - ) + )) .await } } @@ -581,7 +581,7 @@ mod storage { .await?; let key = &lookup.pk_id; - db_utils::try_redis_get_else_try_database_get( + Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -592,7 +592,7 @@ mod storage { .try_into_hget() }, database_call, - ) + )) .await } } @@ -626,7 +626,7 @@ mod storage { .await?; let key = &lookup.pk_id; - db_utils::try_redis_get_else_try_database_get( + Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -637,7 +637,7 @@ mod storage { .try_into_hget() }, database_call, - ) + )) .await } } @@ -664,7 +664,7 @@ mod storage { enums::MerchantStorageScheme::PostgresOnly => database_call().await, enums::MerchantStorageScheme::RedisKv => { let key = format!("mid_{merchant_id}_pid_{payment_id}"); - db_utils::try_redis_get_else_try_database_get( + Box::pin(db_utils::try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -675,7 +675,7 @@ mod storage { .try_into_scan() }, database_call, - ) + )) .await } } diff --git a/crates/router/src/db/reverse_lookup.rs b/crates/router/src/db/reverse_lookup.rs index 4a4056032b18..445e171fa277 100644 --- a/crates/router/src/db/reverse_lookup.rs +++ b/crates/router/src/db/reverse_lookup.rs @@ -150,7 +150,11 @@ mod storage { .try_into_get() }; - db_utils::try_redis_get_else_try_database_get(redis_fut, database_call).await + Box::pin(db_utils::try_redis_get_else_try_database_get( + redis_fut, + database_call, + )) + .await } } } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index e106eb06a766..58d77d9e02f4 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -145,6 +145,7 @@ pub fn mk_app( .service(routes::Disputes::server(state.clone())) .service(routes::Analytics::server(state.clone())) .service(routes::Routing::server(state.clone())) + .service(routes::LockerMigrate::server(state.clone())) .service(routes::Gsm::server(state.clone())) .service(routes::User::server(state.clone())) } @@ -189,7 +190,7 @@ pub async fn start_server(conf: settings::Settings) -> ApplicationResult errors::ApplicationError::ApiClientError(error.current_context().clone()) })?, ); - let state = routes::AppState::new(conf, tx, api_client).await; + let state = Box::pin(routes::AppState::new(conf, tx, api_client)).await; let request_body_limit = server.request_body_limit; let server = actix_web::HttpServer::new(move || mk_app(state.clone(), request_body_limit)) .bind((server.host.as_str(), server.port))? diff --git a/crates/router/src/openapi.rs b/crates/router/src/openapi.rs index dbcd8cbe4ce2..095e1f45f93f 100644 --- a/crates/router/src/openapi.rs +++ b/crates/router/src/openapi.rs @@ -114,7 +114,11 @@ Never share your secret api keys. Keep them guarded and secure. crate::routes::payouts::payouts_fulfill, crate::routes::payouts::payouts_retrieve, crate::routes::payouts::payouts_update, - crate::routes::payment_link::payment_link_retrieve + crate::routes::payment_link::payment_link_retrieve, + crate::routes::gsm::create_gsm_rule, + crate::routes::gsm::get_gsm_rule, + crate::routes::gsm::update_gsm_rule, + crate::routes::gsm::delete_gsm_rule, ), components(schemas( crate::types::api::refunds::RefundRequest, @@ -184,6 +188,13 @@ Never share your secret api keys. Keep them guarded and secure. api_models::admin::PaymentLinkColorSchema, api_models::disputes::DisputeResponse, api_models::disputes::DisputeResponsePaymentsRetrieve, + api_models::gsm::GsmCreateRequest, + api_models::gsm::GsmRetrieveRequest, + api_models::gsm::GsmUpdateRequest, + api_models::gsm::GsmDeleteRequest, + api_models::gsm::GsmDeleteResponse, + api_models::gsm::GsmResponse, + api_models::gsm::GsmDecision, api_models::payments::AddressDetails, api_models::payments::BankDebitData, api_models::payments::AliPayQr, diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index 745433c2074b..5166e326fb91 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -29,6 +29,7 @@ pub mod user; pub mod verification; pub mod webhooks; +pub mod locker_migration; #[cfg(feature = "dummy_connector")] pub use self::app::DummyConnector; #[cfg(feature = "payouts")] @@ -39,8 +40,8 @@ pub use self::app::Routing; pub use self::app::Verify; pub use self::app::{ ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, Customers, Disputes, EphemeralKey, - Files, Gsm, Health, Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, - PaymentMethods, Payments, Refunds, User, Webhooks, + Files, Gsm, Health, LockerMigrate, Mandates, MerchantAccount, MerchantConnectorAccount, + PaymentLink, PaymentMethods, Payments, Refunds, User, Webhooks, }; #[cfg(feature = "stripe")] pub use super::compatibility::stripe::StripeApis; diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs index a8eda22402c3..eef8cacc5f92 100644 --- a/crates/router/src/routes/admin.rs +++ b/crates/router/src/routes/admin.rs @@ -30,7 +30,7 @@ pub async fn merchant_account_create( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::MerchantsAccountCreate; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -38,7 +38,7 @@ pub async fn merchant_account_create( |state, _, req| create_merchant_account(state, req), &auth::AdminApiAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Merchant Account - Retrieve @@ -131,7 +131,7 @@ pub async fn update_merchant_account( ) -> HttpResponse { let flow = Flow::MerchantsAccountUpdate; let merchant_id = mid.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -145,7 +145,7 @@ pub async fn update_merchant_account( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -210,7 +210,7 @@ pub async fn payment_connector_create( ) -> HttpResponse { let flow = Flow::MerchantConnectorsCreate; let merchant_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -224,7 +224,7 @@ pub async fn payment_connector_create( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Merchant Connector - Retrieve @@ -450,7 +450,7 @@ pub async fn business_profile_create( let payload = json_payload.into_inner(); let merchant_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -464,7 +464,7 @@ pub async fn business_profile_create( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?Flow::BusinessProfileRetrieve))] diff --git a/crates/router/src/routes/api_keys.rs b/crates/router/src/routes/api_keys.rs index 1f71f1dc2800..7299aa696390 100644 --- a/crates/router/src/routes/api_keys.rs +++ b/crates/router/src/routes/api_keys.rs @@ -36,7 +36,7 @@ pub async fn api_key_create( let payload = json_payload.into_inner(); let merchant_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -61,7 +61,7 @@ pub async fn api_key_create( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } /// API Key - Retrieve diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 7f5c720be607..070f1eb29bf8 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -19,7 +19,7 @@ use super::routing as cloud_routing; #[cfg(all(feature = "olap", feature = "kms"))] use super::verification::{apple_pay_merchant_registration, retrieve_apple_pay_verified_domains}; #[cfg(feature = "olap")] -use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*, user::*}; +use super::{admin::*, api_keys::*, disputes::*, files::*, gsm::*, locker_migration, user::*}; use super::{cache::*, health::*, payment_link::*}; #[cfg(any(feature = "olap", feature = "oltp"))] use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*}; @@ -112,56 +112,59 @@ impl AppState { shut_down_signal: oneshot::Sender<()>, api_client: Box, ) -> Self { - #[cfg(feature = "kms")] - let kms_client = kms::get_kms_client(&conf.kms).await; - let testable = storage_impl == StorageImpl::PostgresqlTest; - let store: Box = match storage_impl { - StorageImpl::Postgresql | StorageImpl::PostgresqlTest => Box::new( + Box::pin(async move { + #[cfg(feature = "kms")] + let kms_client = kms::get_kms_client(&conf.kms).await; + let testable = storage_impl == StorageImpl::PostgresqlTest; + let store: Box = match storage_impl { + StorageImpl::Postgresql | StorageImpl::PostgresqlTest => Box::new( + #[allow(clippy::expect_used)] + get_store(&conf, shut_down_signal, testable) + .await + .expect("Failed to create store"), + ), #[allow(clippy::expect_used)] - get_store(&conf, shut_down_signal, testable) - .await - .expect("Failed to create store"), - ), - #[allow(clippy::expect_used)] - StorageImpl::Mock => Box::new( - MockDb::new(&conf.redis) - .await - .expect("Failed to create mock store"), - ), - }; + StorageImpl::Mock => Box::new( + MockDb::new(&conf.redis) + .await + .expect("Failed to create mock store"), + ), + }; + + #[cfg(feature = "olap")] + let pool = crate::analytics::AnalyticsProvider::from_conf( + &conf.analytics, + #[cfg(feature = "kms")] + kms_client, + ) + .await; - #[cfg(feature = "olap")] - let pool = crate::analytics::AnalyticsProvider::from_conf( - &conf.analytics, #[cfg(feature = "kms")] - kms_client, - ) - .await; - - #[cfg(feature = "kms")] - #[allow(clippy::expect_used)] - let kms_secrets = settings::ActiveKmsSecrets { - jwekey: conf.jwekey.clone().into(), - } - .decrypt_inner(kms_client) - .await - .expect("Failed while performing KMS decryption"); - - #[cfg(feature = "email")] - let email_client = Arc::new(AwsSes::new(&conf.email).await); - Self { - flow_name: String::from("default"), - store, - conf: Arc::new(conf), + #[allow(clippy::expect_used)] + let kms_secrets = settings::ActiveKmsSecrets { + jwekey: conf.jwekey.clone().into(), + } + .decrypt_inner(kms_client) + .await + .expect("Failed while performing KMS decryption"); + #[cfg(feature = "email")] - email_client, - #[cfg(feature = "kms")] - kms_secrets: Arc::new(kms_secrets), - api_client, - event_handler: Box::::default(), - #[cfg(feature = "olap")] - pool, - } + let email_client = Arc::new(AwsSes::new(&conf.email).await); + Self { + flow_name: String::from("default"), + store, + conf: Arc::new(conf), + #[cfg(feature = "email")] + email_client, + #[cfg(feature = "kms")] + kms_secrets: Arc::new(kms_secrets), + api_client, + event_handler: Box::::default(), + #[cfg(feature = "olap")] + pool, + } + }) + .await } pub async fn new( @@ -169,7 +172,13 @@ impl AppState { shut_down_signal: oneshot::Sender<()>, api_client: Box, ) -> Self { - Self::with_storage(conf, StorageImpl::Postgresql, shut_down_signal, api_client).await + Box::pin(Self::with_storage( + conf, + StorageImpl::Postgresql, + shut_down_signal, + api_client, + )) + .await } } @@ -734,3 +743,16 @@ impl User { .service(web::resource("/v2/signup").route(web::post().to(user_connect_account))) } } + +pub struct LockerMigrate; + +#[cfg(feature = "olap")] +impl LockerMigrate { + pub fn server(state: AppState) -> Scope { + web::scope("locker_migration/{merchant_id}") + .app_data(web::Data::new(state)) + .service( + web::resource("").route(web::post().to(locker_migration::rust_locker_migration)), + ) + } +} diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index ff2ffc2a3fe3..cfc37cbdbb2a 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -30,7 +30,7 @@ pub async fn customers_create( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::CustomersCreate; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -38,7 +38,7 @@ pub async fn customers_create( |state, auth, req| create_customer(state, auth.merchant_account, auth.key_store, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Retrieve Customer @@ -142,7 +142,7 @@ pub async fn customers_update( let flow = Flow::CustomersUpdate; let customer_id = path.into_inner(); json_payload.customer_id = customer_id; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -150,7 +150,7 @@ pub async fn customers_update( |state, auth, req| update_customer(state, auth.merchant_account, req, auth.key_store), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Delete Customer @@ -179,7 +179,7 @@ pub async fn customers_delete( customer_id: path.into_inner(), }) .into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -187,7 +187,7 @@ pub async fn customers_delete( |state, auth, req| delete_customer(state, auth.merchant_account, req, auth.key_store), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?Flow::CustomersGetMandates))] diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index d570a5319687..aaeb118645db 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -117,7 +117,7 @@ pub async fn accept_dispute( let dispute_id = dispute_types::DisputeId { dispute_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -127,7 +127,7 @@ pub async fn accept_dispute( }, auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Disputes - Submit Dispute Evidence @@ -150,7 +150,7 @@ pub async fn submit_dispute_evidence( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::DisputesEvidenceSubmit; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -160,7 +160,7 @@ pub async fn submit_dispute_evidence( }, auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Disputes - Attach Evidence to Dispute @@ -191,7 +191,7 @@ pub async fn attach_dispute_evidence( Ok(valid_request) => valid_request, Err(err) => return api::log_and_return_error_response(err), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -201,7 +201,7 @@ pub async fn attach_dispute_evidence( }, auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Diputes - Retrieve Dispute @@ -229,7 +229,7 @@ pub async fn retrieve_dispute_evidence( let dispute_id = dispute_types::DisputeId { dispute_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -237,6 +237,6 @@ pub async fn retrieve_dispute_evidence( |state, auth, req| disputes::retrieve_dispute_evidence(state, auth.merchant_account, req), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/files.rs b/crates/router/src/routes/files.rs index 4a327ba0807d..bde221ebc161 100644 --- a/crates/router/src/routes/files.rs +++ b/crates/router/src/routes/files.rs @@ -39,7 +39,7 @@ pub async fn files_create( Ok(valid_request) => valid_request, Err(err) => return api::log_and_return_error_response(err), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -47,7 +47,7 @@ pub async fn files_create( |state, auth, req| files_create_core(state, auth.merchant_account, auth.key_store, req), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Files - Delete @@ -77,7 +77,7 @@ pub async fn files_delete( let file_id = files::FileId { file_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -85,7 +85,7 @@ pub async fn files_delete( |state, auth, req| files_delete_core(state, auth.merchant_account, req), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Files - Retrieve @@ -115,7 +115,7 @@ pub async fn files_retrieve( let file_id = files::FileId { file_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -123,6 +123,6 @@ pub async fn files_retrieve( |state, auth, req| files_retrieve_core(state, auth.merchant_account, auth.key_store, req), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/gsm.rs b/crates/router/src/routes/gsm.rs index 02d943792dba..ff70635959fc 100644 --- a/crates/router/src/routes/gsm.rs +++ b/crates/router/src/routes/gsm.rs @@ -8,6 +8,23 @@ use crate::{ services::{api, authentication as auth}, }; +/// Gsm - Create +/// +/// To create a Gsm Rule +#[utoipa::path( + post, + path = "/gsm", + request_body( + content = GsmCreateRequest, + ), + responses( + (status = 200, description = "Gsm created", body = GsmResponse), + (status = 400, description = "Missing Mandatory fields") + ), + tag = "Gsm", + operation_id = "Create Gsm Rule", + security(("admin_api_key" = [])), +)] #[instrument(skip_all, fields(flow = ?Flow::GsmRuleCreate))] pub async fn create_gsm_rule( state: web::Data, @@ -29,6 +46,23 @@ pub async fn create_gsm_rule( .await } +/// Gsm - Get +/// +/// To get a Gsm Rule +#[utoipa::path( + post, + path = "/gsm/get", + request_body( + content = GsmRetrieveRequest, + ), + responses( + (status = 200, description = "Gsm retrieved", body = GsmResponse), + (status = 400, description = "Missing Mandatory fields") + ), + tag = "Gsm", + operation_id = "Retrieve Gsm Rule", + security(("admin_api_key" = [])), +)] #[instrument(skip_all, fields(flow = ?Flow::GsmRuleRetrieve))] pub async fn get_gsm_rule( state: web::Data, @@ -49,6 +83,23 @@ pub async fn get_gsm_rule( .await } +/// Gsm - Update +/// +/// To update a Gsm Rule +#[utoipa::path( + post, + path = "/gsm/update", + request_body( + content = GsmUpdateRequest, + ), + responses( + (status = 200, description = "Gsm updated", body = GsmResponse), + (status = 400, description = "Missing Mandatory fields") + ), + tag = "Gsm", + operation_id = "Update Gsm Rule", + security(("admin_api_key" = [])), +)] #[instrument(skip_all, fields(flow = ?Flow::GsmRuleUpdate))] pub async fn update_gsm_rule( state: web::Data, @@ -70,6 +121,23 @@ pub async fn update_gsm_rule( .await } +/// Gsm - Delete +/// +/// To delete a Gsm Rule +#[utoipa::path( + post, + path = "/gsm/delete", + request_body( + content = GsmDeleteRequest, + ), + responses( + (status = 200, description = "Gsm deleted", body = GsmDeleteResponse), + (status = 400, description = "Missing Mandatory fields") + ), + tag = "Gsm", + operation_id = "Delete Gsm Rule", + security(("admin_api_key" = [])), +)] #[instrument(skip_all, fields(flow = ?Flow::GsmRuleDelete))] pub async fn delete_gsm_rule( state: web::Data, diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs index ae573e871627..c093523d455a 100644 --- a/crates/router/src/routes/lock_utils.rs +++ b/crates/router/src/routes/lock_utils.rs @@ -23,6 +23,7 @@ pub enum ApiIdentifier { ApiKeys, PaymentLink, Routing, + RustLockerMigration, Gsm, User, } @@ -131,6 +132,7 @@ impl From for ApiIdentifier { Flow::Verification => Self::Verification, Flow::PaymentLinkInitiate | Flow::PaymentLinkRetrieve => Self::PaymentLink, + Flow::RustLockerMigration => Self::RustLockerMigration, Flow::GsmRuleCreate | Flow::GsmRuleRetrieve | Flow::GsmRuleUpdate diff --git a/crates/router/src/routes/locker_migration.rs b/crates/router/src/routes/locker_migration.rs new file mode 100644 index 000000000000..892dc5941bd6 --- /dev/null +++ b/crates/router/src/routes/locker_migration.rs @@ -0,0 +1,27 @@ +use actix_web::{web, HttpRequest, HttpResponse}; +use router_env::Flow; + +use super::AppState; +use crate::{ + core::{api_locking, locker_migration}, + services::{api, authentication as auth}, +}; + +pub async fn rust_locker_migration( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> HttpResponse { + let flow = Flow::RustLockerMigration; + let merchant_id = path.into_inner(); + api::server_wrap( + flow, + state, + &req, + &merchant_id, + |state, _, _| locker_migration::rust_locker_migration(state, &merchant_id), + &auth::AdminApiAuth, + api_locking::LockAction::NotApplicable, + ) + .await +} diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index b664ee4429d4..7d6bf1a05f09 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -62,7 +62,7 @@ pub async fn initiate_payment_link( payment_id, merchant_id: merchant_id.clone(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -77,6 +77,6 @@ pub async fn initiate_payment_link( }, &crate::services::authentication::MerchantIdAuth(merchant_id), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index faaf757fd7e7..83d4c7f96611 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -34,7 +34,7 @@ pub async fn create_payment_method_api( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::PaymentMethodsCreate; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -44,7 +44,7 @@ pub async fn create_payment_method_api( }, &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// List payment methods for a Merchant @@ -84,7 +84,7 @@ pub async fn list_payment_method_api( Err(e) => return api::log_and_return_error_response(e), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -94,7 +94,7 @@ pub async fn list_payment_method_api( }, &*auth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// List payment methods for a Customer @@ -135,7 +135,7 @@ pub async fn list_customer_payment_method_api( Err(e) => return api::log_and_return_error_response(e), }; let customer_id = customer_id.into_inner().0; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -151,7 +151,7 @@ pub async fn list_customer_payment_method_api( }, &*auth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// List payment methods for a Customer @@ -191,7 +191,7 @@ pub async fn list_customer_payment_method_api_client( Ok((auth, _auth_flow)) => (auth, _auth_flow), Err(e) => return api::log_and_return_error_response(e), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -207,7 +207,7 @@ pub async fn list_customer_payment_method_api_client( }, &*auth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payment Method - Retrieve @@ -239,7 +239,7 @@ pub async fn payment_method_retrieve_api( }) .into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -247,7 +247,7 @@ pub async fn payment_method_retrieve_api( |state, _auth, pm| cards::retrieve_payment_method(state, pm), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payment Method - Update @@ -278,7 +278,7 @@ pub async fn payment_method_update_api( let flow = Flow::PaymentMethodsUpdate; let payment_method_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -294,7 +294,7 @@ pub async fn payment_method_update_api( }, &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payment Method - Delete @@ -324,7 +324,7 @@ pub async fn payment_method_delete_api( let pm = PaymentMethodId { payment_method_id: payment_method_id.into_inner().0, }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -332,7 +332,7 @@ pub async fn payment_method_delete_api( |state, auth, req| cards::delete_payment_method(state, auth.merchant_account, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } #[cfg(test)] diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index ed36721da445..b05fae65338a 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -102,7 +102,7 @@ pub async fn payments_create( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -123,7 +123,7 @@ pub async fn payments_create( _ => auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), }, locking_action, - ) + )) .await } // /// Payments - Redirect @@ -160,7 +160,7 @@ pub async fn payments_start( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -187,7 +187,7 @@ pub async fn payments_start( }, &auth::MerchantIdAuth(merchant_id), locking_action, - ) + )) .await } /// Payments - Retrieve @@ -234,7 +234,7 @@ pub async fn payments_retrieve( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -258,7 +258,7 @@ pub async fn payments_retrieve( req.headers(), ), locking_action, - ) + )) .await } /// Payments - Retrieve with gateway credentials @@ -300,7 +300,7 @@ pub async fn payments_retrieve_with_gateway_creds( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -320,7 +320,7 @@ pub async fn payments_retrieve_with_gateway_creds( }, &*auth_type, locking_action, - ) + )) .await } /// Payments - Update @@ -367,7 +367,7 @@ pub async fn payments_update( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -385,7 +385,7 @@ pub async fn payments_update( }, &*auth_type, locking_action, - ) + )) .await } /// Payments - Confirm @@ -443,7 +443,7 @@ pub async fn payments_confirm( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -461,7 +461,7 @@ pub async fn payments_confirm( }, &*auth_type, locking_action, - ) + )) .await } /// Payments - Capture @@ -498,7 +498,7 @@ pub async fn payments_capture( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -525,7 +525,7 @@ pub async fn payments_capture( }, &auth::ApiKeyAuth, locking_action, - ) + )) .await } /// Payments - Session token @@ -554,7 +554,7 @@ pub async fn payments_connector_session( let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -581,7 +581,7 @@ pub async fn payments_connector_session( }, &auth::PublishableKeyAuth, locking_action, - ) + )) .await } // /// Payments - Redirect response @@ -772,7 +772,7 @@ pub async fn payments_cancel( let payment_id = path.into_inner(); payload.payment_id = payment_id; let locking_action = payload.get_locking_input(flow.clone()); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -792,7 +792,7 @@ pub async fn payments_cancel( }, &auth::ApiKeyAuth, locking_action, - ) + )) .await } /// Payments - List diff --git a/crates/router/src/routes/payouts.rs b/crates/router/src/routes/payouts.rs index 15cf59aaf32d..cc47263a0c56 100644 --- a/crates/router/src/routes/payouts.rs +++ b/crates/router/src/routes/payouts.rs @@ -33,7 +33,7 @@ pub async fn payouts_create( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::PayoutsCreate; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -41,7 +41,7 @@ pub async fn payouts_create( |state, auth, req| payouts_create_core(state, auth.merchant_account, auth.key_store, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payouts - Retrieve @@ -72,7 +72,7 @@ pub async fn payouts_retrieve( force_sync: query_params.force_sync, }; let flow = Flow::PayoutsRetrieve; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -80,7 +80,7 @@ pub async fn payouts_retrieve( |state, auth, req| payouts_retrieve_core(state, auth.merchant_account, auth.key_store, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payouts - Update @@ -111,7 +111,7 @@ pub async fn payouts_update( let payout_id = path.into_inner(); let mut payout_update_payload = json_payload.into_inner(); payout_update_payload.payout_id = Some(payout_id); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -119,7 +119,7 @@ pub async fn payouts_update( |state, auth, req| payouts_update_core(state, auth.merchant_account, auth.key_store, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payouts - Cancel @@ -150,7 +150,7 @@ pub async fn payouts_cancel( let mut payload = json_payload.into_inner(); payload.payout_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -158,7 +158,7 @@ pub async fn payouts_cancel( |state, auth, req| payouts_cancel_core(state, auth.merchant_account, auth.key_store, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Payouts - Fulfill @@ -189,7 +189,7 @@ pub async fn payouts_fulfill( let mut payload = json_payload.into_inner(); payload.payout_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -197,7 +197,7 @@ pub async fn payouts_fulfill( |state, auth, req| payouts_fulfill_core(state, auth.merchant_account, auth.key_store, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?Flow::PayoutsAccounts))] diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index d1f5cb56fe23..d370af6b8d7a 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -31,7 +31,7 @@ pub async fn refunds_create( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsCreate; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -39,7 +39,7 @@ pub async fn refunds_create( |state, auth, req| refund_create_core(state, auth.merchant_account, auth.key_store, req), auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Refunds - Retrieve (GET) @@ -74,7 +74,7 @@ pub async fn refunds_retrieve( }; let flow = Flow::RefundsRetrieve; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -90,7 +90,7 @@ pub async fn refunds_retrieve( }, auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Refunds - Retrieve (POST) @@ -115,7 +115,7 @@ pub async fn refunds_retrieve_with_body( json_payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsRetrieve; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -131,7 +131,7 @@ pub async fn refunds_retrieve_with_body( }, &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Refunds - Update diff --git a/crates/router/src/routes/verification.rs b/crates/router/src/routes/verification.rs index 2ad061848c92..d0525bb272e8 100644 --- a/crates/router/src/routes/verification.rs +++ b/crates/router/src/routes/verification.rs @@ -18,7 +18,7 @@ pub async fn apple_pay_merchant_registration( let flow = Flow::Verification; let merchant_id = path.into_inner(); let kms_conf = &state.clone().conf.kms; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -34,7 +34,7 @@ pub async fn apple_pay_merchant_registration( }, auth::auth_type(&auth::ApiKeyAuth, &auth::JWTAuth, req.headers()), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/webhooks.rs b/crates/router/src/routes/webhooks.rs index 5c90e46bb90b..63f2328ec6ce 100644 --- a/crates/router/src/routes/webhooks.rs +++ b/crates/router/src/routes/webhooks.rs @@ -21,7 +21,7 @@ pub async fn receive_incoming_webhook( let flow = Flow::IncomingWebhookReceive; let (merchant_id, connector_id_or_name) = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -38,6 +38,6 @@ pub async fn receive_incoming_webhook( }, &auth::MerchantIdAuth(merchant_id), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index 7e9725d1a3b7..7cf8f6b71fa5 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -1213,5 +1213,3 @@ impl } } } - -pub type GsmResponse = storage::GatewayStatusMap; diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 67d2d37f4fea..dc615c4e41fa 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -329,7 +329,7 @@ impl ConnectorData { enums::Connector::Airwallex => Ok(Box::new(&connector::Airwallex)), enums::Connector::Authorizedotnet => Ok(Box::new(&connector::Authorizedotnet)), enums::Connector::Bambora => Ok(Box::new(&connector::Bambora)), - // enums::Connector::Bankofamerica => Ok(Box::new(&connector::Bankofamerica)), Added as template code for future usage + enums::Connector::Bankofamerica => Ok(Box::new(&connector::Bankofamerica)), enums::Connector::Bitpay => Ok(Box::new(&connector::Bitpay)), enums::Connector::Bluesnap => Ok(Box::new(&connector::Bluesnap)), enums::Connector::Boku => Ok(Box::new(&connector::Boku)), @@ -371,7 +371,7 @@ impl ConnectorData { enums::Connector::Payme => Ok(Box::new(&connector::Payme)), enums::Connector::Payu => Ok(Box::new(&connector::Payu)), enums::Connector::Powertranz => Ok(Box::new(&connector::Powertranz)), - // enums::Connector::Prophetpay => Ok(Box::new(&connector::Prophetpay)), + enums::Connector::Prophetpay => Ok(Box::new(&connector::Prophetpay)), enums::Connector::Rapyd => Ok(Box::new(&connector::Rapyd)), enums::Connector::Shift4 => Ok(Box::new(&connector::Shift4)), enums::Connector::Square => Ok(Box::new(&connector::Square)), diff --git a/crates/router/src/types/domain/customer.rs b/crates/router/src/types/domain/customer.rs index 3810523b413f..fe575851dc49 100644 --- a/crates/router/src/types/domain/customer.rs +++ b/crates/router/src/types/domain/customer.rs @@ -99,7 +99,7 @@ impl super::behaviour::Conversion for Customer { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum CustomerUpdate { Update { name: crypto::OptionalEncryptableName, diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 1cd016de18e6..f43abdf73ead 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -193,8 +193,9 @@ impl ForeignTryFrom for api_enums::RoutableConnectors { api_enums::Connector::Adyen => Self::Adyen, api_enums::Connector::Airwallex => Self::Airwallex, api_enums::Connector::Authorizedotnet => Self::Authorizedotnet, - api_enums::Connector::Bitpay => Self::Bitpay, api_enums::Connector::Bambora => Self::Bambora, + api_enums::Connector::Bankofamerica => Self::Bankofamerica, + api_enums::Connector::Bitpay => Self::Bitpay, api_enums::Connector::Bluesnap => Self::Bluesnap, api_enums::Connector::Boku => Self::Boku, api_enums::Connector::Braintree => Self::Braintree, @@ -229,6 +230,7 @@ impl ForeignTryFrom for api_enums::RoutableConnectors { .into_report()? } api_enums::Connector::Powertranz => Self::Powertranz, + api_enums::Connector::Prophetpay => Self::Prophetpay, api_enums::Connector::Rapyd => Self::Rapyd, api_enums::Connector::Shift4 => Self::Shift4, api_enums::Connector::Signifyd => { @@ -272,8 +274,9 @@ impl ForeignFrom for api_enums::RoutableConnectors { dsl_enums::Connector::Adyen => Self::Adyen, dsl_enums::Connector::Airwallex => Self::Airwallex, dsl_enums::Connector::Authorizedotnet => Self::Authorizedotnet, - dsl_enums::Connector::Bitpay => Self::Bitpay, dsl_enums::Connector::Bambora => Self::Bambora, + dsl_enums::Connector::Bankofamerica => Self::Bankofamerica, + dsl_enums::Connector::Bitpay => Self::Bitpay, dsl_enums::Connector::Bluesnap => Self::Bluesnap, dsl_enums::Connector::Boku => Self::Boku, dsl_enums::Connector::Braintree => Self::Braintree, @@ -302,6 +305,7 @@ impl ForeignFrom for api_enums::RoutableConnectors { dsl_enums::Connector::Paypal => Self::Paypal, dsl_enums::Connector::Payu => Self::Payu, dsl_enums::Connector::Powertranz => Self::Powertranz, + dsl_enums::Connector::Prophetpay => Self::Prophetpay, dsl_enums::Connector::Rapyd => Self::Rapyd, dsl_enums::Connector::Shift4 => Self::Shift4, dsl_enums::Connector::Square => Self::Square, @@ -501,7 +505,8 @@ impl ForeignFrom for api_enums::PaymentMethod { } api_enums::PaymentMethodType::Benefit | api_enums::PaymentMethodType::Knet - | api_enums::PaymentMethodType::MomoAtm => Self::CardRedirect, + | api_enums::PaymentMethodType::MomoAtm + | api_enums::PaymentMethodType::CardRedirect => Self::CardRedirect, } } } @@ -1047,3 +1052,19 @@ impl ForeignFrom for storage::GatewayStatusMapp } } } + +impl ForeignFrom for gsm_api_types::GsmResponse { + fn foreign_from(value: storage::GatewayStatusMap) -> Self { + Self { + connector: value.connector.to_string(), + flow: value.flow, + sub_flow: value.sub_flow, + code: value.code, + message: value.message, + decision: value.decision.to_string(), + status: value.status, + router_error: value.router_error, + step_up_possible: value.step_up_possible, + } + } +} diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index aadb714e8ce2..83586e51d66a 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -24,6 +24,7 @@ use nanoid::nanoid; use qrcode; use serde::de::DeserializeOwned; use serde_json::Value; +use tracing_futures::Instrument; use uuid::Uuid; pub use self::ext_traits::{OptionExt, ValidateCall}; @@ -753,22 +754,46 @@ where if let services::ApplicationResponse::JsonWithHeaders((payments_response_json, _)) = payments_response { - Box::pin( - webhooks_core::create_event_and_trigger_appropriate_outgoing_webhook( - state.clone(), - merchant_account, - business_profile, - event_type, - diesel_models::enums::EventClass::Payments, - None, - payment_id, - diesel_models::enums::EventObjectType::PaymentDetails, - webhooks::OutgoingWebhookContent::PaymentDetails(payments_response_json), - ), - ) - .await?; + let m_state = state.clone(); + // This spawns this futures in a background thread, the exception inside this future won't affect + // the current thread and the lifecycle of spawn thread is not handled by runtime. + // So when server shutdown won't wait for this thread's completion. + tokio::spawn( + async move { + Box::pin( + webhooks_core::create_event_and_trigger_appropriate_outgoing_webhook( + m_state, + merchant_account, + business_profile, + event_type, + diesel_models::enums::EventClass::Payments, + None, + payment_id, + diesel_models::enums::EventObjectType::PaymentDetails, + webhooks::OutgoingWebhookContent::PaymentDetails( + payments_response_json, + ), + ), + ) + .await + } + .in_current_span(), + ); } } Ok(()) } + +type Handle = tokio::task::JoinHandle>; + +pub async fn flatten_join_error(handle: Handle) -> RouterResult { + match handle.await { + Ok(Ok(t)) => Ok(t), + Ok(Err(err)) => Err(err), + Err(err) => Err(err) + .into_report() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Join Error"), + } +} diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index f41b300c5127..00e7357d896f 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -61,7 +61,13 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { .await?; let (mut payment_data, _, customer, _, _) = - payment_flows::payments_operation_core::( + Box::pin(payment_flows::payments_operation_core::< + api::PSync, + _, + _, + _, + Oss, + >( state, merchant_account.clone(), key_store, @@ -71,7 +77,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { services::AuthFlow::Client, None, api::HeaderPayload::default(), - ) + )) .await?; let terminal_status = [ @@ -169,7 +175,11 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { // Trigger the outgoing webhook to notify the merchant about failed payment let operation = operations::PaymentStatus; - utils::trigger_payments_webhook::<_, api_models::payments::PaymentsRequest, _>( + Box::pin(utils::trigger_payments_webhook::< + _, + api_models::payments::PaymentsRequest, + _, + >( merchant_account, business_profile, payment_data, @@ -177,7 +187,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { customer, state, operation, - ) + )) .await .map_err(|error| logger::warn!(payments_outgoing_webhook_error=?error)) .ok(); diff --git a/crates/router/src/workflows/refund_router.rs b/crates/router/src/workflows/refund_router.rs index 8ca3551cfc0f..934c208f9115 100644 --- a/crates/router/src/workflows/refund_router.rs +++ b/crates/router/src/workflows/refund_router.rs @@ -13,7 +13,7 @@ impl ProcessTrackerWorkflow for RefundWorkflowRouter { state: &'a AppState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { - Ok(refund_flow::start_refund_workflow(state, &process).await?) + Ok(Box::pin(refund_flow::start_refund_workflow(state, &process)).await?) } async fn error_handler<'a>( diff --git a/crates/router/tests/cache.rs b/crates/router/tests/cache.rs index e1fd3a0f0279..4de45c7132a8 100644 --- a/crates/router/tests/cache.rs +++ b/crates/router/tests/cache.rs @@ -7,10 +7,14 @@ mod utils; #[actix_web::test] async fn invalidate_existing_cache_success() { // Arrange - utils::setup().await; + Box::pin(utils::setup()).await; let (tx, _) = tokio::sync::oneshot::channel(); - let state = - routes::AppState::new(Settings::default(), tx, Box::new(services::MockApiClient)).await; + let state = Box::pin(routes::AppState::new( + Settings::default(), + tx, + Box::new(services::MockApiClient), + )) + .await; let cache_key = "cacheKey".to_string(); let cache_key_value = "val".to_string(); @@ -53,7 +57,7 @@ async fn invalidate_existing_cache_success() { #[actix_web::test] async fn invalidate_non_existing_cache_success() { // Arrange - utils::setup().await; + Box::pin(utils::setup()).await; let cache_key = "cacheKey".to_string(); let api_key = ("api-key", "test_admin"); let client = awc::Client::default(); diff --git a/crates/router/tests/connectors/prophetpay.rs b/crates/router/tests/connectors/prophetpay.rs index 09e4ea422531..94220c11a6aa 100644 --- a/crates/router/tests/connectors/prophetpay.rs +++ b/crates/router/tests/connectors/prophetpay.rs @@ -12,8 +12,7 @@ impl utils::Connector for ProphetpayTest { use router::connector::Prophetpay; types::api::ConnectorData { connector: Box::new(&Prophetpay), - // Remove `dummy_connector` feature gate from module in `main.rs` when updating this to use actual connector variant - connector_name: types::Connector::DummyConnector1, + connector_name: types::Connector::Prophetpay, get_token: types::api::GetToken::Connector, merchant_connector_id: None, } diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index 1f450a19e776..67a0625968fb 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -80,7 +80,7 @@ pub trait ConnectorActions: Connector { ) .await; integration.execute_pretasks(&mut request, &state).await?; - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } async fn create_connector_customer( @@ -104,7 +104,7 @@ pub trait ConnectorActions: Connector { ) .await; integration.execute_pretasks(&mut request, &state).await?; - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } async fn create_connector_pm_token( @@ -128,7 +128,7 @@ pub trait ConnectorActions: Connector { ) .await; integration.execute_pretasks(&mut request, &state).await?; - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } /// For initiating payments when `CaptureMethod` is set to `Automatic` @@ -156,7 +156,7 @@ pub trait ConnectorActions: Connector { ) .await; integration.execute_pretasks(&mut request, &state).await?; - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } async fn sync_payment( @@ -169,7 +169,7 @@ pub trait ConnectorActions: Connector { payment_data.unwrap_or_else(|| PaymentSyncType::default().0), payment_info, ); - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } /// will retry the psync till the given status matches or retry max 3 times @@ -207,7 +207,7 @@ pub trait ConnectorActions: Connector { }, payment_info, ); - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } async fn authorize_and_capture_payment( @@ -243,7 +243,7 @@ pub trait ConnectorActions: Connector { }, payment_info, ); - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } async fn authorize_and_void_payment( @@ -280,7 +280,7 @@ pub trait ConnectorActions: Connector { }, payment_info, ); - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } async fn capture_payment_and_refund( @@ -400,7 +400,7 @@ pub trait ConnectorActions: Connector { }), payment_info, ); - call_connector(request, integration).await + Box::pin(call_connector(request, integration)).await } /// will retry the rsync till the given status matches or retry max 3 times diff --git a/crates/router/tests/customers.rs b/crates/router/tests/customers.rs index aa17635388fd..065f98fe6609 100644 --- a/crates/router/tests/customers.rs +++ b/crates/router/tests/customers.rs @@ -10,7 +10,7 @@ mod utils; #[ignore] // verify the API-KEY/merchant id has stripe as first choice async fn customer_success() { - utils::setup().await; + Box::pin(utils::setup()).await; let customer_id = format!("customer_{}", uuid::Uuid::new_v4()); let api_key = ("API-KEY", "MySecretApiKey"); @@ -79,7 +79,7 @@ async fn customer_success() { #[ignore] // verify the API-KEY/merchant id has stripe as first choice async fn customer_failure() { - utils::setup().await; + Box::pin(utils::setup()).await; let customer_id = format!("customer_{}", uuid::Uuid::new_v4()); let api_key = ("api-key", "MySecretApiKey"); diff --git a/crates/router/tests/integration_demo.rs b/crates/router/tests/integration_demo.rs index 16e7ead0a383..5bdf9a5f525e 100644 --- a/crates/router/tests/integration_demo.rs +++ b/crates/router/tests/integration_demo.rs @@ -10,7 +10,7 @@ use utils::{mk_service, ApiKey, AppClient, MerchantId, PaymentId, Status}; /// 1) Create Merchant account #[actix_web::test] async fn create_merchant_account() { - let server = mk_service().await; + let server = Box::pin(mk_service()).await; let client = AppClient::guest(); let admin_client = client.admin("test_admin"); @@ -59,7 +59,7 @@ async fn create_merchant_account() { #[actix_web::test] async fn partial_refund() { let authentication = ConnectorAuthentication::new(); - let server = mk_service().await; + let server = Box::pin(mk_service()).await; let client = AppClient::guest(); let admin_client = client.admin("test_admin"); @@ -125,7 +125,7 @@ async fn partial_refund() { #[actix_web::test] async fn exceed_refund() { let authentication = ConnectorAuthentication::new(); - let server = mk_service().await; + let server = Box::pin(mk_service()).await; let client = AppClient::guest(); let admin_client = client.admin("test_admin"); diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index d2d6c48507e5..9d48aaddd451 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -24,7 +24,7 @@ use uuid::Uuid; #[ignore] // verify the API-KEY/merchant id has stripe as first choice async fn payments_create_stripe() { - utils::setup().await; + Box::pin(utils::setup()).await; let payment_id = format!("test_{}", uuid::Uuid::new_v4()); let api_key = ("API-KEY", "MySecretApiKey"); @@ -93,7 +93,7 @@ async fn payments_create_stripe() { #[ignore] // verify the API-KEY/merchant id has adyen as first choice async fn payments_create_adyen() { - utils::setup().await; + Box::pin(utils::setup()).await; let payment_id = format!("test_{}", uuid::Uuid::new_v4()); let api_key = ("API-KEY", "321"); @@ -162,7 +162,7 @@ async fn payments_create_adyen() { // verify the API-KEY/merchant id has stripe as first choice #[ignore] async fn payments_create_fail() { - utils::setup().await; + Box::pin(utils::setup()).await; let payment_id = format!("test_{}", uuid::Uuid::new_v4()); let api_key = ("API-KEY", "MySecretApiKey"); @@ -221,7 +221,7 @@ async fn payments_create_fail() { #[actix_web::test] #[ignore] async fn payments_todo() { - utils::setup().await; + Box::pin(utils::setup()).await; let client = awc::Client::default(); let mut response; @@ -360,20 +360,26 @@ async fn payments_create_core() { }; let expected_response = services::ApplicationResponse::JsonWithHeaders((expected_response, vec![])); - let actual_response = - payments::payments_core::( - state, - merchant_account, - key_store, - payments::PaymentCreate, - req, - services::AuthFlow::Merchant, - payments::CallConnectorAction::Trigger, - None, - api::HeaderPayload::default(), - ) - .await - .unwrap(); + let actual_response = Box::pin(payments::payments_core::< + api::Authorize, + api::PaymentsResponse, + _, + _, + _, + Oss, + >( + state, + merchant_account, + key_store, + payments::PaymentCreate, + req, + services::AuthFlow::Merchant, + payments::CallConnectorAction::Trigger, + None, + api::HeaderPayload::default(), + )) + .await + .unwrap(); assert_eq!(expected_response, actual_response); } @@ -531,19 +537,25 @@ async fn payments_create_core_adyen_no_redirect() { }, vec![], )); - let actual_response = - payments::payments_core::( - state, - merchant_account, - key_store, - payments::PaymentCreate, - req, - services::AuthFlow::Merchant, - payments::CallConnectorAction::Trigger, - None, - api::HeaderPayload::default(), - ) - .await - .unwrap(); + let actual_response = Box::pin(payments::payments_core::< + api::Authorize, + api::PaymentsResponse, + _, + _, + _, + Oss, + >( + state, + merchant_account, + key_store, + payments::PaymentCreate, + req, + services::AuthFlow::Merchant, + payments::CallConnectorAction::Trigger, + None, + api::HeaderPayload::default(), + )) + .await + .unwrap(); assert_eq!(expected_response, actual_response); } diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index ed8827a910be..5d4ca844061f 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -120,7 +120,7 @@ async fn payments_create_core() { }; let expected_response = services::ApplicationResponse::JsonWithHeaders((expected_response, vec![])); - let actual_response = router::core::payments::payments_core::< + let actual_response = Box::pin(router::core::payments::payments_core::< api::Authorize, api::PaymentsResponse, _, @@ -137,7 +137,7 @@ async fn payments_create_core() { payments::CallConnectorAction::Trigger, None, api::HeaderPayload::default(), - ) + )) .await .unwrap(); assert_eq!(expected_response, actual_response); @@ -299,7 +299,7 @@ async fn payments_create_core_adyen_no_redirect() { }, vec![], )); - let actual_response = router::core::payments::payments_core::< + let actual_response = Box::pin(router::core::payments::payments_core::< api::Authorize, api::PaymentsResponse, _, @@ -316,7 +316,7 @@ async fn payments_create_core_adyen_no_redirect() { payments::CallConnectorAction::Trigger, None, api::HeaderPayload::default(), - ) + )) .await .unwrap(); assert_eq!(expected_response, actual_response); diff --git a/crates/router/tests/payouts.rs b/crates/router/tests/payouts.rs index 566930cd4e31..ab0bc891a7cc 100644 --- a/crates/router/tests/payouts.rs +++ b/crates/router/tests/payouts.rs @@ -4,7 +4,7 @@ mod utils; #[actix_web::test] async fn payouts_todo() { - utils::setup().await; + Box::pin(utils::setup()).await; let client = awc::Client::default(); let mut response; diff --git a/crates/router/tests/refunds.rs b/crates/router/tests/refunds.rs index c9e08d223503..6b9dfd5ed4a2 100644 --- a/crates/router/tests/refunds.rs +++ b/crates/router/tests/refunds.rs @@ -11,7 +11,7 @@ mod utils; #[actix_web::test] // verify the API-KEY/merchant id has stripe as first choice async fn refund_create_fail_stripe() { - let app = mk_service().await; + let app = Box::pin(mk_service()).await; let client = AppClient::guest(); let user_client = client.user("321"); @@ -25,7 +25,7 @@ async fn refund_create_fail_stripe() { #[actix_web::test] // verify the API-KEY/merchant id has adyen as first choice async fn refund_create_fail_adyen() { - let app = mk_service().await; + let app = Box::pin(mk_service()).await; let client = AppClient::guest(); let user_client = client.user("321"); @@ -39,7 +39,7 @@ async fn refund_create_fail_adyen() { #[actix_web::test] #[ignore] async fn refunds_todo() { - utils::setup().await; + Box::pin(utils::setup()).await; let client = awc::Client::default(); let mut response; diff --git a/crates/router/tests/services.rs b/crates/router/tests/services.rs index 64f1c3d8ee1b..eff7fe7f8738 100644 --- a/crates/router/tests/services.rs +++ b/crates/router/tests/services.rs @@ -10,8 +10,12 @@ async fn get_redis_conn_failure() { // Arrange utils::setup().await; let (tx, _) = tokio::sync::oneshot::channel(); - let state = - routes::AppState::new(Settings::default(), tx, Box::new(services::MockApiClient)).await; + let state = Box::pin(routes::AppState::new( + Settings::default(), + tx, + Box::new(services::MockApiClient), + )) + .await; let _ = state.store.get_redis_conn().map(|conn| { conn.is_redis_available @@ -28,10 +32,14 @@ async fn get_redis_conn_failure() { #[tokio::test] async fn get_redis_conn_success() { // Arrange - utils::setup().await; + Box::pin(utils::setup()).await; let (tx, _) = tokio::sync::oneshot::channel(); - let state = - routes::AppState::new(Settings::default(), tx, Box::new(services::MockApiClient)).await; + let state = Box::pin(routes::AppState::new( + Settings::default(), + tx, + Box::new(services::MockApiClient), + )) + .await; // Act let result = state.store.get_redis_conn(); diff --git a/crates/router/tests/utils.rs b/crates/router/tests/utils.rs index 274c011df7a0..6cddbc043662 100644 --- a/crates/router/tests/utils.rs +++ b/crates/router/tests/utils.rs @@ -20,7 +20,7 @@ static SERVER: OnceCell = OnceCell::const_new(); async fn spawn_server() -> bool { let conf = Settings::new().expect("invalid settings"); - let server = router::start_server(conf) + let server = Box::pin(router::start_server(conf)) .await .expect("failed to create server"); @@ -29,7 +29,7 @@ async fn spawn_server() -> bool { } pub async fn setup() { - SERVER.get_or_init(spawn_server).await; + Box::pin(SERVER.get_or_init(spawn_server)).await; } const STRIPE_MOCK: &str = "http://localhost:12111/"; diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs index 9cd678083959..3bfd1ef7d9f8 100644 --- a/crates/router_env/src/logger/types.rs +++ b/crates/router_env/src/logger/types.rs @@ -235,6 +235,8 @@ pub enum Flow { BusinessProfileList, /// Different verification flows Verification, + /// Rust locker migration + RustLockerMigration, /// Gsm Rule Creation flow GsmRuleCreate, /// Gsm Rule Retrieve flow diff --git a/crates/storage_impl/Cargo.toml b/crates/storage_impl/Cargo.toml index 31115e91589f..77589cc7d782 100644 --- a/crates/storage_impl/Cargo.toml +++ b/crates/storage_impl/Cargo.toml @@ -26,7 +26,7 @@ router_env = { version = "0.1.0", path = "../router_env" } # Third party crates actix-web = "4.3.1" -async-bb8-diesel = "0.1.0" +async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } async-trait = "0.1.72" bb8 = "0.8.1" bytes = "1.4.0" diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index 00d8703940c7..dc0dea4bb59c 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use data_models::errors::{StorageError, StorageResult}; -use diesel_models::{self as store}; +use diesel_models as store; use error_stack::ResultExt; use masking::StrongSecret; use redis::{kv_store::RedisConnInterface, RedisStore}; diff --git a/crates/storage_impl/src/lookup.rs b/crates/storage_impl/src/lookup.rs index dbfd77a8d6a0..bd045fedd379 100644 --- a/crates/storage_impl/src/lookup.rs +++ b/crates/storage_impl/src/lookup.rs @@ -135,7 +135,11 @@ impl ReverseLookupInterface for KVRouterStore { .try_into_get() }; - try_redis_get_else_try_database_get(redis_fut, database_call).await + Box::pin(try_redis_get_else_try_database_get( + redis_fut, + database_call, + )) + .await } } } diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs index d34230e2cb49..3d00e2f2bf7a 100644 --- a/crates/storage_impl/src/payments/payment_attempt.rs +++ b/crates/storage_impl/src/payments/payment_attempt.rs @@ -557,12 +557,12 @@ impl PaymentAttemptInterface for KVRouterStore { .await?; let key = &lookup.pk_id; - try_redis_get_else_try_database_get( + Box::pin(try_redis_get_else_try_database_get( async { kv_wrapper(self, KvOperation::::HGet(&lookup.sk_id), key).await?.try_into_hget() }, || async {self.router_store.find_payment_attempt_by_connector_transaction_id_payment_id_merchant_id(connector_transaction_id, payment_id, merchant_id, storage_scheme).await}, - ) + )) .await } } @@ -607,7 +607,11 @@ impl PaymentAttemptInterface for KVRouterStore { )) }) }; - try_redis_get_else_try_database_get(redis_fut, database_call).await + Box::pin(try_redis_get_else_try_database_get( + redis_fut, + database_call, + )) + .await } } } @@ -635,7 +639,7 @@ impl PaymentAttemptInterface for KVRouterStore { .await?; let key = &lookup.pk_id; - try_redis_get_else_try_database_get( + Box::pin(try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -654,7 +658,7 @@ impl PaymentAttemptInterface for KVRouterStore { ) .await }, - ) + )) .await } } @@ -682,7 +686,7 @@ impl PaymentAttemptInterface for KVRouterStore { MerchantStorageScheme::RedisKv => { let key = format!("mid_{merchant_id}_pid_{payment_id}"); let field = format!("pa_{attempt_id}"); - try_redis_get_else_try_database_get( + Box::pin(try_redis_get_else_try_database_get( async { kv_wrapper(self, KvOperation::::HGet(&field), key) .await? @@ -698,7 +702,7 @@ impl PaymentAttemptInterface for KVRouterStore { ) .await }, - ) + )) .await } } @@ -726,7 +730,7 @@ impl PaymentAttemptInterface for KVRouterStore { .get_lookup_by_lookup_id(&lookup_id, storage_scheme) .await?; let key = &lookup.pk_id; - try_redis_get_else_try_database_get( + Box::pin(try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -745,7 +749,7 @@ impl PaymentAttemptInterface for KVRouterStore { ) .await }, - ) + )) .await } } @@ -774,7 +778,7 @@ impl PaymentAttemptInterface for KVRouterStore { .await?; let key = &lookup.pk_id; - try_redis_get_else_try_database_get( + Box::pin(try_redis_get_else_try_database_get( async { kv_wrapper( self, @@ -793,7 +797,7 @@ impl PaymentAttemptInterface for KVRouterStore { ) .await }, - ) + )) .await } } diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 2dc5cdd1c026..c3b3d22ffe35 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -39,7 +39,7 @@ use crate::connection; use crate::{ diesel_error_to_data_error, redis::kv_store::{kv_wrapper, KvOperation}, - utils::{pg_connection_read, pg_connection_write}, + utils::{self, pg_connection_read, pg_connection_write}, DataModelExt, DatabaseStore, KVRouterStore, }; @@ -206,7 +206,7 @@ impl PaymentIntentInterface for KVRouterStore { MerchantStorageScheme::RedisKv => { let key = format!("mid_{merchant_id}_pid_{payment_id}"); let field = format!("pi_{payment_id}"); - crate::utils::try_redis_get_else_try_database_get( + Box::pin(utils::try_redis_get_else_try_database_get( async { kv_wrapper::( self, @@ -217,7 +217,7 @@ impl PaymentIntentInterface for KVRouterStore { .try_into_hget() }, database_call, - ) + )) .await } } diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index f70fc656d8e3..2fb729fb7b90 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -30,6 +30,7 @@ jwt_secret = "secret" [locker] host = "" +host_rs = "" mock_locker = true basilisk_host = "" @@ -48,6 +49,7 @@ locker_encryption_key2 = "" locker_decryption_key1 = "" locker_decryption_key2 = "" vault_encryption_key = "" +rust_locker_encryption_key = "" vault_private_key = "" [webhooks] diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 23f8f1b3628b..9fddde01b49a 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -745,6 +745,166 @@ ] } }, + "/gsm": { + "post": { + "tags": [ + "Gsm" + ], + "summary": "Gsm - Create", + "description": "Gsm - Create\n\nTo create a Gsm Rule", + "operationId": "Create Gsm Rule", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmCreateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Gsm created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmResponse" + } + } + } + }, + "400": { + "description": "Missing Mandatory fields" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, + "/gsm/delete": { + "post": { + "tags": [ + "Gsm" + ], + "summary": "Gsm - Delete", + "description": "Gsm - Delete\n\nTo delete a Gsm Rule", + "operationId": "Delete Gsm Rule", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmDeleteRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Gsm deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmDeleteResponse" + } + } + } + }, + "400": { + "description": "Missing Mandatory fields" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, + "/gsm/get": { + "post": { + "tags": [ + "Gsm" + ], + "summary": "Gsm - Get", + "description": "Gsm - Get\n\nTo get a Gsm Rule", + "operationId": "Retrieve Gsm Rule", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmRetrieveRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Gsm retrieved", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmResponse" + } + } + } + }, + "400": { + "description": "Missing Mandatory fields" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, + "/gsm/update": { + "post": { + "tags": [ + "Gsm" + ], + "summary": "Gsm - Update", + "description": "Gsm - Update\n\nTo update a Gsm Rule", + "operationId": "Update Gsm Rule", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmUpdateRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Gsm updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GsmResponse" + } + } + } + }, + "400": { + "description": "Missing Mandatory fields" + } + }, + "security": [ + { + "admin_api_key": [] + } + ] + } + }, "/mandates/revoke/{mandate_id}": { "post": { "tags": [ @@ -3878,6 +4038,17 @@ "type": "object" } } + }, + { + "type": "object", + "required": [ + "card_redirect" + ], + "properties": { + "card_redirect": { + "type": "object" + } + } } ] }, @@ -3899,6 +4070,7 @@ "airwallex", "authorizedotnet", "bambora", + "bankofamerica", "bitpay", "bluesnap", "boku", @@ -3928,6 +4100,7 @@ "paypal", "payu", "powertranz", + "prophetpay", "rapyd", "shift4", "square", @@ -5713,6 +5886,228 @@ } } }, + "GsmCreateRequest": { + "type": "object", + "required": [ + "connector", + "flow", + "sub_flow", + "code", + "message", + "status", + "decision", + "step_up_possible" + ], + "properties": { + "connector": { + "$ref": "#/components/schemas/Connector" + }, + "flow": { + "type": "string" + }, + "sub_flow": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + }, + "router_error": { + "type": "string", + "nullable": true + }, + "decision": { + "$ref": "#/components/schemas/GsmDecision" + }, + "step_up_possible": { + "type": "boolean" + } + } + }, + "GsmDecision": { + "type": "string", + "enum": [ + "retry", + "requeue", + "do_default" + ] + }, + "GsmDeleteRequest": { + "type": "object", + "required": [ + "connector", + "flow", + "sub_flow", + "code", + "message" + ], + "properties": { + "connector": { + "type": "string" + }, + "flow": { + "type": "string" + }, + "sub_flow": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "GsmDeleteResponse": { + "type": "object", + "required": [ + "gsm_rule_delete", + "connector", + "flow", + "sub_flow", + "code" + ], + "properties": { + "gsm_rule_delete": { + "type": "boolean" + }, + "connector": { + "type": "string" + }, + "flow": { + "type": "string" + }, + "sub_flow": { + "type": "string" + }, + "code": { + "type": "string" + } + } + }, + "GsmResponse": { + "type": "object", + "required": [ + "connector", + "flow", + "sub_flow", + "code", + "message", + "status", + "decision", + "step_up_possible" + ], + "properties": { + "connector": { + "type": "string" + }, + "flow": { + "type": "string" + }, + "sub_flow": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + }, + "router_error": { + "type": "string", + "nullable": true + }, + "decision": { + "type": "string" + }, + "step_up_possible": { + "type": "boolean" + } + } + }, + "GsmRetrieveRequest": { + "type": "object", + "required": [ + "connector", + "flow", + "sub_flow", + "code", + "message" + ], + "properties": { + "connector": { + "$ref": "#/components/schemas/Connector" + }, + "flow": { + "type": "string" + }, + "sub_flow": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "GsmUpdateRequest": { + "type": "object", + "required": [ + "connector", + "flow", + "sub_flow", + "code", + "message" + ], + "properties": { + "connector": { + "type": "string" + }, + "flow": { + "type": "string" + }, + "sub_flow": { + "type": "string" + }, + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "status": { + "type": "string", + "nullable": true + }, + "router_error": { + "type": "string", + "nullable": true + }, + "decision": { + "allOf": [ + { + "$ref": "#/components/schemas/GsmDecision" + } + ], + "nullable": true + }, + "step_up_possible": { + "type": "boolean", + "nullable": true + } + } + }, "IndomaretVoucherData": { "type": "object", "required": [ @@ -8393,6 +8788,7 @@ "bca_bank_transfer", "bni_va", "bri_va", + "card_redirect", "cimb_va", "classic", "credit", diff --git a/postman/collection-dir/bankofamerica/.auth.json b/postman/collection-dir/bankofamerica/.auth.json new file mode 100644 index 000000000000..915a28357900 --- /dev/null +++ b/postman/collection-dir/bankofamerica/.auth.json @@ -0,0 +1,22 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/.event.meta.json b/postman/collection-dir/bankofamerica/.event.meta.json new file mode 100644 index 000000000000..2df9d47d936d --- /dev/null +++ b/postman/collection-dir/bankofamerica/.event.meta.json @@ -0,0 +1,6 @@ +{ + "eventOrder": [ + "event.prerequest.js", + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/.info.json b/postman/collection-dir/bankofamerica/.info.json new file mode 100644 index 000000000000..2a1b8f809c01 --- /dev/null +++ b/postman/collection-dir/bankofamerica/.info.json @@ -0,0 +1,9 @@ +{ + "info": { + "_postman_id": "646f7167-da26-4a24-adb0-4157fd3a1781", + "name": "bankofamerica", + "description": "## Get started\n\nJuspay Router provides a collection of APIs that enable you to process and manage payments. Our APIs accept and return JSON in the HTTP body, and return standard HTTP response codes. \nYou can consume the APIs directly using your favorite HTTP/REST library. \nWe have a testing environment referred to \"sandbox\", which you can setup to test API calls without affecting production data.\n\n### Base URLs\n\nUse the following base URLs when making requests to the APIs:\n\n| Environment | Base URL |\n| --- | --- |\n| Sandbox | [https://sandbox.hyperswitch.io](https://sandbox.hyperswitch.io) |\n| Production | [https://router.juspay.io](https://router.juspay.io) |\n\n# Authentication\n\nWhen you sign up for an account, you are given a secret key (also referred as api-key). You may authenticate all API requests with Juspay server by providing the appropriate key in the request Authorization header. \nNever share your secret api keys. Keep them guarded and secure.\n\nContact Support: \nName: Juspay Support \nEmail: [support@juspay.in](mailto:support@juspay.in)", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "28305597" + } +} diff --git a/postman/collection-dir/bankofamerica/.meta.json b/postman/collection-dir/bankofamerica/.meta.json new file mode 100644 index 000000000000..e578098f721e --- /dev/null +++ b/postman/collection-dir/bankofamerica/.meta.json @@ -0,0 +1,9 @@ +{ + "childrenOrder": [ + "Health check", + "MerchantAccounts", + "API Key", + "PaymentConnectors", + "Flow Testcases" + ] +} diff --git a/postman/collection-dir/bankofamerica/.variable.json b/postman/collection-dir/bankofamerica/.variable.json new file mode 100644 index 000000000000..492c3b7ed0cb --- /dev/null +++ b/postman/collection-dir/bankofamerica/.variable.json @@ -0,0 +1,100 @@ +{ + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + }, + { + "key": "admin_api_key", + "value": "", + "type": "string" + }, + { + "key": "api_key", + "value": "", + "type": "string" + }, + { + "key": "merchant_id", + "value": "" + }, + { + "key": "payment_id", + "value": "" + }, + { + "key": "customer_id", + "value": "" + }, + { + "key": "mandate_id", + "value": "" + }, + { + "key": "payment_method_id", + "value": "" + }, + { + "key": "refund_id", + "value": "" + }, + { + "key": "merchant_connector_id", + "value": "" + }, + { + "key": "client_secret", + "value": "", + "type": "string" + }, + { + "key": "connector_api_key", + "value": "", + "type": "string" + }, + { + "key": "publishable_key", + "value": "", + "type": "string" + }, + { + "key": "api_key_id", + "value": "", + "type": "string" + }, + { + "key": "payment_token", + "value": "" + }, + { + "key": "gateway_merchant_id", + "value": "", + "type": "string" + }, + { + "key": "certificate", + "value": "", + "type": "string" + }, + { + "key": "certificate_keys", + "value": "", + "type": "string" + }, + { + "key": "organization_id", + "value": "" + }, + { + "key": "connector_api_secret", + "value": "", + "type": "string" + }, + { + "key": "connector_key1", + "value": "", + "type": "string" + } + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/.meta.json b/postman/collection-dir/bankofamerica/API Key/.meta.json new file mode 100644 index 000000000000..0388c2d61b4b --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/.meta.json @@ -0,0 +1,9 @@ +{ + "childrenOrder": [ + "Create API Key", + "Update API Key", + "Retrieve API Key", + "List API Keys", + "Delete API Key" + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/Create API Key/.event.meta.json b/postman/collection-dir/bankofamerica/API Key/Create API Key/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Create API Key/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/Create API Key/event.test.js b/postman/collection-dir/bankofamerica/API Key/Create API Key/event.test.js new file mode 100644 index 000000000000..4e27c5a50253 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Create API Key/event.test.js @@ -0,0 +1,46 @@ +// Validate status 2xx +pm.test("[POST]::/api_keys/:merchant_id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/api_keys/:merchant_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id +if (jsonData?.key_id) { + pm.collectionVariables.set("api_key_id", jsonData.key_id); + console.log( + "- use {{api_key_id}} as collection variable for value", + jsonData.key_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/API Key/Create API Key/request.json b/postman/collection-dir/bankofamerica/API Key/Create API Key/request.json new file mode 100644 index 000000000000..4e4c66284978 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Create API Key/request.json @@ -0,0 +1,52 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw_json_formatted": { + "name": "API Key 1", + "description": null, + "expiration": "2069-09-23T01:02:03.000Z" + } + }, + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/API Key/Create API Key/response.json b/postman/collection-dir/bankofamerica/API Key/Create API Key/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Create API Key/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/API Key/Delete API Key/.event.meta.json b/postman/collection-dir/bankofamerica/API Key/Delete API Key/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Delete API Key/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/Delete API Key/event.test.js b/postman/collection-dir/bankofamerica/API Key/Delete API Key/event.test.js new file mode 100644 index 000000000000..bed2232f1a37 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Delete API Key/event.test.js @@ -0,0 +1,17 @@ +// Validate status 2xx +pm.test( + "[DELETE]::/api_keys/:merchant_id/:api-key - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[DELETE]::/api_keys/:merchant_id/:api-key - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); diff --git a/postman/collection-dir/bankofamerica/API Key/Delete API Key/request.json b/postman/collection-dir/bankofamerica/API Key/Delete API Key/request.json new file mode 100644 index 000000000000..a83d12a2bebc --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Delete API Key/request.json @@ -0,0 +1,49 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/:api-key", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + ":api-key" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + }, + { + "key": "api-key", + "value": "{{api_key_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/API Key/Delete API Key/response.json b/postman/collection-dir/bankofamerica/API Key/Delete API Key/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Delete API Key/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/API Key/List API Keys/.event.meta.json b/postman/collection-dir/bankofamerica/API Key/List API Keys/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/List API Keys/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/List API Keys/event.test.js b/postman/collection-dir/bankofamerica/API Key/List API Keys/event.test.js new file mode 100644 index 000000000000..c6cbb8742e2c --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/List API Keys/event.test.js @@ -0,0 +1,46 @@ +// Validate status 2xx +pm.test("[GET]::/api_keys/:merchant_id/list - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[GET]::/api_keys/:merchant_id/list - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id +if (jsonData?.key_id) { + pm.collectionVariables.set("api_key_id", jsonData.key_id); + console.log( + "- use {{api_key_id}} as collection variable for value", + jsonData.key_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/API Key/List API Keys/request.json b/postman/collection-dir/bankofamerica/API Key/List API Keys/request.json new file mode 100644 index 000000000000..86d12e8c7418 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/List API Keys/request.json @@ -0,0 +1,45 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + "list" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/API Key/List API Keys/response.json b/postman/collection-dir/bankofamerica/API Key/List API Keys/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/List API Keys/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/.event.meta.json b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/event.test.js b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/event.test.js new file mode 100644 index 000000000000..bef13cc35779 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/event.test.js @@ -0,0 +1,49 @@ +// Validate status 2xx +pm.test( + "[GET]::/api_keys/:merchant_id/:api_key_id - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[GET]::/api_keys/:merchant_id/:api_key_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id +if (jsonData?.key_id) { + pm.collectionVariables.set("api_key_id", jsonData.key_id); + console.log( + "- use {{api_key_id}} as collection variable for value", + jsonData.key_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/request.json b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/request.json new file mode 100644 index 000000000000..958049e90879 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/request.json @@ -0,0 +1,49 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/:api_key_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + ":api_key_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + }, + { + "key": "api_key_id", + "value": "{{api_key_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/response.json b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Retrieve API Key/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/API Key/Update API Key/.event.meta.json b/postman/collection-dir/bankofamerica/API Key/Update API Key/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Update API Key/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/API Key/Update API Key/event.test.js b/postman/collection-dir/bankofamerica/API Key/Update API Key/event.test.js new file mode 100644 index 000000000000..fd6ed957bdff --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Update API Key/event.test.js @@ -0,0 +1,49 @@ +// Validate status 2xx +pm.test( + "[POST]::/api_keys/:merchant_id/:api_key_id - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/api_keys/:merchant_id/:api_key_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id +if (jsonData?.key_id) { + pm.collectionVariables.set("api_key_id", jsonData.key_id); + console.log( + "- use {{api_key_id}} as collection variable for value", + jsonData.key_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/API Key/Update API Key/request.json b/postman/collection-dir/bankofamerica/API Key/Update API Key/request.json new file mode 100644 index 000000000000..af2f450969b6 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Update API Key/request.json @@ -0,0 +1,57 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw_json_formatted": { + "name": null, + "description": "My very awesome API key", + "expiration": null + } + }, + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/:api_key_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + ":api_key_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + }, + { + "key": "api_key_id", + "value": "{{api_key_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/API Key/Update API Key/response.json b/postman/collection-dir/bankofamerica/API Key/Update API Key/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/API Key/Update API Key/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/.meta.json new file mode 100644 index 000000000000..bd972090b19e --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/.meta.json @@ -0,0 +1,6 @@ +{ + "childrenOrder": [ + "QuickStart", + "Happy Cases" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/.meta.json new file mode 100644 index 000000000000..d85baac8fbe6 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/.meta.json @@ -0,0 +1,9 @@ +{ + "childrenOrder": [ + "Scenario1-Create payment with confirm true", + "Scenario2-Create payment with confirm false", + "Scenario3-Create payment without PMD", + "Scenario4-Create payment with Manual capture", + "Scenario5-Void the payment" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/.meta.json new file mode 100644 index 000000000000..60051ecca220 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/.meta.json @@ -0,0 +1,6 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js new file mode 100644 index 000000000000..ac3f862e43f4 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/event.test.js @@ -0,0 +1,80 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "processing" for "status" because payment gets succeeded after one day. +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} + +// Response body should have "connector_transaction_id" +pm.test( + "[POST]::/payments - Content check if 'connector_transaction_id' exists", + function () { + pm.expect(typeof jsonData.connector_transaction_id !== "undefined").to.be + .true; + }, +); diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json new file mode 100644 index 000000000000..21f054843897 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json @@ -0,0 +1,98 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": true, + "business_country": "US", + "business_label": "default", + "capture_method": "automatic", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 1, + "customer_id": "bernard123", + "email": "guest@example.com", + "name": "John Doe", + "phone": "999999999", + "phone_country_code": "+65", + "description": "Its my first payment request", + "authentication_type": "no_three_ds", + "return_url": "https://duck.com", + "setup_future_usage": "on_session", + "payment_method": "card", + "payment_method_type": "debit", + "payment_method_data": { + "card": { + "card_number": "4242424242424242", + "card_exp_month": "01", + "card_exp_year": "24", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + }, + "routing": { + "type": "single", + "data": "stripe" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..a6976d95f69e --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/event.test.js @@ -0,0 +1,80 @@ +// Validate status 2xx +// pm.test("[GET]::/payments/:id - Status code is 2xx", function () { +// pm.response.to.be.success; +// }); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments/:id - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} + +// Response body should have "connector_transaction_id" +// pm.test( +// "[POST]::/payments - Content check if 'connector_transaction_id' exists", +// function () { +// pm.expect(typeof jsonData.connector_transaction_id !== "undefined").to.be +// .true; +// }, +// ); diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/request.json new file mode 100644 index 000000000000..b9ebc1be4aa3 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/request.json @@ -0,0 +1,33 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/.meta.json new file mode 100644 index 000000000000..57d3f8e2bc7e --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/.meta.json @@ -0,0 +1,7 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Confirm", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/.event.meta.json new file mode 100644 index 000000000000..4ac527d834af --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/.event.meta.json @@ -0,0 +1,6 @@ +{ + "eventOrder": [ + "event.test.js", + "event.prerequest.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.prerequest.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.prerequest.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js new file mode 100644 index 000000000000..b160ad9dc04b --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/event.test.js @@ -0,0 +1,103 @@ +// Validate status 2xx +pm.test("[POST]::/payments/:id/confirm - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/payments/:id/confirm - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Validate if response has JSON Body +pm.test("[POST]::/payments/:id/confirm - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "6540" for "amount" +if (jsonData?.amount) { + pm.test( + "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", + function () { + pm.expect(jsonData.amount).to.eql(6540); + }, + ); +} + +// Response body should have value "6540" for "amount_capturable" +if (jsonData?.amount) { + pm.test( + "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 0'", + function () { + pm.expect(jsonData.amount_capturable).to.eql(0); + }, + ); +} + +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} + +// Response body should have "connector_transaction_id" +pm.test( + "[POST]::/payments - Content check if 'connector_transaction_id' exists", + function () { + pm.expect(typeof jsonData.connector_transaction_id !== "undefined").to.be + .true; + }, +); diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json new file mode 100644 index 000000000000..16f6e13983f8 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/request.json @@ -0,0 +1,63 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{publishable_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "client_secret": "{{client_secret}}" + } + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/confirm", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "confirm" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Confirm/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/event.test.js new file mode 100644 index 000000000000..55dc35b91280 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "requires_confirmation" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'requires_confirmation'", + function () { + pm.expect(jsonData.status).to.eql("requires_confirmation"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json new file mode 100644 index 000000000000..b1d5ad5ebbf8 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json @@ -0,0 +1,98 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": false, + "business_country": "US", + "business_label": "default", + "capture_method": "automatic", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 1, + "customer_id": "bernard123", + "email": "guest@example.com", + "name": "John Doe", + "phone": "999999999", + "phone_country_code": "+65", + "description": "Its my first payment request", + "authentication_type": "no_three_ds", + "return_url": "https://duck.com", + "setup_future_usage": "on_session", + "payment_method": "card", + "payment_method_type": "debit", + "payment_method_data": { + "card": { + "card_number": "4242424242424242", + "card_exp_month": "01", + "card_exp_year": "24", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + }, + "routing": { + "type": "single", + "data": "stripe" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..f87069589f0a --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/event.test.js @@ -0,0 +1,91 @@ +// Validate status 2xx +// pm.test("[GET]::/payments/:id - Status code is 2xx", function () { +// pm.response.to.be.success; +// }); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments:id - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} + +// Response body should have value "6540" for "amount" +if (jsonData?.amount) { + pm.test( + "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", + function () { + pm.expect(jsonData.amount).to.eql(6540); + }, + ); +} + +// Response body should have value "6540" for "amount_capturable" +if (jsonData?.amount) { + pm.test( + "[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 0'", + function () { + pm.expect(jsonData.amount_capturable).to.eql(0); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/request.json new file mode 100644 index 000000000000..b9ebc1be4aa3 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/request.json @@ -0,0 +1,33 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/.meta.json new file mode 100644 index 000000000000..57d3f8e2bc7e --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/.meta.json @@ -0,0 +1,7 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Confirm", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/.event.meta.json new file mode 100644 index 000000000000..4ac527d834af --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/.event.meta.json @@ -0,0 +1,6 @@ +{ + "eventOrder": [ + "event.test.js", + "event.prerequest.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.prerequest.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.prerequest.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js new file mode 100644 index 000000000000..255743af78c7 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/event.test.js @@ -0,0 +1,73 @@ +// Validate status 2xx +pm.test("[POST]::/payments/:id/confirm - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/payments/:id/confirm - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Validate if response has JSON Body +pm.test("[POST]::/payments/:id/confirm - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments:id/confirm - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json new file mode 100644 index 000000000000..8ac0a623f77d --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/request.json @@ -0,0 +1,73 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{publishable_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "payment_method": "card", + "payment_method_data": { + "card": { + "card_number": "4242424242424242", + "card_exp_month": "10", + "card_exp_year": "25", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "client_secret": "{{client_secret}}" + } + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/confirm", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "confirm" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Confirm/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/event.test.js new file mode 100644 index 000000000000..0444324000a6 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "requires_payment_method" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'", + function () { + pm.expect(jsonData.status).to.eql("requires_payment_method"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json new file mode 100644 index 000000000000..71cc91069581 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json @@ -0,0 +1,84 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": false, + "capture_method": "automatic", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 6540, + "customer_id": "StripeCustomer", + "email": "guest@example.com", + "name": "John Doe", + "phone": "999999999", + "phone_country_code": "+65", + "description": "Its my first payment request", + "authentication_type": "no_three_ds", + "return_url": "https://duck.com", + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "abcd" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "abcd" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + }, + "routing": { + "type": "single", + "data": "bankofamerica" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..4fbefdb8494a --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +// pm.test("[GET]::/payments/:id - Status code is 2xx", function () { +// pm.response.to.be.success; +// }); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments:id - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/request.json new file mode 100644 index 000000000000..b9ebc1be4aa3 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/request.json @@ -0,0 +1,33 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/.meta.json new file mode 100644 index 000000000000..e4ef30e39e8d --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/.meta.json @@ -0,0 +1,7 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Capture", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js new file mode 100644 index 000000000000..fa6deebe16a8 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js @@ -0,0 +1,94 @@ +// Validate status 2xx +pm.test("[POST]::/payments/:id/capture - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/payments/:id/capture - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Validate if response has JSON Body +pm.test("[POST]::/payments/:id/capture - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} + +// Response body should have value "6540" for "amount" +if (jsonData?.amount) { + pm.test( + "[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'", + function () { + pm.expect(jsonData.amount).to.eql(6540); + }, + ); +} + +// Response body should have value "6000" for "amount_received" +if (jsonData?.amount_received) { + pm.test( + "[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'", + function () { + pm.expect(jsonData.amount_received).to.eql(6000); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json new file mode 100644 index 000000000000..8975575ca40e --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json @@ -0,0 +1,45 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount_to_capture": 6000, + "statement_descriptor_name": "Joseph", + "statement_descriptor_suffix": "JS" + } + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/capture", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "capture" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To capture the funds for an uncaptured payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/event.test.js new file mode 100644 index 000000000000..d683186aa007 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "requires_capture" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'requires_capture'", + function () { + pm.expect(jsonData.status).to.eql("requires_capture"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json new file mode 100644 index 000000000000..5e3ff0e70ad2 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json @@ -0,0 +1,98 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": true, + "business_country": "US", + "business_label": "default", + "capture_method": "manual", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 1, + "customer_id": "bernard123", + "email": "guest@example.com", + "name": "John Doe", + "phone": "999999999", + "phone_country_code": "+65", + "description": "Its my first payment request", + "authentication_type": "no_three_ds", + "return_url": "https://duck.com", + "setup_future_usage": "on_session", + "payment_method": "card", + "payment_method_type": "debit", + "payment_method_data": { + "card": { + "card_number": "3566111111111113", + "card_exp_month": "12", + "card_exp_year": "30", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + }, + "routing": { + "type": "single", + "data": "stripe" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..b1b53a360e32 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +// pm.test("[GET]::/payments/:id - Status code is 2xx", function () { +// pm.response.to.be.success; +// }); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "processing" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'processing'", + function () { + pm.expect(jsonData.status).to.eql("processing"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/request.json new file mode 100644 index 000000000000..b9ebc1be4aa3 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/request.json @@ -0,0 +1,33 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/.meta.json new file mode 100644 index 000000000000..14bab2fbd260 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/.meta.json @@ -0,0 +1,7 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Cancel", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/event.test.js new file mode 100644 index 000000000000..dcf3f1916430 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/event.test.js @@ -0,0 +1,61 @@ +// Validate status 2xx +pm.test("[POST]::/payments/:id/cancel - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/payments/:id/cancel - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Validate if response has JSON Body +pm.test("[POST]::/payments/:id/cancel - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "cancelled" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments/:id/cancel - Content check if value for 'status' matches 'cancelled'", + function () { + pm.expect(jsonData.status).to.eql("cancelled"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/request.json new file mode 100644 index 000000000000..f64e37a125a2 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/request.json @@ -0,0 +1,43 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "cancellation_reason": "requested_by_customer" + } + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/cancel", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "cancel" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "A Payment could can be cancelled when it is in one of these statuses: requires_payment_method, requires_capture, requires_confirmation, requires_customer_action" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Cancel/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/event.test.js new file mode 100644 index 000000000000..d683186aa007 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "requires_capture" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'requires_capture'", + function () { + pm.expect(jsonData.status).to.eql("requires_capture"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json new file mode 100644 index 000000000000..5e3ff0e70ad2 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json @@ -0,0 +1,98 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": true, + "business_country": "US", + "business_label": "default", + "capture_method": "manual", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 1, + "customer_id": "bernard123", + "email": "guest@example.com", + "name": "John Doe", + "phone": "999999999", + "phone_country_code": "+65", + "description": "Its my first payment request", + "authentication_type": "no_three_ds", + "return_url": "https://duck.com", + "setup_future_usage": "on_session", + "payment_method": "card", + "payment_method_type": "debit", + "payment_method_data": { + "card": { + "card_number": "3566111111111113", + "card_exp_month": "12", + "card_exp_year": "30", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "sundari", + "last_name": "sundari" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + }, + "routing": { + "type": "single", + "data": "stripe" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..5e52e13a59e1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/event.test.js @@ -0,0 +1,71 @@ +// Validate status 2xx +pm.test("[GET]::/payments/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} + +// Response body should have value "cancelled" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments/:id - Content check if value for 'status' matches 'cancelled'", + function () { + pm.expect(jsonData.status).to.eql("cancelled"); + }, + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/request.json new file mode 100644 index 000000000000..b9ebc1be4aa3 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/request.json @@ -0,0 +1,33 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/.meta.json new file mode 100644 index 000000000000..e3596ba357bc --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/.meta.json @@ -0,0 +1,9 @@ +{ + "childrenOrder": [ + "Merchant Account - Create", + "API Key - Create", + "Payment Connector - Create", + "Payments - Create", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/event.test.js new file mode 100644 index 000000000000..4e27c5a50253 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/event.test.js @@ -0,0 +1,46 @@ +// Validate status 2xx +pm.test("[POST]::/api_keys/:merchant_id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/api_keys/:merchant_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id +if (jsonData?.key_id) { + pm.collectionVariables.set("api_key_id", jsonData.key_id); + console.log( + "- use {{api_key_id}} as collection variable for value", + jsonData.key_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/request.json new file mode 100644 index 000000000000..4e4c66284978 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/request.json @@ -0,0 +1,52 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw_json_formatted": { + "name": "API Key 1", + "description": null, + "expiration": "2069-09-23T01:02:03.000Z" + } + }, + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/API Key - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/event.test.js new file mode 100644 index 000000000000..7de0d5beb316 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/event.test.js @@ -0,0 +1,56 @@ +// Validate status 2xx +pm.test("[POST]::/accounts - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/accounts - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set merchant_id as variable for jsonData.merchant_id +if (jsonData?.merchant_id) { + pm.collectionVariables.set("merchant_id", jsonData.merchant_id); + console.log( + "- use {{merchant_id}} as collection variable for value", + jsonData.merchant_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_id}}, as jsonData.merchant_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} + +// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key +if (jsonData?.publishable_key) { + pm.collectionVariables.set("publishable_key", jsonData.publishable_key); + console.log( + "- use {{publishable_key}} as collection variable for value", + jsonData.publishable_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/request.json new file mode 100644 index 000000000000..5313e3e6b486 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/request.json @@ -0,0 +1,95 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "merchant_id": "postman_merchant_GHAction_{{$guid}}", + "locker_id": "m0010", + "merchant_name": "NewAge Retailer", + "merchant_details": { + "primary_contact_person": "John Test", + "primary_email": "JohnTest@test.com", + "primary_phone": "sunt laborum", + "secondary_contact_person": "John Test2", + "secondary_email": "JohnTest2@test.com", + "secondary_phone": "cillum do dolor id", + "website": "www.example.com", + "about_business": "Online Retail with a wide selection of organic products for North America", + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US" + } + }, + "return_url": "https://duck.com/success", + "webhook_details": { + "webhook_version": "1.0.1", + "webhook_username": "ekart_retail", + "webhook_password": "password_ekart@123", + "payment_created_enabled": true, + "payment_succeeded_enabled": true, + "payment_failed_enabled": true + }, + "sub_merchants_enabled": false, + "metadata": { + "city": "NY", + "unit": "245" + }, + "primary_business_details": [ + { + "country": "US", + "business": "default" + } + ] + } + }, + "url": { + "raw": "{{baseUrl}}/accounts", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts" + ] + }, + "description": "Create a new account for a merchant. The merchant could be a seller or retailer or client who likes to receive and send payments." +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Merchant Account - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/event.test.js new file mode 100644 index 000000000000..88e92d8d84a2 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/event.test.js @@ -0,0 +1,39 @@ +// Validate status 2xx +pm.test( + "[POST]::/account/:account_id/connectors - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/account/:account_id/connectors - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id +if (jsonData?.merchant_connector_id) { + pm.collectionVariables.set( + "merchant_connector_id", + jsonData.merchant_connector_id, + ); + console.log( + "- use {{merchant_connector_id}} as collection variable for value", + jsonData.merchant_connector_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/request.json new file mode 100644 index 000000000000..8ab41d88236e --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/request.json @@ -0,0 +1,108 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "connector_type": "fiz_operations", + "connector_name": "bankofamerica", + "business_country": "US", + "business_label": "default", + "connector_label": "first_boa_connector", + "connector_account_details": { + "auth_type": "SignatureKey", + "api_key": "{{connector_api_key}}", + "api_secret": "{{connector_api_secret}}", + "key1": "{{connector_key1}}" + }, + "test_mode": false, + "disabled": false, + "payment_methods_enabled": [ + { + "payment_method": "card", + "payment_method_types": [ + { + "payment_method_type": "credit", + "card_networks": [ + "Visa", + "Mastercard" + ], + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "debit", + "card_networks": [ + "Visa", + "Mastercard" + ], + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + } + ] + } + ], + "metadata": { + "city": "NY", + "unit": "245" + } + } + }, + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Create a new Payment Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialised services like Fraud / Accounting etc." +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payment Connector - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/event.test.js new file mode 100644 index 000000000000..a6947db94c0b --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/event.test.js @@ -0,0 +1,61 @@ +// Validate status 2xx +pm.test("[POST]::/payments - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/payments - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Validate if response has JSON Body +pm.test("[POST]::/payments - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json new file mode 100644 index 000000000000..6af4c897162c --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json @@ -0,0 +1,103 @@ +{ + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "amount": 6540, + "currency": "USD", + "confirm": true, + "capture_method": "automatic", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 6540, + "customer_id": "StripeCustomer", + "email": "guest@example.com", + "name": "John Doe", + "phone": "999999999", + "phone_country_code": "+1", + "description": "Its my first payment request", + "authentication_type": "no_three_ds", + "return_url": "https://duck.com", + "payment_method": "card", + "payment_method_type": "credit", + "payment_method_data": { + "card": { + "card_number": "4111111111111111", + "card_exp_month": "12", + "card_exp_year": "30", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } + }, + "billing": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "joseph", + "last_name": "Doe" + }, + "phone": { + "number": "8056594427", + "country_code": "+91" + } + }, + "shipping": { + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US", + "first_name": "joseph", + "last_name": "Doe" + }, + "phone": { + "number": "8056594427", + "country_code": "+91" + } + }, + "statement_descriptor_name": "joseph", + "statement_descriptor_suffix": "JS", + "metadata": { + "udf1": "value1", + "new_customer": "true", + "login_date": "2019-09-10T10:11:12Z" + }, + "routing": { + "type": "single", + "data": "bankofamerica" + } + } + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..d0a02af74367 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/event.test.js @@ -0,0 +1,61 @@ +// Validate status 2xx +pm.test("[GET]::/payments/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[GET]::/payments/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// Validate if response has JSON Body +pm.test("[GET]::/payments/:id - Response has JSON Body", function () { + pm.response.to.have.jsonBody(); +}); + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log( + "- use {{payment_id}} as collection variable for value", + jsonData.payment_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.", + ); +} + +// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id +if (jsonData?.mandate_id) { + pm.collectionVariables.set("mandate_id", jsonData.mandate_id); + console.log( + "- use {{mandate_id}} as collection variable for value", + jsonData.mandate_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.", + ); +} + +// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret +if (jsonData?.client_secret) { + pm.collectionVariables.set("client_secret", jsonData.client_secret); + console.log( + "- use {{client_secret}} as collection variable for value", + jsonData.client_secret, + ); +} else { + console.log( + "INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/request.json new file mode 100644 index 000000000000..c71774083b2c --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/request.json @@ -0,0 +1,27 @@ +{ + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" +} diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/response.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/Health check/.meta.json b/postman/collection-dir/bankofamerica/Health check/.meta.json new file mode 100644 index 000000000000..66ee7e50cab8 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Health check/.meta.json @@ -0,0 +1,5 @@ +{ + "childrenOrder": [ + "New Request" + ] +} diff --git a/postman/collection-dir/bankofamerica/Health check/New Request/.event.meta.json b/postman/collection-dir/bankofamerica/Health check/New Request/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Health check/New Request/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/Health check/New Request/event.test.js b/postman/collection-dir/bankofamerica/Health check/New Request/event.test.js new file mode 100644 index 000000000000..b490b8be090f --- /dev/null +++ b/postman/collection-dir/bankofamerica/Health check/New Request/event.test.js @@ -0,0 +1,4 @@ +// Validate status 2xx +pm.test("[POST]::/accounts - Status code is 2xx", function () { + pm.response.to.be.success; +}); diff --git a/postman/collection-dir/bankofamerica/Health check/New Request/request.json b/postman/collection-dir/bankofamerica/Health check/New Request/request.json new file mode 100644 index 000000000000..4cc8d4b1a966 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Health check/New Request/request.json @@ -0,0 +1,20 @@ +{ + "method": "GET", + "header": [ + { + "key": "x-feature", + "value": "router-custom", + "type": "text", + "disabled": true + } + ], + "url": { + "raw": "{{baseUrl}}/health", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "health" + ] + } +} diff --git a/postman/collection-dir/bankofamerica/Health check/New Request/response.json b/postman/collection-dir/bankofamerica/Health check/New Request/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/Health check/New Request/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/.meta.json b/postman/collection-dir/bankofamerica/MerchantAccounts/.meta.json new file mode 100644 index 000000000000..02ea600d2eb8 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/.meta.json @@ -0,0 +1,8 @@ +{ + "childrenOrder": [ + "Merchant Account - Create", + "Merchant Account - Retrieve", + "Merchant Account - List", + "Merchant Account - Update" + ] +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/.event.meta.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/.event.meta.json new file mode 100644 index 000000000000..4ac527d834af --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/.event.meta.json @@ -0,0 +1,6 @@ +{ + "eventOrder": [ + "event.test.js", + "event.prerequest.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/event.prerequest.js b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/event.prerequest.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/event.test.js b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/event.test.js new file mode 100644 index 000000000000..41eecccf83fc --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/event.test.js @@ -0,0 +1,77 @@ +// Validate status 2xx +pm.test("[POST]::/accounts - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[POST]::/accounts - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) { } + +// pm.collectionVariables - Set merchant_id as variable for jsonData.merchant_id +if (jsonData?.merchant_id) { + pm.collectionVariables.set("merchant_id", jsonData.merchant_id); + console.log( + "- use {{merchant_id}} as collection variable for value", + jsonData.merchant_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_id}}, as jsonData.merchant_id is undefined.", + ); +} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} + +// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key +if (jsonData?.publishable_key) { + pm.collectionVariables.set("publishable_key", jsonData.publishable_key); + console.log( + "- use {{publishable_key}} as collection variable for value", + jsonData.publishable_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.", + ); +} + +// pm.collectionVariables - Set merchant_id as variable for jsonData.merchant_id +if (jsonData?.merchant_id) { + pm.collectionVariables.set("organization_id", jsonData.organization_id); + console.log( + "- use {{organization_id}} as collection variable for value", + jsonData.organization_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{organization_id}}, as jsonData.organization_id is undefined.", + ); +} + +// Response body should have "mandate_id" +pm.test( + "[POST]::/accounts - Organization id is generated", + function () { + pm.expect(typeof jsonData.organization_id !== "undefined").to.be.true; + }, +); diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/request.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/request.json new file mode 100644 index 000000000000..5313e3e6b486 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/request.json @@ -0,0 +1,95 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "merchant_id": "postman_merchant_GHAction_{{$guid}}", + "locker_id": "m0010", + "merchant_name": "NewAge Retailer", + "merchant_details": { + "primary_contact_person": "John Test", + "primary_email": "JohnTest@test.com", + "primary_phone": "sunt laborum", + "secondary_contact_person": "John Test2", + "secondary_email": "JohnTest2@test.com", + "secondary_phone": "cillum do dolor id", + "website": "www.example.com", + "about_business": "Online Retail with a wide selection of organic products for North America", + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US" + } + }, + "return_url": "https://duck.com/success", + "webhook_details": { + "webhook_version": "1.0.1", + "webhook_username": "ekart_retail", + "webhook_password": "password_ekart@123", + "payment_created_enabled": true, + "payment_succeeded_enabled": true, + "payment_failed_enabled": true + }, + "sub_merchants_enabled": false, + "metadata": { + "city": "NY", + "unit": "245" + }, + "primary_business_details": [ + { + "country": "US", + "business": "default" + } + ] + } + }, + "url": { + "raw": "{{baseUrl}}/accounts", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts" + ] + }, + "description": "Create a new account for a merchant. The merchant could be a seller or retailer or client who likes to receive and send payments." +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/response.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/.event.meta.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/event.test.js b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/event.test.js new file mode 100644 index 000000000000..0ba15a15ee6a --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/event.test.js @@ -0,0 +1,43 @@ +// Validate status 2xx +pm.test("[GET]::/accounts/list - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[GET]::/accounts/list - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) { } + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} + +// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key +if (jsonData?.publishable_key) { + pm.collectionVariables.set("publishable_key", jsonData.publishable_key); + console.log( + "- use {{publishable_key}} as collection variable for value", + jsonData.publishable_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/request.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/request.json new file mode 100644 index 000000000000..ed2324e03082 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/request.json @@ -0,0 +1,53 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/accounts/list?organization_id={{organization_id}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + "list" + ], + "query": [ + { + "key": "organization_id", + "value": "{{organization_id}}" + } + ], + "variable": [ + { + "key": "organization_id", + "value": "{{organization_id}}", + "description": "(Required) - Organization id" + } + ] + }, + "description": "List merchant accounts for an organization" +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/response.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - List/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/event.test.js new file mode 100644 index 000000000000..7694684a1770 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/event.test.js @@ -0,0 +1,43 @@ +// Validate status 2xx +pm.test("[GET]::/accounts/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test("[GET]::/accounts/:id - Content-Type is application/json", function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); +}); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} + +// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key +if (jsonData?.publishable_key) { + pm.collectionVariables.set("publishable_key", jsonData.publishable_key); + console.log( + "- use {{publishable_key}} as collection variable for value", + jsonData.publishable_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/request.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/request.json new file mode 100644 index 000000000000..536ad17268f5 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/request.json @@ -0,0 +1,47 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/accounts/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Retrieve a merchant account details." +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/response.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/.event.meta.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/event.test.js b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/event.test.js new file mode 100644 index 000000000000..ecd9d862b3f9 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/event.test.js @@ -0,0 +1,46 @@ +// Validate status 2xx +pm.test("[POST]::/accounts/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/accounts/:id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set api_key as variable for jsonData.api_key +if (jsonData?.api_key) { + pm.collectionVariables.set("api_key", jsonData.api_key); + console.log( + "- use {{api_key}} as collection variable for value", + jsonData.api_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.", + ); +} + +// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key +if (jsonData?.publishable_key) { + pm.collectionVariables.set("publishable_key", jsonData.publishable_key); + console.log( + "- use {{publishable_key}} as collection variable for value", + jsonData.publishable_key, + ); +} else { + console.log( + "INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/request.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/request.json new file mode 100644 index 000000000000..c58b1202d111 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/request.json @@ -0,0 +1,98 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "merchant_id": "{{merchant_id}}", + "merchant_name": "NewAge Retailer", + "locker_id": "m0010", + "merchant_details": { + "primary_contact_person": "joseph Test", + "primary_email": "josephTest@test.com", + "primary_phone": "veniam aute officia ullamco esse", + "secondary_contact_person": "joseph Test2", + "secondary_email": "josephTest2@test.com", + "secondary_phone": "proident adipisicing officia nulla", + "website": "www.example.com", + "about_business": "Online Retail with a wide selection of organic products for North America", + "address": { + "line1": "1467", + "line2": "Harrison Street", + "line3": "Harrison Street", + "city": "San Fransico", + "state": "California", + "zip": "94122", + "country": "US" + } + }, + "return_url": "https://duck.com", + "webhook_details": { + "webhook_version": "1.0.1", + "webhook_username": "ekart_retail", + "webhook_password": "password_ekart@123", + "payment_created_enabled": true, + "payment_succeeded_enabled": true, + "payment_failed_enabled": true + }, + "sub_merchants_enabled": false, + "parent_merchant_id": "xkkdf909012sdjki2dkh5sdf", + "metadata": { + "city": "NY", + "unit": "245" + } + } + }, + "url": { + "raw": "{{baseUrl}}/accounts/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "To update an existing merchant account. Helpful in updating merchant details such as email, contact deteails, or other configuration details like webhook, routing algorithm etc" +} diff --git a/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/response.json b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/MerchantAccounts/Merchant Account - Update/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/.meta.json new file mode 100644 index 000000000000..3f8bc360dd41 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/.meta.json @@ -0,0 +1,10 @@ +{ + "childrenOrder": [ + "Payment Connector - Create", + "Payment Connector - Retrieve", + "Payment Connector - Update", + "List Connectors by MID", + "Payment Connector - Delete", + "Merchant Account - Delete" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/.event.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/event.test.js b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/event.test.js new file mode 100644 index 000000000000..c685ff160bf9 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/event.test.js @@ -0,0 +1,17 @@ +// Validate status 2xx +pm.test( + "[GET]::/account/:account_id/connectors - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[GET]::/account/:account_id/connectors - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/request.json b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/request.json new file mode 100644 index 000000000000..89aa4e594064 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/request.json @@ -0,0 +1,41 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}" + } + ] + } +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/response.json b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/List Connectors by MID/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/.event.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/event.test.js b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/event.test.js new file mode 100644 index 000000000000..596c14630df9 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/event.test.js @@ -0,0 +1,42 @@ +// Validate status 2xx +pm.test("[DELETE]::/accounts/:id - Status code is 2xx", function () { + pm.response.to.be.success; +}); + +// Validate if response header has matching content-type +pm.test( + "[DELETE]::/accounts/:id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Response Validation +const schema = { + type: "object", + description: "Merchant Account", + required: ["merchant_id", "deleted"], + properties: { + merchant_id: { + type: "string", + description: "The identifier for the MerchantAccount object.", + maxLength: 255, + example: "y3oqhf46pyzuxjbcn2giaqnb44", + }, + deleted: { + type: "boolean", + description: + "Indicates the deletion status of the Merchant Account object.", + example: true, + }, + }, +}; + +// Validate if response matches JSON schema +pm.test("[DELETE]::/accounts/:id - Schema is valid", function () { + pm.response.to.have.jsonSchema(schema, { + unknownFormats: ["int32", "int64", "float", "double"], + }); +}); diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/request.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/request.json new file mode 100644 index 000000000000..17d56a57ea45 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/request.json @@ -0,0 +1,47 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/accounts/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Delete a Merchant Account" +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/response.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Merchant Account - Delete/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/.event.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/event.test.js b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/event.test.js new file mode 100644 index 000000000000..679a01ff33ed --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/event.test.js @@ -0,0 +1,47 @@ +// Validate status 2xx +pm.test( + "[POST]::/accounts/:account_id/connectors - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/accounts/:account_id/connectors - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) { } + +// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id +if (jsonData?.merchant_connector_id) { + pm.collectionVariables.set( + "merchant_connector_id", + jsonData.merchant_connector_id, + ); + console.log( + "- use {{merchant_connector_id}} as collection variable for value", + jsonData.merchant_connector_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.", + ); +} + +// Validate if the connector label is the one that is passed in the request +pm.test( + "[POST]::/accounts/:account_id/connectors - connector_label is not autogenerated", + function () { + pm.expect(jsonData.connector_label).to.eql("first_boa_connector") + }, +); \ No newline at end of file diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/request.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/request.json new file mode 100644 index 000000000000..8ab41d88236e --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/request.json @@ -0,0 +1,108 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "connector_type": "fiz_operations", + "connector_name": "bankofamerica", + "business_country": "US", + "business_label": "default", + "connector_label": "first_boa_connector", + "connector_account_details": { + "auth_type": "SignatureKey", + "api_key": "{{connector_api_key}}", + "api_secret": "{{connector_api_secret}}", + "key1": "{{connector_key1}}" + }, + "test_mode": false, + "disabled": false, + "payment_methods_enabled": [ + { + "payment_method": "card", + "payment_method_types": [ + { + "payment_method_type": "credit", + "card_networks": [ + "Visa", + "Mastercard" + ], + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "debit", + "card_networks": [ + "Visa", + "Mastercard" + ], + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + } + ] + } + ], + "metadata": { + "city": "NY", + "unit": "245" + } + } + }, + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Create a new Payment Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialised services like Fraud / Accounting etc." +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/response.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/.event.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/event.test.js b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/event.test.js new file mode 100644 index 000000000000..a8f03ce767fd --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/event.test.js @@ -0,0 +1,39 @@ +// Validate status 2xx +pm.test( + "[DELETE]::/account/:account_id/connectors/:connector_id - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[DELETE]::/account/:account_id/connectors/:connector_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id +if (jsonData?.merchant_connector_id) { + pm.collectionVariables.set( + "merchant_connector_id", + jsonData.merchant_connector_id, + ); + console.log( + "- use {{merchant_connector_id}} as collection variable for value", + jsonData.merchant_connector_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/request.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/request.json new file mode 100644 index 000000000000..6d7939d6762a --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/request.json @@ -0,0 +1,52 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors/:connector_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors", + ":connector_id" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}" + }, + { + "key": "connector_id", + "value": "{{merchant_connector_id}}" + } + ] + }, + "description": "Delete or Detach a Payment Connector from Merchant Account" +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/response.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Delete/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/.event.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/event.test.js b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/event.test.js new file mode 100644 index 000000000000..8125c4e1bb73 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/event.test.js @@ -0,0 +1,39 @@ +// Validate status 2xx +pm.test( + "[GET]::/accounts/:account_id/connectors/:connector_id - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[GET]::/accounts/:account_id/connectors/:connector_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id +if (jsonData?.merchant_connector_id) { + pm.collectionVariables.set( + "merchant_connector_id", + jsonData.merchant_connector_id, + ); + console.log( + "- use {{merchant_connector_id}} as collection variable for value", + jsonData.merchant_connector_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.", + ); +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/request.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/request.json new file mode 100644 index 000000000000..b87e65381250 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/request.json @@ -0,0 +1,54 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors/:connector_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors", + ":connector_id" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + }, + { + "key": "connector_id", + "value": "{{merchant_connector_id}}", + "description": "(Required) The unique identifier for the payment connector" + } + ] + }, + "description": "Retrieve Payment Connector details." +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/response.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/.event.meta.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/.event.meta.json new file mode 100644 index 000000000000..688c85746ef1 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/.event.meta.json @@ -0,0 +1,5 @@ +{ + "eventOrder": [ + "event.test.js" + ] +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/event.test.js b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/event.test.js new file mode 100644 index 000000000000..98f405d8bb85 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/event.test.js @@ -0,0 +1,47 @@ +// Validate status 2xx +pm.test( + "[POST]::/account/:account_id/connectors/:connector_id - Status code is 2xx", + function () { + pm.response.to.be.success; + }, +); + +// Validate if response header has matching content-type +pm.test( + "[POST]::/account/:account_id/connectors/:connector_id - Content-Type is application/json", + function () { + pm.expect(pm.response.headers.get("Content-Type")).to.include( + "application/json", + ); + }, +); + +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) { } + +// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id +if (jsonData?.merchant_connector_id) { + pm.collectionVariables.set( + "merchant_connector_id", + jsonData.merchant_connector_id, + ); + console.log( + "- use {{merchant_connector_id}} as collection variable for value", + jsonData.merchant_connector_id, + ); +} else { + console.log( + "INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.", + ); +} + +// Validate if the connector label is the one that is passed in the request +pm.test( + "[POST]::/accounts/:account_id/connectors - connector_label is not autogenerated", + function () { + pm.expect(jsonData.connector_label).to.eql("updated_stripe_connector") + }, +); \ No newline at end of file diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/request.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/request.json new file mode 100644 index 000000000000..3cb7be2537ad --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/request.json @@ -0,0 +1,109 @@ +{ + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw_json_formatted": { + "connector_type": "fiz_operations", + "connector_account_details": { + "auth_type": "SignatureKey", + "api_key": "{{connector_api_key}}", + "api_secret": "{{connector_api_secret}}", + "key1": "{{connector_key1}}" + }, + "connector_label": "updated_stripe_connector", + "test_mode": false, + "disabled": false, + "payment_methods_enabled": [ + { + "payment_method": "card", + "payment_method_types": [ + { + "payment_method_type": "credit", + "card_networks": [ + "Visa", + "Mastercard" + ], + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "debit", + "card_networks": [ + "Visa", + "Mastercard" + ], + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + } + ] + } + ], + "metadata": { + "city": "NY", + "unit": "245" + } + } + }, + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors/:connector_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors", + ":connector_id" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}" + }, + { + "key": "connector_id", + "value": "{{merchant_connector_id}}" + } + ] + }, + "description": "To update an existing Payment Connector. Helpful in enabling / disabling different payment methods and other settings for the connector etc" +} diff --git a/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/response.json b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/bankofamerica/PaymentConnectors/Payment Connector - Update/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/bankofamerica/event.prerequest.js b/postman/collection-dir/bankofamerica/event.prerequest.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/postman/collection-dir/bankofamerica/event.test.js b/postman/collection-dir/bankofamerica/event.test.js new file mode 100644 index 000000000000..fb52caec30fc --- /dev/null +++ b/postman/collection-dir/bankofamerica/event.test.js @@ -0,0 +1,13 @@ +// Set response object as internal variable +let jsonData = {}; +try { + jsonData = pm.response.json(); +} catch (e) {} + +// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id +if (jsonData?.payment_id) { + pm.collectionVariables.set("payment_id", jsonData.payment_id); + console.log("[LOG]::payment_id - " + jsonData.payment_id); +} + +console.log("[LOG]::x-request-id - " + pm.response.headers.get("x-request-id")); diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Confirm/request.json b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Confirm/request.json index c60989439784..8559af25e82c 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Confirm/request.json +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Confirm/request.json @@ -42,6 +42,16 @@ "surcharge_details": { "surcharge_amount": 5, "tax_amount": 5 + }, + "payment_method": "card", + "payment_method_data": { + "card": { + "card_number": "4012000033330026", + "card_exp_month": "10", + "card_exp_year": "25", + "card_holder_name": "joseph Doe", + "card_cvc": "123" + } } } }, diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/event.test.js b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/event.test.js index fe83ca7852a5..b6d04374f6b3 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/event.test.js +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/event.test.js @@ -63,9 +63,9 @@ if (jsonData?.client_secret) { // Response body should have value "requires_confirmation" for "status" if (jsonData?.status) { pm.test( - "[POST]::/payments - Content check if value for 'status' matches 'requires_capture'", + "[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'", function () { - pm.expect(jsonData.status).to.eql("requires_confirmation"); + pm.expect(jsonData.status).to.eql("requires_payment_method"); }, ); } diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json index 8cf69c5039f6..f7d813c34efd 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json @@ -31,16 +31,6 @@ "description": "Its my first payment request", "authentication_type": "no_three_ds", "return_url": "https://duck.com", - "payment_method": "card", - "payment_method_data": { - "card": { - "card_number": "4012000033330026", - "card_exp_month": "10", - "card_exp_year": "25", - "card_holder_name": "joseph Doe", - "card_cvc": "123" - } - }, "billing": { "address": { "line1": "1467", diff --git a/postman/collection-json/bankofamerica.postman_collection.json b/postman/collection-json/bankofamerica.postman_collection.json new file mode 100644 index 000000000000..01524d91953d --- /dev/null +++ b/postman/collection-json/bankofamerica.postman_collection.json @@ -0,0 +1,4310 @@ +{ + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(\"[LOG]::payment_id - \" + jsonData.payment_id);", + "}", + "", + "console.log(\"[LOG]::x-request-id - \" + pm.response.headers.get(\"x-request-id\"));", + "" + ], + "type": "text/javascript" + } + } + ], + "item": [ + { + "name": "Health check", + "item": [ + { + "name": "New Request", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/accounts - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "x-feature", + "value": "router-custom", + "type": "text", + "disabled": true + } + ], + "url": { + "raw": "{{baseUrl}}/health", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "health" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "MerchantAccounts", + "item": [ + { + "name": "Merchant Account - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/accounts - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/accounts - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) { }", + "", + "// pm.collectionVariables - Set merchant_id as variable for jsonData.merchant_id", + "if (jsonData?.merchant_id) {", + " pm.collectionVariables.set(\"merchant_id\", jsonData.merchant_id);", + " console.log(", + " \"- use {{merchant_id}} as collection variable for value\",", + " jsonData.merchant_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_id}}, as jsonData.merchant_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key", + "if (jsonData?.publishable_key) {", + " pm.collectionVariables.set(\"publishable_key\", jsonData.publishable_key);", + " console.log(", + " \"- use {{publishable_key}} as collection variable for value\",", + " jsonData.publishable_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set merchant_id as variable for jsonData.merchant_id", + "if (jsonData?.merchant_id) {", + " pm.collectionVariables.set(\"organization_id\", jsonData.organization_id);", + " console.log(", + " \"- use {{organization_id}} as collection variable for value\",", + " jsonData.organization_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{organization_id}}, as jsonData.organization_id is undefined.\",", + " );", + "}", + "", + "// Response body should have \"mandate_id\"", + "pm.test(", + " \"[POST]::/accounts - Organization id is generated\",", + " function () {", + " pm.expect(typeof jsonData.organization_id !== \"undefined\").to.be.true;", + " },", + ");", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"merchant_id\":\"postman_merchant_GHAction_{{$guid}}\",\"locker_id\":\"m0010\",\"merchant_name\":\"NewAge Retailer\",\"merchant_details\":{\"primary_contact_person\":\"John Test\",\"primary_email\":\"JohnTest@test.com\",\"primary_phone\":\"sunt laborum\",\"secondary_contact_person\":\"John Test2\",\"secondary_email\":\"JohnTest2@test.com\",\"secondary_phone\":\"cillum do dolor id\",\"website\":\"www.example.com\",\"about_business\":\"Online Retail with a wide selection of organic products for North America\",\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\"}},\"return_url\":\"https://duck.com/success\",\"webhook_details\":{\"webhook_version\":\"1.0.1\",\"webhook_username\":\"ekart_retail\",\"webhook_password\":\"password_ekart@123\",\"payment_created_enabled\":true,\"payment_succeeded_enabled\":true,\"payment_failed_enabled\":true},\"sub_merchants_enabled\":false,\"metadata\":{\"city\":\"NY\",\"unit\":\"245\"},\"primary_business_details\":[{\"country\":\"US\",\"business\":\"default\"}]}" + }, + "url": { + "raw": "{{baseUrl}}/accounts", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts" + ] + }, + "description": "Create a new account for a merchant. The merchant could be a seller or retailer or client who likes to receive and send payments." + }, + "response": [] + }, + { + "name": "Merchant Account - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/accounts/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/accounts/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key", + "if (jsonData?.publishable_key) {", + " pm.collectionVariables.set(\"publishable_key\", jsonData.publishable_key);", + " console.log(", + " \"- use {{publishable_key}} as collection variable for value\",", + " jsonData.publishable_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/accounts/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Retrieve a merchant account details." + }, + "response": [] + }, + { + "name": "Merchant Account - List", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/accounts/list - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/accounts/list - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) { }", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key", + "if (jsonData?.publishable_key) {", + " pm.collectionVariables.set(\"publishable_key\", jsonData.publishable_key);", + " console.log(", + " \"- use {{publishable_key}} as collection variable for value\",", + " jsonData.publishable_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/accounts/list?organization_id={{organization_id}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + "list" + ], + "query": [ + { + "key": "organization_id", + "value": "{{organization_id}}" + } + ], + "variable": [ + { + "key": "organization_id", + "value": "{{organization_id}}", + "description": "(Required) - Organization id" + } + ] + }, + "description": "List merchant accounts for an organization" + }, + "response": [] + }, + { + "name": "Merchant Account - Update", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/accounts/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/accounts/:id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key", + "if (jsonData?.publishable_key) {", + " pm.collectionVariables.set(\"publishable_key\", jsonData.publishable_key);", + " console.log(", + " \"- use {{publishable_key}} as collection variable for value\",", + " jsonData.publishable_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"merchant_id\":\"{{merchant_id}}\",\"merchant_name\":\"NewAge Retailer\",\"locker_id\":\"m0010\",\"merchant_details\":{\"primary_contact_person\":\"joseph Test\",\"primary_email\":\"josephTest@test.com\",\"primary_phone\":\"veniam aute officia ullamco esse\",\"secondary_contact_person\":\"joseph Test2\",\"secondary_email\":\"josephTest2@test.com\",\"secondary_phone\":\"proident adipisicing officia nulla\",\"website\":\"www.example.com\",\"about_business\":\"Online Retail with a wide selection of organic products for North America\",\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\"}},\"return_url\":\"https://duck.com\",\"webhook_details\":{\"webhook_version\":\"1.0.1\",\"webhook_username\":\"ekart_retail\",\"webhook_password\":\"password_ekart@123\",\"payment_created_enabled\":true,\"payment_succeeded_enabled\":true,\"payment_failed_enabled\":true},\"sub_merchants_enabled\":false,\"parent_merchant_id\":\"xkkdf909012sdjki2dkh5sdf\",\"metadata\":{\"city\":\"NY\",\"unit\":\"245\"}}" + }, + "url": { + "raw": "{{baseUrl}}/accounts/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "To update an existing merchant account. Helpful in updating merchant details such as email, contact deteails, or other configuration details like webhook, routing algorithm etc" + }, + "response": [] + } + ] + }, + { + "name": "API Key", + "item": [ + { + "name": "Create API Key", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/api_keys/:merchant_id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/api_keys/:merchant_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id", + "if (jsonData?.key_id) {", + " pm.collectionVariables.set(\"api_key_id\", jsonData.key_id);", + " console.log(", + " \"- use {{api_key_id}} as collection variable for value\",", + " jsonData.key_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\"name\":\"API Key 1\",\"description\":null,\"expiration\":\"2069-09-23T01:02:03.000Z\"}" + }, + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Update API Key", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[POST]::/api_keys/:merchant_id/:api_key_id - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/api_keys/:merchant_id/:api_key_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id", + "if (jsonData?.key_id) {", + " pm.collectionVariables.set(\"api_key_id\", jsonData.key_id);", + " console.log(", + " \"- use {{api_key_id}} as collection variable for value\",", + " jsonData.key_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\"name\":null,\"description\":\"My very awesome API key\",\"expiration\":null}" + }, + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/:api_key_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + ":api_key_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + }, + { + "key": "api_key_id", + "value": "{{api_key_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Retrieve API Key", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[GET]::/api_keys/:merchant_id/:api_key_id - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[GET]::/api_keys/:merchant_id/:api_key_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id", + "if (jsonData?.key_id) {", + " pm.collectionVariables.set(\"api_key_id\", jsonData.key_id);", + " console.log(", + " \"- use {{api_key_id}} as collection variable for value\",", + " jsonData.key_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/:api_key_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + ":api_key_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + }, + { + "key": "api_key_id", + "value": "{{api_key_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "List API Keys", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/api_keys/:merchant_id/list - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[GET]::/api_keys/:merchant_id/list - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id", + "if (jsonData?.key_id) {", + " pm.collectionVariables.set(\"api_key_id\", jsonData.key_id);", + " console.log(", + " \"- use {{api_key_id}} as collection variable for value\",", + " jsonData.key_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/list", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + "list" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete API Key", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[DELETE]::/api_keys/:merchant_id/:api-key - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[DELETE]::/api_keys/:merchant_id/:api-key - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id/:api-key", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id", + ":api-key" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + }, + { + "key": "api-key", + "value": "{{api_key_id}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "PaymentConnectors", + "item": [ + { + "name": "Payment Connector - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[POST]::/accounts/:account_id/connectors - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/accounts/:account_id/connectors - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) { }", + "", + "// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id", + "if (jsonData?.merchant_connector_id) {", + " pm.collectionVariables.set(", + " \"merchant_connector_id\",", + " jsonData.merchant_connector_id,", + " );", + " console.log(", + " \"- use {{merchant_connector_id}} as collection variable for value\",", + " jsonData.merchant_connector_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.\",", + " );", + "}", + "", + "// Validate if the connector label is the one that is passed in the request", + "pm.test(", + " \"[POST]::/accounts/:account_id/connectors - connector_label is not autogenerated\",", + " function () {", + " pm.expect(jsonData.connector_label).to.eql(\"first_boa_connector\")", + " },", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"connector_type\":\"fiz_operations\",\"connector_name\":\"bankofamerica\",\"business_country\":\"US\",\"business_label\":\"default\",\"connector_label\":\"first_boa_connector\",\"connector_account_details\":{\"auth_type\":\"SignatureKey\",\"api_key\":\"{{connector_api_key}}\",\"api_secret\":\"{{connector_api_secret}}\",\"key1\":\"{{connector_key1}}\"},\"test_mode\":false,\"disabled\":false,\"payment_methods_enabled\":[{\"payment_method\":\"card\",\"payment_method_types\":[{\"payment_method_type\":\"credit\",\"card_networks\":[\"Visa\",\"Mastercard\"],\"minimum_amount\":1,\"maximum_amount\":68607706,\"recurring_enabled\":true,\"installment_payment_enabled\":true},{\"payment_method_type\":\"debit\",\"card_networks\":[\"Visa\",\"Mastercard\"],\"minimum_amount\":1,\"maximum_amount\":68607706,\"recurring_enabled\":true,\"installment_payment_enabled\":true}]}],\"metadata\":{\"city\":\"NY\",\"unit\":\"245\"}}" + }, + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Create a new Payment Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialised services like Fraud / Accounting etc." + }, + "response": [] + }, + { + "name": "Payment Connector - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[GET]::/accounts/:account_id/connectors/:connector_id - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[GET]::/accounts/:account_id/connectors/:connector_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id", + "if (jsonData?.merchant_connector_id) {", + " pm.collectionVariables.set(", + " \"merchant_connector_id\",", + " jsonData.merchant_connector_id,", + " );", + " console.log(", + " \"- use {{merchant_connector_id}} as collection variable for value\",", + " jsonData.merchant_connector_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors/:connector_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors", + ":connector_id" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + }, + { + "key": "connector_id", + "value": "{{merchant_connector_id}}", + "description": "(Required) The unique identifier for the payment connector" + } + ] + }, + "description": "Retrieve Payment Connector details." + }, + "response": [] + }, + { + "name": "Payment Connector - Update", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[POST]::/account/:account_id/connectors/:connector_id - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/account/:account_id/connectors/:connector_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) { }", + "", + "// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id", + "if (jsonData?.merchant_connector_id) {", + " pm.collectionVariables.set(", + " \"merchant_connector_id\",", + " jsonData.merchant_connector_id,", + " );", + " console.log(", + " \"- use {{merchant_connector_id}} as collection variable for value\",", + " jsonData.merchant_connector_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.\",", + " );", + "}", + "", + "// Validate if the connector label is the one that is passed in the request", + "pm.test(", + " \"[POST]::/accounts/:account_id/connectors - connector_label is not autogenerated\",", + " function () {", + " pm.expect(jsonData.connector_label).to.eql(\"updated_stripe_connector\")", + " },", + ");" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"connector_type\":\"fiz_operations\",\"connector_account_details\":{\"auth_type\":\"SignatureKey\",\"api_key\":\"{{connector_api_key}}\",\"api_secret\":\"{{connector_api_secret}}\",\"key1\":\"{{connector_key1}}\"},\"connector_label\":\"updated_stripe_connector\",\"test_mode\":false,\"disabled\":false,\"payment_methods_enabled\":[{\"payment_method\":\"card\",\"payment_method_types\":[{\"payment_method_type\":\"credit\",\"card_networks\":[\"Visa\",\"Mastercard\"],\"minimum_amount\":1,\"maximum_amount\":68607706,\"recurring_enabled\":true,\"installment_payment_enabled\":true},{\"payment_method_type\":\"debit\",\"card_networks\":[\"Visa\",\"Mastercard\"],\"minimum_amount\":1,\"maximum_amount\":68607706,\"recurring_enabled\":true,\"installment_payment_enabled\":true}]}],\"metadata\":{\"city\":\"NY\",\"unit\":\"245\"}}" + }, + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors/:connector_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors", + ":connector_id" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}" + }, + { + "key": "connector_id", + "value": "{{merchant_connector_id}}" + } + ] + }, + "description": "To update an existing Payment Connector. Helpful in enabling / disabling different payment methods and other settings for the connector etc" + }, + "response": [] + }, + { + "name": "List Connectors by MID", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[GET]::/account/:account_id/connectors - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[GET]::/account/:account_id/connectors - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Payment Connector - Delete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[DELETE]::/account/:account_id/connectors/:connector_id - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[DELETE]::/account/:account_id/connectors/:connector_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id", + "if (jsonData?.merchant_connector_id) {", + " pm.collectionVariables.set(", + " \"merchant_connector_id\",", + " jsonData.merchant_connector_id,", + " );", + " console.log(", + " \"- use {{merchant_connector_id}} as collection variable for value\",", + " jsonData.merchant_connector_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors/:connector_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors", + ":connector_id" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}" + }, + { + "key": "connector_id", + "value": "{{merchant_connector_id}}" + } + ] + }, + "description": "Delete or Detach a Payment Connector from Merchant Account" + }, + "response": [] + }, + { + "name": "Merchant Account - Delete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[DELETE]::/accounts/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[DELETE]::/accounts/:id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Response Validation", + "const schema = {", + " type: \"object\",", + " description: \"Merchant Account\",", + " required: [\"merchant_id\", \"deleted\"],", + " properties: {", + " merchant_id: {", + " type: \"string\",", + " description: \"The identifier for the MerchantAccount object.\",", + " maxLength: 255,", + " example: \"y3oqhf46pyzuxjbcn2giaqnb44\",", + " },", + " deleted: {", + " type: \"boolean\",", + " description:", + " \"Indicates the deletion status of the Merchant Account object.\",", + " example: true,", + " },", + " },", + "};", + "", + "// Validate if response matches JSON schema", + "pm.test(\"[DELETE]::/accounts/:id - Schema is valid\", function () {", + " pm.response.to.have.jsonSchema(schema, {", + " unknownFormats: [\"int32\", \"int64\", \"float\", \"double\"],", + " });", + "});", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/accounts/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Delete a Merchant Account" + }, + "response": [] + } + ] + }, + { + "name": "Flow Testcases", + "item": [ + { + "name": "QuickStart", + "item": [ + { + "name": "Merchant Account - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/accounts - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/accounts - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set merchant_id as variable for jsonData.merchant_id", + "if (jsonData?.merchant_id) {", + " pm.collectionVariables.set(\"merchant_id\", jsonData.merchant_id);", + " console.log(", + " \"- use {{merchant_id}} as collection variable for value\",", + " jsonData.merchant_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_id}}, as jsonData.merchant_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set publishable_key as variable for jsonData.publishable_key", + "if (jsonData?.publishable_key) {", + " pm.collectionVariables.set(\"publishable_key\", jsonData.publishable_key);", + " console.log(", + " \"- use {{publishable_key}} as collection variable for value\",", + " jsonData.publishable_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{publishable_key}}, as jsonData.publishable_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"merchant_id\":\"postman_merchant_GHAction_{{$guid}}\",\"locker_id\":\"m0010\",\"merchant_name\":\"NewAge Retailer\",\"merchant_details\":{\"primary_contact_person\":\"John Test\",\"primary_email\":\"JohnTest@test.com\",\"primary_phone\":\"sunt laborum\",\"secondary_contact_person\":\"John Test2\",\"secondary_email\":\"JohnTest2@test.com\",\"secondary_phone\":\"cillum do dolor id\",\"website\":\"www.example.com\",\"about_business\":\"Online Retail with a wide selection of organic products for North America\",\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\"}},\"return_url\":\"https://duck.com/success\",\"webhook_details\":{\"webhook_version\":\"1.0.1\",\"webhook_username\":\"ekart_retail\",\"webhook_password\":\"password_ekart@123\",\"payment_created_enabled\":true,\"payment_succeeded_enabled\":true,\"payment_failed_enabled\":true},\"sub_merchants_enabled\":false,\"metadata\":{\"city\":\"NY\",\"unit\":\"245\"},\"primary_business_details\":[{\"country\":\"US\",\"business\":\"default\"}]}" + }, + "url": { + "raw": "{{baseUrl}}/accounts", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "accounts" + ] + }, + "description": "Create a new account for a merchant. The merchant could be a seller or retailer or client who likes to receive and send payments." + }, + "response": [] + }, + { + "name": "API Key - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/api_keys/:merchant_id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/api_keys/:merchant_id - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set api_key_id as variable for jsonData.key_id", + "if (jsonData?.key_id) {", + " pm.collectionVariables.set(\"api_key_id\", jsonData.key_id);", + " console.log(", + " \"- use {{api_key_id}} as collection variable for value\",", + " jsonData.key_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key_id}}, as jsonData.key_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set api_key as variable for jsonData.api_key", + "if (jsonData?.api_key) {", + " pm.collectionVariables.set(\"api_key\", jsonData.api_key);", + " console.log(", + " \"- use {{api_key}} as collection variable for value\",", + " jsonData.api_key,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{api_key}}, as jsonData.api_key is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\"name\":\"API Key 1\",\"description\":null,\"expiration\":\"2069-09-23T01:02:03.000Z\"}" + }, + "url": { + "raw": "{{baseUrl}}/api_keys/:merchant_id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api_keys", + ":merchant_id" + ], + "variable": [ + { + "key": "merchant_id", + "value": "{{merchant_id}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Payment Connector - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(", + " \"[POST]::/account/:account_id/connectors - Status code is 2xx\",", + " function () {", + " pm.response.to.be.success;", + " },", + ");", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/account/:account_id/connectors - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set merchant_connector_id as variable for jsonData.merchant_connector_id", + "if (jsonData?.merchant_connector_id) {", + " pm.collectionVariables.set(", + " \"merchant_connector_id\",", + " jsonData.merchant_connector_id,", + " );", + " console.log(", + " \"- use {{merchant_connector_id}} as collection variable for value\",", + " jsonData.merchant_connector_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{merchant_connector_id}}, as jsonData.merchant_connector_id is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{admin_api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"connector_type\":\"fiz_operations\",\"connector_name\":\"bankofamerica\",\"business_country\":\"US\",\"business_label\":\"default\",\"connector_label\":\"first_boa_connector\",\"connector_account_details\":{\"auth_type\":\"SignatureKey\",\"api_key\":\"{{connector_api_key}}\",\"api_secret\":\"{{connector_api_secret}}\",\"key1\":\"{{connector_key1}}\"},\"test_mode\":false,\"disabled\":false,\"payment_methods_enabled\":[{\"payment_method\":\"card\",\"payment_method_types\":[{\"payment_method_type\":\"credit\",\"card_networks\":[\"Visa\",\"Mastercard\"],\"minimum_amount\":1,\"maximum_amount\":68607706,\"recurring_enabled\":true,\"installment_payment_enabled\":true},{\"payment_method_type\":\"debit\",\"card_networks\":[\"Visa\",\"Mastercard\"],\"minimum_amount\":1,\"maximum_amount\":68607706,\"recurring_enabled\":true,\"installment_payment_enabled\":true}]}],\"metadata\":{\"city\":\"NY\",\"unit\":\"245\"}}" + }, + "url": { + "raw": "{{baseUrl}}/account/:account_id/connectors", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "account", + ":account_id", + "connectors" + ], + "variable": [ + { + "key": "account_id", + "value": "{{merchant_id}}", + "description": "(Required) The unique identifier for the merchant account" + } + ] + }, + "description": "Create a new Payment Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialised services like Fraud / Accounting etc." + }, + "response": [] + }, + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"8056594427\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"bankofamerica\"}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, + { + "name": "Happy Cases", + "item": [ + { + "name": "Scenario1-Create payment with confirm true", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\" because payment gets succeeded after one day.", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "", + "// Response body should have \"connector_transaction_id\"", + "pm.test(", + " \"[POST]::/payments - Content check if 'connector_transaction_id' exists\",", + " function () {", + " pm.expect(typeof jsonData.connector_transaction_id !== \"undefined\").to.be", + " .true;", + " },", + ");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "// pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "// pm.response.to.be.success;", + "// });", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "", + "// Response body should have \"connector_transaction_id\"", + "// pm.test(", + "// \"[POST]::/payments - Content check if 'connector_transaction_id' exists\",", + "// function () {", + "// pm.expect(typeof jsonData.connector_transaction_id !== \"undefined\").to.be", + "// .true;", + "// },", + "// );", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, + { + "name": "Scenario2-Create payment with confirm false", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"requires_confirmation\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_confirmation'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"requires_confirmation\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Confirm", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments/:id/confirm - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/payments/:id/confirm - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments/:id/confirm - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"6540\" for \"amount\"", + "if (jsonData?.amount) {", + " pm.test(", + " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", + " function () {", + " pm.expect(jsonData.amount).to.eql(6540);", + " },", + " );", + "}", + "", + "// Response body should have value \"6540\" for \"amount_capturable\"", + "if (jsonData?.amount) {", + " pm.test(", + " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 0'\",", + " function () {", + " pm.expect(jsonData.amount_capturable).to.eql(0);", + " },", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments/:id/confirm - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "", + "// Response body should have \"connector_transaction_id\"", + "pm.test(", + " \"[POST]::/payments - Content check if 'connector_transaction_id' exists\",", + " function () {", + " pm.expect(typeof jsonData.connector_transaction_id !== \"undefined\").to.be", + " .true;", + " },", + ");", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{publishable_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"client_secret\":\"{{client_secret}}\"}" + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/confirm", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "confirm" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "// pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "// pm.response.to.be.success;", + "// });", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "", + "// Response body should have value \"6540\" for \"amount\"", + "if (jsonData?.amount) {", + " pm.test(", + " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", + " function () {", + " pm.expect(jsonData.amount).to.eql(6540);", + " },", + " );", + "}", + "", + "// Response body should have value \"6540\" for \"amount_capturable\"", + "if (jsonData?.amount) {", + " pm.test(", + " \"[post]:://payments/:id/capture - Content check if value for 'amount_capturable' matches 'amount - 0'\",", + " function () {", + " pm.expect(jsonData.amount_capturable).to.eql(0);", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, + { + "name": "Scenario3-Create payment without PMD", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"requires_payment_method\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"requires_payment_method\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"abcd\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"abcd\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"bankofamerica\"}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Confirm", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments/:id/confirm - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/payments/:id/confirm - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments/:id/confirm - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments:id/confirm - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{publishable_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"client_secret\":\"{{client_secret}}\"}" + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/confirm", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "confirm" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "This API is to confirm the payment request and forward payment to the payment processor. This API provides more granular control upon when the API is forwarded to the payment processor. Alternatively you can confirm the payment within the Payments-Create API" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "// pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "// pm.response.to.be.success;", + "// });", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments:id - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, + { + "name": "Scenario4-Create payment with Manual capture", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"requires_capture\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"requires_capture\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"3566111111111113\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Capture", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments/:id/capture - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/payments/:id/capture - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments/:id/capture - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "", + "// Response body should have value \"6540\" for \"amount\"", + "if (jsonData?.amount) {", + " pm.test(", + " \"[post]:://payments/:id/capture - Content check if value for 'amount' matches '6540'\",", + " function () {", + " pm.expect(jsonData.amount).to.eql(6540);", + " },", + " );", + "}", + "", + "// Response body should have value \"6000\" for \"amount_received\"", + "if (jsonData?.amount_received) {", + " pm.test(", + " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", + " function () {", + " pm.expect(jsonData.amount_received).to.eql(6000);", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount_to_capture\":6000,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/capture", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "capture" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To capture the funds for an uncaptured payment" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "// pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + "// pm.response.to.be.success;", + "// });", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"processing\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'processing'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"processing\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + }, + { + "name": "Scenario5-Void the payment", + "item": [ + { + "name": "Payments - Create", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[POST]::/payments - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"requires_capture\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"requires_capture\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"3566111111111113\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + }, + "url": { + "raw": "{{baseUrl}}/payments", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments" + ] + }, + "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" + }, + "response": [] + }, + { + "name": "Payments - Cancel", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[POST]::/payments/:id/cancel - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(", + " \"[POST]::/payments/:id/cancel - Content-Type is application/json\",", + " function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + " },", + ");", + "", + "// Validate if response has JSON Body", + "pm.test(\"[POST]::/payments/:id/cancel - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"cancelled\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments/:id/cancel - Content check if value for 'status' matches 'cancelled'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"cancelled\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "options": { + "raw": { + "language": "json" + } + }, + "raw": "{\"cancellation_reason\":\"requested_by_customer\"}" + }, + "url": { + "raw": "{{baseUrl}}/payments/:id/cancel", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id", + "cancel" + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "A Payment could can be cancelled when it is in one of these statuses: requires_payment_method, requires_capture, requires_confirmation, requires_customer_action" + }, + "response": [] + }, + { + "name": "Payments - Retrieve", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Validate status 2xx", + "pm.test(\"[GET]::/payments/:id - Status code is 2xx\", function () {", + " pm.response.to.be.success;", + "});", + "", + "// Validate if response header has matching content-type", + "pm.test(\"[GET]::/payments/:id - Content-Type is application/json\", function () {", + " pm.expect(pm.response.headers.get(\"Content-Type\")).to.include(", + " \"application/json\",", + " );", + "});", + "", + "// Validate if response has JSON Body", + "pm.test(\"[GET]::/payments/:id - Response has JSON Body\", function () {", + " pm.response.to.have.jsonBody();", + "});", + "", + "// Set response object as internal variable", + "let jsonData = {};", + "try {", + " jsonData = pm.response.json();", + "} catch (e) {}", + "", + "// pm.collectionVariables - Set payment_id as variable for jsonData.payment_id", + "if (jsonData?.payment_id) {", + " pm.collectionVariables.set(\"payment_id\", jsonData.payment_id);", + " console.log(", + " \"- use {{payment_id}} as collection variable for value\",", + " jsonData.payment_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{payment_id}}, as jsonData.payment_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set mandate_id as variable for jsonData.mandate_id", + "if (jsonData?.mandate_id) {", + " pm.collectionVariables.set(\"mandate_id\", jsonData.mandate_id);", + " console.log(", + " \"- use {{mandate_id}} as collection variable for value\",", + " jsonData.mandate_id,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{mandate_id}}, as jsonData.mandate_id is undefined.\",", + " );", + "}", + "", + "// pm.collectionVariables - Set client_secret as variable for jsonData.client_secret", + "if (jsonData?.client_secret) {", + " pm.collectionVariables.set(\"client_secret\", jsonData.client_secret);", + " console.log(", + " \"- use {{client_secret}} as collection variable for value\",", + " jsonData.client_secret,", + " );", + "} else {", + " console.log(", + " \"INFO - Unable to assign variable {{client_secret}}, as jsonData.client_secret is undefined.\",", + " );", + "}", + "", + "// Response body should have value \"cancelled\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments/:id - Content check if value for 'status' matches 'cancelled'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"cancelled\");", + " },", + " );", + "}", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/payments/:id?force_sync=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "payments", + ":id" + ], + "query": [ + { + "key": "force_sync", + "value": "true" + } + ], + "variable": [ + { + "key": "id", + "value": "{{payment_id}}", + "description": "(Required) unique payment id" + } + ] + }, + "description": "To retrieve the properties of a Payment. This may be used to get the status of a previously initiated payment or next action for an ongoing payment" + }, + "response": [] + } + ] + } + ] + } + ] + } + ], + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "{{api_key}}", + "type": "string" + }, + { + "key": "key", + "value": "api-key", + "type": "string" + }, + { + "key": "in", + "value": "header", + "type": "string" + } + ] + }, + "info": { + "_postman_id": "646f7167-da26-4a24-adb0-4157fd3a1781", + "name": "bankofamerica", + "description": "## Get started\n\nJuspay Router provides a collection of APIs that enable you to process and manage payments. Our APIs accept and return JSON in the HTTP body, and return standard HTTP response codes. \nYou can consume the APIs directly using your favorite HTTP/REST library. \nWe have a testing environment referred to \"sandbox\", which you can setup to test API calls without affecting production data.\n\n### Base URLs\n\nUse the following base URLs when making requests to the APIs:\n\n| Environment | Base URL |\n| --- | --- |\n| Sandbox | [https://sandbox.hyperswitch.io](https://sandbox.hyperswitch.io) |\n| Production | [https://router.juspay.io](https://router.juspay.io) |\n\n# Authentication\n\nWhen you sign up for an account, you are given a secret key (also referred as api-key). You may authenticate all API requests with Juspay server by providing the appropriate key in the request Authorization header. \nNever share your secret api keys. Keep them guarded and secure.\n\nContact Support: \nName: Juspay Support \nEmail: [support@juspay.in](mailto:support@juspay.in)", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "28305597" + }, + "variable": [ + { + "key": "baseUrl", + "value": "", + "type": "string" + }, + { + "key": "admin_api_key", + "value": "", + "type": "string" + }, + { + "key": "api_key", + "value": "", + "type": "string" + }, + { + "key": "merchant_id", + "value": "" + }, + { + "key": "payment_id", + "value": "" + }, + { + "key": "customer_id", + "value": "" + }, + { + "key": "mandate_id", + "value": "" + }, + { + "key": "payment_method_id", + "value": "" + }, + { + "key": "refund_id", + "value": "" + }, + { + "key": "merchant_connector_id", + "value": "" + }, + { + "key": "client_secret", + "value": "", + "type": "string" + }, + { + "key": "connector_api_key", + "value": "", + "type": "string" + }, + { + "key": "publishable_key", + "value": "", + "type": "string" + }, + { + "key": "api_key_id", + "value": "", + "type": "string" + }, + { + "key": "payment_token", + "value": "" + }, + { + "key": "gateway_merchant_id", + "value": "", + "type": "string" + }, + { + "key": "certificate", + "value": "", + "type": "string" + }, + { + "key": "certificate_keys", + "value": "", + "type": "string" + }, + { + "key": "organization_id", + "value": "" + }, + { + "key": "connector_api_secret", + "value": "", + "type": "string" + }, + { + "key": "connector_key1", + "value": "", + "type": "string" + } + ] +} diff --git a/postman/collection-json/paypal.postman_collection.json b/postman/collection-json/paypal.postman_collection.json index 4849a27fe051..d9deae47f9af 100644 --- a/postman/collection-json/paypal.postman_collection.json +++ b/postman/collection-json/paypal.postman_collection.json @@ -747,9 +747,9 @@ "// Response body should have value \"requires_confirmation\" for \"status\"", "if (jsonData?.status) {", " pm.test(", - " \"[POST]::/payments - Content check if value for 'status' matches 'requires_capture'\",", + " \"[POST]::/payments - Content check if value for 'status' matches 'requires_payment_method'\",", " function () {", - " pm.expect(jsonData.status).to.eql(\"requires_confirmation\");", + " pm.expect(jsonData.status).to.eql(\"requires_payment_method\");", " },", " );", "}", @@ -808,7 +808,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4012000033330026\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"paypal\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"paypal\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -990,7 +990,7 @@ "language": "json" } }, - "raw": "{\"client_secret\":\"{{client_secret}}\",\"surcharge_details\":{\"surcharge_amount\":5,\"tax_amount\":5}}" + "raw": "{\"client_secret\":\"{{client_secret}}\",\"surcharge_details\":{\"surcharge_amount\":5,\"tax_amount\":5},\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4012000033330026\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}}}" }, "url": { "raw": "{{baseUrl}}/payments/:id/confirm",