diff --git a/.cargo/config.toml b/.cargo/config.toml index 852384da785e..e643b41ec053 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,6 +6,7 @@ rustflags = [ "-Wclippy::expect_used", "-Wclippy::index_refutable_slice", "-Wclippy::indexing_slicing", + "-Wclippy::large_futures", "-Wclippy::match_on_vec_items", "-Wclippy::missing_panics_doc", "-Wclippy::out_of_bounds_indexing", diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c71cf69ee4d..0d88ba7053e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,56 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.05.31.1 + +### Features + +- **cypress:** Add trustpay, adyen bank redirects and corresponding refactor ([#4766](https://github.com/juspay/hyperswitch/pull/4766)) ([`48dac12`](https://github.com/juspay/hyperswitch/commit/48dac12cedc7f11b76c6a9ef8ba4ce04ae2456bf)) + +### Refactors + +- **core:** Reverts Inclusion of constraint graph for merchant Payment Method list ([#4839](https://github.com/juspay/hyperswitch/pull/4839)) ([`f74b9b6`](https://github.com/juspay/hyperswitch/commit/f74b9b622e5565a00bf9ee8223c64a3def37b776)) +- **first_name:** Check if first_name is sent as empty string ([#4832](https://github.com/juspay/hyperswitch/pull/4832)) ([`5cb84f6`](https://github.com/juspay/hyperswitch/commit/5cb84f66e4c59f6ffbd12bf4e91ab0152ac1c2c5)) + +### Miscellaneous Tasks + +- **euclid_wasm:** Update paypal payment experience ([#4811](https://github.com/juspay/hyperswitch/pull/4811)) ([`121b611`](https://github.com/juspay/hyperswitch/commit/121b61123f31db9d94888fa708532cdabca4bbc8)) +- Enable `clippy::large_futures` lint ([#4822](https://github.com/juspay/hyperswitch/pull/4822)) ([`d2d317c`](https://github.com/juspay/hyperswitch/commit/d2d317ce61c0c00ca38af9774bd1b45247d30c82)) + +**Full Changelog:** [`2024.05.31.0...2024.05.31.1`](https://github.com/juspay/hyperswitch/compare/2024.05.31.0...2024.05.31.1) + +- - - + +## 2024.05.31.0 + +### Features + +- **connector:** Implement pre auth flow for gpayments ([#4692](https://github.com/juspay/hyperswitch/pull/4692)) ([`bed42ce`](https://github.com/juspay/hyperswitch/commit/bed42ce4be901f2b8f46033dd395dee8dbe807c9)) +- **payout:** [Payone] add payone connector ([#4553](https://github.com/juspay/hyperswitch/pull/4553)) ([`832968c`](https://github.com/juspay/hyperswitch/commit/832968c0c444af74fb6398950159847d639eb50e)) +- **router:** Added amount conversion function in core for connector module ([#4710](https://github.com/juspay/hyperswitch/pull/4710)) ([`08eefdb`](https://github.com/juspay/hyperswitch/commit/08eefdba4a7f428ffe7f0dac9799c46b82c49864)) +- **users:** Add support to reset totp ([#4821](https://github.com/juspay/hyperswitch/pull/4821)) ([`aca6ad1`](https://github.com/juspay/hyperswitch/commit/aca6ad1bd1f43758b22638a7a2e7e4a99b66e5ff)) +- Add a domain type for `customer_id` ([#4705](https://github.com/juspay/hyperswitch/pull/4705)) ([`93d61d1`](https://github.com/juspay/hyperswitch/commit/93d61d1053a834ac1e7bf6d5dd70053d28f3e7d5)) + +### Bug Fixes + +- **netcetera:** Handle non-ascii characters for cardholdername, error message and send missing fields ([#4755](https://github.com/juspay/hyperswitch/pull/4755)) ([`5d1900e`](https://github.com/juspay/hyperswitch/commit/5d1900e1d96da1e8bb72dadfa1132a2340733fdc)) +- **routing:** Added routing validation for payments req ([#4762](https://github.com/juspay/hyperswitch/pull/4762)) ([`21a3a2e`](https://github.com/juspay/hyperswitch/commit/21a3a2ea8ada838c67b0e5871f01d09bd5a8b9ed)) + +### Refactors + +- **connector:** [Klarna] Refactor configs for sandbox and production and update payment status ([#4819](https://github.com/juspay/hyperswitch/pull/4819)) ([`f7e99e1`](https://github.com/juspay/hyperswitch/commit/f7e99e1eda4bea68351f4249074e35877a95e6ee)) +- **core:** Move router data flow types to hyperswitch domain models crate ([#4801](https://github.com/juspay/hyperswitch/pull/4801)) ([`61e67e4`](https://github.com/juspay/hyperswitch/commit/61e67e42724525660df1d1076d2422f28d58a637)) + +### Miscellaneous Tasks + +- **euclid_wasm:** Update klarna metadata ([#4823](https://github.com/juspay/hyperswitch/pull/4823)) ([`f192fa3`](https://github.com/juspay/hyperswitch/commit/f192fa3866c2ea21555aed8783fad2ac022091ad)) +- **postman:** Update Postman collection files ([`4833f1a`](https://github.com/juspay/hyperswitch/commit/4833f1ac31b725c275465cf9fba34c5950b3c500)) +- Remove redundant caching code ([#4804](https://github.com/juspay/hyperswitch/pull/4804)) ([`971ef1f`](https://github.com/juspay/hyperswitch/commit/971ef1fb8fd16f5af89071ddf4d143330d32f056)) + +**Full Changelog:** [`2024.05.30.0...2024.05.31.0`](https://github.com/juspay/hyperswitch/compare/2024.05.30.0...2024.05.31.0) + +- - - + ## 2024.05.30.0 ### Features diff --git a/Cargo.lock b/Cargo.lock index b116982bb242..d814cb1e5107 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1965,7 +1965,9 @@ dependencies = [ "reqwest", "ring 0.17.8", "router_env", + "rust_decimal", "rustc-hash", + "rusty-money", "semver 1.0.22", "serde", "serde_json", @@ -5748,6 +5750,7 @@ dependencies = [ "totp-rs", "tracing-futures", "unicode-segmentation", + "unidecode", "url", "utoipa", "uuid", @@ -7851,6 +7854,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "unidecode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/config/config.example.toml b/config/config.example.toml index 52d5472d6ece..8aa0ca9e52f2 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -195,7 +195,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.playground.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" @@ -263,8 +263,7 @@ stripe = { banks = "alior_bank,bank_millennium,bank_nowy_bfg_sa,bank_pekao_sa,ba # This data is used to call respective connectors for wallets and cards [connectors.supported] -wallets = ["klarna", - "mifinity", "braintree", "applepay"] +wallets = ["klarna", "mifinity", "braintree", "applepay"] rewards = ["cashtocode", "zen"] cards = [ "adyen", @@ -352,8 +351,8 @@ email_role_arn = "" # The amazon resource name ( arn ) of the role which sts_role_session_name = "" # An identifier for the assumed role session, used to uniquely identify a session. [user] -password_validity_in_days = 90 # Number of days after which password should be updated -two_factor_auth_expiry_in_secs = 300 # Number of seconds after which 2FA should be done again if doing update/change from inside +password_validity_in_days = 90 # Number of days after which password should be updated +two_factor_auth_expiry_in_secs = 300 # Number of seconds after which 2FA should be done again if doing update/change from inside #tokenization configuration which describe token lifetime and payment method for specific connector [tokenization] @@ -364,7 +363,7 @@ stax = { long_lived_token = true, payment_method = "card,bank_debit" } square = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" } gocardless = { long_lived_token = true, payment_method = "bank_debit" } -billwerk = {long_lived_token = false, payment_method = "card"} +billwerk = { long_lived_token = false, payment_method = "card" } [temp_locker_enable_config] stripe = { payment_method = "bank_transfer" } @@ -397,16 +396,16 @@ slack_invite_url = "https://www.example.com/" # Slack invite url for hyperswit discord_invite_url = "https://www.example.com/" # Discord invite url for hyperswitch [mandates.supported_payment_methods] -card.credit = { connector_list = "stripe,adyen,cybersource,bankofamerica"} # Mandate supported payment method type and connector for card -wallet.paypal = { connector_list = "adyen" } # Mandate supported payment method type and connector for wallets -pay_later.klarna = { connector_list = "adyen" } # Mandate supported payment method type and connector for pay_later -bank_debit.ach = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit -bank_debit.becs = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit -bank_debit.sepa = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit -bank_redirect.ideal = { connector_list = "stripe,adyen,globalpay" } # Mandate supported payment method type and connector for bank_redirect +card.credit = { connector_list = "stripe,adyen,cybersource,bankofamerica" } # Mandate supported payment method type and connector for card +wallet.paypal = { connector_list = "adyen" } # Mandate supported payment method type and connector for wallets +pay_later.klarna = { connector_list = "adyen" } # Mandate supported payment method type and connector for pay_later +bank_debit.ach = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit +bank_debit.becs = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit +bank_debit.sepa = { connector_list = "gocardless" } # Mandate supported payment method type and connector for bank_debit +bank_redirect.ideal = { connector_list = "stripe,adyen,globalpay" } # Mandate supported payment method type and connector for bank_redirect bank_redirect.sofort = { connector_list = "stripe,adyen,globalpay" } wallet.apple_pay = { connector_list = "stripe,adyen,cybersource,noon,bankofamerica" } -wallet.google_pay = { connector_list = "bankofamerica"} +wallet.google_pay = { connector_list = "bankofamerica" } bank_redirect.giropay = { connector_list = "adyen,globalpay" } @@ -495,6 +494,9 @@ card_redirect = { currency = "USD" } credit = { currency = "USD" } debit = { currency = "USD" } +[pm_filters.klarna] +klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,CH,GB,US", currency = "CHF,DKK,EUR,GBP,NOK,PLN,SEK,USD,AUD,NZD,CAD" } + [connector_customer] connector_list = "gocardless,stax,stripe" payout_connector_list = "stripe,wise" @@ -586,6 +588,7 @@ outgoing_webhook_logs_topic = "topic" # Kafka topic to be used for outgoing webh dispute_analytics_topic = "topic" # Kafka topic to be used for Dispute events audit_events_topic = "topic" # Kafka topic to be used for Payment Audit events payout_analytics_topic = "topic" # Kafka topic to be used for Payouts and PayoutAttempt events +consolidated_events_topic = "topic" # Kafka topic to be used for Consolidated events # File storage configuration [file_storage] @@ -626,3 +629,9 @@ disputes = "hyperswitch-dispute-events" [saved_payment_methods] sdk_eligible_payment_methods = "card" + +[multitenancy] +enabled = false + +[multitenancy.tenants] +public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "public", redis_key_prefix = ""} # schema -> Postgres db schema, redis_key_prefix -> redis key distinguisher, base_url -> url of the tenant \ No newline at end of file diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index fc02335dcf53..a097e1b66dcd 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -81,6 +81,7 @@ outgoing_webhook_logs_topic = "topic" # Kafka topic to be used for outgoing webh dispute_analytics_topic = "topic" # Kafka topic to be used for Dispute events audit_events_topic = "topic" # Kafka topic to be used for Payment Audit events payout_analytics_topic = "topic" # Kafka topic to be used for Payouts and PayoutAttempt events +consolidated_events_topic = "topic" # Kafka topic to be used for Consolidated events # File storage configuration [file_storage] @@ -229,7 +230,6 @@ recon_admin_api_key = "recon_test_admin" # recon_admin API key for recon authent # Server configuration [server] -base_url = "https://server_base_url" workers = 8 port = 8080 host = "127.0.0.1" @@ -252,3 +252,9 @@ encryption_manager = "aws_kms" # Encryption manager client to be used [encryption_management.aws_kms] key_id = "kms_key_id" # The AWS key ID used by the KMS SDK for decrypting data. region = "kms_region" # The AWS region used by the KMS SDK for decrypting data. + +[multitenancy] +enabled = false + +[multitenancy.tenants] +public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "public", redis_key_prefix = ""} \ No newline at end of file diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 4f8896d4181b..f6ed3ca5ea5b 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -49,7 +49,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.playground.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index fc284594c05d..37b35eb47e7b 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -53,7 +53,7 @@ gocardless.base_url = "https://api.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.klarna.com/" mifinity.base_url = "https://secure.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index ef7e49c7b989..fc496e34fc30 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -53,7 +53,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.playground.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" diff --git a/config/development.toml b/config/development.toml index 6c71df7b0709..d12a667b6315 100644 --- a/config/development.toml +++ b/config/development.toml @@ -89,8 +89,7 @@ vault_private_key = "" tunnel_private_key = "" [connectors.supported] -wallets = ["klarna", - "mifinity", "braintree", "applepay", "adyen"] +wallets = ["klarna", "mifinity", "braintree", "applepay", "adyen"] rewards = ["cashtocode", "zen"] cards = [ "aci", @@ -197,7 +196,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.playground.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" @@ -559,7 +558,7 @@ redis_lock_expiry_seconds = 180 # 3 * 60 seconds delay_between_retries_in_milliseconds = 500 [kv_config] -ttl = 900 # 15 * 60 seconds +ttl = 900 # 15 * 60 seconds soft_kill = false [frm] @@ -579,6 +578,7 @@ outgoing_webhook_logs_topic = "hyperswitch-outgoing-webhook-events" dispute_analytics_topic = "hyperswitch-dispute-events" audit_events_topic = "hyperswitch-audit-events" payout_analytics_topic = "hyperswitch-payout-events" +consolidated_events_topic = "hyperswitch-consolidated-events" [analytics] source = "sqlx" @@ -628,3 +628,9 @@ disputes = "hyperswitch-dispute-events" [saved_payment_methods] sdk_eligible_payment_methods = "card" + +[multitenancy] +enabled = false + +[multitenancy.tenants] +public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "public", redis_key_prefix = ""} \ No newline at end of file diff --git a/config/docker_compose.toml b/config/docker_compose.toml index cedcb8fedbc5..3f6c9523e933 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -133,7 +133,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.playground.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" @@ -180,8 +180,7 @@ zsl.base_url = "https://api.sitoffalb.net/" apple_pay = { country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US,KR,VN,MA,ZA,VA,CL,SV,GT,HN,PA", currency = "AED,AUD,CHF,CAD,EUR,GBP,HKD,SGD,USD" } [connectors.supported] -wallets = ["klarna", - "mifinity", "braintree", "applepay"] +wallets = ["klarna", "mifinity", "braintree", "applepay"] rewards = ["cashtocode", "zen"] cards = [ "aci", @@ -239,7 +238,7 @@ cards = [ "worldline", "worldpay", "zen", - "zsl" + "zsl", ] [delayed_session_response] @@ -269,7 +268,7 @@ stax = { long_lived_token = true, payment_method = "card,bank_debit" } square = { long_lived_token = false, payment_method = "card" } braintree = { long_lived_token = false, payment_method = "card" } gocardless = { long_lived_token = true, payment_method = "bank_debit" } -billwerk = {long_lived_token = false, payment_method = "card"} +billwerk = { long_lived_token = false, payment_method = "card" } [temp_locker_enable_config] stripe = { payment_method = "bank_transfer" } @@ -369,6 +368,9 @@ google_pay = { currency = "USD" } credit = { currency = "USD" } debit = { currency = "USD" } +[pm_filters.klarna] +klarna = { country = "AU,AT,BE,CA,CZ,DK,FI,FR,DE,GR,IE,IT,NL,NZ,NO,PL,PT,ES,SE,CH,GB,US", currency = "CHF,DKK,EUR,GBP,NOK,PLN,SEK,USD,AUD,NZD,CAD" } + [pm_filters.stax] credit = { currency = "USD" } debit = { currency = "USD" } @@ -430,6 +432,7 @@ outgoing_webhook_logs_topic = "hyperswitch-outgoing-webhook-events" dispute_analytics_topic = "hyperswitch-dispute-events" audit_events_topic = "hyperswitch-audit-events" payout_analytics_topic = "hyperswitch-payout-events" +consolidated_events_topic = "hyperswitch-consolidated-events" [analytics] source = "sqlx" @@ -451,7 +454,7 @@ connection_timeout = 10 queue_strategy = "Fifo" [kv_config] -ttl = 900 # 15 * 60 seconds +ttl = 900 # 15 * 60 seconds soft_kill = false [frm] @@ -489,3 +492,9 @@ disputes = "hyperswitch-dispute-events" [saved_payment_methods] sdk_eligible_payment_methods = "card" + +[multitenancy] +enabled = false + +[multitenancy.tenants] +public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "public", redis_key_prefix = ""} \ No newline at end of file diff --git a/crates/analytics/src/lib.rs b/crates/analytics/src/lib.rs index 1deca1e07519..f658f6790972 100644 --- a/crates/analytics/src/lib.rs +++ b/crates/analytics/src/lib.rs @@ -601,20 +601,20 @@ impl AnalyticsProvider { } } - pub async fn from_conf(config: &AnalyticsConfig) -> Self { + pub async fn from_conf(config: &AnalyticsConfig, tenant: &str) -> Self { match config { - AnalyticsConfig::Sqlx { sqlx } => Self::Sqlx(SqlxClient::from_conf(sqlx).await), + AnalyticsConfig::Sqlx { sqlx } => Self::Sqlx(SqlxClient::from_conf(sqlx, tenant).await), AnalyticsConfig::Clickhouse { clickhouse } => Self::Clickhouse(ClickhouseClient { config: Arc::new(clickhouse.clone()), }), AnalyticsConfig::CombinedCkh { sqlx, clickhouse } => Self::CombinedCkh( - SqlxClient::from_conf(sqlx).await, + SqlxClient::from_conf(sqlx, tenant).await, ClickhouseClient { config: Arc::new(clickhouse.clone()), }, ), AnalyticsConfig::CombinedSqlx { sqlx, clickhouse } => Self::CombinedSqlx( - SqlxClient::from_conf(sqlx).await, + SqlxClient::from_conf(sqlx, tenant).await, ClickhouseClient { config: Arc::new(clickhouse.clone()), }, diff --git a/crates/analytics/src/sqlx.rs b/crates/analytics/src/sqlx.rs index d3cb24a00c26..3a1b7f2d4685 100644 --- a/crates/analytics/src/sqlx.rs +++ b/crates/analytics/src/sqlx.rs @@ -4,12 +4,14 @@ use api_models::{ analytics::refunds::RefundType, enums::{DisputeStage, DisputeStatus}, }; -use common_utils::errors::{CustomResult, ParsingError}; +use common_utils::{ + errors::{CustomResult, ParsingError}, + DbConnectionParams, +}; use diesel_models::enums::{ AttemptStatus, AuthenticationType, Currency, PaymentMethod, RefundStatus, }; use error_stack::ResultExt; -use masking::PeekInterface; use sqlx::{ postgres::{PgArgumentBuffer, PgPoolOptions, PgRow, PgTypeInfo, PgValueRef}, Decode, Encode, @@ -49,12 +51,8 @@ impl Default for SqlxClient { } impl SqlxClient { - pub async fn from_conf(conf: &Database) -> Self { - let password = &conf.password.peek(); - let database_url = format!( - "postgres://{}:{}@{}:{}/{}", - conf.username, password, conf.host, conf.port, conf.dbname - ); + pub async fn from_conf(conf: &Database, schema: &str) -> Self { + let database_url = conf.get_database_url(schema); #[allow(clippy::expect_used)] let pool = PgPoolOptions::new() .max_connections(conf.pool_size) diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 19ce82e59bb4..81e50b020e73 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -249,6 +249,9 @@ impl Connector { Self::Checkout | Self::Nmi| Self::Cybersource => true, } } + pub fn is_pre_processing_required_before_authorize(&self) -> bool { + matches!(self, Self::Airwallex) + } } #[derive( diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 30329e132b83..cda13289aa52 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -2904,7 +2904,11 @@ impl AddressDetails { pub fn unify_address_details(self, other: Option<&Self>) -> Self { if let Some(other) = other { - let (first_name, last_name) = if self.first_name.is_some() { + let (first_name, last_name) = if self + .first_name + .as_ref() + .is_some_and(|first_name| !first_name.is_empty_after_trim()) + { (self.first_name, self.last_name) } else { (other.first_name.clone(), other.last_name.clone()) @@ -4591,6 +4595,8 @@ pub struct PaymentsExternalAuthenticationResponse { pub three_dsserver_trans_id: Option, /// Contains the JWS object created by the ACS for the ARes message pub acs_signed_content: Option, + /// Three DS Requestor URL + pub three_ds_requestor_url: String, } #[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] diff --git a/crates/api_models/src/refunds.rs b/crates/api_models/src/refunds.rs index 881cc3912ece..c97344ff48b9 100644 --- a/crates/api_models/src/refunds.rs +++ b/crates/api_models/src/refunds.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use common_utils::pii; -pub use common_utils::types::ChargeRefunds; +pub use common_utils::types::{ChargeRefunds, MinorUnit}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; use utoipa::ToSchema; @@ -36,8 +36,8 @@ pub struct RefundRequest { pub merchant_id: Option, /// Total amount for which the refund is to be initiated. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc., If not provided, this will default to the full payment amount - #[schema(minimum = 100, example = 6540)] - pub amount: Option, + #[schema(value_type = Option , minimum = 100, example = 6540)] + pub amount: Option, /// Reason for the refund. Often useful for displaying to users and your customer support executive. In case the payment went through Stripe, this field needs to be passed with one of these enums: `duplicate`, `fraudulent`, or `requested_by_customer` #[schema(max_length = 255, example = "Customer returned the product")] @@ -115,7 +115,8 @@ pub struct RefundResponse { /// The payment id against which refund is initiated pub payment_id: String, /// The refund amount, which should be less than or equal to the total payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc - pub amount: i64, + #[schema(value_type = i64 , minimum = 100, example = 6540)] + pub amount: MinorUnit, /// The three-letter ISO currency code pub currency: String, /// The status for refund diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index ff64d6517c7f..def64e35db07 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -43,6 +43,8 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order uuid = { version = "1.8.0", features = ["v7"] } # First party crates +rust_decimal = "1.35" +rusty-money = { git = "https://github.com/varunsrin/rusty_money", rev = "bbc0150742a0fff905225ff11ee09388e9babdcc", features = ["iso", "crypto"] } common_enums = { version = "0.1.0", path = "../common_enums" } masking = { version = "0.1.0", path = "../masking" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"], optional = true } diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 0ea826d15cdd..0fe5cd7d7c7b 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -84,6 +84,9 @@ pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60; /// Max ttl for Extended card info in redis (in seconds) pub const MAX_TTL_FOR_EXTENDED_CARD_INFO: u16 = 60 * 60 * 2; +/// Default tenant to be used when multitenancy is disabled +pub const DEFAULT_TENANT: &str = "public"; + /// Max Length for MerchantReferenceId pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64; diff --git a/crates/common_utils/src/errors.rs b/crates/common_utils/src/errors.rs index 967580f0ae59..e4c59ba2a310 100644 --- a/crates/common_utils/src/errors.rs +++ b/crates/common_utils/src/errors.rs @@ -11,6 +11,7 @@ use crate::types::MinorUnit; pub type CustomResult = error_stack::Result; /// Parsing Errors +#[allow(missing_docs)] // Only to prevent warnings about struct fields not being documented #[derive(Debug, thiserror::Error)] pub enum ParsingError { ///Failed to parse enum @@ -34,6 +35,21 @@ pub enum ParsingError { /// Failed to parse phone number #[error("Failed to parse phone number")] PhoneNumberParsingError, + /// Failed to parse Float value for converting to decimal points + #[error("Failed to parse Float value for converting to decimal points")] + FloatToDecimalConversionFailure, + /// Failed to parse Decimal value for i64 value conversion + #[error("Failed to parse Decimal value for i64 value conversion")] + DecimalToI64ConversionFailure, + /// Failed to parse string value for f64 value conversion + #[error("Failed to parse string value for f64 value conversion")] + StringToFloatConversionFailure, + /// Failed to parse i64 value for f64 value conversion + #[error("Failed to parse i64 value for f64 value conversion")] + I64ToDecimalConversionFailure, + /// Failed to parse String value to Decimal value conversion because `error` + #[error("Failed to parse String value to Decimal value conversion because {error}")] + StringToDecimalConversionFailure { error: String }, } /// Validation errors. diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index d77442ad40f1..4e515e3d6a74 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -2,6 +2,8 @@ #![warn(missing_docs, missing_debug_implementations)] #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR" ), "/", "README.md"))] +use masking::{PeekInterface, Secret}; + use crate::{ consts::ID_LENGTH, id_type::{CustomerId, MerchantReferenceId}, @@ -224,6 +226,27 @@ pub fn generate_time_ordered_id(prefix: &str) -> String { format!("{prefix}_{}", uuid::Uuid::now_v7().as_simple()) } +#[allow(missing_docs)] +pub trait DbConnectionParams { + fn get_username(&self) -> &str; + fn get_password(&self) -> Secret; + fn get_host(&self) -> &str; + fn get_port(&self) -> u16; + fn get_dbname(&self) -> &str; + fn get_database_url(&self, schema: &str) -> String { + format!( + "postgres://{}:{}@{}:{}/{}?application_name={}&options=-c search_path%3D{}", + self.get_username(), + self.get_password().peek(), + self.get_host(), + self.get_port(), + self.get_dbname(), + schema, + schema, + ) + } +} + #[cfg(test)] mod nanoid_tests { #![allow(clippy::unwrap_used)] diff --git a/crates/common_utils/src/types.rs b/crates/common_utils/src/types.rs index d9a7aef3f578..7ab78ce32570 100644 --- a/crates/common_utils/src/types.rs +++ b/crates/common_utils/src/types.rs @@ -6,6 +6,7 @@ use std::{ str::FromStr, }; +use common_enums::enums; use diesel::{ backend::Backend, deserialize, @@ -16,6 +17,10 @@ use diesel::{ AsExpression, FromSqlRow, Queryable, }; use error_stack::{report, ResultExt}; +use rust_decimal::{ + prelude::{FromPrimitive, ToPrimitive}, + Decimal, +}; use semver::Version; use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; use utoipa::ToSchema; @@ -226,48 +231,92 @@ where } } -#[derive( - Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression, ToSchema, -)] -#[diesel(sql_type = Jsonb)] -/// Charge object for refunds -pub struct ChargeRefunds { - /// Identifier for charge created for the payment - pub charge_id: String, - - /// Toggle for reverting the application fee that was collected for the payment. - /// If set to false, the funds are pulled from the destination account. - pub revert_platform_fee: Option, +/// Amount convertor trait for connector +pub trait AmountConvertor: Send { + /// Output type for the connector + type Output; + /// helps in conversion of connector required amount type + fn convert( + &self, + amount: MinorUnit, + currency: enums::Currency, + ) -> Result>; - /// Toggle for reverting the transfer that was made during the charge. - /// If set to false, the funds are pulled from the main platform's account. - pub revert_transfer: Option, + /// helps in converting back connector required amount type to core minor unit + fn convert_back( + &self, + amount: Self::Output, + currency: enums::Currency, + ) -> Result>; } -impl FromSql for ChargeRefunds -where - serde_json::Value: FromSql, -{ - fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { - let value = >::from_sql(bytes)?; - Ok(serde_json::from_value(value)?) +/// Connector required amount type +#[derive(Default, Debug, Clone, Copy, PartialEq)] +pub struct StringMinorUnitForConnector; + +impl AmountConvertor for StringMinorUnitForConnector { + type Output = StringMinorUnit; + fn convert( + &self, + amount: MinorUnit, + _currency: enums::Currency, + ) -> Result> { + amount.to_minor_unit_as_string() + } + + fn convert_back( + &self, + amount: Self::Output, + _currency: enums::Currency, + ) -> Result> { + amount.to_minor_unit_as_i64() } } -impl ToSql for ChargeRefunds -where - serde_json::Value: ToSql, -{ - fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result { - let value = serde_json::to_value(self)?; +/// Connector required amount type +#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)] +pub struct StringMajorUnitForConnector; - // the function `reborrow` only works in case of `Pg` backend. But, in case of other backends - // please refer to the diesel migration blog: - // https://github.com/Diesel-rs/Diesel/blob/master/guide_drafts/migration_guide.md#changed-tosql-implementations - >::to_sql(&value, &mut out.reborrow()) +impl AmountConvertor for StringMajorUnitForConnector { + type Output = StringMajorUnit; + fn convert( + &self, + amount: MinorUnit, + currency: enums::Currency, + ) -> Result> { + amount.to_major_unit_as_string(currency) + } + + fn convert_back( + &self, + amount: StringMajorUnit, + currency: enums::Currency, + ) -> Result> { + amount.to_minor_unit_as_i64(currency) } } +/// Connector required amount type +#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)] +pub struct FloatMajorUnitForConnector; + +impl AmountConvertor for FloatMajorUnitForConnector { + type Output = FloatMajorUnit; + fn convert( + &self, + amount: MinorUnit, + currency: enums::Currency, + ) -> Result> { + amount.to_major_unit_as_f64(currency) + } + fn convert_back( + &self, + amount: FloatMajorUnit, + currency: enums::Currency, + ) -> Result> { + amount.to_minor_unit_as_i64(currency) + } +} /// This Unit struct represents MinorUnit in which core amount works #[derive( Default, @@ -287,9 +336,8 @@ where pub struct MinorUnit(i64); impl MinorUnit { - /// gets amount as i64 value + /// gets amount as i64 value will be removed in future pub fn get_amount_as_i64(&self) -> i64 { - // will be removed in future self.0 } @@ -297,6 +345,50 @@ impl MinorUnit { pub fn new(value: i64) -> Self { Self(value) } + + /// Convert the amount to its major denomination based on Currency and return String + /// Paypal Connector accepts Zero and Two decimal currency but not three decimal and it should be updated as required for 3 decimal currencies. + /// Paypal Ref - https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/ + fn to_major_unit_as_string( + self, + currency: enums::Currency, + ) -> Result> { + let amount_f64 = self.to_major_unit_as_f64(currency)?; + let amount_string = if currency.is_zero_decimal_currency() { + amount_f64.0.to_string() + } else if currency.is_three_decimal_currency() { + format!("{:.3}", amount_f64.0) + } else { + format!("{:.2}", amount_f64.0) + }; + Ok(StringMajorUnit::new(amount_string)) + } + + /// Convert the amount to its major denomination based on Currency and return f64 + fn to_major_unit_as_f64( + self, + currency: enums::Currency, + ) -> Result> { + let amount_decimal = + Decimal::from_i64(self.0).ok_or(ParsingError::I64ToDecimalConversionFailure)?; + + let amount = if currency.is_zero_decimal_currency() { + amount_decimal + } else if currency.is_three_decimal_currency() { + amount_decimal / Decimal::from(1000) + } else { + amount_decimal / Decimal::from(100) + }; + let amount_f64 = amount + .to_f64() + .ok_or(ParsingError::FloatToDecimalConversionFailure)?; + Ok(FloatMajorUnit::new(amount_f64)) + } + + ///Convert minor unit to string minor unit + fn to_minor_unit_as_string(self) -> Result> { + Ok(StringMinorUnit::new(self.0.to_string())) + } } impl Display for MinorUnit { @@ -351,3 +443,251 @@ impl Sub for MinorUnit { Self(self.0 - a2.0) } } + +/// Connector specific types to send + +#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq)] +pub struct StringMinorUnit(String); + +impl StringMinorUnit { + /// forms a new minor unit in string from amount + fn new(value: String) -> Self { + Self(value) + } + + /// converts to minor unit i64 from minor unit string value + fn to_minor_unit_as_i64(&self) -> Result> { + let amount_string = &self.0; + let amount_decimal = Decimal::from_str(amount_string).map_err(|e| { + ParsingError::StringToDecimalConversionFailure { + error: e.to_string(), + } + })?; + let amount_i64 = amount_decimal + .to_i64() + .ok_or(ParsingError::DecimalToI64ConversionFailure)?; + Ok(MinorUnit::new(amount_i64)) + } +} + +/// Connector specific types to send +#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, Copy, PartialEq)] +pub struct FloatMajorUnit(f64); + +impl FloatMajorUnit { + /// forms a new major unit from amount + fn new(value: f64) -> Self { + Self(value) + } + + /// forms a new major unit with zero amount + pub fn zero() -> Self { + Self(0.0) + } + + /// converts to minor unit as i64 from FloatMajorUnit + fn to_minor_unit_as_i64( + self, + currency: enums::Currency, + ) -> Result> { + let amount_decimal = + Decimal::from_f64(self.0).ok_or(ParsingError::FloatToDecimalConversionFailure)?; + + let amount = if currency.is_zero_decimal_currency() { + amount_decimal + } else if currency.is_three_decimal_currency() { + amount_decimal * Decimal::from(1000) + } else { + amount_decimal * Decimal::from(100) + }; + + let amount_i64 = amount + .to_i64() + .ok_or(ParsingError::DecimalToI64ConversionFailure)?; + Ok(MinorUnit::new(amount_i64)) + } +} + +/// Connector specific types to send +#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)] +pub struct StringMajorUnit(String); + +impl StringMajorUnit { + /// forms a new major unit from amount + fn new(value: String) -> Self { + Self(value) + } + + /// Converts to minor unit as i64 from StringMajorUnit + fn to_minor_unit_as_i64( + &self, + currency: enums::Currency, + ) -> Result> { + let amount_decimal = Decimal::from_str(&self.0).map_err(|e| { + ParsingError::StringToDecimalConversionFailure { + error: e.to_string(), + } + })?; + + let amount = if currency.is_zero_decimal_currency() { + amount_decimal + } else if currency.is_three_decimal_currency() { + amount_decimal * Decimal::from(1000) + } else { + amount_decimal * Decimal::from(100) + }; + let amount_i64 = amount + .to_i64() + .ok_or(ParsingError::DecimalToI64ConversionFailure)?; + Ok(MinorUnit::new(amount_i64)) + } +} + +#[cfg(test)] +mod amount_conversion_tests { + #![allow(clippy::unwrap_used)] + use super::*; + const TWO_DECIMAL_CURRENCY: enums::Currency = enums::Currency::USD; + const THREE_DECIMAL_CURRENCY: enums::Currency = enums::Currency::BHD; + const ZERO_DECIMAL_CURRENCY: enums::Currency = enums::Currency::JPY; + #[test] + fn amount_conversion_to_float_major_unit() { + let request_amount = MinorUnit::new(999999999); + let required_conversion = FloatMajorUnitForConnector; + + // Two decimal currency conversions + let converted_amount = required_conversion + .convert(request_amount, TWO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_amount.0, 9999999.99); + let converted_back_amount = required_conversion + .convert_back(converted_amount, TWO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + + // Three decimal currency conversions + let converted_amount = required_conversion + .convert(request_amount, THREE_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_amount.0, 999999.999); + let converted_back_amount = required_conversion + .convert_back(converted_amount, THREE_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + + // Zero decimal currency conversions + let converted_amount = required_conversion + .convert(request_amount, ZERO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_amount.0, 999999999.0); + + let converted_back_amount = required_conversion + .convert_back(converted_amount, ZERO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + } + + #[test] + fn amount_conversion_to_string_major_unit() { + let request_amount = MinorUnit::new(999999999); + let required_conversion = StringMajorUnitForConnector; + + // Two decimal currency conversions + let converted_amount_two_decimal_currency = required_conversion + .convert(request_amount, TWO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!( + converted_amount_two_decimal_currency.0, + "9999999.99".to_string() + ); + let converted_back_amount = required_conversion + .convert_back(converted_amount_two_decimal_currency, TWO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + + // Three decimal currency conversions + let converted_amount_three_decimal_currency = required_conversion + .convert(request_amount, THREE_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!( + converted_amount_three_decimal_currency.0, + "999999.999".to_string() + ); + let converted_back_amount = required_conversion + .convert_back( + converted_amount_three_decimal_currency, + THREE_DECIMAL_CURRENCY, + ) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + + // Zero decimal currency conversions + let converted_amount = required_conversion + .convert(request_amount, ZERO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_amount.0, "999999999".to_string()); + + let converted_back_amount = required_conversion + .convert_back(converted_amount, ZERO_DECIMAL_CURRENCY) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + } + + #[test] + fn amount_conversion_to_string_minor_unit() { + let request_amount = MinorUnit::new(999999999); + let currency = TWO_DECIMAL_CURRENCY; + let required_conversion = StringMinorUnitForConnector; + let converted_amount = required_conversion + .convert(request_amount, currency) + .unwrap(); + assert_eq!(converted_amount.0, "999999999".to_string()); + let converted_back_amount = required_conversion + .convert_back(converted_amount, currency) + .unwrap(); + assert_eq!(converted_back_amount, request_amount); + } +} + +// Charges structs +#[derive( + Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, FromSqlRow, AsExpression, ToSchema, +)] +#[diesel(sql_type = Jsonb)] +/// Charge object for refunds +pub struct ChargeRefunds { + /// Identifier for charge created for the payment + pub charge_id: String, + + /// Toggle for reverting the application fee that was collected for the payment. + /// If set to false, the funds are pulled from the destination account. + pub revert_platform_fee: Option, + + /// Toggle for reverting the transfer that was made during the charge. + /// If set to false, the funds are pulled from the main platform's account. + pub revert_transfer: Option, +} + +impl FromSql for ChargeRefunds +where + serde_json::Value: FromSql, +{ + fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result { + let value = >::from_sql(bytes)?; + Ok(serde_json::from_value(value)?) + } +} + +impl ToSql for ChargeRefunds +where + serde_json::Value: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result { + let value = serde_json::to_value(self)?; + + // the function `reborrow` only works in case of `Pg` backend. But, in case of other backends + // please refer to the diesel migration blog: + // https://github.com/Diesel-rs/Diesel/blob/master/guide_drafts/migration_guide.md#changed-tosql-implementations + >::to_sql(&value, &mut out.reborrow()) + } +} diff --git a/crates/connector_configs/src/transformer.rs b/crates/connector_configs/src/transformer.rs index 94cc06786aa8..87d1e3e810b6 100644 --- a/crates/connector_configs/src/transformer.rs +++ b/crates/connector_configs/src/transformer.rs @@ -63,6 +63,36 @@ impl DashboardRequestPayload { }, } } + + pub fn transform_paypal_payment_method( + providers: Vec, + ) -> Vec { + let payment_experiences = [ + api_models::enums::PaymentExperience::RedirectToUrl, + api_models::enums::PaymentExperience::InvokeSdkClient, + ]; + + let mut payment_method_types = Vec::new(); + + for experience in payment_experiences { + for provider in &providers { + let data = payment_methods::RequestPaymentMethodTypes { + payment_method_type: provider.payment_method_type, + card_networks: None, + minimum_amount: Some(0), + maximum_amount: Some(68607706), + recurring_enabled: true, + installment_payment_enabled: false, + accepted_currencies: provider.accepted_currencies.clone(), + accepted_countries: provider.accepted_countries.clone(), + payment_experience: Some(experience), + }; + payment_method_types.push(data); + } + } + + payment_method_types + } pub fn transform_payment_method( connector: Connector, provider: Vec, @@ -125,8 +155,37 @@ impl DashboardRequestPayload { } } - PaymentMethod::Wallet - | PaymentMethod::BankRedirect + PaymentMethod::Wallet => match request.connector { + Connector::Paypal => { + if let Some(provider) = payload.provider { + let val = Self::transform_paypal_payment_method(provider); + if !val.is_empty() { + let methods = PaymentMethodsEnabled { + payment_method: payload.payment_method, + payment_method_types: Some(val), + }; + payment_method_enabled.push(methods); + } + } + } + _ => { + if let Some(provider) = payload.provider { + let val = Self::transform_payment_method( + request.connector, + provider, + payload.payment_method, + ); + if !val.is_empty() { + let methods = PaymentMethodsEnabled { + payment_method: payload.payment_method, + payment_method_types: Some(val), + }; + payment_method_enabled.push(methods); + } + } + } + }, + PaymentMethod::BankRedirect | PaymentMethod::PayLater | PaymentMethod::BankTransfer | PaymentMethod::Crypto diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 5ac7ef7558dd..138fef0cdf30 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -1142,8 +1142,6 @@ key1="Klarna Merchant Username" api_key="Klarna Merchant ID Password" [klarna.metadata] klarna_region=["Europe","NorthAmerica","Oceania"] -[klarna.connector_webhook_details] -merchant_secret="Source verification key" [mollie] [[mollie.credit]] @@ -1652,9 +1650,9 @@ merchant_secret="Source verification key" client_id="Client ID" [paypal_payout] -[[paypal.wallet]] +[[paypal_payout.wallet]] payment_method_type = "paypal" -[[paypal.wallet]] +[[paypal_payout.wallet]] payment_method_type = "venmo" [paypal_payout.connector_auth.BodyKey] api_key="Client Secret" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index 57bb6830b518..32fa87d970d3 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -959,8 +959,6 @@ key1="Klarna Merchant Username" api_key="Klarna Merchant ID Password" [klarna.metadata] klarna_region=["Europe","NorthAmerica","Oceania"] -[klarna.connector_webhook_details] -merchant_secret="Source verification key" [mollie] diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 79847253d576..7236863745da 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -1142,8 +1142,6 @@ key1="Klarna Merchant Username" api_key="Klarna Merchant ID Password" [klarna.metadata] klarna_region=["Europe","NorthAmerica","Oceania"] -[klarna.connector_webhook_details] -merchant_secret="Source verification key" [mollie] [[mollie.credit]] @@ -1652,9 +1650,9 @@ merchant_secret="Source verification key" client_id="Client ID" [paypal_payout] -[[paypal.wallet]] +[[paypal_payout.wallet]] payment_method_type = "paypal" -[[paypal.wallet]] +[[paypal_payout.wallet]] payment_method_type = "venmo" [paypal_payout.connector_auth.BodyKey] api_key="Client Secret" diff --git a/crates/diesel_models/src/refund.rs b/crates/diesel_models/src/refund.rs index aac282992a8a..7d5b20c3d0cd 100644 --- a/crates/diesel_models/src/refund.rs +++ b/crates/diesel_models/src/refund.rs @@ -1,4 +1,7 @@ -use common_utils::{pii, types::ChargeRefunds}; +use common_utils::{ + pii, + types::{ChargeRefunds, MinorUnit}, +}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; @@ -20,9 +23,9 @@ pub struct Refund { pub connector_refund_id: Option, pub external_reference_id: Option, pub refund_type: storage_enums::RefundType, - pub total_amount: i64, + pub total_amount: MinorUnit, pub currency: storage_enums::Currency, - pub refund_amount: i64, + pub refund_amount: MinorUnit, pub refund_status: storage_enums::RefundStatus, pub sent_to_gateway: bool, pub refund_error_message: Option, @@ -65,9 +68,9 @@ pub struct RefundNew { pub connector: String, pub connector_refund_id: Option, pub refund_type: storage_enums::RefundType, - pub total_amount: i64, + pub total_amount: MinorUnit, pub currency: storage_enums::Currency, - pub refund_amount: i64, + pub refund_amount: MinorUnit, pub refund_status: storage_enums::RefundStatus, pub sent_to_gateway: bool, pub metadata: Option, diff --git a/crates/drainer/src/connection.rs b/crates/drainer/src/connection.rs index c539e9a734ce..ec3bc44973b6 100644 --- a/crates/drainer/src/connection.rs +++ b/crates/drainer/src/connection.rs @@ -1,6 +1,6 @@ use bb8::PooledConnection; +use common_utils::DbConnectionParams; use diesel::PgConnection; -use masking::PeekInterface; use crate::{settings::Database, Settings}; @@ -18,15 +18,12 @@ pub async fn redis_connection(conf: &Settings) -> redis_interface::RedisConnecti /// /// Will panic if could not create a db pool #[allow(clippy::expect_used)] -pub async fn diesel_make_pg_pool(database: &Database, _test_transaction: bool) -> PgPool { - let database_url = format!( - "postgres://{}:{}@{}:{}/{}", - database.username, - database.password.peek(), - database.host, - database.port, - database.dbname - ); +pub async fn diesel_make_pg_pool( + database: &Database, + _test_transaction: bool, + schema: &str, +) -> PgPool { + let database_url = database.get_database_url(schema); let manager = async_bb8_diesel::ConnectionManager::::new(database_url); let pool = bb8::Pool::builder() .max_size(database.pool_size) diff --git a/crates/drainer/src/handler.rs b/crates/drainer/src/handler.rs index 35ad467d5feb..77aefb422aff 100644 --- a/crates/drainer/src/handler.rs +++ b/crates/drainer/src/handler.rs @@ -1,4 +1,7 @@ -use std::sync::{atomic, Arc}; +use std::{ + collections::HashMap, + sync::{atomic, Arc}, +}; use router_env::tracing::Instrument; use tokio::{ @@ -31,12 +34,12 @@ pub struct HandlerInner { loop_interval: Duration, active_tasks: Arc, conf: DrainerSettings, - store: Arc, + stores: HashMap>, running: Arc, } impl Handler { - pub fn from_conf(conf: DrainerSettings, store: Arc) -> Self { + pub fn from_conf(conf: DrainerSettings, stores: HashMap>) -> Self { let shutdown_interval = Duration::from_millis(conf.shutdown_interval.into()); let loop_interval = Duration::from_millis(conf.loop_interval.into()); @@ -49,7 +52,7 @@ impl Handler { loop_interval, active_tasks, conf, - store, + stores, running, }; @@ -68,21 +71,23 @@ impl Handler { while self.running.load(atomic::Ordering::SeqCst) { metrics::DRAINER_HEALTH.add(&metrics::CONTEXT, 1, &[]); - if self.store.is_stream_available(stream_index).await { - let _task_handle = tokio::spawn( - drainer_handler( - self.store.clone(), - stream_index, - self.conf.max_read_count, - self.active_tasks.clone(), - jobs_picked.clone(), - ) - .in_current_span(), - ); + for store in self.stores.values() { + if store.is_stream_available(stream_index).await { + let _task_handle = tokio::spawn( + drainer_handler( + store.clone(), + stream_index, + self.conf.max_read_count, + self.active_tasks.clone(), + jobs_picked.clone(), + ) + .in_current_span(), + ); + } } stream_index = utils::increment_stream_index( (stream_index, jobs_picked.clone()), - self.store.config.drainer_num_partitions, + self.conf.num_partitions, ) .await; time::sleep(self.loop_interval).await; @@ -116,18 +121,33 @@ impl Handler { pub fn spawn_error_handlers(&self, tx: mpsc::Sender<()>) -> errors::DrainerResult<()> { let (redis_error_tx, redis_error_rx) = oneshot::channel(); + let redis_conn_clone = self + .stores + .values() + .next() + .map(|store| store.redis_conn.clone()); + match redis_conn_clone { + None => { + logger::error!("No redis connection found"); + Err( + errors::DrainerError::UnexpectedError("No redis connection found".to_string()) + .into(), + ) + } + Some(redis_conn_clone) => { + // Spawn a task to monitor if redis is down or not + let _task_handle = tokio::spawn( + async move { redis_conn_clone.on_error(redis_error_tx).await } + .in_current_span(), + ); - let redis_conn_clone = self.store.redis_conn.clone(); - - // Spawn a task to monitor if redis is down or not - let _task_handle = tokio::spawn( - async move { redis_conn_clone.on_error(redis_error_tx).await }.in_current_span(), - ); - - //Spawns a task to send shutdown signal if redis goes down - let _task_handle = tokio::spawn(redis_error_receiver(redis_error_rx, tx).in_current_span()); + //Spawns a task to send shutdown signal if redis goes down + let _task_handle = + tokio::spawn(redis_error_receiver(redis_error_rx, tx).in_current_span()); - Ok(()) + Ok(()) + } + } } } @@ -208,7 +228,10 @@ async fn drainer( }; // parse_stream_entries returns error if no entries is found, handle it - let entries = utils::parse_stream_entries(&stream_read, stream_name)?; + let entries = utils::parse_stream_entries( + &stream_read, + store.redis_conn.add_prefix(stream_name).as_str(), + )?; let read_count = entries.len(); metrics::JOBS_PICKED_PER_STREAM.add( diff --git a/crates/drainer/src/health_check.rs b/crates/drainer/src/health_check.rs index 443cb5b6f3fb..10514108991b 100644 --- a/crates/drainer/src/health_check.rs +++ b/crates/drainer/src/health_check.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use actix_web::{web, Scope}; use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl}; @@ -8,9 +8,9 @@ use error_stack::ResultExt; use router_env::{instrument, logger, tracing}; use crate::{ - connection::{pg_connection, redis_connection}, + connection::pg_connection, errors::HealthCheckError, - services::{self, Store}, + services::{self, log_and_return_error_response, Store}, Settings, }; @@ -20,10 +20,10 @@ pub const TEST_STREAM_DATA: &[(&str, &str)] = &[("data", "sample_data")]; pub struct Health; impl Health { - pub fn server(conf: Settings, store: Arc) -> Scope { + pub fn server(conf: Settings, stores: HashMap>) -> Scope { web::scope("health") .app_data(web::Data::new(conf)) - .app_data(web::Data::new(store)) + .app_data(web::Data::new(stores)) .service(web::resource("").route(web::get().to(health))) .service(web::resource("/ready").route(web::get().to(deep_health_check))) } @@ -38,25 +38,35 @@ pub async fn health() -> impl actix_web::Responder { #[instrument(skip_all)] pub async fn deep_health_check( conf: web::Data, - store: web::Data>, + stores: web::Data>>, ) -> impl actix_web::Responder { - match deep_health_check_func(conf, store).await { - Ok(response) => services::http_response_json( - serde_json::to_string(&response) + let mut deep_health_res = HashMap::new(); + for (tenant, store) in stores.iter() { + logger::info!("Tenant: {:?}", tenant); + + let response = match deep_health_check_func(conf.clone(), store).await { + Ok(response) => serde_json::to_string(&response) .map_err(|err| { logger::error!(serialization_error=?err); }) .unwrap_or_default(), - ), - - Err(err) => services::log_and_return_error_response(err), + Err(err) => return log_and_return_error_response(err), + }; + deep_health_res.insert(tenant.clone(), response); } + services::http_response_json( + serde_json::to_string(&deep_health_res) + .map_err(|err| { + logger::error!(serialization_error=?err); + }) + .unwrap_or_default(), + ) } #[instrument(skip_all)] pub async fn deep_health_check_func( conf: web::Data, - store: web::Data>, + store: &Arc, ) -> Result> { logger::info!("Deep health check was called"); @@ -146,8 +156,11 @@ impl HealthCheckInterface for Store { Ok(()) } - async fn health_check_redis(&self, conf: &Settings) -> CustomResult<(), HealthCheckRedisError> { - let redis_conn = redis_connection(conf).await; + async fn health_check_redis( + &self, + _conf: &Settings, + ) -> CustomResult<(), HealthCheckRedisError> { + let redis_conn = self.redis_conn.clone(); redis_conn .serialize_and_set_key_with_expiry("test_key", "test_value", 30) @@ -181,15 +194,14 @@ impl HealthCheckInterface for Store { logger::debug!("Stream append succeeded"); - let output = self - .redis_conn + let output = redis_conn .stream_read_entries(TEST_STREAM_NAME, "0-0", Some(10)) .await .change_context(HealthCheckRedisError::StreamReadFailed)?; logger::debug!("Stream read succeeded"); let (_, id_to_trim) = output - .get(TEST_STREAM_NAME) + .get(&redis_conn.add_prefix(TEST_STREAM_NAME)) .and_then(|entries| { entries .last() diff --git a/crates/drainer/src/lib.rs b/crates/drainer/src/lib.rs index cb2b55d202f9..5b67640663c6 100644 --- a/crates/drainer/src/lib.rs +++ b/crates/drainer/src/lib.rs @@ -10,7 +10,7 @@ pub mod settings; mod stream; mod types; mod utils; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; mod secrets_transformers; use actix_web::dev::Server; @@ -30,8 +30,11 @@ use crate::{ connection::pg_connection, services::Store, settings::DrainerSettings, types::StreamData, }; -pub async fn start_drainer(store: Arc, conf: DrainerSettings) -> errors::DrainerResult<()> { - let drainer_handler = handler::Handler::from_conf(conf, store); +pub async fn start_drainer( + stores: HashMap>, + conf: DrainerSettings, +) -> errors::DrainerResult<()> { + let drainer_handler = handler::Handler::from_conf(conf, stores); let (tx, rx) = mpsc::channel::<()>(1); @@ -59,11 +62,11 @@ pub async fn start_drainer(store: Arc, conf: DrainerSettings) -> errors:: pub async fn start_web_server( conf: Settings, - store: Arc, + stores: HashMap>, ) -> Result { let server = conf.server.clone(); let web_server = actix_web::HttpServer::new(move || { - actix_web::App::new().service(health_check::Health::server(conf.clone(), store.clone())) + actix_web::App::new().service(health_check::Health::server(conf.clone(), stores.clone())) }) .bind((server.host.as_str(), server.port))? .run(); diff --git a/crates/drainer/src/main.rs b/crates/drainer/src/main.rs index 64e66619d744..f3f822129c34 100644 --- a/crates/drainer/src/main.rs +++ b/crates/drainer/src/main.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use drainer::{ errors::DrainerResult, logger::logger, services, settings, start_drainer, start_web_server, }; @@ -17,7 +19,11 @@ async fn main() -> DrainerResult<()> { let state = settings::AppState::new(conf.clone()).await; - let store = std::sync::Arc::new(services::Store::new(&state.conf, false).await); + let mut stores = HashMap::new(); + for (tenant_name, tenant) in conf.multitenancy.get_tenants() { + let store = std::sync::Arc::new(services::Store::new(&state.conf, false, tenant).await); + stores.insert(tenant_name.clone(), store); + } #[cfg(feature = "vergen")] println!("Starting drainer (Version: {})", router_env::git_tag!()); @@ -29,9 +35,12 @@ async fn main() -> DrainerResult<()> { ); #[allow(clippy::expect_used)] - let web_server = Box::pin(start_web_server(state.conf.as_ref().clone(), store.clone())) - .await - .expect("Failed to create the server"); + let web_server = Box::pin(start_web_server( + state.conf.as_ref().clone(), + stores.clone(), + )) + .await + .expect("Failed to create the server"); tokio::spawn( async move { @@ -44,7 +53,7 @@ async fn main() -> DrainerResult<()> { logger::debug!(startup_config=?conf); logger::info!("Drainer started [{:?}] [{:?}]", conf.drainer, conf.log); - start_drainer(store.clone(), conf.drainer).await?; + start_drainer(stores.clone(), conf.drainer).await?; Ok(()) } diff --git a/crates/drainer/src/secrets_transformers.rs b/crates/drainer/src/secrets_transformers.rs index 4ffb584a1529..0582ccd798b1 100644 --- a/crates/drainer/src/secrets_transformers.rs +++ b/crates/drainer/src/secrets_transformers.rs @@ -45,5 +45,6 @@ pub async fn fetch_raw_secrets( drainer: conf.drainer, encryption_management: conf.encryption_management, secrets_management: conf.secrets_management, + multitenancy: conf.multitenancy, } } diff --git a/crates/drainer/src/services.rs b/crates/drainer/src/services.rs index 9e0165548a01..55ffe0c4e7fa 100644 --- a/crates/drainer/src/services.rs +++ b/crates/drainer/src/services.rs @@ -2,16 +2,18 @@ use std::sync::Arc; use actix_web::{body, HttpResponse, ResponseError}; use error_stack::Report; +use redis_interface::RedisConnectionPool; use crate::{ connection::{diesel_make_pg_pool, PgPool}, logger, + settings::Tenant, }; #[derive(Clone)] pub struct Store { pub master_pool: PgPool, - pub redis_conn: Arc, + pub redis_conn: Arc, pub config: StoreConfig, pub request_id: Option, } @@ -28,11 +30,19 @@ impl Store { /// Panics if there is a failure while obtaining the HashiCorp client using the provided configuration. /// This panic indicates a critical failure in setting up external services, and the application cannot proceed without a valid HashiCorp client. /// - pub async fn new(config: &crate::Settings, test_transaction: bool) -> Self { + pub async fn new(config: &crate::Settings, test_transaction: bool, tenant: &Tenant) -> Self { + let redis_conn = crate::connection::redis_connection(config).await; Self { - master_pool: diesel_make_pg_pool(config.master_database.get_inner(), test_transaction) - .await, - redis_conn: Arc::new(crate::connection::redis_connection(config).await), + master_pool: diesel_make_pg_pool( + config.master_database.get_inner(), + test_transaction, + &tenant.schema, + ) + .await, + redis_conn: Arc::new(RedisConnectionPool::clone( + &redis_conn, + &tenant.redis_key_prefix, + )), config: StoreConfig { drainer_stream_name: config.drainer.stream_name.clone(), drainer_num_partitions: config.drainer.num_partitions, diff --git a/crates/drainer/src/settings.rs b/crates/drainer/src/settings.rs index e68ede9e8b2e..311cee0cede7 100644 --- a/crates/drainer/src/settings.rs +++ b/crates/drainer/src/settings.rs @@ -1,6 +1,6 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{collections::HashMap, path::PathBuf, sync::Arc}; -use common_utils::ext_traits::ConfigExt; +use common_utils::{ext_traits::ConfigExt, DbConnectionParams}; use config::{Environment, File}; use external_services::managers::{ encryption_management::EncryptionManagementConfig, secrets_management::SecretsManagementConfig, @@ -73,6 +73,7 @@ pub struct Settings { pub drainer: DrainerSettings, pub encryption_management: EncryptionManagementConfig, pub secrets_management: SecretsManagementConfig, + pub multitenancy: Multitenancy, } #[derive(Debug, Deserialize, Clone)] @@ -87,6 +88,24 @@ pub struct Database { pub connection_timeout: u64, } +impl DbConnectionParams for Database { + fn get_username(&self) -> &str { + &self.username + } + fn get_password(&self) -> Secret { + self.password.clone() + } + fn get_host(&self) -> &str { + &self.host + } + fn get_port(&self) -> u16 { + self.port + } + fn get_dbname(&self) -> &str { + &self.dbname + } +} + #[derive(Debug, Clone, Deserialize)] #[serde(default)] pub struct DrainerSettings { @@ -97,6 +116,35 @@ pub struct DrainerSettings { pub loop_interval: u32, // in milliseconds } +#[derive(Debug, Deserialize, Clone, Default)] +pub struct Multitenancy { + pub enabled: bool, + pub tenants: TenantConfig, +} +impl Multitenancy { + pub fn get_tenants(&self) -> &HashMap { + &self.tenants.0 + } + pub fn get_tenant_names(&self) -> Vec { + self.tenants.0.keys().cloned().collect() + } + pub fn get_tenant(&self, tenant_id: &str) -> Option<&Tenant> { + self.tenants.0.get(tenant_id) + } +} + +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(transparent)] +pub struct TenantConfig(pub HashMap); + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct Tenant { + pub name: String, + pub base_url: String, + pub schema: String, + pub redis_key_prefix: String, +} + #[derive(Debug, Deserialize, Clone)] #[serde(default)] pub struct Server { diff --git a/crates/euclid/src/enums.rs b/crates/euclid/src/enums.rs index 8e65d23d5ea9..1aba6338d9d2 100644 --- a/crates/euclid/src/enums.rs +++ b/crates/euclid/src/enums.rs @@ -79,8 +79,6 @@ pub enum MandateAcceptanceType { pub enum PaymentType { SetupMandate, NonMandate, - NewMandate, - UpdateMandate, } #[derive( diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index 26a6f1618fa5..053eb493ce48 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -271,6 +271,10 @@ pub enum ApiErrorResponse { InvalidCookie, #[error(error_type = ErrorType::InvalidRequestError, code = "IR_27", message = "Extended card info does not exist")] ExtendedCardInfoNotFound, + #[error(error_type = ErrorType::ProcessingError, code = "HE_06", message = "Missing tenant id")] + MissingTenantId, + #[error(error_type = ErrorType::ProcessingError, code = "HE_06", message = "Invalid tenant id: {tenant_id}")] + InvalidTenant { tenant_id: String }, } #[derive(Clone)] @@ -603,6 +607,12 @@ impl ErrorSwitch for ApiErrorRespon Self::ExtendedCardInfoNotFound => { AER::NotFound(ApiError::new("IR", 27, "Extended card info does not exist", None)) } + Self::MissingTenantId => { + AER::InternalServerError(ApiError::new("HE", 6, "Missing Tenant ID in the request".to_string(), None)) + } + Self::InvalidTenant { tenant_id } => { + AER::InternalServerError(ApiError::new("HE", 6, format!("Invalid Tenant {tenant_id}"), None)) + } } } } diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index ac1ab08893ba..d9778353cdaa 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -6,6 +6,7 @@ pub mod payments; #[cfg(feature = "payouts")] pub mod payouts; pub mod router_data; +pub mod router_flow_types; pub mod router_request_types; pub mod router_response_types; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types.rs b/crates/hyperswitch_domain_models/src/router_flow_types.rs new file mode 100644 index 000000000000..065b5f0f40b4 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types.rs @@ -0,0 +1,17 @@ +pub mod access_token_auth; +pub mod dispute; +pub mod files; +pub mod fraud_check; +pub mod payments; +pub mod payouts; +pub mod refunds; +pub mod webhooks; + +pub use access_token_auth::*; +pub use dispute::*; +pub use files::*; +pub use fraud_check::*; +pub use payments::*; +pub use payouts::*; +pub use refunds::*; +pub use webhooks::*; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/access_token_auth.rs b/crates/hyperswitch_domain_models/src/router_flow_types/access_token_auth.rs new file mode 100644 index 000000000000..dd45ca9ca372 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/access_token_auth.rs @@ -0,0 +1,2 @@ +#[derive(Clone, Debug)] +pub struct AccessTokenAuth; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/dispute.rs b/crates/hyperswitch_domain_models/src/router_flow_types/dispute.rs new file mode 100644 index 000000000000..ec13cae516f3 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/dispute.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Clone)] +pub struct Accept; +#[derive(Debug, Clone)] +pub struct Evidence; + +#[derive(Debug, Clone)] +pub struct Defend; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/files.rs b/crates/hyperswitch_domain_models/src/router_flow_types/files.rs new file mode 100644 index 000000000000..210b6ef54380 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/files.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone)] +pub struct Retrieve; + +#[derive(Debug, Clone)] +pub struct Upload; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/fraud_check.rs b/crates/hyperswitch_domain_models/src/router_flow_types/fraud_check.rs new file mode 100644 index 000000000000..a7642ea7e48a --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/fraud_check.rs @@ -0,0 +1,14 @@ +#[derive(Debug, Clone)] +pub struct Sale; + +#[derive(Debug, Clone)] +pub struct Checkout; + +#[derive(Debug, Clone)] +pub struct Transaction; + +#[derive(Debug, Clone)] +pub struct Fulfillment; + +#[derive(Debug, Clone)] +pub struct RecordReturn; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs b/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs new file mode 100644 index 000000000000..046866beea55 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs @@ -0,0 +1,48 @@ +// Core related api layer. +#[derive(Debug, Clone)] +pub struct Authorize; + +#[derive(Debug, Clone)] +pub struct AuthorizeSessionToken; + +#[derive(Debug, Clone)] +pub struct CompleteAuthorize; + +#[derive(Debug, Clone)] +pub struct Approve; + +// Used in gift cards balance check +#[derive(Debug, Clone)] +pub struct Balance; + +#[derive(Debug, Clone)] +pub struct InitPayment; + +#[derive(Debug, Clone)] +pub struct Capture; + +#[derive(Debug, Clone)] +pub struct PSync; +#[derive(Debug, Clone)] +pub struct Void; + +#[derive(Debug, Clone)] +pub struct Reject; + +#[derive(Debug, Clone)] +pub struct Session; + +#[derive(Debug, Clone)] +pub struct PaymentMethodToken; + +#[derive(Debug, Clone)] +pub struct CreateConnectorCustomer; + +#[derive(Debug, Clone)] +pub struct SetupMandate; + +#[derive(Debug, Clone)] +pub struct PreProcessing; + +#[derive(Debug, Clone)] +pub struct IncrementalAuthorization; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/payouts.rs b/crates/hyperswitch_domain_models/src/router_flow_types/payouts.rs new file mode 100644 index 000000000000..e0f9c18cef99 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/payouts.rs @@ -0,0 +1,20 @@ +#[derive(Debug, Clone)] +pub struct PoCancel; + +#[derive(Debug, Clone)] +pub struct PoCreate; + +#[derive(Debug, Clone)] +pub struct PoEligibility; + +#[derive(Debug, Clone)] +pub struct PoFulfill; + +#[derive(Debug, Clone)] +pub struct PoQuote; + +#[derive(Debug, Clone)] +pub struct PoRecipient; + +#[derive(Debug, Clone)] +pub struct PoRecipientAccount; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/refunds.rs b/crates/hyperswitch_domain_models/src/router_flow_types/refunds.rs new file mode 100644 index 000000000000..eea6c7d0bb06 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/refunds.rs @@ -0,0 +1,4 @@ +#[derive(Debug, Clone)] +pub struct Execute; +#[derive(Debug, Clone)] +pub struct RSync; diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/webhooks.rs b/crates/hyperswitch_domain_models/src/router_flow_types/webhooks.rs new file mode 100644 index 000000000000..6f28a7f7b35c --- /dev/null +++ b/crates/hyperswitch_domain_models/src/router_flow_types/webhooks.rs @@ -0,0 +1,2 @@ +#[derive(Clone, Debug)] +pub struct VerifyWebhookSource; diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index 5a4838f9b79f..f06f336b8441 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -1,7 +1,9 @@ pub mod authentication; pub mod fraud_check; use api_models::payments::RequestSurchargeDetails; -use common_utils::{consts, errors, ext_traits::OptionExt, id_type, pii, types as common_types}; +use common_utils::{ + consts, errors, ext_traits::OptionExt, id_type, pii, types as common_types, types::MinorUnit, +}; use diesel_models::enums as storage_enums; use error_stack::ResultExt; use masking::Secret; @@ -13,7 +15,7 @@ use crate::{ errors::api_error_response::ApiErrorResponse, mandates, payments, router_data::{self, RouterData}, - router_response_types as response_types, + router_flow_types as flows, router_response_types as response_types, }; #[derive(Debug, Clone)] pub struct PaymentsAuthorizeData { @@ -57,6 +59,9 @@ pub struct PaymentsAuthorizeData { pub metadata: Option, pub authentication_data: Option, pub charges: Option, + + // New amount for amount frame work + pub minor_amount: MinorUnit, } #[derive(Debug, serde::Deserialize, Clone)] @@ -77,6 +82,10 @@ pub struct PaymentsCaptureData { pub browser_info: Option, pub metadata: Option, // This metadata is used to store the metadata shared during the payment intent request. + + // New amount for amount frame work + pub minor_payment_amount: MinorUnit, + pub minor_amount_to_capture: MinorUnit, } #[derive(Debug, Clone, Default)] @@ -125,13 +134,19 @@ impl TryFrom for ConnectorCustomerData { }) } } -impl TryFrom<&RouterData> - for ConnectorCustomerData +impl + TryFrom< + &RouterData, + > for ConnectorCustomerData { type Error = error_stack::Report; fn try_from( - data: &RouterData, + data: &RouterData< + flows::Authorize, + PaymentsAuthorizeData, + response_types::PaymentsResponseData, + >, ) -> Result { Ok(Self { email: data.request.email.clone(), @@ -296,6 +311,9 @@ pub struct CompleteAuthorizeData { pub connector_meta: Option, pub complete_authorize_url: Option, pub metadata: Option, + + // New amount for amount frame work + pub minor_amount: MinorUnit, } #[derive(Debug, Clone)] @@ -387,18 +405,18 @@ impl ResponseId { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct SurchargeDetails { /// original_amount - pub original_amount: common_utils::types::MinorUnit, + pub original_amount: MinorUnit, /// surcharge value pub surcharge: common_utils::types::Surcharge, /// tax on surcharge value pub tax_on_surcharge: Option>, /// surcharge amount for this payment - pub surcharge_amount: common_utils::types::MinorUnit, + pub surcharge_amount: MinorUnit, /// tax on surcharge amount for this payment - pub tax_on_surcharge_amount: common_utils::types::MinorUnit, + pub tax_on_surcharge_amount: MinorUnit, /// sum of original amount, - pub final_amount: common_utils::types::MinorUnit, + pub final_amount: MinorUnit, } impl SurchargeDetails { @@ -410,7 +428,7 @@ impl SurchargeDetails { && request_surcharge_details.tax_amount.unwrap_or_default() == self.tax_on_surcharge_amount } - pub fn get_total_surcharge_amount(&self) -> common_utils::types::MinorUnit { + pub fn get_total_surcharge_amount(&self) -> MinorUnit { self.surcharge_amount + self.tax_on_surcharge_amount } } @@ -460,6 +478,7 @@ pub struct RefundsData { pub currency: storage_enums::Currency, /// Amount for the payment against which this refund is issued pub payment_amount: i64, + pub reason: Option, pub webhook_url: Option, /// Amount to be refunded @@ -469,6 +488,10 @@ pub struct RefundsData { pub browser_info: Option, /// Charges associated with the payment pub charges: Option, + + // New amount for amount frame work + pub minor_payment_amount: MinorUnit, + pub minor_refund_amount: MinorUnit, } #[derive(Debug, serde::Deserialize, Clone)] diff --git a/crates/redis_interface/src/commands.rs b/crates/redis_interface/src/commands.rs index 504820822c08..ccfeb3e5b12e 100644 --- a/crates/redis_interface/src/commands.rs +++ b/crates/redis_interface/src/commands.rs @@ -31,6 +31,13 @@ use crate::{ }; impl super::RedisConnectionPool { + pub fn add_prefix(&self, key: &str) -> String { + if self.key_prefix.is_empty() { + key.to_string() + } else { + format!("{}:{}", self.key_prefix, key) + } + } #[instrument(level = "DEBUG", skip(self))] pub async fn set_key(&self, key: &str, value: V) -> CustomResult<(), errors::RedisError> where @@ -39,7 +46,7 @@ impl super::RedisConnectionPool { { self.pool .set( - key, + self.add_prefix(key), value, Some(Expiration::EX(self.config.default_ttl.into())), None, @@ -144,7 +151,7 @@ impl super::RedisConnectionPool { self.pool .set( - key, + self.add_prefix(key), serialized.as_slice(), Some(Expiration::EX(seconds)), None, @@ -160,7 +167,7 @@ impl super::RedisConnectionPool { V: FromRedis + Unpin + Send + 'static, { self.pool - .get(key) + .get(self.add_prefix(key)) .await .change_context(errors::RedisError::GetFailed) } @@ -171,7 +178,7 @@ impl super::RedisConnectionPool { V: Into + Unpin + Send + 'static, { self.pool - .exists(key) + .exists(self.add_prefix(key)) .await .change_context(errors::RedisError::GetFailed) } @@ -197,7 +204,7 @@ impl super::RedisConnectionPool { #[instrument(level = "DEBUG", skip(self))] pub async fn delete_key(&self, key: &str) -> CustomResult { self.pool - .del(key) + .del(self.add_prefix(key)) .await .change_context(errors::RedisError::DeleteFailed) } @@ -214,7 +221,13 @@ impl super::RedisConnectionPool { V::Error: Into + Send + Sync, { self.pool - .set(key, value, Some(Expiration::EX(seconds)), None, false) + .set( + self.add_prefix(key), + value, + Some(Expiration::EX(seconds)), + None, + false, + ) .await .change_context(errors::RedisError::SetExFailed) } @@ -232,7 +245,7 @@ impl super::RedisConnectionPool { { self.pool .set( - key, + self.add_prefix(key), value, Some(Expiration::EX( seconds.unwrap_or(self.config.default_ttl.into()), @@ -251,7 +264,7 @@ impl super::RedisConnectionPool { seconds: i64, ) -> CustomResult<(), errors::RedisError> { self.pool - .expire(key, seconds) + .expire(self.add_prefix(key), seconds) .await .change_context(errors::RedisError::SetExpiryFailed) } @@ -263,7 +276,7 @@ impl super::RedisConnectionPool { timestamp: i64, ) -> CustomResult<(), errors::RedisError> { self.pool - .expire_at(key, timestamp) + .expire_at(self.add_prefix(key), timestamp) .await .change_context(errors::RedisError::SetExpiryFailed) } @@ -281,7 +294,7 @@ impl super::RedisConnectionPool { { let output: Result<(), _> = self .pool - .hset(key, values) + .hset(self.add_prefix(key), values) .await .change_context(errors::RedisError::SetHashFailed); // setting expiry for the key @@ -306,7 +319,7 @@ impl super::RedisConnectionPool { { let output: Result = self .pool - .hsetnx(key, field, value) + .hsetnx(self.add_prefix(key), field, value) .await .change_context(errors::RedisError::SetHashFieldFailed); @@ -368,7 +381,7 @@ impl super::RedisConnectionPool { Ok(self .pool .next() - .hscan::<&str, &str>(key, pattern, count) + .hscan::<&str, &str>(&self.add_prefix(key), pattern, count) .filter_map(|value| async move { match value { Ok(mut v) => { @@ -419,7 +432,7 @@ impl super::RedisConnectionPool { V: FromRedis + Unpin + Send + 'static, { self.pool - .hget(key, field) + .hget(self.add_prefix(key), field) .await .change_context(errors::RedisError::GetHashFieldFailed) } @@ -456,7 +469,7 @@ impl super::RedisConnectionPool { V::Error: Into + Send, { self.pool - .sadd(key, members) + .sadd(self.add_prefix(key), members) .await .change_context(errors::RedisError::SetAddMembersFailed) } @@ -473,7 +486,7 @@ impl super::RedisConnectionPool { F::Error: Into + Send + Sync, { self.pool - .xadd(stream, false, None, entry_id, fields) + .xadd(self.add_prefix(stream), false, None, entry_id, fields) .await .change_context(errors::RedisError::StreamAppendFailed) } @@ -488,7 +501,7 @@ impl super::RedisConnectionPool { Ids: Into + Debug + Send + Sync, { self.pool - .xdel(stream, ids) + .xdel(self.add_prefix(stream), ids) .await .change_context(errors::RedisError::StreamDeleteFailed) } @@ -504,7 +517,7 @@ impl super::RedisConnectionPool { C::Error: Into + Send + Sync, { self.pool - .xtrim(stream, xcap) + .xtrim(self.add_prefix(stream), xcap) .await .change_context(errors::RedisError::StreamTrimFailed) } @@ -520,22 +533,34 @@ impl super::RedisConnectionPool { Ids: Into + Debug + Send + Sync, { self.pool - .xack(stream, group, ids) + .xack(self.add_prefix(stream), group, ids) .await .change_context(errors::RedisError::StreamAcknowledgeFailed) } #[instrument(level = "DEBUG", skip(self))] - pub async fn stream_get_length(&self, stream: K) -> CustomResult - where - K: Into + Debug + Send + Sync, - { + pub async fn stream_get_length(&self, stream: &str) -> CustomResult { self.pool - .xlen(stream) + .xlen(self.add_prefix(stream)) .await .change_context(errors::RedisError::GetLengthFailed) } + pub fn get_keys_with_prefix(&self, keys: K) -> MultipleKeys + where + K: Into + Debug + Send + Sync, + { + let multiple_keys: MultipleKeys = keys.into(); + let res = multiple_keys + .inner() + .iter() + .filter_map(|key| key.as_str()) + .map(|k| self.add_prefix(k)) + .map(RedisKey::from) + .collect::>(); + MultipleKeys::from(res) + } + #[instrument(level = "DEBUG", skip(self))] pub async fn stream_read_entries( &self, @@ -547,11 +572,12 @@ impl super::RedisConnectionPool { K: Into + Debug + Send + Sync, Ids: Into + Debug + Send + Sync, { + let strms = self.get_keys_with_prefix(streams); self.pool .xread_map( Some(read_count.unwrap_or(self.config.default_stream_read_count)), None, - streams, + strms, ids, ) .await @@ -579,10 +605,22 @@ impl super::RedisConnectionPool { match group { Some((group_name, consumer_name)) => { self.pool - .xreadgroup_map(group_name, consumer_name, count, block, false, streams, ids) + .xreadgroup_map( + group_name, + consumer_name, + count, + block, + false, + self.get_keys_with_prefix(streams), + ids, + ) + .await + } + None => { + self.pool + .xread_map(count, block, self.get_keys_with_prefix(streams), ids) .await } - None => self.pool.xread_map(count, block, streams, ids).await, } .map_err(|err| match err.kind() { RedisErrorKind::NotFound | RedisErrorKind::Parse => { @@ -610,7 +648,7 @@ impl super::RedisConnectionPool { } self.pool - .xgroup_create(stream, group, id, true) + .xgroup_create(self.add_prefix(stream), group, id, true) .await .change_context(errors::RedisError::ConsumerGroupCreateFailed) } @@ -622,7 +660,7 @@ impl super::RedisConnectionPool { group: &str, ) -> CustomResult { self.pool - .xgroup_destroy(stream, group) + .xgroup_destroy(self.add_prefix(stream), group) .await .change_context(errors::RedisError::ConsumerGroupDestroyFailed) } @@ -636,7 +674,7 @@ impl super::RedisConnectionPool { consumer: &str, ) -> CustomResult { self.pool - .xgroup_delconsumer(stream, group, consumer) + .xgroup_delconsumer(self.add_prefix(stream), group, consumer) .await .change_context(errors::RedisError::ConsumerGroupRemoveConsumerFailed) } @@ -649,7 +687,7 @@ impl super::RedisConnectionPool { id: &RedisEntryId, ) -> CustomResult { self.pool - .xgroup_setid(stream, group, id) + .xgroup_setid(self.add_prefix(stream), group, id) .await .change_context(errors::RedisError::ConsumerGroupSetIdFailed) } @@ -669,7 +707,7 @@ impl super::RedisConnectionPool { { self.pool .xclaim( - stream, + self.add_prefix(stream), group, consumer, min_idle_time, diff --git a/crates/redis_interface/src/lib.rs b/crates/redis_interface/src/lib.rs index df74d728331f..dc4fd3bbc9cc 100644 --- a/crates/redis_interface/src/lib.rs +++ b/crates/redis_interface/src/lib.rs @@ -30,10 +30,11 @@ use fred::{interfaces::ClientLike, prelude::EventInterface}; pub use self::types::*; pub struct RedisConnectionPool { - pub pool: fred::prelude::RedisPool, - config: RedisConfig, - pub subscriber: SubscriberClient, - pub publisher: RedisClient, + pub pool: Arc, + pub key_prefix: String, + pub config: Arc, + pub subscriber: Arc, + pub publisher: Arc, pub is_redis_available: Arc, } @@ -166,14 +167,24 @@ impl RedisConnectionPool { let config = RedisConfig::from(conf); Ok(Self { - pool, - config, + pool: Arc::new(pool), + config: Arc::new(config), is_redis_available: Arc::new(atomic::AtomicBool::new(true)), - subscriber, - publisher, + subscriber: Arc::new(subscriber), + publisher: Arc::new(publisher), + key_prefix: String::default(), }) } - + pub fn clone(&self, key_prefix: &str) -> Self { + Self { + pool: Arc::clone(&self.pool), + key_prefix: key_prefix.to_string(), + config: Arc::clone(&self.config), + subscriber: Arc::clone(&self.subscriber), + publisher: Arc::clone(&self.publisher), + is_redis_available: Arc::clone(&self.is_redis_available), + } + } pub async fn on_error(&self, tx: tokio::sync::oneshot::Sender<()>) { use futures::StreamExt; use tokio_stream::wrappers::BroadcastStream; @@ -211,7 +222,7 @@ impl RedisConnectionPool { } } -struct RedisConfig { +pub struct RedisConfig { default_ttl: u32, default_stream_read_count: u64, default_hash_ttl: u32, diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 8155ad50c532..e2d775f0bbbe 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -128,6 +128,7 @@ actix-http = "3.6.0" events = { version = "0.1.0", path = "../events" } totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"]} serde_repr = "0.1.19" +unidecode = "0.3.0" [build-dependencies] router_env = { version = "0.1.0", path = "../router_env", default-features = false } diff --git a/crates/router/src/bin/scheduler.rs b/crates/router/src/bin/scheduler.rs index 5341a1b09c1e..7e78a355e5c4 100644 --- a/crates/router/src/bin/scheduler.rs +++ b/crates/router/src/bin/scheduler.rs @@ -1,5 +1,5 @@ #![recursion_limit = "256"] -use std::{str::FromStr, sync::Arc}; +use std::{collections::HashMap, str::FromStr, sync::Arc}; use actix_web::{dev::Server, web, Scope}; use api_models::health_check::SchedulerHealthCheckResponse; @@ -22,7 +22,7 @@ use router_env::{ }; use scheduler::{ consumer::workflows::ProcessTrackerWorkflow, errors::ProcessTrackerError, - workflows::ProcessTrackerWorkflows, SchedulerAppState, + workflows::ProcessTrackerWorkflows, SchedulerSessionState, }; use storage_impl::errors::ApplicationError; use tokio::sync::{mpsc, oneshot}; @@ -146,24 +146,54 @@ pub async fn deep_health_check( state: web::Data, service: web::Data, ) -> impl actix_web::Responder { - let report = deep_health_check_func(state, service).await; - match report { - Ok(response) => services::http_response_json( - serde_json::to_string(&response) - .map_err(|err| { - logger::error!(serialization_error=?err); - }) - .unwrap_or_default(), - ), - Err(err) => api::log_and_return_error_response(err), + let mut checks = HashMap::new(); + let stores = state.stores.clone(); + let app_state = Arc::clone(&state.into_inner()); + let service_name = service.into_inner(); + for (tenant, _) in stores { + let session_state_res = app_state.clone().get_session_state(&tenant, || { + errors::ApiErrorResponse::MissingRequiredField { + field_name: "tenant_id", + } + .into() + }); + let session_state = match session_state_res { + Ok(state) => state, + Err(err) => { + return api::log_and_return_error_response(err); + } + }; + let report = deep_health_check_func(session_state, &service_name).await; + match report { + Ok(response) => { + checks.insert( + tenant, + serde_json::to_string(&response) + .map_err(|err| { + logger::error!(serialization_error=?err); + }) + .unwrap_or_default(), + ); + } + Err(err) => { + return api::log_and_return_error_response(err); + } + } } + services::http_response_json( + serde_json::to_string(&checks) + .map_err(|err| { + logger::error!(serialization_error=?err); + }) + .unwrap_or_default(), + ) } #[instrument(skip_all)] pub async fn deep_health_check_func( - state: web::Data, - service: web::Data, + state: routes::SessionState, + service: &str, ) -> errors::RouterResult { - logger::info!("{} deep health check was called", service.into_inner()); + logger::info!("{} deep health check was called", service); logger::debug!("Database health check begin"); @@ -215,10 +245,10 @@ pub async fn deep_health_check_func( pub struct WorkflowRunner; #[async_trait::async_trait] -impl ProcessTrackerWorkflows for WorkflowRunner { +impl ProcessTrackerWorkflows for WorkflowRunner { async fn trigger_workflow<'a>( &'a self, - state: &'a routes::AppState, + state: &'a routes::SessionState, process: storage::ProcessTracker, ) -> CustomResult<(), ProcessTrackerError> { let runner = process @@ -233,7 +263,7 @@ impl ProcessTrackerWorkflows for WorkflowRunner { .attach_printable("Failed to parse workflow runner name")?; let get_operation = |runner: storage::ProcessTrackerRunner| -> CustomResult< - Box>, + Box>, ProcessTrackerError, > { match runner { @@ -286,7 +316,7 @@ impl ProcessTrackerWorkflows for WorkflowRunner { let operation = get_operation(runner)?; let app_state = &state.clone(); - let output = operation.execute_workflow(app_state, process.clone()).await; + let output = operation.execute_workflow(state, process.clone()).await; match output { Ok(_) => operation.success_handler(app_state, process).await, Err(error) => match operation @@ -327,6 +357,10 @@ async fn start_scheduler( Arc::new(scheduler_settings), channel, WorkflowRunner {}, + |state, tenant| { + Arc::new(state.clone()) + .get_session_state(tenant, || ProcessTrackerError::TenantNotFound.into()) + }, ) .await } diff --git a/crates/router/src/compatibility/stripe/customers.rs b/crates/router/src/compatibility/stripe/customers.rs index 67c0f973b969..65abb012b9a7 100644 --- a/crates/router/src/compatibility/stripe/customers.rs +++ b/crates/router/src/compatibility/stripe/customers.rs @@ -155,7 +155,7 @@ pub async fn customer_delete( state.into_inner(), &req, payload, - |state, auth, req, _| { + |state, auth: auth::AuthenticationData, req, _| { customers::delete_customer(state, auth.merchant_account, req, auth.key_store) }, &auth::ApiKeyAuth, diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index 528381061cf5..17fcea9b828f 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -264,6 +264,8 @@ pub enum StripeErrorCode { PaymentMethodDeleteFailed, #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Extended card info does not exist")] ExtendedCardInfoNotFound, + #[error(error_type = StripeErrorType::InvalidRequestError, code = "IR_28", message = "Invalid tenant")] + InvalidTenant, // [#216]: https://github.com/juspay/hyperswitch/issues/216 // Implement the remaining stripe error codes @@ -646,6 +648,8 @@ impl From for StripeErrorCode { Self::InvalidWalletToken { wallet_name } } errors::ApiErrorResponse::ExtendedCardInfoNotFound => Self::ExtendedCardInfoNotFound, + errors::ApiErrorResponse::InvalidTenant { tenant_id: _ } + | errors::ApiErrorResponse::MissingTenantId => Self::InvalidTenant, } } } @@ -725,7 +729,8 @@ impl actix_web::ResponseError for StripeErrorCode { | Self::InternalServerError | Self::MandateActive | Self::CustomerRedacted - | Self::WebhookProcessingError => StatusCode::INTERNAL_SERVER_ERROR, + | Self::WebhookProcessingError + | Self::InvalidTenant => StatusCode::INTERNAL_SERVER_ERROR, Self::ReturnUrlUnavailable => StatusCode::SERVICE_UNAVAILABLE, Self::ExternalConnectorError { status_code, .. } => { StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR) diff --git a/crates/router/src/compatibility/stripe/refunds/types.rs b/crates/router/src/compatibility/stripe/refunds/types.rs index 10a018361446..d00da0d8588c 100644 --- a/crates/router/src/compatibility/stripe/refunds/types.rs +++ b/crates/router/src/compatibility/stripe/refunds/types.rs @@ -46,7 +46,7 @@ impl From for refunds::RefundRequest { fn from(req: StripeCreateRefundRequest) -> Self { Self { refund_id: req.refund_id, - amount: req.amount, + amount: req.amount.map(common_utils::types::MinorUnit::new), payment_id: req.payment_intent, reason: req.reason, refund_type: Some(refunds::RefundType::Instant), @@ -82,7 +82,7 @@ impl From for StripeRefundResponse { fn from(res: refunds::RefundResponse) -> Self { Self { id: res.refund_id, - amount: res.amount, + amount: res.amount.get_amount_as_i64(), currency: res.currency.to_ascii_lowercase(), payment_intent: res.payment_id, status: res.status.into(), diff --git a/crates/router/src/compatibility/wrap.rs b/crates/router/src/compatibility/wrap.rs index 96163727da37..b6012c425a58 100644 --- a/crates/router/src/compatibility/wrap.rs +++ b/crates/router/src/compatibility/wrap.rs @@ -10,7 +10,7 @@ use crate::{ events::api_logs::ApiEventMetric, routes::{ app::{AppStateInfo, ReqState}, - metrics, AppState, + metrics, AppState, SessionState, }, services::{self, api, authentication as auth, logger}, }; @@ -22,11 +22,11 @@ pub async fn compatibility_api_wrap<'a, 'b, U, T, Q, F, Fut, S, E, E2>( request: &'a HttpRequest, payload: T, func: F, - api_authentication: &dyn auth::AuthenticateAndFetch, + api_authentication: &dyn auth::AuthenticateAndFetch, lock_action: api_locking::LockAction, ) -> HttpResponse where - F: Fn(AppState, U, T, ReqState) -> Fut, + F: Fn(SessionState, U, T, ReqState) -> Fut, Fut: Future, E2>>, E2: ErrorSwitch + std::error::Error + Send + Sync + 'static, Q: Serialize + std::fmt::Debug + 'a + ApiEventMetric, @@ -49,6 +49,7 @@ where api::server_wrap_util( &flow, state.clone().into(), + request.headers(), req_state, request, payload, diff --git a/crates/router/src/configs/defaults.rs b/crates/router/src/configs/defaults.rs index 77f424d8333a..c989e4c04c0d 100644 --- a/crates/router/src/configs/defaults.rs +++ b/crates/router/src/configs/defaults.rs @@ -11,7 +11,6 @@ impl Default for super::settings::Server { workers: num_cpus::get_physical(), host: "localhost".into(), request_body_limit: 16 * 1024, // POST request body is limited to 16KiB - base_url: "http://localhost:8080".into(), shutdown_timeout: 30, } } diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index dc8b5dd6bd53..2f90833905e6 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -367,5 +367,6 @@ pub(crate) async fn fetch_raw_secrets( cors: conf.cors, unmasked_headers: conf.unmasked_headers, saved_payment_methods: conf.saved_payment_methods, + multitenancy: conf.multitenancy, } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 8ac27cce3189..c71ed4496b08 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -120,9 +120,49 @@ pub struct Settings { #[cfg(feature = "olap")] pub connector_onboarding: SecretStateContainer, pub unmasked_headers: UnmaskedHeaders, + pub multitenancy: Multitenancy, pub saved_payment_methods: EligiblePaymentMethods, } +#[derive(Debug, Deserialize, Clone, Default)] +pub struct Multitenancy { + pub tenants: TenantConfig, + pub enabled: bool, +} + +impl Multitenancy { + pub fn get_tenants(&self) -> &HashMap { + &self.tenants.0 + } + pub fn get_tenant_names(&self) -> Vec { + self.tenants.0.keys().cloned().collect() + } + pub fn get_tenant(&self, tenant_id: &str) -> Option<&Tenant> { + self.tenants.0.get(tenant_id) + } +} + +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(transparent)] +pub struct TenantConfig(pub HashMap); + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct Tenant { + pub name: String, + pub base_url: String, + pub schema: String, + pub redis_key_prefix: String, +} + +impl storage_impl::config::TenantConfig for Tenant { + fn get_schema(&self) -> &str { + self.schema.as_str() + } + fn get_redis_key_prefix(&self) -> &str { + self.redis_key_prefix.as_str() + } +} + #[derive(Debug, Deserialize, Clone, Default)] pub struct UnmaskedHeaders { #[serde(deserialize_with = "deserialize_hashset")] @@ -447,7 +487,6 @@ pub struct Server { pub workers: usize, pub host: String, pub request_body_limit: usize, - pub base_url: String, pub shutdown_timeout: u64, } diff --git a/crates/router/src/connector/airwallex.rs b/crates/router/src/connector/airwallex.rs index ca511534b974..fbc25909489c 100644 --- a/crates/router/src/connector/airwallex.rs +++ b/crates/router/src/connector/airwallex.rs @@ -18,7 +18,7 @@ use crate::{ payments, }, events::connector_api_logs::ConnectorEvent, - headers, logger, routes, + headers, logger, services::{ self, request::{self, Mask}, @@ -27,7 +27,6 @@ use crate::{ types::{ self, api::{self, ConnectorCommon, ConnectorCommonExt}, - transformers::ForeignFrom, ErrorResponse, Response, RouterData, }, utils::{crypto, BytesExt}, @@ -123,6 +122,7 @@ impl ConnectorValidation for Airwallex { } impl api::Payment for Airwallex {} +impl api::PaymentsPreProcessing for Airwallex {} impl api::PaymentsCompleteAuthorize for Airwallex {} impl api::MandateSetup for Airwallex {} impl @@ -242,14 +242,14 @@ impl ConnectorIntegration for Airwallex { fn get_headers( &self, - req: &types::PaymentsInitRouterData, + req: &types::PaymentsPreProcessingRouterData, connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { self.build_headers(req, connectors) @@ -261,7 +261,7 @@ impl fn get_url( &self, - _req: &types::PaymentsInitRouterData, + _req: &types::PaymentsPreProcessingRouterData, connectors: &settings::Connectors, ) -> CustomResult { Ok(format!( @@ -273,7 +273,7 @@ impl fn get_request_body( &self, - req: &types::PaymentsInitRouterData, + req: &types::PaymentsPreProcessingRouterData, _connectors: &settings::Connectors, ) -> CustomResult { let req_obj = airwallex::AirwallexIntentRequest::try_from(req)?; @@ -282,16 +282,20 @@ impl fn build_request( &self, - req: &types::PaymentsInitRouterData, + req: &types::PaymentsPreProcessingRouterData, connectors: &settings::Connectors, ) -> CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() .method(services::Method::Post) - .url(&types::PaymentsInitType::get_url(self, req, connectors)?) + .url(&types::PaymentsPreProcessingType::get_url( + self, req, connectors, + )?) .attach_default_headers() - .headers(types::PaymentsInitType::get_headers(self, req, connectors)?) - .set_body(types::PaymentsInitType::get_request_body( + .headers(types::PaymentsPreProcessingType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsPreProcessingType::get_request_body( self, req, connectors, )?) .build(), @@ -300,10 +304,10 @@ impl fn handle_response( &self, - data: &types::PaymentsInitRouterData, + data: &types::PaymentsPreProcessingRouterData, event_builder: Option<&mut ConnectorEvent>, res: Response, - ) -> CustomResult { + ) -> CustomResult { let response: airwallex::AirwallexPaymentsResponse = res .response .parse_struct("airwallex AirwallexPaymentsResponse") @@ -335,36 +339,6 @@ impl api::PaymentAuthorize for Airwallex {} impl ConnectorIntegration for Airwallex { - async fn execute_pretasks( - &self, - router_data: &mut types::PaymentsAuthorizeRouterData, - app_state: &routes::AppState, - ) -> CustomResult<(), errors::ConnectorError> { - let integ: Box< - &(dyn ConnectorIntegration< - api::InitPayment, - types::PaymentsAuthorizeData, - types::PaymentsResponseData, - > + Send - + Sync - + 'static), - > = Box::new(&Self); - let authorize_data = &types::PaymentsInitRouterData::foreign_from(( - &router_data.to_owned(), - router_data.request.clone(), - )); - let resp = services::execute_connector_processing_step( - app_state, - integ, - authorize_data, - payments::CallConnectorAction::Trigger, - None, - ) - .await?; - router_data.reference_id = resp.reference_id; - Ok(()) - } - fn get_headers( &self, req: &types::PaymentsAuthorizeRouterData, diff --git a/crates/router/src/connector/airwallex/transformers.rs b/crates/router/src/connector/airwallex/transformers.rs index 587a14caedfd..56a6ebe2e448 100644 --- a/crates/router/src/connector/airwallex/transformers.rs +++ b/crates/router/src/connector/airwallex/transformers.rs @@ -41,13 +41,26 @@ pub struct AirwallexIntentRequest { //ID created in merchant's order system that corresponds to this PaymentIntent. merchant_order_id: String, } -impl TryFrom<&types::PaymentsInitRouterData> for AirwallexIntentRequest { +impl TryFrom<&types::PaymentsPreProcessingRouterData> for AirwallexIntentRequest { type Error = error_stack::Report; - fn try_from(item: &types::PaymentsInitRouterData) -> Result { + fn try_from(item: &types::PaymentsPreProcessingRouterData) -> Result { + // amount and currency will always be Some since PaymentsPreProcessingData is constructed using PaymentsAuthorizeData + let amount = item + .request + .amount + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "amount", + })?; + let currency = + item.request + .currency + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "currency", + })?; Ok(Self { request_id: Uuid::new_v4().to_string(), - amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?, - currency: item.request.currency, + amount: utils::to_currency_base_unit(amount, currency)?, + currency, merchant_order_id: item.connector_request_reference_id.clone(), }) } diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index b281d0a0a773..15aedad7599d 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -1,16 +1,15 @@ use common_utils::{ errors::CustomResult, ext_traits::{Encode, ValueExt}, - id_type, pii, }; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret, StrongSecret}; +use rand::distributions::{Alphanumeric, DistString}; use serde::{Deserialize, Serialize}; use crate::{ connector::utils::{ - self, missing_field_err, CardData, PaymentsSyncRequestData, RefundsRequestData, RouterData, - WalletData, + self, CardData, PaymentsSyncRequestData, RefundsRequestData, RouterData, WalletData, }, core::errors, services, @@ -179,7 +178,7 @@ struct PaymentProfileDetails { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CustomerDetails { - id: id_type::CustomerId, + id: String, } #[derive(Debug, Serialize)] @@ -273,10 +272,7 @@ pub struct AuthorizedotnetZeroMandateRequest { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] struct Profile { - merchant_customer_id: id_type::CustomerId, - #[serde(skip_serializing_if = "Option::is_none")] - description: Option, - email: Option, + description: String, payment_profiles: PaymentProfiles, } @@ -318,12 +314,8 @@ impl TryFrom<&types::SetupMandateRouterData> for CreateCustomerProfileRequest { create_customer_profile_request: AuthorizedotnetZeroMandateRequest { merchant_authentication, profile: Profile { - merchant_customer_id: item - .customer_id - .clone() - .ok_or_else(missing_field_err("customer_id"))?, - description: item.description.clone(), - email: item.request.email.clone(), + //The payment ID is included in the description because the connector requires unique description when creating a mandate. + description: item.payment_id.clone(), payment_profiles: PaymentProfiles { customer_type: CustomerType::Individual, payment: PaymentDetails::CreditCard(CreditCardDetails { @@ -393,16 +385,18 @@ impl response: Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::NoResponseId, redirection_data: None, - mandate_reference: item.response.customer_profile_id.map(|mandate_id| { - types::MandateReference { - connector_mandate_id: Some(mandate_id), - payment_method_id: item + mandate_reference: item.response.customer_profile_id.map( + |customer_profile_id| types::MandateReference { + connector_mandate_id: item .response .customer_payment_profile_id_list .first() - .cloned(), - } - }), + .map(|payment_profile_id| { + format!("{customer_profile_id}-{payment_profile_id}") + }), + payment_method_id: None, + }, + ), connector_metadata: None, network_txn_id: None, connector_response_reference_id: None, @@ -635,27 +629,24 @@ impl api_models::payments::ConnectorMandateReferenceId, ), ) -> Result { + let mandate_id = connector_mandate_id + .connector_mandate_id + .ok_or(errors::ConnectorError::MissingConnectorMandateID)?; Ok(Self { transaction_type: TransactionType::try_from(item.router_data.request.capture_method)?, amount: item.amount, currency_code: item.router_data.request.currency, payment: None, - profile: Some(ProfileDetails::CustomerProfileDetails( - CustomerProfileDetails { - customer_profile_id: Secret::from( - connector_mandate_id - .connector_mandate_id - .ok_or(errors::ConnectorError::MissingConnectorMandateID)?, - ), - payment_profile: PaymentProfileDetails { - payment_profile_id: Secret::from( - connector_mandate_id - .payment_method_id - .ok_or(errors::ConnectorError::MissingConnectorMandateID)?, - ), - }, - }, - )), + profile: mandate_id + .split_once('-') + .map(|(customer_profile_id, payment_profile_id)| { + ProfileDetails::CustomerProfileDetails(CustomerProfileDetails { + customer_profile_id: Secret::from(customer_profile_id.to_string()), + payment_profile: PaymentProfileDetails { + payment_profile_id: Secret::from(payment_profile_id.to_string()), + }, + }) + }), order: Order { description: item.router_data.connector_request_reference_id.clone(), }, @@ -709,11 +700,13 @@ impl create_profile: true, })), Some(CustomerDetails { - id: item - .router_data - .customer_id - .clone() - .ok_or_else(missing_field_err("customer_id"))?, + //The payment ID is included in the customer details because the connector requires unique customer information with a length of fewer than 20 characters when creating a mandate. + //If the length exceeds 20 characters, a random alphanumeric string is used instead. + id: if item.router_data.payment_id.len() <= 20 { + item.router_data.payment_id.clone() + } else { + Alphanumeric.sample_string(&mut rand::thread_rng(), 20) + }, }), ) } else { @@ -1087,6 +1080,24 @@ impl .and_then(|x| x.secure_acceptance_url.to_owned()); let redirection_data = url.map(|url| services::RedirectForm::from((url, services::Method::Get))); + let mandate_reference = item.response.profile_response.map(|profile_response| { + let payment_profile_id = profile_response + .customer_payment_profile_id_list + .and_then(|customer_payment_profile_id_list| { + customer_payment_profile_id_list.first().cloned() + }); + types::MandateReference { + connector_mandate_id: profile_response.customer_profile_id.and_then( + |customer_profile_id| { + payment_profile_id.map(|payment_profile_id| { + format!("{customer_profile_id}-{payment_profile_id}") + }) + }, + ), + payment_method_id: None, + } + }); + Ok(Self { status, response: match error { @@ -1096,16 +1107,7 @@ impl transaction_response.transaction_id.clone(), ), redirection_data, - mandate_reference: item.response.profile_response.map( - |profile_response| types::MandateReference { - connector_mandate_id: profile_response.customer_profile_id, - payment_method_id: profile_response - .customer_payment_profile_id_list - .and_then(|customer_payment_profile_id_list| { - customer_payment_profile_id_list.first().cloned() - }), - }, - ), + mandate_reference, connector_metadata: metadata, network_txn_id: transaction_response .network_trans_id diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs index f6e73c0b377c..2059b00ac363 100644 --- a/crates/router/src/connector/klarna.rs +++ b/crates/router/src/connector/klarna.rs @@ -170,14 +170,14 @@ fn build_region_specific_endpoint( ) -> CustomResult { let klarna_metadata_object = transformers::KlarnaConnectorMetadataObject::try_from(connector_metadata)?; - let region_based_endpoint = klarna_metadata_object + let klarna_region = klarna_metadata_object .klarna_region .ok_or(errors::ConnectorError::InvalidConnectorConfig { - config: "merchant_connector_account.metadata.region_based_endpoint", + config: "merchant_connector_account.metadata.klarna_region", }) .map(String::from)?; - Ok(base_url.replace("{{region_based_endpoint}}", ®ion_based_endpoint)) + Ok(base_url.replace("{{klarna_region}}", &klarna_region)) } impl @@ -207,7 +207,7 @@ impl let endpoint = build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?; - Ok(format!("{}{}", endpoint, "payments/v1/sessions")) + Ok(format!("{endpoint}payments/v1/sessions")) } fn get_request_body( @@ -333,8 +333,7 @@ impl build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?; Ok(format!( - "{}{}{}{}", - endpoint, "ordermanagement/v1/orders/", order_id, "/captures" + "{endpoint}ordermanagement/v1/orders/{order_id}/captures" )) } @@ -435,10 +434,7 @@ impl let endpoint = build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?; - Ok(format!( - "{}{}{}", - endpoint, "ordermanagement/v1/orders/", order_id, - )) + Ok(format!("{endpoint}ordermanagement/v1/orders/{order_id}")) } fn build_request( @@ -531,8 +527,7 @@ impl common_enums::PaymentExperience::InvokeSdkClient, common_enums::PaymentMethodType::Klarna, ) => Ok(format!( - "{}payments/v1/authorizations/{}/order", - endpoint, token + "{endpoint}payments/v1/authorizations/{token}/order", )), ( common_enums::PaymentExperience::DisplayQrCode @@ -670,6 +665,7 @@ impl req, ))?; let connector_req = klarna::KlarnaPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) } @@ -751,8 +747,7 @@ impl build_region_specific_endpoint(self.base_url(connectors), &req.connector_meta_data)?; Ok(format!( - "{}{}{}{}", - endpoint, "ordermanagement/v1/orders/", order_id, "/cancel" + "{endpoint}ordermanagement/v1/orders/{order_id}/cancel" )) } @@ -831,8 +826,7 @@ impl services::ConnectorIntegration, + shipping_address: Option, } #[derive(Debug, Deserialize, Serialize)] @@ -90,6 +91,21 @@ pub struct KlarnaSessionRequest { purchase_currency: enums::Currency, order_amount: i64, order_lines: Vec, + shipping_address: Option, +} + +#[derive(Debug, Serialize)] +pub struct KlarnaShippingAddress { + city: Option, + country: Option, + email: Option, + given_name: Option>, + family_name: Option>, + phone: Option>, + postal_code: Option>, + region: Option>, + street_address: Option>, + street_address2: Option>, } #[derive(Deserialize, Serialize, Debug)] @@ -123,6 +139,18 @@ impl TryFrom<&KlarnaRouterData<&types::PaymentsSessionRouterData>> for KlarnaSes total_amount: i64::from(data.quantity) * (data.amount), }) .collect(), + shipping_address: Some(KlarnaShippingAddress { + city: item.router_data.get_optional_shipping_city(), + country: item.router_data.get_optional_shipping_country(), + email: item.router_data.get_optional_shipping_email(), + given_name: item.router_data.get_optional_shipping_first_name(), + family_name: item.router_data.get_optional_shipping_last_name(), + phone: item.router_data.get_optional_shipping_phone_number(), + postal_code: item.router_data.get_optional_shipping_zip(), + region: item.router_data.get_optional_shipping_state(), + street_address: item.router_data.get_optional_shipping_line1(), + street_address2: item.router_data.get_optional_shipping_line2(), + }), }), None => Err(report!(errors::ConnectorError::MissingRequiredField { field_name: "order_details", @@ -176,6 +204,18 @@ impl TryFrom<&KlarnaRouterData<&types::PaymentsAuthorizeRouterData>> for KlarnaP .collect(), merchant_reference1: Some(item.router_data.connector_request_reference_id.clone()), auto_capture: request.is_auto_capture()?, + shipping_address: Some(KlarnaShippingAddress { + city: item.router_data.get_optional_shipping_city(), + country: item.router_data.get_optional_shipping_country(), + email: item.router_data.get_optional_shipping_email(), + given_name: item.router_data.get_optional_shipping_first_name(), + family_name: item.router_data.get_optional_shipping_last_name(), + phone: item.router_data.get_optional_shipping_phone_number(), + postal_code: item.router_data.get_optional_shipping_zip(), + region: item.router_data.get_optional_shipping_state(), + street_address: item.router_data.get_optional_shipping_line1(), + street_address2: item.router_data.get_optional_shipping_line2(), + }), }), None => Err(report!(errors::ConnectorError::MissingRequiredField { field_name: "order_details" @@ -267,7 +307,7 @@ impl ForeignFrom<(KlarnaFraudStatus, bool)> for enums::AttemptStatus { Self::Authorized } } - KlarnaFraudStatus::Pending => Self::Authorizing, + KlarnaFraudStatus::Pending => Self::Pending, KlarnaFraudStatus::Rejected => Self::Failure, } } @@ -362,13 +402,24 @@ pub struct KlarnaCaptureResponse { pub capture_id: Option, } -impl - TryFrom> - for types::RouterData +impl + TryFrom< + types::ResponseRouterData< + F, + KlarnaCaptureResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, + > for types::RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: types::ResponseRouterData< + F, + KlarnaCaptureResponse, + types::PaymentsCaptureData, + types::PaymentsResponseData, + >, ) -> Result { let connector_meta = serde_json::json!(KlarnaMeta { capture_id: item.response.capture_id, @@ -381,10 +432,11 @@ impl } else { item.data.status }; + let resource_id = item.data.request.connector_transaction_id.clone(); Ok(Self { response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::NoResponseId, + resource_id: types::ResponseId::ConnectorTransactionId(resource_id), redirection_data: None, mandate_reference: None, connector_metadata: Some(connector_meta), diff --git a/crates/router/src/connector/netcetera.rs b/crates/router/src/connector/netcetera.rs index 43df87db40f4..714873cfb531 100644 --- a/crates/router/src/connector/netcetera.rs +++ b/crates/router/src/connector/netcetera.rs @@ -107,7 +107,7 @@ impl ConnectorCommon for Netcetera { status_code: res.status_code, code: response.error_details.error_code, message: response.error_details.error_description, - reason: Some(response.error_details.error_detail), + reason: response.error_details.error_detail, attempt_status: None, connector_transaction_id: None, }) diff --git a/crates/router/src/connector/netcetera/netcetera_types.rs b/crates/router/src/connector/netcetera/netcetera_types.rs index 89fa6d9d50cc..87c32aa1e4f3 100644 --- a/crates/router/src/connector/netcetera/netcetera_types.rs +++ b/crates/router/src/connector/netcetera/netcetera_types.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; use common_utils::pii::Email; +use masking::ExposeInterface; use serde::{Deserialize, Serialize}; +use unidecode::unidecode; use crate::{ connector::utils::{AddressDetailsData, PhoneDetailsData}, @@ -701,7 +703,13 @@ impl .address .as_ref() .and_then(|add| add.city.clone()), - bill_addr_country: None, + bill_addr_country: billing_address.address.as_ref().and_then(|add| { + add.country.map(|country| { + common_enums::Country::from_alpha2(country) + .to_numeric() + .to_string() + }) + }), bill_addr_line1: billing_address .address .as_ref() @@ -739,15 +747,25 @@ impl .clone() .map(PhoneNumber::try_from) .transpose()?, - cardholder_name: billing_address - .address - .as_ref() - .and_then(|add| add.first_name.clone()), + cardholder_name: billing_address.address.and_then(|address| { + address + .get_optional_full_name() + .map(|name| masking::Secret::new(unidecode(&name.expose()))) + }), ship_addr_city: shipping_address .as_ref() .and_then(|shipping_add| shipping_add.address.as_ref()) .and_then(|add| add.city.clone()), - ship_addr_country: None, + ship_addr_country: shipping_address + .as_ref() + .and_then(|shipping_add| shipping_add.address.as_ref()) + .and_then(|add| { + add.country.map(|country| { + common_enums::Country::from_alpha2(country) + .to_numeric() + .to_string() + }) + }), ship_addr_line1: shipping_address .as_ref() .and_then(|shipping_add| shipping_add.address.as_ref()) @@ -1313,7 +1331,7 @@ pub struct Browser { /// - with message version = 2.1.0 and deviceChannel = 02 (BRW). /// - with message version = 2.2.0 and deviceChannel = 02 (BRW) and browserJavascriptEnabled = true. #[serde(rename = "browserTZ")] - browser_tz: Option, + browser_tz: Option, /// Exact content of the HTTP user-agent header. The field is limited to maximum 2048 characters. If the total length of /// the User-Agent sent by the browser exceeds 2048 characters, the 3DS Server truncates the excess portion. @@ -1364,7 +1382,7 @@ impl From for Browser { browser_color_depth: value.color_depth.map(|cd| cd.to_string()), browser_screen_height: value.screen_height, browser_screen_width: value.screen_width, - browser_tz: Some(1), + browser_tz: value.time_zone, browser_user_agent: value.user_agent, challenge_window_size: Some(ChallengeWindowSizeEnum::FullScreen), browser_javascript_enabled: value.java_script_enabled, diff --git a/crates/router/src/connector/netcetera/transformers.rs b/crates/router/src/connector/netcetera/transformers.rs index 2da08c402cf2..0fcecadef249 100644 --- a/crates/router/src/connector/netcetera/transformers.rs +++ b/crates/router/src/connector/netcetera/transformers.rs @@ -105,8 +105,8 @@ impl NetceteraPreAuthenticationResponse::Failure(error_response) => { Err(types::ErrorResponse { code: error_response.error_details.error_code, - message: error_response.error_details.error_detail, - reason: Some(error_response.error_details.error_description), + message: error_response.error_details.error_description, + reason: error_response.error_details.error_detail, status_code: item.http_code, attempt_status: None, connector_transaction_id: None, @@ -174,8 +174,8 @@ impl } NetceteraAuthenticationResponse::Error(error_response) => Err(types::ErrorResponse { code: error_response.error_details.error_code, - message: error_response.error_details.error_detail, - reason: Some(error_response.error_details.error_description), + message: error_response.error_details.error_description, + reason: error_response.error_details.error_detail, status_code: item.http_code, attempt_status: None, connector_transaction_id: None, @@ -235,20 +235,20 @@ pub struct NetceteraErrorDetails { pub error_code: String, /// Code indicating the 3-D Secure component that identified the error. - pub error_component: String, + pub error_component: Option, /// Text describing the problem identified. pub error_description: String, /// Additional detail regarding the problem identified. - pub error_detail: String, + pub error_detail: Option, /// Universally unique identifier for the transaction assigned by the 3DS SDK. #[serde(rename = "sdkTransID")] pub sdk_trans_id: Option, /// The Message Type that was identified as erroneous. - pub error_message_type: String, + pub error_message_type: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -454,27 +454,12 @@ impl TryFrom<&NetceteraRouterData<&types::authentication::ConnectorAuthenticatio item: &NetceteraRouterData<&types::authentication::ConnectorAuthenticationRouterData>, ) -> Result { let now = common_utils::date_time::now(); - let three_ds_req_auth_timestamp = common_utils::date_time::format_date( - now, - common_utils::date_time::DateFormat::YYYYMMDDHHmm, - ) - .change_context(errors::ConnectorError::RequestEncodingFailedWithReason( - "Failed to format Date".to_string(), - ))?; let request = item.router_data.request.clone(); let pre_authn_data = request.pre_authentication_data.clone(); let three_ds_requestor = netcetera_types::ThreeDSRequestor { three_ds_requestor_authentication_ind: netcetera_types::ThreeDSRequestorAuthenticationIndicator::Payment, - three_ds_requestor_authentication_info: Some( - netcetera_types::SingleOrListElement::new_single( - netcetera_types::ThreeDSRequestorAuthenticationInformation { - three_ds_req_auth_method: netcetera_types::ThreeDSReqAuthMethod::Guest, - three_ds_req_auth_timestamp, - three_ds_req_auth_data: None, - }, - ), - ), + three_ds_requestor_authentication_info: None, three_ds_requestor_challenge_ind: None, three_ds_requestor_prior_authentication_info: None, three_ds_requestor_dec_req_ind: None, @@ -518,8 +503,8 @@ impl TryFrom<&NetceteraRouterData<&types::authentication::ConnectorAuthenticatio ), )?, ), - recurring_expiry: Some("20240401".to_string()), - recurring_frequency: Some(1), + recurring_expiry: None, + recurring_frequency: None, trans_type: None, recurring_amount: None, recurring_currency: None, @@ -558,7 +543,12 @@ impl TryFrom<&NetceteraRouterData<&types::authentication::ConnectorAuthenticatio } api_models::payments::DeviceChannel::App => None, }; - let sdk_information = request.sdk_information.map(netcetera_types::Sdk::from); + let sdk_information = match request.device_channel { + api_models::payments::DeviceChannel::App => { + request.sdk_information.map(netcetera_types::Sdk::from) + } + api_models::payments::DeviceChannel::Browser => None, + }; let device_render_options = match request.device_channel { api_models::payments::DeviceChannel::App => { Some(netcetera_types::DeviceRenderingOptionsSupported { diff --git a/crates/router/src/connector/nmi.rs b/crates/router/src/connector/nmi.rs index 744eeb4a975a..2c0712c54080 100644 --- a/crates/router/src/connector/nmi.rs +++ b/crates/router/src/connector/nmi.rs @@ -1,8 +1,11 @@ pub mod transformers; -use std::fmt::Debug; - -use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; +use common_utils::{ + crypto, + ext_traits::ByteSliceExt, + request::RequestContent, + types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}, +}; use diesel_models::enums; use error_stack::{report, ResultExt}; use regex::Regex; @@ -25,8 +28,18 @@ use crate::{ }, }; -#[derive(Clone, Debug)] -pub struct Nmi; +#[derive(Clone)] +pub struct Nmi { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Nmi { + pub const fn new() -> &'static Self { + &Self { + amount_converter: &FloatMajorUnitForConnector, + } + } +} impl api::Payment for Nmi {} impl api::PaymentSession for Nmi {} @@ -325,12 +338,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = nmi::NmiRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = nmi::NmiRouterData::from((amount, req)); let connector_req = nmi::NmiPaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } @@ -413,12 +426,12 @@ impl req: &types::PaymentsCompleteAuthorizeRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = nmi::NmiRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = nmi::NmiRouterData::from((amount, req)); let connector_req = nmi::NmiCompleteRequest::try_from(&connector_router_data)?; Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } @@ -565,12 +578,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = nmi::NmiRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; + )?; + let connector_router_data = nmi::NmiRouterData::from((amount, req)); let connector_req = nmi::NmiCaptureRequest::try_from(&connector_router_data)?; Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } @@ -713,12 +726,13 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = nmi::NmiRouterData::try_from(( - &self.get_currency_unit(), + let refund_amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; + )?; + + let connector_router_data = nmi::NmiRouterData::from((refund_amount, req)); let connector_req = nmi::NmiRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs index 44836002024d..c18feefa2f34 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -5,6 +5,7 @@ use common_utils::{ errors::CustomResult, ext_traits::XmlExt, pii::{self, Email}, + types::FloatMajorUnit, }; use error_stack::{report, Report, ResultExt}; use masking::{ExposeInterface, PeekInterface, Secret}; @@ -57,25 +58,16 @@ impl TryFrom<&ConnectorAuthType> for NmiAuthType { #[derive(Debug, Serialize)] pub struct NmiRouterData { - pub amount: f64, + pub amount: FloatMajorUnit, pub router_data: T, } -impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for NmiRouterData { - type Error = Report; - - fn try_from( - (_currency_unit, currency, amount, router_data): ( - &api::CurrencyUnit, - enums::Currency, - i64, - T, - ), - ) -> Result { - Ok(Self { - amount: utils::to_currency_base_unit_asf64(amount, currency)?, +impl From<(FloatMajorUnit, T)> for NmiRouterData { + fn from((amount, router_data): (FloatMajorUnit, T)) -> Self { + Self { + amount, router_data, - }) + } } } @@ -251,7 +243,7 @@ impl #[derive(Debug, Serialize)] pub struct NmiCompleteRequest { - amount: f64, + amount: FloatMajorUnit, #[serde(rename = "type")] transaction_type: TransactionType, security_key: Secret, @@ -422,7 +414,7 @@ impl ForeignFrom<(NmiCompleteResponse, u16)> for types::ErrorResponse { pub struct NmiPaymentsRequest { #[serde(rename = "type")] transaction_type: TransactionType, - amount: f64, + amount: FloatMajorUnit, security_key: Secret, currency: enums::Currency, #[serde(flatten)] @@ -675,7 +667,7 @@ impl TryFrom<&types::SetupMandateRouterData> for NmiPaymentsRequest { Ok(Self { transaction_type: TransactionType::Validate, security_key: auth_type.api_key, - amount: 0.0, + amount: FloatMajorUnit::zero(), currency: item.request.currency, payment_method, merchant_defined_field: None, @@ -707,7 +699,7 @@ pub struct NmiCaptureRequest { pub transaction_type: TransactionType, pub security_key: Secret, pub transactionid: String, - pub amount: Option, + pub amount: Option, } impl TryFrom<&NmiRouterData<&types::PaymentsCaptureRouterData>> for NmiCaptureRequest { @@ -1062,7 +1054,7 @@ pub struct NmiRefundRequest { security_key: Secret, transactionid: String, orderid: String, - amount: f64, + amount: FloatMajorUnit, } impl TryFrom<&NmiRouterData<&types::RefundsRouterData>> for NmiRefundRequest { diff --git a/crates/router/src/connector/nuvei.rs b/crates/router/src/connector/nuvei.rs index bc556336c3ce..132311f87844 100644 --- a/crates/router/src/connector/nuvei.rs +++ b/crates/router/src/connector/nuvei.rs @@ -526,7 +526,7 @@ impl ConnectorIntegration CustomResult<(), errors::ConnectorError> { let integ: Box< &(dyn ConnectorIntegration< diff --git a/crates/router/src/connector/shift4.rs b/crates/router/src/connector/shift4.rs index 6c54c7019c83..f314d621de41 100644 --- a/crates/router/src/connector/shift4.rs +++ b/crates/router/src/connector/shift4.rs @@ -216,7 +216,7 @@ impl ConnectorIntegration CustomResult<(), errors::ConnectorError> { if router_data.auth_type == enums::AuthenticationType::ThreeDs && router_data.payment_method == enums::PaymentMethod::Card diff --git a/crates/router/src/connector/square.rs b/crates/router/src/connector/square.rs index f9a81031b07a..4e0812d16124 100644 --- a/crates/router/src/connector/square.rs +++ b/crates/router/src/connector/square.rs @@ -196,7 +196,7 @@ impl async fn execute_pretasks( &self, router_data: &mut types::TokenizationRouterData, - app_state: &crate::routes::AppState, + app_state: &crate::routes::SessionState, ) -> CustomResult<(), errors::ConnectorError> { let integ: Box< &(dyn ConnectorIntegration< diff --git a/crates/router/src/connector/stripe.rs b/crates/router/src/connector/stripe.rs index febb039768c7..171cde7bc9d5 100644 --- a/crates/router/src/connector/stripe.rs +++ b/crates/router/src/connector/stripe.rs @@ -1,6 +1,6 @@ pub mod transformers; -use std::{collections::HashMap, fmt::Debug, ops::Deref}; +use std::{collections::HashMap, ops::Deref}; use common_utils::request::RequestContent; use diesel_models::enums; diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index a9c78cb4bf18..6737bbf3af0f 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -13,7 +13,7 @@ use common_utils::{ ext_traits::StringExt, id_type, pii::{self, Email, IpAddress}, - types::MinorUnit, + types::{AmountConvertor, MinorUnit}, }; use diesel_models::enums; use error_stack::{report, ResultExt}; @@ -101,6 +101,16 @@ pub trait RouterData { fn get_optional_billing(&self) -> Option<&api::Address>; fn get_optional_shipping(&self) -> Option<&api::Address>; + fn get_optional_shipping_line1(&self) -> Option>; + fn get_optional_shipping_line2(&self) -> Option>; + fn get_optional_shipping_city(&self) -> Option; + fn get_optional_shipping_country(&self) -> Option; + fn get_optional_shipping_zip(&self) -> Option>; + fn get_optional_shipping_state(&self) -> Option>; + fn get_optional_shipping_first_name(&self) -> Option>; + fn get_optional_shipping_last_name(&self) -> Option>; + fn get_optional_shipping_phone_number(&self) -> Option>; + fn get_optional_shipping_email(&self) -> Option; fn get_optional_billing_full_name(&self) -> Option>; fn get_optional_billing_line1(&self) -> Option>; @@ -199,6 +209,91 @@ impl RouterData for types::RouterData Option> { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.first_name) + }) + } + + fn get_optional_shipping_last_name(&self) -> Option> { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.last_name) + }) + } + + fn get_optional_shipping_line1(&self) -> Option> { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.line1) + }) + } + + fn get_optional_shipping_line2(&self) -> Option> { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.line2) + }) + } + + fn get_optional_shipping_city(&self) -> Option { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.city) + }) + } + + fn get_optional_shipping_state(&self) -> Option> { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.state) + }) + } + + fn get_optional_shipping_country(&self) -> Option { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.country) + }) + } + + fn get_optional_shipping_zip(&self) -> Option> { + self.address.get_shipping().and_then(|shipping_address| { + shipping_address + .clone() + .address + .and_then(|shipping_address_details| shipping_address_details.zip) + }) + } + + fn get_optional_shipping_email(&self) -> Option { + self.address + .get_shipping() + .and_then(|shipping_address| shipping_address.clone().email) + } + + fn get_optional_shipping_phone_number(&self) -> Option> { + self.address + .get_shipping() + .and_then(|shipping_address| shipping_address.clone().phone) + .and_then(|phone_details| phone_details.get_number_with_country_code().ok()) + } + fn get_description(&self) -> Result { self.description .clone() @@ -2618,3 +2713,23 @@ impl From for PaymentMethodDataType { } } } + +pub fn convert_amount( + amount_convertor: &dyn AmountConvertor, + amount: MinorUnit, + currency: enums::Currency, +) -> Result> { + amount_convertor + .convert(amount, currency) + .change_context(errors::ConnectorError::AmountConversionFailed) +} + +pub fn convert_back( + amount_convertor: &dyn AmountConvertor, + amount: T, + currency: enums::Currency, +) -> Result> { + amount_convertor + .convert_back(amount, currency) + .change_context(errors::ConnectorError::AmountConversionFailed) +} diff --git a/crates/router/src/connector/wise.rs b/crates/router/src/connector/wise.rs index 667e182b674b..763adca1eeec 100644 --- a/crates/router/src/connector/wise.rs +++ b/crates/router/src/connector/wise.rs @@ -506,7 +506,7 @@ impl services::ConnectorIntegration, - app_state: &routes::AppState, + app_state: &routes::SessionState, ) -> CustomResult<(), errors::ConnectorError> { // Create a quote let quote_router_data = diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index bac825a341c7..6b582c2c9579 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -26,7 +26,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services::{self, api as service_api}, types::{ self, api, @@ -50,7 +50,7 @@ pub fn create_merchant_publishable_key() -> String { } pub async fn create_merchant_account( - state: AppState, + state: SessionState, req: api::MerchantAccountCreate, ) -> RouterResponse { let db = state.store.as_ref(); @@ -287,7 +287,7 @@ pub async fn create_merchant_account( #[cfg(feature = "olap")] pub async fn list_merchant_account( - state: AppState, + state: SessionState, req: api_models::admin::MerchantAccountListRequest, ) -> RouterResponse> { let merchant_accounts = state @@ -311,7 +311,7 @@ pub async fn list_merchant_account( } pub async fn get_merchant_account( - state: AppState, + state: SessionState, req: api::MerchantId, ) -> RouterResponse { let db = state.store.as_ref(); @@ -399,7 +399,7 @@ pub async fn create_business_profile_from_business_labels( /// For backwards compatibility /// If any of the fields of merchant account are updated, then update these fields in business profiles pub async fn update_business_profile_cascade( - state: AppState, + state: SessionState, merchant_account_update: api::MerchantAccountUpdate, merchant_id: String, ) -> RouterResult<()> { @@ -463,7 +463,7 @@ pub async fn update_business_profile_cascade( } pub async fn merchant_account_update( - state: AppState, + state: SessionState, merchant_id: &String, req: api::MerchantAccountUpdate, ) -> RouterResponse { @@ -617,7 +617,7 @@ pub async fn merchant_account_update( } pub async fn merchant_account_delete( - state: AppState, + state: SessionState, merchant_id: String, ) -> RouterResponse { let mut is_deleted = false; @@ -733,7 +733,7 @@ fn validate_certificate_in_mca_metadata( } pub async fn create_payment_connector( - state: AppState, + state: SessionState, req: api::MerchantConnectorCreate, merchant_id: &String, ) -> RouterResponse { @@ -1084,7 +1084,7 @@ async fn validate_pm_auth( } pub async fn retrieve_payment_connector( - state: AppState, + state: SessionState, merchant_id: String, merchant_connector_id: String, ) -> RouterResponse { @@ -1117,7 +1117,7 @@ pub async fn retrieve_payment_connector( } pub async fn list_payment_connectors( - state: AppState, + state: SessionState, merchant_id: String, ) -> RouterResponse> { let store = state.store.as_ref(); @@ -1154,7 +1154,7 @@ pub async fn list_payment_connectors( } pub async fn update_payment_connector( - state: AppState, + state: SessionState, merchant_id: &str, merchant_connector_id: &str, req: api_models::admin::MerchantConnectorUpdate, @@ -1305,7 +1305,7 @@ pub async fn update_payment_connector( } pub async fn delete_payment_connector( - state: AppState, + state: SessionState, merchant_id: String, merchant_connector_id: String, ) -> RouterResponse { @@ -1350,7 +1350,7 @@ pub async fn delete_payment_connector( } pub async fn kv_for_merchant( - state: AppState, + state: SessionState, merchant_id: String, enable: bool, ) -> RouterResponse { @@ -1417,7 +1417,7 @@ pub async fn kv_for_merchant( } pub async fn toggle_kv_for_all_merchants( - state: AppState, + state: SessionState, enable: bool, ) -> RouterResponse { let db = state.store.as_ref(); @@ -1447,7 +1447,7 @@ pub async fn toggle_kv_for_all_merchants( } pub async fn check_merchant_account_kv_status( - state: AppState, + state: SessionState, merchant_id: String, ) -> RouterResponse { let db = state.store.as_ref(); @@ -1519,7 +1519,7 @@ pub async fn create_and_insert_business_profile( } pub async fn create_business_profile( - state: AppState, + state: SessionState, request: api::BusinessProfileCreate, merchant_id: &str, ) -> RouterResponse { @@ -1566,7 +1566,7 @@ pub async fn create_business_profile( } pub async fn list_business_profile( - state: AppState, + state: SessionState, merchant_id: String, ) -> RouterResponse> { let db = state.store.as_ref(); @@ -1586,7 +1586,7 @@ pub async fn list_business_profile( } pub async fn retrieve_business_profile( - state: AppState, + state: SessionState, profile_id: String, ) -> RouterResponse { let db = state.store.as_ref(); @@ -1604,7 +1604,7 @@ pub async fn retrieve_business_profile( } pub async fn delete_business_profile( - state: AppState, + state: SessionState, profile_id: String, merchant_id: &str, ) -> RouterResponse { @@ -1620,7 +1620,7 @@ pub async fn delete_business_profile( } pub async fn update_business_profile( - state: AppState, + state: SessionState, profile_id: &str, merchant_id: &str, request: api::BusinessProfileUpdate, @@ -1738,7 +1738,7 @@ pub async fn update_business_profile( } pub async fn extended_card_info_toggle( - state: AppState, + state: SessionState, profile_id: &str, ext_card_info_choice: admin_types::ExtendedCardInfoChoice, ) -> RouterResponse { @@ -1771,7 +1771,7 @@ pub async fn extended_card_info_toggle( } pub async fn connector_agnostic_mit_toggle( - state: AppState, + state: SessionState, merchant_id: &str, profile_id: &str, connector_agnostic_mit_choice: admin_types::ConnectorAgnosticMitChoice, @@ -2085,7 +2085,7 @@ pub(crate) fn validate_auth_and_metadata_type( #[cfg(feature = "dummy_connector")] pub async fn validate_dummy_connector_enabled( - state: &AppState, + state: &SessionState, connector_name: &api_enums::Connector, ) -> Result<(), errors::ApiErrorResponse> { if !state.conf.dummy_connector.enabled diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs index 7f2b343b9681..8fe536731d3d 100644 --- a/crates/router/src/core/api_keys.rs +++ b/crates/router/src/core/api_keys.rs @@ -9,7 +9,7 @@ use crate::{ configs::settings, consts, core::errors::{self, RouterResponse, StorageErrorExt}, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services::ApplicationResponse, types::{api, storage, transformers::ForeignInto}, utils, @@ -110,7 +110,7 @@ impl PlaintextApiKey { #[instrument(skip_all)] pub async fn create_api_key( - state: AppState, + state: SessionState, api_key: api::CreateApiKeyRequest, merchant_id: String, ) -> RouterResponse { @@ -244,7 +244,7 @@ pub async fn add_api_key_expiry_task( #[instrument(skip_all)] pub async fn retrieve_api_key( - state: AppState, + state: SessionState, merchant_id: &str, key_id: &str, ) -> RouterResponse { @@ -261,7 +261,7 @@ pub async fn retrieve_api_key( #[instrument(skip_all)] pub async fn update_api_key( - state: AppState, + state: SessionState, api_key: api::UpdateApiKeyRequest, ) -> RouterResponse { let merchant_id = api_key.merchant_id.clone(); @@ -395,7 +395,7 @@ pub async fn update_api_key_expiry_task( #[instrument(skip_all)] pub async fn revoke_api_key( - state: AppState, + state: SessionState, merchant_id: &str, key_id: &str, ) -> RouterResponse { @@ -463,7 +463,7 @@ pub async fn revoke_api_key_expiry_task( #[instrument(skip_all)] pub async fn list_api_keys( - state: AppState, + state: SessionState, merchant_id: String, limit: Option, offset: Option, diff --git a/crates/router/src/core/api_locking.rs b/crates/router/src/core/api_locking.rs index ec205f524271..8723c5fc1417 100644 --- a/crates/router/src/core/api_locking.rs +++ b/crates/router/src/core/api_locking.rs @@ -6,7 +6,7 @@ use redis_interface as redis; use router_env::{instrument, logger, tracing}; use super::errors::{self, RouterResult}; -use crate::routes::{app::AppStateInfo, lock_utils}; +use crate::routes::{app::SessionStateInfo, lock_utils}; pub const API_LOCK_PREFIX: &str = "API_LOCK"; @@ -50,7 +50,7 @@ impl LockAction { #[instrument(skip_all)] pub async fn perform_locking_action(self, state: &A, merchant_id: String) -> RouterResult<()> where - A: AppStateInfo, + A: SessionStateInfo, { match self { Self::Hold { input } => { @@ -111,7 +111,7 @@ impl LockAction { #[instrument(skip_all)] pub async fn free_lock_action(self, state: &A, merchant_id: String) -> RouterResult<()> where - A: AppStateInfo, + A: SessionStateInfo, { match self { Self::Hold { input } => { diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index 5acd14bb3ee2..ec799365f7a4 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -12,21 +12,21 @@ use masking::ExposeInterface; use super::errors::StorageErrorExt; use crate::{ core::{errors::ApiErrorResponse, payments as payments_core}, - routes::AppState, + routes::SessionState, types::{self as core_types, api, domain, storage}, utils::check_if_pull_mechanism_for_external_3ds_enabled_from_connector_metadata, }; #[allow(clippy::too_many_arguments)] pub async fn perform_authentication( - state: &AppState, + state: &SessionState, + merchant_id: String, authentication_connector: String, payment_method_data: payments::PaymentMethodData, payment_method: common_enums::PaymentMethod, billing_address: payments::Address, shipping_address: Option, browser_details: Option, - business_profile: storage::BusinessProfile, merchant_connector_account: payments_core::helpers::MerchantConnectorAccountType, amount: Option, currency: Option, @@ -38,8 +38,10 @@ pub async fn perform_authentication( threeds_method_comp_ind: payments::ThreeDsCompletionIndicator, email: Option, webhook_url: String, + three_ds_requestor_url: String, ) -> CustomResult { let router_data = transformers::construct_authentication_router_data( + merchant_id, authentication_connector.clone(), payment_method_data, payment_method, @@ -50,7 +52,6 @@ pub async fn perform_authentication( currency, message_category, device_channel, - business_profile, merchant_connector_account, authentication_data.clone(), return_url, @@ -58,6 +59,7 @@ pub async fn perform_authentication( threeds_method_comp_ind, email, webhook_url, + three_ds_requestor_url, )?; let response = utils::do_auth_connector_call(state, authentication_connector.clone(), router_data).await?; @@ -76,7 +78,7 @@ pub async fn perform_authentication( } pub async fn perform_post_authentication( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, business_profile: storage::BusinessProfile, authentication_id: String, @@ -115,7 +117,7 @@ pub async fn perform_post_authentication( } pub async fn perform_pre_authentication( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, card_number: cards::CardNumber, token: String, diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index e0e9ee0b5263..2255e1ff5820 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -26,6 +26,7 @@ const IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_AUTHENTICATION_FLOW: &str = #[allow(clippy::too_many_arguments)] pub fn construct_authentication_router_data( + merchant_id: String, authentication_connector: String, payment_method_data: payments::PaymentMethodData, payment_method: PaymentMethod, @@ -36,7 +37,6 @@ pub fn construct_authentication_router_data( currency: Option, message_category: types::api::authentication::MessageCategory, device_channel: payments::DeviceChannel, - business_profile: storage::BusinessProfile, merchant_connector_account: payments_helpers::MerchantConnectorAccountType, authentication_data: storage::Authentication, return_url: Option, @@ -44,18 +44,8 @@ pub fn construct_authentication_router_data( threeds_method_comp_ind: payments::ThreeDsCompletionIndicator, email: Option, webhook_url: String, + three_ds_requestor_url: String, ) -> RouterResult { - let authentication_details: api_models::admin::AuthenticationConnectorDetails = - business_profile - .authentication_connector_details - .clone() - .get_required_value("authentication_details") - .attach_printable("authentication_details not configured by the merchant")? - .parse_value("AuthenticationDetails") - .change_context(errors::ApiErrorResponse::UnprocessableEntity { - message: "Invalid data format found for authentication_details".into(), - }) - .attach_printable("Error while parsing authentication_details from merchant_account")?; let router_request = types::authentication::ConnectorAuthenticationRequestData { payment_method_data: From::from(payment_method_data), billing_address, @@ -71,14 +61,14 @@ pub fn construct_authentication_router_data( return_url, sdk_information, email, - three_ds_requestor_url: authentication_details.three_ds_requestor_url, + three_ds_requestor_url, threeds_method_comp_ind, webhook_url, }; construct_router_data( authentication_connector, payment_method, - business_profile.merchant_id.clone(), + merchant_id.clone(), types::PaymentAddress::default(), router_request, &merchant_connector_account, diff --git a/crates/router/src/core/authentication/utils.rs b/crates/router/src/core/authentication/utils.rs index c2285b6de443..ec44f299c70e 100644 --- a/crates/router/src/core/authentication/utils.rs +++ b/crates/router/src/core/authentication/utils.rs @@ -8,7 +8,7 @@ use crate::{ payments, }, errors::RouterResult, - routes::AppState, + routes::SessionState, services::{self, execute_connector_processing_step}, types::{ api, authentication::AuthenticationResponseData, domain, storage, @@ -48,7 +48,7 @@ pub fn get_connector_data_if_separate_authn_supported( } pub async fn update_trackers( - state: &AppState, + state: &SessionState, router_data: RouterData, authentication: storage::Authentication, acquirer_details: Option, @@ -141,7 +141,10 @@ pub async fn update_trackers( Err(error) => storage::AuthenticationUpdate::ErrorUpdate { connector_authentication_id: error.connector_transaction_id, authentication_status: common_enums::AuthenticationStatus::Failed, - error_message: Some(error.message), + error_message: error + .reason + .map(|reason| format!("message: {}, reason: {}", error.message, reason)) + .or(Some(error.message)), error_code: Some(error.code), }, }; @@ -168,7 +171,7 @@ impl ForeignFrom for common_enums::AttemptSt } pub async fn create_new_authentication( - state: &AppState, + state: &SessionState, merchant_id: String, authentication_connector: String, token: String, @@ -225,7 +228,7 @@ pub async fn create_new_authentication( } pub async fn do_auth_connector_call( - state: &AppState, + state: &SessionState, authentication_connector_name: String, router_data: RouterData, ) -> RouterResult> @@ -252,7 +255,7 @@ where } pub async fn get_authentication_connector_data( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, business_profile: &storage::BusinessProfile, ) -> RouterResult<( diff --git a/crates/router/src/core/blocklist.rs b/crates/router/src/core/blocklist.rs index 12a0802517c4..947acc506dff 100644 --- a/crates/router/src/core/blocklist.rs +++ b/crates/router/src/core/blocklist.rs @@ -5,13 +5,13 @@ use api_models::blocklist as api_blocklist; use crate::{ core::errors::{self, RouterResponse}, - routes::AppState, + routes::SessionState, services, types::domain, }; pub async fn add_entry_to_blocklist( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, body: api_blocklist::AddToBlocklistRequest, ) -> RouterResponse { @@ -21,7 +21,7 @@ pub async fn add_entry_to_blocklist( } pub async fn remove_entry_from_blocklist( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, body: api_blocklist::DeleteFromBlocklistRequest, ) -> RouterResponse { @@ -31,7 +31,7 @@ pub async fn remove_entry_from_blocklist( } pub async fn list_blocklist_entries( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, query: api_blocklist::ListBlocklistQuery, ) -> RouterResponse> { @@ -41,7 +41,7 @@ pub async fn list_blocklist_entries( } pub async fn toggle_blocklist_guard( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, query: api_blocklist::ToggleBlocklistQuery, ) -> RouterResponse { diff --git a/crates/router/src/core/blocklist/transformers.rs b/crates/router/src/core/blocklist/transformers.rs index a2583e5b6045..c3499445602d 100644 --- a/crates/router/src/core/blocklist/transformers.rs +++ b/crates/router/src/core/blocklist/transformers.rs @@ -111,7 +111,7 @@ async fn generate_jwe_payload_for_request( #[instrument(skip_all)] pub async fn generate_fingerprint( - state: &routes::AppState, + state: &routes::SessionState, card_number: StrongSecret, hash_key: StrongSecret, locker_choice: api_enums::LockerChoice, @@ -129,7 +129,7 @@ pub async fn generate_fingerprint( #[instrument(skip_all)] async fn call_to_locker_for_fingerprint( - state: &routes::AppState, + state: &routes::SessionState, payload: &blocklist::GenerateFingerprintRequest, locker_choice: api_enums::LockerChoice, ) -> CustomResult { diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs index bc8724da8235..6c0d3fcd6c3b 100644 --- a/crates/router/src/core/blocklist/utils.rs +++ b/crates/router/src/core/blocklist/utils.rs @@ -5,7 +5,7 @@ use diesel_models::configs; use error_stack::ResultExt; use masking::StrongSecret; -use super::{errors, transformers::generate_fingerprint, AppState}; +use super::{errors, transformers::generate_fingerprint, SessionState}; use crate::{ consts, core::{ @@ -18,7 +18,7 @@ use crate::{ }; pub async fn delete_entry_from_blocklist( - state: &AppState, + state: &SessionState, merchant_id: String, request: api_blocklist::DeleteFromBlocklistRequest, ) -> RouterResult { @@ -44,7 +44,7 @@ pub async fn delete_entry_from_blocklist( } pub async fn toggle_blocklist_guard_for_merchant( - state: &AppState, + state: &SessionState, merchant_id: String, query: api_blocklist::ToggleBlocklistQuery, ) -> CustomResult { @@ -94,7 +94,7 @@ pub fn get_blocklist_guard_key(merchant_id: &str) -> String { } pub async fn list_blocklist_entries_for_merchant( - state: &AppState, + state: &SessionState, merchant_id: String, query: api_blocklist::ListBlocklistQuery, ) -> RouterResult> { @@ -138,7 +138,7 @@ fn validate_extended_card_bin(bin: &str) -> RouterResult<()> { } pub async fn insert_entry_into_blocklist( - state: &AppState, + state: &SessionState, merchant_id: String, to_block: api_blocklist::AddToBlocklistRequest, ) -> RouterResult { @@ -207,7 +207,7 @@ pub async fn insert_entry_into_blocklist( } pub async fn get_merchant_fingerprint_secret( - state: &AppState, + state: &SessionState, merchant_id: &str, ) -> RouterResult { let key = get_merchant_fingerprint_secret_key(merchant_id); @@ -246,7 +246,7 @@ fn get_merchant_fingerprint_secret_key(merchant_id: &str) -> String { async fn duplicate_check_insert_bin( bin: &str, - state: &AppState, + state: &SessionState, merchant_id: &str, data_kind: common_enums::BlocklistDataKind, ) -> RouterResult { @@ -287,7 +287,7 @@ async fn duplicate_check_insert_bin( } async fn delete_card_bin_blocklist_entry( - state: &AppState, + state: &SessionState, bin: &str, merchant_id: &str, ) -> RouterResult { @@ -301,7 +301,7 @@ async fn delete_card_bin_blocklist_entry( } pub async fn validate_data_for_blocklist( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, ) -> CustomResult @@ -452,7 +452,7 @@ where } pub async fn generate_payment_fingerprint( - state: &AppState, + state: &SessionState, merchant_id: String, payment_method_data: Option, ) -> CustomResult, errors::ApiErrorResponse> { diff --git a/crates/router/src/core/cache.rs b/crates/router/src/core/cache.rs index 8f3cf765e920..8cda60cf7009 100644 --- a/crates/router/src/core/cache.rs +++ b/crates/router/src/core/cache.rs @@ -3,10 +3,10 @@ use error_stack::{report, ResultExt}; use storage_impl::redis::cache::{publish_into_redact_channel, CacheKind}; use super::errors; -use crate::{routes::AppState, services}; +use crate::{routes::SessionState, services}; pub async fn invalidate( - state: AppState, + state: SessionState, key: &str, ) -> CustomResult, errors::ApiErrorResponse> { let store = state.store.as_ref(); diff --git a/crates/router/src/core/cards_info.rs b/crates/router/src/core/cards_info.rs index 4352e1808c73..bc01a786d154 100644 --- a/crates/router/src/core/cards_info.rs +++ b/crates/router/src/core/cards_info.rs @@ -21,7 +21,7 @@ fn verify_iin_length(card_iin: &str) -> Result<(), errors::ApiErrorResponse> { #[instrument(skip_all)] pub async fn retrieve_card_info( - state: routes::AppState, + state: routes::SessionState, merchant_account: domain::MerchantAccount, request: api_models::cards_info::CardsInfoRequest, ) -> RouterResponse { diff --git a/crates/router/src/core/conditional_config.rs b/crates/router/src/core/conditional_config.rs index c352e825052a..a8bc8a797dc3 100644 --- a/crates/router/src/core/conditional_config.rs +++ b/crates/router/src/core/conditional_config.rs @@ -12,14 +12,14 @@ use super::routing::helpers::{ }; use crate::{ core::errors::{self, RouterResponse}, - routes::AppState, + routes::SessionState, services::api as service_api, types::domain, utils::OptionExt, }; pub async fn upsert_conditional_config( - state: AppState, + state: SessionState, key_store: domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, request: DecisionManager, @@ -149,7 +149,7 @@ pub async fn upsert_conditional_config( } pub async fn delete_conditional_config( - state: AppState, + state: SessionState, key_store: domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, ) -> RouterResponse<()> { @@ -177,7 +177,7 @@ pub async fn delete_conditional_config( } pub async fn retrieve_conditional_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, ) -> RouterResponse { let db = state.store.as_ref(); diff --git a/crates/router/src/core/configs.rs b/crates/router/src/core/configs.rs index ba2303167d76..be8dfc83828d 100644 --- a/crates/router/src/core/configs.rs +++ b/crates/router/src/core/configs.rs @@ -2,12 +2,12 @@ use error_stack::ResultExt; use crate::{ core::errors::{self, utils::StorageErrorExt, RouterResponse}, - routes::AppState, + routes::SessionState, services::ApplicationResponse, types::{api, transformers::ForeignInto}, }; -pub async fn set_config(state: AppState, config: api::Config) -> RouterResponse { +pub async fn set_config(state: SessionState, config: api::Config) -> RouterResponse { let store = state.store.as_ref(); let config = store .insert_config(diesel_models::configs::ConfigNew { @@ -21,7 +21,7 @@ pub async fn set_config(state: AppState, config: api::Config) -> RouterResponse< Ok(ApplicationResponse::Json(config.foreign_into())) } -pub async fn read_config(state: AppState, key: &str) -> RouterResponse { +pub async fn read_config(state: SessionState, key: &str) -> RouterResponse { let store = state.store.as_ref(); let config = store .find_config_by_key(key) @@ -31,7 +31,7 @@ pub async fn read_config(state: AppState, key: &str) -> RouterResponse RouterResponse { let store = state.store.as_ref(); @@ -42,7 +42,7 @@ pub async fn update_config( Ok(ApplicationResponse::Json(config.foreign_into())) } -pub async fn config_delete(state: AppState, key: String) -> RouterResponse { +pub async fn config_delete(state: SessionState, key: String) -> RouterResponse { let store = state.store.as_ref(); let config = store .delete_config_by_key(&key) diff --git a/crates/router/src/core/connector_onboarding.rs b/crates/router/src/core/connector_onboarding.rs index b69b2996a6ac..d6c20cc28104 100644 --- a/crates/router/src/core/connector_onboarding.rs +++ b/crates/router/src/core/connector_onboarding.rs @@ -7,18 +7,18 @@ use crate::{ services::{authentication as auth, ApplicationResponse}, types as oss_types, utils::connector_onboarding as utils, - AppState, + SessionState, }; pub mod paypal; #[async_trait::async_trait] pub trait AccessToken { - async fn access_token(state: &AppState) -> RouterResult; + async fn access_token(state: &SessionState) -> RouterResult; } pub async fn get_action_url( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, request: api::ActionUrlRequest, _req_state: ReqState, @@ -53,7 +53,7 @@ pub async fn get_action_url( } pub async fn sync_onboarding_status( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, request: api::OnboardingSyncRequest, _req_state: ReqState, @@ -107,7 +107,7 @@ pub async fn sync_onboarding_status( } pub async fn reset_tracking_id( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, request: api::ResetTrackingIdRequest, _req_state: ReqState, diff --git a/crates/router/src/core/connector_onboarding/paypal.rs b/crates/router/src/core/connector_onboarding/paypal.rs index 616fcbdc8cc3..6f28cfcbf8ec 100644 --- a/crates/router/src/core/connector_onboarding/paypal.rs +++ b/crates/router/src/core/connector_onboarding/paypal.rs @@ -11,10 +11,10 @@ use crate::{ services::{send_request, ApplicationResponse, Request}, types::{self as oss_types, api as oss_api_types, api::connector_onboarding as types}, utils::connector_onboarding as utils, - AppState, + SessionState, }; -fn build_referral_url(state: AppState) -> String { +fn build_referral_url(state: SessionState) -> String { format!( "{}v2/customer/partner-referrals", state.conf.connectors.paypal.base_url @@ -22,7 +22,7 @@ fn build_referral_url(state: AppState) -> String { } async fn build_referral_request( - state: AppState, + state: SessionState, tracking_id: String, return_url: String, ) -> RouterResult { @@ -37,7 +37,7 @@ async fn build_referral_request( } pub async fn get_action_url_from_paypal( - state: AppState, + state: SessionState, tracking_id: String, return_url: String, ) -> RouterResult { @@ -61,7 +61,7 @@ pub async fn get_action_url_from_paypal( parsed_response.extract_action_url() } -fn merchant_onboarding_status_url(state: AppState, tracking_id: String) -> String { +fn merchant_onboarding_status_url(state: SessionState, tracking_id: String) -> String { let partner_id = state .conf .connector_onboarding @@ -78,7 +78,7 @@ fn merchant_onboarding_status_url(state: AppState, tracking_id: String) -> Strin } pub async fn sync_merchant_onboarding_status( - state: AppState, + state: SessionState, tracking_id: String, ) -> RouterResult { let access_token = utils::paypal::generate_access_token(state.clone()).await?; @@ -113,7 +113,7 @@ pub async fn sync_merchant_onboarding_status( } async fn find_paypal_merchant_by_tracking_id( - state: AppState, + state: SessionState, tracking_id: String, access_token: &oss_types::AccessToken, ) -> RouterResult> { @@ -139,7 +139,7 @@ async fn find_paypal_merchant_by_tracking_id( } pub async fn update_mca( - state: &AppState, + state: &SessionState, merchant_id: String, connector_id: String, auth_details: oss_types::ConnectorAuthType, diff --git a/crates/router/src/core/currency.rs b/crates/router/src/core/currency.rs index f2791deb7b6e..96d75098271b 100644 --- a/crates/router/src/core/currency.rs +++ b/crates/router/src/core/currency.rs @@ -5,11 +5,11 @@ use crate::{ core::errors::ApiErrorResponse, services::ApplicationResponse, utils::currency::{self, convert_currency, get_forex_rates}, - AppState, + SessionState, }; pub async fn retrieve_forex( - state: AppState, + state: SessionState, ) -> CustomResult, ApiErrorResponse> { let forex_api = state.conf.forex_api.get_inner(); Ok(ApplicationResponse::Json( @@ -27,7 +27,7 @@ pub async fn retrieve_forex( } pub async fn convert_forex( - state: AppState, + state: SessionState, amount: i64, to_currency: String, from_currency: String, diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 1c983dcde950..ad1a292df090 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -13,7 +13,7 @@ use crate::{ payment_methods::cards, }, pii::PeekInterface, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{ api::customers, @@ -30,7 +30,7 @@ pub const REDACTED: &str = "Redacted"; #[instrument(skip(state))] pub async fn create_customer( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, mut customer_data: customers::CustomerRequest, @@ -142,7 +142,7 @@ pub async fn create_customer( #[instrument(skip(state))] pub async fn retrieve_customer( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: customers::CustomerId, @@ -172,7 +172,7 @@ pub async fn retrieve_customer( #[instrument(skip(state))] pub async fn list_customers( - state: AppState, + state: SessionState, merchant_id: String, key_store: domain::MerchantKeyStore, ) -> errors::CustomerResponse> { @@ -193,7 +193,7 @@ pub async fn list_customers( #[instrument(skip_all)] pub async fn delete_customer( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: customers::CustomerId, key_store: domain::MerchantKeyStore, @@ -348,7 +348,7 @@ pub async fn delete_customer( #[instrument(skip(state))] pub async fn update_customer( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, update_customer: customers::CustomerRequest, key_store: domain::MerchantKeyStore, diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 4928dc949dc9..883612869039 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -10,7 +10,7 @@ use super::{ }; use crate::{ core::{files, payments, utils as core_utils}, - routes::AppState, + routes::SessionState, services, types::{ api::{self, disputes}, @@ -24,7 +24,7 @@ use crate::{ #[instrument(skip(state))] pub async fn retrieve_dispute( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: disputes::DisputeId, ) -> RouterResponse { @@ -41,7 +41,7 @@ pub async fn retrieve_dispute( #[instrument(skip(state))] pub async fn retrieve_disputes_list( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, constraints: api_models::disputes::DisputeListConstraints, ) -> RouterResponse> { @@ -60,7 +60,7 @@ pub async fn retrieve_disputes_list( #[instrument(skip(state))] pub async fn accept_dispute( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: disputes::DisputeId, @@ -161,7 +161,7 @@ pub async fn accept_dispute( #[instrument(skip(state))] pub async fn submit_evidence( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: dispute_models::SubmitEvidenceRequest, @@ -324,7 +324,7 @@ pub async fn submit_evidence( } pub async fn attach_evidence( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, attach_evidence_request: api::AttachEvidenceRequest, @@ -358,12 +358,12 @@ pub async fn attach_evidence( }) }, )?; - let create_file_response = files::files_create_core( + let create_file_response = Box::pin(files::files_create_core( state.clone(), merchant_account, key_store, attach_evidence_request.create_file_request, - ) + )) .await?; let file_id = match &create_file_response { services::ApplicationResponse::Json(res) => res.file_id.clone(), @@ -401,7 +401,7 @@ pub async fn attach_evidence( #[instrument(skip(state))] pub async fn retrieve_dispute_evidence( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: disputes::DisputeId, ) -> RouterResponse> { @@ -424,7 +424,7 @@ pub async fn retrieve_dispute_evidence( } pub async fn delete_evidence( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, delete_evidence_request: dispute_models::DeleteEvidenceRequest, ) -> RouterResponse { diff --git a/crates/router/src/core/disputes/transformers.rs b/crates/router/src/core/disputes/transformers.rs index 936184fc9937..d37410d5ef83 100644 --- a/crates/router/src/core/disputes/transformers.rs +++ b/crates/router/src/core/disputes/transformers.rs @@ -4,7 +4,7 @@ use error_stack::ResultExt; use crate::{ core::{errors, files::helpers::retrieve_file_and_provider_file_id_from_file_id}, - routes::AppState, + routes::SessionState, types::{ api::{self, DisputeEvidence}, domain, @@ -14,7 +14,7 @@ use crate::{ }; pub async fn get_evidence_request_data( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, evidence_request: api_models::disputes::SubmitEvidenceRequest, @@ -203,7 +203,7 @@ pub fn update_dispute_evidence( } pub async fn get_dispute_evidence_block( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, evidence_type: EvidenceType, file_id: String, @@ -271,7 +271,7 @@ pub fn delete_evidence_file( } pub async fn get_dispute_evidence_vec( - state: &AppState, + state: &SessionState, merchant_account: domain::MerchantAccount, dispute_evidence: DisputeEvidence, ) -> CustomResult, errors::ApiErrorResponse> { diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index 3a8cab8c5161..0e9e40227985 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -219,6 +219,8 @@ pub enum ConnectorError { }, #[error("Invalid Configuration")] InvalidConnectorConfig { config: &'static str }, + #[error("Failed to convert amount to required type")] + AmountConversionFailed, } #[derive(Debug, thiserror::Error)] diff --git a/crates/router/src/core/errors/utils.rs b/crates/router/src/core/errors/utils.rs index 1e2f5a2c3057..8ec95514f881 100644 --- a/crates/router/src/core/errors/utils.rs +++ b/crates/router/src/core/errors/utils.rs @@ -215,7 +215,8 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::InSufficientBalanceInPaymentMethod | errors::ConnectorError::RequestTimeoutReceived | errors::ConnectorError::CurrencyNotSupported { .. } - | errors::ConnectorError::InvalidConnectorConfig { .. } => { + | errors::ConnectorError::InvalidConnectorConfig { .. } + | errors::ConnectorError::AmountConversionFailed { .. } => { err.change_context(errors::ApiErrorResponse::RefundFailed { data: None }) } }) @@ -309,7 +310,8 @@ impl ConnectorErrorExt for error_stack::Result errors::ConnectorError::MissingPaymentMethodType | errors::ConnectorError::InSufficientBalanceInPaymentMethod | errors::ConnectorError::RequestTimeoutReceived | - errors::ConnectorError::ProcessingStepFailed(None) => errors::ApiErrorResponse::InternalServerError + errors::ConnectorError::ProcessingStepFailed(None)| + errors::ConnectorError::AmountConversionFailed => errors::ApiErrorResponse::InternalServerError }; err.change_context(error) }) @@ -399,7 +401,8 @@ impl ConnectorErrorExt for error_stack::Result | errors::ConnectorError::InSufficientBalanceInPaymentMethod | errors::ConnectorError::RequestTimeoutReceived | errors::ConnectorError::CurrencyNotSupported { .. } - | errors::ConnectorError::ProcessingStepFailed(None) => { + | errors::ConnectorError::ProcessingStepFailed(None) + | errors::ConnectorError::AmountConversionFailed { .. } => { logger::error!(%error,"Setup Mandate flow failed"); errors::ApiErrorResponse::PaymentAuthorizationFailed { data: None } } diff --git a/crates/router/src/core/files.rs b/crates/router/src/core/files.rs index 5dedcb148633..9ba5c29ce760 100644 --- a/crates/router/src/core/files.rs +++ b/crates/router/src/core/files.rs @@ -6,13 +6,13 @@ use error_stack::ResultExt; use super::errors::{self, RouterResponse}; use crate::{ consts, - routes::AppState, + routes::SessionState, services::ApplicationResponse, types::{api, domain}, }; pub async fn files_create_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, create_file_request: api::CreateFileRequest, @@ -78,7 +78,7 @@ pub async fn files_create_core( } pub async fn files_delete_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: api::FileId, ) -> RouterResponse { @@ -94,7 +94,7 @@ pub async fn files_delete_core( } pub async fn files_retrieve_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: api::FileId, diff --git a/crates/router/src/core/files/helpers.rs b/crates/router/src/core/files/helpers.rs index 0c6a88d8270c..0be198377b86 100644 --- a/crates/router/src/core/files/helpers.rs +++ b/crates/router/src/core/files/helpers.rs @@ -8,7 +8,7 @@ use crate::{ errors::{self, StorageErrorExt}, payments, utils, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, transformers::ForeignTryFrom}, }; @@ -31,7 +31,7 @@ pub async fn get_file_purpose(field: &mut Field) -> Option { } pub async fn validate_file_upload( - state: &AppState, + state: &SessionState, merchant_account: domain::MerchantAccount, create_file_request: api::CreateFileRequest, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -81,7 +81,7 @@ pub async fn validate_file_upload( } pub async fn delete_file_using_file_id( - state: &AppState, + state: &SessionState, file_key: String, merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -113,7 +113,7 @@ pub async fn delete_file_using_file_id( } pub async fn retrieve_file_from_connector( - state: &AppState, + state: &SessionState, file_metadata: diesel_models::file::FileMetadata, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -171,7 +171,7 @@ pub async fn retrieve_file_from_connector( } pub async fn retrieve_file_and_provider_file_id_from_file_id( - state: &AppState, + state: &SessionState, file_id: Option, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -227,7 +227,7 @@ pub async fn retrieve_file_and_provider_file_id_from_file_id( //Upload file to connector if it supports / store it in S3 and return file_upload_provider, provider_file_id accordingly pub async fn upload_and_get_provider_provider_file_id_profile_id( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, create_file_request: &api::CreateFileRequest, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 63bf80bac020..f7f7108c2baa 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -24,7 +24,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ self as oss_types, @@ -50,7 +50,7 @@ pub mod types; #[instrument(skip_all)] pub async fn call_frm_service( - state: &AppState, + state: &SessionState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, merchant_account: &domain::MerchantAccount, @@ -302,7 +302,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn make_frm_data_and_fraud_check_operation<'a, F>( _db: &dyn StorageInterface, - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_data: payments::PaymentData, frm_routing_algorithm: FrmRoutingAlgorithm, @@ -370,7 +370,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn pre_payment_frm_core<'a, F, Req>( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut payments::PaymentData, frm_info: &mut FrmInfo, @@ -456,7 +456,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn post_payment_frm_core<'a, F>( - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: &domain::MerchantAccount, payment_data: &mut payments::PaymentData, @@ -547,7 +547,7 @@ pub async fn call_frm_before_connector_call<'a, F, Req>( operation: &BoxedOperation<'_, F, Req>, merchant_account: &domain::MerchantAccount, payment_data: &mut payments::PaymentData, - state: &AppState, + state: &SessionState, frm_info: &mut Option>, customer: &Option, should_continue_transaction: &mut bool, @@ -563,7 +563,7 @@ where frm_routing_algorithm.zip(frm_connector_label) { if let Some(frm_configs) = frm_configs.clone() { - let mut updated_frm_info = make_frm_data_and_fraud_check_operation( + let mut updated_frm_info = Box::pin(make_frm_data_and_fraud_check_operation( db, state, merchant_account, @@ -572,7 +572,7 @@ where profile_id, frm_configs.clone(), customer, - ) + )) .await?; if is_frm_enabled { @@ -643,7 +643,7 @@ impl From for PaymentDetails { #[instrument(skip_all)] pub async fn frm_fulfillment_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: frm_core_types::FrmFulfillmentRequest, @@ -705,7 +705,7 @@ pub async fn make_fulfillment_api_call( db: &dyn StorageInterface, fraud_check: FraudCheck, payment_intent: PaymentIntent, - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: frm_core_types::FrmFulfillmentRequest, diff --git a/crates/router/src/core/fraud_check/flows.rs b/crates/router/src/core/fraud_check/flows.rs index 3d4916a372be..896d062bc14c 100644 --- a/crates/router/src/core/fraud_check/flows.rs +++ b/crates/router/src/core/fraud_check/flows.rs @@ -11,7 +11,7 @@ use crate::{ errors::RouterResult, payments::{self, flows::ConstructFlowSpecificData}, }, - routes::AppState, + routes::SessionState, services, types::{ api::{Connector, FraudCheckConnectorData}, @@ -24,7 +24,7 @@ use crate::{ pub trait FeatureFrm { async fn decide_frm_flows<'a>( self, - state: &AppState, + state: &SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index c754682f72d5..74353e83b3d5 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -19,7 +19,7 @@ use crate::{ storage::enums as storage_enums, BrowserInformation, ConnectorAuthType, ResponseId, RouterData, }, - AppState, + SessionState, }; #[async_trait] @@ -28,7 +28,7 @@ impl ConstructFlowSpecificData( &self, - _state: &AppState, + _state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -138,7 +138,7 @@ impl ConstructFlowSpecificData for FrmCheckoutRouterData { async fn decide_frm_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, merchant_account: &domain::MerchantAccount, @@ -156,7 +156,7 @@ impl FeatureFrm for FrmCheckoutRouter pub async fn decide_frm_flow<'a, 'b>( router_data: &'b mut FrmCheckoutRouterData, - state: &'a AppState, + state: &'a SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, _merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs b/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs index 1f4f2ccebeee..f91d87c808c0 100644 --- a/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs +++ b/crates/router/src/core/fraud_check/flows/fulfillment_flow.rs @@ -13,12 +13,12 @@ use crate::{ fraud_check::{FraudCheckFulfillmentData, FrmFulfillmentRouterData}, storage, ConnectorAuthType, ErrorResponse, PaymentAddress, RouterData, }, - utils, AppState, + utils, SessionState, }; #[instrument(skip_all)] pub async fn construct_fulfillment_router_data<'a>( - state: &'a AppState, + state: &'a SessionState, payment_intent: &'a storage::PaymentIntent, payment_attempt: &storage::PaymentAttempt, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index b854a3337700..b8f8810f09b3 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -19,7 +19,7 @@ use crate::{ storage::enums as storage_enums, ConnectorAuthType, ResponseId, RouterData, }, - utils, AppState, + utils, SessionState, }; #[async_trait] @@ -28,7 +28,7 @@ impl ConstructFlowSpecificData( &self, - _state: &AppState, + _state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -110,7 +110,7 @@ impl ConstructFlowSpecificData for FrmRecordReturnRouterData { async fn decide_frm_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, merchant_account: &domain::MerchantAccount, @@ -128,7 +128,7 @@ impl FeatureFrm for FrmRecordReturnRou pub async fn decide_frm_flow<'a, 'b>( router_data: &'b mut FrmRecordReturnRouterData, - state: &'a AppState, + state: &'a SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, _merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/flows/sale_flow.rs b/crates/router/src/core/fraud_check/flows/sale_flow.rs index 829f8b736b0b..b605c5406568 100644 --- a/crates/router/src/core/fraud_check/flows/sale_flow.rs +++ b/crates/router/src/core/fraud_check/flows/sale_flow.rs @@ -17,7 +17,7 @@ use crate::{ storage::enums as storage_enums, ConnectorAuthType, ResponseId, RouterData, }, - AppState, + SessionState, }; #[async_trait] @@ -26,7 +26,7 @@ impl ConstructFlowSpecificData( &self, - _state: &AppState, + _state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -119,7 +119,7 @@ impl ConstructFlowSpecificData for FrmSaleRouterData { async fn decide_frm_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, merchant_account: &domain::MerchantAccount, @@ -137,7 +137,7 @@ impl FeatureFrm for FrmSaleRouterData { pub async fn decide_frm_flow<'a, 'b>( router_data: &'b mut FrmSaleRouterData, - state: &'a AppState, + state: &'a SessionState, connector: &FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, _merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/flows/transaction_flow.rs b/crates/router/src/core/fraud_check/flows/transaction_flow.rs index 0b851d7b6263..8c496792a81e 100644 --- a/crates/router/src/core/fraud_check/flows/transaction_flow.rs +++ b/crates/router/src/core/fraud_check/flows/transaction_flow.rs @@ -18,7 +18,7 @@ use crate::{ storage::enums as storage_enums, ConnectorAuthType, ResponseId, RouterData, }, - AppState, + SessionState, }; #[async_trait] @@ -31,7 +31,7 @@ impl { async fn construct_router_data<'a>( &self, - _state: &AppState, + _state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -123,7 +123,7 @@ impl impl FeatureFrm for FrmTransactionRouterData { async fn decide_frm_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &frm_api::FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, merchant_account: &domain::MerchantAccount, @@ -141,7 +141,7 @@ impl FeatureFrm for FrmTransact pub async fn decide_frm_flow<'a, 'b>( router_data: &'b mut FrmTransactionRouterData, - state: &'a AppState, + state: &'a SessionState, connector: &frm_api::FraudCheckConnectorData, call_connector_action: payments::CallConnectorAction, _merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/operation.rs b/crates/router/src/core/fraud_check/operation.rs index a9b95cdf3b5e..33a370ac8499 100644 --- a/crates/router/src/core/fraud_check/operation.rs +++ b/crates/router/src/core/fraud_check/operation.rs @@ -15,7 +15,7 @@ use crate::{ payments, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, types::{domain, fraud_check::FrmRouterData}, }; @@ -40,7 +40,7 @@ pub trait FraudCheckOperation: Send + std::fmt::Debug { pub trait GetTracker: Send { async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: D, frm_connector_details: ConnectorDetailsCore, ) -> RouterResult>; @@ -51,7 +51,7 @@ pub trait GetTracker: Send { pub trait Domain: Send + Sync { async fn post_payment_frm<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, req_state: ReqState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, @@ -64,7 +64,7 @@ pub trait Domain: Send + Sync { async fn pre_payment_frm<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, merchant_account: &domain::MerchantAccount, @@ -79,7 +79,7 @@ pub trait Domain: Send + Sync { #[allow(clippy::too_many_arguments)] async fn execute_post_tasks( &self, - _state: &AppState, + _state: &SessionState, _req_state: ReqState, frm_data: &mut FrmData, _merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index 6a310aa131dd..cc4fc0370e97 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -38,7 +38,7 @@ use crate::{ }, ResponseId, }, - utils, AppState, + utils, SessionState, }; #[derive(Debug, Clone, Copy)] @@ -73,7 +73,7 @@ impl GetTracker for FraudCheckPost { #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: PaymentToFrmData, frm_connector_details: ConnectorDetailsCore, ) -> RouterResult> { @@ -142,7 +142,7 @@ impl Domain for FraudCheckPost { #[instrument(skip_all)] async fn post_payment_frm<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, _req_state: ReqState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, @@ -182,7 +182,7 @@ impl Domain for FraudCheckPost { #[instrument(skip_all)] async fn execute_post_tasks( &self, - state: &AppState, + state: &SessionState, req_state: ReqState, frm_data: &mut FrmData, merchant_account: &domain::MerchantAccount, @@ -293,7 +293,7 @@ impl Domain for FraudCheckPost { #[instrument(skip_all)] async fn pre_payment_frm<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs index ae316e7a1108..272cd5aa7044 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs @@ -32,7 +32,7 @@ use crate::{ }, ResponseId, }, - AppState, + SessionState, }; #[derive(Debug, Clone, Copy)] @@ -67,7 +67,7 @@ impl GetTracker for FraudCheckPre { #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: PaymentToFrmData, frm_connector_details: ConnectorDetailsCore, ) -> RouterResult> { @@ -139,7 +139,7 @@ impl Domain for FraudCheckPre { #[instrument(skip_all)] async fn post_payment_frm<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, _req_state: ReqState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, @@ -178,7 +178,7 @@ impl Domain for FraudCheckPre { async fn pre_payment_frm<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut payments::PaymentData, frm_data: &mut FrmData, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/gsm.rs b/crates/router/src/core/gsm.rs index 668c1773e2cd..4a678a5c4089 100644 --- a/crates/router/src/core/gsm.rs +++ b/crates/router/src/core/gsm.rs @@ -11,12 +11,12 @@ use crate::{ db::gsm::GsmInterface, services, types::transformers::ForeignInto, - AppState, + SessionState, }; #[instrument(skip_all)] pub async fn create_gsm_rule( - state: AppState, + state: SessionState, gsm_rule: gsm_api_types::GsmCreateRequest, ) -> RouterResponse { let db = state.store.as_ref(); @@ -30,7 +30,7 @@ pub async fn create_gsm_rule( #[instrument(skip_all)] pub async fn retrieve_gsm_rule( - state: AppState, + state: SessionState, gsm_request: gsm_api_types::GsmRetrieveRequest, ) -> RouterResponse { let db = state.store.as_ref(); @@ -51,7 +51,7 @@ pub async fn retrieve_gsm_rule( #[instrument(skip_all)] pub async fn update_gsm_rule( - state: AppState, + state: SessionState, gsm_request: gsm_api_types::GsmUpdateRequest, ) -> RouterResponse { let db = state.store.as_ref(); @@ -94,7 +94,7 @@ pub async fn update_gsm_rule( #[instrument(skip_all)] pub async fn delete_gsm_rule( - state: AppState, + state: SessionState, gsm_request: gsm_api_types::GsmDeleteRequest, ) -> RouterResponse { let db = state.store.as_ref(); diff --git a/crates/router/src/core/health_check.rs b/crates/router/src/core/health_check.rs index e90fe77e8087..6092ac8bbb98 100644 --- a/crates/router/src/core/health_check.rs +++ b/crates/router/src/core/health_check.rs @@ -31,7 +31,7 @@ pub trait HealthCheckInterface { } #[async_trait::async_trait] -impl HealthCheckInterface for app::AppState { +impl HealthCheckInterface for app::SessionState { async fn health_check_db(&self) -> CustomResult { let db = &*self.store; db.health_check_db().await?; diff --git a/crates/router/src/core/locker_migration.rs b/crates/router/src/core/locker_migration.rs index 9dbc54287484..d968e62bea88 100644 --- a/crates/router/src/core/locker_migration.rs +++ b/crates/router/src/core/locker_migration.rs @@ -7,13 +7,13 @@ use futures::TryFutureExt; use super::{errors::StorageErrorExt, payment_methods::cards}; use crate::{ errors, - routes::AppState, + routes::SessionState, services::{self, logger}, types::{api, domain}, }; pub async fn rust_locker_migration( - state: AppState, + state: SessionState, merchant_id: &str, ) -> CustomResult, errors::ApiErrorResponse> { let db = state.store.as_ref(); @@ -75,7 +75,7 @@ pub async fn rust_locker_migration( } pub async fn call_to_locker( - state: &AppState, + state: &SessionState, payment_methods: Vec, customer_id: &id_type::CustomerId, merchant_id: &str, @@ -140,6 +140,7 @@ pub async fn call_to_locker( merchant_account, api_enums::LockerChoice::HyperswitchCardVault, Some(pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id)), + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs index 839f55dea2db..f484467f3547 100644 --- a/crates/router/src/core/mandate.rs +++ b/crates/router/src/core/mandate.rs @@ -14,7 +14,7 @@ use crate::{ payments::CallConnectorAction, }, db::StorageInterface, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{ self, @@ -32,7 +32,7 @@ use crate::{ #[instrument(skip(state))] pub async fn get_mandate( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: mandates::MandateId, @@ -60,7 +60,7 @@ pub async fn get_mandate( #[instrument(skip(state))] pub async fn revoke_mandate( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: mandates::MandateId, @@ -223,7 +223,7 @@ pub async fn update_connector_mandate_id( #[instrument(skip(state))] pub async fn get_customer_mandates( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: customers::CustomerId, @@ -271,7 +271,7 @@ where } } pub async fn update_mandate_procedure( - state: &AppState, + state: &SessionState, resp: types::RouterData, mandate: Mandate, merchant_id: &str, @@ -342,7 +342,7 @@ where } pub async fn mandate_procedure( - state: &AppState, + state: &SessionState, resp: &types::RouterData, customer_id: &Option, pm_id: Option, @@ -472,7 +472,7 @@ where #[instrument(skip(state))] pub async fn retrieve_mandates_list( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, constraints: api_models::mandates::MandateListConstraints, diff --git a/crates/router/src/core/mandate/helpers.rs b/crates/router/src/core/mandate/helpers.rs index 4e9d07b7f828..b0c727925df5 100644 --- a/crates/router/src/core/mandate/helpers.rs +++ b/crates/router/src/core/mandate/helpers.rs @@ -7,12 +7,12 @@ use hyperswitch_domain_models::mandates::MandateData; use crate::{ core::{errors, payments}, - routes::AppState, + routes::SessionState, types::{api, domain}, }; pub async fn get_profile_id_for_mandate( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, mandate: Mandate, ) -> CustomResult { diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 9b8fd3cbe0ef..e4ef38ae3512 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -14,7 +14,7 @@ use time::PrimitiveDateTime; use super::errors::{self, RouterResult, StorageErrorExt}; use crate::{ errors::RouterResponse, - routes::AppState, + routes::SessionState, services, types::{ api::payment_link::PaymentLinkResponseExt, domain, storage::enums as storage_enums, @@ -23,7 +23,7 @@ use crate::{ }; pub async fn retrieve_payment_link( - state: AppState, + state: SessionState, payment_link_id: String, ) -> RouterResponse { let db = &*state.store; @@ -47,7 +47,7 @@ pub async fn retrieve_payment_link( } pub async fn initiate_payment_link_flow( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, merchant_id: String, payment_id: String, @@ -287,7 +287,7 @@ fn validate_sdk_requirements( } pub async fn list_payment_link( - state: AppState, + state: SessionState, merchant: domain::MerchantAccount, constraints: api_models::payments::PaymentLinkListConstraints, ) -> RouterResponse> { @@ -501,7 +501,7 @@ fn check_payment_link_invalid_conditions( } pub async fn get_payment_link_status( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, merchant_id: String, payment_id: String, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 4b4eb34b7e2d..b6d95248abc6 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1,7 +1,6 @@ pub mod cards; pub mod surcharge_decision_configs; pub mod transformers; -pub mod utils; pub mod vault; pub use api_models::enums::Connector; @@ -14,7 +13,7 @@ use router_env::{instrument, tracing}; use crate::{ core::{errors::RouterResult, payments::helpers, pm_auth as core_pm_auth}, - routes::AppState, + routes::SessionState, types::{ api::{self, payments}, domain, storage, @@ -24,7 +23,7 @@ use crate::{ #[instrument(skip_all)] pub async fn retrieve_payment_method( pm_data: &Option, - state: &AppState, + state: &SessionState, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, merchant_key_store: &domain::MerchantKeyStore, @@ -96,7 +95,7 @@ pub async fn retrieve_payment_method( #[instrument(skip_all)] pub async fn retrieve_payment_method_with_token( - state: &AppState, + state: &SessionState, merchant_key_store: &domain::MerchantKeyStore, token_data: &storage::PaymentTokenData, payment_intent: &PaymentIntent, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 33103b7a901c..c62f47b5362b 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -5,7 +5,7 @@ use std::{ }; use api_models::{ - admin::PaymentMethodsEnabled, + admin::{self, PaymentMethodsEnabled}, enums::{self as api_enums}, payment_methods::{ BankAccountTokenData, Card, CardDetailUpdate, CardDetailsPaymentMethod, CardNetworkTypes, @@ -19,22 +19,18 @@ use api_models::{ pm_auth::PaymentMethodAuthConfig, surcharge_decision_configs as api_surcharge_decision_configs, }; -use cgraph::ConstraintGraph; use common_enums::enums::MerchantStorageScheme; use common_utils::{ consts, ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, generate_id, id_type, }; -use diesel_models::{business_profile::BusinessProfile, encryption::Encryption, payment_method}; +use diesel_models::{ + business_profile::BusinessProfile, encryption::Encryption, enums as storage_enums, + payment_method, +}; use domain::CustomerUpdate; use error_stack::{report, ResultExt}; -use euclid::{ - dssa::graph::{AnalysisContext, CgraphExt}, - frontend::dir, -}; -use hyperswitch_constraint_graph as cgraph; -use kgraph_utils::transformers::IntoDirValue; use masking::Secret; use router_env::{instrument, tracing}; use strum::IntoEnumIterator; @@ -49,11 +45,7 @@ use crate::{ configs::settings, core::{ errors::{self, StorageErrorExt}, - payment_methods::{ - transformers as payment_methods, - utils::{get_merchant_pm_filter_graph, make_pm_graph, refresh_pm_filters_cache}, - vault, - }, + payment_methods::{transformers as payment_methods, vault}, payments::{ helpers, routing::{self, SessionFlowRoutingInput}, @@ -271,7 +263,7 @@ pub async fn get_or_insert_payment_method( #[instrument(skip_all)] pub async fn get_client_secret_or_add_payment_method( - state: routes::AppState, + state: routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -286,7 +278,7 @@ pub async fn get_client_secret_or_add_payment_method( let condition = req.card.is_some() || req.bank_transfer.is_some() || req.wallet.is_some(); if condition { - add_payment_method(state, req, merchant_account, key_store).await + Box::pin(add_payment_method(state, req, merchant_account, key_store)).await } else { let payment_method_id = generate_id(consts::ID_LENGTH, "pm"); @@ -345,7 +337,7 @@ pub fn authenticate_pm_client_secret_and_check_expiry( #[instrument(skip_all)] pub async fn add_payment_method_data( - state: routes::AppState, + state: routes::SessionState, req: api::PaymentMethodCreate, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, @@ -393,14 +385,14 @@ pub async fn add_payment_method_data( match pmd { api_models::payment_methods::PaymentMethodCreateData::Card(card) => { helpers::validate_card_expiry(&card.card_exp_month, &card.card_exp_year)?; - let resp = add_card_to_locker( + let resp = Box::pin(add_card_to_locker( &state, req.clone(), &card, &customer_id, &merchant_account, None, - ) + )) .await .change_context(errors::ApiErrorResponse::InternalServerError); @@ -521,7 +513,7 @@ pub async fn add_payment_method_data( #[instrument(skip_all)] pub async fn add_payment_method( - state: routes::AppState, + state: routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -555,14 +547,14 @@ pub async fn add_payment_method( api_enums::PaymentMethod::Card => match req.card.clone() { Some(card) => { helpers::validate_card_expiry(&card.card_exp_month, &card.card_exp_year)?; - add_card_to_locker( + Box::pin(add_card_to_locker( &state, req.clone(), &card, &customer_id, merchant_account, None, - ) + )) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Add Card Failed") @@ -769,7 +761,7 @@ pub async fn insert_payment_method( #[instrument(skip_all)] pub async fn update_customer_payment_method( - state: routes::AppState, + state: routes::SessionState, merchant_account: domain::MerchantAccount, req: api::PaymentMethodUpdate, payment_method_id: &str, @@ -888,14 +880,14 @@ pub async fn update_customer_payment_method( .await?; // Add the updated payment method data to locker - let (mut add_card_resp, _) = add_card_to_locker( + let (mut add_card_resp, _) = Box::pin(add_card_to_locker( &state, new_pm.clone(), &updated_card_details, &pm.customer_id, &merchant_account, Some(pm.locker_id.as_ref().unwrap_or(&pm.payment_method_id)), - ) + )) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to add updated payment method to locker")?; @@ -1025,7 +1017,7 @@ pub fn validate_payment_method_update( #[cfg(feature = "payouts")] pub async fn add_bank_to_locker( - state: &routes::AppState, + state: &routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -1090,7 +1082,7 @@ pub async fn add_bank_to_locker( /// The response will be the tuple of PaymentMethodResponse and the duplication check of payment_method pub async fn add_card_to_locker( - state: &routes::AppState, + state: &routes::SessionState, req: api::PaymentMethodCreate, card: &api::CardDetail, customer_id: &id_type::CustomerId, @@ -1138,7 +1130,7 @@ pub async fn add_card_to_locker( } pub async fn get_card_from_locker( - state: &routes::AppState, + state: &routes::SessionState, customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &str, @@ -1179,7 +1171,7 @@ pub async fn get_card_from_locker( } pub async fn delete_card_from_locker( - state: &routes::AppState, + state: &routes::SessionState, customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &str, @@ -1203,7 +1195,7 @@ pub async fn delete_card_from_locker( #[instrument(skip_all)] pub async fn add_card_hs( - state: &routes::AppState, + state: &routes::SessionState, req: api::PaymentMethodCreate, card: &api::CardDetail, customer_id: &id_type::CustomerId, @@ -1267,7 +1259,7 @@ pub async fn decode_and_decrypt_locker_data( #[instrument(skip_all)] pub async fn get_payment_method_from_hs_locker<'a>( - state: &'a routes::AppState, + state: &'a routes::SessionState, key_store: &domain::MerchantKeyStore, customer_id: &id_type::CustomerId, merchant_id: &str, @@ -1329,7 +1321,7 @@ pub async fn get_payment_method_from_hs_locker<'a>( #[instrument(skip_all)] pub async fn call_to_locker_hs<'a>( - state: &routes::AppState, + state: &routes::SessionState, payload: &payment_methods::StoreLockerReq<'a>, customer_id: &id_type::CustomerId, locker_choice: api_enums::LockerChoice, @@ -1402,7 +1394,7 @@ pub async fn update_payment_method_connector_mandate_details( } #[instrument(skip_all)] pub async fn get_card_from_hs_locker<'a>( - state: &'a routes::AppState, + state: &'a routes::SessionState, customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &'a str, @@ -1455,7 +1447,7 @@ pub async fn get_card_from_hs_locker<'a>( #[instrument(skip_all)] pub async fn delete_card_from_hs_locker<'a>( - state: &routes::AppState, + state: &routes::SessionState, customer_id: &id_type::CustomerId, merchant_id: &str, card_reference: &'a str, @@ -1660,7 +1652,7 @@ pub async fn mock_delete_card<'a>( } //------------------------------------------------------------------------------ pub fn get_banks( - state: &routes::AppState, + state: &routes::SessionState, pm_type: common_enums::enums::PaymentMethodType, connectors: Vec, ) -> Result, errors::ApiErrorResponse> { @@ -1742,7 +1734,7 @@ fn get_val(str: String, val: &serde_json::Value) -> Option { } pub async fn list_payment_methods( - state: routes::AppState, + state: routes::SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, mut req: api::PaymentMethodListRequest, @@ -1885,94 +1877,31 @@ pub async fn list_payment_methods( .await?; // filter out connectors based on the business country - let filtered_mcas = - helpers::filter_mca_based_on_business_profile(all_mcas.clone(), profile_id.clone()); + let filtered_mcas = helpers::filter_mca_based_on_business_profile(all_mcas, profile_id); logger::debug!(mca_before_filtering=?filtered_mcas); let mut response: Vec = vec![]; + for mca in &filtered_mcas { + let payment_methods = match &mca.payment_methods_enabled { + Some(pm) => pm.clone(), + None => continue, + }; - // Key creation for storing PM_FILTER_CGRAPH - #[cfg(feature = "business_profile_routing")] - let key = { - let profile_id = profile_id - .clone() - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::GenericNotFoundError { - message: "Profile id not found".to_string(), - })?; - format!( - "pm_filters_cgraph_{}_{}", - &merchant_account.merchant_id, profile_id + filter_payment_methods( + payment_methods, + &mut req, + &mut response, + payment_intent.as_ref(), + payment_attempt.as_ref(), + billing_address.as_ref(), + mca.connector_name.clone(), + pm_config_mapping, + &state.conf.mandates.supported_payment_methods, + &state.conf.mandates.update_mandate_supported, + &state.conf.saved_payment_methods, ) - }; - - #[cfg(not(feature = "business_profile_routing"))] - let key = { format!("pm_filters_cgraph_{}", &merchant_account.merchant_id) }; - - if let Some(graph) = get_merchant_pm_filter_graph(&key).await { - // Derivation of PM_FILTER_CGRAPH from MokaCache successful - for mca in &filtered_mcas { - let payment_methods = match &mca.payment_methods_enabled { - Some(pm) => pm, - None => continue, - }; - filter_payment_methods( - &graph, - payment_methods, - &mut req, - &mut response, - payment_intent.as_ref(), - payment_attempt.as_ref(), - billing_address.as_ref(), - mca.connector_name.clone(), - &state.conf.saved_payment_methods, - ) - .await?; - } - } else { - // No PM_FILTER_CGRAPH Cache present in MokaCache - let mut builder = cgraph::ConstraintGraphBuilder::<'static, _>::new(); - for mca in &filtered_mcas { - let payment_methods = match &mca.payment_methods_enabled { - Some(pm) => pm, - None => continue, - }; - if let Err(e) = make_pm_graph( - &mut builder, - payment_methods, - mca.connector_name.clone(), - pm_config_mapping, - &state.conf.mandates.supported_payment_methods, - &state.conf.mandates.update_mandate_supported, - ) { - logger::error!( - "Failed to construct constraint graph for list payment methods {e:?}" - ); - } - } - - // Refreshing our CGraph cache - let graph = refresh_pm_filters_cache(&key, builder.build()).await; - - for mca in &filtered_mcas { - let payment_methods = match &mca.payment_methods_enabled { - Some(pm) => pm, - None => continue, - }; - filter_payment_methods( - &graph, - payment_methods, - &mut req, - &mut response, - payment_intent.as_ref(), - payment_attempt.as_ref(), - billing_address.as_ref(), - mca.connector_name.clone(), - &state.conf.saved_payment_methods, - ) - .await?; - } + .await?; } // Filter out wallet payment method from mca if customer has already saved it @@ -2731,7 +2660,7 @@ async fn validate_payment_method_and_client_secret( } pub async fn call_surcharge_decision_management( - state: routes::AppState, + state: routes::SessionState, merchant_account: &domain::MerchantAccount, business_profile: &BusinessProfile, payment_attempt: &storage::PaymentAttempt, @@ -2781,7 +2710,7 @@ pub async fn call_surcharge_decision_management( } pub async fn call_surcharge_decision_management_for_saved_card( - state: &routes::AppState, + state: &routes::SessionState, merchant_account: &domain::MerchantAccount, business_profile: &BusinessProfile, payment_attempt: &storage::PaymentAttempt, @@ -2829,18 +2758,20 @@ pub async fn call_surcharge_decision_management_for_saved_card( #[allow(clippy::too_many_arguments)] pub async fn filter_payment_methods( - graph: &ConstraintGraph<'_, dir::DirValue>, - payment_methods: &[serde_json::Value], + payment_methods: Vec, req: &mut api::PaymentMethodListRequest, resp: &mut Vec, payment_intent: Option<&storage::PaymentIntent>, payment_attempt: Option<&storage::PaymentAttempt>, address: Option<&domain::Address>, connector: String, + config: &settings::ConnectorFilters, + supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, + supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, saved_payment_methods: &settings::EligiblePaymentMethods, ) -> errors::CustomResult<(), errors::ApiErrorResponse> { - for payment_method in payment_methods.iter() { - let parse_result = serde_json::from_value::(payment_method.clone()); + for payment_method in payment_methods.into_iter() { + let parse_result = serde_json::from_value::(payment_method); if let Ok(payment_methods_enabled) = parse_result { let payment_method = payment_methods_enabled.payment_method; @@ -2871,13 +2802,57 @@ pub async fn filter_payment_methods( .map(|minor_amount| minor_amount.get_amount_as_i64()), ) { - let payment_method_object = payment_method_type_info.clone(); + let mut payment_method_object = payment_method_type_info; + + let filter; + ( + payment_method_object.accepted_countries, + req.accepted_countries, + filter, + ) = filter_pm_country_based( + &payment_method_object.accepted_countries, + &req.accepted_countries, + ); + let filter2; + ( + payment_method_object.accepted_currencies, + req.accepted_currencies, + filter2, + ) = filter_pm_currencies_based( + &payment_method_object.accepted_currencies, + &req.accepted_currencies, + ); - let pm_dir_value: dir::DirValue = - (payment_method_type_info.payment_method_type, payment_method) - .into_dir_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("pm_value_node not created")?; + let filter4 = filter_pm_card_network_based( + payment_method_object.card_networks.as_ref(), + req.card_networks.as_ref(), + &payment_method_object.payment_method_type, + ); + + let filter3 = if let Some(payment_intent) = payment_intent { + filter_payment_country_based(&payment_method_object, address).await? + && filter_payment_currency_based(payment_intent, &payment_method_object) + && filter_payment_amount_based(payment_intent, &payment_method_object) + && filter_payment_mandate_based(payment_attempt, &payment_method_object) + .await? + } else { + true + }; + + let filter5 = filter_pm_based_on_config( + config, + &connector, + &payment_method_object.payment_method_type, + payment_attempt, + &mut payment_method_object.card_networks, + &address.and_then(|inner| inner.country), + payment_attempt.and_then(|value| value.currency), + ); + + let filter6 = filter_pm_based_on_allowed_types( + allowed_payment_method_types.as_ref(), + &payment_method_object.payment_method_type, + ); let connector_variant = api_enums::Connector::from_str(connector.as_str()) .change_context(errors::ConnectorError::InvalidConnectorName) @@ -2887,98 +2862,35 @@ pub async fn filter_payment_methods( .attach_printable_lazy(|| { format!("unable to parse connector name {connector:?}") })?; - - let mut context_values: Vec = Vec::new(); - context_values.push(pm_dir_value.clone()); - - payment_intent.map(|intent| { - intent.currency.map(|currency| { - context_values.push(dir::DirValue::PaymentCurrency(currency)) - }) - }); - address.map(|address| { - address.country.map(|country| { - context_values.push(dir::DirValue::BillingCountry( - common_enums::Country::from_alpha2(country), - )) - }) - }); - - let filter_pm_based_on_allowed_types = filter_pm_based_on_allowed_types( - allowed_payment_method_types.as_ref(), - &payment_method_object.payment_method_type, - ); - - if payment_attempt + let filter7 = payment_attempt .and_then(|attempt| attempt.mandate_details.as_ref()) - .is_some() - { - context_values.push(dir::DirValue::PaymentType( - euclid::enums::PaymentType::NewMandate, - )); - if let Ok(connector) = api_enums::RoutableConnectors::from_str( - connector_variant.to_string().as_str(), - ) { - context_values.push(dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, - ))); - }; - }; + .map(|_mandate_details| { + filter_pm_based_on_supported_payments_for_mandate( + supported_payment_methods_for_mandate, + &payment_method, + &payment_method_object.payment_method_type, + connector_variant, + ) + }) + .unwrap_or(true); - payment_attempt + let filter8 = payment_attempt .and_then(|attempt| attempt.mandate_data.as_ref()) .map(|mandate_detail| { if mandate_detail.update_mandate_id.is_some() { - context_values.push(dir::DirValue::PaymentType( - euclid::enums::PaymentType::UpdateMandate, - )); - if let Ok(connector) = api_enums::RoutableConnectors::from_str( - connector_variant.to_string().as_str(), - ) { - context_values.push(dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, - ))); - }; + filter_pm_based_on_update_mandate_support_for_connector( + supported_payment_methods_for_update_mandate, + &payment_method, + &payment_method_object.payment_method_type, + connector_variant, + ) + } else { + true } - }); - - payment_attempt - .map(|attempt| { - attempt.mandate_data.is_none() && attempt.mandate_details.is_none() }) - .and_then(|res| { - res.then(|| { - context_values.push(dir::DirValue::PaymentType( - euclid::enums::PaymentType::NonMandate, - )) - }) - }); - - payment_attempt - .and_then(|inner| inner.capture_method) - .and_then(|capture_method| { - (capture_method == common_enums::CaptureMethod::Manual).then(|| { - context_values.push(dir::DirValue::CaptureMethod( - common_enums::CaptureMethod::Manual, - )); - }) - }); - - let filter_pm_card_network_based = filter_pm_card_network_based( - payment_method_object.card_networks.as_ref(), - req.card_networks.as_ref(), - &payment_method_object.payment_method_type, - ); + .unwrap_or(true); - let saved_payment_methods_filter = req + let filter9 = req .client_secret .as_ref() .map(|cs| { @@ -2992,28 +2904,25 @@ pub async fn filter_payment_methods( }) .unwrap_or(true); - let context = AnalysisContext::from_dir_values(context_values.clone()); + let connector = connector.clone(); - let result = graph.key_value_analysis( - pm_dir_value.clone(), - &context, - &mut cgraph::Memoization::new(), - &mut cgraph::CycleCheck::new(), - None, + let response_pm_type = ResponsePaymentMethodIntermediate::new( + payment_method_object, + connector, + payment_method, ); - if filter_pm_based_on_allowed_types - && filter_pm_card_network_based - && saved_payment_methods_filter - && matches!(result, Ok(())) + + if filter + && filter2 + && filter3 + && filter4 + && filter5 + && filter6 + && filter7 + && filter8 + && filter9 { - let response_pm_type = ResponsePaymentMethodIntermediate::new( - payment_method_object, - connector.clone(), - payment_method, - ); resp.push(response_pm_type); - } else { - logger::error!("Filtering Payment Methods Failed"); } } } @@ -3021,12 +2930,287 @@ pub async fn filter_payment_methods( } Ok(()) } +pub fn filter_pm_based_on_update_mandate_support_for_connector( + supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, + payment_method: &api_enums::PaymentMethod, + payment_method_type: &api_enums::PaymentMethodType, + connector: api_enums::Connector, +) -> bool { + if payment_method == &api_enums::PaymentMethod::Card { + supported_payment_methods_for_mandate + .0 + .get(payment_method) + .map(|payment_method_type_hm| { + let pm_credit = payment_method_type_hm + .0 + .get(&api_enums::PaymentMethodType::Credit) + .map(|conn| conn.connector_list.clone()) + .unwrap_or_default(); + let pm_debit = payment_method_type_hm + .0 + .get(&api_enums::PaymentMethodType::Debit) + .map(|conn| conn.connector_list.clone()) + .unwrap_or_default(); + &pm_credit | &pm_debit + }) + .map(|supported_connectors| supported_connectors.contains(&connector)) + .unwrap_or(false) + } else { + supported_payment_methods_for_mandate + .0 + .get(payment_method) + .and_then(|payment_method_type_hm| payment_method_type_hm.0.get(payment_method_type)) + .map(|supported_connectors| supported_connectors.connector_list.contains(&connector)) + .unwrap_or(false) + } +} -fn filter_pm_based_on_allowed_types( - allowed_types: Option<&Vec>, +fn filter_pm_based_on_supported_payments_for_mandate( + supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, + payment_method: &api_enums::PaymentMethod, payment_method_type: &api_enums::PaymentMethodType, + connector: api_enums::Connector, ) -> bool { - allowed_types.map_or(true, |pm| pm.contains(payment_method_type)) + supported_payment_methods_for_mandate + .0 + .get(payment_method) + .and_then(|payment_method_type_hm| payment_method_type_hm.0.get(payment_method_type)) + .map(|supported_connectors| supported_connectors.connector_list.contains(&connector)) + .unwrap_or(false) +} + +fn filter_pm_based_on_config<'a>( + config: &'a settings::ConnectorFilters, + connector: &'a str, + payment_method_type: &'a api_enums::PaymentMethodType, + payment_attempt: Option<&storage::PaymentAttempt>, + card_network: &mut Option>, + country: &Option, + currency: Option, +) -> bool { + config + .0 + .get(connector) + .or_else(|| config.0.get("default")) + .and_then(|inner| match payment_method_type { + api_enums::PaymentMethodType::Credit | api_enums::PaymentMethodType::Debit => { + let country_currency_filter = inner + .0 + .get(&settings::PaymentMethodFilterKey::PaymentMethodType( + *payment_method_type, + )) + .map(|value| global_country_currency_filter(value, country, currency)); + + card_network_filter(country, currency, card_network, inner); + + let capture_method_filter = payment_attempt + .and_then(|inner| inner.capture_method) + .and_then(|capture_method| { + (capture_method == storage_enums::CaptureMethod::Manual).then(|| { + filter_pm_based_on_capture_method_used(inner, payment_method_type) + }) + }); + + Some( + country_currency_filter.unwrap_or(true) + && capture_method_filter.unwrap_or(true), + ) + } + payment_method_type => inner + .0 + .get(&settings::PaymentMethodFilterKey::PaymentMethodType( + *payment_method_type, + )) + .map(|value| global_country_currency_filter(value, country, currency)), + }) + .unwrap_or(true) +} + +///Filters the payment method list on basis of Capture methods, checks whether the connector issues Manual payments using cards or not if not it won't be visible in payment methods list +fn filter_pm_based_on_capture_method_used( + payment_method_filters: &settings::PaymentMethodFilters, + payment_method_type: &api_enums::PaymentMethodType, +) -> bool { + payment_method_filters + .0 + .get(&settings::PaymentMethodFilterKey::PaymentMethodType( + *payment_method_type, + )) + .and_then(|v| v.not_available_flows) + .and_then(|v| v.capture_method) + .map(|v| !matches!(v, api_enums::CaptureMethod::Manual)) + .unwrap_or(true) +} + +fn card_network_filter( + country: &Option, + currency: Option, + card_network: &mut Option>, + payment_method_filters: &settings::PaymentMethodFilters, +) { + if let Some(value) = card_network.as_mut() { + let filtered_card_networks = value + .iter() + .filter(|&element| { + let key = settings::PaymentMethodFilterKey::CardNetwork(element.clone()); + payment_method_filters + .0 + .get(&key) + .map(|value| global_country_currency_filter(value, country, currency)) + .unwrap_or(true) + }) + .cloned() + .collect::>(); + *value = filtered_card_networks; + } +} + +fn global_country_currency_filter( + item: &settings::CurrencyCountryFlowFilter, + country: &Option, + currency: Option, +) -> bool { + let country_condition = item + .country + .as_ref() + .zip(country.as_ref()) + .map(|(lhs, rhs)| lhs.contains(rhs)); + let currency_condition = item + .currency + .as_ref() + .zip(currency) + .map(|(lhs, rhs)| lhs.contains(&rhs)); + country_condition.unwrap_or(true) && currency_condition.unwrap_or(true) +} + +fn filter_pm_card_network_based( + pm_card_networks: Option<&Vec>, + request_card_networks: Option<&Vec>, + pm_type: &api_enums::PaymentMethodType, +) -> bool { + match pm_type { + api_enums::PaymentMethodType::Credit | api_enums::PaymentMethodType::Debit => { + match (pm_card_networks, request_card_networks) { + (Some(pm_card_networks), Some(request_card_networks)) => request_card_networks + .iter() + .all(|card_network| pm_card_networks.contains(card_network)), + (None, Some(_)) => false, + _ => true, + } + } + _ => true, + } +} +fn filter_pm_country_based( + accepted_countries: &Option, + req_country_list: &Option>, +) -> ( + Option, + Option>, + bool, +) { + match (accepted_countries, req_country_list) { + (None, None) => (None, None, true), + (None, Some(ref r)) => ( + Some(admin::AcceptedCountries::EnableOnly(r.to_vec())), + Some(r.to_vec()), + true, + ), + (Some(l), None) => (Some(l.to_owned()), None, true), + (Some(l), Some(ref r)) => { + let updated = match l { + admin::AcceptedCountries::EnableOnly(acc) => { + filter_accepted_enum_based(&Some(acc.clone()), &Some(r.to_owned())) + .map(admin::AcceptedCountries::EnableOnly) + } + + admin::AcceptedCountries::DisableOnly(den) => { + filter_disabled_enum_based(&Some(den.clone()), &Some(r.to_owned())) + .map(admin::AcceptedCountries::DisableOnly) + } + + admin::AcceptedCountries::AllAccepted => { + Some(admin::AcceptedCountries::AllAccepted) + } + }; + + (updated, Some(r.to_vec()), true) + } + } +} + +fn filter_pm_currencies_based( + accepted_currency: &Option, + req_currency_list: &Option>, +) -> ( + Option, + Option>, + bool, +) { + match (accepted_currency, req_currency_list) { + (None, None) => (None, None, true), + (None, Some(ref r)) => ( + Some(admin::AcceptedCurrencies::EnableOnly(r.to_vec())), + Some(r.to_vec()), + true, + ), + (Some(l), None) => (Some(l.to_owned()), None, true), + (Some(l), Some(ref r)) => { + let updated = match l { + admin::AcceptedCurrencies::EnableOnly(acc) => { + filter_accepted_enum_based(&Some(acc.clone()), &Some(r.to_owned())) + .map(admin::AcceptedCurrencies::EnableOnly) + } + + admin::AcceptedCurrencies::DisableOnly(den) => { + filter_disabled_enum_based(&Some(den.clone()), &Some(r.to_owned())) + .map(admin::AcceptedCurrencies::DisableOnly) + } + + admin::AcceptedCurrencies::AllAccepted => { + Some(admin::AcceptedCurrencies::AllAccepted) + } + }; + + (updated, Some(r.to_vec()), true) + } + } +} + +fn filter_accepted_enum_based( + left: &Option>, + right: &Option>, +) -> Option> { + match (left, right) { + (Some(ref l), Some(ref r)) => { + let a: HashSet<&T> = HashSet::from_iter(l.iter()); + let b: HashSet<&T> = HashSet::from_iter(r.iter()); + + let y: Vec = a.intersection(&b).map(|&i| i.to_owned()).collect(); + Some(y) + } + (Some(ref l), None) => Some(l.to_vec()), + (_, _) => None, + } +} + +fn filter_disabled_enum_based( + left: &Option>, + right: &Option>, +) -> Option> { + match (left, right) { + (Some(ref l), Some(ref r)) => { + let mut enabled = Vec::new(); + for element in r { + if !l.contains(element) { + enabled.push(element.to_owned()); + } + } + Some(enabled) + } + (None, Some(r)) => Some(r.to_vec()), + (_, _) => None, + } } fn filter_amount_based(payment_method: &RequestPaymentMethodTypes, amount: Option) -> bool { @@ -3044,9 +3228,24 @@ fn filter_amount_based(payment_method: &RequestPaymentMethodTypes, amount: Optio .map(|max_amt| amt <= max_amt.into()) }) .unwrap_or(true); + // let min_check = match (amount, payment_method.minimum_amount) { + // (Some(amt), Some(min_amt)) => amt >= min_amt, + // (_, _) => true, + // }; + // let max_check = match (amount, payment_method.maximum_amount) { + // (Some(amt), Some(max_amt)) => amt <= max_amt, + // (_, _) => true, + // }; (min_check && max_check) || amount == Some(0) } +fn filter_pm_based_on_allowed_types( + allowed_types: Option<&Vec>, + payment_method_type: &api_enums::PaymentMethodType, +) -> bool { + allowed_types.map_or(true, |pm| pm.contains(payment_method_type)) +} + fn filter_recurring_based( payment_method: &RequestPaymentMethodTypes, recurring_enabled: Option, @@ -3063,27 +3262,58 @@ fn filter_installment_based( }) } -fn filter_pm_card_network_based( - pm_card_networks: Option<&Vec>, - request_card_networks: Option<&Vec>, - pm_type: &api_enums::PaymentMethodType, +async fn filter_payment_country_based( + pm: &RequestPaymentMethodTypes, + address: Option<&domain::Address>, +) -> errors::CustomResult { + Ok(address.map_or(true, |address| { + address.country.as_ref().map_or(true, |country| { + pm.accepted_countries.as_ref().map_or(true, |ac| match ac { + admin::AcceptedCountries::EnableOnly(acc) => acc.contains(country), + admin::AcceptedCountries::DisableOnly(den) => !den.contains(country), + admin::AcceptedCountries::AllAccepted => true, + }) + }) + })) +} + +fn filter_payment_currency_based( + payment_intent: &storage::PaymentIntent, + pm: &RequestPaymentMethodTypes, ) -> bool { - match pm_type { - api_enums::PaymentMethodType::Credit | api_enums::PaymentMethodType::Debit => { - match (pm_card_networks, request_card_networks) { - (Some(pm_card_networks), Some(request_card_networks)) => request_card_networks - .iter() - .all(|card_network| pm_card_networks.contains(card_network)), - (None, Some(_)) => false, - _ => true, - } - } - _ => true, - } + payment_intent.currency.map_or(true, |currency| { + pm.accepted_currencies.as_ref().map_or(true, |ac| match ac { + admin::AcceptedCurrencies::EnableOnly(acc) => acc.contains(¤cy), + admin::AcceptedCurrencies::DisableOnly(den) => !den.contains(¤cy), + admin::AcceptedCurrencies::AllAccepted => true, + }) + }) +} + +fn filter_payment_amount_based( + payment_intent: &storage::PaymentIntent, + pm: &RequestPaymentMethodTypes, +) -> bool { + let amount = payment_intent.amount.get_amount_as_i64(); + (pm.maximum_amount.map_or(true, |amt| amount <= amt.into()) + && pm.minimum_amount.map_or(true, |amt| amount >= amt.into())) + || payment_intent.amount.get_amount_as_i64() == 0 +} + +async fn filter_payment_mandate_based( + payment_attempt: Option<&storage::PaymentAttempt>, + pm: &RequestPaymentMethodTypes, +) -> errors::CustomResult { + let recurring_filter = if !pm.recurring_enabled { + payment_attempt.map_or(true, |pa| pa.mandate_id.is_none()) + } else { + true + }; + Ok(recurring_filter) } pub async fn do_list_customer_pm_fetch_customer_if_not_passed( - state: routes::AppState, + state: routes::SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: Option, @@ -3139,7 +3369,7 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( } pub async fn list_customer_payment_method( - state: &routes::AppState, + state: &routes::SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, payment_intent: Option, @@ -3454,7 +3684,7 @@ pub async fn list_customer_payment_method( Ok(services::ApplicationResponse::Json(response)) } pub async fn get_mca_status( - state: &routes::AppState, + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, merchant_id: &str, connector_mandate_details: Option, @@ -3514,7 +3744,7 @@ where pub async fn get_card_details_with_locker_fallback( pm: &payment_method::PaymentMethod, key: &[u8], - state: &routes::AppState, + state: &routes::SessionState, ) -> errors::RouterResult> { let card_decrypted = decrypt::(pm.payment_method_data.clone(), key) @@ -3545,7 +3775,7 @@ pub async fn get_card_details_with_locker_fallback( pub async fn get_card_details_without_locker_fallback( pm: &payment_method::PaymentMethod, key: &[u8], - state: &routes::AppState, + state: &routes::SessionState, ) -> errors::RouterResult { let card_decrypted = decrypt::(pm.payment_method_data.clone(), key) @@ -3570,7 +3800,7 @@ pub async fn get_card_details_without_locker_fallback( } pub async fn get_card_details_from_locker( - state: &routes::AppState, + state: &routes::SessionState, pm: &storage::PaymentMethod, ) -> errors::RouterResult { let card = get_card_from_locker( @@ -3589,7 +3819,7 @@ pub async fn get_card_details_from_locker( } pub async fn get_lookup_key_from_locker( - state: &routes::AppState, + state: &routes::SessionState, payment_token: &str, pm: &storage::PaymentMethod, merchant_key_store: &domain::MerchantKeyStore, @@ -3774,7 +4004,7 @@ pub async fn set_default_payment_method( pub async fn update_last_used_at( pm_id: &str, - state: &routes::AppState, + state: &routes::SessionState, storage_scheme: MerchantStorageScheme, ) -> errors::RouterResult<()> { let update_last_used = storage::PaymentMethodUpdate::LastUsedUpdate { @@ -3796,7 +4026,7 @@ pub async fn update_last_used_at( } #[cfg(feature = "payouts")] pub async fn get_bank_from_hs_locker( - state: &routes::AppState, + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, temp_token: &str, customer_id: &id_type::CustomerId, @@ -3849,7 +4079,7 @@ pub struct TempLockerCardSupport; impl TempLockerCardSupport { #[instrument(skip_all)] async fn create_payment_method_data_in_temp_locker( - state: &routes::AppState, + state: &routes::SessionState, payment_token: &str, card: api::CardDetailFromLocker, pm: &storage::PaymentMethod, @@ -3931,7 +4161,7 @@ impl TempLockerCardSupport { #[instrument(skip_all)] pub async fn retrieve_payment_method( - state: routes::AppState, + state: routes::SessionState, pm: api::PaymentMethodId, key_store: domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, @@ -3987,7 +4217,7 @@ pub async fn retrieve_payment_method( #[instrument(skip_all)] pub async fn delete_payment_method( - state: routes::AppState, + state: routes::SessionState, merchant_account: domain::MerchantAccount, pm_id: api::PaymentMethodId, key_store: domain::MerchantKeyStore, @@ -4112,7 +4342,7 @@ where } pub async fn list_countries_currencies_for_connector_payment_method( - state: routes::AppState, + state: routes::SessionState, req: ListCountriesCurrenciesRequest, ) -> errors::RouterResponse { Ok(services::ApplicationResponse::Json( diff --git a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs index 76881950e3bf..14beb6be1310 100644 --- a/crates/router/src/core/payment_methods/surcharge_decision_configs.rs +++ b/crates/router/src/core/payment_methods/surcharge_decision_configs.rs @@ -26,7 +26,7 @@ use crate::{ storage::{self, payment_attempt::PaymentAttemptExt}, transformers::ForeignTryFrom, }, - AppState, + SessionState, }; static CONF_CACHE: StaticCache = StaticCache::new(); @@ -95,7 +95,7 @@ impl SurchargeSource { } pub async fn perform_surcharge_decision_management_for_payment_method_list( - state: &AppState, + state: &SessionState, algorithm_ref: routing::RoutingAlgorithmRef, payment_attempt: &storage::PaymentAttempt, payment_intent: &storage::PaymentIntent, @@ -215,7 +215,7 @@ pub async fn perform_surcharge_decision_management_for_payment_method_list( } pub async fn perform_surcharge_decision_management_for_session_flow( - state: &AppState, + state: &SessionState, algorithm_ref: routing::RoutingAlgorithmRef, payment_data: &mut PaymentData, payment_method_type_list: &Vec, @@ -276,7 +276,7 @@ where Ok(surcharge_metadata) } pub async fn perform_surcharge_decision_management_for_saved_cards( - state: &AppState, + state: &SessionState, algorithm_ref: routing::RoutingAlgorithmRef, payment_attempt: &storage::PaymentAttempt, payment_intent: &storage::PaymentIntent, diff --git a/crates/router/src/core/payment_methods/utils.rs b/crates/router/src/core/payment_methods/utils.rs deleted file mode 100644 index fc7416bf7edf..000000000000 --- a/crates/router/src/core/payment_methods/utils.rs +++ /dev/null @@ -1,828 +0,0 @@ -use std::{str::FromStr, sync::Arc}; - -use api_models::{ - admin::{self, PaymentMethodsEnabled}, - enums as api_enums, - payment_methods::RequestPaymentMethodTypes, -}; -use common_enums::enums; -use euclid::frontend::dir; -use hyperswitch_constraint_graph as cgraph; -use kgraph_utils::{error::KgraphError, transformers::IntoDirValue}; -use storage_impl::redis::cache::PM_FILTERS_CGRAPH_CACHE; - -use crate::configs::settings; - -pub fn make_pm_graph( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - payment_methods: &[serde_json::value::Value], - connector: String, - pm_config_mapping: &settings::ConnectorFilters, - supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, - supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, -) -> Result<(), KgraphError> { - for payment_method in payment_methods.iter() { - let pm_enabled = serde_json::from_value::(payment_method.clone()); - if let Ok(payment_methods_enabled) = pm_enabled { - compile_pm_graph( - builder, - payment_methods_enabled.clone(), - connector.clone(), - pm_config_mapping, - supported_payment_methods_for_mandate, - supported_payment_methods_for_update_mandate, - )?; - }; - } - Ok(()) -} - -pub async fn get_merchant_pm_filter_graph<'a>( - key: &str, -) -> Option>> { - PM_FILTERS_CGRAPH_CACHE - .get_val::>>(key) - .await -} - -pub async fn refresh_pm_filters_cache( - key: &str, - graph: cgraph::ConstraintGraph<'static, dir::DirValue>, -) -> Arc> { - let pm_filter_graph = Arc::new(graph); - PM_FILTERS_CGRAPH_CACHE - .push(key.to_string(), pm_filter_graph.clone()) - .await; - pm_filter_graph -} - -fn compile_pm_graph( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - pm_enabled: PaymentMethodsEnabled, - connector: String, - config: &settings::ConnectorFilters, - supported_payment_methods_for_mandate: &settings::SupportedPaymentMethodsForMandate, - supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, -) -> Result<(), KgraphError> { - if let Some(payment_method_types) = pm_enabled.payment_method_types { - for pmt in payment_method_types { - let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = - Vec::new(); - let mut agg_or_nodes_for_mandate_filters: Vec<( - cgraph::NodeId, - cgraph::Relation, - cgraph::Strength, - )> = Vec::new(); - - // Connector supported for Update mandate filter - let res = construct_supported_connectors_for_update_mandate_node( - builder, - supported_payment_methods_for_update_mandate, - pmt.clone(), - &pm_enabled.payment_method, - ); - if let Ok(Some(connector_eligible_for_update_mandates_node)) = res { - agg_or_nodes_for_mandate_filters.push(( - connector_eligible_for_update_mandates_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )) - } - - // Connector supported for mandates filter - if let Some(supported_pm_for_mandates) = supported_payment_methods_for_mandate - .0 - .get(&pm_enabled.payment_method) - { - if let Some(supported_connector_for_mandates) = - supported_pm_for_mandates.0.get(&pmt.payment_method_type) - { - let supported_connectors: Vec = - supported_connector_for_mandates - .connector_list - .clone() - .into_iter() - .collect(); - if let Ok(Some(connector_eligible_for_mandates_node)) = - construct_supported_connectors_for_mandate_node( - builder, - supported_connectors, - ) - { - agg_or_nodes_for_mandate_filters.push(( - connector_eligible_for_mandates_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )) - } - } - } - - // Non Prominent Mandate flows - let payment_type_non_mandate_value_node = builder.make_value_node( - cgraph::NodeValue::Value(dir::DirValue::PaymentType( - euclid::enums::PaymentType::NonMandate, - )), - None, - None::<()>, - ); - let payment_type_setup_mandate_value_node = builder.make_value_node( - cgraph::NodeValue::Value(dir::DirValue::PaymentType( - euclid::enums::PaymentType::SetupMandate, - )), - None, - None::<()>, - ); - - let non_major_mandate_any_node = builder - .make_any_aggregator( - &[ - ( - payment_type_non_mandate_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - payment_type_setup_mandate_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - - agg_or_nodes_for_mandate_filters.push(( - non_major_mandate_any_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )); - - let agg_or_node = builder - .make_any_aggregator(&agg_or_nodes_for_mandate_filters, None, None::<()>, None) - .map_err(KgraphError::GraphConstructionError)?; - - agg_nodes.push(( - agg_or_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )); - - // Capture Method filter - config - .0 - .get(connector.as_str()) - .or_else(|| config.0.get("default")) - .map(|inner| { - if let Ok(Some(capture_method_filter)) = - construct_capture_method_node(builder, inner, &pmt.payment_method_type) - { - agg_nodes.push(( - capture_method_filter, - cgraph::Relation::Negative, - cgraph::Strength::Strong, - )) - } - }); - - // Country filter - if let Some(pm_object_countries) = pmt.accepted_countries { - if let Ok(Some(country_node)) = compile_accepted_countries_for_mca( - builder, - &pmt.payment_method_type, - pm_object_countries, - config, - connector.clone(), - ) { - agg_nodes.push(( - country_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )) - } - } - - // Currency filter - if let Some(pm_object_currencies) = pmt.accepted_currencies { - if let Ok(Some(currency_node)) = compile_accepted_currency_for_mca( - builder, - &pmt.payment_method_type, - pm_object_currencies, - config, - connector.clone(), - ) { - agg_nodes.push(( - currency_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )) - } - } - - let and_node_for_all_the_filters = builder - .make_all_aggregator(&agg_nodes, None, None::<()>, None) - .map_err(KgraphError::GraphConstructionError)?; - - // Making our output node - let pmt_info = "PaymentMethodType"; - let dir_node: cgraph::NodeValue = - (pmt.payment_method_type, pm_enabled.payment_method) - .into_dir_value() - .map(Into::into)?; - let payment_method_type_value_node = - builder.make_value_node(dir_node, Some(pmt_info), None::<()>); - - builder - .make_edge( - and_node_for_all_the_filters, - payment_method_type_value_node, - cgraph::Strength::Strong, - cgraph::Relation::Positive, - None::, - ) - .map_err(KgraphError::GraphConstructionError)?; - } - } - Ok(()) -} - -fn construct_capture_method_node( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - payment_method_filters: &settings::PaymentMethodFilters, - payment_method_type: &api_enums::PaymentMethodType, -) -> Result, KgraphError> { - if !payment_method_filters - .0 - .get(&settings::PaymentMethodFilterKey::PaymentMethodType( - *payment_method_type, - )) - .and_then(|v| v.not_available_flows) - .and_then(|v| v.capture_method) - .map(|v| !matches!(v, api_enums::CaptureMethod::Manual)) - .unwrap_or(true) - { - return Ok(Some(builder.make_value_node( - cgraph::NodeValue::Value(dir::DirValue::CaptureMethod( - common_enums::CaptureMethod::Manual, - )), - None, - None::<()>, - ))); - } - Ok(None) -} - -fn construct_supported_connectors_for_update_mandate_node( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - supported_payment_methods_for_update_mandate: &settings::SupportedPaymentMethodsForMandate, - pmt: RequestPaymentMethodTypes, - payment_method: &enums::PaymentMethod, -) -> Result, KgraphError> { - let card_value_node = builder.make_value_node( - cgraph::NodeValue::Value(dir::DirValue::PaymentMethod(enums::PaymentMethod::Card)), - None, - None::<()>, - ); - - let payment_type_value_node = builder.make_value_node( - cgraph::NodeValue::Value(dir::DirValue::PaymentType( - euclid::enums::PaymentType::UpdateMandate, - )), - None, - None::<()>, - ); - - let mut agg_nodes: Vec<(cgraph::NodeId, cgraph::Relation, cgraph::Strength)> = Vec::new(); - let mut card_dir_values = Vec::new(); - let mut non_card_dir_values = Vec::new(); - - if let Some(supported_pm_for_mandates) = supported_payment_methods_for_update_mandate - .0 - .get(payment_method) - { - if payment_method == &enums::PaymentMethod::Card { - if let Some(credit_connector_list) = supported_pm_for_mandates - .0 - .get(&api_enums::PaymentMethodType::Credit) - { - card_dir_values.extend( - credit_connector_list - .connector_list - .clone() - .into_iter() - .filter_map(|connector| { - api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) - .ok() - .map(|connector| { - dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, - )) - }) - }), - ); - } - - if let Some(debit_connector_list) = supported_pm_for_mandates - .0 - .get(&api_enums::PaymentMethodType::Debit) - { - card_dir_values.extend( - debit_connector_list - .connector_list - .clone() - .into_iter() - .filter_map(|connector| { - api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) - .ok() - .map(|connector| { - dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, - )) - }) - }), - ); - } - let card_in_node = builder - .make_in_aggregator(card_dir_values, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - - let card_and_node = builder - .make_all_aggregator( - &[ - ( - card_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - payment_type_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - card_in_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - - agg_nodes.push(( - card_and_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )); - } else if let Some(connector_list) = - supported_pm_for_mandates.0.get(&pmt.payment_method_type) - { - non_card_dir_values.extend( - connector_list - .connector_list - .clone() - .into_iter() - .filter_map(|connector| { - api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) - .ok() - .map(|connector| { - dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, - )) - }) - }), - ); - let non_card_mandate_in_node = builder - .make_in_aggregator(non_card_dir_values, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - - let non_card_and_node = builder - .make_all_aggregator( - &[ - ( - card_value_node, - cgraph::Relation::Negative, - cgraph::Strength::Strong, - ), - ( - payment_type_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - non_card_mandate_in_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - - agg_nodes.push(( - non_card_and_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - )); - } - } - - Ok(Some( - builder - .make_any_aggregator( - &agg_nodes, - Some("any node for card and non card pm"), - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?, - )) -} - -fn construct_supported_connectors_for_mandate_node( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - eligible_connectors: Vec, -) -> Result, KgraphError> { - let payment_type_value_node = builder.make_value_node( - cgraph::NodeValue::Value(dir::DirValue::PaymentType( - euclid::enums::PaymentType::NewMandate, - )), - None, - None::<()>, - ); - let connectors_from_config: Vec = eligible_connectors - .into_iter() - .filter_map(|connector| { - match api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) { - Ok(connector) => Some(dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, - ))), - Err(_) => None, - } - }) - .collect(); - - if connectors_from_config.is_empty() { - Ok(None) - } else { - let connector_in_aggregator = builder - .make_in_aggregator(connectors_from_config, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - Ok(Some( - builder - .make_all_aggregator( - &[ - ( - payment_type_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - connector_in_aggregator, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?, - )) - } -} - -// fn construct_card_network_nodes( -// builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, -// mca_card_networks: Vec, -// ) -> Result, KgraphError> { -// Ok(Some( -// builder -// .make_in_aggregator( -// mca_card_networks -// .into_iter() -// .map(dir::DirValue::CardNetwork) -// .collect(), -// None, -// None::<()>, -// ) -// .map_err(KgraphError::GraphConstructionError)?, -// )) -// } - -fn compile_accepted_countries_for_mca( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - payment_method_type: &enums::PaymentMethodType, - pm_obj_countries: admin::AcceptedCountries, - config: &settings::ConnectorFilters, - connector: String, -) -> Result, KgraphError> { - match pm_obj_countries { - admin::AcceptedCountries::EnableOnly(countries) => { - // Country from the MCA - let pm_object_country_value_node = builder - .make_in_aggregator( - countries - .into_iter() - .map(|country| { - dir::DirValue::BillingCountry(common_enums::Country::from_alpha2( - country, - )) - }) - .collect(), - None, - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; - if let Some(config) = config - .0 - .get(connector.as_str()) - .or_else(|| config.0.get("default")) - { - if let Some(value) = - config - .0 - .get(&settings::PaymentMethodFilterKey::PaymentMethodType( - *payment_method_type, - )) - { - // country from config - if let Some(config_countries) = value.country.as_ref() { - let config_countries: Vec = - Vec::from_iter(config_countries) - .into_iter() - .map(|country| common_enums::Country::from_alpha2(*country)) - .collect(); - let dir_countries: Vec = config_countries - .into_iter() - .map(dir::DirValue::BillingCountry) - .collect(); - - let config_country_agg_node = builder - .make_in_aggregator(dir_countries, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - - let node = builder - .make_all_aggregator( - &[ - ( - pm_object_country_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - config_country_agg_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - return Ok(Some(node)); - } - } - } - } - admin::AcceptedCountries::DisableOnly(countries) => { - if let Some(config) = config - .0 - .get(connector.as_str()) - .or_else(|| config.0.get("default")) - { - // Country from the MCA - let pm_object_country_value_node = builder - .make_in_aggregator( - countries - .into_iter() - .map(|country| { - dir::DirValue::BillingCountry(common_enums::Country::from_alpha2( - country, - )) - }) - .collect(), - None, - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; - - if let Some(value) = - config - .0 - .get(&settings::PaymentMethodFilterKey::PaymentMethodType( - *payment_method_type, - )) - { - // country from config - if let Some(config_countries) = value.country.as_ref() { - let config_countries: Vec = - Vec::from_iter(config_countries) - .into_iter() - .map(|country| common_enums::Country::from_alpha2(*country)) - .collect(); - let dir_countries: Vec = config_countries - .into_iter() - .map(dir::DirValue::BillingCountry) - .collect(); - - let config_country_agg_node = builder - .make_in_aggregator(dir_countries, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - - let node = builder - .make_all_aggregator( - &[ - ( - pm_object_country_value_node, - cgraph::Relation::Negative, - cgraph::Strength::Strong, - ), - ( - config_country_agg_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - return Ok(Some(node)); - } - } - } - } - admin::AcceptedCountries::AllAccepted => return Ok(None), - } - Ok(None) -} - -fn compile_accepted_currency_for_mca( - builder: &mut cgraph::ConstraintGraphBuilder<'_, dir::DirValue>, - payment_method_type: &enums::PaymentMethodType, - pm_obj_currency: admin::AcceptedCurrencies, - config: &settings::ConnectorFilters, - connector: String, -) -> Result, KgraphError> { - match pm_obj_currency { - admin::AcceptedCurrencies::EnableOnly(currency) => { - // Currency from the MCA - let pm_object_currency_value_node = builder - .make_in_aggregator( - currency - .into_iter() - .map(dir::DirValue::PaymentCurrency) - .collect(), - None, - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; - - if let Some(config) = config - .0 - .get(connector.as_str()) - .or_else(|| config.0.get("default")) - { - if let Some(value) = - config - .0 - .get(&settings::PaymentMethodFilterKey::PaymentMethodType( - *payment_method_type, - )) - { - // Currency from config - if let Some(config_currencies) = value.currency.as_ref() { - let config_currency: Vec = - Vec::from_iter(config_currencies) - .into_iter() - .cloned() - .collect(); - - let dir_currencies: Vec = config_currency - .into_iter() - .map(dir::DirValue::PaymentCurrency) - .collect(); - - let config_currency_agg_node = builder - .make_in_aggregator(dir_currencies, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - - let node = builder - .make_all_aggregator( - &[ - ( - pm_object_currency_value_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ( - config_currency_agg_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - return Ok(Some(node)); - } - } - } - } - admin::AcceptedCurrencies::DisableOnly(currency) => { - // Currency from the MCA - let pm_object_currency_value_node = builder - .make_in_aggregator( - currency - .into_iter() - .map(dir::DirValue::PaymentCurrency) - .collect(), - None, - None::<()>, - ) - .map_err(KgraphError::GraphConstructionError)?; - - if let Some(config) = config - .0 - .get(connector.as_str()) - .or_else(|| config.0.get("default")) - { - if let Some(value) = - config - .0 - .get(&settings::PaymentMethodFilterKey::PaymentMethodType( - *payment_method_type, - )) - { - // Currency from config - if let Some(config_currencies) = value.currency.as_ref() { - let config_currency: Vec = - Vec::from_iter(config_currencies) - .into_iter() - .cloned() - .collect(); - - let dir_currencies: Vec = config_currency - .into_iter() - .map(dir::DirValue::PaymentCurrency) - .collect(); - - let config_currency_agg_node = builder - .make_in_aggregator(dir_currencies, None, None::<()>) - .map_err(KgraphError::GraphConstructionError)?; - - let node = builder - .make_all_aggregator( - &[ - ( - pm_object_currency_value_node, - cgraph::Relation::Negative, - cgraph::Strength::Strong, - ), - ( - config_currency_agg_node, - cgraph::Relation::Positive, - cgraph::Strength::Strong, - ), - ], - None, - None::<()>, - None, - ) - .map_err(KgraphError::GraphConstructionError)?; - return Ok(Some(node)); - } - } - } - } - admin::AcceptedCurrencies::AllAccepted => return Ok(None), - } - Ok(None) -} diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index e0d8bada65ef..9e20017d57fb 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -818,7 +818,7 @@ pub struct Vault; impl Vault { #[instrument(skip_all)] pub async fn get_payment_method_data_from_locker( - state: &routes::AppState, + state: &routes::SessionState, lookup_key: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, SupplementaryVaultData)> { @@ -834,7 +834,7 @@ impl Vault { #[instrument(skip_all)] pub async fn store_payment_method_data_in_locker( - state: &routes::AppState, + state: &routes::SessionState, token_id: Option, payment_method: &api::PaymentMethodData, customer_id: Option, @@ -869,7 +869,7 @@ impl Vault { #[cfg(feature = "payouts")] #[instrument(skip_all)] pub async fn get_payout_method_data_from_temporary_locker( - state: &routes::AppState, + state: &routes::SessionState, lookup_key: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> RouterResult<(Option, SupplementaryVaultData)> { @@ -886,7 +886,7 @@ impl Vault { #[cfg(feature = "payouts")] #[instrument(skip_all)] pub async fn store_payout_method_data_in_locker( - state: &routes::AppState, + state: &routes::SessionState, token_id: Option, payout_method: &api::PayoutMethodData, customer_id: Option, @@ -920,7 +920,7 @@ impl Vault { #[instrument(skip_all)] pub async fn delete_locker_payment_method_by_lookup_key( - state: &routes::AppState, + state: &routes::SessionState, lookup_key: &Option, ) { if let Some(lookup_key) = lookup_key { @@ -942,7 +942,7 @@ fn get_redis_locker_key(lookup_key: &str) -> String { #[instrument(skip(state, value1, value2))] pub async fn create_tokenize( - state: &routes::AppState, + state: &routes::SessionState, value1: String, value2: Option, lookup_key: String, @@ -1007,7 +1007,7 @@ pub async fn create_tokenize( #[instrument(skip(state))] pub async fn get_tokenized_data( - state: &routes::AppState, + state: &routes::SessionState, lookup_key: &str, _should_get_value2: bool, encryption_key: &masking::Secret>, @@ -1069,7 +1069,10 @@ pub async fn get_tokenized_data( } #[instrument(skip(state))] -pub async fn delete_tokenized_data(state: &routes::AppState, lookup_key: &str) -> RouterResult<()> { +pub async fn delete_tokenized_data( + state: &routes::SessionState, + lookup_key: &str, +) -> RouterResult<()> { let redis_key = get_redis_locker_key(lookup_key); let func = || async { metrics::DELETED_TOKENIZED_CARD.add(&metrics::CONTEXT, 1, &[]); @@ -1153,7 +1156,7 @@ pub async fn add_delete_tokenized_data_task( } pub async fn start_tokenize_data_workflow( - state: &routes::AppState, + state: &routes::SessionState, tokenize_tracker: &storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { let db = &*state.store; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 7bc0b532239e..caaecdc8629c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -77,7 +77,7 @@ use crate::{ }, db::StorageInterface, logger, - routes::{app::ReqState, metrics, payment_methods::ParentPaymentMethodToken, AppState}, + routes::{app::ReqState, metrics, payment_methods::ParentPaymentMethodToken, SessionState}, services::{self, api::Authenticate}, types::{ self as router_types, @@ -97,7 +97,7 @@ use crate::{ #[allow(clippy::too_many_arguments, clippy::type_complexity)] #[instrument(skip_all, fields(payment_id, merchant_id))] pub async fn payments_operation_core( - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, @@ -571,7 +571,7 @@ where #[instrument(skip_all)] pub async fn call_decision_manager( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, ) -> RouterResult<()> @@ -606,7 +606,7 @@ where #[instrument(skip_all)] async fn populate_surcharge_details( - state: &AppState, + state: &SessionState, payment_data: &mut PaymentData, ) -> RouterResult<()> where @@ -686,7 +686,7 @@ pub fn get_connector_data( #[instrument(skip_all)] pub async fn call_surcharge_decision_management_for_session_flow( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, session_connector_data: &[api::SessionConnectorData], @@ -741,7 +741,7 @@ where } #[allow(clippy::too_many_arguments)] pub async fn payments_core( - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, @@ -794,7 +794,7 @@ where payment_data, customer, auth_flow, - &state.conf.server, + &state.base_url, operation, &state.conf.connector_request_reference_id_config, connector_http_status_code, @@ -826,7 +826,7 @@ pub trait PaymentRedirectFlow: Sync { #[allow(clippy::too_many_arguments)] async fn call_payment_flow( &self, - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, merchant_key_store: domain::MerchantKeyStore, @@ -848,7 +848,7 @@ pub trait PaymentRedirectFlow: Sync { #[allow(clippy::too_many_arguments)] async fn handle_payments_redirect_response( &self, - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, @@ -923,7 +923,7 @@ impl PaymentRedirectFlow for PaymentRedirectCompleteAuthorize { #[allow(clippy::too_many_arguments)] async fn call_payment_flow( &self, - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, merchant_key_store: domain::MerchantKeyStore, @@ -1055,7 +1055,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync { #[allow(clippy::too_many_arguments)] async fn call_payment_flow( &self, - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, merchant_key_store: domain::MerchantKeyStore, @@ -1146,7 +1146,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { #[allow(clippy::too_many_arguments)] async fn call_payment_flow( &self, - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, merchant_key_store: domain::MerchantKeyStore, @@ -1378,7 +1378,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub async fn call_connector_service( - state: &AppState, + state: &SessionState, req_state: ReqState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -1613,7 +1613,7 @@ where } async fn blocklist_guard( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, operation: &BoxedOperation<'_, F, ApiRequest>, payment_data: &mut PaymentData, @@ -1652,7 +1652,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn call_multiple_connectors_service( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connectors: Vec, @@ -1769,7 +1769,7 @@ where } pub async fn call_create_connector_customer_if_required( - state: &AppState, + state: &SessionState, customer: &Option, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -1871,7 +1871,7 @@ where } async fn complete_preprocessing_steps_if_required( - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, payment_data: &PaymentData, mut router_data: RouterData, @@ -1885,6 +1885,14 @@ where dyn api::Connector: services::api::ConnectorIntegration, { + if !is_operation_complete_authorize(&operation) + && connector + .connector_name + .is_pre_processing_required_before_authorize() + { + router_data = router_data.preprocessing_steps(state, connector).await?; + return Ok((router_data, should_continue_payment)); + } //TODO: For ACH transfers, if preprocessing_step is not required for connectors encountered in future, add the check let router_data_and_should_continue_payment = match payment_data.payment_method_data.clone() { Some(api_models::payments::PaymentMethodData::BankTransfer(data)) => match data.deref() { @@ -2003,7 +2011,7 @@ pub fn is_preprocessing_required_for_wallets(connector_name: String) -> bool { #[instrument(skip_all)] pub async fn construct_profile_id_and_get_mca<'a, F>( - state: &'a AppState, + state: &'a SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, connector_name: &str, @@ -2041,7 +2049,7 @@ where } fn is_payment_method_tokenization_enabled_for_connector( - state: &AppState, + state: &SessionState, connector_name: &str, payment_method: &storage::enums::PaymentMethod, payment_method_type: &Option, @@ -2160,7 +2168,7 @@ fn is_payment_method_type_allowed_for_connector( } async fn decide_payment_method_tokenize_action( - state: &AppState, + state: &SessionState, connector_name: &str, payment_method: &storage::enums::PaymentMethod, pm_parent_token: Option<&String>, @@ -2233,7 +2241,7 @@ pub enum TokenizationAction { #[allow(clippy::too_many_arguments)] pub async fn get_connector_tokenization_action_when_confirm_true( - state: &AppState, + state: &SessionState, operation: &BoxedOperation<'_, F, Req>, payment_data: &mut PaymentData, validate_result: &operations::ValidateResult<'_>, @@ -2357,7 +2365,7 @@ where } pub async fn tokenize_in_router_when_confirm_false_or_external_authentication( - state: &AppState, + state: &SessionState, operation: &BoxedOperation<'_, F, Req>, payment_data: &mut PaymentData, validate_result: &operations::ValidateResult<'_>, @@ -2608,7 +2616,7 @@ pub fn is_operation_complete_authorize(operation: &Op) -> bool { #[cfg(feature = "olap")] pub async fn list_payments( - state: AppState, + state: SessionState, merchant: domain::MerchantAccount, constraints: api::PaymentListConstraints, ) -> RouterResponse { @@ -2674,7 +2682,7 @@ pub async fn list_payments( } #[cfg(feature = "olap")] pub async fn apply_filters_on_payments( - state: AppState, + state: SessionState, merchant: domain::MerchantAccount, constraints: api::PaymentListFilterConstraints, ) -> RouterResponse { @@ -2727,7 +2735,7 @@ pub async fn apply_filters_on_payments( #[cfg(feature = "olap")] pub async fn get_filters_for_payments( - state: AppState, + state: SessionState, merchant: domain::MerchantAccount, time_range: api::TimeRange, ) -> RouterResponse { @@ -2765,7 +2773,7 @@ pub async fn get_filters_for_payments( #[cfg(feature = "olap")] pub async fn get_payment_filters( - state: AppState, + state: SessionState, merchant: domain::MerchantAccount, ) -> RouterResponse { let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) = @@ -2923,7 +2931,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn get_connector_choice( operation: &BoxedOperation<'_, F, Req>, - state: &AppState, + state: &SessionState, req: &Req, merchant_account: &domain::MerchantAccount, business_profile: &storage::business_profile::BusinessProfile, @@ -3002,7 +3010,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn connector_selection( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, business_profile: &storage::business_profile::BusinessProfile, key_store: &domain::MerchantKeyStore, @@ -3077,7 +3085,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn decide_connector( - state: AppState, + state: SessionState, merchant_account: &domain::MerchantAccount, business_profile: &storage::business_profile::BusinessProfile, key_store: &domain::MerchantKeyStore, @@ -3324,7 +3332,7 @@ where } pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( - state: &AppState, + state: &SessionState, payment_data: &mut PaymentData, routing_data: &mut storage::RoutingData, connectors: Vec, @@ -3503,7 +3511,7 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, connector: enums::Connector, payment_method_info: &storage::PaymentMethod, @@ -3532,7 +3540,7 @@ pub fn should_add_task_to_process_tracker(payment_data: &PaymentData( - state: AppState, + state: SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_data: &mut PaymentData, @@ -3644,7 +3652,7 @@ where #[allow(clippy::too_many_arguments)] pub async fn route_connector_v1( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, business_profile: &storage::business_profile::BusinessProfile, key_store: &domain::MerchantKeyStore, @@ -3767,7 +3775,7 @@ where #[instrument(skip_all)] pub async fn payment_external_authentication( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: api_models::payments::PaymentsExternalAuthenticationRequest, @@ -3900,15 +3908,12 @@ pub async fn payment_external_authentication( .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("missing connector in payment_attempt")?; let return_url = Some(helpers::create_authorize_url( - &state.conf.server.base_url, + &state.base_url, &payment_attempt.clone(), payment_connector_name, )); - let webhook_url = helpers::create_webhook_url( - &state.conf.server.base_url, - merchant_id, - &authentication_connector, - ); + let webhook_url = + helpers::create_webhook_url(&state.base_url, merchant_id, &authentication_connector); let business_profile = state .store @@ -3918,8 +3923,23 @@ pub async fn payment_external_authentication( id: profile_id.to_string(), })?; + let authentication_details: api_models::admin::AuthenticationConnectorDetails = + business_profile + .authentication_connector_details + .clone() + .get_required_value("authentication_connector_details") + .attach_printable("authentication_connector_details not configured by the merchant")? + .parse_value("AuthenticationConnectorDetails") + .change_context(errors::ApiErrorResponse::UnprocessableEntity { + message: "Invalid data format found for authentication_connector_details".into(), + }) + .attach_printable( + "Error while parsing authentication_connector_details from business_profile", + )?; + let authentication_response = Box::pin(authentication_core::perform_authentication( &state, + business_profile.merchant_id, authentication_connector, payment_method_details.0, payment_method_details.1, @@ -3931,7 +3951,6 @@ pub async fn payment_external_authentication( })?, shipping_address.as_ref().map(|address| address.into()), browser_info, - business_profile, merchant_connector_account, Some(amount), Some(currency), @@ -3943,6 +3962,7 @@ pub async fn payment_external_authentication( req.threeds_method_comp_ind, optional_customer.and_then(|customer| customer.email.map(pii::Email::from)), webhook_url, + authentication_details.three_ds_requestor_url.clone(), )) .await?; Ok(services::ApplicationResponse::Json( @@ -3957,13 +3977,14 @@ pub async fn payment_external_authentication( acs_trans_id: authentication_response.acs_trans_id, three_dsserver_trans_id: authentication_response.three_dsserver_trans_id, acs_signed_content: authentication_response.acs_signed_content, + three_ds_requestor_url: authentication_details.three_ds_requestor_url, }, )) } #[instrument(skip_all)] pub async fn get_extended_card_info( - state: AppState, + state: SessionState, merchant_id: String, payment_id: String, ) -> RouterResponse { diff --git a/crates/router/src/core/payments/access_token.rs b/crates/router/src/core/payments/access_token.rs index 0c5aa13eb2f9..4241db6742da 100644 --- a/crates/router/src/core/payments/access_token.rs +++ b/crates/router/src/core/payments/access_token.rs @@ -9,7 +9,7 @@ use crate::{ errors::{self, RouterResult}, payments, }, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services::{self, logger}, types::{self, api as api_types, domain}, }; @@ -53,7 +53,7 @@ pub async fn add_access_token< Req: Debug + Clone + 'static, Res: Debug + Clone + 'static, >( - state: &AppState, + state: &SessionState, connector: &api_types::ConnectorData, merchant_account: &domain::MerchantAccount, router_data: &types::RouterData, @@ -194,7 +194,7 @@ pub async fn add_access_token< } pub async fn refresh_connector_auth( - state: &AppState, + state: &SessionState, connector: &api_types::ConnectorData, _merchant_account: &domain::MerchantAccount, router_data: &types::RouterData< diff --git a/crates/router/src/core/payments/conditional_configs.rs b/crates/router/src/core/payments/conditional_configs.rs index 5dc78d42e5ea..c73fa3659b70 100644 --- a/crates/router/src/core/payments/conditional_configs.rs +++ b/crates/router/src/core/payments/conditional_configs.rs @@ -21,7 +21,7 @@ pub type ConditionalConfigResult = errors::CustomResult; #[instrument(skip_all)] pub async fn perform_decision_management( - state: &routes::AppState, + state: &routes::SessionState, algorithm_ref: routing::RoutingAlgorithmRef, merchant_id: &str, payment_data: &mut payments::PaymentData, @@ -51,7 +51,7 @@ pub async fn perform_decision_management( #[instrument(skip_all)] pub async fn ensure_algorithm_cached( - state: &routes::AppState, + state: &routes::SessionState, merchant_id: &str, timestamp: i64, algorithm_id: &str, @@ -73,7 +73,7 @@ pub async fn ensure_algorithm_cached( #[instrument(skip_all)] pub async fn refresh_routing_cache( - state: &routes::AppState, + state: &routes::SessionState, key: String, algorithm_id: &str, timestamp: i64, diff --git a/crates/router/src/core/payments/customers.rs b/crates/router/src/core/payments/customers.rs index ff56eaf46ebf..448e5fedb8b1 100644 --- a/crates/router/src/core/payments/customers.rs +++ b/crates/router/src/core/payments/customers.rs @@ -6,14 +6,14 @@ use crate::{ payments, }, logger, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{self, api, domain, storage}, }; #[instrument(skip_all)] pub async fn create_connector_customer( - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, router_data: &types::RouterData, customer_request_data: types::ConnectorCustomerData, @@ -88,7 +88,7 @@ pub fn get_connector_customer_details_if_present<'a>( } pub fn should_call_connector_create_customer<'a>( - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, customer: &'a Option, connector_label: &str, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index bf924c25982f..674fa4539276 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -19,7 +19,7 @@ use crate::{ errors::{ConnectorError, CustomResult, RouterResult}, payments::{self, helpers}, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, storage}, }; @@ -28,7 +28,7 @@ use crate::{ pub trait ConstructFlowSpecificData { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -42,7 +42,7 @@ pub trait ConstructFlowSpecificData { pub trait Feature { async fn decide_flows<'a>( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -55,7 +55,7 @@ pub trait Feature { async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult @@ -66,7 +66,7 @@ pub trait Feature { async fn add_payment_method_token<'a>( &mut self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, _tokenization_action: &payments::TokenizationAction, ) -> RouterResult> @@ -80,7 +80,7 @@ pub trait Feature { async fn preprocessing_steps<'a>( self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, ) -> RouterResult where @@ -93,7 +93,7 @@ pub trait Feature { async fn create_connector_customer<'a>( &self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, ) -> RouterResult> where @@ -107,7 +107,7 @@ pub trait Feature { /// Returns the connector request and a bool which specifies whether to proceed with further async fn build_flow_specific_connector_request( &mut self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { @@ -928,7 +928,6 @@ impl default_imp_for_pre_processing_steps!( connector::Aci, - connector::Airwallex, connector::Authorizedotnet, connector::Bambora, connector::Billwerk, diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index 95ca0c3e31c7..6d4a88c0bf0e 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -6,7 +6,7 @@ use crate::{ errors::{ApiErrorResponse, NotImplementedMessage, RouterResult}, payments::{self, access_token, helpers, transformers, PaymentData}, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, storage}, }; @@ -18,7 +18,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -47,7 +47,7 @@ impl Feature { async fn decide_flows<'a>( self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, _connector_request: Option, @@ -61,7 +61,7 @@ impl Feature async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -70,7 +70,7 @@ impl Feature async fn build_flow_specific_connector_request( &mut self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 931ea26a9f8e..d672da99910d 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -11,7 +11,7 @@ use crate::{ }, }, logger, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{self, api, domain, storage}, }; @@ -26,7 +26,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -58,7 +58,7 @@ impl impl Feature for types::PaymentsAuthorizeRouterData { async fn decide_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -93,7 +93,7 @@ impl Feature for types::PaymentsAu async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -102,7 +102,7 @@ impl Feature for types::PaymentsAu async fn add_payment_method_token<'a>( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, tokenization_action: &payments::TokenizationAction, ) -> RouterResult> { @@ -119,7 +119,7 @@ impl Feature for types::PaymentsAu async fn preprocessing_steps<'a>( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, ) -> RouterResult { authorize_preprocessing_steps(state, &self, true, connector).await @@ -127,7 +127,7 @@ impl Feature for types::PaymentsAu async fn create_connector_customer<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, ) -> RouterResult> { customers::create_connector_customer( @@ -141,7 +141,7 @@ impl Feature for types::PaymentsAu async fn build_flow_specific_connector_request( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { @@ -266,7 +266,7 @@ impl mandate::MandateBehaviour for types::PaymentsAuthorizeData { } pub async fn authorize_preprocessing_steps( - state: &AppState, + state: &SessionState, router_data: &types::RouterData, confirm: bool, connector: &api::ConnectorData, @@ -322,13 +322,14 @@ pub async fn authorize_preprocessing_steps( ), ], ); - - let authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( + let mut authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( resp.clone(), router_data.request.to_owned(), resp.response, ); - + if connector.connector_name == api_models::enums::Connector::Airwallex { + authorize_router_data.reference_id = resp.reference_id; + } Ok(authorize_router_data) } else { Ok(router_data.clone()) diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index 0e90ab40b696..d7e8cc3d9b57 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -6,7 +6,7 @@ use crate::{ errors::{ConnectorErrorExt, RouterResult}, payments::{self, access_token, helpers, transformers, PaymentData}, }, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{self, api, domain, storage}, }; @@ -17,7 +17,7 @@ impl ConstructFlowSpecificData( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -46,7 +46,7 @@ impl Feature { async fn decide_flows<'a>( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -83,7 +83,7 @@ impl Feature async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -92,7 +92,7 @@ impl Feature async fn build_flow_specific_connector_request( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index b979eb2a337d..268281e51573 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -6,7 +6,7 @@ use crate::{ errors::{ConnectorErrorExt, RouterResult}, payments::{self, access_token, helpers, transformers, Feature, PaymentData}, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, storage}, }; @@ -18,7 +18,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -47,7 +47,7 @@ impl Feature { async fn decide_flows<'a>( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -75,7 +75,7 @@ impl Feature async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -84,7 +84,7 @@ impl Feature async fn build_flow_specific_connector_request( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { 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 c14cf6c80892..5d0cb9f9f381 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -6,7 +6,7 @@ use crate::{ errors::{ConnectorErrorExt, RouterResult}, payments::{self, access_token, helpers, transformers, PaymentData}, }, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{self, api, domain, storage}, }; @@ -21,7 +21,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -60,7 +60,7 @@ impl Feature { async fn decide_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -88,7 +88,7 @@ impl Feature async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -97,7 +97,7 @@ impl Feature async fn add_payment_method_token<'a>( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, _tokenization_action: &payments::TokenizationAction, ) -> RouterResult> { @@ -119,7 +119,7 @@ impl Feature async fn build_flow_specific_connector_request( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { @@ -144,7 +144,7 @@ impl Feature async fn preprocessing_steps<'a>( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, ) -> RouterResult { complete_authorize_preprocessing_steps(state, &self, true, connector).await @@ -152,7 +152,7 @@ impl Feature } pub async fn complete_authorize_preprocessing_steps( - state: &AppState, + state: &SessionState, router_data: &types::RouterData, confirm: bool, connector: &api::ConnectorData, diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 716228d73f7b..58e5db8fbdef 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -6,7 +6,7 @@ use crate::{ errors::{ConnectorErrorExt, RouterResult}, payments::{self, access_token, helpers, transformers, Feature, PaymentData}, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, storage}, }; @@ -21,7 +21,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -54,7 +54,7 @@ impl Feature( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -82,7 +82,7 @@ impl Feature( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -91,7 +91,7 @@ impl Feature RouterResult<(Option, bool)> { diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index 51627e2d68b7..bec29f97e93f 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -8,7 +8,7 @@ use crate::{ errors::{ApiErrorResponse, ConnectorErrorExt, RouterResult}, payments::{self, access_token, helpers, transformers, PaymentData}, }, - routes::AppState, + routes::SessionState, services::{self, logger}, types::{self, api, domain, storage}, }; @@ -19,7 +19,7 @@ impl ConstructFlowSpecificData( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -50,7 +50,7 @@ impl Feature { async fn decide_flows<'a>( mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -101,7 +101,7 @@ impl Feature async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -110,7 +110,7 @@ impl Feature async fn build_flow_specific_connector_request( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { @@ -152,7 +152,7 @@ where { async fn execute_connector_processing_step_for_each_capture( &self, - _state: &AppState, + _state: &SessionState, _pending_connector_capture_id_list: Vec, _call_connector_action: payments::CallConnectorAction, _connector_integration: services::BoxedConnectorIntegration< @@ -170,7 +170,7 @@ impl RouterDataPSync { async fn execute_connector_processing_step_for_each_capture( &self, - state: &AppState, + state: &SessionState, pending_connector_capture_id_list: Vec, call_connector_action: payments::CallConnectorAction, connector_integration: services::BoxedConnectorIntegration< diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs index 638efa054eb8..865c6ea201d0 100644 --- a/crates/router/src/core/payments/flows/reject_flow.rs +++ b/crates/router/src/core/payments/flows/reject_flow.rs @@ -6,7 +6,7 @@ use crate::{ errors::{ApiErrorResponse, NotImplementedMessage, RouterResult}, payments::{self, access_token, helpers, transformers, PaymentData}, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, storage}, }; @@ -17,7 +17,7 @@ impl ConstructFlowSpecificData( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -46,7 +46,7 @@ impl Feature { async fn decide_flows<'a>( self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, _connector_request: Option, @@ -60,7 +60,7 @@ impl Feature async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -69,7 +69,7 @@ impl Feature async fn build_flow_specific_connector_request( &mut self, - _state: &AppState, + _state: &SessionState, _connector: &api::ConnectorData, _call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index c74ed4bae153..76441f40759d 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -28,7 +28,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &routes::AppState, + state: &routes::SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -55,7 +55,7 @@ impl impl Feature for types::PaymentsSessionRouterData { async fn decide_flows<'a>( self, - state: &routes::AppState, + state: &routes::SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, _connector_request: Option, @@ -81,7 +81,7 @@ impl Feature for types::PaymentsSessio async fn add_access_token<'a>( &self, - state: &routes::AppState, + state: &routes::SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -123,7 +123,7 @@ fn is_dynamic_fields_required( } fn build_apple_pay_session_request( - state: &routes::AppState, + state: &routes::SessionState, request: payment_types::ApplepaySessionRequest, apple_pay_merchant_cert: masking::Secret, apple_pay_merchant_cert_key: masking::Secret, @@ -147,7 +147,7 @@ fn build_apple_pay_session_request( } async fn create_applepay_session_token( - state: &routes::AppState, + state: &routes::SessionState, router_data: &types::PaymentsSessionRouterData, connector: &api::ConnectorData, business_profile: &storage::business_profile::BusinessProfile, @@ -467,7 +467,7 @@ fn create_apple_pay_session_response( } fn create_gpay_session_token( - state: &routes::AppState, + state: &routes::SessionState, router_data: &types::PaymentsSessionRouterData, connector: &api::ConnectorData, business_profile: &storage::business_profile::BusinessProfile, @@ -598,7 +598,10 @@ fn create_gpay_session_token( } } -fn is_session_response_delayed(state: &routes::AppState, connector: &api::ConnectorData) -> bool { +fn is_session_response_delayed( + state: &routes::SessionState, + connector: &api::ConnectorData, +) -> bool { let connectors_with_delayed_response = &state .conf .delayed_session_response @@ -626,7 +629,7 @@ where { async fn decide_flow<'a, 'b>( &'b self, - state: &'a routes::AppState, + state: &'a routes::SessionState, connector: &api::ConnectorData, _confirm: Option, call_connector_action: payments::CallConnectorAction, @@ -635,7 +638,7 @@ where } fn create_paypal_sdk_session_token( - _state: &routes::AppState, + _state: &routes::SessionState, router_data: &types::PaymentsSessionRouterData, connector: &api::ConnectorData, _business_profile: &storage::business_profile::BusinessProfile, @@ -674,7 +677,7 @@ fn create_paypal_sdk_session_token( impl RouterDataSession for types::PaymentsSessionRouterData { async fn decide_flow<'a, 'b>( &'b self, - state: &'a routes::AppState, + state: &'a routes::SessionState, connector: &api::ConnectorData, _confirm: Option, call_connector_action: payments::CallConnectorAction, 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 b0fa39d5258d..987d3f33803a 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -9,7 +9,7 @@ use crate::{ self, access_token, customers, helpers, tokenization, transformers, PaymentData, }, }, - routes::AppState, + routes::SessionState, services, types::{self, api, domain, storage}, }; @@ -24,7 +24,7 @@ impl { async fn construct_router_data<'a>( &self, - state: &AppState, + state: &SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -51,7 +51,7 @@ impl impl Feature for types::SetupMandateRouterData { async fn decide_flows<'a>( self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, connector_request: Option, @@ -78,7 +78,7 @@ impl Feature for types::Setup async fn add_access_token<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, ) -> RouterResult { @@ -87,7 +87,7 @@ impl Feature for types::Setup async fn add_payment_method_token<'a>( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, tokenization_action: &payments::TokenizationAction, ) -> RouterResult> { @@ -104,7 +104,7 @@ impl Feature for types::Setup async fn create_connector_customer<'a>( &self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, ) -> RouterResult> { customers::create_connector_customer( @@ -118,7 +118,7 @@ impl Feature for types::Setup async fn build_flow_specific_connector_request( &mut self, - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, call_connector_action: payments::CallConnectorAction, ) -> RouterResult<(Option, bool)> { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 8f7ed4da2565..a58a6ae32b21 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -34,7 +34,7 @@ use super::{ CustomerDetails, PaymentData, }; use crate::{ - configs::settings::{ConnectorRequestReferenceIdConfig, Server, TempLockerEnableConfig}, + configs::settings::{ConnectorRequestReferenceIdConfig, TempLockerEnableConfig}, connector, consts::{self, BASE64_ENGINE}, core::{ @@ -46,7 +46,7 @@ use crate::{ pm_auth::retrieve_payment_method_from_auth_service, }, db::StorageInterface, - routes::{metrics, payment_methods as payment_methods_handler, AppState}, + routes::{metrics, payment_methods as payment_methods_handler, SessionState}, services, types::{ api::{self, admin, enums as api_enums, MandateValidationFieldsExt}, @@ -427,7 +427,7 @@ pub async fn get_address_by_id( } pub async fn get_token_pm_type_mandate_details( - state: &AppState, + state: &SessionState, request: &api::PaymentsRequest, mandate_type: Option, merchant_account: &domain::MerchantAccount, @@ -579,7 +579,7 @@ pub async fn get_token_pm_type_mandate_details( } pub async fn get_token_for_recurring_mandate( - state: &AppState, + state: &SessionState, req: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, @@ -1023,16 +1023,13 @@ pub fn validate_customer_id_mandatory_cases( } pub fn create_startpay_url( - server: &Server, + base_url: &str, payment_attempt: &PaymentAttempt, payment_intent: &PaymentIntent, ) -> String { format!( "{}/payments/redirect/{}/{}/{}", - server.base_url, - payment_intent.payment_id, - payment_intent.merchant_id, - payment_attempt.attempt_id + base_url, payment_intent.payment_id, payment_intent.merchant_id, payment_attempt.attempt_id ) } @@ -1050,7 +1047,7 @@ pub fn create_redirect_url( } pub fn create_authentication_url( - router_base_url: &String, + router_base_url: &str, payment_attempt: &PaymentAttempt, ) -> String { format!( @@ -1060,7 +1057,7 @@ pub fn create_authentication_url( } pub fn create_authorize_url( - router_base_url: &String, + router_base_url: &str, payment_attempt: &PaymentAttempt, connector_name: &String, ) -> String { @@ -1205,7 +1202,7 @@ pub fn payment_intent_status_fsm( } pub async fn add_domain_task_to_pt( operation: &Op, - state: &AppState, + state: &SessionState, payment_attempt: &PaymentAttempt, requeue: bool, schedule_time: Option, @@ -1494,7 +1491,7 @@ pub fn get_customer_details_from_request( } pub async fn get_connector_default( - _state: &AppState, + _state: &SessionState, request_connector: Option, ) -> CustomResult { Ok(request_connector.map_or( @@ -1676,7 +1673,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( } pub async fn retrieve_payment_method_with_temporary_token( - state: &AppState, + state: &SessionState, token: &str, payment_intent: &PaymentIntent, merchant_key_store: &domain::MerchantKeyStore, @@ -1773,7 +1770,7 @@ pub async fn retrieve_payment_method_with_temporary_token( } pub async fn retrieve_card_with_permanent_token( - state: &AppState, + state: &SessionState, locker_id: &str, payment_method_id: &str, payment_intent: &PaymentIntent, @@ -1838,7 +1835,7 @@ pub async fn retrieve_card_with_permanent_token( } pub async fn retrieve_payment_method_from_db_with_token_data( - state: &AppState, + state: &SessionState, token_data: &storage::PaymentTokenData, storage_scheme: storage::enums::MerchantStorageScheme, ) -> RouterResult> { @@ -1873,7 +1870,7 @@ pub async fn retrieve_payment_method_from_db_with_token_data( } pub async fn retrieve_payment_token_data( - state: &AppState, + state: &SessionState, token: String, payment_method: Option, ) -> RouterResult { @@ -1924,7 +1921,7 @@ pub async fn retrieve_payment_token_data( pub async fn make_pm_data<'a, F: Clone, R>( operation: BoxedOperation<'a, F, R>, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, @@ -2021,7 +2018,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( } pub async fn store_in_vault_and_generate_ppmt( - state: &AppState, + state: &SessionState, payment_method_data: &api_models::payments::PaymentMethodData, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, @@ -2057,7 +2054,7 @@ pub async fn store_in_vault_and_generate_ppmt( } pub async fn store_payment_method_data_in_vault( - state: &AppState, + state: &SessionState, payment_attempt: &PaymentAttempt, payment_intent: &PaymentIntent, payment_method: enums::PaymentMethod, @@ -2541,7 +2538,7 @@ pub fn make_merchant_url_with_response( } pub async fn make_ephemeral_key( - state: AppState, + state: SessionState, customer_id: id_type::CustomerId, merchant_id: String, ) -> errors::RouterResponse { @@ -2563,7 +2560,7 @@ pub async fn make_ephemeral_key( } pub async fn delete_ephemeral_key( - state: AppState, + state: SessionState, ek_id: String, ) -> errors::RouterResponse { let db = state.store.as_ref(); @@ -3207,7 +3204,7 @@ impl MerchantConnectorAccountType { /// If profile_id is passed use it, or use connector_label to query merchant connector account #[instrument(skip_all)] pub async fn get_merchant_connector_account( - state: &AppState, + state: &SessionState, merchant_id: &str, creds_identifier: Option, key_store: &domain::MerchantKeyStore, @@ -3962,7 +3959,7 @@ pub fn get_applepay_metadata( } pub async fn get_apple_pay_retryable_connectors( - state: AppState, + state: SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, key_store: &domain::MerchantKeyStore, @@ -4067,7 +4064,7 @@ impl ApplePayData { pub async fn decrypt( &self, - state: &AppState, + state: &SessionState, ) -> CustomResult { let merchant_id = self.merchant_id(state).await?; let shared_secret = self.shared_secret(state).await?; @@ -4080,7 +4077,7 @@ impl ApplePayData { pub async fn merchant_id( &self, - state: &AppState, + state: &SessionState, ) -> CustomResult { let cert_data = state .conf @@ -4125,7 +4122,7 @@ impl ApplePayData { pub async fn shared_secret( &self, - state: &AppState, + state: &SessionState, ) -> CustomResult, errors::ApplePayDecryptionError> { let public_ec_bytes = BASE64_ENGINE .decode(self.header.ephemeral_public_key.peek().as_bytes()) @@ -4301,7 +4298,7 @@ pub fn validate_payment_link_request( } pub async fn get_gsm_record( - state: &AppState, + state: &SessionState, error_code: Option, error_message: Option, connector_name: String, @@ -4438,7 +4435,7 @@ pub fn update_additional_payment_data_with_connector_response_pm_data( } pub async fn get_payment_method_details_from_payment_token( - state: &AppState, + state: &SessionState, payment_attempt: &PaymentAttempt, payment_intent: &PaymentIntent, key_store: &domain::MerchantKeyStore, @@ -4589,7 +4586,7 @@ pub enum PaymentExternalAuthenticationFlow { } pub async fn get_payment_external_authentication_flow_during_confirm( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, business_profile: &storage::BusinessProfile, payment_data: &mut PaymentData, diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 35fc2897ea48..45e45bbe9cb6 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -29,7 +29,7 @@ use super::{helpers, CustomerDetails, PaymentData}; use crate::{ core::errors::{self, CustomResult, RouterResult}, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ self, @@ -102,7 +102,7 @@ pub trait GetTracker: Send { #[allow(clippy::too_many_arguments)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &R, merchant_account: &domain::MerchantAccount, @@ -127,7 +127,7 @@ pub trait Domain: Send + Sync { #[allow(clippy::too_many_arguments)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -140,7 +140,7 @@ pub trait Domain: Send + Sync { async fn add_task_to_process_tracker<'a>( &'a self, - _db: &'a AppState, + _db: &'a SessionState, _payment_attempt: &storage::PaymentAttempt, _requeue: bool, _schedule_time: Option, @@ -151,7 +151,7 @@ pub trait Domain: Send + Sync { async fn get_connector<'a>( &'a self, merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &R, payment_intent: &storage::PaymentIntent, mechant_key_store: &domain::MerchantKeyStore, @@ -159,7 +159,7 @@ pub trait Domain: Send + Sync { async fn populate_payment_data<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _payment_data: &mut PaymentData, _merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -168,7 +168,7 @@ pub trait Domain: Send + Sync { async fn call_external_three_ds_authentication_if_eligible<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _payment_data: &mut PaymentData, _should_continue_confirm_transaction: &mut bool, _connector_call_type: &ConnectorCallType, @@ -181,7 +181,7 @@ pub trait Domain: Send + Sync { #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -190,7 +190,7 @@ pub trait Domain: Send + Sync { async fn store_extended_card_info_temporarily<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _payment_id: &str, _business_profile: &storage::BusinessProfile, _payment_method_data: &Option, @@ -204,7 +204,7 @@ pub trait Domain: Send + Sync { pub trait UpdateTracker: Send { async fn update_trackers<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, req_state: ReqState, payment_data: D, customer: Option, @@ -222,7 +222,7 @@ pub trait UpdateTracker: Send { pub trait PostUpdateTracker: Send { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, payment_data: D, response: types::RouterData, @@ -233,7 +233,7 @@ pub trait PostUpdateTracker: Send { async fn save_pm_and_mandate<'b>( &self, - _state: &AppState, + _state: &SessionState, _resp: &types::RouterData, _merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -285,7 +285,7 @@ where async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, _request: &api::PaymentsRetrieveRequest, _payment_intent: &storage::PaymentIntent, _merchant_key_store: &domain::MerchantKeyStore, @@ -296,7 +296,7 @@ where #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -320,7 +320,7 @@ where #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -365,7 +365,7 @@ where #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, @@ -381,7 +381,7 @@ where async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, _request: &api::PaymentsCaptureRequest, _payment_intent: &storage::PaymentIntent, _merchant_key_store: &domain::MerchantKeyStore, @@ -392,7 +392,7 @@ where #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -438,7 +438,7 @@ where #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, @@ -454,7 +454,7 @@ where async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, _request: &api::PaymentsCancelRequest, _payment_intent: &storage::PaymentIntent, _merchant_key_store: &domain::MerchantKeyStore, @@ -465,7 +465,7 @@ where #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -500,7 +500,7 @@ where #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_data: &mut PaymentData, _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, @@ -516,7 +516,7 @@ where async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, _request: &api::PaymentsRejectRequest, _payment_intent: &storage::PaymentIntent, _merchant_key_store: &domain::MerchantKeyStore, @@ -527,7 +527,7 @@ where #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 52325db753a6..a492cb760389 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -12,7 +12,7 @@ use crate::{ errors::{self, RouterResult, StorageErrorExt}, payments::{helpers, operations, PaymentData}, }, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -34,7 +34,7 @@ impl GetTracker, api::PaymentsCaptureRequest> #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, _request: &api::PaymentsCaptureRequest, merchant_account: &domain::MerchantAccount, @@ -194,7 +194,7 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 64114fc40e65..ce79f9392c80 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -14,7 +14,7 @@ use crate::{ payments::{helpers, operations, PaymentData}, }, events::audit_events::{AuditEvent, AuditEventType}, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ self as core_types, @@ -34,7 +34,7 @@ impl GetTracker, api::PaymentsCancelRequest> #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsCancelRequest, merchant_account: &domain::MerchantAccount, @@ -207,7 +207,7 @@ impl UpdateTracker, api::PaymentsCancelRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, req_state: ReqState, mut payment_data: PaymentData, _customer: Option, diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index ec86d1d17245..447f7b929066 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -12,7 +12,7 @@ use crate::{ errors::{self, RouterResult, StorageErrorExt}, payments::{self, helpers, operations, types::MultipleCaptureData}, }, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ self as core_types, @@ -34,7 +34,7 @@ impl GetTracker, api::PaymentsCaptu #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsCaptureRequest, merchant_account: &domain::MerchantAccount, @@ -254,7 +254,7 @@ impl UpdateTracker, api::PaymentsCaptureRe #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, _req_state: ReqState, mut payment_data: payments::PaymentData, _customer: Option, 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 49fe8ce20114..1ff95c37f667 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -15,7 +15,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -34,7 +34,7 @@ impl GetTracker, api::PaymentsRequest> for Co #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, @@ -367,7 +367,7 @@ impl Domain for CompleteAuthorize { #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -392,7 +392,7 @@ impl Domain for CompleteAuthorize { #[instrument(skip_all)] async fn add_task_to_process_tracker<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_attempt: &storage::PaymentAttempt, _requeue: bool, _schedule_time: Option, @@ -403,7 +403,7 @@ impl Domain for CompleteAuthorize { async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &api::PaymentsRequest, _payment_intent: &storage::PaymentIntent, _key_store: &domain::MerchantKeyStore, @@ -416,7 +416,7 @@ impl Domain for CompleteAuthorize { #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -429,7 +429,7 @@ impl UpdateTracker, api::PaymentsRequest> for Comple #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - state: &'b AppState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index edda62d79f7c..ea5f2c0ecb43 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -29,7 +29,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ self, @@ -48,7 +48,7 @@ impl GetTracker, api::PaymentsRequest> for Pa #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, @@ -687,7 +687,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: storage_enums::MerchantStorageScheme, key_store: &domain::MerchantKeyStore, @@ -717,7 +717,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn add_task_to_process_tracker<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_attempt: &storage::PaymentAttempt, requeue: bool, schedule_time: Option, @@ -732,7 +732,7 @@ impl Domain for PaymentConfirm { async move { helpers::add_domain_task_to_pt( &m_self, - m_state.as_ref(), + &m_state, &m_payment_attempt, requeue, schedule_time, @@ -748,7 +748,7 @@ impl Domain for PaymentConfirm { async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &api::PaymentsRequest, _payment_intent: &storage::PaymentIntent, _key_store: &domain::MerchantKeyStore, @@ -761,7 +761,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn populate_payment_data<'a>( &'a self, - state: &AppState, + state: &SessionState, payment_data: &mut PaymentData, _merchant_account: &domain::MerchantAccount, ) -> CustomResult<(), errors::ApiErrorResponse> { @@ -770,7 +770,7 @@ impl Domain for PaymentConfirm { async fn call_external_three_ds_authentication_if_eligible<'a>( &'a self, - state: &AppState, + state: &SessionState, payment_data: &mut PaymentData, should_continue_confirm_transaction: &mut bool, connector_call_type: &ConnectorCallType, @@ -857,7 +857,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_data: &mut PaymentData, ) -> CustomResult { @@ -867,7 +867,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn store_extended_card_info_temporarily<'a>( &'a self, - state: &AppState, + state: &SessionState, payment_id: &str, business_profile: &storage::BusinessProfile, payment_method_data: &Option, @@ -934,7 +934,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - state: &'b AppState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, customer: Option, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 9a4e87890582..bb249e9a9cd1 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -30,7 +30,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -54,7 +54,7 @@ impl GetTracker, api::PaymentsRequest> for Pa #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, @@ -215,7 +215,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .map(|merchant_name| merchant_name.into_inner().peek().to_owned()) .unwrap_or_default(); - let default_domain_name = state.conf.server.base_url.clone(); + let default_domain_name = state.base_url.clone(); let (payment_link_config, domain_name) = payment_link::get_payment_link_config_based_on_priority( @@ -497,7 +497,7 @@ impl Domain for PaymentCreate { #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -521,7 +521,7 @@ impl Domain for PaymentCreate { #[instrument(skip_all)] async fn add_task_to_process_tracker<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_attempt: &PaymentAttempt, _requeue: bool, _schedule_time: Option, @@ -532,7 +532,7 @@ impl Domain for PaymentCreate { async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &api::PaymentsRequest, _payment_intent: &storage::PaymentIntent, _merchant_key_store: &domain::MerchantKeyStore, @@ -543,7 +543,7 @@ impl Domain for PaymentCreate { #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -556,7 +556,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - state: &'b AppState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, @@ -771,7 +771,7 @@ impl PaymentCreate { payment_method_type: Option, request: &api::PaymentsRequest, browser_info: Option, - state: &AppState, + state: &SessionState, payment_method_billing_address_id: Option, payment_method_info: &Option, key_store: &domain::MerchantKeyStore, @@ -1074,7 +1074,7 @@ impl PaymentCreate { #[instrument(skip_all)] pub async fn get_ephemeral_key( request: &api::PaymentsRequest, - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, ) -> Option { match request.customer_id.clone() { diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index 60061d655c1c..cf8217718cbc 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -12,7 +12,7 @@ use crate::{ errors::{self, RouterResult, StorageErrorExt}, payments::{helpers, operations, PaymentAddress, PaymentData}, }, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -31,7 +31,7 @@ impl GetTracker, PaymentsCancelRequest> for P #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, _request: &PaymentsCancelRequest, merchant_account: &domain::MerchantAccount, @@ -191,7 +191,7 @@ impl UpdateTracker, PaymentsCancelRequest> for Payme #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - state: &'b AppState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 3b5bd9fd43ba..4843d1145d50 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -28,7 +28,7 @@ use crate::{ }, utils as core_utils, }, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, types::{ self, api, domain, storage::{self, enums}, @@ -51,7 +51,7 @@ impl PostUpdateTracker, types::PaymentsAuthor { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -82,7 +82,7 @@ impl PostUpdateTracker, types::PaymentsAuthor async fn save_pm_and_mandate<'b>( &self, - state: &AppState, + state: &SessionState, resp: &types::RouterData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -237,7 +237,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, _payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -368,7 +368,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu impl PostUpdateTracker, types::PaymentsSyncData> for PaymentResponse { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, payment_data: PaymentData, router_data: types::RouterData, @@ -389,7 +389,7 @@ impl PostUpdateTracker, types::PaymentsSyncData> for async fn save_pm_and_mandate<'b>( &self, - state: &AppState, + state: &SessionState, resp: &types::RouterData, merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -418,7 +418,7 @@ impl PostUpdateTracker, types::PaymentsSessionData> { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -446,7 +446,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -472,7 +472,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> impl PostUpdateTracker, types::PaymentsCancelData> for PaymentResponse { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -501,7 +501,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -528,7 +528,7 @@ impl PostUpdateTracker, types::PaymentsApproveData> impl PostUpdateTracker, types::PaymentsRejectData> for PaymentResponse { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -557,7 +557,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -590,7 +590,7 @@ impl PostUpdateTracker, types::SetupMandateRequestDa async fn save_pm_and_mandate<'b>( &self, - state: &AppState, + state: &SessionState, resp: &types::RouterData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -658,7 +658,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData { async fn update_tracker<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, payment_id: &api::PaymentIdType, payment_data: PaymentData, response: types::RouterData, @@ -679,7 +679,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData async fn save_pm_and_mandate<'b>( &self, - state: &AppState, + state: &SessionState, resp: &types::RouterData, merchant_account: &domain::MerchantAccount, _key_store: &domain::MerchantKeyStore, @@ -704,7 +704,7 @@ impl PostUpdateTracker, types::CompleteAuthorizeData #[instrument(skip_all)] async fn payment_response_update_tracker( - state: &AppState, + state: &SessionState, _payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData, @@ -1235,7 +1235,7 @@ async fn payment_response_update_tracker( } async fn update_payment_method_status_and_ntid( - state: &AppState, + state: &SessionState, payment_data: &mut PaymentData, attempt_status: common_enums::AttemptStatus, payment_response: Result, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 2aa01cd587fa..4d81914aca5f 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -14,7 +14,7 @@ use crate::{ payments::{self, helpers, operations, PaymentData}, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -35,7 +35,7 @@ impl GetTracker, api::PaymentsSessionRequest> #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsSessionRequest, merchant_account: &domain::MerchantAccount, @@ -218,7 +218,7 @@ impl UpdateTracker, api::PaymentsSessionRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - state: &'b AppState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, @@ -316,7 +316,7 @@ where #[instrument(skip_all)] async fn make_pm_data<'b>( &'b self, - _state: &'b AppState, + _state: &'b SessionState, _payment_data: &mut PaymentData, _storage_scheme: storage_enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, @@ -342,7 +342,7 @@ where async fn get_connector<'a>( &'a self, merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &api::PaymentsSessionRequest, payment_intent: &storage::PaymentIntent, key_store: &domain::MerchantKeyStore, @@ -458,7 +458,7 @@ where #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> errors::CustomResult { diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index e5567e5b4748..7c1c117a7288 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -13,7 +13,7 @@ use crate::{ payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -32,7 +32,7 @@ impl GetTracker, api::PaymentsStartRequest> f #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, _request: &api::PaymentsStartRequest, merchant_account: &domain::MerchantAccount, @@ -203,7 +203,7 @@ impl UpdateTracker, api::PaymentsStartRequest> for P #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - _state: &'b AppState, + _state: &'b SessionState, _req_state: ReqState, payment_data: PaymentData, _customer: Option, @@ -290,7 +290,7 @@ where #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -324,7 +324,7 @@ where async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, _request: &api::PaymentsStartRequest, _payment_intent: &storage::PaymentIntent, _mechant_key_store: &domain::MerchantKeyStore, @@ -335,7 +335,7 @@ where #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 0bedd70b921e..9971ea76aa3d 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -17,7 +17,7 @@ use crate::{ }, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api, domain, @@ -85,7 +85,7 @@ impl Domain for PaymentStatus { #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -109,7 +109,7 @@ impl Domain for PaymentStatus { #[instrument(skip_all)] async fn add_task_to_process_tracker<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_attempt: &storage::PaymentAttempt, requeue: bool, schedule_time: Option, @@ -120,7 +120,7 @@ impl Domain for PaymentStatus { async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &api::PaymentsRequest, _payment_intent: &storage::PaymentIntent, _key_store: &domain::MerchantKeyStore, @@ -131,7 +131,7 @@ impl Domain for PaymentStatus { #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -143,7 +143,7 @@ impl Domain for PaymentStatus { impl UpdateTracker, api::PaymentsRequest> for PaymentStatus { async fn update_trackers<'b>( &'b self, - _state: &'b AppState, + _state: &'b SessionState, _req_state: ReqState, payment_data: PaymentData, _customer: Option, @@ -164,7 +164,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen impl UpdateTracker, api::PaymentsRetrieveRequest> for PaymentStatus { async fn update_trackers<'b>( &'b self, - _state: &'b AppState, + _state: &'b SessionState, _req_state: ReqState, payment_data: PaymentData, _customer: Option, @@ -191,7 +191,7 @@ impl GetTracker, api::PaymentsRetrieveRequest #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsRetrieveRequest, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index e44dc0c1f2c1..ffa122e54af3 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -18,7 +18,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -37,7 +37,7 @@ impl GetTracker, api::PaymentsRequest> for Pa #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &api::PaymentsRequest, merchant_account: &domain::MerchantAccount, @@ -500,7 +500,7 @@ impl Domain for PaymentUpdate { #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_data: &mut PaymentData, storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, @@ -524,7 +524,7 @@ impl Domain for PaymentUpdate { #[instrument(skip_all)] async fn add_task_to_process_tracker<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_attempt: &storage::PaymentAttempt, _requeue: bool, _schedule_time: Option, @@ -535,7 +535,7 @@ impl Domain for PaymentUpdate { async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, request: &api::PaymentsRequest, _payment_intent: &storage::PaymentIntent, _key_store: &domain::MerchantKeyStore, @@ -546,7 +546,7 @@ impl Domain for PaymentUpdate { #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut PaymentData, ) -> CustomResult { @@ -559,7 +559,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - state: &'b AppState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, customer: Option, diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index 56665d65a32d..26ffd65035d6 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -18,7 +18,7 @@ use crate::{ }, routes::{ app::{ReqState, StorageInterface}, - AppState, + SessionState, }, services, types::{ @@ -41,7 +41,7 @@ impl #[instrument(skip_all)] async fn get_trackers<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, payment_id: &api::PaymentIdType, request: &PaymentsIncrementalAuthorizationRequest, merchant_account: &domain::MerchantAccount, @@ -174,7 +174,7 @@ impl UpdateTracker, PaymentsIncrementalAut #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b AppState, + db: &'b SessionState, _req_state: ReqState, mut payment_data: payments::PaymentData, _customer: Option, @@ -307,7 +307,7 @@ impl Domain #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, _payment_data: &mut payments::PaymentData, _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, @@ -323,7 +323,7 @@ impl Domain async fn get_connector<'a>( &'a self, _merchant_account: &domain::MerchantAccount, - state: &AppState, + state: &SessionState, _request: &PaymentsIncrementalAuthorizationRequest, _payment_intent: &storage::PaymentIntent, _merchant_key_store: &domain::MerchantKeyStore, @@ -334,7 +334,7 @@ impl Domain #[instrument(skip_all)] async fn guard_payment_against_blocklist<'a>( &'a self, - _state: &AppState, + _state: &SessionState, _merchant_account: &domain::MerchantAccount, _payment_data: &mut payments::PaymentData, ) -> CustomResult { diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 3bafd7774099..af41e3e4897b 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -31,7 +31,7 @@ use crate::{ #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn do_gsm_actions( - state: &app::AppState, + state: &app::SessionState, req_state: ReqState, payment_data: &mut payments::PaymentData, mut connectors: IntoIter, @@ -165,7 +165,7 @@ where #[instrument(skip_all)] pub async fn is_step_up_enabled_for_merchant_connector( - state: &app::AppState, + state: &app::SessionState, merchant_id: &str, connector_name: types::Connector, ) -> bool { @@ -189,7 +189,7 @@ pub async fn is_step_up_enabled_for_merchant_connector( #[instrument(skip_all)] pub async fn get_retries( - state: &app::AppState, + state: &app::SessionState, retries: Option, merchant_id: &str, ) -> Option { @@ -219,7 +219,7 @@ pub async fn get_retries( #[instrument(skip_all)] pub async fn get_gsm( - state: &app::AppState, + state: &app::SessionState, router_data: &types::RouterData, ) -> RouterResult> { let error_response = router_data.response.as_ref().err(); @@ -269,7 +269,7 @@ fn get_flow_name() -> RouterResult { #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub async fn do_retry( - state: &routes::AppState, + state: &routes::SessionState, req_state: ReqState, connector: api::ConnectorData, operation: &operations::BoxedOperation<'_, F, ApiRequest>, @@ -326,7 +326,7 @@ where #[instrument(skip_all)] pub async fn modify_trackers( - state: &routes::AppState, + state: &routes::SessionState, connector: String, payment_data: &mut payments::PaymentData, storage_scheme: storage_enums::MerchantStorageScheme, diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index d45f804268b3..145800ebc6aa 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -32,7 +32,7 @@ use rand::{ SeedableRng, }; use rustc_hash::FxHashMap; -use storage_impl::redis::cache::{CGRAPH_CACHE, ROUTING_CACHE}; +use storage_impl::redis::cache::{CacheKey, CGRAPH_CACHE, ROUTING_CACHE}; #[cfg(feature = "payouts")] use crate::core::payouts; @@ -50,7 +50,7 @@ use crate::{ transformers::{ForeignFrom, ForeignInto}, }, utils::{OptionExt, ValueExt}, - AppState, + SessionState, }; pub enum CachedAlgorithm { @@ -61,7 +61,7 @@ pub enum CachedAlgorithm { } pub struct SessionFlowRoutingInput<'a> { - pub state: &'a AppState, + pub state: &'a SessionState, pub country: Option, pub key_store: &'a domain::MerchantKeyStore, pub merchant_account: &'a domain::MerchantAccount, @@ -71,7 +71,7 @@ pub struct SessionFlowRoutingInput<'a> { } pub struct SessionRoutingPmTypeInput<'a> { - state: &'a AppState, + state: &'a SessionState, key_store: &'a domain::MerchantKeyStore, attempt_id: &'a str, routing_algorithm: &'a MerchantAccountRoutingAlgorithm, @@ -262,7 +262,7 @@ where } pub async fn perform_static_routing_v1( - state: &AppState, + state: &SessionState, merchant_id: &str, algorithm_ref: routing_types::RoutingAlgorithmRef, transaction_data: &routing::TransactionData<'_, F>, @@ -330,7 +330,7 @@ pub async fn perform_static_routing_v1( } async fn ensure_algorithm_cached_v1( - state: &AppState, + state: &SessionState, merchant_id: &str, algorithm_id: &str, #[cfg(feature = "business_profile_routing")] profile_id: Option, @@ -366,7 +366,10 @@ async fn ensure_algorithm_cached_v1( }; let cached_algorithm = ROUTING_CACHE - .get_val::>(key.as_str()) + .get_val::>(CacheKey { + key: key.clone(), + prefix: state.tenant.clone(), + }) .await; let algorithm = if let Some(algo) = cached_algorithm { @@ -428,7 +431,7 @@ fn execute_dsl_and_get_connector_v1( } pub async fn refresh_routing_cache_v1( - state: &AppState, + state: &SessionState, key: String, algorithm_id: &str, #[cfg(feature = "business_profile_routing")] profile_id: Option, @@ -483,7 +486,15 @@ pub async fn refresh_routing_cache_v1( let arc_cached_algorithm = Arc::new(cached_algorithm); - ROUTING_CACHE.push(key, arc_cached_algorithm.clone()).await; + ROUTING_CACHE + .push( + CacheKey { + key, + prefix: state.tenant.clone(), + }, + arc_cached_algorithm.clone(), + ) + .await; Ok(arc_cached_algorithm) } @@ -523,7 +534,7 @@ pub fn perform_volume_split( } pub async fn get_merchant_cgraph<'a>( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, #[cfg(feature = "business_profile_routing")] profile_id: Option, transaction_type: &api_enums::TransactionType, @@ -554,7 +565,10 @@ pub async fn get_merchant_cgraph<'a>( let cached_cgraph = CGRAPH_CACHE .get_val::>>( - key.as_str(), + CacheKey { + key: key.clone(), + prefix: state.tenant.clone(), + }, ) .await; @@ -576,7 +590,7 @@ pub async fn get_merchant_cgraph<'a>( } pub async fn refresh_cgraph_cache<'a>( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, key: String, #[cfg(feature = "business_profile_routing")] profile_id: Option, @@ -650,14 +664,22 @@ pub async fn refresh_cgraph_cache<'a>( .attach_printable("when construction cgraph")?, ); - CGRAPH_CACHE.push(key, Arc::clone(&cgraph)).await; + CGRAPH_CACHE + .push( + CacheKey { + key, + prefix: state.tenant.clone(), + }, + Arc::clone(&cgraph), + ) + .await; Ok(cgraph) } #[allow(clippy::too_many_arguments)] async fn perform_cgraph_filtering( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, chosen: Vec, backend_input: dsl_inputs::BackendInput, @@ -708,7 +730,7 @@ async fn perform_cgraph_filtering( } pub async fn perform_eligibility_analysis( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, chosen: Vec, transaction_data: &routing::TransactionData<'_, F>, @@ -735,7 +757,7 @@ pub async fn perform_eligibility_analysis( } pub async fn perform_fallback_routing( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, transaction_data: &routing::TransactionData<'_, F>, eligible_connectors: Option<&Vec>, @@ -781,7 +803,7 @@ pub async fn perform_fallback_routing( } pub async fn perform_eligibility_analysis_with_fallback( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, chosen: Vec, transaction_data: &routing::TransactionData<'_, F>, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 3341011bb5d6..48637a0d2b32 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -18,7 +18,7 @@ use crate::{ mandate, payment_methods, payments, }, logger, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{ self, @@ -54,7 +54,7 @@ impl From<&types::RouterData #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn save_payment_method( - state: &AppState, + state: &SessionState, connector_name: String, merchant_connector_id: Option, save_payment_method_data: SavePaymentMethodData, @@ -646,7 +646,7 @@ async fn skip_saving_card_in_locker( } pub async fn save_in_locker( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_method_request: api::PaymentMethodCreate, ) -> RouterResult<( @@ -660,14 +660,14 @@ pub async fn save_in_locker( .clone() .get_required_value("customer_id")?; match payment_method_request.card.clone() { - Some(card) => payment_methods::cards::add_card_to_locker( + Some(card) => Box::pin(payment_methods::cards::add_card_to_locker( state, payment_method_request, &card, &customer_id, merchant_account, None, - ) + )) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Add Card Failed"), @@ -719,7 +719,7 @@ pub fn create_payment_method_metadata( } pub async fn add_payment_method_token( - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, tokenization_action: &payments::TokenizationAction, router_data: &mut types::RouterData, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index d8a7092455b3..8eca589ca28e 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -15,7 +15,7 @@ use router_env::{instrument, tracing}; use super::{flows::Feature, types::AuthenticationData, PaymentData}; use crate::{ - configs::settings::{ConnectorRequestReferenceIdConfig, Server}, + configs::settings::ConnectorRequestReferenceIdConfig, connector::{Helcim, Nexinets}, core::{ errors::{self, RouterResponse, RouterResult}, @@ -23,7 +23,7 @@ use crate::{ utils as core_utils, }, headers::X_PAYMENT_CONFIRM_SOURCE, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services::{self, RedirectForm}, types::{ self, api, domain, @@ -36,7 +36,7 @@ use crate::{ #[instrument(skip_all)] pub async fn construct_payment_router_data<'a, F, T>( - state: &'a AppState, + state: &'a SessionState, payment_data: PaymentData, connector_id: &str, merchant_account: &domain::MerchantAccount, @@ -93,7 +93,7 @@ where }); let additional_data = PaymentAdditionalData { - router_base_url: state.conf.server.base_url.clone(), + router_base_url: state.base_url.clone(), connector_name: connector_id.to_string(), payment_data: payment_data.clone(), state, @@ -204,7 +204,7 @@ where data: D, customer: Option, auth_flow: services::AuthFlow, - server: &Server, + base_url: &str, operation: Op, connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, connector_http_status_code: Option, @@ -223,7 +223,7 @@ where payment_data: PaymentData, customer: Option, auth_flow: services::AuthFlow, - server: &Server, + base_url: &str, operation: Op, connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, connector_http_status_code: Option, @@ -253,7 +253,7 @@ where captures, customer, auth_flow, - server, + base_url, &operation, connector_request_reference_id_config, connector_http_status_code, @@ -273,7 +273,7 @@ where payment_data: PaymentData, _customer: Option, _auth_flow: services::AuthFlow, - _server: &Server, + _base_url: &str, _operation: Op, _connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, _connector_http_status_code: Option, @@ -305,7 +305,7 @@ where data: PaymentData, customer: Option, _auth_flow: services::AuthFlow, - _server: &Server, + _base_url: &str, _operation: Op, _connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, _connector_http_status_code: Option, @@ -361,7 +361,7 @@ pub fn payments_to_payments_response( captures: Option>, customer: Option, auth_flow: services::AuthFlow, - server: &Server, + base_url: &str, operation: &Op, connector_request_reference_id_config: &ConnectorRequestReferenceIdConfig, connector_http_status_code: Option, @@ -589,7 +589,7 @@ where .or(payment_attempt.authentication_data.as_ref().map(|_| { api_models::payments::NextActionData::RedirectToUrl { redirect_to_url: helpers::create_startpay_url( - server, + base_url, &payment_attempt, &payment_intent, ), @@ -606,9 +606,9 @@ where .get_required_value("connector")?; Some(api_models::payments::NextActionData::ThreeDsInvoke { three_ds_data: api_models::payments::ThreeDsData { - three_ds_authentication_url: helpers::create_authentication_url(&server.base_url, &payment_attempt), + three_ds_authentication_url: helpers::create_authentication_url(base_url, &payment_attempt), three_ds_authorize_url: helpers::create_authorize_url( - &server.base_url, + base_url, &payment_attempt, payment_connector_name, ), @@ -1144,7 +1144,7 @@ where router_base_url: String, connector_name: String, payment_data: PaymentData, - state: &'a AppState, + state: &'a SessionState, customer_data: &'a Option, } impl TryFrom> for types::PaymentsAuthorizeData { @@ -1260,6 +1260,7 @@ impl TryFrom> for types::PaymentsAuthoriz statement_descriptor: payment_data.payment_intent.statement_descriptor_name, capture_method: payment_data.payment_attempt.capture_method, amount: amount.get_amount_as_i64(), + minor_amount: amount, currency: payment_data.currency, browser_info, email: payment_data.email, @@ -1420,12 +1421,14 @@ impl TryFrom> for types::PaymentsCaptureD let amount = MinorUnit::from(payment_data.amount); Ok(Self { amount_to_capture: amount_to_capture.get_amount_as_i64(), // This should be removed once we start moving to connector module + minor_amount_to_capture: amount_to_capture, currency: payment_data.currency, connector_transaction_id: connector .connector .connector_transaction_id(payment_data.payment_attempt.clone())? .ok_or(errors::ApiErrorResponse::ResourceIdNotFound)?, payment_amount: amount.get_amount_as_i64(), // This should be removed once we start moving to connector module + minor_payment_amount: amount, connector_meta: payment_data.payment_attempt.connector_metadata, multiple_capture_data: match payment_data.multiple_capture_data { Some(multiple_capture_data) => Some(MultipleCaptureRequestData { @@ -1702,6 +1705,7 @@ impl TryFrom> for types::CompleteAuthoriz statement_descriptor_suffix: payment_data.payment_intent.statement_descriptor_suffix, capture_method: payment_data.payment_attempt.capture_method, amount: amount.get_amount_as_i64(), // need to change once we move to connector module + minor_amount: amount, currency: payment_data.currency, browser_info, email: payment_data.email, diff --git a/crates/router/src/core/payments/types.rs b/crates/router/src/core/payments/types.rs index 92c9eec5ff80..1f16c4d667ea 100644 --- a/crates/router/src/core/payments/types.rs +++ b/crates/router/src/core/payments/types.rs @@ -18,7 +18,7 @@ use router_env::{instrument, tracing}; use crate::{ consts as router_consts, core::errors::{self, RouterResult}, - routes::AppState, + routes::SessionState, types::{ storage::{self, enums as storage_enums}, transformers::ForeignTryFrom, @@ -290,7 +290,7 @@ impl SurchargeMetadata { #[instrument(skip_all)] pub async fn persist_individual_surcharge_details_in_redis( &self, - state: &AppState, + state: &SessionState, business_profile: &BusinessProfile, ) -> RouterResult<()> { if !self.is_empty_result() { @@ -325,7 +325,7 @@ impl SurchargeMetadata { #[instrument(skip_all)] pub async fn get_individual_surcharge_detail_from_redis( - state: &AppState, + state: &SessionState, surcharge_key: SurchargeKey, payment_attempt_id: &str, ) -> CustomResult { diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 7aaf215ab51c..93c634b07fc9 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -34,7 +34,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::AppState, + routes::SessionState, services, types::{ self, @@ -71,7 +71,7 @@ pub fn get_next_connector( #[cfg(feature = "payouts")] pub async fn get_connector_choice( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector: Option, @@ -152,7 +152,7 @@ pub async fn get_connector_choice( #[instrument(skip_all)] pub async fn make_connector_decision( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_call_type: api::ConnectorCallType, @@ -254,7 +254,7 @@ pub async fn make_connector_decision( #[instrument(skip_all)] pub async fn payouts_core( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payout_data: &mut PayoutData, @@ -288,7 +288,7 @@ pub async fn payouts_core( #[instrument(skip_all)] pub async fn payouts_create_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutCreateRequest, @@ -339,7 +339,7 @@ pub async fn payouts_create_core( } pub async fn payouts_update_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutCreateRequest, @@ -470,7 +470,7 @@ pub async fn payouts_update_core( #[instrument(skip_all)] pub async fn payouts_retrieve_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutRetrieveRequest, @@ -488,7 +488,7 @@ pub async fn payouts_retrieve_core( #[instrument(skip_all)] pub async fn payouts_cancel_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutActionRequest, @@ -584,7 +584,7 @@ pub async fn payouts_cancel_core( #[instrument(skip_all)] pub async fn payouts_fulfill_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutActionRequest, @@ -670,7 +670,7 @@ pub async fn payouts_fulfill_core( #[cfg(feature = "olap")] pub async fn payouts_list_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, constraints: payouts::PayoutListConstraints, @@ -767,7 +767,7 @@ pub async fn payouts_list_core( #[cfg(feature = "olap")] pub async fn payouts_filtered_list_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, filters: payouts::PayoutListFilterConstraints, @@ -817,7 +817,7 @@ pub async fn payouts_filtered_list_core( #[cfg(feature = "olap")] pub async fn payouts_list_available_filters_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, time_range: api::TimeRange, ) -> RouterResponse { @@ -852,7 +852,7 @@ pub async fn payouts_list_available_filters_core( // ********************************************** HELPERS ********************************************** pub async fn call_connector_payout( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -962,7 +962,7 @@ pub async fn call_connector_payout( } pub async fn complete_create_recipient( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -989,7 +989,7 @@ pub async fn complete_create_recipient( } pub async fn create_recipient( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1124,7 +1124,7 @@ pub async fn create_recipient( } pub async fn complete_payout_eligibility( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1167,7 +1167,7 @@ pub async fn complete_payout_eligibility( } pub async fn check_payout_eligibility( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1281,7 +1281,7 @@ pub async fn check_payout_eligibility( } pub async fn complete_create_payout( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1343,7 +1343,7 @@ pub async fn complete_create_payout( } pub async fn create_payout( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1473,7 +1473,7 @@ pub async fn create_payout( } pub async fn complete_create_recipient_disburse_account( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1500,7 +1500,7 @@ pub async fn complete_create_recipient_disburse_account( } pub async fn create_recipient_disburse_account( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1586,7 +1586,7 @@ pub async fn create_recipient_disburse_account( } pub async fn cancel_payout( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1692,7 +1692,7 @@ pub async fn cancel_payout( } pub async fn fulfill_payout( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_data: &api::ConnectorData, @@ -1904,7 +1904,7 @@ pub async fn response_handler( // DB entries #[allow(clippy::too_many_arguments)] pub async fn payout_create_db_entries( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, req: &payouts::PayoutCreateRequest, @@ -2061,7 +2061,7 @@ pub async fn payout_create_db_entries( } pub async fn make_payout_data( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, req: &payouts::PayoutRequest, @@ -2169,7 +2169,7 @@ pub async fn add_external_account_addition_task( } async fn validate_and_get_business_profile( - state: &AppState, + state: &SessionState, profile_id: &String, merchant_id: &str, ) -> RouterResult { diff --git a/crates/router/src/core/payouts/access_token.rs b/crates/router/src/core/payouts/access_token.rs index 8dddc0c570c9..af4f9e5f2883 100644 --- a/crates/router/src/core/payouts/access_token.rs +++ b/crates/router/src/core/payouts/access_token.rs @@ -7,7 +7,7 @@ use crate::{ errors::{self, RouterResult}, payments, }, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{self, api as api_types, domain, storage::enums}, }; @@ -17,7 +17,7 @@ use crate::{ /// There was an error, cannot proceed further #[cfg(feature = "payouts")] pub async fn create_access_token( - state: &AppState, + state: &SessionState, connector_data: &api_types::ConnectorData, merchant_account: &domain::MerchantAccount, router_data: &mut types::PayoutsRouterData, @@ -48,7 +48,7 @@ pub async fn create_access_token( #[cfg(feature = "payouts")] pub async fn add_access_token_for_payout( - state: &AppState, + state: &SessionState, connector: &api_types::ConnectorData, merchant_account: &domain::MerchantAccount, router_data: &types::PayoutsRouterData, @@ -132,7 +132,7 @@ pub async fn add_access_token_for_payout( #[cfg(feature = "payouts")] pub async fn refresh_connector_auth( - state: &AppState, + state: &SessionState, connector: &api_types::ConnectorData, _merchant_account: &domain::MerchantAccount, router_data: &types::RouterData< diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 3059d3a4164c..6f8a7784aebb 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -25,7 +25,7 @@ use crate::{ routing::TransactionData, }, db::StorageInterface, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{ api::{self, enums as api_enums}, @@ -41,7 +41,7 @@ use crate::{ #[allow(clippy::too_many_arguments)] pub async fn make_payout_method_data<'a>( - state: &'a AppState, + state: &'a SessionState, payout_method_data: Option<&api::PayoutMethodData>, payout_token: Option<&str>, customer_id: &id_type::CustomerId, @@ -186,7 +186,7 @@ pub async fn make_payout_method_data<'a>( } pub async fn save_payout_data_to_locker( - state: &AppState, + state: &SessionState, payout_data: &mut PayoutData, payout_method_data: &api::PayoutMethodData, merchant_account: &domain::MerchantAccount, @@ -574,7 +574,7 @@ pub async fn save_payout_data_to_locker( } pub async fn get_or_create_customer_details( - state: &AppState, + state: &SessionState, customer_details: &CustomerDetails, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -638,7 +638,7 @@ pub async fn get_or_create_customer_details( } pub async fn decide_payout_connector( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, request_straight_through: Option, @@ -795,7 +795,7 @@ pub async fn decide_payout_connector( } pub async fn get_default_payout_connector( - _state: &AppState, + _state: &SessionState, request_connector: Option, ) -> CustomResult { Ok(request_connector.map_or( @@ -805,7 +805,7 @@ pub async fn get_default_payout_connector( } pub fn should_call_payout_connector_create_customer<'a>( - state: &AppState, + state: &SessionState, connector: &api::ConnectorData, customer: &'a Option, connector_label: &str, @@ -834,7 +834,7 @@ pub fn should_call_payout_connector_create_customer<'a>( } pub async fn get_gsm_record( - state: &AppState, + state: &SessionState, error_code: Option, error_message: Option, connector_name: Option, diff --git a/crates/router/src/core/payouts/retry.rs b/crates/router/src/core/payouts/retry.rs index 7cb929db94a2..792309920cb9 100644 --- a/crates/router/src/core/payouts/retry.rs +++ b/crates/router/src/core/payouts/retry.rs @@ -28,7 +28,7 @@ pub enum PayoutRetryType { #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn do_gsm_multiple_connector_actions( - state: &app::AppState, + state: &app::SessionState, mut connectors: IntoIter, original_connector_data: api::ConnectorData, payout_data: &mut PayoutData, @@ -95,7 +95,7 @@ pub async fn do_gsm_multiple_connector_actions( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn do_gsm_single_connector_actions( - state: &app::AppState, + state: &app::SessionState, original_connector_data: api::ConnectorData, payout_data: &mut PayoutData, merchant_account: &domain::MerchantAccount, @@ -158,7 +158,7 @@ pub async fn do_gsm_single_connector_actions( #[instrument(skip_all)] pub async fn get_retries( - state: &app::AppState, + state: &app::SessionState, retries: Option, merchant_id: &str, retry_type: PayoutRetryType, @@ -196,7 +196,7 @@ pub async fn get_retries( #[instrument(skip_all)] pub async fn get_gsm( - state: &app::AppState, + state: &app::SessionState, original_connector_data: &api::ConnectorData, payout_data: &PayoutData, ) -> RouterResult> { @@ -236,7 +236,7 @@ pub fn get_gsm_decision( #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub async fn do_retry( - state: &routes::AppState, + state: &routes::SessionState, connector: api::ConnectorData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -251,7 +251,7 @@ pub async fn do_retry( #[instrument(skip_all)] pub async fn modify_trackers( - state: &routes::AppState, + state: &routes::SessionState, connector: &api::ConnectorData, merchant_account: &domain::MerchantAccount, payout_data: &mut PayoutData, diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index 1819c5152e0b..58972cf4276e 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -11,7 +11,7 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::AppState, + routes::SessionState, types::{api::payouts, domain, storage}, utils, }; @@ -45,7 +45,7 @@ pub async fn validate_uniqueness_of_payout_id_against_merchant_id( /// - payout_id is unique against merchant_id /// - payout_token provided is legitimate pub async fn validate_create_request( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, req: &payouts::PayoutCreateRequest, merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index f184e166e22d..09d18963a29e 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -36,15 +36,12 @@ use crate::{ errors::{self, ApiErrorResponse, RouterResponse, RouterResult, StorageErrorExt}, payment_methods::cards, payments::helpers as oss_helpers, - pm_auth::helpers::{self as pm_auth_helpers}, + pm_auth::helpers as pm_auth_helpers, }, db::StorageInterface, logger, - routes::AppState, - services::{ - pm_auth::{self as pm_auth_services}, - ApplicationResponse, - }, + routes::SessionState, + services::{pm_auth as pm_auth_services, ApplicationResponse}, types::{ self, domain::{self, types::decrypt}, @@ -55,7 +52,7 @@ use crate::{ }; pub async fn create_link_token( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, payload: api_models::pm_auth::LinkTokenCreateRequest, @@ -160,7 +157,7 @@ pub async fn create_link_token( }; let connector_resp = pm_auth_services::execute_connector_processing_step( - state.as_ref(), + &state, connector_integration, &router_data, &connector.connector_name, @@ -205,7 +202,7 @@ impl ForeignTryFrom<&types::ConnectorAuthType> for PlaidAuthType { } pub async fn exchange_token_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, payload: api_models::pm_auth::ExchangeTokenCreateRequest, @@ -270,7 +267,7 @@ async fn store_bank_details_in_payment_methods( key_store: domain::MerchantKeyStore, payload: api_models::pm_auth::ExchangeTokenCreateRequest, merchant_account: domain::MerchantAccount, - state: AppState, + state: SessionState, bank_account_details_resp: pm_auth_types::BankAccountCredentialsResponse, connector_details: (&str, Secret), mca_id: String, @@ -523,7 +520,7 @@ pub async fn get_bank_account_creds( connector_name: &str, access_token: &Secret, auth_type: pm_auth_types::ConnectorAuthType, - state: &AppState, + state: &SessionState, bank_account_id: Option>, ) -> RouterResult { let connector_integration_bank_details: BoxedConnectorIntegration< @@ -578,7 +575,7 @@ async fn get_access_token_from_exchange_api( connector_name: &str, payload: &api_models::pm_auth::ExchangeTokenCreateRequest, auth_type: &pm_auth_types::ConnectorAuthType, - state: &AppState, + state: &SessionState, ) -> RouterResult> { let connector_integration: BoxedConnectorIntegration< '_, @@ -662,7 +659,7 @@ async fn get_selected_config_from_redis( } pub async fn retrieve_payment_method_from_auth_service( - state: &AppState, + state: &SessionState, key_store: &domain::MerchantKeyStore, auth_token: &payment_methods::BankAccountTokenData, payment_intent: &PaymentIntent, diff --git a/crates/router/src/core/poll.rs b/crates/router/src/core/poll.rs index 32a7b0f547ce..53b4b02e610e 100644 --- a/crates/router/src/core/poll.rs +++ b/crates/router/src/core/poll.rs @@ -4,11 +4,13 @@ use error_stack::ResultExt; use router_env::{instrument, tracing}; use super::errors; -use crate::{core::errors::RouterResponse, services::ApplicationResponse, types::domain, AppState}; +use crate::{ + core::errors::RouterResponse, services::ApplicationResponse, types::domain, SessionState, +}; #[instrument(skip_all)] pub async fn retrieve_poll_status( - state: AppState, + state: SessionState, req: crate::types::api::PollId, merchant_account: domain::MerchantAccount, ) -> RouterResponse { diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index b9861ea1f23a..0d441e95382d 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -5,7 +5,10 @@ use std::collections::HashMap; #[cfg(feature = "olap")] use api_models::admin::MerchantConnectorInfo; -use common_utils::ext_traits::{AsyncExt, ValueExt}; +use common_utils::{ + ext_traits::{AsyncExt, ValueExt}, + types::MinorUnit, +}; use error_stack::{report, ResultExt}; use masking::PeekInterface; use router_env::{instrument, tracing}; @@ -21,7 +24,7 @@ use crate::{ utils as core_utils, }, db, logger, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services, types::{ self, @@ -39,7 +42,7 @@ use crate::{ #[instrument(skip_all)] pub async fn refund_create_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: refunds::RefundRequest, @@ -75,14 +78,12 @@ pub async fn refund_create_core( // Amount is not passed in request refer from payment intent. amount = req .amount - .or(payment_intent - .amount_captured - .map(|capture_amount| capture_amount.get_amount_as_i64())) + .or(payment_intent.amount_captured) .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("amount captured is none in a successful payment")?; //[#299]: Can we change the flow based on some workflow idea - utils::when(amount <= 0, || { + utils::when(amount <= MinorUnit::new(0), || { Err(report!(errors::ApiErrorResponse::InvalidDataFormat { field_name: "amount".to_string(), expected_format: "positive integer".to_string() @@ -133,7 +134,7 @@ pub async fn refund_create_core( #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub async fn trigger_refund_to_gateway( - state: &AppState, + state: &SessionState, refund: &storage::Refund, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -178,7 +179,7 @@ pub async fn trigger_refund_to_gateway( &routed_through, merchant_account, key_store, - (payment_attempt.amount.get_amount_as_i64(), currency), + (payment_attempt.amount, currency), payment_intent, payment_attempt, refund, @@ -316,14 +317,14 @@ pub async fn trigger_refund_to_gateway( // ********************************************** REFUND SYNC ********************************************** pub async fn refund_response_wrapper<'a, F, Fut, T, Req>( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, request: Req, f: F, ) -> RouterResponse where - F: Fn(AppState, domain::MerchantAccount, domain::MerchantKeyStore, Req) -> Fut, + F: Fn(SessionState, domain::MerchantAccount, domain::MerchantKeyStore, Req) -> Fut, Fut: futures::Future>, T: ForeignInto, { @@ -336,7 +337,7 @@ where #[instrument(skip_all)] pub async fn refund_retrieve_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, request: refunds::RefundsRetrieveRequest, @@ -431,7 +432,7 @@ fn should_call_refund(refund: &diesel_models::refund::Refund, force_sync: bool) #[instrument(skip_all)] pub async fn sync_refund_with_gateway( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_attempt: &storage::PaymentAttempt, @@ -458,7 +459,7 @@ pub async fn sync_refund_with_gateway( &connector_id, merchant_account, key_store, - (payment_attempt.amount.get_amount_as_i64(), currency), + (payment_attempt.amount, currency), payment_intent, payment_attempt, refund, @@ -545,7 +546,7 @@ pub async fn sync_refund_with_gateway( // ********************************************** REFUND UPDATE ********************************************** pub async fn refund_update_core( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: refunds::RefundUpdateRequest, ) -> RouterResponse { @@ -583,12 +584,12 @@ pub async fn refund_update_core( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn validate_and_create_refund( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payment_attempt: &storage::PaymentAttempt, payment_intent: &storage::PaymentIntent, - refund_amount: i64, + refund_amount: MinorUnit, req: refunds::RefundRequest, creds_identifier: Option, ) -> RouterResult { @@ -680,7 +681,7 @@ pub async fn validate_and_create_refund( validator::validate_refund_amount( total_amount_captured.get_amount_as_i64(), &all_refunds, - refund_amount, + refund_amount.get_amount_as_i64(), ) .change_context(errors::ApiErrorResponse::RefundAmountExceedsPaymentAmount)?; @@ -705,7 +706,7 @@ pub async fn validate_and_create_refund( .set_connector_transaction_id(connecter_transaction_id.to_string()) .set_connector(connector) .set_refund_type(req.refund_type.unwrap_or_default().foreign_into()) - .set_total_amount(payment_attempt.amount.get_amount_as_i64()) + .set_total_amount(payment_attempt.amount) .set_refund_amount(refund_amount) .set_currency(currency) .set_created_at(Some(common_utils::date_time::now())) @@ -725,7 +726,7 @@ pub async fn validate_and_create_refund( .await { Ok(refund) => { - schedule_refund_execution( + Box::pin(schedule_refund_execution( state, refund.clone(), refund_type, @@ -735,7 +736,7 @@ pub async fn validate_and_create_refund( payment_intent, creds_identifier, charges, - ) + )) .await? } Err(err) => { @@ -766,7 +767,7 @@ pub async fn validate_and_create_refund( #[instrument(skip_all)] #[cfg(feature = "olap")] pub async fn refund_list( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: api_models::refunds::RefundListRequest, ) -> RouterResponse { @@ -811,7 +812,7 @@ pub async fn refund_list( #[instrument(skip_all)] #[cfg(feature = "olap")] pub async fn refund_filter_list( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, req: api_models::payments::TimeRange, ) -> RouterResponse { @@ -831,7 +832,7 @@ pub async fn refund_filter_list( #[instrument(skip_all)] #[cfg(feature = "olap")] pub async fn get_filters_for_refunds( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, ) -> RouterResponse { let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) = @@ -899,7 +900,7 @@ impl ForeignFrom for api::RefundResponse { #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn schedule_refund_execution( - state: &AppState, + state: &SessionState, refund: storage::Refund, refund_type: api_models::refunds::RefundType, merchant_account: &domain::MerchantAccount, @@ -978,7 +979,7 @@ pub async fn schedule_refund_execution( #[instrument(skip_all)] pub async fn sync_refund_with_gateway_workflow( - state: &AppState, + state: &SessionState, refund_tracker: &storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { let refund_core = @@ -1047,7 +1048,7 @@ pub async fn sync_refund_with_gateway_workflow( #[instrument(skip_all)] pub async fn start_refund_workflow( - state: &AppState, + state: &SessionState, refund_tracker: &storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { match refund_tracker.name.as_deref() { @@ -1063,7 +1064,7 @@ pub async fn start_refund_workflow( #[instrument(skip_all)] pub async fn trigger_refund_execute_workflow( - state: &AppState, + state: &SessionState, refund_tracker: &storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { let db = &*state.store; diff --git a/crates/router/src/core/refunds/validator.rs b/crates/router/src/core/refunds/validator.rs index 58be964d7025..1167fde5677c 100644 --- a/crates/router/src/core/refunds/validator.rs +++ b/crates/router/src/core/refunds/validator.rs @@ -54,7 +54,7 @@ pub fn validate_refund_amount( if refund.refund_status != enums::RefundStatus::Failure && refund.refund_status != enums::RefundStatus::TransactionFailure { - Some(refund.refund_amount) + Some(refund.refund_amount.get_amount_as_i64()) } else { None } diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 31825617398a..a64dadaa35d9 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -27,7 +27,7 @@ use crate::{ errors::{RouterResponse, StorageErrorExt}, metrics, utils as core_utils, }, - routes::AppState, + routes::SessionState, types::domain, utils::{self, OptionExt, ValueExt}, }; @@ -46,7 +46,7 @@ where } pub async fn retrieve_merchant_routing_dictionary( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, #[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveQuery, #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, @@ -93,7 +93,7 @@ pub async fn retrieve_merchant_routing_dictionary( } pub async fn create_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, request: routing_types::RoutingConfigRequest, @@ -248,7 +248,7 @@ pub async fn create_routing_config( } pub async fn link_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, #[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore, algorithm_id: String, @@ -371,7 +371,7 @@ pub async fn link_routing_config( } pub async fn retrieve_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, algorithm_id: RoutingAlgorithmId, ) -> RouterResponse { @@ -445,7 +445,7 @@ pub async fn retrieve_routing_config( } } pub async fn unlink_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, #[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore, #[cfg(feature = "business_profile_routing")] request: routing_types::RoutingConfigRequest, @@ -630,7 +630,7 @@ pub async fn unlink_routing_config( } pub async fn update_default_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, updated_config: Vec, transaction_type: &enums::TransactionType, @@ -679,7 +679,7 @@ pub async fn update_default_routing_config( } pub async fn retrieve_default_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, transaction_type: &enums::TransactionType, ) -> RouterResponse> { @@ -699,7 +699,7 @@ pub async fn retrieve_default_routing_config( } pub async fn retrieve_linked_routing_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, #[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveLinkQuery, #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, @@ -808,7 +808,7 @@ pub async fn retrieve_linked_routing_config( } pub async fn retrieve_default_routing_config_for_profiles( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, transaction_type: &enums::TransactionType, ) -> RouterResponse> { @@ -847,7 +847,7 @@ pub async fn retrieve_default_routing_config_for_profiles( } pub async fn update_default_routing_config_for_profile( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, updated_config: Vec, profile_id: String, diff --git a/crates/router/src/core/surcharge_decision_config.rs b/crates/router/src/core/surcharge_decision_config.rs index b489b92cdf1f..7f451e6008fe 100644 --- a/crates/router/src/core/surcharge_decision_config.rs +++ b/crates/router/src/core/surcharge_decision_config.rs @@ -15,14 +15,14 @@ use super::routing::helpers::{ }; use crate::{ core::errors::{self, RouterResponse}, - routes::AppState, + routes::SessionState, services::api as service_api, types::domain, utils::OptionExt, }; pub async fn upsert_surcharge_decision_config( - state: AppState, + state: SessionState, key_store: domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, request: SurchargeDecisionConfigReq, @@ -139,7 +139,7 @@ pub async fn upsert_surcharge_decision_config( } pub async fn delete_surcharge_decision_config( - state: AppState, + state: SessionState, key_store: domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, ) -> RouterResponse<()> { @@ -167,7 +167,7 @@ pub async fn delete_surcharge_decision_config( } pub async fn retrieve_surcharge_decision_config( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, ) -> RouterResponse { let db = state.store.as_ref(); diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index ec26c37927a8..34560c330845 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -21,7 +21,7 @@ use super::errors::{StorageErrorExt, UserErrors, UserResponse, UserResult}; use crate::services::email::types as email_types; use crate::{ consts, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services::{authentication as auth, authorization::roles, ApplicationResponse}, types::{domain, transformers::ForeignInto}, utils::{self, user::two_factor_auth as tfa_utils}, @@ -33,7 +33,7 @@ pub mod sample_data; #[cfg(feature = "email")] pub async fn signup_with_merchant_id( - state: AppState, + state: SessionState, request: user_api::SignUpWithMerchantIdRequest, ) -> UserResponse { let new_user = domain::NewUser::try_from(request.clone())?; @@ -79,7 +79,7 @@ pub async fn signup_with_merchant_id( } pub async fn get_user_details( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, ) -> UserResponse { let user = user_from_token.get_user_from_db(&state).await?; @@ -99,7 +99,7 @@ pub async fn get_user_details( } pub async fn signup( - state: AppState, + state: SessionState, request: user_api::SignUpRequest, ) -> UserResponse> { let new_user = domain::NewUser::try_from(request)?; @@ -128,7 +128,7 @@ pub async fn signup( } pub async fn signup_token_only_flow( - state: AppState, + state: SessionState, request: user_api::SignUpRequest, ) -> UserResponse> { let new_user = domain::NewUser::try_from(request)?; @@ -163,7 +163,7 @@ pub async fn signup_token_only_flow( } pub async fn signin( - state: AppState, + state: SessionState, request: user_api::SignInRequest, ) -> UserResponse> { let user_from_db: domain::UserFromStorage = state @@ -207,7 +207,7 @@ pub async fn signin( } pub async fn signin_token_only_flow( - state: AppState, + state: SessionState, request: user_api::SignInRequest, ) -> UserResponse> { let user_from_db: domain::UserFromStorage = state @@ -233,7 +233,7 @@ pub async fn signin_token_only_flow( #[cfg(feature = "email")] pub async fn connect_account( - state: AppState, + state: SessionState, request: user_api::ConnectAccountRequest, ) -> UserResponse { let find_user = state.store.find_user_by_email(&request.email).await; @@ -324,13 +324,16 @@ pub async fn connect_account( } } -pub async fn signout(state: AppState, user_from_token: auth::UserFromToken) -> UserResponse<()> { +pub async fn signout( + state: SessionState, + user_from_token: auth::UserFromToken, +) -> UserResponse<()> { auth::blacklist::insert_user_in_blacklist(&state, &user_from_token.user_id).await?; auth::cookies::remove_cookie_response() } pub async fn change_password( - state: AppState, + state: SessionState, request: user_api::ChangePasswordRequest, user_from_token: auth::UserFromToken, ) -> UserResponse<()> { @@ -386,7 +389,7 @@ pub async fn change_password( #[cfg(feature = "email")] pub async fn forgot_password( - state: AppState, + state: SessionState, request: user_api::ForgotPasswordRequest, ) -> UserResponse<()> { let user_email = domain::UserEmail::from_pii_email(request.email)?; @@ -424,7 +427,7 @@ pub async fn forgot_password( } pub async fn rotate_password( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, request: user_api::RotatePasswordRequest, _req_state: ReqState, @@ -463,7 +466,7 @@ pub async fn rotate_password( #[cfg(feature = "email")] pub async fn reset_password_token_only_flow( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, request: user_api::ResetPasswordRequest, ) -> UserResponse<()> { @@ -517,7 +520,7 @@ pub async fn reset_password_token_only_flow( #[cfg(feature = "email")] pub async fn reset_password( - state: AppState, + state: SessionState, request: user_api::ResetPasswordRequest, ) -> UserResponse<()> { let token = request.token.expose(); @@ -569,7 +572,7 @@ pub async fn reset_password( } pub async fn invite_multiple_user( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, requests: Vec, req_state: ReqState, @@ -598,7 +601,7 @@ pub async fn invite_multiple_user( } async fn handle_invitation( - state: &AppState, + state: &SessionState, user_from_token: &auth::UserFromToken, request: &user_api::InviteUserRequest, req_state: &ReqState, @@ -656,7 +659,7 @@ async fn handle_invitation( //TODO: send email async fn handle_existing_user_invitation( - state: &AppState, + state: &SessionState, user_from_token: &auth::UserFromToken, request: &user_api::InviteUserRequest, invitee_user_from_db: domain::UserFromStorage, @@ -727,7 +730,7 @@ async fn handle_existing_user_invitation( } async fn handle_new_user_invitation( - state: &AppState, + state: &SessionState, user_from_token: &auth::UserFromToken, request: &user_api::InviteUserRequest, req_state: ReqState, @@ -839,7 +842,7 @@ async fn handle_new_user_invitation( #[cfg(feature = "email")] pub async fn resend_invite( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, request: user_api::ReInviteUserRequest, _req_state: ReqState, @@ -901,7 +904,7 @@ pub async fn resend_invite( #[cfg(feature = "email")] pub async fn accept_invite_from_email( - state: AppState, + state: SessionState, request: user_api::AcceptInviteFromEmailRequest, ) -> UserResponse { let token = request.token.expose(); @@ -968,7 +971,7 @@ pub async fn accept_invite_from_email( #[cfg(feature = "email")] pub async fn accept_invite_from_email_token_only_flow( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, request: user_api::AcceptInviteFromEmailRequest, ) -> UserResponse> { @@ -1034,7 +1037,7 @@ pub async fn accept_invite_from_email_token_only_flow( } pub async fn create_internal_user( - state: AppState, + state: SessionState, request: user_api::CreateInternalUserRequest, ) -> UserResponse<()> { let new_user = domain::NewUser::try_from(request)?; @@ -1097,7 +1100,7 @@ pub async fn create_internal_user( } pub async fn switch_merchant_id( - state: AppState, + state: SessionState, request: user_api::SwitchMerchantIdRequest, user_from_token: auth::UserFromToken, ) -> UserResponse { @@ -1196,7 +1199,7 @@ pub async fn switch_merchant_id( } pub async fn create_merchant_account( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, req: user_api::UserMerchantCreate, ) -> UserResponse<()> { @@ -1227,7 +1230,7 @@ pub async fn create_merchant_account( } pub async fn list_merchants_for_user( - state: AppState, + state: SessionState, user_from_token: auth::UserIdFromAuth, ) -> UserResponse> { let user_roles = state @@ -1260,7 +1263,7 @@ pub async fn list_merchants_for_user( } pub async fn get_user_details_in_merchant_account( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, request: user_api::GetUserRoleDetailsRequest, _req_state: ReqState, @@ -1304,7 +1307,7 @@ pub async fn get_user_details_in_merchant_account( } pub async fn list_users_for_merchant_account( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, ) -> UserResponse { let users_and_user_roles = state @@ -1352,7 +1355,7 @@ pub async fn list_users_for_merchant_account( #[cfg(feature = "email")] pub async fn verify_email( - state: AppState, + state: SessionState, req: user_api::VerifyEmailRequest, ) -> UserResponse { let token = req.token.clone().expose(); @@ -1411,7 +1414,7 @@ pub async fn verify_email( #[cfg(feature = "email")] pub async fn verify_email_token_only_flow( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, req: user_api::VerifyEmailRequest, ) -> UserResponse> { @@ -1469,7 +1472,7 @@ pub async fn verify_email_token_only_flow( #[cfg(feature = "email")] pub async fn send_verification_mail( - state: AppState, + state: SessionState, req: user_api::SendVerifyEmailRequest, ) -> UserResponse<()> { let user_email = domain::UserEmail::try_from(req.email)?; @@ -1509,7 +1512,7 @@ pub async fn send_verification_mail( #[cfg(feature = "recon")] pub async fn verify_token( - state: AppState, + state: SessionState, req: auth::ReconUser, ) -> UserResponse { let user = state @@ -1537,7 +1540,7 @@ pub async fn verify_token( } pub async fn update_user_details( - state: AppState, + state: SessionState, user_token: auth::UserFromToken, req: user_api::UpdateUserAccountDetailsRequest, _req_state: ReqState, @@ -1582,7 +1585,7 @@ pub async fn update_user_details( #[cfg(feature = "email")] pub async fn user_from_email( - state: AppState, + state: SessionState, req: user_api::UserFromEmailRequest, ) -> UserResponse { let token = req.token.expose(); @@ -1616,7 +1619,7 @@ pub async fn user_from_email( } pub async fn begin_totp( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, ) -> UserResponse { let user_from_db: domain::UserFromStorage = state @@ -1645,7 +1648,7 @@ pub async fn begin_totp( } pub async fn reset_totp( - state: AppState, + state: SessionState, user_token: auth::UserFromToken, ) -> UserResponse { let user_from_db: domain::UserFromStorage = state @@ -1678,7 +1681,7 @@ pub async fn reset_totp( } pub async fn verify_totp( - state: AppState, + state: SessionState, user_token: auth::UserIdFromAuth, req: user_api::VerifyTotpRequest, ) -> UserResponse { @@ -1714,7 +1717,7 @@ pub async fn verify_totp( } pub async fn update_totp( - state: AppState, + state: SessionState, user_token: auth::UserIdFromAuth, req: user_api::VerifyTotpRequest, ) -> UserResponse<()> { @@ -1779,7 +1782,7 @@ pub async fn update_totp( } pub async fn generate_recovery_codes( - state: AppState, + state: SessionState, user_token: auth::UserIdFromAuth, ) -> UserResponse { if !tfa_utils::check_totp_in_redis(&state, &user_token.user_id).await? { @@ -1811,7 +1814,7 @@ pub async fn generate_recovery_codes( } pub async fn verify_recovery_code( - state: AppState, + state: SessionState, user_token: auth::UserIdFromAuth, req: user_api::VerifyRecoveryCodeRequest, ) -> UserResponse { @@ -1856,7 +1859,7 @@ pub async fn verify_recovery_code( } pub async fn terminate_two_factor_auth( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, skip_two_factor_auth: bool, ) -> UserResponse { @@ -1908,7 +1911,7 @@ pub async fn terminate_two_factor_auth( } pub async fn check_two_factor_auth_status( - state: AppState, + state: SessionState, user_token: auth::UserFromToken, ) -> UserResponse { Ok(ApplicationResponse::Json( diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index ee1693b0c9d4..364898462946 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -12,14 +12,14 @@ use router_env::logger; use crate::services::email::types as email_types; use crate::{ core::errors::{UserErrors, UserResponse, UserResult}, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services::{authentication::UserFromToken, ApplicationResponse}, types::domain::{self, user::dashboard_metadata as types, MerchantKeyStore}, utils::user::dashboard_metadata as utils, }; pub async fn set_metadata( - state: AppState, + state: SessionState, user: UserFromToken, request: api::SetMetaDataRequest, _req_state: ReqState, @@ -33,7 +33,7 @@ pub async fn set_metadata( } pub async fn get_multiple_metadata( - state: AppState, + state: SessionState, user: UserFromToken, request: GetMultipleMetaDataPayload, _req_state: ReqState, @@ -230,7 +230,7 @@ fn into_response( } async fn insert_metadata( - state: &AppState, + state: &SessionState, user: UserFromToken, metadata_key: DBEnum, metadata_value: types::MetaData, @@ -571,7 +571,7 @@ async fn insert_metadata( } async fn fetch_metadata( - state: &AppState, + state: &SessionState, user: &UserFromToken, metadata_keys: Vec, ) -> UserResult> { @@ -606,7 +606,7 @@ async fn fetch_metadata( } pub async fn backfill_metadata( - state: &AppState, + state: &SessionState, user: &UserFromToken, key: &DBEnum, ) -> UserResult> { @@ -705,7 +705,7 @@ pub async fn backfill_metadata( } pub async fn get_merchant_connector_account_by_name( - state: &AppState, + state: &SessionState, merchant_id: &str, connector_name: &str, key_store: &MerchantKeyStore, diff --git a/crates/router/src/core/user/sample_data.rs b/crates/router/src/core/user/sample_data.rs index e1bde652305f..bab620620fa6 100644 --- a/crates/router/src/core/user/sample_data.rs +++ b/crates/router/src/core/user/sample_data.rs @@ -7,13 +7,13 @@ pub type SampleDataApiResponse = SampleDataResult>; use crate::{ core::errors::sample_data::SampleDataResult, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services::{authentication::UserFromToken, ApplicationResponse}, utils::user::sample_data::generate_sample_data, }; pub async fn generate_sample_data_for_user( - state: AppState, + state: SessionState, user_from_token: UserFromToken, req: SampleDataRequest, _req_state: ReqState, @@ -57,7 +57,7 @@ pub async fn generate_sample_data_for_user( } pub async fn delete_sample_data_for_user( - state: AppState, + state: SessionState, user_from_token: UserFromToken, _req: SampleDataRequest, _req_state: ReqState, diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index 0d196e1c4af6..8b636f9935b8 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -6,7 +6,7 @@ use router_env::logger; use crate::{ consts, core::errors::{StorageErrorExt, UserErrors, UserResponse}, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services::{ authentication as auth, authorization::{info, roles}, @@ -20,7 +20,7 @@ pub mod role; // TODO: To be deprecated once groups are stable pub async fn get_authorization_info_with_modules( - _state: AppState, + _state: SessionState, ) -> UserResponse { Ok(ApplicationResponse::Json( user_role_api::AuthorizationInfoResponse( @@ -33,7 +33,7 @@ pub async fn get_authorization_info_with_modules( } pub async fn get_authorization_info_with_groups( - _state: AppState, + _state: SessionState, ) -> UserResponse { Ok(ApplicationResponse::Json( user_role_api::AuthorizationInfoResponse( @@ -46,7 +46,7 @@ pub async fn get_authorization_info_with_groups( } pub async fn update_user_role( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, req: user_role_api::UpdateUserRoleRequest, _req_state: ReqState, @@ -117,7 +117,7 @@ pub async fn update_user_role( } pub async fn transfer_org_ownership( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, req: user_role_api::TransferOrgOwnershipRequest, _req_state: ReqState, @@ -169,7 +169,7 @@ pub async fn transfer_org_ownership( } pub async fn accept_invitation( - state: AppState, + state: SessionState, user_token: auth::UserFromToken, req: user_role_api::AcceptInvitationRequest, ) -> UserResponse<()> { @@ -199,7 +199,7 @@ pub async fn accept_invitation( } pub async fn merchant_select( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, req: user_role_api::MerchantSelectRequest, ) -> UserResponse> { @@ -253,7 +253,7 @@ pub async fn merchant_select( } pub async fn merchant_select_token_only_flow( - state: AppState, + state: SessionState, user_token: auth::UserFromSinglePurposeToken, req: user_role_api::MerchantSelectRequest, ) -> UserResponse> { @@ -303,7 +303,7 @@ pub async fn merchant_select_token_only_flow( } pub async fn delete_user_role( - state: AppState, + state: SessionState, user_from_token: auth::UserFromToken, request: user_role_api::DeleteUserRoleRequest, _req_state: ReqState, diff --git a/crates/router/src/core/user_role/role.rs b/crates/router/src/core/user_role/role.rs index 504d5dd632be..386156f58e74 100644 --- a/crates/router/src/core/user_role/role.rs +++ b/crates/router/src/core/user_role/role.rs @@ -7,7 +7,7 @@ use error_stack::{report, ResultExt}; use crate::{ consts, core::errors::{StorageErrorExt, UserErrors, UserResponse}, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, services::{ authentication::{blacklist, UserFromToken}, authorization::roles::{self, predefined_roles::PREDEFINED_ROLES}, @@ -18,7 +18,7 @@ use crate::{ }; pub async fn get_role_from_token_with_permissions( - state: AppState, + state: SessionState, user_from_token: UserFromToken, ) -> UserResponse { let role_info = user_from_token @@ -38,7 +38,7 @@ pub async fn get_role_from_token_with_permissions( } pub async fn get_role_from_token_with_groups( - state: AppState, + state: SessionState, user_from_token: UserFromToken, ) -> UserResponse { let role_info = user_from_token @@ -54,7 +54,7 @@ pub async fn get_role_from_token_with_groups( } pub async fn create_role( - state: AppState, + state: SessionState, user_from_token: UserFromToken, req: role_api::CreateRoleRequest, _req_state: ReqState, @@ -107,7 +107,7 @@ pub async fn create_role( // TODO: To be deprecated once groups are stable pub async fn list_invitable_roles_with_permissions( - state: AppState, + state: SessionState, user_from_token: UserFromToken, ) -> UserResponse { let predefined_roles_map = PREDEFINED_ROLES @@ -156,7 +156,7 @@ pub async fn list_invitable_roles_with_permissions( } pub async fn list_invitable_roles_with_groups( - state: AppState, + state: SessionState, user_from_token: UserFromToken, ) -> UserResponse { let predefined_roles_map = PREDEFINED_ROLES @@ -198,7 +198,7 @@ pub async fn list_invitable_roles_with_groups( // TODO: To be deprecated once groups are stable pub async fn get_role_with_permissions( - state: AppState, + state: SessionState, user_from_token: UserFromToken, role: role_api::GetRoleRequest, ) -> UserResponse { @@ -232,7 +232,7 @@ pub async fn get_role_with_permissions( } pub async fn get_role_with_groups( - state: AppState, + state: SessionState, user_from_token: UserFromToken, role: role_api::GetRoleRequest, ) -> UserResponse { @@ -260,7 +260,7 @@ pub async fn get_role_with_groups( } pub async fn update_role( - state: AppState, + state: SessionState, user_from_token: UserFromToken, req: role_api::UpdateRoleRequest, role_id: &str, diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index ca464cc62701..679838a6dd53 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -6,7 +6,7 @@ use api_models::payouts::PayoutVendorAccountDetails; use common_enums::{IntentStatus, RequestIncrementalAuthorization}; #[cfg(feature = "payouts")] use common_utils::{crypto::Encryptable, pii::Email}; -use common_utils::{errors::CustomResult, ext_traits::AsyncExt}; +use common_utils::{errors::CustomResult, ext_traits::AsyncExt, types::MinorUnit}; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{payment_address::PaymentAddress, router_data::ErrorResponse}; #[cfg(feature = "payouts")] @@ -25,7 +25,7 @@ use crate::{ consts, core::errors::{self, RouterResult, StorageErrorExt}, db::StorageInterface, - routes::AppState, + routes::SessionState, types::{ self, domain, storage::{self, enums}, @@ -45,7 +45,7 @@ const IRRELEVANT_ATTEMPT_ID_IN_DISPUTE_FLOW: &str = "irrelevant_attempt_id_in_di #[cfg(feature = "payouts")] #[instrument(skip_all)] pub async fn get_mca_for_payout<'a>( - state: &'a AppState, + state: &'a SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -72,7 +72,7 @@ pub async fn get_mca_for_payout<'a>( #[cfg(feature = "payouts")] #[instrument(skip_all)] pub async fn construct_payout_router_data<'a, F>( - state: &'a AppState, + state: &'a SessionState, connector_name: &api_models::enums::Connector, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -214,11 +214,11 @@ pub async fn construct_payout_router_data<'a, F>( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn construct_refund_router_data<'a, F>( - state: &'a AppState, + state: &'a SessionState, connector_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - money: (i64, enums::Currency), + money: (MinorUnit, enums::Currency), payment_intent: &'a storage::PaymentIntent, payment_attempt: &storage::PaymentAttempt, refund: &'a storage::Refund, @@ -263,7 +263,7 @@ pub async fn construct_refund_router_data<'a, F>( .change_context(errors::ApiErrorResponse::InternalServerError)?; let webhook_url = Some(helpers::create_webhook_url( - &state.conf.server.base_url.clone(), + &state.base_url.clone(), &merchant_account.merchant_id, &connector_id.to_string(), )); @@ -323,9 +323,11 @@ pub async fn construct_refund_router_data<'a, F>( request: types::RefundsData { refund_id: refund.refund_id.clone(), connector_transaction_id: refund.connector_transaction_id.clone(), - refund_amount: refund.refund_amount, + refund_amount: refund.refund_amount.get_amount_as_i64(), + minor_refund_amount: refund.refund_amount, currency, - payment_amount, + payment_amount: payment_amount.get_amount_as_i64(), + minor_payment_amount: payment_amount, webhook_url, connector_metadata: payment_attempt.connector_metadata.clone(), reason: refund.refund_reason.clone(), @@ -511,7 +513,7 @@ pub fn validate_dispute_stage_and_dispute_status( #[instrument(skip_all)] pub async fn construct_accept_dispute_router_data<'a>( - state: &'a AppState, + state: &'a SessionState, payment_intent: &'a storage::PaymentIntent, payment_attempt: &storage::PaymentAttempt, merchant_account: &domain::MerchantAccount, @@ -605,7 +607,7 @@ pub async fn construct_accept_dispute_router_data<'a>( #[instrument(skip_all)] pub async fn construct_submit_evidence_router_data<'a>( - state: &'a AppState, + state: &'a SessionState, payment_intent: &'a storage::PaymentIntent, payment_attempt: &storage::PaymentAttempt, merchant_account: &domain::MerchantAccount, @@ -699,7 +701,7 @@ pub async fn construct_submit_evidence_router_data<'a>( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn construct_upload_file_router_data<'a>( - state: &'a AppState, + state: &'a SessionState, payment_intent: &'a storage::PaymentIntent, payment_attempt: &storage::PaymentAttempt, merchant_account: &domain::MerchantAccount, @@ -797,7 +799,7 @@ pub async fn construct_upload_file_router_data<'a>( #[instrument(skip_all)] pub async fn construct_defend_dispute_router_data<'a>( - state: &'a AppState, + state: &'a SessionState, payment_intent: &'a storage::PaymentIntent, payment_attempt: &storage::PaymentAttempt, merchant_account: &domain::MerchantAccount, @@ -893,7 +895,7 @@ pub async fn construct_defend_dispute_router_data<'a>( #[instrument(skip_all)] pub async fn construct_retrieve_file_router_data<'a>( - state: &'a AppState, + state: &'a SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, file_metadata: &diesel_models::file::FileMetadata, diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index 802a4ec3c638..b6e71b2828fa 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -4,12 +4,12 @@ use common_utils::{errors::CustomResult, request::RequestContent}; use error_stack::ResultExt; use masking::ExposeInterface; -use crate::{core::errors, headers, logger, routes::AppState, services}; +use crate::{core::errors, headers, logger, routes::SessionState, services}; const APPLEPAY_INTERNAL_MERCHANT_NAME: &str = "Applepay_merchant"; pub async fn verify_merchant_creds_for_applepay( - state: AppState, + state: SessionState, body: verifications::ApplepayMerchantVerificationRequest, merchant_id: String, ) -> CustomResult, errors::ApiErrorResponse> @@ -83,7 +83,7 @@ pub async fn verify_merchant_creds_for_applepay( } pub async fn get_verified_apple_domains_with_mid_mca_id( - state: AppState, + state: SessionState, merchant_id: String, merchant_connector_id: String, ) -> CustomResult< diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index 56960d3cb480..7518091ee871 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -4,13 +4,13 @@ use error_stack::{Report, ResultExt}; use crate::{ core::errors::{self, utils::StorageErrorExt}, logger, - routes::AppState, + routes::SessionState, types, types::storage, }; pub async fn check_existence_and_add_domain_to_db( - state: &AppState, + state: &SessionState, merchant_id: String, merchant_connector_id: String, domain_from_req: Vec, diff --git a/crates/router/src/core/verify_connector.rs b/crates/router/src/core/verify_connector.rs index 8a8b0a14f7db..f42f9336a18b 100644 --- a/crates/router/src/core/verify_connector.rs +++ b/crates/router/src/core/verify_connector.rs @@ -13,11 +13,11 @@ use crate::{ transformers::ForeignInto, }, utils::verify_connector as utils, - AppState, + SessionState, }; pub async fn verify_connector_credentials( - state: AppState, + state: SessionState, req: VerifyConnectorRequest, ) -> errors::RouterResponse<()> { let boxed_connector = api::ConnectorData::get_connector_by_name( diff --git a/crates/router/src/core/webhooks.rs b/crates/router/src/core/webhooks.rs index 7e29036c40fe..7962e86e1228 100644 --- a/crates/router/src/core/webhooks.rs +++ b/crates/router/src/core/webhooks.rs @@ -39,10 +39,10 @@ use crate::{ }, logger, routes::{ - app::{AppStateInfo, ReqState}, + app::{ReqState, SessionStateInfo}, lock_utils, metrics::request::add_attributes, - AppState, + SessionState, }, services::{self, authentication as auth}, types::{ @@ -59,7 +59,7 @@ const OUTGOING_WEBHOOK_TIMEOUT_SECS: u64 = 5; const MERCHANT_ID: &str = "merchant_id"; pub async fn payments_incoming_webhook_flow( - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, @@ -204,7 +204,7 @@ pub async fn payments_incoming_webhook_flow( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn refunds_incoming_webhook_flow( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, key_store: domain::MerchantKeyStore, @@ -302,7 +302,7 @@ pub async fn refunds_incoming_webhook_flow( } pub async fn get_payment_attempt_from_object_reference_id( - state: &AppState, + state: &SessionState, object_reference_id: webhooks::ObjectReferenceId, merchant_account: &domain::MerchantAccount, ) -> CustomResult< @@ -342,7 +342,7 @@ pub async fn get_payment_attempt_from_object_reference_id( #[allow(clippy::too_many_arguments)] pub async fn get_or_update_dispute_object( - state: AppState, + state: SessionState, option_dispute: Option, dispute_details: api::disputes::DisputePayload, merchant_id: &str, @@ -418,7 +418,7 @@ pub async fn get_or_update_dispute_object( #[allow(clippy::too_many_arguments)] pub async fn external_authentication_incoming_webhook_flow( - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, @@ -592,7 +592,7 @@ pub async fn external_authentication_incoming_webhook_flow( } pub async fn mandates_incoming_webhook_flow( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, key_store: domain::MerchantKeyStore, @@ -680,7 +680,7 @@ pub async fn mandates_incoming_webhook_flow( #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub(crate) async fn frm_incoming_webhook_flow( - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, @@ -792,7 +792,7 @@ pub(crate) async fn frm_incoming_webhook_flow( #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub async fn disputes_incoming_webhook_flow( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, key_store: domain::MerchantKeyStore, @@ -862,7 +862,7 @@ pub async fn disputes_incoming_webhook_flow( } async fn bank_transfer_webhook_flow( - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, @@ -951,7 +951,7 @@ async fn bank_transfer_webhook_flow( #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub(crate) async fn create_event_and_trigger_outgoing_webhook( - state: AppState, + state: SessionState, merchant_account: domain::MerchantAccount, business_profile: diesel_models::business_profile::BusinessProfile, merchant_key_store: &domain::MerchantKeyStore, @@ -1070,7 +1070,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( // may have an actix arbiter tokio::spawn( async move { - trigger_webhook_and_raise_event( + Box::pin(trigger_webhook_and_raise_event( state, business_profile, &cloned_key_store, @@ -1079,7 +1079,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( delivery_attempt, Some(content), process_tracker, - ) + )) .await; } .in_current_span(), @@ -1091,7 +1091,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( #[allow(clippy::too_many_arguments)] #[instrument(skip_all)] pub(crate) async fn trigger_webhook_and_raise_event( - state: AppState, + state: SessionState, business_profile: diesel_models::business_profile::BusinessProfile, merchant_key_store: &domain::MerchantKeyStore, event: domain::Event, @@ -1123,7 +1123,7 @@ pub(crate) async fn trigger_webhook_and_raise_event( } async fn trigger_webhook_to_merchant( - state: AppState, + state: SessionState, business_profile: diesel_models::business_profile::BusinessProfile, merchant_key_store: &domain::MerchantKeyStore, event: domain::Event, @@ -1189,7 +1189,7 @@ async fn trigger_webhook_to_merchant( logger::debug!(outgoing_webhook_response=?response); let update_event_if_client_error = - |state: AppState, + |state: SessionState, merchant_key_store: domain::MerchantKeyStore, merchant_id: String, event_id: String, @@ -1234,7 +1234,7 @@ async fn trigger_webhook_to_merchant( }; let api_client_error_handler = - |state: AppState, + |state: SessionState, merchant_key_store: domain::MerchantKeyStore, merchant_id: String, event_id: String, @@ -1261,7 +1261,7 @@ async fn trigger_webhook_to_merchant( Ok::<_, error_stack::Report>(()) }; - let update_event_in_storage = |state: AppState, + let update_event_in_storage = |state: SessionState, merchant_key_store: domain::MerchantKeyStore, merchant_id: String, event_id: String, @@ -1339,7 +1339,7 @@ async fn trigger_webhook_to_merchant( ) }; let success_response_handler = - |state: AppState, + |state: SessionState, merchant_id: String, process_tracker: Option, business_status: &'static str| async move { @@ -1521,7 +1521,7 @@ async fn trigger_webhook_to_merchant( } fn raise_webhooks_analytics_event( - state: AppState, + state: SessionState, trigger_webhook_result: CustomResult<(), errors::WebhooksFlowError>, content: Option, merchant_id: String, @@ -1558,7 +1558,7 @@ fn raise_webhooks_analytics_event( #[allow(clippy::too_many_arguments)] pub async fn webhooks_wrapper( flow: &impl router_env::types::FlowMetric, - state: AppState, + state: SessionState, req_state: ReqState, req: &actix_web::HttpRequest, merchant_account: domain::MerchantAccount, @@ -1622,7 +1622,7 @@ pub async fn webhooks_wrapper( #[instrument(skip_all)] pub async fn webhooks_core( - state: AppState, + state: SessionState, req_state: ReqState, req: &actix_web::HttpRequest, merchant_account: domain::MerchantAccount, @@ -2019,7 +2019,7 @@ pub async fn get_payment_id( } fn get_connector_by_connector_name( - state: &AppState, + state: &SessionState, connector_name: &str, merchant_connector_id: Option, ) -> CustomResult<(&'static (dyn api::Connector + Sync), String), errors::ApiErrorResponse> { @@ -2067,7 +2067,7 @@ fn get_connector_by_connector_name( /// This function fetches the merchant connector account ( if the url used is /{merchant_connector_id}) /// if merchant connector id is not passed in the request, then this will return None for mca async fn fetch_optional_mca_and_connector( - state: &AppState, + state: &SessionState, merchant_account: &domain::MerchantAccount, connector_name_or_mca_id: &str, key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/webhooks/webhook_events.rs b/crates/router/src/core/webhooks/webhook_events.rs index 6db214c2b054..2cb16a53b3a8 100644 --- a/crates/router/src/core/webhooks/webhook_events.rs +++ b/crates/router/src/core/webhooks/webhook_events.rs @@ -4,7 +4,7 @@ use router_env::{instrument, tracing}; use crate::{ core::errors::{self, RouterResponse, StorageErrorExt}, - routes::AppState, + routes::SessionState, services::ApplicationResponse, types::{api, domain, storage, transformers::ForeignTryFrom}, utils::{OptionExt, StringExt}, @@ -20,7 +20,7 @@ enum MerchantAccountOrBusinessProfile { #[instrument(skip(state))] pub async fn list_initial_delivery_attempts( - state: AppState, + state: SessionState, merchant_id_or_profile_id: String, constraints: api::webhook_events::EventListConstraints, ) -> RouterResponse> { @@ -108,7 +108,7 @@ pub async fn list_initial_delivery_attempts( #[instrument(skip(state))] pub async fn list_delivery_attempts( - state: AppState, + state: SessionState, merchant_id_or_profile_id: String, initial_attempt_id: String, ) -> RouterResponse> { @@ -157,7 +157,7 @@ pub async fn list_delivery_attempts( #[instrument(skip(state))] pub async fn retry_delivery_attempt( - state: AppState, + state: SessionState, merchant_id_or_profile_id: String, event_id: String, ) -> RouterResponse { @@ -231,7 +231,7 @@ pub async fn retry_delivery_attempt( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse webhook event request information")?; - super::trigger_webhook_and_raise_event( + Box::pin(super::trigger_webhook_and_raise_event( state.clone(), business_profile, &key_store, @@ -240,7 +240,7 @@ pub async fn retry_delivery_attempt( delivery_attempt, None, None, - ) + )) .await; let updated_event = store @@ -254,7 +254,7 @@ pub async fn retry_delivery_attempt( } async fn determine_identifier_and_get_key_store( - state: AppState, + state: SessionState, merchant_id_or_profile_id: String, ) -> errors::RouterResult<(MerchantAccountOrBusinessProfile, domain::MerchantKeyStore)> { let store = state.store.as_ref(); diff --git a/crates/router/src/db/api_keys.rs b/crates/router/src/db/api_keys.rs index 5d2f1c57a2a1..ea63a8817043 100644 --- a/crates/router/src/db/api_keys.rs +++ b/crates/router/src/db/api_keys.rs @@ -373,7 +373,7 @@ impl ApiKeyInterface for MockDb { #[cfg(test)] mod tests { use storage_impl::redis::{ - cache::{self, CacheKind, ACCOUNTS_CACHE}, + cache::{self, CacheKey, CacheKind, ACCOUNTS_CACHE}, kv_store::RedisConnInterface, pub_sub::PubSubInterface, }; @@ -525,15 +525,12 @@ mod tests { .await .unwrap(); - assert!( - ACCOUNTS_CACHE - .get_val::(&format!( - "{}_{}", - merchant_id, - hashed_api_key.into_inner() - ),) - .await - .is_none() - ) + assert!(ACCOUNTS_CACHE + .get_val::(CacheKey { + key: format!("{}_{}", merchant_id, hashed_api_key.into_inner()), + prefix: String::default(), + },) + .await + .is_none()) } } diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 0e3b365ae075..910ad085f631 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1098,8 +1098,9 @@ impl QueueInterface for KafkaStore { entry_id: &RedisEntryId, fields: Vec<(&str, String)>, ) -> CustomResult<(), RedisError> { + let stream_name = format!("{}_{}", &self.tenant_id.0, stream); self.diesel_store - .stream_append_entry(stream, entry_id, fields) + .stream_append_entry(&stream_name, entry_id, fields) .await } diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index be37fd9fd6fd..aea37b517be6 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -430,9 +430,6 @@ impl MerchantConnectorAccountInterface for Store { cache::CacheKind::CGraph( format!("cgraph_{}_{_profile_id}", _merchant_id).into(), ), - cache::CacheKind::PmFiltersCGraph( - format!("pm_filters_cgraph_{}_{_profile_id}", _merchant_id).into(), - ), ], update_call, ) @@ -490,9 +487,6 @@ impl MerchantConnectorAccountInterface for Store { cache::CacheKind::CGraph( format!("cgraph_{}_{_profile_id}", mca.merchant_id).into(), ), - cache::CacheKind::PmFiltersCGraph( - format!("pm_filters_cgraph_{}_{_profile_id}", mca.merchant_id).into(), - ), ], delete_call, ) @@ -775,7 +769,7 @@ mod merchant_connector_account_cache_tests { use error_stack::ResultExt; use masking::PeekInterface; use storage_impl::redis::{ - cache::{self, CacheKind, ACCOUNTS_CACHE}, + cache::{self, CacheKey, CacheKind, ACCOUNTS_CACHE}, kv_store::RedisConnInterface, pub_sub::PubSubInterface, }; @@ -908,10 +902,10 @@ mod merchant_connector_account_cache_tests { .unwrap(); assert!(ACCOUNTS_CACHE - .get_val::(&format!( - "{}_{}", - merchant_id, connector_label - ),) + .get_val::(CacheKey { + key: format!("{}_{}", merchant_id, connector_label), + prefix: String::default(), + },) .await .is_none()) } diff --git a/crates/router/src/db/refund.rs b/crates/router/src/db/refund.rs index 1b1bfed38714..c5edd863caf7 100644 --- a/crates/router/src/db/refund.rs +++ b/crates/router/src/db/refund.rs @@ -1,6 +1,8 @@ #[cfg(feature = "olap")] use std::collections::HashSet; +#[cfg(feature = "olap")] +use common_utils::types::MinorUnit; use diesel_models::{errors::DatabaseError, refund::RefundUpdateInternal}; use error_stack::ResultExt; @@ -1024,8 +1026,10 @@ impl RefundInterface for MockDb { .amount_filter .as_ref() .map_or(true, |amount| { - refund.refund_amount >= amount.start_amount.unwrap_or(i64::MIN) - && refund.refund_amount <= amount.end_amount.unwrap_or(i64::MAX) + refund.refund_amount + >= MinorUnit::new(amount.start_amount.unwrap_or(i64::MIN)) + && refund.refund_amount + <= MinorUnit::new(amount.end_amount.unwrap_or(i64::MAX)) }) }) .filter(|refund| { @@ -1173,8 +1177,10 @@ impl RefundInterface for MockDb { .amount_filter .as_ref() .map_or(true, |amount| { - refund.refund_amount >= amount.start_amount.unwrap_or(i64::MIN) - && refund.refund_amount <= amount.end_amount.unwrap_or(i64::MAX) + refund.refund_amount + >= MinorUnit::new(amount.start_amount.unwrap_or(i64::MIN)) + && refund.refund_amount + <= MinorUnit::new(amount.end_amount.unwrap_or(i64::MAX)) }) }) .filter(|refund| { diff --git a/crates/router/src/events.rs b/crates/router/src/events.rs index 4bca58248351..e1ecd09804cf 100644 --- a/crates/router/src/events.rs +++ b/crates/router/src/events.rs @@ -30,6 +30,7 @@ pub enum EventType { AuditEvent, #[cfg(feature = "payouts")] Payout, + Consolidated, } #[derive(Debug, Default, Deserialize, Clone)] diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 2e9ecc13dcb1..7344b85d150d 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -32,7 +32,7 @@ use actix_web::{ use http::StatusCode; use hyperswitch_interfaces::secrets_interface::secret_state::SecuredSecret; use router_env::tracing::Instrument; -use routes::AppState; +use routes::{AppState, SessionState}; use storage_impl::errors::ApplicationResult; use tokio::sync::{mpsc, oneshot}; diff --git a/crates/router/src/routes.rs b/crates/router/src/routes.rs index cd216cb0e81b..9f13635ca901 100644 --- a/crates/router/src/routes.rs +++ b/crates/router/src/routes.rs @@ -60,7 +60,8 @@ pub use self::app::Recon; pub use self::app::{ ApiKeys, AppState, BusinessProfile, Cache, Cards, Configs, ConnectorOnboarding, Customers, Disputes, EphemeralKey, Files, Gsm, Health, Mandates, MerchantAccount, - MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll, Refunds, User, Webhooks, + MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll, Refunds, SessionState, + User, Webhooks, }; #[cfg(feature = "olap")] pub use self::app::{Blocklist, Routing, Verify, WebhookEvents}; diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 39b8ed368b76..457e232ebd17 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use actix_web::{web, Scope}; #[cfg(all(feature = "business_profile_routing", feature = "olap"))] @@ -14,7 +14,7 @@ use hyperswitch_interfaces::{ }; use router_env::tracing_actix_web::RequestId; use scheduler::SchedulerInterface; -use storage_impl::MockDb; +use storage_impl::{config::TenantConfig, redis::RedisStore, MockDb}; use tokio::sync::oneshot; #[cfg(feature = "olap")] @@ -43,7 +43,8 @@ use super::{ephemeral_key::*, webhooks::*}; use super::{pm_auth, poll::retrieve_poll_status}; #[cfg(feature = "olap")] pub use crate::analytics::opensearch::OpenSearchClient; -use crate::configs::secrets_transformers; +#[cfg(feature = "olap")] +use crate::analytics::AnalyticsProvider; #[cfg(all(feature = "frm", feature = "oltp"))] use crate::routes::fraud_check as frm_routes; #[cfg(all(feature = "recon", feature = "olap"))] @@ -56,7 +57,11 @@ pub use crate::{ db::{StorageImpl, StorageInterface}, events::EventsHandler, routes::cards_info::card_iin_info, - services::get_store, + services::{get_cache_store, get_store}, +}; +use crate::{ + configs::{secrets_transformers, Settings}, + db::kafka_store::{KafkaStore, TenantID}, }; #[derive(Clone)] @@ -64,33 +69,87 @@ pub struct ReqState { pub event_context: events::EventContext, } +#[derive(Clone)] +pub struct SessionState { + pub store: Box, + pub conf: Arc>, + pub api_client: Box, + pub event_handler: EventsHandler, + #[cfg(feature = "email")] + pub email_client: Arc, + #[cfg(feature = "olap")] + pub pool: AnalyticsProvider, + pub file_storage_client: Box, + pub request_id: Option, + pub base_url: String, + pub tenant: String, + #[cfg(feature = "olap")] + pub opensearch_client: Arc, +} +impl scheduler::SchedulerSessionState for SessionState { + fn get_db(&self) -> Box { + self.store.get_scheduler_db() + } +} +impl SessionState { + pub fn get_req_state(&self) -> ReqState { + ReqState { + event_context: events::EventContext::new(self.event_handler.clone()), + } + } +} + +pub trait SessionStateInfo { + fn conf(&self) -> settings::Settings; + fn store(&self) -> Box; + fn event_handler(&self) -> EventsHandler; + fn get_request_id(&self) -> Option; + fn add_request_id(&mut self, request_id: RequestId); +} + +impl SessionStateInfo for SessionState { + fn store(&self) -> Box { + self.store.to_owned() + } + fn conf(&self) -> settings::Settings { + self.conf.as_ref().to_owned() + } + fn event_handler(&self) -> EventsHandler { + self.event_handler.clone() + } + fn get_request_id(&self) -> Option { + self.api_client.get_request_id() + } + fn add_request_id(&mut self, request_id: RequestId) { + self.api_client.add_request_id(request_id); + self.store.add_request_id(request_id.to_string()); + self.request_id.replace(request_id); + } +} #[derive(Clone)] pub struct AppState { pub flow_name: String, - pub store: Box, + pub stores: HashMap>, pub conf: Arc>, pub event_handler: EventsHandler, #[cfg(feature = "email")] pub email_client: Arc, pub api_client: Box, #[cfg(feature = "olap")] - pub pool: crate::analytics::AnalyticsProvider, + pub pools: HashMap, #[cfg(feature = "olap")] - pub opensearch_client: OpenSearchClient, + pub opensearch_client: Arc, pub request_id: Option, pub file_storage_client: Box, pub encryption_client: Box, } - impl scheduler::SchedulerAppState for AppState { - fn get_db(&self) -> Box { - self.store.get_scheduler_db() + fn get_tenants(&self) -> Vec { + self.conf.multitenancy.get_tenant_names() } } - pub trait AppStateInfo { fn conf(&self) -> settings::Settings; - fn store(&self) -> Box; fn event_handler(&self) -> EventsHandler; #[cfg(feature = "email")] fn email_client(&self) -> Arc; @@ -104,9 +163,6 @@ impl AppStateInfo for AppState { fn conf(&self) -> settings::Settings { self.conf.as_ref().to_owned() } - fn store(&self) -> Box { - self.store.to_owned() - } #[cfg(feature = "email")] fn email_client(&self) -> Arc { self.email_client.to_owned() @@ -116,7 +172,6 @@ impl AppStateInfo for AppState { } fn add_request_id(&mut self, request_id: RequestId) { self.api_client.add_request_id(request_id); - self.store.add_request_id(request_id.to_string()); self.request_id.replace(request_id); } @@ -183,43 +238,38 @@ impl AppState { #[allow(clippy::expect_used)] #[cfg(feature = "olap")] - let opensearch_client = conf - .opensearch - .get_opensearch_client() - .await - .expect("Failed to create opensearch client"); - - let store: Box = match storage_impl { - StorageImpl::Postgresql | StorageImpl::PostgresqlTest => match &event_handler { - EventsHandler::Kafka(kafka_client) => Box::new( - crate::db::KafkaStore::new( - #[allow(clippy::expect_used)] - get_store(&conf.clone(), shut_down_signal, testable) - .await - .expect("Failed to create store"), - kafka_client.clone(), - crate::db::kafka_store::TenantID("default".to_string()), - ) - .await, - ), - EventsHandler::Logs(_) => Box::new( - #[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"), - ), - }; + let opensearch_client = Arc::new( + conf.opensearch + .get_opensearch_client() + .await + .expect("Failed to create opensearch client"), + ); #[cfg(feature = "olap")] - let pool = - crate::analytics::AnalyticsProvider::from_conf(conf.analytics.get_inner()).await; + let mut pools: HashMap = HashMap::new(); + let mut stores = HashMap::new(); + #[allow(clippy::expect_used)] + let cache_store = get_cache_store(&conf.clone(), shut_down_signal, testable) + .await + .expect("Failed to create store"); + for (tenant_name, tenant) in conf.clone().multitenancy.get_tenants() { + let store: Box = Self::get_store_interface( + &storage_impl, + &event_handler, + &conf, + tenant, + Arc::clone(&cache_store), + testable, + ) + .await; + stores.insert(tenant_name.clone(), store); + #[cfg(feature = "olap")] + let pool = + AnalyticsProvider::from_conf(conf.analytics.get_inner(), tenant_name.as_str()) + .await; + #[cfg(feature = "olap")] + pools.insert(tenant_name.clone(), pool); + } #[cfg(feature = "email")] let email_client = Arc::new(create_email_client(&conf).await); @@ -228,14 +278,14 @@ impl AppState { Self { flow_name: String::from("default"), - store, + stores, conf: Arc::new(conf), #[cfg(feature = "email")] email_client, api_client, event_handler, #[cfg(feature = "olap")] - pool, + pools, #[cfg(feature = "olap")] opensearch_client, request_id: None, @@ -246,6 +296,43 @@ impl AppState { .await } + async fn get_store_interface( + storage_impl: &StorageImpl, + event_handler: &EventsHandler, + conf: &Settings, + tenant: &settings::Tenant, + cache_store: Arc, + testable: bool, + ) -> Box { + match storage_impl { + StorageImpl::Postgresql | StorageImpl::PostgresqlTest => match event_handler { + EventsHandler::Kafka(kafka_client) => Box::new( + KafkaStore::new( + #[allow(clippy::expect_used)] + get_store(&conf.clone(), tenant, Arc::clone(&cache_store), testable) + .await + .expect("Failed to create store"), + kafka_client.clone(), + TenantID(tenant.get_schema().to_string()), + ) + .await, + ), + EventsHandler::Logs(_) => Box::new( + #[allow(clippy::expect_used)] + get_store(conf, tenant, Arc::clone(&cache_store), 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"), + ), + } + } + pub async fn new( conf: settings::Settings, shut_down_signal: oneshot::Sender<()>, @@ -265,6 +352,34 @@ impl AppState { event_context: events::EventContext::new(self.event_handler.clone()), } } + pub fn get_session_state(self: Arc, tenant: &str, err: F) -> Result + where + F: FnOnce() -> E + Copy, + { + Ok(SessionState { + store: self.stores.get(tenant).ok_or_else(err)?.clone(), + conf: Arc::clone(&self.conf), + api_client: self.api_client.clone(), + event_handler: self.event_handler.clone(), + #[cfg(feature = "olap")] + pool: self.pools.get(tenant).ok_or_else(err)?.clone(), + file_storage_client: self.file_storage_client.clone(), + request_id: self.request_id, + base_url: self + .conf + .multitenancy + .get_tenant(tenant) + .ok_or_else(err)? + .clone() + .base_url + .clone(), + tenant: tenant.to_string().clone(), + #[cfg(feature = "email")] + email_client: Arc::clone(&self.email_client), + #[cfg(feature = "olap")] + opensearch_client: Arc::clone(&self.opensearch_client), + }) + } } pub struct Health; diff --git a/crates/router/src/routes/cards_info.rs b/crates/router/src/routes/cards_info.rs index 4d65fc720368..ca59e072a900 100644 --- a/crates/router/src/routes/cards_info.rs +++ b/crates/router/src/routes/cards_info.rs @@ -41,7 +41,7 @@ pub async fn card_iin_info( Err(e) => return api::log_and_return_error_response(e), }; - api::server_wrap( + Box::pin(api::server_wrap( Flow::CardsInfo, state, &req, @@ -49,6 +49,6 @@ pub async fn card_iin_info( |state, auth, req, _| cards_info::retrieve_card_info(state, auth.merchant_account, req), &*auth, api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index b97cfc3fa0d8..87366cf5c6d9 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -47,13 +47,13 @@ pub async fn customers_retrieve( let auth = if auth::is_jwt_auth(req.headers()) { Box::new(auth::JWTAuth(Permission::CustomerRead)) } else { - match auth::is_ephemeral_auth(req.headers(), &*state.store, &payload.customer_id).await { + match auth::is_ephemeral_auth(req.headers(), &payload.customer_id) { Ok(auth) => auth, Err(err) => return api::log_and_return_error_response(err), } }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -61,7 +61,7 @@ pub async fn customers_retrieve( |state, auth, req, _| retrieve_customer(state, auth.merchant_account, auth.key_store, req), &*auth, api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/disputes.rs b/crates/router/src/routes/disputes.rs index a4a6c7507a4f..4c7199f2f29d 100644 --- a/crates/router/src/routes/disputes.rs +++ b/crates/router/src/routes/disputes.rs @@ -38,7 +38,7 @@ pub async fn retrieve_dispute( let dispute_id = dispute_types::DisputeId { dispute_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -50,7 +50,7 @@ pub async fn retrieve_dispute( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Disputes - List Disputes @@ -85,7 +85,7 @@ pub async fn retrieve_disputes_list( ) -> HttpResponse { let flow = Flow::DisputesList; let payload = payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -97,7 +97,7 @@ pub async fn retrieve_disputes_list( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } /// Disputes - Accept Dispute diff --git a/crates/router/src/routes/dummy_connector/core.rs b/crates/router/src/routes/dummy_connector/core.rs index e8f84b3047eb..513a586d193a 100644 --- a/crates/router/src/routes/dummy_connector/core.rs +++ b/crates/router/src/routes/dummy_connector/core.rs @@ -1,4 +1,4 @@ -use app::AppState; +use app::SessionState; use common_utils::generate_id_with_default_len; use error_stack::ResultExt; @@ -10,7 +10,7 @@ use crate::{ }; pub async fn payment( - state: AppState, + state: SessionState, req: types::DummyConnectorPaymentRequest, ) -> types::DummyConnectorResponse { utils::tokio_mock_sleep( @@ -41,7 +41,7 @@ pub async fn payment( } pub async fn payment_data( - state: AppState, + state: SessionState, req: types::DummyConnectorPaymentRetrieveRequest, ) -> types::DummyConnectorResponse { utils::tokio_mock_sleep( @@ -55,7 +55,7 @@ pub async fn payment_data( } pub async fn payment_authorize( - state: AppState, + state: SessionState, req: types::DummyConnectorPaymentConfirmRequest, ) -> types::DummyConnectorResponse { let payment_data = utils::get_payment_data_by_attempt_id(&state, req.attempt_id.clone()).await; @@ -64,7 +64,7 @@ pub async fn payment_authorize( if let Ok(payment_data_inner) = payment_data { let return_url = format!( "{}/dummy-connector/complete/{}", - state.conf.server.base_url, req.attempt_id + state.base_url, req.attempt_id ); Ok(api::ApplicationResponse::FileData(( utils::get_authorize_page(payment_data_inner, return_url, dummy_connector_conf) @@ -83,7 +83,7 @@ pub async fn payment_authorize( } pub async fn payment_complete( - state: AppState, + state: SessionState, req: types::DummyConnectorPaymentCompleteRequest, ) -> types::DummyConnectorResponse<()> { utils::tokio_mock_sleep( @@ -145,7 +145,7 @@ pub async fn payment_complete( } pub async fn refund_payment( - state: AppState, + state: SessionState, req: types::DummyConnectorRefundRequest, ) -> types::DummyConnectorResponse { utils::tokio_mock_sleep( @@ -197,7 +197,7 @@ pub async fn refund_payment( } pub async fn refund_data( - state: AppState, + state: SessionState, req: types::DummyConnectorRefundRetrieveRequest, ) -> types::DummyConnectorResponse { let refund_id = req.refund_id; diff --git a/crates/router/src/routes/dummy_connector/utils.rs b/crates/router/src/routes/dummy_connector/utils.rs index 8bba182ad0a5..6211c3abf800 100644 --- a/crates/router/src/routes/dummy_connector/utils.rs +++ b/crates/router/src/routes/dummy_connector/utils.rs @@ -11,7 +11,7 @@ use super::{ consts, errors, types::{self, GetPaymentMethodDetails}, }; -use crate::{configs::settings, routes::AppState}; +use crate::{configs::settings, routes::SessionState}; pub async fn tokio_mock_sleep(delay: u64, tolerance: u64) { let mut rng = rand::thread_rng(); @@ -26,7 +26,7 @@ pub async fn tokio_mock_sleep(delay: u64, tolerance: u64) { } pub async fn store_data_in_redis( - state: &AppState, + state: &SessionState, key: String, data: impl serde::Serialize + Debug, ttl: i64, @@ -46,7 +46,7 @@ pub async fn store_data_in_redis( } pub async fn get_payment_data_from_payment_id( - state: &AppState, + state: &SessionState, payment_id: String, ) -> types::DummyConnectorResult { let redis_conn = state @@ -65,7 +65,7 @@ pub async fn get_payment_data_from_payment_id( } pub async fn get_payment_data_by_attempt_id( - state: &AppState, + state: &SessionState, attempt_id: String, ) -> types::DummyConnectorResult { let redis_conn = state @@ -336,12 +336,12 @@ impl ProcessPaymentAttempt for types::DummyConnectorPaymentMethodData { impl types::DummyConnectorPaymentData { pub fn process_payment_attempt( - state: &AppState, + state: &SessionState, payment_attempt: types::DummyConnectorPaymentAttempt, ) -> types::DummyConnectorResult { let redirect_url = format!( "{}/dummy-connector/authorize/{}", - state.conf.server.base_url, payment_attempt.attempt_id + state.base_url, payment_attempt.attempt_id ); payment_attempt .clone() diff --git a/crates/router/src/routes/health.rs b/crates/router/src/routes/health.rs index 7d35a91a31eb..721dbd6a78dd 100644 --- a/crates/router/src/routes/health.rs +++ b/crates/router/src/routes/health.rs @@ -40,7 +40,9 @@ pub async fn deep_health_check( .await } -async fn deep_health_check_func(state: app::AppState) -> RouterResponse { +async fn deep_health_check_func( + state: app::SessionState, +) -> RouterResponse { logger::info!("Deep health check was called"); logger::debug!("Database health check begin"); diff --git a/crates/router/src/routes/mandates.rs b/crates/router/src/routes/mandates.rs index 365f9a432483..c2ed58ae40b4 100644 --- a/crates/router/src/routes/mandates.rs +++ b/crates/router/src/routes/mandates.rs @@ -36,7 +36,7 @@ pub async fn get_mandate( let mandate_id = mandates::MandateId { mandate_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -46,7 +46,7 @@ pub async fn get_mandate( }, &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Mandates - Revoke Mandate diff --git a/crates/router/src/routes/payment_link.rs b/crates/router/src/routes/payment_link.rs index 7742f6c1c0e0..f375a18ab614 100644 --- a/crates/router/src/routes/payment_link.rs +++ b/crates/router/src/routes/payment_link.rs @@ -112,7 +112,7 @@ pub async fn payments_link_list( ) -> impl Responder { let flow = Flow::PaymentLinkList; let payload = payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -120,7 +120,7 @@ pub async fn payments_link_list( |state, auth, payload, _| list_payment_link(state, auth.merchant_account, payload), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 96daf95d1156..e36a8fc3ea51 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -5,7 +5,7 @@ use error_stack::ResultExt; use router_env::{instrument, logger, tracing, Flow}; use time::PrimitiveDateTime; -use super::app::AppState; +use super::app::{AppState, SessionState}; use crate::{ core::{api_locking, errors, payment_methods::cards}, services::{api, authentication as auth, authorization::permissions::Permission}, @@ -139,11 +139,10 @@ pub async fn list_customer_payment_method_api( let payload = query_payload.into_inner(); let customer_id = customer_id.into_inner().0; - let ephemeral_auth = - match auth::is_ephemeral_auth(req.headers(), &*state.store, &customer_id).await { - Ok(auth) => auth, - Err(err) => return api::log_and_return_error_response(err), - }; + let ephemeral_auth = match auth::is_ephemeral_auth(req.headers(), &customer_id) { + Ok(auth) => auth, + Err(err) => return api::log_and_return_error_response(err), + }; Box::pin(api::server_wrap( flow, state, @@ -343,28 +342,28 @@ pub async fn default_payment_method_set_api( ) -> HttpResponse { let flow = Flow::DefaultPaymentMethodsSet; let payload = path.into_inner(); - let customer_id = payload.clone().customer_id; + let pc = payload.clone(); + let customer_id = &pc.customer_id; - let ephemeral_auth = - match auth::is_ephemeral_auth(req.headers(), &*state.store, &customer_id).await { - Ok(auth) => auth, - Err(err) => return api::log_and_return_error_response(err), - }; - let db = &*state.store.clone(); + let ephemeral_auth = match auth::is_ephemeral_auth(req.headers(), customer_id) { + Ok(auth) => auth, + Err(err) => return api::log_and_return_error_response(err), + }; Box::pin(api::server_wrap( flow, state, &req, payload, - |_state, auth: auth::AuthenticationData, default_payment_method, _| { + |state, auth: auth::AuthenticationData, default_payment_method, _| async move { cards::set_default_payment_method( - db, + &*state.clone().store, auth.merchant_account.merchant_id, auth.key_store, - &customer_id, + customer_id, default_payment_method.payment_method_id, auth.merchant_account.storage_scheme, ) + .await }, &*ephemeral_auth, api_locking::LockAction::NotApplicable, @@ -416,7 +415,7 @@ impl ParentPaymentMethodToken { &self, intent_created_at: Option, token: PaymentTokenData, - state: &AppState, + state: &SessionState, ) -> CustomResult<(), errors::ApiErrorResponse> { let token_json_str = token .encode_to_string_of_json() @@ -452,7 +451,7 @@ impl ParentPaymentMethodToken { .contains(&status) } - pub async fn delete(&self, state: &AppState) -> CustomResult<(), errors::ApiErrorResponse> { + pub async fn delete(&self, state: &SessionState) -> CustomResult<(), errors::ApiErrorResponse> { let redis_conn = state .store .get_redis_conn() diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs index f28cad89004f..a89410a2b0f9 100644 --- a/crates/router/src/routes/payments.rs +++ b/crates/router/src/routes/payments.rs @@ -681,6 +681,7 @@ pub async fn payments_redirect_response( auth.merchant_account, auth.key_store, req, + ) }, &auth::MerchantIdAuth(merchant_id), @@ -742,6 +743,7 @@ pub async fn payments_redirect_response_with_creds_identifier( auth.merchant_account, auth.key_store, req, + ) }, &auth::MerchantIdAuth(merchant_id), @@ -786,6 +788,7 @@ pub async fn payments_complete_authorize_redirect( auth.merchant_account, auth.key_store, req, + ) }, &auth::MerchantIdAuth(merchant_id), @@ -949,7 +952,7 @@ pub async fn payments_list( ) -> impl Responder { let flow = Flow::PaymentsList; let payload = payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -961,7 +964,7 @@ pub async fn payments_list( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?Flow::PaymentsList))] @@ -973,7 +976,7 @@ pub async fn payments_list_by_filter( ) -> impl Responder { let flow = Flow::PaymentsList; let payload = payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -983,7 +986,7 @@ pub async fn payments_list_by_filter( }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, - ) + )) .await } #[instrument(skip_all, fields(flow = ?Flow::PaymentsList))] @@ -995,7 +998,7 @@ pub async fn get_filters_for_payments( ) -> impl Responder { let flow = Flow::PaymentsList; let payload = payload.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -1005,7 +1008,7 @@ pub async fn get_filters_for_payments( }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -1016,7 +1019,7 @@ pub async fn get_payment_filters( req: actix_web::HttpRequest, ) -> impl Responder { let flow = Flow::PaymentsFilters; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -1026,7 +1029,7 @@ pub async fn get_payment_filters( }, &auth::JWTAuth(Permission::PaymentRead), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -1142,7 +1145,7 @@ pub async fn payments_reject( #[allow(clippy::too_many_arguments)] async fn authorize_verify_select( operation: Op, - state: app::AppState, + state: app::SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, diff --git a/crates/router/src/routes/poll.rs b/crates/router/src/routes/poll.rs index 39bb63832a93..e4591b403275 100644 --- a/crates/router/src/routes/poll.rs +++ b/crates/router/src/routes/poll.rs @@ -33,7 +33,7 @@ pub async fn retrieve_poll_status( let poll_id = PollId { poll_id: path.into_inner(), }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -41,6 +41,6 @@ pub async fn retrieve_poll_status( |state, auth, req, _| poll::retrieve_poll_status(state, req, auth.merchant_account), &auth::PublishableKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/recon.rs b/crates/router/src/routes/recon.rs index fc0794ff9e0b..053037b57a5f 100644 --- a/crates/router/src/routes/recon.rs +++ b/crates/router/src/routes/recon.rs @@ -4,7 +4,7 @@ use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret}; use router_env::Flow; -use super::AppState; +use super::{AppState, SessionState}; use crate::{ core::{ api_locking, @@ -70,7 +70,7 @@ pub async fn get_recon_token(state: web::Data, req: HttpRequest) -> Ht } pub async fn send_recon_request( - state: AppState, + state: SessionState, user: UserFromToken, ) -> RouterResponse { let db = &*state.store; @@ -150,7 +150,7 @@ pub async fn send_recon_request( } pub async fn recon_merchant_account_update( - state: AppState, + state: SessionState, req: recon_api::ReconUpdateMerchantRequest, ) -> RouterResponse { let merchant_id = &req.merchant_id.clone(); @@ -217,7 +217,7 @@ pub async fn recon_merchant_account_update( } pub async fn generate_recon_token( - state: AppState, + state: SessionState, req: ReconUser, ) -> RouterResponse { let db = &*state.store; @@ -243,7 +243,7 @@ pub async fn generate_recon_token( pub async fn get_recon_auth_token( user: UserFromStorage, - state: AppState, + state: SessionState, ) -> RouterResult> { ReconToken::new_token(user.0.user_id.clone(), &state.conf).await } diff --git a/crates/router/src/routes/refunds.rs b/crates/router/src/routes/refunds.rs index 3df6c8716682..50e0c0c917f6 100644 --- a/crates/router/src/routes/refunds.rs +++ b/crates/router/src/routes/refunds.rs @@ -182,7 +182,7 @@ pub async fn refunds_update( let flow = Flow::RefundsUpdate; let mut refund_update_req = json_payload.into_inner(); refund_update_req.refund_id = path.into_inner(); - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -190,7 +190,7 @@ pub async fn refunds_update( |state, auth, req, _| refund_update_core(state, auth.merchant_account, req), &auth::ApiKeyAuth, api_locking::LockAction::NotApplicable, - ) + )) .await } /// Refunds - List @@ -215,7 +215,7 @@ pub async fn refunds_list( payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsList; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -227,7 +227,7 @@ pub async fn refunds_list( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -253,7 +253,7 @@ pub async fn refunds_filter_list( payload: web::Json, ) -> HttpResponse { let flow = Flow::RefundsList; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -265,7 +265,7 @@ pub async fn refunds_filter_list( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -286,7 +286,7 @@ pub async fn refunds_filter_list( #[cfg(feature = "olap")] pub async fn get_refunds_filters(state: web::Data, req: HttpRequest) -> HttpResponse { let flow = Flow::RefundsFilters; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -298,6 +298,6 @@ pub async fn get_refunds_filters(state: web::Data, req: HttpRequest) - req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 556e91d86946..74bd61765b0d 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -259,7 +259,7 @@ pub async fn routing_update_default_config( json_payload: web::Json>, transaction_type: &enums::TransactionType, ) -> impl Responder { - oss_api::server_wrap( + Box::pin(oss_api::server_wrap( Flow::RoutingUpdateDefaultConfig, state, &req, @@ -281,7 +281,7 @@ pub async fn routing_update_default_config( #[cfg(feature = "release")] &auth::JWTAuth(Permission::RoutingWrite), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -292,7 +292,7 @@ pub async fn routing_retrieve_default_config( req: HttpRequest, transaction_type: &enums::TransactionType, ) -> impl Responder { - oss_api::server_wrap( + Box::pin(oss_api::server_wrap( Flow::RoutingRetrieveDefaultConfig, state, &req, @@ -309,7 +309,7 @@ pub async fn routing_retrieve_default_config( #[cfg(feature = "release")] &auth::JWTAuth(Permission::RoutingRead), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -573,7 +573,7 @@ pub async fn routing_retrieve_default_config_for_profiles( req: HttpRequest, transaction_type: &enums::TransactionType, ) -> impl Responder { - oss_api::server_wrap( + Box::pin(oss_api::server_wrap( Flow::RoutingRetrieveDefaultConfig, state, &req, @@ -598,7 +598,7 @@ pub async fn routing_retrieve_default_config_for_profiles( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -615,7 +615,7 @@ pub async fn routing_update_default_config_for_profile( updated_config: json_payload.into_inner(), profile_id: path.into_inner(), }; - oss_api::server_wrap( + Box::pin(oss_api::server_wrap( Flow::RoutingUpdateDefaultConfig, state, &req, @@ -638,6 +638,6 @@ pub async fn routing_update_default_config_for_profile( #[cfg(feature = "release")] &auth::JWTAuth(Permission::RoutingWrite), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/routes/webhook_events.rs b/crates/router/src/routes/webhook_events.rs index ef1d64f54e95..b4839f5bd075 100644 --- a/crates/router/src/routes/webhook_events.rs +++ b/crates/router/src/routes/webhook_events.rs @@ -27,7 +27,7 @@ pub async fn list_initial_webhook_delivery_attempts( constraints, }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -48,7 +48,7 @@ pub async fn list_initial_webhook_delivery_attempts( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -66,7 +66,7 @@ pub async fn list_webhook_delivery_attempts( initial_attempt_id, }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -87,7 +87,7 @@ pub async fn list_webhook_delivery_attempts( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } @@ -105,7 +105,7 @@ pub async fn retry_webhook_delivery_attempt( event_id, }; - api::server_wrap( + Box::pin(api::server_wrap( flow, state, &req, @@ -126,6 +126,6 @@ pub async fn retry_webhook_delivery_attempt( req.headers(), ), api_locking::LockAction::NotApplicable, - ) + )) .await } diff --git a/crates/router/src/services.rs b/crates/router/src/services.rs index bb50e27b8cd6..5c860aa8a615 100644 --- a/crates/router/src/services.rs +++ b/crates/router/src/services.rs @@ -13,12 +13,14 @@ pub mod recon; #[cfg(feature = "email")] pub mod email; +use std::sync::Arc; + use error_stack::ResultExt; use hyperswitch_domain_models::errors::StorageResult; use masking::{ExposeInterface, StrongSecret}; #[cfg(feature = "kv_store")] use storage_impl::KVRouterStore; -use storage_impl::RouterStore; +use storage_impl::{redis::RedisStore, RouterStore}; use tokio::sync::oneshot; pub use self::{api::*, encryption::*}; @@ -40,7 +42,8 @@ pub type Store = KVRouterStore; #[allow(clippy::expect_used)] pub async fn get_store( config: &Settings, - shut_down_signal: oneshot::Sender<()>, + tenant: &crate::configs::settings::Tenant, + cache_store: Arc, test_transaction: bool, ) -> StorageResult { let master_config = config.master_database.clone().into_inner(); @@ -61,13 +64,13 @@ pub async fn get_store( let conf = (master_config.into(), replica_config.into()); let store: RouterStore = if test_transaction { - RouterStore::test_store(conf, &config.redis, master_enc_key).await? + RouterStore::test_store(conf, tenant, &config.redis, master_enc_key).await? } else { RouterStore::from_config( conf, - &config.redis, + tenant, master_enc_key, - shut_down_signal, + cache_store, storage_impl::redis::cache::PUB_SUB_CHANNEL, ) .await? @@ -85,6 +88,15 @@ pub async fn get_store( Ok(store) } +#[allow(clippy::expect_used)] +pub async fn get_cache_store( + config: &Settings, + shut_down_signal: oneshot::Sender<()>, + _test_transaction: bool, +) -> StorageResult> { + RouterStore::::cache_store(&config.redis, shut_down_signal).await +} + #[inline] pub fn generate_aes256_key() -> errors::CustomResult<[u8; 32], common_utils::errors::CryptoError> { use ring::rand::SecureRandom; diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 5f69a9d40339..098060e144b2 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -1,14 +1,16 @@ pub mod client; pub mod request; use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, error::Error, fmt::Debug, future::Future, str, + sync::Arc, time::{Duration, Instant}, }; +use actix_http::header::HeaderMap; use actix_web::{ body, http::header::HeaderValue, web, FromRequest, HttpRequest, HttpResponse, Responder, ResponseError, @@ -45,9 +47,9 @@ use crate::{ }, logger, routes::{ - app::{AppStateInfo, ReqState}, + app::{AppStateInfo, ReqState, SessionStateInfo}, metrics::{self, request as metrics_request}, - AppState, + AppState, SessionState, }, types::{ self, @@ -174,7 +176,7 @@ pub trait ConnectorIntegration: ConnectorIntegrationAny, - _app_state: &AppState, + _app_state: &SessionState, ) -> CustomResult<(), errors::ConnectorError> { Ok(()) } @@ -184,7 +186,7 @@ pub trait ConnectorIntegration: ConnectorIntegrationAny, - _app_state: &AppState, + _app_state: &SessionState, ) -> CustomResult<(), errors::ConnectorError> { Ok(()) } @@ -297,7 +299,7 @@ pub async fn execute_connector_processing_step< Req: Debug + Clone + 'static, Resp: Debug + Clone + 'static, >( - state: &'b AppState, + state: &'b SessionState, connector_integration: BoxedConnectorIntegration<'a, T, Req, Resp>, req: &'b types::RouterData, call_connector_action: payments::CallConnectorAction, @@ -555,7 +557,7 @@ where #[instrument(skip_all)] pub async fn call_connector_api( - state: &AppState, + state: &SessionState, request: Request, flow_name: &str, ) -> CustomResult, errors::ApiClientError> { @@ -591,7 +593,7 @@ pub async fn call_connector_api( #[instrument(skip_all)] pub async fn send_request( - state: &AppState, + state: &SessionState, request: Request, option_timeout_secs: Option, ) -> CustomResult { @@ -915,15 +917,16 @@ pub enum AuthFlow { pub async fn server_wrap_util<'a, 'b, U, T, Q, F, Fut, E, OErr>( flow: &'a impl router_env::types::FlowMetric, state: web::Data, + incoming_request_header: &HeaderMap, mut request_state: ReqState, request: &'a HttpRequest, payload: T, func: F, - api_auth: &dyn AuthenticateAndFetch, + api_auth: &dyn AuthenticateAndFetch, lock_action: api_locking::LockAction, ) -> CustomResult, OErr> where - F: Fn(AppState, U, T, ReqState) -> Fut, + F: Fn(SessionState, U, T, ReqState) -> Fut, 'b: 'a, Fut: Future, E>>, Q: Serialize + Debug + 'a + ApiEventMetric, @@ -945,17 +948,49 @@ where let mut app_state = state.get_ref().clone(); - app_state.add_request_id(request_id); let start_instant = Instant::now(); let serialized_request = masking::masked_serialize(&payload) .attach_printable("Failed to serialize json request") .change_context(errors::ApiErrorResponse::InternalServerError.switch())?; let mut event_type = payload.get_api_event_type(); + let tenants: HashSet<_> = state + .conf + .multitenancy + .get_tenant_names() + .into_iter() + .collect(); + let tenant_id = if !state.conf.multitenancy.enabled { + common_utils::consts::DEFAULT_TENANT.to_string() + } else { + incoming_request_header + .get("x-tenant-id") + .and_then(|value| value.to_str().ok()) + .ok_or_else(|| errors::ApiErrorResponse::MissingTenantId.switch()) + .map(|req_tenant_id| { + if !tenants.contains(req_tenant_id) { + Err(errors::ApiErrorResponse::InvalidTenant { + tenant_id: req_tenant_id.to_string(), + } + .switch()) + } else { + Ok(req_tenant_id.to_string()) + } + })?? + }; + // let tenant_id = "public".to_string(); + let mut session_state = + Arc::new(app_state.clone()).get_session_state(tenant_id.as_str(), || { + errors::ApiErrorResponse::InvalidTenant { + tenant_id: tenant_id.clone(), + } + .switch() + })?; + session_state.add_request_id(request_id); // Currently auth failures are not recorded as API events let (auth_out, auth_type) = api_auth - .authenticate_and_fetch(request.headers(), &app_state) + .authenticate_and_fetch(request.headers(), &session_state) .await .switch()?; @@ -975,14 +1010,14 @@ where let output = { lock_action .clone() - .perform_locking_action(&app_state, merchant_id.to_owned()) + .perform_locking_action(&session_state, merchant_id.to_owned()) .await .switch()?; - let res = func(app_state.clone(), auth_out, payload, request_state) + let res = func(session_state.clone(), auth_out, payload, request_state) .await .switch(); lock_action - .free_lock_action(&app_state, merchant_id.to_owned()) + .free_lock_action(&session_state, merchant_id.to_owned()) .await .switch()?; res @@ -1064,11 +1099,11 @@ pub async fn server_wrap<'a, T, U, Q, F, Fut, E>( request: &'a HttpRequest, payload: T, func: F, - api_auth: &dyn AuthenticateAndFetch, + api_auth: &dyn AuthenticateAndFetch, lock_action: api_locking::LockAction, ) -> HttpResponse where - F: Fn(AppState, U, T, ReqState) -> Fut, + F: Fn(SessionState, U, T, ReqState) -> Fut, Fut: Future, E>>, Q: Serialize + Debug + ApiEventMetric + 'a, T: Debug + Serialize + ApiEventMetric, @@ -1109,6 +1144,7 @@ where server_wrap_util( &flow, state.clone(), + incoming_request_header, req_state, request, payload, diff --git a/crates/router/src/services/api/client.rs b/crates/router/src/services/api/client.rs index d6c55baf602b..add93e991194 100644 --- a/crates/router/src/services/api/client.rs +++ b/crates/router/src/services/api/client.rs @@ -15,7 +15,7 @@ use crate::{ errors::{ApiClientError, CustomResult}, payments, }, - routes::AppState, + routes::SessionState, }; static NON_PROXIED_CLIENT: OnceCell = OnceCell::new(); @@ -165,7 +165,7 @@ where async fn send_request( &self, - state: &AppState, + state: &SessionState, request: Request, option_timeout_secs: Option, forward_to_kafka: bool, @@ -340,7 +340,7 @@ impl ApiClient for ProxyClient { } async fn send_request( &self, - state: &AppState, + state: &SessionState, request: Request, option_timeout_secs: Option, _forward_to_kafka: bool, @@ -392,7 +392,7 @@ impl ApiClient for MockApiClient { async fn send_request( &self, - _state: &AppState, + _state: &SessionState, _request: Request, _option_timeout_secs: Option, _forward_to_kafka: bool, diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 6953fffa85ce..c07ac836cae2 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -25,14 +25,13 @@ use crate::consts; #[cfg(feature = "olap")] use crate::core::errors::UserResult; #[cfg(feature = "recon")] -use crate::routes::AppState; +use crate::routes::SessionState; use crate::{ core::{ api_keys, errors::{self, utils::StorageErrorExt, RouterResult}, }, - db::StorageInterface, - routes::app::AppStateInfo, + routes::app::SessionStateInfo, services::api, types::domain, utils::OptionExt, @@ -228,7 +227,7 @@ impl AuthInfo for AuthenticationData { #[async_trait] pub trait AuthenticateAndFetch where - A: AppStateInfo, + A: SessionStateInfo, { async fn authenticate_and_fetch( &self, @@ -245,7 +244,7 @@ pub struct NoAuth; #[async_trait] impl AuthenticateAndFetch<(), A> for NoAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -259,7 +258,7 @@ where #[async_trait] impl AuthenticateAndFetch for ApiKeyAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -337,7 +336,7 @@ pub(crate) struct SinglePurposeJWTAuth(pub TokenPurpose); #[async_trait] impl AuthenticateAndFetch for SinglePurposeJWTAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -377,7 +376,7 @@ pub struct SinglePurposeOrLoginTokenAuth { #[async_trait] impl AuthenticateAndFetch for SinglePurposeOrLoginTokenAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -418,7 +417,7 @@ pub struct AdminApiAuth; #[async_trait] impl AuthenticateAndFetch<(), A> for AdminApiAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -440,13 +439,42 @@ where } } +#[derive(Debug)] +pub struct EphemeralKeyAuth(pub id_type::CustomerId); + +#[async_trait] +impl AuthenticateAndFetch for EphemeralKeyAuth +where + A: SessionStateInfo + Sync, +{ + async fn authenticate_and_fetch( + &self, + request_headers: &HeaderMap, + state: &A, + ) -> RouterResult<(AuthenticationData, AuthenticationType)> { + let api_key = + get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; + let ephemeral_key = state + .store() + .get_ephemeral_key(api_key) + .await + .change_context(errors::ApiErrorResponse::Unauthorized)?; + + if ephemeral_key.customer_id.ne(&self.0) { + return Err(report!(errors::ApiErrorResponse::InvalidEphemeralKey)); + } + MerchantIdAuth(ephemeral_key.merchant_id) + .authenticate_and_fetch(request_headers, state) + .await + } +} #[derive(Debug)] pub struct MerchantIdAuth(pub String); #[async_trait] impl AuthenticateAndFetch for MerchantIdAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -500,7 +528,7 @@ pub struct PublishableKeyAuth; #[async_trait] impl AuthenticateAndFetch for PublishableKeyAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -544,7 +572,7 @@ struct JwtAuthPayloadFetchUnit { #[async_trait] impl AuthenticateAndFetch<(), A> for JWTAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -573,7 +601,7 @@ where #[async_trait] impl AuthenticateAndFetch for JWTAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -611,7 +639,7 @@ pub struct JWTAuthMerchantFromRoute { #[async_trait] impl AuthenticateAndFetch<(), A> for JWTAuthMerchantFromRoute where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -648,7 +676,7 @@ pub struct JWTAuthMerchantOrProfileFromRoute { #[async_trait] impl AuthenticateAndFetch<(), A> for JWTAuthMerchantOrProfileFromRoute where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -705,7 +733,7 @@ where pub async fn parse_jwt_payload(headers: &HeaderMap, state: &A) -> RouterResult where T: serde::de::DeserializeOwned, - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { let token = match get_cookie_from_header(headers).and_then(cookies::parse_cookie) { Ok(cookies) => cookies, @@ -723,7 +751,7 @@ where #[async_trait] impl AuthenticateAndFetch for JWTAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -773,7 +801,7 @@ pub type AuthenticationDataWithUserId = (AuthenticationData, String); #[async_trait] impl AuthenticateAndFetch for JWTAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -824,7 +852,7 @@ pub struct DashboardNoPermissionAuth; #[async_trait] impl AuthenticateAndFetch for DashboardNoPermissionAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -855,7 +883,7 @@ where #[async_trait] impl AuthenticateAndFetch<(), A> for DashboardNoPermissionAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -874,7 +902,7 @@ where #[async_trait] impl AuthenticateAndFetch for DashboardNoPermissionAuth where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -971,7 +999,7 @@ impl ClientSecretFetch for api_models::payment_methods::PaymentMethodUpdate { } } -pub fn get_auth_type_and_flow( +pub fn get_auth_type_and_flow( headers: &HeaderMap, ) -> RouterResult<( Box>, @@ -993,7 +1021,7 @@ pub fn check_client_secret_and_get_auth( api::AuthFlow, )> where - T: AppStateInfo, + T: SessionStateInfo, ApiKeyAuth: AuthenticateAndFetch, PublishableKeyAuth: AuthenticateAndFetch, { @@ -1018,27 +1046,17 @@ where Ok((Box::new(ApiKeyAuth), api::AuthFlow::Merchant)) } -pub async fn is_ephemeral_auth( +pub fn is_ephemeral_auth( headers: &HeaderMap, - db: &dyn StorageInterface, customer_id: &id_type::CustomerId, ) -> RouterResult>> { let api_key = get_api_key(headers)?; if !api_key.starts_with("epk") { - return Ok(Box::new(ApiKeyAuth)); + Ok(Box::new(ApiKeyAuth)) + } else { + Ok(Box::new(EphemeralKeyAuth(customer_id.to_owned()))) } - - let ephemeral_key = db - .get_ephemeral_key(api_key) - .await - .change_context(errors::ApiErrorResponse::Unauthorized)?; - - if ephemeral_key.customer_id.ne(customer_id) { - return Err(report!(errors::ApiErrorResponse::InvalidEphemeralKey)); - } - - Ok(Box::new(MerchantIdAuth(ephemeral_key.merchant_id))) } pub fn is_jwt_auth(headers: &HeaderMap) -> bool { @@ -1048,7 +1066,7 @@ pub fn is_jwt_auth(headers: &HeaderMap) -> bool { .is_ok() } -pub async fn decode_jwt(token: &str, state: &impl AppStateInfo) -> RouterResult +pub async fn decode_jwt(token: &str, state: &impl SessionStateInfo) -> RouterResult where T: serde::de::DeserializeOwned, { @@ -1124,7 +1142,7 @@ pub struct ReconAdmin; #[cfg(feature = "recon")] impl AuthenticateAndFetch<(), A> for ReconAdmin where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { async fn authenticate_and_fetch( &self, @@ -1160,13 +1178,13 @@ impl AuthInfo for ReconUser { } #[cfg(all(feature = "olap", feature = "recon"))] #[async_trait] -impl AuthenticateAndFetch for ReconJWT { +impl AuthenticateAndFetch for ReconJWT { async fn authenticate_and_fetch( &self, request_headers: &HeaderMap, - state: &AppState, + state: &SessionState, ) -> RouterResult<(ReconUser, AuthenticationType)> { - let payload = parse_jwt_payload::(request_headers, state).await?; + let payload = parse_jwt_payload::(request_headers, state).await?; Ok(( ReconUser { diff --git a/crates/router/src/services/authentication/blacklist.rs b/crates/router/src/services/authentication/blacklist.rs index 6804329da5dd..0ac8c419ef69 100644 --- a/crates/router/src/services/authentication/blacklist.rs +++ b/crates/router/src/services/authentication/blacklist.rs @@ -13,17 +13,17 @@ use crate::consts::{EMAIL_TOKEN_BLACKLIST_PREFIX, EMAIL_TOKEN_TIME_IN_SECS}; use crate::{ consts::{JWT_TOKEN_TIME_IN_SECS, ROLE_BLACKLIST_PREFIX, USER_BLACKLIST_PREFIX}, core::errors::{ApiErrorResponse, RouterResult}, - routes::app::AppStateInfo, + routes::app::SessionStateInfo, }; #[cfg(feature = "olap")] use crate::{ core::errors::{UserErrors, UserResult}, - routes::AppState, + routes::SessionState, services::authorization as authz, }; #[cfg(feature = "olap")] -pub async fn insert_user_in_blacklist(state: &AppState, user_id: &str) -> UserResult<()> { +pub async fn insert_user_in_blacklist(state: &SessionState, user_id: &str) -> UserResult<()> { let user_blacklist_key = format!("{}{}", USER_BLACKLIST_PREFIX, user_id); let expiry = expiry_to_i64(JWT_TOKEN_TIME_IN_SECS).change_context(UserErrors::InternalServerError)?; @@ -39,7 +39,7 @@ pub async fn insert_user_in_blacklist(state: &AppState, user_id: &str) -> UserRe } #[cfg(feature = "olap")] -pub async fn insert_role_in_blacklist(state: &AppState, role_id: &str) -> UserResult<()> { +pub async fn insert_role_in_blacklist(state: &SessionState, role_id: &str) -> UserResult<()> { let role_blacklist_key = format!("{}{}", ROLE_BLACKLIST_PREFIX, role_id); let expiry = expiry_to_i64(JWT_TOKEN_TIME_IN_SECS).change_context(UserErrors::InternalServerError)?; @@ -58,7 +58,7 @@ pub async fn insert_role_in_blacklist(state: &AppState, role_id: &str) -> UserRe } #[cfg(feature = "olap")] -async fn invalidate_role_cache(state: &AppState, role_id: &str) -> RouterResult<()> { +async fn invalidate_role_cache(state: &SessionState, role_id: &str) -> RouterResult<()> { let redis_conn = get_redis_connection(state)?; redis_conn .delete_key(authz::get_cache_key_from_role_id(role_id).as_str()) @@ -67,7 +67,7 @@ async fn invalidate_role_cache(state: &AppState, role_id: &str) -> RouterResult< .change_context(ApiErrorResponse::InternalServerError) } -pub async fn check_user_in_blacklist( +pub async fn check_user_in_blacklist( state: &A, user_id: &str, token_expiry: u64, @@ -82,7 +82,7 @@ pub async fn check_user_in_blacklist( .map(|timestamp| timestamp.map_or(false, |timestamp| timestamp > token_issued_at)) } -pub async fn check_role_in_blacklist( +pub async fn check_role_in_blacklist( state: &A, role_id: &str, token_expiry: u64, @@ -98,7 +98,7 @@ pub async fn check_role_in_blacklist( } #[cfg(feature = "email")] -pub async fn insert_email_token_in_blacklist(state: &AppState, token: &str) -> UserResult<()> { +pub async fn insert_email_token_in_blacklist(state: &SessionState, token: &str) -> UserResult<()> { let redis_conn = get_redis_connection(state).change_context(UserErrors::InternalServerError)?; let blacklist_key = format!("{}{token}", EMAIL_TOKEN_BLACKLIST_PREFIX); let expiry = @@ -110,7 +110,7 @@ pub async fn insert_email_token_in_blacklist(state: &AppState, token: &str) -> U } #[cfg(feature = "email")] -pub async fn check_email_token_in_blacklist(state: &AppState, token: &str) -> UserResult<()> { +pub async fn check_email_token_in_blacklist(state: &SessionState, token: &str) -> UserResult<()> { let redis_conn = get_redis_connection(state).change_context(UserErrors::InternalServerError)?; let blacklist_key = format!("{}{token}", EMAIL_TOKEN_BLACKLIST_PREFIX); let key_exists = redis_conn @@ -124,7 +124,7 @@ pub async fn check_email_token_in_blacklist(state: &AppState, token: &str) -> Us Ok(()) } -fn get_redis_connection(state: &A) -> RouterResult> { +fn get_redis_connection(state: &A) -> RouterResult> { state .store() .get_redis_conn() @@ -140,14 +140,14 @@ fn expiry_to_i64(expiry: u64) -> RouterResult { pub trait BlackList { async fn check_in_blacklist(&self, state: &A) -> RouterResult where - A: AppStateInfo + Sync; + A: SessionStateInfo + Sync; } #[async_trait::async_trait] impl BlackList for AuthToken { async fn check_in_blacklist(&self, state: &A) -> RouterResult where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { Ok( check_user_in_blacklist(state, &self.user_id, self.exp).await? @@ -161,7 +161,7 @@ impl BlackList for AuthToken { impl BlackList for SinglePurposeToken { async fn check_in_blacklist(&self, state: &A) -> RouterResult where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { check_user_in_blacklist(state, &self.user_id, self.exp).await } @@ -172,7 +172,7 @@ impl BlackList for SinglePurposeToken { impl BlackList for SinglePurposeOrLoginToken { async fn check_in_blacklist(&self, state: &A) -> RouterResult where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { check_user_in_blacklist(state, &self.user_id, self.exp).await } diff --git a/crates/router/src/services/authorization.rs b/crates/router/src/services/authorization.rs index 33edb173255f..768c57a36d0c 100644 --- a/crates/router/src/services/authorization.rs +++ b/crates/router/src/services/authorization.rs @@ -9,7 +9,7 @@ use super::authentication::AuthToken; use crate::{ consts, core::errors::{ApiErrorResponse, RouterResult, StorageErrorExt}, - routes::app::AppStateInfo, + routes::app::SessionStateInfo, }; #[cfg(feature = "olap")] @@ -23,7 +23,7 @@ pub async fn get_permissions( token: &AuthToken, ) -> RouterResult> where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { if let Some(permissions) = get_permissions_from_predefined_roles(&token.role_id) { return Ok(permissions); @@ -55,7 +55,7 @@ async fn get_permissions_from_cache( role_id: &str, ) -> RouterResult> where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { let redis_conn = get_redis_connection(state)?; @@ -82,7 +82,7 @@ async fn get_permissions_from_db( org_id: &str, ) -> RouterResult> where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { state .store() @@ -99,7 +99,7 @@ pub async fn set_permissions_in_cache( expiry: i64, ) -> RouterResult<()> where - A: AppStateInfo + Sync, + A: SessionStateInfo + Sync, { let redis_conn = get_redis_connection(state)?; @@ -139,7 +139,7 @@ pub fn check_authorization( ) } -fn get_redis_connection(state: &A) -> RouterResult> { +fn get_redis_connection(state: &A) -> RouterResult> { state .store() .get_redis_conn() diff --git a/crates/router/src/services/authorization/roles.rs b/crates/router/src/services/authorization/roles.rs index f99892c565d9..8e5cb6fbeaa9 100644 --- a/crates/router/src/services/authorization/roles.rs +++ b/crates/router/src/services/authorization/roles.rs @@ -4,7 +4,7 @@ use common_enums::{PermissionGroup, RoleScope}; use common_utils::errors::CustomResult; use super::{permission_groups::get_permissions_vec, permissions::Permission}; -use crate::{core::errors, routes::AppState}; +use crate::{core::errors, routes::SessionState}; pub mod predefined_roles; @@ -67,7 +67,7 @@ impl RoleInfo { } pub async fn from_role_id( - state: &AppState, + state: &SessionState, role_id: &str, merchant_id: &str, org_id: &str, diff --git a/crates/router/src/services/email/types.rs b/crates/router/src/services/email/types.rs index ee51d976b40e..d05e6ab22575 100644 --- a/crates/router/src/services/email/types.rs +++ b/crates/router/src/services/email/types.rs @@ -7,7 +7,7 @@ use error_stack::ResultExt; use external_services::email::{EmailContents, EmailData, EmailError}; use masking::{ExposeInterface, PeekInterface, Secret}; -use crate::{configs, consts, routes::AppState}; +use crate::{configs, consts, routes::SessionState}; #[cfg(feature = "olap")] use crate::{ core::errors::{UserErrors, UserResult}, @@ -407,7 +407,7 @@ pub struct BizEmailProd { } impl BizEmailProd { - pub fn new(state: &AppState, data: ProdIntent) -> UserResult { + pub fn new(state: &SessionState, data: ProdIntent) -> UserResult { Ok(Self { recipient_email: (domain::UserEmail::new( consts::user::BUSINESS_EMAIL.to_string().into(), diff --git a/crates/router/src/services/kafka.rs b/crates/router/src/services/kafka.rs index 6f2aa9fcd100..3523ab9261fd 100644 --- a/crates/router/src/services/kafka.rs +++ b/crates/router/src/services/kafka.rs @@ -12,9 +12,13 @@ use rdkafka::{ pub mod payout; use crate::events::EventType; mod dispute; +mod dispute_event; mod payment_attempt; +mod payment_attempt_event; mod payment_intent; +mod payment_intent_event; mod refund; +mod refund_event; use diesel_models::refund::Refund; use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; use serde::Serialize; @@ -23,8 +27,10 @@ use time::{OffsetDateTime, PrimitiveDateTime}; #[cfg(feature = "payouts")] use self::payout::KafkaPayout; use self::{ - dispute::KafkaDispute, payment_attempt::KafkaPaymentAttempt, - payment_intent::KafkaPaymentIntent, refund::KafkaRefund, + dispute::KafkaDispute, dispute_event::KafkaDisputeEvent, payment_attempt::KafkaPaymentAttempt, + payment_attempt_event::KafkaPaymentAttemptEvent, payment_intent::KafkaPaymentIntent, + payment_intent_event::KafkaPaymentIntentEvent, refund::KafkaRefund, + refund_event::KafkaRefundEvent, }; use crate::types::storage::Dispute; @@ -89,6 +95,42 @@ impl<'a, T: KafkaMessage> KafkaMessage for KafkaEvent<'a, T> { } } +#[derive(serde::Serialize, Debug)] +struct KafkaConsolidatedLog<'a, T: KafkaMessage> { + #[serde(flatten)] + event: &'a T, + tenant_id: TenantID, +} + +#[derive(serde::Serialize, Debug)] +struct KafkaConsolidatedEvent<'a, T: KafkaMessage> { + log: KafkaConsolidatedLog<'a, T>, + log_type: EventType, +} + +impl<'a, T: KafkaMessage> KafkaConsolidatedEvent<'a, T> { + fn new(event: &'a T, tenant_id: TenantID) -> Self { + Self { + log: KafkaConsolidatedLog { event, tenant_id }, + log_type: event.event_type(), + } + } +} + +impl<'a, T: KafkaMessage> KafkaMessage for KafkaConsolidatedEvent<'a, T> { + fn key(&self) -> String { + self.log.event.key() + } + + fn event_type(&self) -> EventType { + EventType::Consolidated + } + + fn creation_timestamp(&self) -> Option { + self.log.event.creation_timestamp() + } +} + #[derive(Debug, serde::Deserialize, Clone, Default)] #[serde(default)] pub struct KafkaSettings { @@ -103,6 +145,7 @@ pub struct KafkaSettings { audit_events_topic: String, #[cfg(feature = "payouts")] payout_analytics_topic: String, + consolidated_events_topic: String, } impl KafkaSettings { @@ -175,6 +218,12 @@ impl KafkaSettings { )) })?; + common_utils::fp_utils::when(self.consolidated_events_topic.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "Consolidated Events topic must not be empty".into(), + )) + })?; + Ok(()) } } @@ -192,6 +241,7 @@ pub struct KafkaProducer { audit_events_topic: String, #[cfg(feature = "payouts")] payout_analytics_topic: String, + consolidated_events_topic: String, } struct RdKafkaProducer(ThreadedProducer); @@ -233,23 +283,13 @@ impl KafkaProducer { audit_events_topic: conf.audit_events_topic.clone(), #[cfg(feature = "payouts")] payout_analytics_topic: conf.payout_analytics_topic.clone(), + consolidated_events_topic: conf.consolidated_events_topic.clone(), }) } pub fn log_event(&self, event: &T) -> MQResult<()> { router_env::logger::debug!("Logging Kafka Event {event:?}"); - let topic = match event.event_type() { - EventType::PaymentIntent => &self.intent_analytics_topic, - EventType::PaymentAttempt => &self.attempt_analytics_topic, - EventType::Refund => &self.refund_analytics_topic, - EventType::ApiLogs => &self.api_logs_topic, - EventType::ConnectorApiLogs => &self.connector_logs_topic, - EventType::OutgoingWebhookLogs => &self.outgoing_webhook_logs_topic, - EventType::Dispute => &self.dispute_analytics_topic, - EventType::AuditEvent => &self.audit_events_topic, - #[cfg(feature = "payouts")] - EventType::Payout => &self.payout_analytics_topic, - }; + let topic = self.get_topic(event.event_type()); self.producer .0 .send( @@ -281,11 +321,18 @@ impl KafkaProducer { format!("Failed to add negative attempt event {negative_event:?}") })?; }; + self.log_event(&KafkaEvent::new( &KafkaPaymentAttempt::from_storage(attempt), tenant_id.clone(), )) - .attach_printable_lazy(|| format!("Failed to add positive attempt event {attempt:?}")) + .attach_printable_lazy(|| format!("Failed to add positive attempt event {attempt:?}"))?; + + self.log_event(&KafkaConsolidatedEvent::new( + &KafkaPaymentAttemptEvent::from_storage(attempt), + tenant_id.clone(), + )) + .attach_printable_lazy(|| format!("Failed to add consolidated attempt event {attempt:?}")) } pub async fn log_payment_attempt_delete( @@ -317,11 +364,18 @@ impl KafkaProducer { format!("Failed to add negative intent event {negative_event:?}") })?; }; + self.log_event(&KafkaEvent::new( &KafkaPaymentIntent::from_storage(intent), tenant_id.clone(), )) - .attach_printable_lazy(|| format!("Failed to add positive intent event {intent:?}")) + .attach_printable_lazy(|| format!("Failed to add positive intent event {intent:?}"))?; + + self.log_event(&KafkaConsolidatedEvent::new( + &KafkaPaymentIntentEvent::from_storage(intent), + tenant_id.clone(), + )) + .attach_printable_lazy(|| format!("Failed to add consolidated intent event {intent:?}")) } pub async fn log_payment_intent_delete( @@ -353,11 +407,18 @@ impl KafkaProducer { format!("Failed to add negative refund event {negative_event:?}") })?; }; + self.log_event(&KafkaEvent::new( &KafkaRefund::from_storage(refund), tenant_id.clone(), )) - .attach_printable_lazy(|| format!("Failed to add positive refund event {refund:?}")) + .attach_printable_lazy(|| format!("Failed to add positive refund event {refund:?}"))?; + + self.log_event(&KafkaConsolidatedEvent::new( + &KafkaRefundEvent::from_storage(refund), + tenant_id.clone(), + )) + .attach_printable_lazy(|| format!("Failed to add consolidated refund event {refund:?}")) } pub async fn log_refund_delete( @@ -389,11 +450,18 @@ impl KafkaProducer { format!("Failed to add negative dispute event {negative_event:?}") })?; }; + self.log_event(&KafkaEvent::new( &KafkaDispute::from_storage(dispute), tenant_id.clone(), )) - .attach_printable_lazy(|| format!("Failed to add positive dispute event {dispute:?}")) + .attach_printable_lazy(|| format!("Failed to add positive dispute event {dispute:?}"))?; + + self.log_event(&KafkaConsolidatedEvent::new( + &KafkaDisputeEvent::from_storage(dispute), + tenant_id.clone(), + )) + .attach_printable_lazy(|| format!("Failed to add consolidated dispute event {dispute:?}")) } #[cfg(feature = "payouts")] @@ -437,6 +505,7 @@ impl KafkaProducer { EventType::AuditEvent => &self.audit_events_topic, #[cfg(feature = "payouts")] EventType::Payout => &self.payout_analytics_topic, + EventType::Consolidated => &self.consolidated_events_topic, } } } diff --git a/crates/router/src/services/kafka/dispute_event.rs b/crates/router/src/services/kafka/dispute_event.rs new file mode 100644 index 000000000000..71e0a11e04d5 --- /dev/null +++ b/crates/router/src/services/kafka/dispute_event.rs @@ -0,0 +1,77 @@ +use diesel_models::enums as storage_enums; +use masking::Secret; +use time::OffsetDateTime; + +use crate::types::storage::dispute::Dispute; + +#[serde_with::skip_serializing_none] +#[derive(serde::Serialize, Debug)] +pub struct KafkaDisputeEvent<'a> { + pub dispute_id: &'a String, + pub dispute_amount: i64, + pub currency: &'a String, + pub dispute_stage: &'a storage_enums::DisputeStage, + pub dispute_status: &'a storage_enums::DisputeStatus, + pub payment_id: &'a String, + pub attempt_id: &'a String, + pub merchant_id: &'a String, + pub connector_status: &'a String, + pub connector_dispute_id: &'a String, + pub connector_reason: Option<&'a String>, + pub connector_reason_code: Option<&'a String>, + #[serde(default, with = "time::serde::timestamp::milliseconds::option")] + pub challenge_required_by: Option, + #[serde(default, with = "time::serde::timestamp::milliseconds::option")] + pub connector_created_at: Option, + #[serde(default, with = "time::serde::timestamp::milliseconds::option")] + pub connector_updated_at: Option, + #[serde(default, with = "time::serde::timestamp::milliseconds")] + pub created_at: OffsetDateTime, + #[serde(default, with = "time::serde::timestamp::milliseconds")] + pub modified_at: OffsetDateTime, + pub connector: &'a String, + pub evidence: &'a Secret, + pub profile_id: Option<&'a String>, + pub merchant_connector_id: Option<&'a String>, +} + +impl<'a> KafkaDisputeEvent<'a> { + pub fn from_storage(dispute: &'a Dispute) -> Self { + Self { + dispute_id: &dispute.dispute_id, + dispute_amount: dispute.amount.parse::().unwrap_or_default(), + currency: &dispute.currency, + dispute_stage: &dispute.dispute_stage, + dispute_status: &dispute.dispute_status, + payment_id: &dispute.payment_id, + attempt_id: &dispute.attempt_id, + merchant_id: &dispute.merchant_id, + connector_status: &dispute.connector_status, + connector_dispute_id: &dispute.connector_dispute_id, + connector_reason: dispute.connector_reason.as_ref(), + connector_reason_code: dispute.connector_reason_code.as_ref(), + challenge_required_by: dispute.challenge_required_by.map(|i| i.assume_utc()), + connector_created_at: dispute.connector_created_at.map(|i| i.assume_utc()), + connector_updated_at: dispute.connector_updated_at.map(|i| i.assume_utc()), + created_at: dispute.created_at.assume_utc(), + modified_at: dispute.modified_at.assume_utc(), + connector: &dispute.connector, + evidence: &dispute.evidence, + profile_id: dispute.profile_id.as_ref(), + merchant_connector_id: dispute.merchant_connector_id.as_ref(), + } + } +} + +impl<'a> super::KafkaMessage for KafkaDisputeEvent<'a> { + fn key(&self) -> String { + format!( + "{}_{}_{}", + self.merchant_id, self.payment_id, self.dispute_id + ) + } + + fn event_type(&self) -> crate::events::EventType { + crate::events::EventType::Dispute + } +} diff --git a/crates/router/src/services/kafka/payment_attempt_event.rs b/crates/router/src/services/kafka/payment_attempt_event.rs new file mode 100644 index 000000000000..bb4d69eda27f --- /dev/null +++ b/crates/router/src/services/kafka/payment_attempt_event.rs @@ -0,0 +1,119 @@ +// use diesel_models::enums::MandateDetails; +use common_utils::types::MinorUnit; +use diesel_models::enums as storage_enums; +use hyperswitch_domain_models::{ + mandates::MandateDetails, payments::payment_attempt::PaymentAttempt, +}; +use time::OffsetDateTime; + +#[serde_with::skip_serializing_none] +#[derive(serde::Serialize, Debug)] +pub struct KafkaPaymentAttemptEvent<'a> { + pub payment_id: &'a String, + pub merchant_id: &'a String, + pub attempt_id: &'a String, + pub status: storage_enums::AttemptStatus, + pub amount: MinorUnit, + pub currency: Option, + pub save_to_locker: Option, + pub connector: Option<&'a String>, + pub error_message: Option<&'a String>, + pub offer_amount: Option, + pub surcharge_amount: Option, + pub tax_amount: Option, + pub payment_method_id: Option<&'a String>, + pub payment_method: Option, + pub connector_transaction_id: Option<&'a String>, + pub capture_method: Option, + #[serde(default, with = "time::serde::timestamp::milliseconds::option")] + pub capture_on: Option, + pub confirm: bool, + pub authentication_type: Option, + #[serde(with = "time::serde::timestamp::milliseconds")] + pub created_at: OffsetDateTime, + #[serde(with = "time::serde::timestamp::milliseconds")] + pub modified_at: OffsetDateTime, + #[serde(default, with = "time::serde::timestamp::milliseconds::option")] + pub last_synced: Option, + pub cancellation_reason: Option<&'a String>, + pub amount_to_capture: Option, + pub mandate_id: Option<&'a String>, + pub browser_info: Option, + pub error_code: Option<&'a String>, + pub connector_metadata: Option, + // TODO: These types should implement copy ideally + pub payment_experience: Option<&'a storage_enums::PaymentExperience>, + pub payment_method_type: Option<&'a storage_enums::PaymentMethodType>, + pub payment_method_data: Option, + pub error_reason: Option<&'a String>, + pub multiple_capture_count: Option, + pub amount_capturable: MinorUnit, + pub merchant_connector_id: Option<&'a String>, + pub net_amount: MinorUnit, + pub unified_code: Option<&'a String>, + pub unified_message: Option<&'a String>, + pub mandate_data: Option<&'a MandateDetails>, + pub client_source: Option<&'a String>, + pub client_version: Option<&'a String>, +} + +impl<'a> KafkaPaymentAttemptEvent<'a> { + pub fn from_storage(attempt: &'a PaymentAttempt) -> Self { + Self { + payment_id: &attempt.payment_id, + merchant_id: &attempt.merchant_id, + attempt_id: &attempt.attempt_id, + status: attempt.status, + amount: attempt.amount, + currency: attempt.currency, + save_to_locker: attempt.save_to_locker, + connector: attempt.connector.as_ref(), + error_message: attempt.error_message.as_ref(), + offer_amount: attempt.offer_amount, + surcharge_amount: attempt.surcharge_amount, + tax_amount: attempt.tax_amount, + payment_method_id: attempt.payment_method_id.as_ref(), + payment_method: attempt.payment_method, + connector_transaction_id: attempt.connector_transaction_id.as_ref(), + capture_method: attempt.capture_method, + capture_on: attempt.capture_on.map(|i| i.assume_utc()), + confirm: attempt.confirm, + authentication_type: attempt.authentication_type, + created_at: attempt.created_at.assume_utc(), + modified_at: attempt.modified_at.assume_utc(), + last_synced: attempt.last_synced.map(|i| i.assume_utc()), + cancellation_reason: attempt.cancellation_reason.as_ref(), + amount_to_capture: attempt.amount_to_capture, + mandate_id: attempt.mandate_id.as_ref(), + browser_info: attempt.browser_info.as_ref().map(|v| v.to_string()), + error_code: attempt.error_code.as_ref(), + connector_metadata: attempt.connector_metadata.as_ref().map(|v| v.to_string()), + payment_experience: attempt.payment_experience.as_ref(), + payment_method_type: attempt.payment_method_type.as_ref(), + payment_method_data: attempt.payment_method_data.as_ref().map(|v| v.to_string()), + error_reason: attempt.error_reason.as_ref(), + multiple_capture_count: attempt.multiple_capture_count, + amount_capturable: attempt.amount_capturable, + merchant_connector_id: attempt.merchant_connector_id.as_ref(), + net_amount: attempt.net_amount, + unified_code: attempt.unified_code.as_ref(), + unified_message: attempt.unified_message.as_ref(), + mandate_data: attempt.mandate_data.as_ref(), + client_source: attempt.client_source.as_ref(), + client_version: attempt.client_version.as_ref(), + } + } +} + +impl<'a> super::KafkaMessage for KafkaPaymentAttemptEvent<'a> { + fn key(&self) -> String { + format!( + "{}_{}_{}", + self.merchant_id, self.payment_id, self.attempt_id + ) + } + + fn event_type(&self) -> crate::events::EventType { + crate::events::EventType::PaymentAttempt + } +} diff --git a/crates/router/src/services/kafka/payment_intent_event.rs b/crates/router/src/services/kafka/payment_intent_event.rs new file mode 100644 index 000000000000..a3fbd9ddc458 --- /dev/null +++ b/crates/router/src/services/kafka/payment_intent_event.rs @@ -0,0 +1,75 @@ +use common_utils::{id_type, types::MinorUnit}; +use diesel_models::enums as storage_enums; +use hyperswitch_domain_models::payments::PaymentIntent; +use time::OffsetDateTime; + +#[serde_with::skip_serializing_none] +#[derive(serde::Serialize, Debug)] +pub struct KafkaPaymentIntentEvent<'a> { + pub payment_id: &'a String, + pub merchant_id: &'a String, + pub status: storage_enums::IntentStatus, + pub amount: MinorUnit, + pub currency: Option, + pub amount_captured: Option, + pub customer_id: Option<&'a id_type::CustomerId>, + pub description: Option<&'a String>, + pub return_url: Option<&'a String>, + pub connector_id: Option<&'a String>, + pub statement_descriptor_name: Option<&'a String>, + pub statement_descriptor_suffix: Option<&'a String>, + #[serde(with = "time::serde::timestamp::milliseconds")] + pub created_at: OffsetDateTime, + #[serde(with = "time::serde::timestamp::milliseconds")] + pub modified_at: OffsetDateTime, + #[serde(default, with = "time::serde::timestamp::milliseconds::option")] + pub last_synced: Option, + pub setup_future_usage: Option, + pub off_session: Option, + pub client_secret: Option<&'a String>, + pub active_attempt_id: String, + pub business_country: Option, + pub business_label: Option<&'a String>, + pub attempt_count: i16, + pub payment_confirm_source: Option, +} + +impl<'a> KafkaPaymentIntentEvent<'a> { + pub fn from_storage(intent: &'a PaymentIntent) -> Self { + Self { + payment_id: &intent.payment_id, + merchant_id: &intent.merchant_id, + status: intent.status, + amount: intent.amount, + currency: intent.currency, + amount_captured: intent.amount_captured, + customer_id: intent.customer_id.as_ref(), + description: intent.description.as_ref(), + return_url: intent.return_url.as_ref(), + connector_id: intent.connector_id.as_ref(), + statement_descriptor_name: intent.statement_descriptor_name.as_ref(), + statement_descriptor_suffix: intent.statement_descriptor_suffix.as_ref(), + created_at: intent.created_at.assume_utc(), + modified_at: intent.modified_at.assume_utc(), + last_synced: intent.last_synced.map(|i| i.assume_utc()), + setup_future_usage: intent.setup_future_usage, + off_session: intent.off_session, + client_secret: intent.client_secret.as_ref(), + active_attempt_id: intent.active_attempt.get_id(), + business_country: intent.business_country, + business_label: intent.business_label.as_ref(), + attempt_count: intent.attempt_count, + payment_confirm_source: intent.payment_confirm_source, + } + } +} + +impl<'a> super::KafkaMessage for KafkaPaymentIntentEvent<'a> { + fn key(&self) -> String { + format!("{}_{}", self.merchant_id, self.payment_id) + } + + fn event_type(&self) -> crate::events::EventType { + crate::events::EventType::PaymentIntent + } +} diff --git a/crates/router/src/services/kafka/refund.rs b/crates/router/src/services/kafka/refund.rs index 4bfe2cd31eca..d5ef71bf651f 100644 --- a/crates/router/src/services/kafka/refund.rs +++ b/crates/router/src/services/kafka/refund.rs @@ -1,3 +1,4 @@ +use common_utils::types::MinorUnit; use diesel_models::{enums as storage_enums, refund::Refund}; use time::OffsetDateTime; @@ -12,9 +13,9 @@ pub struct KafkaRefund<'a> { pub connector_refund_id: Option<&'a String>, pub external_reference_id: Option<&'a String>, pub refund_type: &'a storage_enums::RefundType, - pub total_amount: &'a i64, + pub total_amount: &'a MinorUnit, pub currency: &'a storage_enums::Currency, - pub refund_amount: &'a i64, + pub refund_amount: &'a MinorUnit, pub refund_status: &'a storage_enums::RefundStatus, pub sent_to_gateway: &'a bool, pub refund_error_message: Option<&'a String>, diff --git a/crates/router/src/services/kafka/refund_event.rs b/crates/router/src/services/kafka/refund_event.rs new file mode 100644 index 000000000000..6aa80b243c11 --- /dev/null +++ b/crates/router/src/services/kafka/refund_event.rs @@ -0,0 +1,74 @@ +use common_utils::types::MinorUnit; +use diesel_models::{enums as storage_enums, refund::Refund}; +use time::OffsetDateTime; + +#[serde_with::skip_serializing_none] +#[derive(serde::Serialize, Debug)] +pub struct KafkaRefundEvent<'a> { + pub internal_reference_id: &'a String, + pub refund_id: &'a String, //merchant_reference id + pub payment_id: &'a String, + pub merchant_id: &'a String, + pub connector_transaction_id: &'a String, + pub connector: &'a String, + pub connector_refund_id: Option<&'a String>, + pub external_reference_id: Option<&'a String>, + pub refund_type: &'a storage_enums::RefundType, + pub total_amount: &'a MinorUnit, + pub currency: &'a storage_enums::Currency, + pub refund_amount: &'a MinorUnit, + pub refund_status: &'a storage_enums::RefundStatus, + pub sent_to_gateway: &'a bool, + pub refund_error_message: Option<&'a String>, + pub refund_arn: Option<&'a String>, + #[serde(default, with = "time::serde::timestamp::milliseconds")] + pub created_at: OffsetDateTime, + #[serde(default, with = "time::serde::timestamp::milliseconds")] + pub modified_at: OffsetDateTime, + pub description: Option<&'a String>, + pub attempt_id: &'a String, + pub refund_reason: Option<&'a String>, + pub refund_error_code: Option<&'a String>, +} + +impl<'a> KafkaRefundEvent<'a> { + pub fn from_storage(refund: &'a Refund) -> Self { + Self { + internal_reference_id: &refund.internal_reference_id, + refund_id: &refund.refund_id, + payment_id: &refund.payment_id, + merchant_id: &refund.merchant_id, + connector_transaction_id: &refund.connector_transaction_id, + connector: &refund.connector, + connector_refund_id: refund.connector_refund_id.as_ref(), + external_reference_id: refund.external_reference_id.as_ref(), + refund_type: &refund.refund_type, + total_amount: &refund.total_amount, + currency: &refund.currency, + refund_amount: &refund.refund_amount, + refund_status: &refund.refund_status, + sent_to_gateway: &refund.sent_to_gateway, + refund_error_message: refund.refund_error_message.as_ref(), + refund_arn: refund.refund_arn.as_ref(), + created_at: refund.created_at.assume_utc(), + modified_at: refund.updated_at.assume_utc(), + description: refund.description.as_ref(), + attempt_id: &refund.attempt_id, + refund_reason: refund.refund_reason.as_ref(), + refund_error_code: refund.refund_error_code.as_ref(), + } + } +} + +impl<'a> super::KafkaMessage for KafkaRefundEvent<'a> { + fn key(&self) -> String { + format!( + "{}_{}_{}_{}", + self.merchant_id, self.payment_id, self.attempt_id, self.refund_id + ) + } + + fn event_type(&self) -> crate::events::EventType { + crate::events::EventType::Refund + } +} diff --git a/crates/router/src/services/pm_auth.rs b/crates/router/src/services/pm_auth.rs index a54ba8819954..d63dadc8b157 100644 --- a/crates/router/src/services/pm_auth.rs +++ b/crates/router/src/services/pm_auth.rs @@ -7,12 +7,12 @@ use pm_auth::{ use crate::{ core::errors::{self}, logger, - routes::AppState, + routes::SessionState, services::{self}, }; pub async fn execute_connector_processing_step<'b, 'a, T, Req, Resp>( - state: &'b AppState, + state: &'b SessionState, connector_integration: BoxedConnectorIntegration<'a, T, Req, Resp>, req: &'b PaymentAuthRouterData, connector: &pm_auth_types::PaymentMethodAuthConnectors, diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index fc1b59f431ce..e17ec7916377 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -21,7 +21,7 @@ use std::marker::PhantomData; pub use api_models::{enums::Connector, mandates}; #[cfg(feature = "payouts")] pub use api_models::{enums::PayoutConnectors, payouts as payout_types}; -pub use common_utils::request::RequestContent; +pub use common_utils::{pii, pii::Email, request::RequestContent, types::MinorUnit}; #[cfg(feature = "payouts")] pub use hyperswitch_domain_models::router_request_types::PayoutsData; #[cfg(feature = "payouts")] @@ -761,6 +761,7 @@ impl ForeignFrom<&SetupMandateRouterData> for PaymentsAuthorizeData { email: data.request.email.clone(), customer_name: data.request.customer_name.clone(), amount: 0, + minor_amount: MinorUnit::new(0), statement_descriptor: None, capture_method: None, webhook_url: None, diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 9ad0542243aa..c427f60cd1f9 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -29,6 +29,9 @@ pub mod webhooks; use std::{fmt::Debug, str::FromStr}; use error_stack::{report, ResultExt}; +pub use hyperswitch_domain_models::router_flow_types::{ + access_token_auth::AccessTokenAuth, webhooks::VerifyWebhookSource, +}; #[cfg(feature = "frm")] pub use self::fraud_check::*; @@ -50,10 +53,6 @@ use crate::{ services::{request, ConnectorIntegration, ConnectorRedirectResponse, ConnectorValidation}, types::{self, api::enums as api_enums}, }; - -#[derive(Clone, Debug)] -pub struct AccessTokenAuth; - pub trait ConnectorAccessToken: ConnectorIntegration { @@ -66,9 +65,6 @@ pub enum ConnectorCallType { SessionMultiple(Vec), } -#[derive(Clone, Debug)] -pub struct VerifyWebhookSource; - pub trait ConnectorVerifyWebhookSource: ConnectorIntegration< VerifyWebhookSource, @@ -170,7 +166,6 @@ pub trait Connector: Send + Refund + Payment - + Debug + ConnectorRedirectResponse + IncomingWebhook + ConnectorAccessToken @@ -192,7 +187,6 @@ pub struct Pe; impl< T: Refund + Payment - + Debug + ConnectorRedirectResponse + Send + IncomingWebhook @@ -224,7 +218,7 @@ pub enum GetToken { /// Routing algorithm will output merchant connector identifier instead of connector name /// In order to support backwards compatibility for older routing algorithms and merchant accounts /// the support for connector name is retained -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ConnectorData { pub connector: BoxedConnector, pub connector_name: types::Connector, @@ -284,7 +278,7 @@ impl ConnectorData { let connector_name = api_enums::Connector::from_str(name) .change_context(errors::ConnectorError::InvalidConnectorName) .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("unable to parse connector name {connector:?}"))?; + .attach_printable_lazy(|| format!("unable to parse connector name {name}"))?; Ok(Self { connector, connector_name, @@ -304,9 +298,7 @@ impl ConnectorData { let payout_connector_name = api_enums::PayoutConnectors::from_str(name) .change_context(errors::ConnectorError::InvalidConnectorName) .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| { - format!("unable to parse payout connector name {connector:?}") - })?; + .attach_printable_lazy(|| format!("unable to parse payout connector name {name}"))?; let connector_name = api_enums::Connector::from(payout_connector_name); Ok(Self { connector, @@ -364,7 +356,7 @@ impl ConnectorData { enums::Connector::Klarna => Ok(Box::new(&connector::Klarna)), // enums::Connector::Mifinity => Ok(Box::new(&connector::Mifinity)), Added as template code for future usage enums::Connector::Mollie => Ok(Box::new(&connector::Mollie)), - enums::Connector::Nmi => Ok(Box::new(&connector::Nmi)), + enums::Connector::Nmi => Ok(Box::new(connector::Nmi::new())), enums::Connector::Noon => Ok(Box::new(&connector::Noon)), enums::Connector::Nuvei => Ok(Box::new(&connector::Nuvei)), enums::Connector::Opennode => Ok(Box::new(&connector::Opennode)), diff --git a/crates/router/src/types/api/authentication.rs b/crates/router/src/types/api/authentication.rs index 9837059dc012..85d4d3718c77 100644 --- a/crates/router/src/types/api/authentication.rs +++ b/crates/router/src/types/api/authentication.rs @@ -119,7 +119,7 @@ pub trait ExternalAuthentication: { } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct AuthenticationConnectorData { pub connector: BoxedConnector, pub connector_name: enums::AuthenticationConnectors, diff --git a/crates/router/src/types/api/disputes.rs b/crates/router/src/types/api/disputes.rs index 85fdb307ed68..a8107fe4c9d8 100644 --- a/crates/router/src/types/api/disputes.rs +++ b/crates/router/src/types/api/disputes.rs @@ -8,6 +8,8 @@ pub struct DisputeId { pub dispute_id: String, } +pub use hyperswitch_domain_models::router_flow_types::dispute::{Accept, Defend, Evidence}; + #[derive(Default, Debug)] pub struct DisputePayload { pub amount: String, @@ -58,9 +60,6 @@ pub enum EvidenceType { UncategorizedFile, } -#[derive(Debug, Clone)] -pub struct Accept; - pub trait AcceptDispute: services::ConnectorIntegration< Accept, @@ -70,9 +69,6 @@ pub trait AcceptDispute: { } -#[derive(Debug, Clone)] -pub struct Evidence; - pub trait SubmitEvidence: services::ConnectorIntegration< Evidence, @@ -82,9 +78,6 @@ pub trait SubmitEvidence: { } -#[derive(Debug, Clone)] -pub struct Defend; - pub trait DefendDispute: services::ConnectorIntegration< Defend, diff --git a/crates/router/src/types/api/files.rs b/crates/router/src/types/api/files.rs index 688962bd8367..bef393bf946c 100644 --- a/crates/router/src/types/api/files.rs +++ b/crates/router/src/types/api/files.rs @@ -1,4 +1,5 @@ use api_models::enums::FileUploadProvider; +pub use hyperswitch_domain_models::router_flow_types::files::{Retrieve, Upload}; use masking::{Deserialize, Serialize}; use serde_with::serde_as; @@ -67,17 +68,11 @@ pub enum FilePurpose { DisputeEvidence, } -#[derive(Debug, Clone)] -pub struct Upload; - pub trait UploadFile: services::ConnectorIntegration { } -#[derive(Debug, Clone)] -pub struct Retrieve; - pub trait RetrieveFile: services::ConnectorIntegration< Retrieve, diff --git a/crates/router/src/types/api/fraud_check.rs b/crates/router/src/types/api/fraud_check.rs index 45c79930e80c..6c50ed9db90b 100644 --- a/crates/router/src/types/api/fraud_check.rs +++ b/crates/router/src/types/api/fraud_check.rs @@ -3,6 +3,9 @@ use std::str::FromStr; use api_models::enums; use common_utils::errors::CustomResult; use error_stack::ResultExt; +pub use hyperswitch_domain_models::router_flow_types::fraud_check::{ + Checkout, Fulfillment, RecordReturn, Sale, Transaction, +}; use super::{BoxedConnector, ConnectorData, SessionConnectorData}; use crate::{ @@ -15,47 +18,32 @@ use crate::{ }, }; -#[derive(Debug, Clone)] -pub struct Sale; - pub trait FraudCheckSale: api::ConnectorIntegration { } -#[derive(Debug, Clone)] -pub struct Checkout; - pub trait FraudCheckCheckout: api::ConnectorIntegration { } -#[derive(Debug, Clone)] -pub struct Transaction; - pub trait FraudCheckTransaction: api::ConnectorIntegration { } -#[derive(Debug, Clone)] -pub struct Fulfillment; - pub trait FraudCheckFulfillment: api::ConnectorIntegration { } -#[derive(Debug, Clone)] -pub struct RecordReturn; - pub trait FraudCheckRecordReturn: api::ConnectorIntegration { } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct FraudCheckConnectorData { pub connector: BoxedConnector, pub connector_name: enums::FrmConnectors, diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index 0e80fd224da1..71b90ab41c3d 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -11,7 +11,7 @@ use crate::{ payment_methods, }, newtype, - routes::AppState, + routes::SessionState, types::{ api, domain, storage::{self, enums as storage_enums}, @@ -26,7 +26,7 @@ newtype!( #[async_trait::async_trait] pub(crate) trait MandateResponseExt: Sized { async fn from_db_mandate( - state: &AppState, + state: &SessionState, key_store: domain::MerchantKeyStore, mandate: storage::Mandate, storage_scheme: storage_enums::MerchantStorageScheme, @@ -36,7 +36,7 @@ pub(crate) trait MandateResponseExt: Sized { #[async_trait::async_trait] impl MandateResponseExt for MandateResponse { async fn from_db_mandate( - state: &AppState, + state: &SessionState, key_store: domain::MerchantKeyStore, mandate: storage::Mandate, storage_scheme: storage_enums::MerchantStorageScheme, diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index 923f4a552685..c1f045c74a7d 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -15,6 +15,11 @@ pub use api_models::payments::{ WalletData, }; use error_stack::ResultExt; +pub use hyperswitch_domain_models::router_flow_types::payments::{ + Approve, Authorize, AuthorizeSessionToken, Balance, Capture, CompleteAuthorize, + CreateConnectorCustomer, IncrementalAuthorization, InitPayment, PSync, PaymentMethodToken, + PreProcessing, Reject, Session, SetupMandate, Void, +}; use crate::{ core::errors, @@ -24,55 +29,6 @@ use crate::{ impl super::Router for PaymentsRequest {} -// Core related api layer. -#[derive(Debug, Clone)] -pub struct Authorize; - -#[derive(Debug, Clone)] -pub struct AuthorizeSessionToken; - -#[derive(Debug, Clone)] -pub struct CompleteAuthorize; - -#[derive(Debug, Clone)] -pub struct Approve; - -// Used in gift cards balance check -#[derive(Debug, Clone)] -pub struct Balance; - -#[derive(Debug, Clone)] -pub struct InitPayment; - -#[derive(Debug, Clone)] -pub struct Capture; - -#[derive(Debug, Clone)] -pub struct PSync; -#[derive(Debug, Clone)] -pub struct Void; - -#[derive(Debug, Clone)] -pub struct Reject; - -#[derive(Debug, Clone)] -pub struct Session; - -#[derive(Debug, Clone)] -pub struct PaymentMethodToken; - -#[derive(Debug, Clone)] -pub struct CreateConnectorCustomer; - -#[derive(Debug, Clone)] -pub struct SetupMandate; - -#[derive(Debug, Clone)] -pub struct PreProcessing; - -#[derive(Debug, Clone)] -pub struct IncrementalAuthorization; - pub trait PaymentIdTypeExt { fn get_payment_intent_id(&self) -> errors::CustomResult; } diff --git a/crates/router/src/types/api/payouts.rs b/crates/router/src/types/api/payouts.rs index 53f015290df1..d4417c5f1ccd 100644 --- a/crates/router/src/types/api/payouts.rs +++ b/crates/router/src/types/api/payouts.rs @@ -4,30 +4,12 @@ pub use api_models::payouts::{ PayoutListFilters, PayoutListResponse, PayoutMethodData, PayoutRequest, PayoutRetrieveBody, PayoutRetrieveRequest, PixBankTransfer, SepaBankTransfer, Wallet as WalletPayout, }; +pub use hyperswitch_domain_models::router_flow_types::payouts::{ + PoCancel, PoCreate, PoEligibility, PoFulfill, PoQuote, PoRecipient, PoRecipientAccount, +}; use crate::{services::api, types}; -#[derive(Debug, Clone)] -pub struct PoCancel; - -#[derive(Debug, Clone)] -pub struct PoCreate; - -#[derive(Debug, Clone)] -pub struct PoEligibility; - -#[derive(Debug, Clone)] -pub struct PoFulfill; - -#[derive(Debug, Clone)] -pub struct PoQuote; - -#[derive(Debug, Clone)] -pub struct PoRecipient; - -#[derive(Debug, Clone)] -pub struct PoRecipientAccount; - pub trait PayoutCancel: api::ConnectorIntegration { diff --git a/crates/router/src/types/api/refunds.rs b/crates/router/src/types/api/refunds.rs index cb3306c6ffd0..ac0433080cc6 100644 --- a/crates/router/src/types/api/refunds.rs +++ b/crates/router/src/types/api/refunds.rs @@ -2,6 +2,7 @@ pub use api_models::refunds::{ RefundRequest, RefundResponse, RefundStatus, RefundType, RefundUpdateRequest, RefundsRetrieveRequest, }; +pub use hyperswitch_domain_models::router_flow_types::refunds::{Execute, RSync}; use super::ConnectorCommon; use crate::{ @@ -21,11 +22,6 @@ impl ForeignFrom for RefundStatus { } } -#[derive(Debug, Clone)] -pub struct Execute; -#[derive(Debug, Clone)] -pub struct RSync; - pub trait RefundExecute: api::ConnectorIntegration { diff --git a/crates/router/src/types/api/verify_connector.rs b/crates/router/src/types/api/verify_connector.rs index cf7ba2382a33..0b932f9aeb1e 100644 --- a/crates/router/src/types/api/verify_connector.rs +++ b/crates/router/src/types/api/verify_connector.rs @@ -1,6 +1,5 @@ pub mod paypal; pub mod stripe; - use error_stack::ResultExt; use crate::{ @@ -9,10 +8,10 @@ use crate::{ services, services::ConnectorIntegration, types::{self, api, domain, storage::enums as storage_enums}, - AppState, + SessionState, }; -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct VerifyConnectorData { pub connector: &'static (dyn api::Connector + Sync), pub connector_auth: types::ConnectorAuthType, @@ -26,6 +25,7 @@ impl VerifyConnectorData { email: None, customer_name: None, amount: 1000, + minor_amount: common_utils::types::MinorUnit::new(1000), confirm: true, currency: storage_enums::Currency::USD, metadata: None, @@ -113,7 +113,7 @@ impl VerifyConnectorData { #[async_trait::async_trait] pub trait VerifyConnector { async fn verify( - state: &AppState, + state: &SessionState, connector_data: VerifyConnectorData, ) -> errors::RouterResponse<()> { let authorize_data = connector_data.get_payment_authorize_data(); @@ -147,7 +147,7 @@ pub trait VerifyConnector { } async fn get_access_token( - _state: &AppState, + _state: &SessionState, _connector_data: VerifyConnectorData, ) -> errors::CustomResult, errors::ApiErrorResponse> { // AccessToken is None for the connectors without the AccessToken Flow. diff --git a/crates/router/src/types/api/verify_connector/paypal.rs b/crates/router/src/types/api/verify_connector/paypal.rs index 2cb2a8061d12..99b7feb66166 100644 --- a/crates/router/src/types/api/verify_connector/paypal.rs +++ b/crates/router/src/types/api/verify_connector/paypal.rs @@ -4,7 +4,7 @@ use super::{VerifyConnector, VerifyConnectorData}; use crate::{ connector, core::errors, - routes::AppState, + routes::SessionState, services, types::{self, api}, }; @@ -12,7 +12,7 @@ use crate::{ #[async_trait::async_trait] impl VerifyConnector for connector::Paypal { async fn get_access_token( - state: &AppState, + state: &SessionState, connector_data: VerifyConnectorData, ) -> errors::CustomResult, errors::ApiErrorResponse> { let token_data: types::AccessTokenRequestData = diff --git a/crates/router/src/types/api/webhooks.rs b/crates/router/src/types/api/webhooks.rs index 964885cae551..726a5a154269 100644 --- a/crates/router/src/types/api/webhooks.rs +++ b/crates/router/src/types/api/webhooks.rs @@ -146,7 +146,7 @@ pub trait IncomingWebhook: ConnectorCommon + Sync { async fn verify_webhook_source_verification_call( &self, - state: &crate::routes::AppState, + state: &crate::routes::SessionState, merchant_account: &domain::MerchantAccount, merchant_connector_account: domain::MerchantConnectorAccount, connector_name: &str, diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index 91323b0c16d3..60d75ce81d1b 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -30,7 +30,7 @@ use crate::{ errors::{self, UserErrors, UserResult}, }, db::StorageInterface, - routes::AppState, + routes::SessionState, services::{self, authentication as auth, authentication::UserFromToken, authorization::info}, types::transformers::ForeignFrom, utils::{self, user::password}, @@ -237,7 +237,7 @@ impl UserCompanyName { pub struct NewUserOrganization(diesel_org::OrganizationNew); impl NewUserOrganization { - pub async fn insert_org_in_db(self, state: AppState) -> UserResult { + pub async fn insert_org_in_db(self, state: SessionState) -> UserResult { state .store .insert_organization(self.0) @@ -351,7 +351,7 @@ impl NewUserMerchant { self.new_organization.clone() } - pub async fn check_if_already_exists_in_db(&self, state: AppState) -> UserResult<()> { + pub async fn check_if_already_exists_in_db(&self, state: SessionState) -> UserResult<()> { if state .store .get_merchant_key_store_by_merchant_id( @@ -370,7 +370,10 @@ impl NewUserMerchant { Ok(()) } - pub async fn create_new_merchant_and_insert_in_db(&self, state: AppState) -> UserResult<()> { + pub async fn create_new_merchant_and_insert_in_db( + &self, + state: SessionState, + ) -> UserResult<()> { self.check_if_already_exists_in_db(state.clone()).await?; Box::pin(admin::create_merchant_account( state.clone(), @@ -554,7 +557,7 @@ impl NewUser { .attach_printable("Error while inserting user") } - pub async fn check_if_already_exists_in_db(&self, state: AppState) -> UserResult<()> { + pub async fn check_if_already_exists_in_db(&self, state: SessionState) -> UserResult<()> { if state .store .find_user_by_email(&self.get_email().into_inner()) @@ -568,7 +571,7 @@ impl NewUser { pub async fn insert_user_and_merchant_in_db( &self, - state: AppState, + state: SessionState, ) -> UserResult { self.check_if_already_exists_in_db(state.clone()).await?; let db = state.store.as_ref(); @@ -585,7 +588,7 @@ impl NewUser { pub async fn insert_user_role_in_db( self, - state: AppState, + state: SessionState, role_id: String, user_status: UserStatus, ) -> UserResult { @@ -790,7 +793,7 @@ impl UserFromStorage { self.0.email.clone() } - pub async fn get_role_from_db(&self, state: AppState) -> UserResult { + pub async fn get_role_from_db(&self, state: SessionState) -> UserResult { state .store .find_user_role_by_user_id(&self.0.user_id) @@ -798,7 +801,7 @@ impl UserFromStorage { .change_context(UserErrors::InternalServerError) } - pub async fn get_roles_from_db(&self, state: &AppState) -> UserResult> { + pub async fn get_roles_from_db(&self, state: &SessionState) -> UserResult> { state .store .list_user_roles_by_user_id(&self.0.user_id) @@ -807,7 +810,7 @@ impl UserFromStorage { } #[cfg(feature = "email")] - pub fn get_verification_days_left(&self, state: &AppState) -> UserResult> { + pub fn get_verification_days_left(&self, state: &SessionState) -> UserResult> { if self.0.is_verified { return Ok(None); } @@ -829,7 +832,7 @@ impl UserFromStorage { Ok(Some(days_left_for_verification.whole_days())) } - pub fn is_password_rotate_required(&self, state: &AppState) -> UserResult { + pub fn is_password_rotate_required(&self, state: &SessionState) -> UserResult { let last_password_modified_at = if let Some(last_password_modified_at) = self.0.last_password_modified_at { last_password_modified_at.date() @@ -855,7 +858,7 @@ impl UserFromStorage { pub async fn get_role_from_db_by_merchant_id( &self, - state: &AppState, + state: &SessionState, merchant_id: &str, ) -> CustomResult { state @@ -866,7 +869,7 @@ impl UserFromStorage { pub async fn get_preferred_or_active_user_role_from_db( &self, - state: &AppState, + state: &SessionState, ) -> CustomResult { if let Some(preferred_merchant_id) = self.get_preferred_merchant_id() { self.get_role_from_db_by_merchant_id(state, &preferred_merchant_id) @@ -887,7 +890,7 @@ impl UserFromStorage { } } - pub async fn get_or_create_key_store(&self, state: &AppState) -> UserResult { + pub async fn get_or_create_key_store(&self, state: &SessionState) -> UserResult { let master_key = state.store.get_master_key(); let key_store_result = state .store @@ -936,7 +939,7 @@ impl UserFromStorage { pub async fn decrypt_and_get_totp_secret( &self, - state: &AppState, + state: &SessionState, ) -> UserResult>> { if self.0.totp_secret.is_none() { return Ok(None); @@ -1023,7 +1026,7 @@ impl SignInWithRoleStrategyType { pub async fn get_signin_response( self, - state: &AppState, + state: &SessionState, ) -> UserResult { match self { Self::SingleRole(strategy) => strategy.get_signin_response(state).await, @@ -1038,7 +1041,10 @@ pub struct SignInWithSingleRoleStrategy { } impl SignInWithSingleRoleStrategy { - async fn get_signin_response(self, state: &AppState) -> UserResult { + async fn get_signin_response( + self, + state: &SessionState, + ) -> UserResult { let token = utils::user::generate_jwt_auth_token(state, &self.user, &self.user_role).await?; utils::user_role::set_role_permissions_in_cache_by_user_role(state, &self.user_role).await; @@ -1058,7 +1064,10 @@ pub struct SignInWithMultipleRolesStrategy { } impl SignInWithMultipleRolesStrategy { - async fn get_signin_response(self, state: &AppState) -> UserResult { + async fn get_signin_response( + self, + state: &SessionState, + ) -> UserResult { let merchant_accounts = state .store .list_multiple_merchant_accounts( diff --git a/crates/router/src/types/domain/user/decision_manager.rs b/crates/router/src/types/domain/user/decision_manager.rs index 5a0388c7f36f..41ae12350fb3 100644 --- a/crates/router/src/types/domain/user/decision_manager.rs +++ b/crates/router/src/types/domain/user/decision_manager.rs @@ -5,7 +5,7 @@ use masking::Secret; use super::UserFromStorage; use crate::{ core::errors::{StorageErrorExt, UserErrors, UserResult}, - routes::AppState, + routes::SessionState, services::authentication as auth, utils, }; @@ -17,7 +17,7 @@ pub enum UserFlow { } impl UserFlow { - async fn is_required(&self, user: &UserFromStorage, state: &AppState) -> UserResult { + async fn is_required(&self, user: &UserFromStorage, state: &SessionState) -> UserResult { match self { Self::SPTFlow(flow) => flow.is_required(user, state).await, Self::JWTFlow(flow) => flow.is_required(user, state).await, @@ -36,7 +36,7 @@ pub enum SPTFlow { } impl SPTFlow { - async fn is_required(&self, user: &UserFromStorage, state: &AppState) -> UserResult { + async fn is_required(&self, user: &UserFromStorage, state: &SessionState) -> UserResult { match self { // TOTP Self::TOTP => Ok(true), @@ -54,7 +54,7 @@ impl SPTFlow { pub async fn generate_spt( self, - state: &AppState, + state: &SessionState, next_flow: &NextFlow, ) -> UserResult> { auth::SinglePurposeToken::new_token( @@ -74,13 +74,17 @@ pub enum JWTFlow { } impl JWTFlow { - async fn is_required(&self, _user: &UserFromStorage, _state: &AppState) -> UserResult { + async fn is_required( + &self, + _user: &UserFromStorage, + _state: &SessionState, + ) -> UserResult { Ok(true) } pub async fn generate_jwt( self, - state: &AppState, + state: &SessionState, next_flow: &NextFlow, user_role: &UserRole, ) -> UserResult> { @@ -183,7 +187,7 @@ impl CurrentFlow { }) } - pub async fn next(&self, user: UserFromStorage, state: &AppState) -> UserResult { + pub async fn next(&self, user: UserFromStorage, state: &SessionState) -> UserResult { let flows = self.origin.get_flows(); let remaining_flows = flows.iter().skip(self.current_flow_index + 1); for flow in remaining_flows { @@ -209,7 +213,7 @@ impl NextFlow { pub async fn from_origin( origin: Origin, user: UserFromStorage, - state: &AppState, + state: &SessionState, ) -> UserResult { let flows = origin.get_flows(); for flow in flows { @@ -228,7 +232,7 @@ impl NextFlow { self.next_flow } - pub async fn get_token(&self, state: &AppState) -> UserResult> { + pub async fn get_token(&self, state: &SessionState) -> UserResult> { match self.next_flow { UserFlow::SPTFlow(spt_flow) => spt_flow.generate_spt(state, self).await, UserFlow::JWTFlow(jwt_flow) => { @@ -251,7 +255,7 @@ impl NextFlow { pub async fn get_token_with_user_role( &self, - state: &AppState, + state: &SessionState, user_role: &UserRole, ) -> UserResult> { match self.next_flow { diff --git a/crates/router/src/types/storage/payment_attempt.rs b/crates/router/src/types/storage/payment_attempt.rs index 4ad45f554ce7..1cba84ad9f03 100644 --- a/crates/router/src/types/storage/payment_attempt.rs +++ b/crates/router/src/types/storage/payment_attempt.rs @@ -124,8 +124,11 @@ mod tests { ..PaymentAttemptNew::default() }; - let response = state - .store + let store = state + .stores + .get(state.conf.multitenancy.get_tenant_names().first().unwrap()) + .unwrap(); + let response = store .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly) .await .unwrap(); @@ -165,14 +168,16 @@ mod tests { attempt_id: attempt_id.clone(), ..PaymentAttemptNew::default() }; - state - .store + let store = state + .stores + .get(state.conf.multitenancy.get_tenant_names().first().unwrap()) + .unwrap(); + store .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly) .await .unwrap(); - let response = state - .store + let response = store .find_payment_attempt_by_payment_id_merchant_id_attempt_id( &payment_id, &merchant_id, @@ -218,14 +223,16 @@ mod tests { attempt_id: uuid.clone(), ..PaymentAttemptNew::default() }; - state - .store + let store = state + .stores + .get(state.conf.multitenancy.get_tenant_names().first().unwrap()) + .unwrap(); + store .insert_payment_attempt(payment_attempt, enums::MerchantStorageScheme::PostgresOnly) .await .unwrap(); - let response = state - .store + let response = store .find_payment_attempt_by_payment_id_merchant_id_attempt_id( &uuid, "1", diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index da6cac195ca2..b83d3138b7ce 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -826,7 +826,7 @@ pub async fn trigger_payments_webhook( key_store: &domain::MerchantKeyStore, payment_data: crate::core::payments::PaymentData, customer: Option, - state: &crate::routes::AppState, + state: &crate::routes::SessionState, operation: Op, ) -> RouterResult<()> where @@ -858,7 +858,7 @@ where captures, customer, services::AuthFlow::Merchant, - &state.conf.server, + &state.base_url, &operation, &state.conf.connector_request_reference_id_config, None, diff --git a/crates/router/src/utils/connector_onboarding.rs b/crates/router/src/utils/connector_onboarding.rs index aeea146df292..15ad2eb89540 100644 --- a/crates/router/src/utils/connector_onboarding.rs +++ b/crates/router/src/utils/connector_onboarding.rs @@ -5,7 +5,7 @@ use super::errors::StorageErrorExt; use crate::{ consts, core::errors::{ApiErrorResponse, NotImplementedMessage, RouterResult}, - routes::{app::settings, AppState}, + routes::{app::settings, SessionState}, types::{self, api::enums}, }; @@ -41,7 +41,7 @@ pub fn is_enabled( } pub async fn check_if_connector_exists( - state: &AppState, + state: &SessionState, connector_id: &str, merchant_id: &str, ) -> RouterResult<()> { @@ -70,7 +70,7 @@ pub async fn check_if_connector_exists( } pub async fn set_tracking_id_in_configs( - state: &AppState, + state: &SessionState, connector_id: &str, connector: enums::Connector, ) -> RouterResult<()> { @@ -115,7 +115,7 @@ pub async fn set_tracking_id_in_configs( } pub async fn get_tracking_id_from_configs( - state: &AppState, + state: &SessionState, connector_id: &str, connector: enums::Connector, ) -> RouterResult { diff --git a/crates/router/src/utils/connector_onboarding/paypal.rs b/crates/router/src/utils/connector_onboarding/paypal.rs index 708763a44409..9827a4b09375 100644 --- a/crates/router/src/utils/connector_onboarding/paypal.rs +++ b/crates/router/src/utils/connector_onboarding/paypal.rs @@ -5,7 +5,7 @@ use http::header; use crate::{ connector, core::errors::{ApiErrorResponse, RouterResult}, - routes::AppState, + routes::SessionState, types, types::api::{ enums, @@ -14,7 +14,7 @@ use crate::{ utils::verify_connector as verify_connector_utils, }; -pub async fn generate_access_token(state: AppState) -> RouterResult { +pub async fn generate_access_token(state: SessionState) -> RouterResult { let connector = enums::Connector::Paypal; let boxed_connector = types::api::ConnectorData::convert_connector( &state.conf.connectors, diff --git a/crates/router/src/utils/currency.rs b/crates/router/src/utils/currency.rs index 5cf68635c4e1..c16b46e51870 100644 --- a/crates/router/src/utils/currency.rs +++ b/crates/router/src/utils/currency.rs @@ -14,7 +14,7 @@ use tokio::{sync::RwLock, time::sleep}; use crate::{ logger, routes::app::settings::{Conversion, DefaultExchangeRates}, - services, AppState, + services, SessionState, }; const REDIX_FOREX_CACHE_KEY: &str = "{forex_cache}_lock"; const REDIX_FOREX_CACHE_DATA: &str = "{forex_cache}_data"; @@ -121,7 +121,7 @@ async fn save_forex_to_local( // Alternative handler for handling the case, When no data in local as well as redis #[allow(dead_code)] async fn waited_fetch_and_update_caches( - state: &AppState, + state: &SessionState, local_fetch_retry_delay: u64, local_fetch_retry_count: u64, ) -> CustomResult { @@ -171,7 +171,7 @@ impl From for CurrencyFactors { } } pub async fn get_forex_rates( - state: &AppState, + state: &SessionState, call_delay: i64, local_fetch_retry_delay: u64, local_fetch_retry_count: u64, @@ -197,7 +197,7 @@ pub async fn get_forex_rates( } async fn handler_local_no_data( - state: &AppState, + state: &SessionState, call_delay: i64, _local_fetch_retry_delay: u64, _local_fetch_retry_count: u64, @@ -216,7 +216,7 @@ async fn handler_local_no_data( } async fn successive_fetch_and_save_forex( - state: &AppState, + state: &SessionState, stale_redis_data: Option, ) -> CustomResult { match acquire_redis_lock(state).await { @@ -249,7 +249,7 @@ async fn successive_fetch_and_save_forex( } async fn successive_save_data_to_redis_local( - state: &AppState, + state: &SessionState, forex: FxExchangeRatesCacheEntry, ) -> CustomResult { Ok(save_forex_to_redis(state, &forex) @@ -268,7 +268,7 @@ async fn successive_save_data_to_redis_local( } async fn fallback_forex_redis_check( - state: &AppState, + state: &SessionState, redis_data: FxExchangeRatesCacheEntry, call_delay: i64, ) -> CustomResult { @@ -287,7 +287,7 @@ async fn fallback_forex_redis_check( } async fn handler_local_expired( - state: &AppState, + state: &SessionState, call_delay: i64, local_rates: FxExchangeRatesCacheEntry, ) -> CustomResult { @@ -316,7 +316,7 @@ async fn handler_local_expired( } async fn fetch_forex_rates( - state: &AppState, + state: &SessionState, ) -> Result> { let forex_api_key = state.conf.forex_api.get_inner().api_key.peek(); @@ -371,7 +371,7 @@ async fn fetch_forex_rates( } pub async fn fallback_fetch_forex_rates( - state: &AppState, + state: &SessionState, ) -> CustomResult { let fallback_forex_api_key = state.conf.forex_api.get_inner().fallback_api_key.peek(); @@ -438,7 +438,7 @@ pub async fn fallback_fetch_forex_rates( } async fn release_redis_lock( - state: &AppState, + state: &SessionState, ) -> Result> { state .store @@ -449,9 +449,9 @@ async fn release_redis_lock( .change_context(ForexCacheError::RedisLockReleaseFailed) } -async fn acquire_redis_lock(app_state: &AppState) -> CustomResult { - let forex_api = app_state.conf.forex_api.get_inner(); - app_state +async fn acquire_redis_lock(state: &SessionState) -> CustomResult { + let forex_api = state.conf.forex_api.get_inner(); + state .store .get_redis_conn() .change_context(ForexCacheError::RedisConnectionError)? @@ -472,7 +472,7 @@ async fn acquire_redis_lock(app_state: &AppState) -> CustomResult CustomResult<(), ForexCacheError> { app_state @@ -485,7 +485,7 @@ async fn save_forex_to_redis( } async fn retrieve_forex_from_redis( - app_state: &AppState, + app_state: &SessionState, ) -> CustomResult, ForexCacheError> { app_state .store @@ -510,7 +510,7 @@ async fn is_redis_expired( } pub async fn convert_currency( - state: AppState, + state: SessionState, amount: i64, to_currency: String, from_currency: String, diff --git a/crates/router/src/utils/ext_traits.rs b/crates/router/src/utils/ext_traits.rs index 7b08dc296c7b..33fdb4c9b25f 100644 --- a/crates/router/src/utils/ext_traits.rs +++ b/crates/router/src/utils/ext_traits.rs @@ -26,15 +26,12 @@ pub trait OptionExt { fn update_value(&mut self, value: Option); } -impl OptionExt for Option -where - T: std::fmt::Debug, -{ +impl OptionExt for Option { fn check_value_present(&self, field_name: &'static str) -> RouterResult<()> { when(self.is_none(), || { Err( Report::new(ApiErrorResponse::MissingRequiredField { field_name }) - .attach_printable(format!("Missing required field {field_name} in {self:?}")), + .attach_printable(format!("Missing required field {field_name}")), ) }) } @@ -46,7 +43,7 @@ where Some(v) => Ok(v), None => Err( Report::new(ApiErrorResponse::MissingRequiredField { field_name }) - .attach_printable(format!("Missing required field {field_name} in {self:?}")), + .attach_printable(format!("Missing required field {field_name}")), ), } } @@ -63,7 +60,7 @@ where E::from_str(value.as_ref()) .change_context(errors::ParsingError::UnknownError) - .attach_printable_lazy(|| format!("Invalid {{ {enum_name}: {value:?} }} ")) + .attach_printable_lazy(|| format!("Invalid {{ {enum_name} }} ")) } fn parse_value(self, type_name: &'static str) -> CustomResult diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index db1d0ea25088..eaa386d7f6cc 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -8,7 +8,7 @@ use redis_interface::RedisConnectionPool; use crate::{ core::errors::{StorageError, UserErrors, UserResult}, - routes::AppState, + routes::SessionState, services::{ authentication::{AuthToken, UserFromToken}, authorization::roles::RoleInfo, @@ -25,7 +25,7 @@ pub mod two_factor_auth; impl UserFromToken { pub async fn get_merchant_account_from_db( &self, - state: AppState, + state: SessionState, ) -> UserResult { let key_store = state .store @@ -55,7 +55,7 @@ impl UserFromToken { Ok(merchant_account) } - pub async fn get_user_from_db(&self, state: &AppState) -> UserResult { + pub async fn get_user_from_db(&self, state: &SessionState) -> UserResult { let user = state .store .find_user_by_id(&self.user_id) @@ -64,7 +64,7 @@ impl UserFromToken { Ok(user.into()) } - pub async fn get_role_info_from_db(&self, state: &AppState) -> UserResult { + pub async fn get_role_info_from_db(&self, state: &SessionState) -> UserResult { RoleInfo::from_role_id(state, &self.role_id, &self.merchant_id, &self.org_id) .await .change_context(UserErrors::InternalServerError) @@ -72,7 +72,7 @@ impl UserFromToken { } pub async fn generate_jwt_auth_token( - state: &AppState, + state: &SessionState, user: &UserFromStorage, user_role: &UserRole, ) -> UserResult> { @@ -88,7 +88,7 @@ pub async fn generate_jwt_auth_token( } pub async fn generate_jwt_auth_token_with_custom_role_attributes( - state: &AppState, + state: &SessionState, user: &UserFromStorage, merchant_id: String, org_id: String, @@ -106,7 +106,7 @@ pub async fn generate_jwt_auth_token_with_custom_role_attributes( } pub fn get_dashboard_entry_response( - state: &AppState, + state: &SessionState, user: UserFromStorage, user_role: UserRole, token: masking::Secret, @@ -126,7 +126,7 @@ pub fn get_dashboard_entry_response( #[allow(unused_variables)] pub fn get_verification_days_left( - state: &AppState, + state: &SessionState, user: &UserFromStorage, ) -> UserResult> { #[cfg(feature = "email")] @@ -176,7 +176,7 @@ pub fn get_multiple_merchant_details_with_status( } pub async fn get_user_from_db_by_email( - state: &AppState, + state: &SessionState, email: domain::UserEmail, ) -> CustomResult { state @@ -193,7 +193,7 @@ pub fn get_token_from_signin_response(resp: &user_api::SignInResponse) -> maskin } } -pub fn get_redis_connection(state: &AppState) -> UserResult> { +pub fn get_redis_connection(state: &SessionState) -> UserResult> { state .store .get_redis_conn() diff --git a/crates/router/src/utils/user/dashboard_metadata.rs b/crates/router/src/utils/user/dashboard_metadata.rs index 3979a8649447..5622a43f4dcf 100644 --- a/crates/router/src/utils/user/dashboard_metadata.rs +++ b/crates/router/src/utils/user/dashboard_metadata.rs @@ -13,11 +13,11 @@ use masking::Secret; use crate::{ core::errors::{UserErrors, UserResult}, - headers, AppState, + headers, SessionState, }; pub async fn insert_merchant_scoped_metadata_to_db( - state: &AppState, + state: &SessionState, user_id: String, merchant_id: String, org_id: String, @@ -50,7 +50,7 @@ pub async fn insert_merchant_scoped_metadata_to_db( }) } pub async fn insert_user_scoped_metadata_to_db( - state: &AppState, + state: &SessionState, user_id: String, merchant_id: String, org_id: String, @@ -84,7 +84,7 @@ pub async fn insert_user_scoped_metadata_to_db( } pub async fn get_merchant_scoped_metadata_from_db( - state: &AppState, + state: &SessionState, merchant_id: String, org_id: String, metadata_keys: Vec, @@ -97,7 +97,7 @@ pub async fn get_merchant_scoped_metadata_from_db( .attach_printable("DB Error Fetching DashboardMetaData") } pub async fn get_user_scoped_metadata_from_db( - state: &AppState, + state: &SessionState, user_id: String, merchant_id: String, org_id: String, @@ -121,7 +121,7 @@ pub async fn get_user_scoped_metadata_from_db( } pub async fn update_merchant_scoped_metadata( - state: &AppState, + state: &SessionState, user_id: String, merchant_id: String, org_id: String, @@ -149,7 +149,7 @@ pub async fn update_merchant_scoped_metadata( .change_context(UserErrors::InternalServerError) } pub async fn update_user_scoped_metadata( - state: &AppState, + state: &SessionState, user_id: String, merchant_id: String, org_id: String, diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index 256b115dbbad..9be2ddb57a4e 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -2,7 +2,7 @@ use api_models::{ enums::Connector::{DummyConnector4, DummyConnector7}, user::sample_data::SampleDataRequest, }; -use common_utils::id_type; +use common_utils::{id_type, types::MinorUnit}; use diesel_models::{user::sample_data::PaymentAttemptBatchNew, RefundNew}; use error_stack::ResultExt; use hyperswitch_domain_models::payments::payment_intent::PaymentIntentNew; @@ -12,12 +12,12 @@ use time::OffsetDateTime; use crate::{ consts, core::errors::sample_data::{SampleDataError, SampleDataResult}, - AppState, + SessionState, }; #[allow(clippy::type_complexity)] pub async fn generate_sample_data( - state: &AppState, + state: &SessionState, req: SampleDataRequest, merchant_id: &str, ) -> SampleDataResult)>> { @@ -179,7 +179,7 @@ pub async fn generate_sample_data( true => common_enums::IntentStatus::Failed, _ => common_enums::IntentStatus::Succeeded, }, - amount: common_utils::types::MinorUnit::new(amount * 100), + amount: MinorUnit::new(amount * 100), currency: Some( *currency_vec .get((num - 1) % currency_vec_len) @@ -197,7 +197,7 @@ pub async fn generate_sample_data( ), attempt_count: 1, customer_id: Some(dashboard_customer_id.clone()), - amount_captured: Some(common_utils::types::MinorUnit::new(amount * 100)), + amount_captured: Some(MinorUnit::new(amount * 100)), profile_id: Some(profile_id.clone()), return_url: Default::default(), metadata: Default::default(), @@ -291,8 +291,8 @@ pub async fn generate_sample_data( currency: *currency_vec .get((num - 1) % currency_vec_len) .unwrap_or(&common_enums::Currency::USD), - total_amount: amount * 100, - refund_amount: amount * 100, + total_amount: MinorUnit::new(amount * 100), + refund_amount: MinorUnit::new(amount * 100), refund_status: common_enums::RefundStatus::Success, sent_to_gateway: true, refund_type: diesel_models::enums::RefundType::InstantRefund, diff --git a/crates/router/src/utils/user/two_factor_auth.rs b/crates/router/src/utils/user/two_factor_auth.rs index 6bd693c07f5c..a0915f3ed86e 100644 --- a/crates/router/src/utils/user/two_factor_auth.rs +++ b/crates/router/src/utils/user/two_factor_auth.rs @@ -6,7 +6,7 @@ use totp_rs::{Algorithm, TOTP}; use crate::{ consts, core::errors::{UserErrors, UserResult}, - routes::AppState, + routes::SessionState, }; pub fn generate_default_totp( @@ -31,7 +31,7 @@ pub fn generate_default_totp( .change_context(UserErrors::InternalServerError) } -pub async fn check_totp_in_redis(state: &AppState, user_id: &str) -> UserResult { +pub async fn check_totp_in_redis(state: &SessionState, user_id: &str) -> UserResult { let redis_conn = super::get_redis_connection(state)?; let key = format!("{}{}", consts::user::REDIS_TOTP_PREFIX, user_id); redis_conn @@ -40,7 +40,7 @@ pub async fn check_totp_in_redis(state: &AppState, user_id: &str) -> UserResult< .change_context(UserErrors::InternalServerError) } -pub async fn check_recovery_code_in_redis(state: &AppState, user_id: &str) -> UserResult { +pub async fn check_recovery_code_in_redis(state: &SessionState, user_id: &str) -> UserResult { let redis_conn = super::get_redis_connection(state)?; let key = format!("{}{}", consts::user::REDIS_RECOVERY_CODE_PREFIX, user_id); redis_conn @@ -49,7 +49,7 @@ pub async fn check_recovery_code_in_redis(state: &AppState, user_id: &str) -> Us .change_context(UserErrors::InternalServerError) } -pub async fn insert_totp_in_redis(state: &AppState, user_id: &str) -> UserResult<()> { +pub async fn insert_totp_in_redis(state: &SessionState, user_id: &str) -> UserResult<()> { let redis_conn = super::get_redis_connection(state)?; let key = format!("{}{}", consts::user::REDIS_TOTP_PREFIX, user_id); redis_conn @@ -63,7 +63,7 @@ pub async fn insert_totp_in_redis(state: &AppState, user_id: &str) -> UserResult } pub async fn insert_totp_secret_in_redis( - state: &AppState, + state: &SessionState, user_id: &str, secret: &masking::Secret, ) -> UserResult<()> { @@ -79,7 +79,7 @@ pub async fn insert_totp_secret_in_redis( } pub async fn get_totp_secret_from_redis( - state: &AppState, + state: &SessionState, user_id: &str, ) -> UserResult>> { let redis_conn = super::get_redis_connection(state)?; @@ -90,7 +90,7 @@ pub async fn get_totp_secret_from_redis( .map(|secret| secret.map(Into::into)) } -pub async fn delete_totp_secret_from_redis(state: &AppState, user_id: &str) -> UserResult<()> { +pub async fn delete_totp_secret_from_redis(state: &SessionState, user_id: &str) -> UserResult<()> { let redis_conn = super::get_redis_connection(state)?; redis_conn .delete_key(&get_totp_secret_key(user_id)) @@ -103,7 +103,7 @@ fn get_totp_secret_key(user_id: &str) -> String { format!("{}{}", consts::user::REDIS_TOTP_SECRET_PREFIX, user_id) } -pub async fn insert_recovery_code_in_redis(state: &AppState, user_id: &str) -> UserResult<()> { +pub async fn insert_recovery_code_in_redis(state: &SessionState, user_id: &str) -> UserResult<()> { let redis_conn = super::get_redis_connection(state)?; let key = format!("{}{}", consts::user::REDIS_RECOVERY_CODE_PREFIX, user_id); redis_conn diff --git a/crates/router/src/utils/user_role.rs b/crates/router/src/utils/user_role.rs index ba4c28786670..d0ad08848ca2 100644 --- a/crates/router/src/utils/user_role.rs +++ b/crates/router/src/utils/user_role.rs @@ -9,7 +9,7 @@ use router_env::logger; use crate::{ consts, core::errors::{StorageErrorExt, UserErrors, UserResult}, - routes::AppState, + routes::SessionState, services::authorization::{self as authz, permissions::Permission, roles}, types::domain, }; @@ -73,7 +73,7 @@ pub fn validate_role_groups(groups: &[PermissionGroup]) -> UserResult<()> { } pub async fn validate_role_name( - state: &AppState, + state: &SessionState, role_name: &domain::RoleName, merchant_id: &str, org_id: &str, @@ -101,7 +101,7 @@ pub async fn validate_role_name( } pub async fn set_role_permissions_in_cache_by_user_role( - state: &AppState, + state: &SessionState, user_role: &UserRole, ) -> bool { set_role_permissions_in_cache_if_required( @@ -116,7 +116,7 @@ pub async fn set_role_permissions_in_cache_by_user_role( } pub async fn set_role_permissions_in_cache_if_required( - state: &AppState, + state: &SessionState, role_id: &str, merchant_id: &str, org_id: &str, @@ -143,7 +143,7 @@ pub async fn set_role_permissions_in_cache_if_required( } pub async fn get_multiple_role_info_for_user_roles( - state: &AppState, + state: &SessionState, user_roles: &[UserRole], ) -> UserResult> { futures::future::try_join_all(user_roles.iter().map(|user_role| async { diff --git a/crates/router/src/workflows/api_key_expiry.rs b/crates/router/src/workflows/api_key_expiry.rs index c5914810108c..dff7905a2624 100644 --- a/crates/router/src/workflows/api_key_expiry.rs +++ b/crates/router/src/workflows/api_key_expiry.rs @@ -1,12 +1,12 @@ use common_utils::{errors::ValidationError, ext_traits::ValueExt}; use diesel_models::{enums as storage_enums, ApiKeyExpiryTrackingData}; use router_env::logger; -use scheduler::{workflows::ProcessTrackerWorkflow, SchedulerAppState}; +use scheduler::{workflows::ProcessTrackerWorkflow, SchedulerSessionState}; use crate::{ errors, logger::error, - routes::{metrics, AppState}, + routes::{metrics, SessionState}, services::email::types::ApiKeyExpiryReminder, types::{api, domain::UserEmail, storage}, utils::OptionExt, @@ -15,10 +15,10 @@ use crate::{ pub struct ApiKeyExpiryWorkflow; #[async_trait::async_trait] -impl ProcessTrackerWorkflow for ApiKeyExpiryWorkflow { +impl ProcessTrackerWorkflow for ApiKeyExpiryWorkflow { async fn execute_workflow<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { let db = &*state.store; @@ -137,7 +137,7 @@ impl ProcessTrackerWorkflow for ApiKeyExpiryWorkflow { async fn error_handler<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, process: storage::ProcessTracker, _error: errors::ProcessTrackerError, ) -> errors::CustomResult<(), errors::ProcessTrackerError> { diff --git a/crates/router/src/workflows/attach_payout_account_workflow.rs b/crates/router/src/workflows/attach_payout_account_workflow.rs index 98d3f7844b4e..eb60510ad754 100644 --- a/crates/router/src/workflows/attach_payout_account_workflow.rs +++ b/crates/router/src/workflows/attach_payout_account_workflow.rs @@ -7,17 +7,17 @@ use scheduler::{ use crate::{ core::payouts, errors as core_errors, - routes::AppState, + routes::SessionState, types::{api, storage}, }; pub struct AttachPayoutAccountWorkflow; #[async_trait::async_trait] -impl ProcessTrackerWorkflow for AttachPayoutAccountWorkflow { +impl ProcessTrackerWorkflow for AttachPayoutAccountWorkflow { async fn execute_workflow<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { // Gather context @@ -63,7 +63,7 @@ impl ProcessTrackerWorkflow for AttachPayoutAccountWorkflow { async fn error_handler<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, error: errors::ProcessTrackerError, ) -> core_errors::CustomResult<(), errors::ProcessTrackerError> { diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index 726cbf9de09b..53b275dfebce 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -17,18 +17,18 @@ use crate::{ core::webhooks::{self as webhooks_core, types::OutgoingWebhookTrackingData}, db::StorageInterface, errors, logger, - routes::{app::ReqState, AppState}, + routes::{app::ReqState, SessionState}, types::{domain, storage}, }; pub struct OutgoingWebhookRetryWorkflow; #[async_trait::async_trait] -impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { +impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { #[instrument(skip_all)] async fn execute_workflow<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { let delivery_attempt = storage::enums::WebhookDeliveryAttempt::AutomaticRetry; @@ -114,7 +114,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { .peek() .parse_struct("OutgoingWebhookRequestContent")?; - webhooks_core::trigger_webhook_and_raise_event( + Box::pin(webhooks_core::trigger_webhook_and_raise_event( state.clone(), business_profile, &key_store, @@ -123,7 +123,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { delivery_attempt, None, Some(process), - ) + )) .await; } @@ -168,7 +168,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { errors::ProcessTrackerError::EApiErrorResponse })?; - webhooks_core::trigger_webhook_and_raise_event( + Box::pin(webhooks_core::trigger_webhook_and_raise_event( state.clone(), business_profile, &key_store, @@ -177,7 +177,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { delivery_attempt, Some(content), Some(process), - ) + )) .await; } // Resource status has changed since the event was created, finish task @@ -207,7 +207,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { #[instrument(skip_all)] async fn error_handler<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, error: errors::ProcessTrackerError, ) -> errors::CustomResult<(), errors::ProcessTrackerError> { @@ -313,7 +313,7 @@ pub(crate) async fn retry_webhook_delivery_task( #[instrument(skip_all)] async fn get_outgoing_webhook_content_and_event_type( - state: AppState, + state: SessionState, req_state: ReqState, merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index 14f372789ed3..c34b8ebe73ba 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -3,7 +3,7 @@ use error_stack::ResultExt; use router_env::logger; use scheduler::{ consumer::{self, types::process_data, workflows::ProcessTrackerWorkflow}, - errors as sch_errors, utils as scheduler_utils, SchedulerAppState, + errors as sch_errors, utils as scheduler_utils, }; use crate::{ @@ -14,7 +14,7 @@ use crate::{ }, db::StorageInterface, errors, - routes::AppState, + routes::SessionState, services, types::{ api, @@ -26,10 +26,10 @@ use crate::{ pub struct PaymentsSyncWorkflow; #[async_trait::async_trait] -impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { +impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { async fn execute_workflow<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, ) -> Result<(), sch_errors::ProcessTrackerError> { let db: &dyn StorageInterface = &*state.store; @@ -87,7 +87,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { match &payment_data.payment_attempt.status { status if terminal_status.contains(status) => { state - .get_db() + .store .as_scheduler() .finish_process_with_business_status(process, "COMPLETED_BY_PT".to_string()) .await? @@ -192,7 +192,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { async fn error_handler<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, error: sch_errors::ProcessTrackerError, ) -> errors::CustomResult<(), sch_errors::ProcessTrackerError> { diff --git a/crates/router/src/workflows/refund_router.rs b/crates/router/src/workflows/refund_router.rs index 934c208f9115..515e34c06897 100644 --- a/crates/router/src/workflows/refund_router.rs +++ b/crates/router/src/workflows/refund_router.rs @@ -1,16 +1,16 @@ use scheduler::consumer::workflows::ProcessTrackerWorkflow; use crate::{ - core::refunds as refund_flow, errors, logger::error, routes::AppState, types::storage, + core::refunds as refund_flow, errors, logger::error, routes::SessionState, types::storage, }; pub struct RefundWorkflowRouter; #[async_trait::async_trait] -impl ProcessTrackerWorkflow for RefundWorkflowRouter { +impl ProcessTrackerWorkflow for RefundWorkflowRouter { async fn execute_workflow<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { Ok(Box::pin(refund_flow::start_refund_workflow(state, &process)).await?) @@ -18,7 +18,7 @@ impl ProcessTrackerWorkflow for RefundWorkflowRouter { async fn error_handler<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, process: storage::ProcessTracker, _error: errors::ProcessTrackerError, ) -> errors::CustomResult<(), errors::ProcessTrackerError> { diff --git a/crates/router/src/workflows/tokenized_data.rs b/crates/router/src/workflows/tokenized_data.rs index 0674982f92fe..bc1842205ae4 100644 --- a/crates/router/src/workflows/tokenized_data.rs +++ b/crates/router/src/workflows/tokenized_data.rs @@ -1,16 +1,16 @@ use scheduler::consumer::workflows::ProcessTrackerWorkflow; use crate::{ - core::payment_methods::vault, errors, logger::error, routes::AppState, types::storage, + core::payment_methods::vault, errors, logger::error, routes::SessionState, types::storage, }; pub struct DeleteTokenizeDataWorkflow; #[async_trait::async_trait] -impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { +impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { async fn execute_workflow<'a>( &'a self, - state: &'a AppState, + state: &'a SessionState, process: storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { Ok(vault::start_tokenize_data_workflow(state, &process).await?) @@ -18,7 +18,7 @@ impl ProcessTrackerWorkflow for DeleteTokenizeDataWorkflow { async fn error_handler<'a>( &'a self, - _state: &'a AppState, + _state: &'a SessionState, process: storage::ProcessTracker, _error: errors::ProcessTrackerError, ) -> errors::CustomResult<(), errors::ProcessTrackerError> { diff --git a/crates/router/tests/cache.rs b/crates/router/tests/cache.rs index c6eef06db7b1..f0c79e4dcbc0 100644 --- a/crates/router/tests/cache.rs +++ b/crates/router/tests/cache.rs @@ -1,6 +1,8 @@ #![allow(clippy::unwrap_used)] +use std::sync::Arc; + use router::{configs::settings::Settings, routes, services}; -use storage_impl::redis::cache; +use storage_impl::redis::cache::{self, CacheKey}; mod utils; @@ -9,13 +11,15 @@ async fn invalidate_existing_cache_success() { // Arrange Box::pin(utils::setup()).await; let (tx, _) = tokio::sync::oneshot::channel(); - let state = Box::pin(routes::AppState::new( + let app_state = Box::pin(routes::AppState::new( Settings::default(), tx, Box::new(services::MockApiClient), )) .await; - + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let cache_key = "cacheKey".to_string(); let cache_key_value = "val".to_string(); let _ = state @@ -29,11 +33,23 @@ async fn invalidate_existing_cache_success() { let client = awc::Client::default(); cache::CONFIG_CACHE - .push(cache_key.clone(), cache_key_value.clone()) + .push( + CacheKey { + key: cache_key.clone(), + prefix: String::default(), + }, + cache_key_value.clone(), + ) .await; cache::ACCOUNTS_CACHE - .push(cache_key.clone(), cache_key_value.clone()) + .push( + CacheKey { + key: cache_key.clone(), + prefix: String::default(), + }, + cache_key_value.clone(), + ) .await; // Act @@ -51,11 +67,17 @@ async fn invalidate_existing_cache_success() { println!("invalidate Cache: {response:?} : {response_body:?}"); assert_eq!(response.status(), awc::http::StatusCode::OK); assert!(cache::CONFIG_CACHE - .get_val::(&cache_key) + .get_val::(CacheKey { + key: cache_key.clone(), + prefix: String::default() + }) .await .is_none()); assert!(cache::ACCOUNTS_CACHE - .get_val::(&cache_key) + .get_val::(CacheKey { + key: cache_key, + prefix: String::default() + }) .await .is_none()); } diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 414ab8cd1d5f..10e8b3665303 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, str::FromStr}; +use std::{marker::PhantomData, str::FromStr, sync::Arc}; use api_models::payments::{Address, AddressDetails, PhoneDetails}; use common_utils::id_type; @@ -191,13 +191,16 @@ async fn payments_create_success() { let conf = Settings::new().unwrap(); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); static CV: aci::Aci = aci::Aci; let connector = types::api::ConnectorData { @@ -236,13 +239,16 @@ async fn payments_create_failure() { static CV: aci::Aci = aci::Aci; let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let connector = types::api::ConnectorData { connector: Box::new(&CV), connector_name: types::Connector::Aci, @@ -297,13 +303,16 @@ async fn refund_for_successful_payments() { }; let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let connector_integration: services::BoxedConnectorIntegration< '_, types::api::Authorize, @@ -366,13 +375,16 @@ async fn refunds_create_failure() { }; let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let connector_integration: services::BoxedConnectorIntegration< '_, types::api::Execute, diff --git a/crates/router/tests/connectors/nmi.rs b/crates/router/tests/connectors/nmi.rs index 1d719e052a09..d5f1ebd4fad5 100644 --- a/crates/router/tests/connectors/nmi.rs +++ b/crates/router/tests/connectors/nmi.rs @@ -13,7 +13,7 @@ impl utils::Connector for NmiTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Nmi; types::api::ConnectorData { - connector: Box::new(&Nmi), + connector: Box::new(Nmi::new()), connector_name: types::Connector::Nmi, 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 ae93ca326bac..0de19f17b54a 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -1,4 +1,4 @@ -use std::{fmt::Debug, marker::PhantomData, str::FromStr, time::Duration}; +use std::{fmt::Debug, marker::PhantomData, str::FromStr, sync::Arc, time::Duration}; use async_trait::async_trait; use common_utils::pii::Email; @@ -11,7 +11,7 @@ use router::{ core::{errors::ConnectorError, payments}, db::StorageImpl, routes, services, - types::{self, storage::enums, AccessToken, PaymentAddress, RouterData}, + types::{self, storage::enums, AccessToken, MinorUnit, PaymentAddress, RouterData}, }; use test_utils::connector_auth::ConnectorAuthType; use tokio::sync::oneshot; @@ -95,13 +95,16 @@ pub trait ConnectorActions: Connector { payment_info, ); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); integration.execute_pretasks(&mut request, &state).await?; Box::pin(call_connector(request, integration)).await } @@ -120,13 +123,16 @@ pub trait ConnectorActions: Connector { ); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); integration.execute_pretasks(&mut request, &state).await?; Box::pin(call_connector(request, integration)).await } @@ -145,13 +151,16 @@ pub trait ConnectorActions: Connector { ); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); integration.execute_pretasks(&mut request, &state).await?; Box::pin(call_connector(request, integration)).await } @@ -174,13 +183,16 @@ pub trait ConnectorActions: Connector { ); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); integration.execute_pretasks(&mut request, &state).await?; Box::pin(call_connector(request, integration)).await } @@ -414,11 +426,13 @@ pub trait ConnectorActions: Connector { let request = self.generate_data( payment_data.unwrap_or_else(|| types::RefundsData { payment_amount: 1000, + minor_payment_amount: MinorUnit::new(1000), currency: enums::Currency::USD, refund_id: uuid::Uuid::new_v4().to_string(), connector_transaction_id: "".to_string(), webhook_url: None, refund_amount: 100, + minor_refund_amount: MinorUnit::new(100), connector_metadata: None, reason: None, connector_refund_id: Some(refund_id), @@ -596,13 +610,16 @@ pub trait ConnectorActions: Connector { let mut request = self.get_payout_request(None, payout_type, payment_info); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); connector_integration .execute_pretasks(&mut request, &state) .await?; @@ -637,13 +654,16 @@ pub trait ConnectorActions: Connector { let mut request = self.get_payout_request(connector_payout_id, payout_type, payment_info); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); connector_integration .execute_pretasks(&mut request, &state) .await?; @@ -679,13 +699,16 @@ pub trait ConnectorActions: Connector { request.connector_customer = connector_customer; let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); connector_integration .execute_pretasks(&mut request, &state) .await?; @@ -721,13 +744,16 @@ pub trait ConnectorActions: Connector { self.get_payout_request(Some(connector_payout_id), payout_type, payment_info); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); connector_integration .execute_pretasks(&mut request, &state) .await?; @@ -809,13 +835,16 @@ pub trait ConnectorActions: Connector { let mut request = self.get_payout_request(None, payout_type, payment_info); let tx = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( Settings::new().unwrap(), StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); connector_integration .execute_pretasks(&mut request, &state) .await?; @@ -842,13 +871,16 @@ async fn call_connector< let conf = Settings::new().unwrap(); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); services::api::execute_connector_processing_step( &state, integration, @@ -915,6 +947,7 @@ impl Default for PaymentAuthorizeType { let data = types::PaymentsAuthorizeData { payment_method_data: types::domain::PaymentMethodData::Card(CCardType::default().0), amount: 100, + minor_amount: MinorUnit::new(100), currency: enums::Currency::USD, confirm: true, statement_descriptor_suffix: None, @@ -1012,10 +1045,12 @@ impl Default for PaymentRefundType { fn default() -> Self { let data = types::RefundsData { payment_amount: 100, + minor_payment_amount: MinorUnit::new(100), currency: enums::Currency::USD, refund_id: uuid::Uuid::new_v4().to_string(), connector_transaction_id: String::new(), refund_amount: 100, + minor_refund_amount: MinorUnit::new(100), webhook_url: None, connector_metadata: None, reason: Some("Customer returned product".to_string()), diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index e76eee4c1792..ed11f3b8b1cb 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -2,6 +2,8 @@ mod utils; +use std::sync::Arc; + use common_utils::{id_type, types::MinorUnit}; use router::{ configs, @@ -276,13 +278,16 @@ async fn payments_create_core() { use configs::settings::Settings; let conf = Settings::new().expect("invalid settings"); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let key_store = state .store @@ -457,13 +462,16 @@ async fn payments_create_core_adyen_no_redirect() { use crate::configs::settings::Settings; let conf = Settings::new().expect("invalid settings"); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let customer_id = format!("cust_{}", Uuid::new_v4()); let merchant_id = "arunraj".to_string(); diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 42cfe3b74c1d..4c178e4122bb 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -2,6 +2,8 @@ mod utils; +use std::sync::Arc; + use common_utils::{id_type, types::MinorUnit}; use router::{ core::payments, @@ -36,13 +38,16 @@ async fn payments_create_core() { use router::configs::settings::Settings; let conf = Settings::new().expect("invalid settings"); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let key_store = state .store @@ -224,13 +229,16 @@ async fn payments_create_core_adyen_no_redirect() { let conf = Settings::new().expect("invalid settings"); let tx: oneshot::Sender<()> = oneshot::channel().0; - let state = Box::pin(routes::AppState::with_storage( + let app_state = Box::pin(routes::AppState::with_storage( conf, StorageImpl::PostgresqlTest, tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let customer_id = format!("cust_{}", Uuid::new_v4()); let merchant_id = "arunraj".to_string(); diff --git a/crates/router/tests/services.rs b/crates/router/tests/services.rs index eff7fe7f8738..d907000cccd9 100644 --- a/crates/router/tests/services.rs +++ b/crates/router/tests/services.rs @@ -1,4 +1,4 @@ -use std::sync::atomic; +use std::sync::{atomic, Arc}; use router::{configs::settings::Settings, routes, services}; @@ -6,16 +6,20 @@ mod utils; #[tokio::test] #[should_panic] +#[allow(clippy::unwrap_used)] async fn get_redis_conn_failure() { // Arrange utils::setup().await; let (tx, _) = tokio::sync::oneshot::channel(); - let state = Box::pin(routes::AppState::new( + let app_state = Box::pin(routes::AppState::new( Settings::default(), tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let _ = state.store.get_redis_conn().map(|conn| { conn.is_redis_available @@ -30,16 +34,20 @@ async fn get_redis_conn_failure() { } #[tokio::test] +#[allow(clippy::unwrap_used)] async fn get_redis_conn_success() { // Arrange Box::pin(utils::setup()).await; let (tx, _) = tokio::sync::oneshot::channel(); - let state = Box::pin(routes::AppState::new( + let app_state = Box::pin(routes::AppState::new( Settings::default(), tx, Box::new(services::MockApiClient), )) .await; + let state = Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); // Act let result = state.store.get_redis_conn(); diff --git a/crates/scheduler/src/consumer.rs b/crates/scheduler/src/consumer.rs index a95ac4932471..3e15a7cde0fb 100644 --- a/crates/scheduler/src/consumer.rs +++ b/crates/scheduler/src/consumer.rs @@ -1,6 +1,9 @@ // TODO: Figure out what to log -use std::sync::{self, atomic}; +use std::{ + sync::{self, atomic}, + time as std_time, +}; pub mod types; pub mod workflows; @@ -22,7 +25,7 @@ use super::env::logger; pub use super::workflows::ProcessTrackerWorkflow; use crate::{ configs::settings::SchedulerSettings, db::process_tracker::ProcessTrackerInterface, errors, - metrics, utils as pt_utils, SchedulerAppState, SchedulerInterface, + metrics, utils as pt_utils, SchedulerAppState, SchedulerInterface, SchedulerSessionState, }; // Valid consumer business statuses @@ -31,12 +34,16 @@ pub fn valid_business_statuses() -> Vec<&'static str> { } #[instrument(skip_all)] -pub async fn start_consumer( +pub async fn start_consumer( state: &T, settings: sync::Arc, - workflow_selector: impl workflows::ProcessTrackerWorkflows + 'static + Copy + std::fmt::Debug, + workflow_selector: impl workflows::ProcessTrackerWorkflows + 'static + Copy + std::fmt::Debug, (tx, mut rx): (mpsc::Sender<()>, mpsc::Receiver<()>), -) -> CustomResult<(), errors::ProcessTrackerError> { + app_state_to_session_state: F, +) -> CustomResult<(), errors::ProcessTrackerError> +where + F: Fn(&T, &str) -> CustomResult, +{ use std::time::Duration; use rand::distributions::{Distribution, Uniform}; @@ -77,17 +84,29 @@ pub async fn start_consumer( if settings.consumer.disabled { continue; } + consumer_operation_counter.fetch_add(1, atomic::Ordering::SeqCst); + let start_time = std_time::Instant::now(); + let tenants = state.get_tenants(); + for tenant in tenants { + let session_state = app_state_to_session_state(state, tenant.as_str())?; + pt_utils::consumer_operation_handler( + session_state.clone(), + settings.clone(), + |error| { + logger::error!(?error, "Failed to perform consumer operation"); + }, + workflow_selector, + ) + .await; + } + + let end_time = std_time::Instant::now(); + let duration = end_time.saturating_duration_since(start_time).as_secs_f64(); + logger::debug!("Time taken to execute consumer_operation: {}s", duration); - pt_utils::consumer_operation_handler( - state.clone(), - settings.clone(), - |error| { - logger::error!(?error, "Failed to perform consumer operation"); - }, - sync::Arc::clone(&consumer_operation_counter), - workflow_selector, - ) - .await; + let current_count = + consumer_operation_counter.fetch_sub(1, atomic::Ordering::SeqCst); + logger::info!("Current tasks being executed: {}", current_count); } Ok(()) | Err(mpsc::error::TryRecvError::Disconnected) => { logger::debug!("Awaiting shutdown!"); @@ -116,7 +135,7 @@ pub async fn start_consumer( } #[instrument(skip_all)] -pub async fn consumer_operations( +pub async fn consumer_operations( state: &T, settings: &SchedulerSettings, workflow_selector: impl workflows::ProcessTrackerWorkflows + 'static + Copy + std::fmt::Debug, @@ -125,13 +144,10 @@ pub async fn consumer_operations( let group_name = settings.consumer.consumer_group.clone(); let consumer_name = format!("consumer_{}", Uuid::new_v4()); - let group_created = &mut state + let _group_created = &mut state .get_db() .consumer_group_create(&stream_name, &group_name, &RedisEntryId::AfterLastID) .await; - if group_created.is_err() { - logger::info!("Consumer group {group_name} already exists"); - } let mut tasks = state .get_db() @@ -139,7 +155,9 @@ pub async fn consumer_operations( .fetch_consumer_tasks(&stream_name, &group_name, &consumer_name) .await?; - logger::info!("{} picked {} tasks", consumer_name, tasks.len()); + if !tasks.is_empty() { + logger::info!("{} picked {} tasks", consumer_name, tasks.len()); + } let mut handler = vec![]; for task in tasks.iter_mut() { @@ -216,11 +234,10 @@ pub async fn start_workflow( workflow_selector: impl workflows::ProcessTrackerWorkflows + 'static + std::fmt::Debug, ) -> CustomResult<(), errors::ProcessTrackerError> where - T: SchedulerAppState, + T: SchedulerSessionState, { tracing::Span::current().record("workflow_id", Uuid::new_v4().to_string()); logger::info!(pt.name=?process.name, pt.id=%process.id); - let res = workflow_selector .trigger_workflow(&state.clone(), process.clone()) .await diff --git a/crates/scheduler/src/consumer/workflows.rs b/crates/scheduler/src/consumer/workflows.rs index 3e8a4c8e5028..bbece87f3094 100644 --- a/crates/scheduler/src/consumer/workflows.rs +++ b/crates/scheduler/src/consumer/workflows.rs @@ -3,7 +3,7 @@ use common_utils::errors::CustomResult; pub use diesel_models::process_tracker as storage; use router_env::logger; -use crate::{errors, SchedulerAppState}; +use crate::{errors, SchedulerSessionState}; pub type WorkflowSelectorFn = fn(&storage::ProcessTracker) -> Result<(), errors::ProcessTrackerError>; @@ -26,7 +26,7 @@ pub trait ProcessTrackerWorkflows: Send + Sync { process: storage::ProcessTracker, ) -> CustomResult<(), errors::ProcessTrackerError> where - T: SchedulerAppState, + T: SchedulerSessionState, { let app_state = &state.clone(); let output = operation.execute_workflow(app_state, process.clone()).await; diff --git a/crates/scheduler/src/errors.rs b/crates/scheduler/src/errors.rs index e630287c316a..52ea03e0c470 100644 --- a/crates/scheduler/src/errors.rs +++ b/crates/scheduler/src/errors.rs @@ -59,6 +59,8 @@ pub enum ProcessTrackerError { EEmailError(error_stack::Report), #[error("Type Conversion error")] TypeConversionError, + #[error("Tenant not found")] + TenantNotFound, } #[macro_export] diff --git a/crates/scheduler/src/producer.rs b/crates/scheduler/src/producer.rs index 397aab84e912..6390bc66897a 100644 --- a/crates/scheduler/src/producer.rs +++ b/crates/scheduler/src/producer.rs @@ -16,17 +16,20 @@ use super::{ }; use crate::{ configs::settings::SchedulerSettings, errors, flow::SchedulerFlow, - scheduler::SchedulerInterface, utils::*, SchedulerAppState, + scheduler::SchedulerInterface, utils::*, SchedulerAppState, SchedulerSessionState, }; #[instrument(skip_all)] -pub async fn start_producer( +pub async fn start_producer( state: &T, scheduler_settings: Arc, (tx, mut rx): (mpsc::Sender<()>, mpsc::Receiver<()>), + app_state_to_session_state: F, ) -> CustomResult<(), errors::ProcessTrackerError> where + F: Fn(&T, &str) -> CustomResult, T: SchedulerAppState, + U: SchedulerSessionState, { use std::time::Duration; @@ -64,13 +67,17 @@ where match rx.try_recv() { Err(mpsc::error::TryRecvError::Empty) => { interval.tick().await; - match run_producer_flow(state, &scheduler_settings).await { - Ok(_) => (), - Err(error) => { - // Intentionally not propagating error to caller. - // Any errors that occur in the producer flow must be handled here only, as - // this is the topmost level function which is concerned with the producer flow. - error!(%error); + let tenants = state.get_tenants(); + for tenant in tenants { + let session_state = app_state_to_session_state(state, tenant.as_str())?; + match run_producer_flow(&session_state, &scheduler_settings).await { + Ok(_) => (), + Err(error) => { + // Intentionally not propagating error to caller. + // Any errors that occur in the producer flow must be handled here only, as + // this is the topmost level function which is concerned with the producer flow. + error!(%error); + } } } } @@ -78,7 +85,7 @@ where logger::debug!("Awaiting shutdown!"); rx.close(); shutdown_interval.tick().await; - logger::info!("Terminating consumer"); + logger::info!("Terminating producer"); break; } } @@ -97,7 +104,7 @@ pub async fn run_producer_flow( settings: &SchedulerSettings, ) -> CustomResult<(), errors::ProcessTrackerError> where - T: SchedulerAppState, + T: SchedulerSessionState, { lock_acquire_release::<_, _, _>(state.get_db().as_scheduler(), settings, move || async { let tasks = fetch_producer_tasks(state.get_db().as_scheduler(), settings).await?; diff --git a/crates/scheduler/src/scheduler.rs b/crates/scheduler/src/scheduler.rs index 273f10819f8d..39a45d02ba96 100644 --- a/crates/scheduler/src/scheduler.rs +++ b/crates/scheduler/src/scheduler.rs @@ -52,22 +52,46 @@ impl SchedulerInterface for MockDb {} #[async_trait::async_trait] pub trait SchedulerAppState: Send + Sync + Clone { + fn get_tenants(&self) -> Vec; +} +#[async_trait::async_trait] +pub trait SchedulerSessionState: Send + Sync + Clone { fn get_db(&self) -> Box; } - -pub async fn start_process_tracker( +pub async fn start_process_tracker< + T: SchedulerAppState + 'static, + U: SchedulerSessionState + 'static, + F, +>( state: &T, scheduler_flow: SchedulerFlow, scheduler_settings: Arc, channel: (mpsc::Sender<()>, mpsc::Receiver<()>), - runner_from_task: impl workflows::ProcessTrackerWorkflows + 'static + Copy + std::fmt::Debug, -) -> CustomResult<(), errors::ProcessTrackerError> { + runner_from_task: impl workflows::ProcessTrackerWorkflows + 'static + Copy + std::fmt::Debug, + app_state_to_session_state: F, +) -> CustomResult<(), errors::ProcessTrackerError> +where + F: Fn(&T, &str) -> CustomResult, +{ match scheduler_flow { SchedulerFlow::Producer => { - producer::start_producer(state, scheduler_settings, channel).await? + producer::start_producer( + state, + scheduler_settings, + channel, + app_state_to_session_state, + ) + .await? } SchedulerFlow::Consumer => { - consumer::start_consumer(state, scheduler_settings, runner_from_task, channel).await? + consumer::start_consumer( + state, + scheduler_settings, + runner_from_task, + channel, + app_state_to_session_state, + ) + .await? } SchedulerFlow::Cleaner => { error!("This flow has not been implemented yet!"); diff --git a/crates/scheduler/src/utils.rs b/crates/scheduler/src/utils.rs index 8b80b7c72c1f..7f073a8e10b9 100644 --- a/crates/scheduler/src/utils.rs +++ b/crates/scheduler/src/utils.rs @@ -1,7 +1,4 @@ -use std::{ - sync::{self, atomic}, - time as std_time, -}; +use std::sync; use common_utils::errors::CustomResult; use diesel_models::enums::{self, ProcessTrackerStatus}; @@ -17,7 +14,7 @@ use super::{ }; use crate::{ configs::settings::SchedulerSettings, consumer::types::ProcessTrackerBatch, errors, - flow::SchedulerFlow, metrics, SchedulerAppState, SchedulerInterface, + flow::SchedulerFlow, metrics, SchedulerInterface, SchedulerSessionState, }; pub async fn divide_and_append_tasks( @@ -258,26 +255,16 @@ pub async fn consumer_operation_handler( state: T, settings: sync::Arc, error_handler_fun: E, - consumer_operation_counter: sync::Arc, workflow_selector: impl workflows::ProcessTrackerWorkflows + 'static + Copy + std::fmt::Debug, ) where // Error handler function E: FnOnce(error_stack::Report), - T: SchedulerAppState + Send + Sync + 'static, + T: SchedulerSessionState + Send + Sync + 'static, { - consumer_operation_counter.fetch_add(1, atomic::Ordering::SeqCst); - let start_time = std_time::Instant::now(); - match consumer::consumer_operations(&state, &settings, workflow_selector).await { Ok(_) => (), Err(err) => error_handler_fun(err), } - let end_time = std_time::Instant::now(); - let duration = end_time.saturating_duration_since(start_time).as_secs_f64(); - logger::debug!("Time taken to execute consumer_operation: {}s", duration); - - let current_count = consumer_operation_counter.fetch_sub(1, atomic::Ordering::SeqCst); - logger::info!("Current tasks being executed: {}", current_count); } pub fn add_histogram_metrics( diff --git a/crates/storage_impl/src/config.rs b/crates/storage_impl/src/config.rs index fd95a6d315d6..e371936a04dd 100644 --- a/crates/storage_impl/src/config.rs +++ b/crates/storage_impl/src/config.rs @@ -1,3 +1,4 @@ +use common_utils::DbConnectionParams; use masking::Secret; #[derive(Debug, Clone, serde::Deserialize)] @@ -14,6 +15,29 @@ pub struct Database { pub max_lifetime: Option, } +impl DbConnectionParams for Database { + fn get_username(&self) -> &str { + &self.username + } + fn get_password(&self) -> Secret { + self.password.clone() + } + fn get_host(&self) -> &str { + &self.host + } + fn get_port(&self) -> u16 { + self.port + } + fn get_dbname(&self) -> &str { + &self.dbname + } +} + +pub trait TenantConfig: Send + Sync { + fn get_schema(&self) -> &str; + fn get_redis_key_prefix(&self) -> &str; +} + #[derive(Debug, serde::Deserialize, Clone, Copy, Default)] #[serde(rename_all = "PascalCase")] pub enum QueueStrategy { diff --git a/crates/storage_impl/src/database/store.rs b/crates/storage_impl/src/database/store.rs index 17ae76b5bb03..543cc47870d9 100644 --- a/crates/storage_impl/src/database/store.rs +++ b/crates/storage_impl/src/database/store.rs @@ -1,11 +1,11 @@ use async_bb8_diesel::{AsyncConnection, ConnectionError}; use bb8::CustomizeConnection; +use common_utils::DbConnectionParams; use diesel::PgConnection; use error_stack::ResultExt; use hyperswitch_domain_models::errors::{StorageError, StorageResult}; -use masking::PeekInterface; -use crate::config::Database; +use crate::config::{Database, TenantConfig}; pub type PgPool = bb8::Pool>; pub type PgPooledConn = async_bb8_diesel::Connection; @@ -13,7 +13,11 @@ pub type PgPooledConn = async_bb8_diesel::Connection; #[async_trait::async_trait] pub trait DatabaseStore: Clone + Send + Sync { type Config: Send; - async fn new(config: Self::Config, test_transaction: bool) -> StorageResult; + async fn new( + config: Self::Config, + tenant_config: &dyn TenantConfig, + test_transaction: bool, + ) -> StorageResult; fn get_master_pool(&self) -> &PgPool; fn get_replica_pool(&self) -> &PgPool; } @@ -26,9 +30,14 @@ pub struct Store { #[async_trait::async_trait] impl DatabaseStore for Store { type Config = Database; - async fn new(config: Database, test_transaction: bool) -> StorageResult { + async fn new( + config: Database, + tenant_config: &dyn TenantConfig, + test_transaction: bool, + ) -> StorageResult { Ok(Self { - master_pool: diesel_make_pg_pool(&config, test_transaction).await?, + master_pool: diesel_make_pg_pool(&config, tenant_config.get_schema(), test_transaction) + .await?, }) } @@ -50,14 +59,23 @@ pub struct ReplicaStore { #[async_trait::async_trait] impl DatabaseStore for ReplicaStore { type Config = (Database, Database); - async fn new(config: (Database, Database), test_transaction: bool) -> StorageResult { + async fn new( + config: (Database, Database), + tenant_config: &dyn TenantConfig, + test_transaction: bool, + ) -> StorageResult { let (master_config, replica_config) = config; - let master_pool = diesel_make_pg_pool(&master_config, test_transaction) - .await - .attach_printable("failed to create master pool")?; - let replica_pool = diesel_make_pg_pool(&replica_config, test_transaction) - .await - .attach_printable("failed to create replica pool")?; + let master_pool = + diesel_make_pg_pool(&master_config, tenant_config.get_schema(), test_transaction) + .await + .attach_printable("failed to create master pool")?; + let replica_pool = diesel_make_pg_pool( + &replica_config, + tenant_config.get_schema(), + test_transaction, + ) + .await + .attach_printable("failed to create replica pool")?; Ok(Self { master_pool, replica_pool, @@ -75,16 +93,10 @@ impl DatabaseStore for ReplicaStore { pub async fn diesel_make_pg_pool( database: &Database, + schema: &str, test_transaction: bool, ) -> StorageResult { - let database_url = format!( - "postgres://{}:{}@{}:{}/{}", - database.username, - database.password.peek(), - database.host, - database.port, - database.dbname - ); + let database_url = database.get_database_url(schema); let manager = async_bb8_diesel::ConnectionManager::::new(database_url); let mut pool = bb8::Pool::builder() .max_size(database.pool_size) diff --git a/crates/storage_impl/src/lib.rs b/crates/storage_impl/src/lib.rs index b1582bb6e9af..d55f812c5a21 100644 --- a/crates/storage_impl/src/lib.rs +++ b/crates/storage_impl/src/lib.rs @@ -29,7 +29,7 @@ use database::store::PgPool; #[cfg(not(feature = "payouts"))] use hyperswitch_domain_models::{PayoutAttemptInterface, PayoutsInterface}; pub use mock_db::MockDb; -use redis_interface::{errors::RedisError, SaddReply}; +use redis_interface::{errors::RedisError, RedisConnectionPool, SaddReply}; pub use crate::database::store::DatabaseStore; #[cfg(not(feature = "payouts"))] @@ -38,7 +38,7 @@ pub use crate::database::store::Store; #[derive(Debug, Clone)] pub struct RouterStore { db_store: T, - cache_store: RedisStore, + cache_store: Arc, master_encryption_key: StrongSecret>, pub request_id: Option, } @@ -55,19 +55,23 @@ where tokio::sync::oneshot::Sender<()>, &'static str, ); - async fn new(config: Self::Config, test_transaction: bool) -> StorageResult { + async fn new( + config: Self::Config, + tenant_config: &dyn config::TenantConfig, + test_transaction: bool, + ) -> StorageResult { let (db_conf, cache_conf, encryption_key, cache_error_signal, inmemory_cache_stream) = config; if test_transaction { - Self::test_store(db_conf, &cache_conf, encryption_key) + Self::test_store(db_conf, tenant_config, &cache_conf, encryption_key) .await .attach_printable("failed to create test router store") } else { Self::from_config( db_conf, - &cache_conf, + tenant_config, encryption_key, - cache_error_signal, + Self::cache_store(&cache_conf, cache_error_signal).await?, inmemory_cache_stream, ) .await @@ -83,9 +87,7 @@ where } impl RedisConnInterface for RouterStore { - fn get_redis_conn( - &self, - ) -> error_stack::Result, RedisError> { + fn get_redis_conn(&self) -> error_stack::Result, RedisError> { self.cache_store.get_redis_conn() } } @@ -93,23 +95,26 @@ impl RedisConnInterface for RouterStore { impl RouterStore { pub async fn from_config( db_conf: T::Config, - cache_conf: &redis_interface::RedisSettings, + tenant_config: &dyn config::TenantConfig, encryption_key: StrongSecret>, - cache_error_signal: tokio::sync::oneshot::Sender<()>, + cache_store: Arc, inmemory_cache_stream: &str, ) -> StorageResult { - let db_store = T::new(db_conf, false).await?; - let cache_store = RedisStore::new(cache_conf) - .await - .change_context(StorageError::InitializationError) - .attach_printable("Failed to create cache store")?; - cache_store.set_error_callback(cache_error_signal); + let db_store = T::new(db_conf, tenant_config, false).await?; + let redis_conn = cache_store.redis_conn.clone(); + let cache_store = Arc::new(RedisStore { + redis_conn: Arc::new(RedisConnectionPool::clone( + &redis_conn, + tenant_config.get_redis_key_prefix(), + )), + }); cache_store .redis_conn .subscribe(inmemory_cache_stream) .await .change_context(StorageError::InitializationError) .attach_printable("Failed to subscribe to inmemory cache stream")?; + Ok(Self { db_store, cache_store, @@ -118,6 +123,18 @@ impl RouterStore { }) } + pub async fn cache_store( + cache_conf: &redis_interface::RedisSettings, + cache_error_signal: tokio::sync::oneshot::Sender<()>, + ) -> StorageResult> { + let cache_store = RedisStore::new(cache_conf) + .await + .change_context(StorageError::InitializationError) + .attach_printable("Failed to create cache store")?; + cache_store.set_error_callback(cache_error_signal); + Ok(Arc::new(cache_store)) + } + pub fn master_key(&self) -> &StrongSecret> { &self.master_encryption_key } @@ -127,18 +144,19 @@ impl RouterStore { /// Will panic if `CONNECTOR_AUTH_FILE_PATH` is not set pub async fn test_store( db_conf: T::Config, + tenant_config: &dyn config::TenantConfig, cache_conf: &redis_interface::RedisSettings, encryption_key: StrongSecret>, ) -> StorageResult { // TODO: create an error enum and return proper error here - let db_store = T::new(db_conf, true).await?; + let db_store = T::new(db_conf, tenant_config, true).await?; let cache_store = RedisStore::new(cache_conf) .await .change_context(StorageError::InitializationError) .attach_printable("failed to create redis cache")?; Ok(Self { db_store, - cache_store, + cache_store: Arc::new(cache_store), master_encryption_key: encryption_key, request_id: None, }) @@ -162,9 +180,13 @@ where T: DatabaseStore, { type Config = (RouterStore, String, u8, u32, Option); - async fn new(config: Self::Config, _test_transaction: bool) -> StorageResult { - let (router_store, drainer_stream_name, drainer_num_partitions, ttl_for_kv, soft_kill_mode) = - config; + async fn new( + config: Self::Config, + tenant_config: &dyn config::TenantConfig, + _test_transaction: bool, + ) -> StorageResult { + let (router_store, _, drainer_num_partitions, ttl_for_kv, soft_kill_mode) = config; + let drainer_stream_name = format!("{}_{}", tenant_config.get_schema(), config.1); Ok(Self::from_store( router_store, drainer_stream_name, @@ -182,9 +204,7 @@ where } impl RedisConnInterface for KVRouterStore { - fn get_redis_conn( - &self, - ) -> error_stack::Result, RedisError> { + fn get_redis_conn(&self) -> error_stack::Result, RedisError> { self.router_store.get_redis_conn() } } @@ -282,7 +302,7 @@ pub trait UniqueConstraints { fn table_name(&self) -> &str; async fn check_for_constraints( &self, - redis_conn: &Arc, + redis_conn: &Arc, ) -> CustomResult<(), RedisError> { let constraints = self.unique_constraints(); let sadd_result = redis_conn diff --git a/crates/storage_impl/src/redis/cache.rs b/crates/storage_impl/src/redis/cache.rs index 68f04936daf5..c0375773b544 100644 --- a/crates/storage_impl/src/redis/cache.rs +++ b/crates/storage_impl/src/redis/cache.rs @@ -1,4 +1,4 @@ -use std::{any::Any, borrow::Cow, sync::Arc}; +use std::{any::Any, borrow::Cow, fmt::Debug, sync::Arc}; use common_utils::{ errors::{self, CustomResult}, @@ -8,7 +8,7 @@ use dyn_clone::DynClone; use error_stack::{Report, ResultExt}; use moka::future::Cache as MokaCache; use once_cell::sync::Lazy; -use redis_interface::{errors::RedisError, RedisValue}; +use redis_interface::{errors::RedisError, RedisConnectionPool, RedisValue}; use router_env::tracing::{self, instrument}; use crate::{ @@ -31,9 +31,6 @@ const ROUTING_CACHE_PREFIX: &str = "routing"; /// Prefix for cgraph cache key const CGRAPH_CACHE_PREFIX: &str = "cgraph"; -/// Prefix for PM Filter cgraph cache key -const PM_FILTERS_CGRAPH_CACHE_PREFIX: &str = "pm_filters_cgraph"; - /// Prefix for all kinds of cache key const ALL_CACHE_PREFIX: &str = "all_cache_kind"; @@ -61,10 +58,6 @@ pub static ROUTING_CACHE: Lazy = pub static CGRAPH_CACHE: Lazy = Lazy::new(|| Cache::new(CACHE_TTL, CACHE_TTI, Some(MAX_CAPACITY))); -/// PM Filter CGraph Cache -pub static PM_FILTERS_CGRAPH_CACHE: Lazy = - Lazy::new(|| Cache::new(CACHE_TTL, CACHE_TTI, Some(MAX_CAPACITY))); - /// Trait which defines the behaviour of types that's gonna be stored in Cache pub trait Cacheable: Any + Send + Sync + DynClone { fn as_any(&self) -> &dyn Any; @@ -75,7 +68,6 @@ pub enum CacheKind<'a> { Accounts(Cow<'a, str>), Routing(Cow<'a, str>), CGraph(Cow<'a, str>), - PmFiltersCGraph(Cow<'a, str>), All(Cow<'a, str>), } @@ -86,7 +78,6 @@ impl<'a> From> for RedisValue { CacheKind::Accounts(s) => format!("{ACCOUNTS_CACHE_PREFIX},{s}"), CacheKind::Routing(s) => format!("{ROUTING_CACHE_PREFIX},{s}"), CacheKind::CGraph(s) => format!("{CGRAPH_CACHE_PREFIX},{s}"), - CacheKind::PmFiltersCGraph(s) => format!("{PM_FILTERS_CGRAPH_CACHE_PREFIX},{s}"), CacheKind::All(s) => format!("{ALL_CACHE_PREFIX},{s}"), }; Self::from_string(value) @@ -106,9 +97,6 @@ impl<'a> TryFrom for CacheKind<'a> { CONFIG_CACHE_PREFIX => Ok(Self::Config(Cow::Owned(split.1.to_string()))), ROUTING_CACHE_PREFIX => Ok(Self::Routing(Cow::Owned(split.1.to_string()))), CGRAPH_CACHE_PREFIX => Ok(Self::CGraph(Cow::Owned(split.1.to_string()))), - PM_FILTERS_CGRAPH_CACHE_PREFIX => { - Ok(Self::PmFiltersCGraph(Cow::Owned(split.1.to_string()))) - } ALL_CACHE_PREFIX => Ok(Self::All(Cow::Owned(split.1.to_string()))), _ => Err(validation_err.into()), } @@ -130,6 +118,23 @@ pub struct Cache { inner: MokaCache>, } +#[derive(Debug, Clone)] +pub struct CacheKey { + pub key: String, + // #TODO: make it usage specific enum Eg: CacheKind { Tenant(String), NoTenant, Partition(String) } + pub prefix: String, +} + +impl From for String { + fn from(val: CacheKey) -> Self { + if val.prefix.is_empty() { + val.key + } else { + format!("{}:{}", val.prefix, val.key) + } + } +} + impl Cache { /// With given `time_to_live` and `time_to_idle` creates a moka cache. /// @@ -150,44 +155,38 @@ impl Cache { } } - pub async fn push(&self, key: String, val: T) { - self.inner.insert(key, Arc::new(val)).await; + pub async fn push(&self, key: CacheKey, val: T) { + self.inner.insert(key.into(), Arc::new(val)).await; } - pub async fn get_val(&self, key: &str) -> Option { - let val = self.inner.get(key).await?; + pub async fn get_val(&self, key: CacheKey) -> Option { + let val = self.inner.get::(&key.into()).await?; (*val).as_any().downcast_ref::().cloned() } /// Check if a key exists in cache - pub async fn exists(&self, key: &str) -> bool { - self.inner.contains_key(key) + pub async fn exists(&self, key: CacheKey) -> bool { + self.inner.contains_key::(&key.into()) } - pub async fn remove(&self, key: &str) { - self.inner.invalidate(key).await; + pub async fn remove(&self, key: CacheKey) { + self.inner.invalidate::(&key.into()).await; } } #[instrument(skip_all)] pub async fn get_or_populate_redis( - store: &(dyn RedisConnInterface + Send + Sync), + redis: &Arc, key: impl AsRef, fun: F, ) -> CustomResult where - T: serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug, + T: serde::Serialize + serde::de::DeserializeOwned + Debug, F: FnOnce() -> Fut + Send, Fut: futures::Future> + Send, { let type_name = std::any::type_name::(); let key = key.as_ref(); - let redis = &store - .get_redis_conn() - .change_context(StorageError::RedisError( - RedisError::RedisConnectionError.into(), - )) - .attach_printable("Failed to get redis connection")?; let redis_val = redis.get_and_deserialize_key::(key, type_name).await; let get_data_set_redis = || async { let data = fun().await?; @@ -218,16 +217,35 @@ pub async fn get_or_populate_in_memory( cache: &Cache, ) -> CustomResult where - T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + std::fmt::Debug + Clone, + T: Cacheable + serde::Serialize + serde::de::DeserializeOwned + Debug + Clone, F: FnOnce() -> Fut + Send, Fut: futures::Future> + Send, { - let cache_val = cache.get_val::(key).await; + let redis = &store + .get_redis_conn() + .change_context(StorageError::RedisError( + RedisError::RedisConnectionError.into(), + )) + .attach_printable("Failed to get redis connection")?; + let cache_val = cache + .get_val::(CacheKey { + key: key.to_string(), + prefix: redis.key_prefix.clone(), + }) + .await; if let Some(val) = cache_val { Ok(val) } else { - let val = get_or_populate_redis(store, key, fun).await?; - cache.push(key.to_string(), val.clone()).await; + let val = get_or_populate_redis(redis, key, fun).await?; + cache + .push( + CacheKey { + key: key.to_string(), + prefix: redis.key_prefix.clone(), + }, + val.clone(), + ) + .await; Ok(val) } } @@ -235,7 +253,7 @@ where #[instrument(skip_all)] pub async fn redact_cache( store: &(dyn RedisConnInterface + Send + Sync), - key: &str, + key: &'static str, fun: F, in_memory: Option<&Cache>, ) -> CustomResult @@ -244,7 +262,6 @@ where Fut: futures::Future> + Send, { let data = fun().await?; - in_memory.async_map(|cache| cache.remove(key)).await; let redis_conn = store .get_redis_conn() @@ -252,6 +269,11 @@ where RedisError::RedisConnectionError.into(), )) .attach_printable("Failed to get redis connection")?; + let tenant_key = CacheKey { + key: key.to_string(), + prefix: redis_conn.key_prefix.clone(), + }; + in_memory.async_map(|cache| cache.remove(tenant_key)).await; redis_conn .delete_key(key) @@ -324,9 +346,22 @@ mod cache_tests { #[tokio::test] async fn construct_and_get_cache() { let cache = Cache::new(1800, 1800, None); - cache.push("key".to_string(), "val".to_string()).await; + cache + .push( + CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string(), + }, + "val".to_string(), + ) + .await; assert_eq!( - cache.get_val::("key").await, + cache + .get_val::(CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string() + }) + .await, Some(String::from("val")) ); } @@ -334,25 +369,78 @@ mod cache_tests { #[tokio::test] async fn eviction_on_size_test() { let cache = Cache::new(2, 2, Some(0)); - cache.push("key".to_string(), "val".to_string()).await; - assert_eq!(cache.get_val::("key").await, None); + cache + .push( + CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string(), + }, + "val".to_string(), + ) + .await; + assert_eq!( + cache + .get_val::(CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string() + }) + .await, + None + ); } #[tokio::test] async fn invalidate_cache_for_key() { let cache = Cache::new(1800, 1800, None); - cache.push("key".to_string(), "val".to_string()).await; - - cache.remove("key").await; + cache + .push( + CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string(), + }, + "val".to_string(), + ) + .await; + + cache + .remove(CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string(), + }) + .await; - assert_eq!(cache.get_val::("key").await, None); + assert_eq!( + cache + .get_val::(CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string() + }) + .await, + None + ); } #[tokio::test] async fn eviction_on_time_test() { let cache = Cache::new(2, 2, None); - cache.push("key".to_string(), "val".to_string()).await; + cache + .push( + CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string(), + }, + "val".to_string(), + ) + .await; tokio::time::sleep(std::time::Duration::from_secs(3)).await; - assert_eq!(cache.get_val::("key").await, None); + assert_eq!( + cache + .get_val::(CacheKey { + key: "key".to_string(), + prefix: "prefix".to_string() + }) + .await, + None + ); } } diff --git a/crates/storage_impl/src/redis/pub_sub.rs b/crates/storage_impl/src/redis/pub_sub.rs index 7b23cb25ad38..1f079784243a 100644 --- a/crates/storage_impl/src/redis/pub_sub.rs +++ b/crates/storage_impl/src/redis/pub_sub.rs @@ -3,7 +3,7 @@ use redis_interface::{errors as redis_errors, PubsubInterface, RedisValue}; use router_env::{logger, tracing::Instrument}; use crate::redis::cache::{ - CacheKind, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE, PM_FILTERS_CGRAPH_CACHE, ROUTING_CACHE, + CacheKey, CacheKind, ACCOUNTS_CACHE, CGRAPH_CACHE, CONFIG_CACHE, ROUTING_CACHE, }; #[async_trait::async_trait] @@ -57,7 +57,7 @@ impl PubSubInterface for std::sync::Arc { #[inline] async fn on_message(&self) -> error_stack::Result<(), redis_errors::RedisError> { - logger::debug!("Started on message"); + logger::debug!("Started on message: {:?}", self.key_prefix); let mut rx = self.subscriber.on_message(); while let Ok(message) = rx.recv().await { logger::debug!("Invalidating {message:?}"); @@ -73,31 +73,66 @@ impl PubSubInterface for std::sync::Arc { let key = match key { CacheKind::Config(key) => { - CONFIG_CACHE.remove(key.as_ref()).await; + CONFIG_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; key } CacheKind::Accounts(key) => { - ACCOUNTS_CACHE.remove(key.as_ref()).await; + ACCOUNTS_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; key } CacheKind::CGraph(key) => { - CGRAPH_CACHE.remove(key.as_ref()).await; - key - } - CacheKind::PmFiltersCGraph(key) => { - PM_FILTERS_CGRAPH_CACHE.remove(key.as_ref()).await; + CGRAPH_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; key } CacheKind::Routing(key) => { - ROUTING_CACHE.remove(key.as_ref()).await; + ROUTING_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; key } CacheKind::All(key) => { - CONFIG_CACHE.remove(key.as_ref()).await; - ACCOUNTS_CACHE.remove(key.as_ref()).await; - CGRAPH_CACHE.remove(key.as_ref()).await; - PM_FILTERS_CGRAPH_CACHE.remove(key.as_ref()).await; - ROUTING_CACHE.remove(key.as_ref()).await; + CONFIG_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; + ACCOUNTS_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; + CGRAPH_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; + ROUTING_CACHE + .remove(CacheKey { + key: key.to_string(), + prefix: self.key_prefix.clone(), + }) + .await; key } }; diff --git a/cypress-tests/cypress.config.js b/cypress-tests/cypress.config.js index 7def1f1b9f46..e36f68e80e24 100644 --- a/cypress-tests/cypress.config.js +++ b/cypress-tests/cypress.config.js @@ -1,25 +1,24 @@ - let globalState; module.exports = { e2e: { setupNodeEvents(on, config) { - on('task', { + on("task", { setGlobalState: (val) => { - return (globalState = (val || {})) + return (globalState = val || {}); }, getGlobalState: () => { - return (globalState || {}) + return globalState || {}; }, cli_log: (message) => { console.log("Logging console message from task"); console.log(message); return null; - } - }) + }, + }); }, - experimentalRunAllSpecs: true + experimentalRunAllSpecs: true, }, chromeWebSecurity: false, defaultCommandTimeout: 10000, - pageLoadTimeout: 20000 -}; \ No newline at end of file + pageLoadTimeout: 20000, +}; diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00000-AccountCreate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00000-AccountCreate.cy.js index 4c16fef8df41..aed901c29972 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00000-AccountCreate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00000-AccountCreate.cy.js @@ -9,11 +9,9 @@ describe("Account Create flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00001-CustomerCreate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00001-CustomerCreate.cy.js index c21152ca896e..5e8bcb5aa14c 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00001-CustomerCreate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00001-CustomerCreate.cy.js @@ -9,11 +9,9 @@ describe("Customer Create flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) it("customer-create-call-test", () => { diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00002-ConnectorCreate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00002-ConnectorCreate.cy.js index 1241048f2862..927f0d020110 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00002-ConnectorCreate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00002-ConnectorCreate.cy.js @@ -8,12 +8,10 @@ describe("Connector Account Create flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00003-NoThreeDSAutoCapture.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00003-NoThreeDSAutoCapture.cy.js index 4dc3f2a3e78f..ec36fa16023e 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00003-NoThreeDSAutoCapture.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00003-NoThreeDSAutoCapture.cy.js @@ -13,12 +13,10 @@ describe("Card - NoThreeDS payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) @@ -78,7 +76,5 @@ describe("Card - NoThreeDS payment flow test", () => { it("retrieve-payment-call-test", () => { cy.retrievePaymentCallTest(globalState); }); - - }); }); diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00004-ThreeDSAutoCapture.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00004-ThreeDSAutoCapture.cy.js index c970d9716eff..3178170021c5 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00004-ThreeDSAutoCapture.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00004-ThreeDSAutoCapture.cy.js @@ -19,16 +19,12 @@ describe("Card - ThreeDS payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); - cy.task('cli_log', "SEEDING GLOBAL STATE -> " + JSON.stringify(globalState)); }) }) afterEach("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); - cy.task('cli_log', " FLUSHING GLOBAL STATE -> " + JSON.stringify(globalState)); }) @@ -41,7 +37,6 @@ describe("Card - ThreeDS payment flow test", () => { }); it("payment_methods-call-test", () => { - cy.task('cli_log', "PM CALL "); cy.paymentMethodsCallTest(globalState); }); @@ -49,7 +44,6 @@ describe("Card - ThreeDS payment flow test", () => { let data = getConnectorDetails(globalState.get("connectorId"))["card_pm"]["3DSAutoCapture"]; let req_data = data["Request"]; let res_data = data["Response"]; - cy.task('cli_log', "GLOBAL STATE -> " + JSON.stringify(globalState.data)); cy.confirmCallTest(confirmBody, req_data, res_data, true, globalState); if(should_continue) should_continue = utils.should_continue_further(res_data); }); diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00005-NoThreeDSManualCapture.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00005-NoThreeDSManualCapture.cy.js index 48a22f5a5c03..4ee4483c961a 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00005-NoThreeDSManualCapture.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00005-NoThreeDSManualCapture.cy.js @@ -14,12 +14,10 @@ describe("Card - NoThreeDS Manual payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00006-VoidPayment.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00006-VoidPayment.cy.js index 15f2e0e7930a..ef8157775688 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00006-VoidPayment.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00006-VoidPayment.cy.js @@ -13,12 +13,10 @@ describe("Card - NoThreeDS Manual payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00007-SyncPayment.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00007-SyncPayment.cy.js index 10c16f7ff6e9..1920cdd09dbc 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00007-SyncPayment.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00007-SyncPayment.cy.js @@ -19,12 +19,10 @@ describe("Card - Sync payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) it("create-payment-call-test", () => { diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00008-RefundPayment.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00008-RefundPayment.cy.js index 028e48a41dc8..672c5cf8f4aa 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00008-RefundPayment.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00008-RefundPayment.cy.js @@ -18,13 +18,11 @@ describe("Card - Refund flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) afterEach("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) @@ -534,13 +532,11 @@ describe("Card - Refund flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) afterEach("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00009-SyncRefund.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00009-SyncRefund.cy.js index 31e5e6855d6a..f0cdc9a68903 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00009-SyncRefund.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00009-SyncRefund.cy.js @@ -20,12 +20,10 @@ describe("Card - Sync Refund flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00010-CreateSingleuseMandate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00010-CreateSingleuseMandate.cy.js index 4ece0319324e..ec85017f55d3 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00010-CreateSingleuseMandate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00010-CreateSingleuseMandate.cy.js @@ -13,12 +13,10 @@ describe("Card - SingleUse Mandates flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00011-CreateMultiuseMandate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00011-CreateMultiuseMandate.cy.js index 898258c8d6a3..3e14d69db88e 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00011-CreateMultiuseMandate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00011-CreateMultiuseMandate.cy.js @@ -13,12 +13,10 @@ describe("Card - MultiUse Mandates flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00012-ListAndRevokeMandate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00012-ListAndRevokeMandate.cy.js index 8de48fbf95a4..1f9782f22740 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00012-ListAndRevokeMandate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00012-ListAndRevokeMandate.cy.js @@ -13,12 +13,10 @@ describe("Card - SingleUse Mandates flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00013-SaveCardFlow.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00013-SaveCardFlow.cy.js index 1168a5690893..828fb5101ac7 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00013-SaveCardFlow.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00013-SaveCardFlow.cy.js @@ -15,7 +15,6 @@ describe("Card - SaveCard payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00014-ZeroAuthMandate.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00014-ZeroAuthMandate.cy.js index ee0edcb94ac2..7a0eba4b6ad9 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00014-ZeroAuthMandate.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00014-ZeroAuthMandate.cy.js @@ -12,12 +12,10 @@ describe("Card - SingleUse Mandates flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) after("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js index 4c3d2a1cd0b5..50d136b59600 100644 --- a/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js +++ b/cypress-tests/cypress/e2e/ConnectorTest/00015-ThreeDSManualCapture.cy.js @@ -15,13 +15,11 @@ describe("Card - ThreeDS Manual payment flow test", () => { cy.task('getGlobalState').then((state) => { globalState = new State(state); - console.log("seeding globalState -> " + JSON.stringify(globalState)); }) }) afterEach("flush global state", () => { - console.log("flushing globalState -> " + JSON.stringify(globalState)); cy.task('setGlobalState', globalState.data); }) diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00016-BankTransfers.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00016-BankTransfers.cy.js new file mode 100644 index 000000000000..da4fab34b8e5 --- /dev/null +++ b/cypress-tests/cypress/e2e/ConnectorTest/00016-BankTransfers.cy.js @@ -0,0 +1,77 @@ +import confirmBody from "../../fixtures/confirm-body.json"; +import createPaymentBody from "../../fixtures/create-payment-body.json"; +import State from "../../utils/State"; +import getConnectorDetails, * as utils from "../ConnectorUtils/utils"; + +let globalState; + +describe("Bank Transfers", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + context("Bank transfer - Pix forward flow", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + it("create-payment-call-test", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_transfer_pm" + ]["PaymentIntent"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.createPaymentIntentTest( + createPaymentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("Confirm bank transfer", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_transfer_pm" + ]["Pix"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.confirmBankTransferCallTest( + confirmBody, + req_data, + res_data, + true, + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Handle bank transfer redirection", () => { + let expected_redirection = confirmBody["return_url"]; + let payment_method_type = globalState.get("paymentMethodType"); + cy.handleBankTransferRedirection( + globalState, + payment_method_type, + expected_redirection + ); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/ConnectorTest/00017-BankRedirect.cy.js b/cypress-tests/cypress/e2e/ConnectorTest/00017-BankRedirect.cy.js new file mode 100644 index 000000000000..3a636b2020a0 --- /dev/null +++ b/cypress-tests/cypress/e2e/ConnectorTest/00017-BankRedirect.cy.js @@ -0,0 +1,308 @@ +import confirmBody from "../../fixtures/confirm-body.json"; +import createPaymentBody from "../../fixtures/create-payment-body.json"; +import State from "../../utils/State"; +import getConnectorDetails, * as utils from "../ConnectorUtils/utils"; + +let globalState; + +describe("Bank Redirect tests", () => { + before("seed global state", () => { + cy.task("getGlobalState").then((state) => { + globalState = new State(state); + }); + }); + + after("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + afterEach("flush global state", () => { + cy.task("setGlobalState", globalState.data); + }); + + context("Blik Create and Confirm flow test", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + it("create-payment-call-test", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["blikPaymentIntent"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.createPaymentIntentTest( + createPaymentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("Confirm bank redirect", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["blik"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.confirmBankRedirectCallTest( + confirmBody, + req_data, + res_data, + true, + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + }); + + context("EPS Create and Confirm flow test", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + it("create-payment-call-test", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["PaymentIntent"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.createPaymentIntentTest( + createPaymentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("Confirm bank redirect", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["eps"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.confirmBankRedirectCallTest( + confirmBody, + req_data, + res_data, + true, + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Handle bank redirect redirection", () => { + // return_url is a static url (https://hyperswitch.io) taken from confirm-body fixture and is not updated + let expected_redirection = confirmBody["return_url"]; + let payment_method_type = globalState.get("paymentMethodType"); + cy.handleBankRedirectRedirection( + globalState, + payment_method_type, + expected_redirection + ); + }); + }); + + context("iDEAL Create and Confirm flow test", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + + it("create-payment-call-test", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["PaymentIntent"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.createPaymentIntentTest( + createPaymentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("Confirm bank redirect", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["ideal"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.confirmBankRedirectCallTest( + confirmBody, + req_data, + res_data, + true, + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Handle bank redirect redirection", () => { + // return_url is a static url (https://hyperswitch.io) taken from confirm-body fixture and is not updated + let expected_redirection = confirmBody["return_url"]; + let payment_method_type = globalState.get("paymentMethodType"); + cy.handleBankRedirectRedirection( + globalState, + payment_method_type, + expected_redirection + ); + }); + }); + + context("Giropay Create and Confirm flow test", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + it("create-payment-call-test", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["PaymentIntent"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.createPaymentIntentTest( + createPaymentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("Confirm bank redirect", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["giropay"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.confirmBankRedirectCallTest( + confirmBody, + req_data, + res_data, + true, + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Handle bank redirect redirection", () => { + // return_url is a static url (https://hyperswitch.io) taken from confirm-body fixture and is not updated + let expected_redirection = confirmBody["return_url"]; + let payment_method_type = globalState.get("paymentMethodType"); + cy.handleBankRedirectRedirection( + globalState, + payment_method_type, + expected_redirection + ); + }); + }); + + context("Sofort Create and Confirm flow test", () => { + let should_continue = true; // variable that will be used to skip tests if a previous test fails + + beforeEach(function () { + if (!should_continue) { + this.skip(); + } + }); + it("create-payment-call-test", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["PaymentIntent"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.createPaymentIntentTest( + createPaymentBody, + req_data, + res_data, + "three_ds", + "automatic", + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("payment_methods-call-test", () => { + cy.paymentMethodsCallTest(globalState); + }); + + it("Confirm bank redirect", () => { + let data = getConnectorDetails(globalState.get("connectorId"))[ + "bank_redirect_pm" + ]["sofort"]; + let req_data = data["Request"]; + let res_data = data["Response"]; + cy.confirmBankRedirectCallTest( + confirmBody, + req_data, + res_data, + true, + globalState + ); + if (should_continue) + should_continue = utils.should_continue_further(res_data); + }); + + it("Handle bank redirect redirection", () => { + // return_url is a static url (https://hyperswitch.io) taken from confirm-body fixture and is not updated + let expected_redirection = confirmBody["return_url"]; + let payment_method_type = globalState.get("paymentMethodType"); + cy.handleBankRedirectRedirection( + globalState, + payment_method_type, + expected_redirection + ); + }); + }); +}); diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js b/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js index 1bbbbbd4b5b5..19b6f342249a 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Adyen.js @@ -1,382 +1,531 @@ +import { getCustomExchange } from "./Commons"; const successfulNo3DSCardDetails = { - "card_number": "371449635398431", - "card_exp_month": "03", - "card_exp_year": "30", - "card_holder_name": "John Doe", - "card_cvc": "7373" + card_number: "371449635398431", + card_exp_month: "03", + card_exp_year: "30", + card_holder_name: "John Doe", + card_cvc: "7373", }; const successfulThreeDSTestCardDetails = { - "card_number": "4917610000000000", - "card_exp_month": "03", - "card_exp_year": "30", - "card_holder_name": "Joseph Doe", - "card_cvc": "737" + card_number: "4917610000000000", + card_exp_month: "03", + card_exp_year: "30", + card_holder_name: "Joseph Doe", + card_cvc: "737", }; export const connectorDetails = { -card_pm:{ - "PaymentIntent": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "requires_payment_method" - } - } + card_pm: { + PaymentIntent: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, }, "3DSManualCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "processing" - } - } + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, }, "3DSAutoCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "No3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "requires_capture" - } - } + No3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, }, - "No3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + No3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "Capture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - }, - "Response": { - "status": 200, - "body":{ - "status": "processing", - "amount": 6500, - "amount_capturable": 6500, - "amount_received": 0, - - } - } + Capture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "processing", + amount: 6500, + amount_capturable: 6500, + amount_received: 0, + }, + }, }, - "PartialCapture": { - "Request": { - }, - "Response": { - "status": 200, - "body": { - "status": "processing", - "amount": 6500, - "amount_capturable": 6500, - "amount_received": 0, - } - - } + PartialCapture: { + Request: {}, + Response: { + status: 200, + body: { + status: "processing", + amount: 6500, + amount_capturable: 6500, + amount_received: 0, + }, + }, }, - "Void":{ - "Request": { - }, - "Response": { - "status": 200, - "body":{ - status: "processing" - - } - } + Void: { + Request: {}, + Response: { + status: 200, + body: { + status: "processing", + }, + }, }, - "Refund": { - "Request": { - - "currency": "USD", - - }, - "Response": { - "status": 400, - "body": { - "status": "pending", - } - - } + Refund: { + Request: { + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "pending", + }, + status: 200, + body: { + status: "pending", + }, + }, }, - "PartialRefund": { - "Request": { - - "currency": "USD", - - }, - "Response": { - "status": 200, - "body": { - "status": "pending", - } - - } + PartialRefund: { + Request: { + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "pending", + }, + }, }, - "SyncRefund": { - "Request": { - - "currency": "USD", - - }, - "Response": { - "status": 200, - "body": { - "status": "pending", - } - - } + SyncRefund: { + Request: { + currency: "USD", + }, + Response: { + status: 200, + body: { + status: "pending", + }, + }, }, - "MandateSingleUse3DSAutoCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } - + MandateSingleUse3DSAutoCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "MandateSingleUse3DSManualCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "requires_customer_action" - } - } - + MandateSingleUse3DSManualCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, }, - "MandateSingleUseNo3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + MandateSingleUseNo3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "MandateSingleUseNo3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "processing" - } - } + MandateSingleUseNo3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, }, - "MandateMultiUseNo3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + MandateMultiUseNo3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "MandateMultiUseNo3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "requires_capture" - } - } + MandateMultiUseNo3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, }, - "MandateMultiUse3DSAutoCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } + MandateMultiUse3DSAutoCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, + }, + MandateMultiUse3DSManualCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, + }, + ZeroAuthMandate: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + SaveCardUseNo3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + SaveCardUseNo3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_capture", + }, + }, + }, + }, + bank_transfer_pm: { + PaymentIntent: { + Request: { + currency: "BRL", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }, + Pix: { + Request: { + payment_method: "bank_transfer", + payment_method_type: "pix", + payment_method_data: { + bank_transfer: { + pix: {}, + }, + }, + currency: "BRL", + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, + }, + }, + bank_redirect_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "EUR", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + ideal: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "ideal", + payment_method_data: { + bank_redirect: { + ideal: { + bank_name: "ing", + country: "NL", }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", }, - "Response": { - "status": 200, - "body": { - "status": "requires_capture" - } - } + }, }, - "MandateMultiUse3DSManualCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } + giropay: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "giropay", + payment_method_data: { + bank_redirect: { + giropay: { + bank_name: "", + bank_account_bic: "", + bank_account_iban: "", + preferred_language: "en", + country: "DE", }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", }, - "Response": { - "status": 200, - "body": { - "status": "requires_capture" - } - } + }, }, - "ZeroAuthMandate": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + sofort: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "sofort", + payment_method_data: { + bank_redirect: { + sofort: { + country: "DE", + preferred_language: "en", + }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, }, - "SaveCardUseNo3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "setup_future_usage": "on_session", - "customer_acceptance": { - "acceptance_type": "offline", - "accepted_at": "1963-05-03T04:07:52.723Z", - "online": { - "ip_address": "127.0.0.1", - "user_agent": "amet irure esse" - } + eps: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "eps", + payment_method_data: { + bank_redirect: { + eps: { + bank_name: "ing", }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + }, }, - "SaveCardUseNo3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "setup_future_usage": "on_session", - "customer_acceptance": { - "acceptance_type": "offline", - "accepted_at": "1963-05-03T04:07:52.723Z", - "online": { - "ip_address": "127.0.0.1", - "user_agent": "amet irure esse" - } + blik: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "blik", + payment_method_data: { + bank_redirect: { + blik: { + name: "John Doe", + email: "example@email.com", + blik_code: "777987", }, + }, }, - "Response": { - "status": 200, - "body": { - "status": "requires_capture" - } - } + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "PL", + first_name: "john", + last_name: "doe", + }, + }, + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, }, -} -}; \ No newline at end of file + }, +}; diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Commons.js b/cypress-tests/cypress/e2e/ConnectorUtils/Commons.js new file mode 100644 index 000000000000..9436f334068b --- /dev/null +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Commons.js @@ -0,0 +1,507 @@ +// This file is the default. To override, add to connector.js +import State from "../../utils/State"; + +const globalState = new State({ + connectorId: Cypress.env("CONNECTOR"), + baseUrl: Cypress.env("BASEURL"), + adminApiKey: Cypress.env("ADMINAPIKEY"), + connectorAuthFilePath: Cypress.env("CONNECTOR_AUTH_FILE_PATH"), +}); + +const connectorId = globalState.get("connectorId"); + +const successfulNo3DSCardDetails = { + card_number: "4111111111111111", + card_exp_month: "08", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "999", +}; + +const successfulThreeDSTestCardDetails = { + card_number: "4111111111111111", + card_exp_month: "10", + card_exp_year: "25", + card_holder_name: "morino", + card_cvc: "999", +}; + +/* +`getDefaultExchange` contains the default Request and Response to be considered if none provided. +`getCustomExchange` takes in 2 optional fields named as Request and Response. +with `getCustomExchange`, if 501 response is expected, there is no need to pass Response as it considers default values. +*/ + +// Const to get default PaymentExchange object +const getDefaultExchange = () => ({ + Request: { + currency: "EUR", + }, + Response: { + status: 501, + body: { + error: { + type: "invalid_request", + message: `Selected payment method through ${connectorId} is not implemented`, + code: "IR_00", + }, + }, + }, +}); + +// Const to get PaymentExchange with overridden properties +export const getCustomExchange = (overrides) => { + const defaultExchange = getDefaultExchange(); + + return { + ...defaultExchange, + Request: { + ...defaultExchange.Request, + ...(overrides.Request || {}), + }, + Response: { + ...defaultExchange.Response, + ...(overrides.Response || {}), + }, + }; +}; + +export const connectorDetails = { + card_pm: { + PaymentIntent: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + "3DSManualCapture": { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + "3DSAutoCapture": { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + No3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + No3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + Capture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "processing", + amount: 6500, + amount_capturable: 6500, + }, + }, + }, + PartialCapture: { + Request: {}, + Response: { + status: 200, + body: { + status: "processing", + amount: 6500, + amount_capturable: 6500, + }, + }, + }, + Void: { + Request: {}, + Response: { + status: 400, + body: { + error: { + code: "IR_16", + message: + "You cannot cancel this payment because it has status processing", + type: "invalid_request", + }, + }, + }, + }, + Refund: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be refund because it has a status of processing. The expected state is succeeded, partially_captured", + code: "IR_14", + }, + }, + }, + }, + PartialRefund: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be refund because it has a status of processing. The expected state is succeeded, partially_captured", + code: "IR_14", + }, + }, + }, + }, + SyncRefund: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be refund because it has a status of processing. The expected state is succeeded, partially_captured", + code: "IR_14", + }, + }, + }, + }, + MandateSingleUse3DSAutoCapture: getCustomExchange({ + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateSingleUse3DSManualCapture: getCustomExchange({ + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateSingleUseNo3DSAutoCapture: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateSingleUseNo3DSManualCapture: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateMultiUseNo3DSAutoCapture: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateMultiUseNo3DSManualCapture: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateMultiUse3DSAutoCapture: getCustomExchange({ + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + MandateMultiUse3DSManualCapture: getCustomExchange({ + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + ZeroAuthMandate: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + }), + SaveCardUseNo3DSAutoCapture: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + SaveCardUseNo3DSManualCapture: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + }), + }, + bank_transfer_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "BRL", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + Pix: getCustomExchange({ + Request: { + payment_method: "bank_transfer", + payment_method_type: "pix", + payment_method_data: { + bank_transfer: { + pix: {}, + }, + }, + currency: "BRL", + }, + }), + }, + bank_redirect_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "EUR", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + ideal: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "ideal", + payment_method_data: { + bank_redirect: { + ideal: { + bank_name: "ing", + country: "NL", + }, + }, + }, + }, + }), + giropay: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "giropay", + payment_method_data: { + bank_redirect: { + giropay: { + bank_name: "", + bank_account_bic: "", + bank_account_iban: "", + preferred_language: "en", + country: "DE", + }, + }, + }, + }, + }), + sofort: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "sofort", + payment_method_data: { + bank_redirect: { + sofort: { + country: "DE", + preferred_language: "en", + }, + }, + }, + }, + }), + eps: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "eps", + payment_method_data: { + bank_redirect: { + eps: { + bank_name: "ing", + }, + }, + }, + }, + }), + blikPaymentIntent: getCustomExchange({ + Request: { + currency: "PLN", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + blik: getCustomExchange({ + Request: { + payment_method: "bank_redirect", + payment_method_type: "blik", + payment_method_data: { + bank_redirect: { + blik: { + blik_code: "777987", + }, + }, + }, + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "PL", + first_name: "john", + last_name: "doe", + }, + }, + }, + }), + }, +}; diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js b/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js index 182d10857ece..d2a8b956b9d6 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/Trustpay.js @@ -1,434 +1,560 @@ +import { getCustomExchange } from "./Commons"; + const successfulNo3DSCardDetails = { - "card_number": "4200000000000000", - "card_exp_month": "10", - "card_exp_year": "25", - "card_holder_name": "joseph Doe", - "card_cvc": "123" + card_number: "4200000000000000", + card_exp_month: "10", + card_exp_year: "25", + card_holder_name: "joseph Doe", + card_cvc: "123", }; const successfulThreeDSTestCardDetails = { - "card_number": "4200000000000067", - "card_exp_month": "03", - "card_exp_year": "2030", - "card_holder_name": "John Doe", - "card_cvc": "737", + card_number: "4200000000000067", + card_exp_month: "03", + card_exp_year: "2030", + card_holder_name: "John Doe", + card_cvc: "737", }; export const connectorDetails = { -card_pm:{ - "PaymentIntent": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "requires_payment_method" - } - } - }, + card_pm: { + PaymentIntent: getCustomExchange({ + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), "3DSAutoCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, "3DSManualCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - "reason": "manual is not supported by trustpay" - } - } - } + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + reason: "manual is not supported by trustpay", + }, + }, + }, }, - "No3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + No3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "No3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - "setup_future_usage": "on_session" - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - "reason": "manual is not supported by trustpay" - } - } - } + No3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + reason: "manual is not supported by trustpay", + }, + }, + }, }, - "Capture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "This Payment could not be captured because it has a payment.status of requires_payment_method. The expected state is requires_capture, partially_captured_and_capturable, processing", - "code": "IR_14", - } - } - } + Capture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be captured because it has a payment.status of requires_payment_method. The expected state is requires_capture, partially_captured_and_capturable, processing", + code: "IR_14", + }, + }, + }, }, - "PartialCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "paymentSuccessfulStatus": "succeeded", - "paymentSyncStatus": "succeeded", - "refundStatus": "succeeded", - "refundSyncStatus": "succeeded", - "customer_acceptance": null, - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "This Payment could not be captured because it has a payment.status of requires_payment_method. The expected state is requires_capture, partially_captured_and_capturable, processing", - "code": "IR_14", - } - } - } + PartialCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + paymentSuccessfulStatus: "succeeded", + paymentSyncStatus: "succeeded", + refundStatus: "succeeded", + refundSyncStatus: "succeeded", + customer_acceptance: null, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: + "This Payment could not be captured because it has a payment.status of requires_payment_method. The expected state is requires_capture, partially_captured_and_capturable, processing", + code: "IR_14", + }, + }, + }, }, - "Void":{ - "Request": { - }, - "Response": { - "status": 200, - "body":{ - status: "cancelled" - - } - } + Void: { + Request: {}, + Response: { + status: 200, + body: { + status: "cancelled", + }, + }, }, - "Refund": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded", - } - - } + Refund: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "PartialRefund": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - }, - "Response": { - "status": 200, - "body": { - "error_code": "1", - "error_message": "transaction declined (invalid amount)", - } - - } + PartialRefund: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + error_code: "1", + error_message: "transaction declined (invalid amount)", + }, + }, }, - "SyncRefund": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "customer_acceptance": null, - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded", - } - - } + SyncRefund: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, }, - "MandateSingleUse3DSAutoCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } - + MandateSingleUse3DSAutoCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, }, - "MandateSingleUse3DSManualCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } - + MandateSingleUse3DSManualCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, }, - "MandateSingleUseNo3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } + MandateSingleUseNo3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, }, - "MandateSingleUseNo3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } + MandateSingleUseNo3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, }, - "MandateMultiUseNo3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } + MandateMultiUseNo3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, }, - "MandateMultiUseNo3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } + MandateMultiUseNo3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, }, - "MandateMultiUse3DSAutoCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + MandateMultiUse3DSAutoCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + MandateMultiUse3DSManualCapture: { + Request: { + card: successfulThreeDSTestCardDetails, + currency: "USD", + mandate_type: { + multi_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, + }, + ZeroAuthMandate: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + mandate_type: { + single_use: { + amount: 8000, + currency: "USD", + }, + }, + }, + Response: { + status: 501, + body: { + error: { + type: "invalid_request", + message: "Setup Mandate flow for Trustpay is not implemented", + code: "IR_00", + }, + }, + }, + }, + SaveCardUseNo3DSAutoCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + SaveCardUseNo3DSManualCapture: { + Request: { + card: successfulNo3DSCardDetails, + currency: "USD", + setup_future_usage: "on_session", + customer_acceptance: { + acceptance_type: "offline", + accepted_at: "1963-05-03T04:07:52.723Z", + online: { + ip_address: "127.0.0.1", + user_agent: "amet irure esse", + }, + }, + }, + Response: { + status: 400, + body: { + error: { + type: "invalid_request", + message: "Payment method type not supported", + code: "HE_03", + }, + }, + }, + }, + }, + bank_redirect_pm: { + PaymentIntent: getCustomExchange({ + Request: { + currency: "EUR", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }), + ideal: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "ideal", + payment_method_data: { + bank_redirect: { + ideal: { + bank_name: "ing", + country: "NL", + }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, }, - "MandateMultiUse3DSManualCapture": { - "Request": { - "card": successfulThreeDSTestCardDetails, - "currency": "USD", - "mandate_type": { - "multi_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } + giropay: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "giropay", + payment_method_data: { + bank_redirect: { + giropay: { + bank_name: "", + bank_account_bic: "", + bank_account_iban: "", + preferred_language: "en", + country: "DE", + }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, }, - "ZeroAuthMandate": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "mandate_type": { - "single_use": { - "amount": 8000, - "currency": "USD" - } - } - }, - "Response": { - "status": 501, - "body": { - "error": { - "type": "invalid_request", - "message": "Setup Mandate flow for Trustpay is not implemented", - "code": "IR_00", - } - } - } + sofort: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "sofort", + payment_method_data: { + bank_redirect: { + sofort: { + country: "DE", + preferred_language: "en", + }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "failed", + error_code: "1133001", + }, + }, }, - "SaveCardUseNo3DSAutoCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "setup_future_usage": "on_session", - "customer_acceptance": { - "acceptance_type": "offline", - "accepted_at": "1963-05-03T04:07:52.723Z", - "online": { - "ip_address": "127.0.0.1", - "user_agent": "amet irure esse" - } + eps: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "eps", + payment_method_data: { + bank_redirect: { + eps: { + bank_name: "ing", }, + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", }, - "Response": { - "status": 200, - "body": { - "status": "succeeded" - } - } + }, }, - "SaveCardUseNo3DSManualCapture": { - "Request": { - "card": successfulNo3DSCardDetails, - "currency": "USD", - "setup_future_usage": "on_session", - "customer_acceptance": { - "acceptance_type": "offline", - "accepted_at": "1963-05-03T04:07:52.723Z", - "online": { - "ip_address": "127.0.0.1", - "user_agent": "amet irure esse" - } + blik: { + Request: { + payment_method: "bank_redirect", + payment_method_type: "blik", + payment_method_data: { + bank_redirect: { + blik: { + name: "John Doe", + email: "example@email.com", }, + }, }, - "Response": { - "status": 400, - "body": { - "error": { - "type": "invalid_request", - "message": "Payment method type not supported", - "code": "HE_03", - } - } - } + billing: { + address: { + line1: "1467", + line2: "Harrison Street", + line3: "Harrison Street", + city: "San Fransico", + state: "California", + zip: "94122", + country: "PL", + first_name: "john", + last_name: "doe", + }, + }, + }, + Response: { + status: 200, + body: { + status: "requires_customer_action", + }, + }, }, -} -} \ No newline at end of file + }, +}; diff --git a/cypress-tests/cypress/e2e/ConnectorUtils/utils.js b/cypress-tests/cypress/e2e/ConnectorUtils/utils.js index 960481a1252a..f14c68ce94c4 100644 --- a/cypress-tests/cypress/e2e/ConnectorUtils/utils.js +++ b/cypress-tests/cypress/e2e/ConnectorUtils/utils.js @@ -1,6 +1,7 @@ import { connectorDetails as adyenConnectorDetails } from "./Adyen.js"; import { connectorDetails as bankOfAmericaConnectorDetails } from "./BankOfAmerica.js"; import { connectorDetails as bluesnapConnectorDetails } from "./Bluesnap.js"; +import { connectorDetails as CommonConnectorDetails } from "./Commons.js"; import { connectorDetails as cybersourceConnectorDetails } from "./Cybersource.js"; import { connectorDetails as nmiConnectorDetails } from "./Nmi.js"; import { connectorDetails as paypalConnectorDetails } from "./Paypal.js"; @@ -8,28 +9,62 @@ import { connectorDetails as stripeConnectorDetails } from "./Stripe.js"; import { connectorDetails as trustpayConnectorDetails } from "./Trustpay.js"; const connectorDetails = { - "adyen": adyenConnectorDetails, - "bankofamerica": bankOfAmericaConnectorDetails, - "bluesnap": bluesnapConnectorDetails, - "cybersource": cybersourceConnectorDetails, - "nmi": nmiConnectorDetails, - "paypal": paypalConnectorDetails, - "stripe": stripeConnectorDetails, - "trustpay": trustpayConnectorDetails + adyen: adyenConnectorDetails, + bankofamerica: bankOfAmericaConnectorDetails, + bluesnap: bluesnapConnectorDetails, + commons: CommonConnectorDetails, + cybersource: cybersourceConnectorDetails, + nmi: nmiConnectorDetails, + paypal: paypalConnectorDetails, + stripe: stripeConnectorDetails, + trustpay: trustpayConnectorDetails, +}; +export default function getConnectorDetails(connectorId) { + let x = mergeDetails(connectorId); + return x; +} +function mergeDetails(connectorId) { + const connectorData = getValueByKey(connectorDetails, connectorId); + const fallbackData = getValueByKey(connectorDetails, "commons"); + // Merge data, prioritizing connectorData and filling missing data from fallbackData + const mergedDetails = mergeConnectorDetails(connectorData, fallbackData); + return mergedDetails; } +function mergeConnectorDetails(source, fallback) { + const merged = {}; -export default function getConnectorDetails(connectorId) { - let x = getValueByKey(connectorDetails, connectorId); - return x; + // Loop through each key in the source object + for (const key in source) { + merged[key] = { ...source[key] }; // Copy properties from source + + // Check if fallback has the same key and properties are missing in source + if (fallback[key]) { + for (const subKey in fallback[key]) { + if (!merged[key][subKey]) { + merged[key][subKey] = fallback[key][subKey]; + } + } + } + } + + // Add missing keys from fallback that are not present in source + for (const key in fallback) { + if (!merged[key]) { + merged[key] = fallback[key]; + } + } + + return merged; } function getValueByKey(jsonObject, key) { - const data = typeof jsonObject === 'string' ? JSON.parse(jsonObject) : jsonObject; + const data = + typeof jsonObject === "string" ? JSON.parse(jsonObject) : jsonObject; - if (data && typeof data === 'object' && key in data) { + if (data && typeof data === "object" && key in data) { return data[key]; } else { return null; @@ -37,10 +72,13 @@ function getValueByKey(jsonObject, key) { } export const should_continue_further = (res_data) => { - if(res_data.body.error !== undefined || res_data.body.error_code !== undefined || res_data.body.error_message !== undefined){ - return false; - } - else { - return true; + if ( + res_data.body.error !== undefined || + res_data.body.error_code !== undefined || + res_data.body.error_message !== undefined + ) { + return false; + } else { + return true; } -} \ No newline at end of file +}; diff --git a/cypress-tests/cypress/fixtures/confirm-body.json b/cypress-tests/cypress/fixtures/confirm-body.json index 32b9a7108be7..fbf23a2016b6 100644 --- a/cypress-tests/cypress/fixtures/confirm-body.json +++ b/cypress-tests/cypress/fixtures/confirm-body.json @@ -1,20 +1,9 @@ { "client_secret": "", "return_url": "https://hyperswitch.io", - "payment_method": "card", "confirm": true, - "payment_method_data": { - "card": { - "_comment": "these details are fetched from utils", - "card_number": "YourCardNumberHere", - "card_exp_month": "04", - "card_exp_year": "24", - "card_holder_name": "xyz", - "card_cvc": "424", - "card_issuer": "", - "card_network": "Visa" - } - }, + "payment_method": "card", + "payment_method_data": {}, "customer_acceptance": { "acceptance_type": "offline", "accepted_at": "1963-05-03T04:07:52.723Z", diff --git a/cypress-tests/cypress/fixtures/create-connector-body.json b/cypress-tests/cypress/fixtures/create-connector-body.json index d3575c0b2ccf..9ab253565ed7 100644 --- a/cypress-tests/cypress/fixtures/create-connector-body.json +++ b/cypress-tests/cypress/fixtures/create-connector-body.json @@ -52,6 +52,78 @@ "installment_payment_enabled": true } ] + }, + { + "payment_method": "bank_transfer", + "payment_method_types": [ + { + "payment_method_type": "pix", + "minimum_amount": 0, + "maximum_amount": 68607706, + "recurring_enabled": false, + "installment_payment_enabled": true + } + ] + }, + { + "payment_method": "bank_redirect", + "payment_method_types": [ + { + "payment_method_type": "ideal", + "payment_experience": null, + "card_networks": null, + "accepted_currencies": null, + "accepted_countries": null, + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "giropay", + "payment_experience": null, + "card_networks": null, + "accepted_currencies": null, + "accepted_countries": null, + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "sofort", + "payment_experience": null, + "card_networks": null, + "accepted_currencies": null, + "accepted_countries": null, + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "eps", + "payment_experience": null, + "card_networks": null, + "accepted_currencies": null, + "accepted_countries": null, + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + }, + { + "payment_method_type": "blik", + "payment_experience": null, + "card_networks": null, + "accepted_currencies": null, + "accepted_countries": null, + "minimum_amount": 1, + "maximum_amount": 68607706, + "recurring_enabled": true, + "installment_payment_enabled": true + } + ] } ], "metadata": { diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index e78802e366fe..4842ed017344 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -26,6 +26,7 @@ // commands.js or your custom support file import * as RequestBodyUtils from "../utils/RequestBodyUtils"; +import { handleRedirection } from "./redirectionHandler"; function logRequestId(xRequestId) { if (xRequestId) { @@ -35,6 +36,13 @@ function logRequestId(xRequestId) { } } +function defaultErrorHandler(response, response_data) { + expect(response.body).to.have.property("error"); + for (const key in response_data.body.error) { + expect(response_data.body.error[key]).to.equal(response.body.error[key]); + } +} + Cypress.Commands.add("merchantCreateCallTest", (merchantCreateBody, globalState) => { const randomMerchantId = RequestBodyUtils.generateRandomString(); RequestBodyUtils.setMerchantId(merchantCreateBody, randomMerchantId); @@ -171,10 +179,7 @@ Cypress.Commands.add("createPaymentIntentTest", (request, req_data, res_data, au expect(request.amount).to.equal(response.body.amount_capturable); } else { - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); }); @@ -219,7 +224,6 @@ Cypress.Commands.add("confirmCallTest", (confirmBody, req_data, res_data, confir body: confirmBody, }).then((response) => { logRequestId(response.headers['x-request-id']); - expect(res_data.status).to.equal(response.status); expect(response.headers["content-type"]).to.include("application/json"); if(response.status === 200){ @@ -227,15 +231,14 @@ Cypress.Commands.add("confirmCallTest", (confirmBody, req_data, res_data, confir if (response.body.capture_method === "automatic") { if (response.body.authentication_type === "three_ds") { expect(response.body).to.have.property("next_action") - .to.have.property("redirect_to_url"); + .to.have.property("redirect_to_url"); globalState.set("nextActionUrl", response.body.next_action.redirect_to_url); } else if (response.body.authentication_type === "no_three_ds") { for(const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + defaultErrorHandler(response, res_data); } } else if (response.body.capture_method === "manual") { if (response.body.authentication_type === "three_ds") { @@ -244,24 +247,158 @@ Cypress.Commands.add("confirmCallTest", (confirmBody, req_data, res_data, confir globalState.set("nextActionUrl", response.body.next_action.redirect_to_url); } else if (response.body.authentication_type === "no_three_ds") { - for(const key in res_data.body) { + for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); - } } else { - // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + } + } else { + defaultErrorHandler(response, res_data); } } } else { - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } - }); }); +Cypress.Commands.add( + "confirmBankRedirectCallTest", + (confirmBody, req_data, res_data, confirm, globalState) => { + const paymentIntentId = globalState.get("paymentID"); + const connectorId = globalState.get("connectorId"); + for (const key in req_data) { + confirmBody[key] = req_data[key]; + } + confirmBody.confirm = confirm; + confirmBody.client_secret = globalState.get("clientSecret"); + + cy.request({ + method: "POST", + url: `${globalState.get("baseUrl")}/payments/${paymentIntentId}/confirm`, + headers: { + "Content-Type": "application/json", + "api-key": globalState.get("publishableKey"), + }, + failOnStatusCode: false, + body: confirmBody, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + expect(res_data.status).to.equal(response.status); + expect(response.headers["content-type"]).to.include("application/json"); + globalState.set("paymentID", paymentIntentId); + globalState.set("paymentMethodType", confirmBody.payment_method_type); + + switch (response.body.authentication_type) { + case "three_ds": + if ( + response.body.capture_method === "automatic" || + response.body.capture_method === "manual" + ) { + if (response.body.status !== "failed") { // we get many statuses here, hence this verification + if (connectorId === "adyen" && response.body.payment_method_type === "blik") { + expect(response.body) + .to.have.property("next_action") + .to.have.property("type") + .to.equal("wait_screen_information"); + } else { + expect(response.body) + .to.have.property("next_action") + .to.have.property("redirect_to_url"); + globalState.set( + "nextActionUrl", + response.body.next_action.redirect_to_url + ); + } + } else if (response.body.status === "failed") { + expect(response.body.error_code).to.equal(res_data.body.error_code); + } + } else { + defaultErrorHandler(response, res_data); + } + break; + case "no_three_ds": + if ( + response.body.capture_method === "automatic" || + response.body.capture_method === "manual" + ) { + expect(response.body) + .to.have.property("next_action") + .to.have.property("redirect_to_url"); + globalState.set( + "nextActionUrl", + response.body.next_action.redirect_to_url + ); + } else { + defaultErrorHandler(response, res_data); + } + break; + default: + defaultErrorHandler(response, res_data); + } + }); + } +); + +Cypress.Commands.add( + "confirmBankTransferCallTest", + (confirmBody, req_data, res_data, confirm, globalState) => { + const paymentIntentID = globalState.get("paymentID"); + for (const key in req_data) { + confirmBody[key] = req_data[key]; + } + confirmBody.confirm = confirm; + confirmBody.client_secret = globalState.get("clientSecret"); + globalState.set("paymentMethodType", confirmBody.payment_method_type); + + cy.request({ + method: "POST", + url: `${globalState.get("baseUrl")}/payments/${paymentIntentID}/confirm`, + headers: { + "Content-Type": "application/json", + "api-key": globalState.get("publishableKey"), + }, + failOnStatusCode: false, + body: confirmBody, + }).then((response) => { + logRequestId(response.headers["x-request-id"]); + expect(res_data.status).to.equal(response.status); + expect(response.headers["content-type"]).to.include("application/json"); + globalState.set("paymentID", paymentIntentID); + if (response.status === 200) { + if ( + response.body.capture_method === "automatic" || + response.body.capture_method === "manual" + ) { + switch (response.body.payment_method_type) { + case "pix": + expect(response.body) + .to.have.property("next_action") + .to.have.property("qr_code_url"); + globalState.set( + "nextActionUrl", // This is intentionally kept as nextActionUrl to avoid issues during handleRedirection call, + response.body.next_action.qr_code_url + ); + break; + default: + expect(response.body) + .to.have.property("next_action") + .to.have.property("redirect_to_url"); + globalState.set( + "nextActionUrl", + response.body.next_action.redirect_to_url + ); + break; + } + } else { + defaultErrorHandler(response, res_data); + } + } else { + defaultErrorHandler(response, res_data); + } + }); + } +); + Cypress.Commands.add("createConfirmPaymentTest", (createConfirmPaymentBody, req_data, res_data, authentication_type, capture_method, globalState) => { createConfirmPaymentBody.payment_method_data.card = req_data.card; createConfirmPaymentBody.authentication_type = authentication_type; @@ -301,8 +438,7 @@ Cypress.Commands.add("createConfirmPaymentTest", (createConfirmPaymentBody, req_ expect(res_data.body[key]).to.equal(response.body[key]); } } else { - // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + defaultErrorHandler(response, res_data); } } else if (response.body.capture_method === "manual") { @@ -317,28 +453,24 @@ Cypress.Commands.add("createConfirmPaymentTest", (createConfirmPaymentBody, req_ else if (response.body.authentication_type === "no_three_ds") { for(const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); - } } else { - // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + } + } else { + defaultErrorHandler(response, res_data); } } } else{ - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); }); // This is consequent saved card payment confirm call test(Using payment token) -Cypress.Commands.add("saveCardConfirmCallTest", (SaveCardConfirmBody, req_data, res_data,globalState) => { +Cypress.Commands.add("saveCardConfirmCallTest", (saveCardConfirmBody, req_data, res_data,globalState) => { const paymentIntentID = globalState.get("paymentID"); - SaveCardConfirmBody.card_cvc = req_data.card.card_cvc; - SaveCardConfirmBody.payment_token = globalState.get("paymentToken"); - SaveCardConfirmBody.client_secret = globalState.get("clientSecret"); - console.log("conf conn ->" + globalState.get("connectorId")); + saveCardConfirmBody.card_cvc = req_data.card.card_cvc; + saveCardConfirmBody.payment_token = globalState.get("paymentToken"); + saveCardConfirmBody.client_secret = globalState.get("clientSecret"); cy.request({ method: "POST", url: `${globalState.get("baseUrl")}/payments/${paymentIntentID}/confirm`, @@ -347,7 +479,7 @@ Cypress.Commands.add("saveCardConfirmCallTest", (SaveCardConfirmBody, req_data, "api-key": globalState.get("publishableKey"), }, failOnStatusCode: false, - body: SaveCardConfirmBody, + body: saveCardConfirmBody, }) .then((response) => { logRequestId(response.headers['x-request-id']); @@ -368,7 +500,7 @@ Cypress.Commands.add("saveCardConfirmCallTest", (SaveCardConfirmBody, req_data, expect(response.body.customer_id).to.equal(globalState.get("customerId")); } else { // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + defaultErrorHandler(response, res_data); } } else if (response.body.capture_method === "manual") { if (response.body.authentication_type === "three_ds") { @@ -378,18 +510,16 @@ Cypress.Commands.add("saveCardConfirmCallTest", (SaveCardConfirmBody, req_data, else if (response.body.authentication_type === "no_three_ds") { for(const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); - } expect(response.body.customer_id).to.equal(globalState.get("customerId")); + } + expect(response.body.customer_id).to.equal(globalState.get("customerId")); } else { // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + defaultErrorHandler(response, res_data); } } } else { - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); }); @@ -419,12 +549,8 @@ Cypress.Commands.add("captureCallTest", (requestBody, req_data, res_data, amount } } else{ - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } - }); }); @@ -450,16 +576,12 @@ Cypress.Commands.add("voidCallTest", (requestBody, req_data, res_data, globalSta } } else{ - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); }); Cypress.Commands.add("retrievePaymentCallTest", (globalState) => { - console.log("syncpaymentID ->" + globalState.get("paymentID")); const payment_id = globalState.get("paymentID"); cy.request({ method: "GET", @@ -476,7 +598,6 @@ Cypress.Commands.add("retrievePaymentCallTest", (globalState) => { expect(response.body.payment_id).to.equal(payment_id); expect(response.body.amount).to.equal(globalState.get("paymentAmount")); globalState.set("paymentID", response.body.payment_id); - }); }); @@ -506,10 +627,7 @@ Cypress.Commands.add("refundCallTest", (requestBody, req_data, res_data, refund_ expect(response.body.payment_id).to.equal(payment_id); } else{ - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); @@ -591,10 +709,7 @@ Cypress.Commands.add("citForMandatesCallTest", (requestBody, req_data, res_data, } } else{ - expect(response.body).to.have.property("error"); - for(const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); }); @@ -606,7 +721,6 @@ Cypress.Commands.add("mitForMandatesCallTest", (requestBody, amount, confirm, ca requestBody.mandate_id = globalState.get("mandateId"); requestBody.customer_id = globalState.get("customerId"); globalState.set("paymentAmount", requestBody.amount); - console.log("mit body " + JSON.stringify(requestBody)); cy.request({ method: "POST", url: `${globalState.get("baseUrl")}/payments`, @@ -620,7 +734,6 @@ Cypress.Commands.add("mitForMandatesCallTest", (requestBody, amount, confirm, ca logRequestId(response.headers['x-request-id']); expect(response.headers["content-type"]).to.include("application/json"); globalState.set("paymentID", response.body.payment_id); - console.log("mit statusss-> " + response.body.status); if (response.body.capture_method === "automatic") { if (response.body.authentication_type === "three_ds") { expect(response.body).to.have.property("next_action") @@ -631,8 +744,7 @@ Cypress.Commands.add("mitForMandatesCallTest", (requestBody, amount, confirm, ca } else if (response.body.authentication_type === "no_three_ds") { expect(response.body.status).to.equal("succeeded"); } else { - // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + defaultErrorHandler(response, res_data); } } else if (response.body.capture_method === "manual") { @@ -645,8 +757,7 @@ Cypress.Commands.add("mitForMandatesCallTest", (requestBody, amount, confirm, ca } else if (response.body.authentication_type === "no_three_ds") { expect(response.body.status).to.equal("requires_capture"); } else { - // Handle other authentication types as needed - throw new Error(`Unsupported authentication type: ${authentication_type}`); + defaultErrorHandler(response, res_data); } } }); @@ -698,82 +809,53 @@ Cypress.Commands.add("revokeMandateCallTest", (globalState) => { }); }); - Cypress.Commands.add("handleRedirection", (globalState, expected_redirection) => { let connectorId = globalState.get("connectorId"); let expected_url = new URL(expected_redirection); let redirection_url = new URL(globalState.get("nextActionUrl")); - cy.visit(redirection_url.href); - if (globalState.get("connectorId") == "adyen") { - cy.get('iframe') - .its('0.contentDocument.body') - .within((body) => { - cy.get('input[type="password"]').click(); - cy.get('input[type="password"]').type("password"); - cy.get('#buttonSubmit').click(); - }) - } - else if (globalState.get("connectorId") === "cybersource" || globalState.get("connectorId") === "bankofamerica") { - cy.get('iframe', { timeout: 15000 }) - .its('0.contentDocument.body') - .within((body) => { - cy.get('input[type="text"]').click().type("1234"); - cy.get('input[value="SUBMIT"]').click(); - }) - } - else if (globalState.get("connectorId") === "nmi" || globalState.get("connectorId") === "noon") { - cy.get('iframe', { timeout: 150000 }) - .its('0.contentDocument.body') - .within((body) => { - cy.get('iframe', { timeout: 20000 }) - .its('0.contentDocument.body') - .within((body) => { - cy.get('form[name="cardholderInput"]', { timeout: 20000 }).should('exist').then(form => { - cy.get('input[name="challengeDataEntry"]').click().type("1234"); - cy.get('input[value="SUBMIT"]').click(); - }) - }) - }) - } - - else if (globalState.get("connectorId") === "stripe") { - cy.get('iframe', { timeout: 30000 }) - .its('0.contentDocument.body') - .within((body) => { - cy.get('iframe') - .its('0.contentDocument.body') - .within((body) => { - cy.get('#test-source-authorize-3ds').click(); - }) - }) - } - else if (globalState.get("connectorId") === "trustpay") { - cy.get('form[name="challengeForm"]', { timeout: 10000 }).should('exist').then(form => { - cy.get('#outcomeSelect').select('Approve').should('have.value', 'Y') - cy.get('button[type="submit"]').click(); - }) - } - + handleRedirection( + "three_ds", + { redirection_url, expected_url }, + connectorId, + null + ); +}); - else { - // If connectorId is neither of adyen, trustpay, nmi, stripe, bankofamerica or cybersource, wait for 10 seconds - cy.wait(10000); +Cypress.Commands.add( + "handleBankRedirectRedirection", + (globalState, payment_method_type, expected_redirection) => { + let connectorId = globalState.get("connectorId"); + let expected_url = new URL(expected_redirection); + let redirection_url = new URL(globalState.get("nextActionUrl")); + // explicitly restricting `sofort` payment method by adyen from running as it stops other tests from running + // trying to handle that specific case results in stripe 3ds tests to fail + if (!(connectorId == "adyen" && payment_method_type == "sofort")) { + handleRedirection( + "bank_redirect", + { redirection_url, expected_url }, + connectorId, + payment_method_type + ); + } } - - // Handling redirection - if (redirection_url.host.endsWith(expected_url.host)) { - // No CORS workaround needed - cy.window().its('location.origin').should('eq', expected_url.origin); - } else { - // Workaround for CORS to allow cross-origin iframe - cy.origin(expected_url.origin, { args: { expected_url: expected_url.origin } }, ({ expected_url }) => { - cy.window().its('location.origin').should('eq', expected_url); - }) +); + +Cypress.Commands.add( + "handleBankTransferRedirection", + (globalState, payment_method_type, expected_redirection) => { + let connectorId = globalState.get("connectorId"); + let redirection_url = new URL(globalState.get("nextActionUrl")); + cy.log(payment_method_type); + handleRedirection( + "bank_transfer", + { redirection_url, expected_redirection }, + connectorId, + payment_method_type + ); } -}); +); Cypress.Commands.add("listCustomerPMCallTest", (globalState) => { - console.log("customerID ->" + globalState.get("customerId")); const customerId = globalState.get("customerId"); cy.request({ method: "GET", @@ -792,7 +874,7 @@ Cypress.Commands.add("listCustomerPMCallTest", (globalState) => { expect(paymentToken).to.equal(globalState.get("paymentToken")); // Verify paymentToken } else { - throw new Error(`Payment token not found`); + defaultErrorHandler(response, res_data); } }); }); @@ -808,11 +890,7 @@ Cypress.Commands.add("listRefundCallTest", (requestBody, globalState) => { body: requestBody, }).then((response) => { logRequestId(response.headers['x-request-id']); - expect(response.headers["content-type"]).to.include("application/json"); expect(response.body.data).to.be.an('array').and.not.empty; - }); }); - - diff --git a/cypress-tests/cypress/support/redirectionhandler.js b/cypress-tests/cypress/support/redirectionhandler.js new file mode 100644 index 000000000000..6e4818581531 --- /dev/null +++ b/cypress-tests/cypress/support/redirectionhandler.js @@ -0,0 +1,319 @@ +import jsQR from "jsqr"; + +export function handleRedirection( + redirection_type, + urls, + connectorId, + payment_method_type +) { + switch (redirection_type) { + case "three_ds": + threeDsRedirection(urls.redirection_url, urls.expected_url, connectorId); + break; + case "bank_redirect": + bankRedirectRedirection( + urls.redirection_url, + urls.expected_url, + connectorId, + payment_method_type + ); + break; + case "bank_transfer": + bankTransferRedirection( + urls.redirection_url, + urls.expected_url, + connectorId, + payment_method_type + ); + break; + default: + throw new Error(`Redirection known: ${redirection_type}`); + } +} + +function bankTransferRedirection( + redirection_url, + expected_url, + connectorId, + payment_method_type +) { + cy.request(redirection_url.href).then((response) => { + switch (connectorId) { + case "adyen": + switch (payment_method_type) { + case "pix": + expect(response.status).to.eq(200); + fetchAndParseQRCode(redirection_url.href).then((qrCodeData) => { + expect(qrCodeData).to.eq("TestQRCodeEMVToken"); + }); + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + // expected_redirection can be used here to handle other payment methods + } + break; + default: + verifyReturnUrl(redirection_url, expected_url, true); + } + }); +} + +function threeDsRedirection(redirection_url, expected_url, connectorId) { + cy.visit(redirection_url.href); + if (connectorId == "adyen") { + cy.get("iframe") + .its("0.contentDocument.body") + .within((body) => { + cy.get('input[type="password"]').click(); + cy.get('input[type="password"]').type("password"); + cy.get("#buttonSubmit").click(); + }); + } else if (connectorId === "bankofamerica" || connectorId === "cybersource") { + cy.get("iframe", { timeout: 15000 }) + .its("0.contentDocument.body") + .within((body) => { + cy.get('input[type="text"]').click().type("1234"); + cy.get('input[value="SUBMIT"]').click(); + }); + } else if (connectorId === "nmi" || connectorId === "noon") { + cy.get("iframe", { timeout: 150000 }) + .its("0.contentDocument.body") + .within((body) => { + cy.get("iframe", { timeout: 20000 }) + .its("0.contentDocument.body") + .within((body) => { + cy.get('form[name="cardholderInput"]', { timeout: 20000 }) + .should("exist") + .then((form) => { + cy.get('input[name="challengeDataEntry"]').click().type("1234"); + cy.get('input[value="SUBMIT"]').click(); + }); + }); + }); + } else if (connectorId === "stripe") { + cy.get("iframe", { timeout: 15000 }) + .its("0.contentDocument.body") + .within((body) => { + cy.get("iframe") + .its("0.contentDocument.body") + .within((body) => { + cy.get("#test-source-authorize-3ds").click(); + }); + }); + } else if (connectorId === "trustpay") { + cy.get('form[name="challengeForm"]', { timeout: 10000 }) + .should("exist") + .then((form) => { + cy.get("#outcomeSelect").select("Approve").should("have.value", "Y"); + cy.get('button[type="submit"]').click(); + }); + } else { + // If connectorId is neither of adyen, trustpay, nmi, stripe, bankofamerica or cybersource, wait for 10 seconds + cy.wait(10000); + } + + verifyReturnUrl(redirection_url, expected_url, true); +} + +function bankRedirectRedirection( + redirection_url, + expected_url, + connectorId, + payment_method_type +) { + let verifyUrl = false; + cy.visit(redirection_url.href); + + switch (connectorId) { + case "adyen": + switch (payment_method_type) { + case "eps": + cy.get("h1").should("contain.text", "Acquirer Simulator"); + cy.get('[value="authorised"]').click(); + cy.url().should("include", "status=succeeded"); + cy.wait(5000); + break; + case "ideal": + cy.get(":nth-child(4) > td > p").should( + "contain.text", + "Your Payment was Authorised/Refused/Cancelled (It may take up to five minutes to show on the Payment List)" + ); + cy.get(".btnLink").click(); + cy.url().should("include", "status=succeeded"); + cy.wait(5000); + break; + case "giropay": + cy.get( + ".rds-cookies-overlay__allow-all-cookies-btn > .rds-button" + ).click(); + cy.wait(5000); + cy.get(".normal-3").should( + "contain.text", + "Bank suchen ‑ mit giropay zahlen." + ); + cy.get("#bankSearch").type("giropay TestBank{enter}"); + cy.get(".normal-2 > div").click(); + cy.get('[data-testid="customerIban"]').type("DE48499999601234567890"); + cy.get('[data-testid="customerIdentification"]').type("1234567890"); + cy.get(":nth-child(3) > .rds-button").click(); + cy.get('[data-testid="onlineBankingPin"]').type("1234"); + cy.get(".rds-button--primary").click(); + cy.get(":nth-child(5) > .rds-radio-input-group__label").click(); + cy.get(".rds-button--primary").click(); + cy.get('[data-testid="photoTan"]').type("123456"); + cy.get(".rds-button--primary").click(); + cy.wait(5000); + cy.url().should("include", "status=succeeded"); + cy.wait(5000); + break; + case "sofort": + cy.get(".modal-overlay.modal-shown.in", { timeout: 5000 }).then( + ($modal) => { + // If modal is found, handle it + if ($modal.length > 0) { + cy.get("button.cookie-modal-deny-all.button-tertiary") + .should("be.visible") + .should("contain", "Reject All") + .click({ force: true, multiple: true }); + cy.get("div#TopBanks.top-banks-multistep") + .should("contain", "Demo Bank") + .as("btn") + .click(); + cy.get("@btn").click(); + } else { + cy.get("input.phone").type("9123456789"); + cy.get("#button.onContinue") + .should("contain", "Continue") + .click(); + } + } + ); + break; + case "trustly": + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + verifyUrl = true; + break; + case "trustpay": + switch (payment_method_type) { + case "eps": + cy.get("._transactionId__header__iXVd_").should( + "contain.text", + "Bank suchen ‑ mit eps zahlen." + ); + cy.get(".BankSearch_searchInput__uX_9l").type( + "Allgemeine Sparkasse Oberösterreich Bank AG{enter}" + ); + cy.get(".BankSearch_searchResultItem__lbcKm").click(); + cy.get("._transactionId__primaryButton__nCa0r").click(); + cy.get("#loginTitle").should( + "contain.text", + "eps Online-Ãœberweisung Login" + ); + cy.get("#user") + .should("be.visible") + .should("be.enabled") + .focus() + .type("Verfügernummer"); + cy.get("input#submitButton.btn.btn-primary").click(); + break; + case "ideal": + cy.get("p").should( + "contain.text", + "Choose your iDeal Issuer Bank please" + ); + cy.get("#issuerSearchInput").click(); + cy.get("#issuerSearchInput").type("ING{enter}"); + cy.get("#trustpay__selectIssuer_submit").click(); + break; + case "giropay": + cy.get("._transactionId__header__iXVd_").should( + "contain.text", + "Bank suchen ‑ mit giropay zahlen." + ); + cy.get(".BankSearch_searchInput__uX_9l").type( + "Volksbank Hildesheim{enter}" + ); + cy.get(".BankSearch_searchIcon__EcVO7").click(); + cy.get(".BankSearch_bankWrapper__R5fUK").click(); + cy.get("._transactionId__primaryButton__nCa0r").click(); + cy.get(".normal-3").should("contain.text", "Kontoauswahl"); + break; + case "sofort": + break; + case "trustly": + break; + default: + throw new Error( + `Unsupported payment method type: ${payment_method_type}` + ); + } + verifyUrl = false; + break; + default: + throw new Error(`Unsupported connector: ${connectorId}`); + } + verifyReturnUrl(redirection_url, expected_url, verifyUrl); +} + +function verifyReturnUrl(redirection_url, expected_url, forward_flow) { + if (forward_flow) { + // Handling redirection + if (redirection_url.host.endsWith(expected_url.host)) { + // No CORS workaround needed + cy.window().its("location.origin").should("eq", expected_url.origin); + } else { + // Workaround for CORS to allow cross-origin iframe + cy.origin( + expected_url.origin, + { args: { expected_url: expected_url.origin } }, + ({ expected_url }) => { + cy.window().its("location.origin").should("eq", expected_url); + } + ); + } + } +} + +async function fetchAndParseQRCode(url) { + const response = await fetch(url, { encoding: "binary" }); + if (!response.ok) { + throw new Error(`Failed to fetch QR code image: ${response.statusText}`); + } + const blob = await response.blob(); + const reader = new FileReader(); + return await new Promise((resolve, reject) => { + reader.onload = () => { + const base64Image = reader.result.split(",")[1]; // Remove data URI prefix + const image = new Image(); + image.src = base64Image; + + image.onload = () => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + canvas.width = image.width; + canvas.height = image.height; + ctx.drawImage(image, 0, 0); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const qrCodeData = jsQR( + imageData.data, + imageData.width, + imageData.height + ); + + if (qrCodeData) { + resolve(qrCodeData.data); + } else { + reject(new Error("Failed to decode QR code")); + } + }; + image.onerror = reject; // Handle image loading errors + }; + reader.readAsDataURL(blob); + }); +} diff --git a/cypress-tests/package.json b/cypress-tests/package.json index fff8cd8b3f17..af4b0244fbd5 100644 --- a/cypress-tests/package.json +++ b/cypress-tests/package.json @@ -9,5 +9,8 @@ "cypress:ci": "npx cypress run --headless" }, "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "jsqr": "^1.4.0" + } } diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 837ef4fe75a7..2abe90f6086a 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -100,7 +100,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" -klarna.base_url = "https://api{{region_based_endpoint}}.playground.klarna.com/" +klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" mollie.secondary_base_url = "https://api.cc.mollie.com/v1/" @@ -322,3 +322,9 @@ partner_id = "" [unmasked_headers] keys = "user-agent" + +[multitenancy] +enabled = false + +[multitenancy.tenants] +public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "public", redis_key_prefix = ""} \ No newline at end of file diff --git a/openapi/openapi_spec.json b/openapi/openapi_spec.json index 9defe94f94c5..f42481798a49 100644 --- a/openapi/openapi_spec.json +++ b/openapi/openapi_spec.json @@ -14594,7 +14594,8 @@ "PaymentsExternalAuthenticationResponse": { "type": "object", "required": [ - "trans_status" + "trans_status", + "three_ds_requestor_url" ], "properties": { "trans_status": { @@ -14629,6 +14630,10 @@ "type": "string", "description": "Contains the JWS object created by the ACS for the ARes message", "nullable": true + }, + "three_ds_requestor_url": { + "type": "string", + "description": "Three DS Requestor URL" } } }, @@ -17400,7 +17405,9 @@ "amount": { "type": "integer", "format": "int64", - "description": "The refund amount, which should be less than or equal to the total payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc" + "description": "The refund amount, which should be less than or equal to the total payment amount. Amount for the payment in lowest denomination of the currency. (i.e) in cents for USD denomination, in paisa for INR denomination etc", + "example": 6540, + "minimum": 100 }, "currency": { "type": "string", diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/.meta.json new file mode 100644 index 000000000000..e4ef30e39e8d --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/.meta.json @@ -0,0 +1,7 @@ +{ + "childrenOrder": [ + "Payments - Create", + "Payments - Capture", + "Payments - Retrieve" + ] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/.event.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/.event.meta.json new file mode 100644 index 000000000000..0731450e6b25 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/.event.meta.json @@ -0,0 +1,3 @@ +{ + "eventOrder": ["event.test.js"] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/event.test.js new file mode 100644 index 000000000000..f560d84ea730 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial 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 "succeeded" for "status" +if (jsonData?.status) { + pm.test( + "[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'", + function () { + pm.expect(jsonData.status).to.eql("partially_captured"); + }, + ); +} + +// 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/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/request.json new file mode 100644 index 000000000000..9fe257ed85e6 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/request.json @@ -0,0 +1,39 @@ +{ + "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/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/response.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Capture/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/.event.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/.event.meta.json new file mode 100644 index 000000000000..0731450e6b25 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/.event.meta.json @@ -0,0 +1,3 @@ +{ + "eventOrder": ["event.test.js"] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/event.test.js new file mode 100644 index 000000000000..d683186aa007 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial 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/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/request.json new file mode 100644 index 000000000000..0cc6a0477a98 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/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": true, + "capture_method": "manual", + "capture_on": "2022-09-10T10:11:12Z", + "amount_to_capture": 6540, + "customer_id": "AirwallexCustomer", + "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", + "payment_method": "card", + "payment_method_data": { + "card": { + "card_number": "4035501000000008", + "card_exp_month": "10", + "card_exp_year": "2025", + "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" + } + } + }, + "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/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/response.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Create/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/.event.meta.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/.event.meta.json new file mode 100644 index 000000000000..0731450e6b25 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/.event.meta.json @@ -0,0 +1,3 @@ +{ + "eventOrder": ["event.test.js"] +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/event.test.js new file mode 100644 index 000000000000..ca68dd7045be --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial 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 "succeeded" for "status" +if (jsonData?.status) { + pm.test( + "[POST]::/payments - Content check if value for 'status' matches 'partially_captured'", + function () { + pm.expect(jsonData.status).to.eql("partially_captured"); + }, + ); +} diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/request.json new file mode 100644 index 000000000000..6cd4b7d96c52 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/request.json @@ -0,0 +1,28 @@ +{ + "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/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/response.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/response.json new file mode 100644 index 000000000000..fe51488c7066 --- /dev/null +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario11-Create payment with Manual Partial capture/Payments - Retrieve/response.json @@ -0,0 +1 @@ +[] diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js index 2d7dbc507fb0..2681a4daa9e9 100644 --- a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/event.test.js @@ -88,7 +88,7 @@ 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); + pm.expect(jsonData.amount_received).to.eql(6540); }, ); } diff --git a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json index 9fe257ed85e6..cceb2b55f0a7 100644 --- a/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json +++ b/postman/collection-dir/airwallex/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Capture/request.json @@ -18,7 +18,7 @@ } }, "raw_json_formatted": { - "amount_to_capture": 6000, + "amount_to_capture": 6540, "statement_descriptor_name": "Joseph", "statement_descriptor_suffix": "JS" } diff --git a/postman/collection-json/adyen_uk.postman_collection.json b/postman/collection-json/adyen_uk.postman_collection.json index dc34efad9e3b..255e099f0904 100644 --- a/postman/collection-json/adyen_uk.postman_collection.json +++ b/postman/collection-json/adyen_uk.postman_collection.json @@ -8960,7 +8960,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9780,7 +9780,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10423,7 +10423,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/airwallex.postman_collection.json b/postman/collection-json/airwallex.postman_collection.json index 3e6b467f9bd4..ce92a22c8bb1 100644 --- a/postman/collection-json/airwallex.postman_collection.json +++ b/postman/collection-json/airwallex.postman_collection.json @@ -806,6 +806,398 @@ { "name": "Happy Cases", "item": [ + { + "name": "Scenario11-Create payment with Manual Partial 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,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"AirwallexCustomer\",\"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\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4035501000000008\",\"card_exp_month\":\"10\",\"card_exp_year\":\"2025\",\"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\"}}" + }, + "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 \"succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]:://payments/:id/capture - Content check if value for 'status' matches 'partially_captured'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " },", + " );", + "}", + "", + "// 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 \"succeeded\" for \"status\"", + "if (jsonData?.status) {", + " pm.test(", + " \"[POST]::/payments - Content check if value for 'status' matches 'partially_captured'\",", + " function () {", + " pm.expect(jsonData.status).to.eql(\"partially_captured\");", + " },", + " );", + "}", + "" + ], + "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": "Scenario1-Create payment with confirm true", "item": [ @@ -2050,7 +2442,7 @@ " pm.test(", " \"[POST]::/payments:id/capture - Content check if value for 'amount_received' matches '6000'\",", " function () {", - " pm.expect(jsonData.amount_received).to.eql(6000);", + " pm.expect(jsonData.amount_received).to.eql(6540);", " },", " );", "}", @@ -2079,7 +2471,7 @@ "language": "json" } }, - "raw": "{\"amount_to_capture\":6000,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" + "raw": "{\"amount_to_capture\":6540,\"statement_descriptor_name\":\"Joseph\",\"statement_descriptor_suffix\":\"JS\"}" }, "url": { "raw": "{{baseUrl}}/payments/:id/capture", diff --git a/postman/collection-json/bluesnap.postman_collection.json b/postman/collection-json/bluesnap.postman_collection.json index d1188336c112..e796a01eaca7 100644 --- a/postman/collection-json/bluesnap.postman_collection.json +++ b/postman/collection-json/bluesnap.postman_collection.json @@ -1909,7 +1909,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2562,7 +2562,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3701,7 +3701,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/checkout.postman_collection.json b/postman/collection-json/checkout.postman_collection.json index cdb94b4a84b5..3f022e588539 100644 --- a/postman/collection-json/checkout.postman_collection.json +++ b/postman/collection-json/checkout.postman_collection.json @@ -997,7 +997,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2004,7 +2004,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2640,7 +2640,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3304,7 +3304,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/cybersource.postman_collection.json b/postman/collection-json/cybersource.postman_collection.json index 1616eb0c445f..9c8dbc652918 100644 --- a/postman/collection-json/cybersource.postman_collection.json +++ b/postman/collection-json/cybersource.postman_collection.json @@ -6936,7 +6936,7 @@ "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\":\"stripesavecard_{{random_number}}\",\"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://google.com\",\"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\":{\"count_tickets\":1,\"transaction_number\":\"5590045\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard\",\"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://google.com\",\"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\":{\"count_tickets\":1,\"transaction_number\":\"5590045\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -7712,7 +7712,7 @@ "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\":\"stripesavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\":{\"count_tickets\":1,\"transaction_number\":\"5590045\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\":{\"count_tickets\":1,\"transaction_number\":\"5590045\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -8106,7 +8106,7 @@ "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\":\"adyensavecard_{{random_number}}\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\":{\"count_tickets\":1,\"transaction_number\":\"5590045\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"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://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\":{\"count_tickets\":1,\"transaction_number\":\"5590045\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/nmi.postman_collection.json b/postman/collection-json/nmi.postman_collection.json index 519b1409544c..136c2d24737e 100644 --- a/postman/collection-json/nmi.postman_collection.json +++ b/postman/collection-json/nmi.postman_collection.json @@ -6603,7 +6603,7 @@ "language": "json" } }, - "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"stripesavecard_{{random_number}}\",\"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\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"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\"}}" + "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"stripesavecard\",\"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\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -7207,7 +7207,7 @@ "language": "json" } }, - "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"stripesavecard_{{random_number}}\",\"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\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" + "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"stripesavecard\",\"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\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"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\"}}" }, "url": { "raw": "{{baseUrl}}/payments",