From 0b1e2320e8c59befd58e606c51ae47c69baa8854 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 24 Jul 2024 18:28:45 +0530 Subject: [PATCH 01/59] insert required fields in merchant account, payment method table --- config/config.example.toml | 5 ++++- config/deployments/integration_test.toml | 3 +++ config/deployments/sandbox.toml | 3 +++ config/development.toml | 3 +++ config/docker_compose.toml | 3 +++ crates/diesel_models/src/merchant_account.rs | 3 +++ crates/diesel_models/src/payment_method.rs | 14 ++++++++++++++ crates/diesel_models/src/query/payment_method.rs | 10 ++++++++++ crates/diesel_models/src/schema.rs | 3 +++ .../src/merchant_account.rs | 4 ++++ crates/router/src/configs/secrets_transformers.rs | 2 ++ crates/router/src/configs/settings.rs | 7 +++++++ crates/router/src/core/admin.rs | 1 + crates/router/src/core/payment_methods/cards.rs | 3 +++ crates/router/src/core/pm_auth.rs | 1 + crates/router/src/db/payment_method.rs | 1 + .../down.sql | 2 ++ .../up.sql | 2 ++ .../down.sql | 3 +++ .../up.sql | 3 +++ 20 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql create mode 100644 migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql create mode 100644 migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql create mode 100644 migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql diff --git a/config/config.example.toml b/config/config.example.toml index 5c99bf1cfb2c..38ebe7331e4c 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -707,4 +707,7 @@ public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "p encryption_key = "" # Encryption key used for encrypting data in user_authentication_methods table [locker_based_open_banking_connectors] -connector_list = "" \ No newline at end of file +connector_list = "" + +[network_tokenization_supported_card_networks] +card_networks = "visa" # Supported card networks for network tokenization \ No newline at end of file diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 277fee3c6e51..c786a6e0bd53 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -356,3 +356,6 @@ sdk_eligible_payment_methods = "card" [locker_based_open_banking_connectors] connector_list = "" + +[network_tokenization_supported_card_networks] +card_networks = "visa" \ No newline at end of file diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 8c19767947c7..9f24ec7205f4 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -379,3 +379,6 @@ sdk_eligible_payment_methods = "card" [locker_based_open_banking_connectors] connector_list = "" + +[network_tokenization_supported_card_networks] +card_networks = "visa" \ No newline at end of file diff --git a/config/development.toml b/config/development.toml index 7a90ccec5030..6d5d6ddb26a9 100644 --- a/config/development.toml +++ b/config/development.toml @@ -706,3 +706,6 @@ encryption_key = "A8EF32E029BC3342E54BF2E172A4D7AA43E8EF9D2C3A624A9F04E2EF79DC69 [locker_based_open_banking_connectors] connector_list = "" + +[network_tokenization_supported_card_networks] +card_networks = "visa" \ No newline at end of file diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 7ce3bc646c55..d306936252b6 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -563,3 +563,6 @@ ach = { country = "US", currency = "USD" } [locker_based_open_banking_connectors] connector_list = "" + +[network_tokenization_supported_card_networks] +card_networks = "visa" \ No newline at end of file diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index a8a2e7ce19d2..bda06a1ce25b 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -54,6 +54,7 @@ pub struct MerchantAccount { pub recon_status: storage_enums::ReconStatus, pub payment_link_config: Option, pub pm_collect_link_config: Option, + pub is_network_tokenization_enabled: bool, } #[cfg(all(feature = "v2", feature = "merchant_account_v2"))] @@ -127,6 +128,7 @@ pub struct MerchantAccountNew { pub recon_status: storage_enums::ReconStatus, pub payment_link_config: Option, pub pm_collect_link_config: Option, + pub is_network_tokenization_enabled: bool, } #[derive(Clone, Debug, Default, AsChangeset, router_derive::DebugAsDisplay)] @@ -157,4 +159,5 @@ pub struct MerchantAccountUpdateInternal { pub recon_status: Option, pub payment_link_config: Option, pub pm_collect_link_config: Option, + pub is_network_tokenization_enabled: bool, } diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 6bb40e4b1666..90830c477307 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -45,6 +45,7 @@ pub struct PaymentMethod { pub client_secret: Option, pub payment_method_billing_address: Option, pub updated_by: Option, + pub network_token_reference_id: Option, } #[derive( @@ -82,6 +83,7 @@ pub struct PaymentMethodNew { pub client_secret: Option, pub payment_method_billing_address: Option, pub updated_by: Option, + pub network_token_reference_id: Option, } impl PaymentMethodNew { @@ -123,6 +125,7 @@ pub enum PaymentMethodUpdate { payment_method_data: Option, status: Option, locker_id: Option, + network_token_reference_id: Option, payment_method: Option, payment_method_type: Option, payment_method_issuer: Option, @@ -154,6 +157,7 @@ pub struct PaymentMethodUpdateInternal { network_transaction_id: Option, status: Option, locker_id: Option, + network_token_reference_id: Option, payment_method: Option, connector_mandate_details: Option, updated_by: Option, @@ -208,6 +212,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, + network_token_reference_id:None, payment_method: None, connector_mandate_details: None, updated_by: None, @@ -223,6 +228,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, + network_token_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, @@ -236,6 +242,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, + network_token_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, @@ -252,6 +259,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, + network_token_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, @@ -268,6 +276,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id, status, locker_id: None, + network_token_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, @@ -281,6 +290,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status, locker_id: None, + network_token_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, @@ -291,6 +301,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_data, status, locker_id, + network_token_reference_id, payment_method, payment_method_type, payment_method_issuer, @@ -301,6 +312,7 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status, locker_id, + network_token_reference_id, payment_method, connector_mandate_details: None, updated_by: None, @@ -315,6 +327,7 @@ impl From for PaymentMethodUpdateInternal { last_used_at: None, status: None, locker_id: None, + network_token_reference_id: None, payment_method: None, connector_mandate_details, network_transaction_id: None, @@ -334,6 +347,7 @@ impl From<&PaymentMethodNew> for PaymentMethod { merchant_id: payment_method_new.merchant_id.clone(), payment_method_id: payment_method_new.payment_method_id.clone(), locker_id: payment_method_new.locker_id.clone(), + network_token_reference_id: payment_method_new.network_token_reference_id.clone(), accepted_currency: payment_method_new.accepted_currency.clone(), scheme: payment_method_new.scheme.clone(), token: payment_method_new.token.clone(), diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs index 8008fec0206a..5d1bc6730a95 100644 --- a/crates/diesel_models/src/query/payment_method.rs +++ b/crates/diesel_models/src/query/payment_method.rs @@ -54,6 +54,16 @@ impl PaymentMethod { .await } + // pub async fn find_by_network_token_reference_id(conn: &PgPooledConn, network_token_reference_id: &str) -> StorageResult { + // generics::generic_find_one::<::Table, _, _>( + // conn, + // dsl::locker_id.eq(network_token_reference_id.to_owned()), + // ) + // .await + // } + + + pub async fn find_by_payment_method_id( conn: &PgPooledConn, payment_method_id: &str, diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index b8c5344cbbba..1c9429fc90d4 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -670,6 +670,7 @@ diesel::table! { recon_status -> ReconStatus, payment_link_config -> Nullable, pm_collect_link_config -> Nullable, + is_network_tokenization_enabled -> Bool, } } @@ -983,6 +984,8 @@ diesel::table! { payment_method_billing_address -> Nullable, #[max_length = 64] updated_by -> Nullable, + #[max_length = 64] + network_token_reference_id -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 07bf32381919..de333e66413e 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -50,6 +50,7 @@ pub struct MerchantAccount { pub recon_status: diesel_models::enums::ReconStatus, pub payment_link_config: Option, pub pm_collect_link_config: Option, + pub is_network_tokenization_enabled: bool, } #[cfg(all(feature = "v2", feature = "merchant_account_v2"))] @@ -362,6 +363,7 @@ impl super::behaviour::Conversion for MerchantAccount { recon_status: self.recon_status, payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } @@ -420,6 +422,7 @@ impl super::behaviour::Conversion for MerchantAccount { recon_status: item.recon_status, payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, + is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } .await @@ -457,6 +460,7 @@ impl super::behaviour::Conversion for MerchantAccount { recon_status: self.recon_status, payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } } diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index de53f9c189e3..9ed7561af152 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -401,6 +401,8 @@ pub(crate) async fn fetch_raw_secrets( mandates: conf.mandates, network_transaction_id_supported_connectors: conf .network_transaction_id_supported_connectors, + network_tokenization_supported_card_networks: conf + .network_tokenization_supported_card_networks, required_fields: conf.required_fields, delayed_session_response: conf.delayed_session_response, webhook_source_verification_call: conf.webhook_source_verification_call, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index adab685ea091..cea6b5cb8950 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -96,6 +96,7 @@ pub struct Settings { pub cors: CorsSettings, pub mandates: Mandates, pub network_transaction_id_supported_connectors: NetworkTransactionIdSupportedConnectors, + pub network_tokenization_supported_card_networks: NetworkTokenizationSupportedCardNetworks, pub required_fields: RequiredFields, pub delayed_session_response: DelayedSessionConfig, pub webhook_source_verification_call: WebhookSourceVerificationCall, @@ -401,6 +402,12 @@ pub struct NetworkTransactionIdSupportedConnectors { pub connector_list: HashSet, } +#[derive(Debug, Deserialize, Clone, Default)] +pub struct NetworkTokenizationSupportedCardNetworks { + #[serde(deserialize_with = "deserialize_hashset")] + pub connector_list: HashSet, +} + #[derive(Debug, Deserialize, Clone)] pub struct SupportedPaymentMethodsForMandate( pub HashMap, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 5f3a820447d9..c8b3a22ef371 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -322,6 +322,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { recon_status: diesel_models::enums::ReconStatus::NotRequested, payment_link_config: None, pm_collect_link_config, + is_network_tokenization_enabled: false, }, ) } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 5f5e59a89a51..f374b157a251 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -150,6 +150,7 @@ pub async fn create_payment_method( last_used_at: current_time, payment_method_billing_address, updated_by: None, + network_token_reference_id: None, }, storage_scheme, ) @@ -537,6 +538,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( last_used_at: current_time, payment_method_billing_address: payment_method_billing_address.map(Into::into), updated_by: None, + network_token_reference_id: None, }, merchant_account.storage_scheme, ) @@ -820,6 +822,7 @@ pub async fn add_payment_method_data( payment_method_data: Some(pm_data_encrypted.into()), status: Some(enums::PaymentMethodStatus::Active), locker_id: Some(locker_id), + network_token_reference_id: None, payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index df28d257b36a..55444852dea6 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -481,6 +481,7 @@ async fn store_bank_details_in_payment_methods( created_at: now, last_modified: now, locker_id: None, + network_token_reference_id: None, last_used_at: now, connector_mandate_details: None, customer_acceptance: None, diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index 9ec25b1d1b2e..371a3fc645da 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -670,6 +670,7 @@ impl PaymentMethodInterface for MockDb { merchant_id: payment_method_new.merchant_id, payment_method_id: payment_method_new.payment_method_id, locker_id: payment_method_new.locker_id, + network_token_reference_id: payment_method_new.network_token_reference_id, accepted_currency: payment_method_new.accepted_currency, scheme: payment_method_new.scheme, token: payment_method_new.token, diff --git a/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql new file mode 100644 index 000000000000..edd6883468c6 --- /dev/null +++ b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_reference_id; \ No newline at end of file diff --git a/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql new file mode 100644 index 000000000000..2eebb7af1d4a --- /dev/null +++ b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_reference_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file diff --git a/migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql b/migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql new file mode 100644 index 000000000000..3816696829f6 --- /dev/null +++ b/migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` + +ALTER TABLE merchant_account DROP COLUMN IF EXISTS is_network_tokenization_enabled; \ No newline at end of file diff --git a/migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql b/migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql new file mode 100644 index 000000000000..1d5e7e1a66f8 --- /dev/null +++ b/migrations/2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here + +ALTER TABLE merchant_account ADD COLUMN IF NOT EXISTS is_network_tokenization_enabled BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file From a64d5c10cc6bae3ec3c454c6f9f15d14f00fce95 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 29 Jul 2024 17:12:07 +0530 Subject: [PATCH 02/59] add support for network tokenization --- config/development.toml | 10 +- crates/api_models/src/admin.rs | 5 + crates/common_enums/src/enums.rs | 1 + crates/common_utils/src/request.rs | 14 +- crates/diesel_models/src/merchant_account.rs | 2 +- crates/diesel_models/src/payment_method.rs | 14 + crates/diesel_models/src/schema.rs | 2 + .../src/configs/secrets_transformers.rs | 36 +- crates/router/src/configs/settings.rs | 15 +- crates/router/src/core/admin.rs | 2 +- .../router/src/core/blocklist/transformers.rs | 2 +- crates/router/src/core/payment_methods.rs | 3 +- .../router/src/core/payment_methods/cards.rs | 19 +- .../payment_methods/network_tokenization.rs | 341 ++++++++++++++++++ .../src/core/payment_methods/transformers.rs | 2 +- crates/router/src/core/payments/helpers.rs | 44 ++- .../payments/operations/payment_confirm.rs | 2 +- .../router/src/core/payments/tokenization.rs | 159 +++++--- crates/router/src/core/payouts/helpers.rs | 2 + crates/router/src/core/pm_auth.rs | 1 + crates/router/src/db/payment_method.rs | 1 + crates/router/src/services/encryption.rs | 8 +- crates/router/src/types/api/admin.rs | 1 + crates/router/src/types/domain/user.rs | 1 + .../src/types/storage/payment_method.rs | 3 + .../down.sql | 3 + .../up.sql | 2 + 27 files changed, 618 insertions(+), 77 deletions(-) create mode 100644 crates/router/src/core/payment_methods/network_tokenization.rs create mode 100644 migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql create mode 100644 migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql diff --git a/config/development.toml b/config/development.toml index 6d5d6ddb26a9..ca9b925a552e 100644 --- a/config/development.toml +++ b/config/development.toml @@ -708,4 +708,12 @@ encryption_key = "A8EF32E029BC3342E54BF2E172A4D7AA43E8EF9D2C3A624A9F04E2EF79DC69 connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "visa" \ No newline at end of file +card_networks = "Visa" + +[network_tokenization_service] +generate_token_url= "" +fetch_token_url= "" +token_service_api_key= "" +public_key= "" +private_key= "" +key_id= "" diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 853f4308b172..e66c20dab17f 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -113,6 +113,9 @@ pub struct MerchantAccountCreate { /// Default payment method collect link config #[schema(value_type = Option)] pub pm_collect_link_config: Option, + + #[serde(default)] + pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -430,6 +433,8 @@ pub struct MerchantAccountResponse { /// Default payment method collect link config #[schema(value_type = Option)] pub pm_collect_link_config: Option, + + pub is_network_tokenization_enabled: bool, } #[cfg(all(feature = "v2", feature = "merchant_account_v2"))] diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 4cc57d180a8e..4ae7eb15061d 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1731,6 +1731,7 @@ pub enum MandateStatus { )] #[router_derive::diesel_enum(storage_type = "text")] pub enum CardNetwork { + #[serde(alias = "VISA")] Visa, Mastercard, AmericanExpress, diff --git a/crates/common_utils/src/request.rs b/crates/common_utils/src/request.rs index 264179fc6037..3fa4d1e94962 100644 --- a/crates/common_utils/src/request.rs +++ b/crates/common_utils/src/request.rs @@ -42,13 +42,13 @@ pub struct Request { impl std::fmt::Debug for RequestContent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(match self { - Self::Json(_) => "JsonRequestBody", - Self::FormUrlEncoded(_) => "FormUrlEncodedRequestBody", - Self::FormData(_) => "FormDataRequestBody", - Self::Xml(_) => "XmlRequestBody", - Self::RawBytes(_) => "RawBytesRequestBody", - }) + match self { + Self::Json(a) => a.masked_serialize().fmt(f), + Self::FormUrlEncoded(_) => f.write_str("FormUrlEncodedRequestBody"), + Self::FormData(_) => f.write_str("FormDataRequestBody"), + Self::Xml(_) => f.write_str("XmlRequestBody"), + Self::RawBytes(_) => f.write_str("RawBytesRequestBody"), + } } } diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index bda06a1ce25b..a035f10572af 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -159,5 +159,5 @@ pub struct MerchantAccountUpdateInternal { pub recon_status: Option, pub payment_link_config: Option, pub pm_collect_link_config: Option, - pub is_network_tokenization_enabled: bool, + pub is_network_tokenization_enabled: Option, } diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 90830c477307..6202ec64c305 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -46,6 +46,7 @@ pub struct PaymentMethod { pub payment_method_billing_address: Option, pub updated_by: Option, pub network_token_reference_id: Option, + pub token_locker_id: Option, } #[derive( @@ -84,6 +85,7 @@ pub struct PaymentMethodNew { pub payment_method_billing_address: Option, pub updated_by: Option, pub network_token_reference_id: Option, + pub token_locker_id: Option, } impl PaymentMethodNew { @@ -129,6 +131,7 @@ pub enum PaymentMethodUpdate { payment_method: Option, payment_method_type: Option, payment_method_issuer: Option, + token_locker_id: Option, }, ConnectorMandateDetailsUpdate { connector_mandate_details: Option, @@ -163,6 +166,7 @@ pub struct PaymentMethodUpdateInternal { updated_by: Option, payment_method_type: Option, payment_method_issuer: Option, + token_locker_id: Option, } impl PaymentMethodUpdateInternal { @@ -218,6 +222,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data, @@ -234,6 +239,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { metadata: None, @@ -248,6 +254,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { payment_method_data, @@ -265,6 +272,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { network_transaction_id, @@ -282,6 +290,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { metadata: None, @@ -296,6 +305,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, PaymentMethodUpdate::AdditionalDataUpdate { payment_method_data, @@ -305,6 +315,7 @@ impl From for PaymentMethodUpdateInternal { payment_method, payment_method_type, payment_method_issuer, + token_locker_id, } => Self { metadata: None, payment_method_data, @@ -318,6 +329,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer, payment_method_type, + token_locker_id, }, PaymentMethodUpdate::ConnectorMandateDetailsUpdate { connector_mandate_details, @@ -334,6 +346,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_issuer: None, payment_method_type: None, + token_locker_id: None, }, } } @@ -376,6 +389,7 @@ impl From<&PaymentMethodNew> for PaymentMethod { payment_method_billing_address: payment_method_new .payment_method_billing_address .clone(), + token_locker_id: payment_method_new.token_locker_id.clone(), } } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 1c9429fc90d4..1c93385dd3d1 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -986,6 +986,8 @@ diesel::table! { updated_by -> Nullable, #[max_length = 64] network_token_reference_id -> Nullable, + #[max_length = 64] + token_locker_id -> Nullable, } } diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 9ed7561af152..077b29d0a7e6 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -265,6 +265,32 @@ impl SecretsHandler for settings::UserAuthMethodSettings { } } +#[async_trait::async_trait] +impl SecretsHandler for settings::NetworkTokenizationService { + async fn convert_to_raw_secret( + value: SecretStateContainer, + secret_management_client: &dyn SecretManagementInterface, + ) -> CustomResult, SecretsManagementError> { + let network_tokenization = value.get_inner(); + let token_service_api_key = secret_management_client + .get_secret(network_tokenization.token_service_api_key.clone()) + .await?; + let public_key = secret_management_client + .get_secret(network_tokenization.public_key.clone()) + .await?; + let private_key = secret_management_client + .get_secret(network_tokenization.private_key.clone()) + .await?; + + Ok(value.transition_state(|network_tokenization| Self { + public_key, + private_key, + token_service_api_key, + ..network_tokenization + })) + } +} + /// # Panics /// /// Will panic even if kms decryption fails for at least one field @@ -363,6 +389,11 @@ pub(crate) async fn fetch_raw_secrets( .await .expect("Failed to decrypt user_auth_methods configs"); + let network_tokenization_service = settings::NetworkTokenizationService::convert_to_raw_secret( + conf.network_tokenization_service, + secret_management_client, + ).await.expect("Failed to decrypt network tokenization service configs"); + Settings { server: conf.server, master_database, @@ -401,8 +432,6 @@ pub(crate) async fn fetch_raw_secrets( mandates: conf.mandates, network_transaction_id_supported_connectors: conf .network_transaction_id_supported_connectors, - network_tokenization_supported_card_networks: conf - .network_tokenization_supported_card_networks, required_fields: conf.required_fields, delayed_session_response: conf.delayed_session_response, webhook_source_verification_call: conf.webhook_source_verification_call, @@ -437,5 +466,8 @@ pub(crate) async fn fetch_raw_secrets( user_auth_methods, decision: conf.decision, locker_based_open_banking_connectors: conf.locker_based_open_banking_connectors, + network_tokenization_supported_card_networks: conf + .network_tokenization_supported_card_networks, + network_tokenization_service, } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index cea6b5cb8950..15f5e215e986 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -96,7 +96,6 @@ pub struct Settings { pub cors: CorsSettings, pub mandates: Mandates, pub network_transaction_id_supported_connectors: NetworkTransactionIdSupportedConnectors, - pub network_tokenization_supported_card_networks: NetworkTokenizationSupportedCardNetworks, pub required_fields: RequiredFields, pub delayed_session_response: DelayedSessionConfig, pub webhook_source_verification_call: WebhookSourceVerificationCall, @@ -131,6 +130,8 @@ pub struct Settings { pub user_auth_methods: SecretStateContainer, pub decision: Option, pub locker_based_open_banking_connectors: LockerBasedRecipientConnectorList, + pub network_tokenization_supported_card_networks: NetworkTokenizationSupportedCardNetworks, + pub network_tokenization_service: SecretStateContainer, } #[derive(Debug, Deserialize, Clone, Default)] @@ -405,7 +406,17 @@ pub struct NetworkTransactionIdSupportedConnectors { #[derive(Debug, Deserialize, Clone, Default)] pub struct NetworkTokenizationSupportedCardNetworks { #[serde(deserialize_with = "deserialize_hashset")] - pub connector_list: HashSet, + pub card_networks: HashSet, +} + +#[derive(Debug, Deserialize, Clone, Default)] +pub struct NetworkTokenizationService { + pub generate_token_url: String, + pub fetch_token_url: String, + pub token_service_api_key: Secret, + pub public_key: Secret, + pub private_key: Secret, + pub key_id: String, } #[derive(Debug, Deserialize, Clone)] diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index c8b3a22ef371..6b0c27eaab0d 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -322,7 +322,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { recon_status: diesel_models::enums::ReconStatus::NotRequested, payment_link_config: None, pm_collect_link_config, - is_network_tokenization_enabled: false, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }, ) } diff --git a/crates/router/src/core/blocklist/transformers.rs b/crates/router/src/core/blocklist/transformers.rs index d17322f2be5d..e716831052a9 100644 --- a/crates/router/src/core/blocklist/transformers.rs +++ b/crates/router/src/core/blocklist/transformers.rs @@ -87,7 +87,7 @@ async fn generate_jwe_payload_for_request( } }; - let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key) + let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, "A256GCM", None) .await .change_context(errors::VaultError::SaveCardFailed) .attach_printable("Error on jwe encrypt")?; diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 86cb1fc1b1e7..5ee61d8cd0a3 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -5,6 +5,7 @@ pub mod surcharge_decision_configs; pub mod transformers; pub mod utils; pub mod vault; +pub mod network_tokenization; pub use api_models::enums::Connector; #[cfg(feature = "payouts")] pub use api_models::{enums::PayoutConnectors, payouts as payout_types}; @@ -510,7 +511,7 @@ pub async fn retrieve_payment_method_with_token( storage::PaymentTokenData::PermanentCard(card_token) => { helpers::retrieve_card_with_permanent_token( state, - card_token.locker_id.as_ref().unwrap_or(&card_token.token), + card_token.token_locker_id.as_ref().unwrap_or(card_token.locker_id.as_ref().unwrap_or(&card_token.token)), card_token .payment_method_id .as_ref() diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index f374b157a251..68e39c183988 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -97,6 +97,8 @@ pub async fn create_payment_method( storage_scheme: MerchantStorageScheme, payment_method_billing_address: Option, card_scheme: Option, + network_token_reference_id: Option, + token_locker_id: Option, ) -> errors::CustomResult { let db = &*state.store; let customer = db @@ -150,7 +152,8 @@ pub async fn create_payment_method( last_used_at: current_time, payment_method_billing_address, updated_by: None, - network_token_reference_id: None, + network_token_reference_id, + token_locker_id, }, storage_scheme, ) @@ -261,6 +264,8 @@ pub async fn get_or_insert_payment_method( req.network_transaction_id.clone(), merchant_account.storage_scheme, None, + None, // todo! + None, //todo! ) .await } else { @@ -539,6 +544,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( payment_method_billing_address: payment_method_billing_address.map(Into::into), updated_by: None, network_token_reference_id: None, + token_locker_id: None, }, merchant_account.storage_scheme, ) @@ -638,6 +644,8 @@ pub async fn get_client_secret_or_add_payment_method( merchant_account.storage_scheme, payment_method_billing_address.map(Into::into), None, + None, + None, ) .await?; @@ -826,6 +834,7 @@ pub async fn add_payment_method_data( payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, + token_locker_id: None, // todo! }; db.update_payment_method( @@ -1116,6 +1125,8 @@ pub async fn add_payment_method( req.network_transaction_id.clone(), merchant_account.storage_scheme, payment_method_billing_address.map(Into::into), + None, //todo! + None, //todo! ) .await?; @@ -1141,6 +1152,8 @@ pub async fn insert_payment_method( network_transaction_id: Option, storage_scheme: MerchantStorageScheme, payment_method_billing_address: Option, + network_token_reference_id: Option, + token_locker_id: Option, ) -> errors::RouterResult { let pm_card_details = resp .card @@ -1175,6 +1188,8 @@ pub async fn insert_payment_method( card.card_network .map(|card_network| card_network.to_string()) }), + network_token_reference_id, + token_locker_id, ) .await } @@ -1535,6 +1550,7 @@ pub async fn add_card_to_locker( errors::VaultError, > { metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]); + println!("add_card_to_lockerrr"); let add_card_to_hs_resp = Box::pin(common_utils::metrics::utils::record_operation_time( async { add_card_hs( @@ -3790,6 +3806,7 @@ pub async fn list_customer_payment_method( Some(pm.payment_method_id.clone()), pm.locker_id.clone().or(Some(pm.payment_method_id.clone())), pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()), + pm.network_token_reference_id.clone().or(Some(pm.payment_method_id.clone())), ), } } else { diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs new file mode 100644 index 000000000000..183ca0bb532c --- /dev/null +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -0,0 +1,341 @@ +use masking::{ExposeInterface, Mask, PeekInterface, Secret}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Debug, + str::FromStr, +}; + +use api_models::{ + admin::PaymentMethodsEnabled, + enums as api_enums, + payment_methods::{Card, PaymentMethodsData}, +}; +use common_enums::enums::MerchantStorageScheme; +use common_utils::{ + consts, + crypto::Encryptable, + encryption::Encryption, + errors::{self as common_utils_erros, CustomResult, ParsingError, ValidationError}, + ext_traits::{Encode, OptionExt}, + id_type, + request::RequestContent, + types::keymanager::Identifier, +}; +use error_stack::{report, ResultExt}; +use euclid::dssa::graph::{AnalysisContext, CgraphExt}; +use strum::IntoEnumIterator; + +use cards::CardNumber; +use josekit::jwe; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "payouts")] +use crate::{ + core::errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt}, + headers, logger, + routes::{self}, + services::{ + self, encryption, + request::{self}, + }, + types::{ + api::{self}, + domain::{self, types::decrypt_optional}, + storage::{self, enums as storage_enums}, + }, + utils::{ ConnectorResponseExt}, +}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CardData { + card_number: CardNumber, + exp_month: Secret, + exp_year: Secret, + card_security_code: Secret, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OrderData { + consent_id: String, + customer_id: id_type::CustomerId, + amount: String, + currency: String, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ApiPayload { + service: String, + card_data: String, + order_data: OrderData, + sub_merchant_id: String, + key_id: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct CardNetworkTokenResponse { + payload: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CardNetworkTokenResponsePayloadTemporary { + pub card_brand: api_enums::CardNetwork, + pub card_fingerprint: String, + pub card_reference: String, + pub correlation_id: String, + pub customer_id: String, + pub par: String, + pub token_expiry_month: Secret, + pub token_expiry_year: Secret, + pub token_isin: String, + pub token_last_four: String, + pub token_status: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CardNetworkTokenResponsePayload { + pub card_brand: api_enums::CardNetwork, + pub card_fingerprint: String, + pub card_reference: CardNumber, + pub correlation_id: String, + pub customer_id: String, + pub par: String, + pub token_expiry_month: Secret, + pub token_expiry_year: Secret, + pub token_isin: String, + pub token_last_four: String, + pub token_status: String, +} + +pub async fn make_card_network_tokenization_request( + state: &routes::SessionState, + payment_method_data: Option<&domain::PaymentMethodData>, + merchant_account: &domain::MerchantAccount, + customer_id: &Option, + amount: Option, + currency: Option, +) -> errors::CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> +{ + let customer_id = customer_id + .clone() + .get_required_value("customer_id") + .change_context(errors::ApiErrorResponse::InternalServerError)?; + let card_data = match payment_method_data { + Some(pm_data) => match pm_data { + domain::PaymentMethodData::Card(card) => CardData { + card_number: card.card_number.clone(), + exp_month: card.card_exp_month.clone(), + exp_year: card.card_exp_year.clone(), + card_security_code: card.card_cvc.clone(), + }, + _ => todo!(), + }, + _ => todo!(), + }; + + let payload = card_data + .encode_to_string_of_json() + .and_then(|x| x.encode_to_string_of_json()) + .change_context(errors::VaultError::FetchCardFailed) + .map_err(|e| { + logger::error!(fetch_err=?e); + errors::ApiErrorResponse::InternalServerError + })?; + println!("payloaddd: {:?}", payload); + let payload_bytes = payload.as_bytes(); + println!("payload_bytesss: {:?}", payload_bytes); + let tokenization_service = &state.conf.network_tokenization_service.get_inner(); + + let enc_key = tokenization_service.public_key.peek().clone(); + + let key_id = tokenization_service.key_id.clone(); + let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + println!("jwtt: {:?}", jwt); + let amount_str = amount.map_or_else(String::new, |a| a.to_string()); + let currency_str = currency.map_or_else(String::new, |c| c.to_string()); + let order_data = OrderData { + consent_id: "test12324".to_string(), // ?? + customer_id: customer_id.clone(), + amount:amount_str, + currency:currency_str, + }; + + let api_payload = ApiPayload { + service: "NETWORK_TOKEN".to_string(), + card_data: jwt, + order_data, + sub_merchant_id: "visa_sbx_working".to_string(), + key_id, + }; + + let mut request = services::Request::new( + services::Method::Post, + tokenization_service.generate_token_url.as_str(), + ); + request.add_header(headers::CONTENT_TYPE, "application/json".into()); + request.add_header( + headers::AUTHORIZATION, + tokenization_service.token_service_api_key.peek().clone().into_masked(), + ); + request.set_body(RequestContent::Json(Box::new(api_payload))); + logger::debug!("Requestt to euler: {:?}", request); + + let response = services::call_connector_api(state, request, "generate_token") + .await + .change_context(errors::VaultError::SaveCardFailed); + + logger::debug!("Responsee from euler: {:?}", response); + + let res: CardNetworkTokenResponse = response + .get_response_inner("cardNetworkTokenResponse") + .change_context(errors::VaultError::FetchCardFailed) + .map_err(|e| { + logger::error!(fetch_err=?e); + errors::ApiErrorResponse::InternalServerError + })?; + let dec_key = tokenization_service.private_key.peek().clone(); + + let card_network_token_response = services::decrypt_jwe( + &res.payload, + services::KeyIdCheck::SkipKeyIdCheck, + dec_key, + jwe::RSA_OAEP_256, + ) + .await + .unwrap(); + println!( + "card_network_token_response: {:?}", + card_network_token_response + ); + + let cn_response_temp: CardNetworkTokenResponsePayloadTemporary = + serde_json::from_str(&card_network_token_response) + .change_context(errors::VaultError::ResponseDeserializationFailed) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let cn_response = CardNetworkTokenResponsePayload { + card_brand: cn_response_temp.card_brand, + card_fingerprint: cn_response_temp.card_fingerprint, + card_reference: CardNumber::from_str("xxxxxxxxxxxxx").unwrap(), + correlation_id: cn_response_temp.correlation_id, + customer_id: cn_response_temp.customer_id, + par: cn_response_temp.par, + token_expiry_month: cn_response_temp.token_expiry_month, + token_expiry_year: cn_response_temp.token_expiry_year, + token_isin: cn_response_temp.token_isin, + token_last_four: cn_response_temp.token_last_four, + token_status: cn_response_temp.token_status, + }; + Ok((cn_response, Some(cn_response_temp.card_reference))) +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GetCardToken { + card_reference: String, + customer_id: id_type::CustomerId, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct AuthenticationDetails { + cryptogram: String, + token: Secret, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct TokenResponse { + authentication_details: AuthenticationDetails, +} + +pub async fn get_token_from_tokenization_service( + state: &routes::SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + pm_id: String, //to fetch from pm table +) -> errors::RouterResult { + let db = state.store.as_ref(); + let key = key_store.key.get_inner().peek(); + let pm_data = db + .find_payment_method(&pm_id, merchant_account.storage_scheme) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + let token_ref = pm_data.clone().network_token_reference_id; + + let tokenization_service = &state.conf.network_tokenization_service.get_inner(); + let mut request = services::Request::new( + services::Method::Post, + tokenization_service.clone().fetch_token_url.as_str(), + ); + let payload = GetCardToken { + card_reference: token_ref.unwrap(), + customer_id: pm_data.clone().customer_id, + }; + + request.add_header(headers::CONTENT_TYPE, "application/json".into()); + request.add_header( + headers::AUTHORIZATION, + tokenization_service.token_service_api_key.clone().peek().clone().into_masked(), + ); + request.set_body(RequestContent::Json(Box::new(payload))); + logger::debug!("reqq to euler: {:?}", request); + + // Send the request using `call_connector_api` + let response = services::call_connector_api(state, request, "get network token") + .await + .change_context(errors::VaultError::SaveCardFailed); + logger::debug!("Response from euler: {:?}", response); + let res: TokenResponse = response + .get_response_inner("cardNetworkTokenResponse") + .change_context(errors::VaultError::FetchCardFailed) + .map_err(|e| { + logger::error!(fetch_err=?e); + errors::ApiErrorResponse::InternalServerError + })?; + println!("ressss: {:?}", res); + // let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let card_decrypted = decrypt_optional::( + &state.into(), + pm_data.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); + println!("card_decrypted: {:?}", card_decrypted); + let card_data = Card { + card_number: card_decrypted + .clone() + .unwrap() + .card_number + .unwrap_or_default(), + name_on_card: card_decrypted.clone().unwrap().card_holder_name, + card_exp_month: card_decrypted + .clone() + .unwrap() + .expiry_month + .unwrap_or_default(), + card_exp_year: card_decrypted + .clone() + .unwrap() + .expiry_year + .unwrap_or_default(), + card_brand: Some("Visa".to_string()), + card_isin: card_decrypted.clone().unwrap().card_isin, + nick_name: None, + }; + Ok(card_data) +} diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 6bfadabdb35b..c36d0a12e1bc 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -264,7 +264,7 @@ pub async fn mk_basilisk_req( } }; - let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key) + let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, "A256GCM", None) .await .change_context(errors::VaultError::SaveCardFailed) .attach_printable("Error on jwe encrypt")?; diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index aa630ad98351..d351186b3aa5 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -52,6 +52,7 @@ use crate::{ payment_methods::{ self, cards::{self, create_encrypted_data}, + network_tokenization, vault, }, payments, @@ -1815,10 +1816,10 @@ pub async fn retrieve_payment_method_with_temporary_token( pub async fn retrieve_card_with_permanent_token( state: &SessionState, locker_id: &str, - _payment_method_id: &str, + payment_method_id: &str, payment_intent: &PaymentIntent, card_token_data: Option<&CardToken>, - _merchant_key_store: &domain::MerchantKeyStore, + merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult { let customer_id = payment_intent @@ -1828,11 +1829,27 @@ pub async fn retrieve_card_with_permanent_token( .change_context(errors::ApiErrorResponse::UnprocessableEntity { message: "no customer id provided for the payment".to_string(), })?; - let card = + // let card = + // cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) + // .await + // .change_context(errors::ApiErrorResponse::InternalServerError) + // .attach_printable("failed to fetch card information from the permanent locker")?; + + let db = state.store.as_ref(); + let key_manager_state = &state.into(); + let merchant_account = db.find_merchant_account_by_merchant_id(key_manager_state, &payment_intent.merchant_id, merchant_key_store).await.change_context(errors::ApiErrorResponse::InternalServerError)?; + let card = if merchant_account.is_network_tokenization_enabled{ + network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)? + } + else{ cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch card information from the permanent locker")?; + .attach_printable("failed to fetch card information from the permanent locker")? + + }; + // let key = merchant_key_store.key.get_inner().peek(); + // let card = network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)?; // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked // from payment_method_data.card_token object @@ -1842,11 +1859,12 @@ pub async fn retrieve_card_with_permanent_token( .and_then(|token_data| token_data.card_holder_name.clone()) .or(Some(name)) } else { - card.name_on_card + card.clone().name_on_card } } else { card_token_data.and_then(|token_data| token_data.card_holder_name.clone()) }; + println!("carddd: {:?}", card.clone()); let api_card = api::Card { card_number: card.card_number, @@ -1860,15 +1878,7 @@ pub async fn retrieve_card_with_permanent_token( .unwrap_or_default(), card_issuer: None, nick_name: card.nick_name.map(masking::Secret::new), - card_network: card - .card_brand - .map(|card_brand| enums::CardNetwork::from_str(&card_brand)) - .transpose() - .map_err(|e| { - logger::error!("Failed to parse card network {e:?}"); - }) - .ok() - .flatten(), + card_network: None, card_type: None, card_issuing_country: None, bank_code: None, @@ -2006,6 +2016,10 @@ pub async fn make_pm_data<'a, F: Clone, R>( .locker_id .clone() .unwrap_or(payment_method_info.payment_method_id.clone()), + token_locker_id:payment_method_info + .network_token_reference_id + .clone() + .or(Some(payment_method_info.payment_method_id.clone())), })); } } @@ -2025,6 +2039,8 @@ pub async fn make_pm_data<'a, F: Clone, R>( ) .await; + + let payment_method_details = pm_data.attach_printable("in 'make_pm_data'")?; Ok::<_, error_stack::Report>( diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index ad3f8fb013e8..635e381ddf80 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -930,7 +930,7 @@ impl Domain for PaymentConfirm { }; let encrypted_payload = - services::encrypt_jwe(&card_data, merchant_config.public_key.peek()) + services::encrypt_jwe(&card_data, merchant_config.public_key.peek(), "A256GCM", None) .await .map_err(|err| { logger::error!(jwe_encryption_err=?err,"Error while JWE encrypting extended card info") diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 40163367139c..185cacc647ed 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -17,7 +17,7 @@ use crate::{ core::{ errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt}, mandate, - payment_methods::{self, cards::create_encrypted_data}, + payment_methods::{self, cards::create_encrypted_data, network_tokenization}, payments, }, logger, @@ -178,6 +178,7 @@ where .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to serialize customer acceptance to value")?; + let is_network_token_enabled = merchant_account.is_network_tokenization_enabled; let pm_id = if customer_acceptance.is_some() { let payment_method_create_request = helpers::get_payment_method_create_request( @@ -190,23 +191,42 @@ where .await?; let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = &merchant_account.merchant_id; - let (mut resp, duplication_check) = if !state.conf.locker.locker_enabled { - skip_saving_card_in_locker( - merchant_account, - payment_method_create_request.to_owned(), - ) - .await? - } else { - pm_status = Some(common_enums::PaymentMethodStatus::from( - save_payment_method_data.attempt_status, - )); - Box::pin(save_in_locker( - state, - merchant_account, - payment_method_create_request.to_owned(), - )) - .await? - }; + let ((mut resp, duplication_check, network_token_ref_id), token_locker_id) = + if !state.conf.locker.locker_enabled { + let (res, dc) = skip_saving_card_in_locker( + merchant_account, + payment_method_create_request.to_owned(), + ) + .await?; + ((res, dc, None), None) + } else { + pm_status = Some(common_enums::PaymentMethodStatus::from( + save_payment_method_data.attempt_status, + )); + let (res, dc, ref_id) = Box::pin(save_in_locker( + state, + merchant_account, + Some(&save_payment_method_data.request.get_payment_method_data()), + payment_method_create_request.to_owned(), + false, + amount.clone(), + currency.clone(), + )) + .await?; + + let (res2, dc2, ref_id2) = Box::pin(save_in_locker( + state, + merchant_account, + Some(&save_payment_method_data.request.get_payment_method_data()), + payment_method_create_request.to_owned(), + true, + amount, + currency, + )) + .await?; + + ((res, dc, ref_id2), Some(res2.payment_method_id)) + }; let pm_card_details = resp.card.as_ref().map(|card| { PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) @@ -330,6 +350,8 @@ where card.card_network .map(|card_network| card_network.to_string()) }), + network_token_ref_id, //todo! + token_locker_id, ) .await } else { @@ -421,6 +443,8 @@ where merchant_account.storage_scheme, encrypted_payment_method_billing_address .map(Into::into), + network_token_ref_id, //todo! + token_locker_id, ) .await } else { @@ -618,6 +642,8 @@ where card.card_network .map(|card_network| card_network.to_string()) }), + network_token_ref_id, //todo! + token_locker_id, //todo! ) .await?; }; @@ -725,10 +751,15 @@ async fn skip_saving_card_in_locker( pub async fn save_in_locker( state: &SessionState, merchant_account: &domain::MerchantAccount, + payment_method_data: Option<&domain::PaymentMethodData>, payment_method_request: api::PaymentMethodCreate, + save_token: bool, + amount: Option, + currency: Option, ) -> RouterResult<( api_models::payment_methods::PaymentMethodResponse, Option, + Option, )> { payment_method_request.validate()?; let merchant_id = &merchant_account.merchant_id; @@ -736,38 +767,82 @@ pub async fn save_in_locker( .customer_id .clone() .get_required_value("customer_id")?; - match payment_method_request.card.clone() { - Some(card) => Box::pin(payment_methods::cards::add_card_to_locker( + println!("before saveingggg"); + if save_token { + let (token_response, token_ref_id) = + network_tokenization::make_card_network_tokenization_request( + state, + payment_method_data, + merchant_account, + &payment_method_request.customer_id, + amount, + currency, + ) + .await?; + let card_data = api::CardDetail { + card_number: token_response.card_reference.clone(), + card_exp_month: token_response.token_expiry_month.clone(), + card_exp_year: token_response.token_expiry_year.clone(), + card_holder_name: None, + nick_name: None, + card_issuing_country: None, + card_network: Some(token_response.card_brand.clone()), + card_issuer: None, + card_type: None, + }; + println!("savinggg token"); + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( state, payment_method_request, - &card, + &card_data, &customer_id, merchant_account, None, )) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Card Failed"), - None => { - let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); - let payment_method_response = api::PaymentMethodResponse { - merchant_id: merchant_id.to_string(), - customer_id: Some(customer_id), - payment_method_id: pm_id, - payment_method: payment_method_request.payment_method, - payment_method_type: payment_method_request.payment_method_type, - #[cfg(feature = "payouts")] - bank_transfer: None, - card: None, - metadata: None, - created: Some(common_utils::date_time::now()), - recurring_enabled: false, //[#219] - installment_payment_enabled: false, //[#219] - payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), //[#219] - last_used_at: Some(common_utils::date_time::now()), - client_secret: None, - }; - Ok((payment_method_response, None)) + .attach_printable("Add Card Failed")?; + Ok((res, dc, token_ref_id)) + } else { + println!("savinggg card"); + match payment_method_request.card.clone() { + Some(card) => { + let (res, dc) = 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")?; + Ok((res, dc, None)) + } + None => { + let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); + let payment_method_response = api::PaymentMethodResponse { + merchant_id: merchant_id.to_string(), + customer_id: Some(customer_id), + payment_method_id: pm_id, + payment_method: payment_method_request.payment_method, + payment_method_type: payment_method_request.payment_method_type, + #[cfg(feature = "payouts")] + bank_transfer: None, + card: None, + metadata: None, + created: Some(common_utils::date_time::now()), + recurring_enabled: false, //[#219] + installment_payment_enabled: false, //[#219] + payment_experience: Some(vec![ + api_models::enums::PaymentExperience::RedirectToUrl, + ]), //[#219] + last_used_at: Some(common_utils::date_time::now()), + client_secret: None, + }; + Ok((payment_method_response, None, None)) + } } } } diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 6cda7b284334..ccbf5eb5c59b 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -517,6 +517,8 @@ pub async fn save_payout_data_to_locker( merchant_account.storage_scheme, None, None, + None, + None, ) .await?; } diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 55444852dea6..1d0e6d8d9a52 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -489,6 +489,7 @@ async fn store_bank_details_in_payment_methods( client_secret: None, payment_method_billing_address: None, updated_by: None, + token_locker_id: None, }; new_entries.push(pm_new); diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index 371a3fc645da..6e86ccaf90ee 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -697,6 +697,7 @@ impl PaymentMethodInterface for MockDb { network_transaction_id: payment_method_new.network_transaction_id, updated_by: payment_method_new.updated_by, payment_method_billing_address: payment_method_new.payment_method_billing_address, + token_locker_id: payment_method_new.token_locker_id, }; payment_methods.push(payment_method.clone()); Ok(payment_method) diff --git a/crates/router/src/services/encryption.rs b/crates/router/src/services/encryption.rs index dc7e5db079a6..b5700b67aedd 100644 --- a/crates/router/src/services/encryption.rs +++ b/crates/router/src/services/encryption.rs @@ -29,12 +29,16 @@ pub struct JweBody { pub async fn encrypt_jwe( payload: &[u8], public_key: impl AsRef<[u8]>, + enc: &str, + key_id: Option<&str>, ) -> CustomResult { let alg = jwe::RSA_OAEP_256; - let enc = "A256GCM"; let mut src_header = jwe::JweHeader::new(); src_header.set_content_encryption(enc); src_header.set_token_type("JWT"); + if let Some(kid) = key_id { + src_header.set_key_id(kid); + } let encrypter = alg .encrypter_from_pem(public_key) .change_context(errors::EncryptionError) @@ -208,7 +212,7 @@ VuY3OeNxi+dC2r7HppP3O/MJ4gX/RJJfSrcaGP8/Ke1W5+jE97Qy #[actix_rt::test] async fn test_jwe() { - let jwt = encrypt_jwe("request_payload".as_bytes(), ENCRYPTION_KEY) + let jwt = encrypt_jwe("request_payload".as_bytes(), ENCRYPTION_KEY, "A256GCM", None) .await .unwrap(); let alg = jwe::RSA_OAEP_256; diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index f3eecc8c76a2..d9747465104c 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -64,6 +64,7 @@ impl ForeignTryFrom for MerchantAccountResponse { default_profile: item.default_profile, recon_status: item.recon_status, pm_collect_link_config, + is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } } diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index d66f09f442ab..c25606ec9b02 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -436,6 +436,7 @@ impl NewUserMerchant { enable_payment_response_hash: None, redirect_to_merchant_with_http_post: None, pm_collect_link_config: None, + is_network_tokenization_enabled: false, }) } diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 308aa3625626..19c7628ef689 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -24,6 +24,7 @@ pub struct CardTokenData { pub payment_method_id: Option, pub locker_id: Option, pub token: String, + pub token_locker_id: Option, } #[derive(Debug, Clone, serde::Serialize, Default, serde::Deserialize)] @@ -61,11 +62,13 @@ impl PaymentTokenData { payment_method_id: Option, locker_id: Option, token: String, + token_locker_id: Option, ) -> Self { Self::PermanentCard(CardTokenData { payment_method_id, locker_id, token, + token_locker_id, }) } diff --git a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql new file mode 100644 index 000000000000..27382dac75d1 --- /dev/null +++ b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql @@ -0,0 +1,3 @@ +-- This file should undo anything in `up.sql` + +ALTER TABLE payment_methods DROP COLUMN IF EXISTS token_locker_id; \ No newline at end of file diff --git a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql new file mode 100644 index 000000000000..90789cc85508 --- /dev/null +++ b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS token_locker_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file From 038f54107b903ae5234b660ffd9f0b75cadcef99 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 31 Jul 2024 16:11:18 +0530 Subject: [PATCH 03/59] move from api_models to domain_models --- .../src/connectors/bambora/transformers.rs | 3 +- .../src/connectors/fiserv/transformers.rs | 3 +- .../src/connectors/helcim/transformers.rs | 6 +- .../src/connectors/stax/transformers.rs | 12 ++- .../src/payment_method_data.rs | 102 +++++++++++++++++- .../router/src/connector/aci/transformers.rs | 3 +- .../src/connector/adyen/transformers.rs | 6 +- .../src/connector/airwallex/transformers.rs | 3 +- .../connector/authorizedotnet/transformers.rs | 9 +- .../connector/bankofamerica/transformers.rs | 6 +- .../src/connector/billwerk/transformers.rs | 3 +- .../src/connector/bluesnap/transformers.rs | 6 +- .../router/src/connector/boku/transformers.rs | 3 +- .../braintree_graphql_transformers.rs | 9 +- .../src/connector/braintree/transformers.rs | 3 +- .../src/connector/checkout/transformers.rs | 6 +- .../src/connector/cryptopay/transformers.rs | 3 +- .../src/connector/cybersource/transformers.rs | 15 ++- .../src/connector/datatrans/transformers.rs | 3 +- .../src/connector/dlocal/transformers.rs | 3 +- .../src/connector/forte/transformers.rs | 3 +- .../src/connector/globepay/transformers.rs | 3 +- .../src/connector/gocardless/transformers.rs | 6 +- .../src/connector/iatapay/transformers.rs | 3 +- crates/router/src/connector/klarna.rs | 3 +- .../src/connector/mifinity/transformers.rs | 3 +- .../connector/multisafepay/transformers.rs | 6 +- .../src/connector/nexinets/transformers.rs | 3 +- .../router/src/connector/nmi/transformers.rs | 3 +- .../router/src/connector/noon/transformers.rs | 3 +- .../src/connector/nuvei/transformers.rs | 4 +- .../src/connector/opayo/transformers.rs | 3 +- .../src/connector/payeezy/transformers.rs | 3 +- .../src/connector/payme/transformers.rs | 10 +- .../src/connector/paypal/transformers.rs | 3 +- .../src/connector/placetopay/transformers.rs | 3 +- .../src/connector/powertranz/transformers.rs | 3 +- .../src/connector/razorpay/transformers.rs | 3 +- .../src/connector/shift4/transformers.rs | 4 +- .../src/connector/square/transformers.rs | 6 +- .../src/connector/stripe/transformers.rs | 10 +- .../src/connector/trustpay/transformers.rs | 3 +- .../router/src/connector/tsys/transformers.rs | 3 +- crates/router/src/connector/utils.rs | 2 + .../router/src/connector/volt/transformers.rs | 3 +- .../src/connector/worldline/transformers.rs | 3 +- .../src/connector/worldpay/transformers.rs | 3 +- .../router/src/connector/zen/transformers.rs | 3 +- .../router/src/connector/zsl/transformers.rs | 1 + crates/router/src/core/payment_methods.rs | 32 +++--- .../router/src/core/payment_methods/vault.rs | 66 ++++++------ crates/router/src/core/payments.rs | 20 ++-- crates/router/src/core/payments/helpers.rs | 32 +++--- crates/router/src/core/payments/operations.rs | 4 +- .../payments/operations/payment_confirm.rs | 6 +- .../payments/operations/payment_create.rs | 2 +- .../payments/operations/payment_update.rs | 2 +- crates/router/src/core/pm_auth.rs | 8 +- crates/router/src/types/domain/payments.rs | 4 +- .../src/types/storage/payment_method.rs | 10 +- 60 files changed, 341 insertions(+), 161 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs b/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs index bfa2da851f84..57bdac8b7f8b 100644 --- a/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs @@ -196,7 +196,8 @@ impl TryFrom> for Bambora | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("bambora"), ) .into()), diff --git a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs index 0ae59d4526e7..0cfcbfe56e16 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs @@ -198,7 +198,8 @@ impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservP | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => { + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("fiserv"), )) diff --git a/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs b/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs index 3aed97a2cbca..5558cd507794 100644 --- a/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs @@ -180,7 +180,8 @@ impl TryFrom<&SetupMandateRouterData> for HelcimVerifyRequest { | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( crate::utils::get_unimplemented_payment_method_error_message("Helcim"), ))?, } @@ -273,7 +274,8 @@ impl TryFrom<&HelcimRouterData<&PaymentsAuthorizeRouterData>> for HelcimPayments | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( crate::utils::get_unimplemented_payment_method_error_message("Helcim"), ))?, } diff --git a/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs b/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs index 8c767e848da5..9e7dd968fd0f 100644 --- a/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs @@ -117,7 +117,8 @@ impl TryFrom<&StaxRouterData<&types::PaymentsAuthorizeRouterData>> for StaxPayme | PaymentMethodData::CardRedirect(_) | PaymentMethodData::Upi(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Stax"), ))?, } @@ -267,9 +268,12 @@ impl TryFrom<&types::TokenizationRouterData> for StaxTokenRequest { | PaymentMethodData::CardRedirect(_) | PaymentMethodData::Upi(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Stax"), - ))?, + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Stax"), + ))? + } } } } diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index a440ba944f17..01a4fbb4aa50 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -1,4 +1,4 @@ -use common_utils::pii::{self, Email}; +use common_utils::{pii::{self, Email}, id_type}; use masking::Secret; use serde::{Deserialize, Serialize}; use time::Date; @@ -23,6 +23,7 @@ pub enum PaymentMethodData { GiftCard(Box), CardToken(CardToken), OpenBanking(OpenBankingData), + NetworkToken(NetworkTokenData), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -34,7 +35,7 @@ pub enum ApplePayFlow { impl PaymentMethodData { pub fn get_payment_method(&self) -> Option { match self { - Self::Card(_) => Some(common_enums::PaymentMethod::Card), + Self::Card(_) | Self::NetworkToken(_) => Some(common_enums::PaymentMethod::Card), Self::CardRedirect(_) => Some(common_enums::PaymentMethod::CardRedirect), Self::Wallet(_) => Some(common_enums::PaymentMethod::Wallet), Self::PayLater(_) => Some(common_enums::PaymentMethod::PayLater), @@ -468,6 +469,20 @@ pub struct SepaAndBacsBillingDetails { pub name: Secret, } +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)] +pub struct NetworkTokenData { + pub token_number: cards::CardNumber, + pub token_exp_month: Secret, + pub token_exp_year: Secret, + pub token_cryptogram: Secret, + pub card_issuer: Option, + pub card_network: Option, + pub card_type: Option, + pub card_issuing_country: Option, + pub bank_code: Option, + pub nick_name: Option>, +} + impl From for PaymentMethodData { fn from(api_model_payment_method_data: api_models::payments::PaymentMethodData) -> Self { match api_model_payment_method_data { @@ -550,6 +565,37 @@ impl From for Card { } } +// impl From for NetworkTokenData { +// fn from(value: api_models::payments::Card) -> Self { +// let api_models::payments::Card { +// card_number, +// card_exp_month, +// card_exp_year, +// card_holder_name: _, +// card_cvc, +// card_issuer, +// card_network, +// card_type, +// card_issuing_country, +// bank_code, +// nick_name, +// } = value; + +// Self { +// token_number, +// token_exp_month, +// token_exp_year, +// token_cryptogram, +// card_issuer, +// card_network, +// card_type, +// card_issuing_country, +// bank_code, +// nick_name, +// } +// } +// } + impl From for CardRedirectData { fn from(value: api_models::payments::CardRedirectData) -> Self { match value { @@ -952,3 +998,55 @@ impl From for OpenBankingData { } } } + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TokenizedCardValue1 { + pub card_number: String, + pub exp_year: String, + pub exp_month: String, + pub nickname: Option, + pub card_last_four: Option, + pub card_token: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TokenizedCardValue2 { + pub card_security_code: Option, + pub card_fingerprint: Option, + pub external_id: Option, + pub customer_id: Option, + pub payment_method_id: Option, +} + + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct TokenizedWalletValue1 { + pub data: WalletData, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct TokenizedWalletValue2 { + pub customer_id: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct TokenizedBankTransferValue1 { + pub data: BankTransferData, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct TokenizedBankTransferValue2 { + pub customer_id: Option, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct TokenizedBankRedirectValue1 { + pub data: BankRedirectData, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +pub struct TokenizedBankRedirectValue2 { + pub customer_id: Option, +} \ No newline at end of file diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 175138add758..fb5f76f1b11c 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -448,7 +448,8 @@ impl TryFrom<&AciRouterData<&types::PaymentsAuthorizeRouterData>> for AciPayment | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Aci"), ))? diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index b1f14e8305be..3304496be496 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1556,7 +1556,8 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyen"), ))? @@ -2558,7 +2559,8 @@ impl<'a> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotSupported { message: "Network tokenization for payment method".to_string(), connector: "Adyen", diff --git a/crates/router/src/connector/airwallex/transformers.rs b/crates/router/src/connector/airwallex/transformers.rs index 9fab84559b54..a83fb6fbd9d8 100644 --- a/crates/router/src/connector/airwallex/transformers.rs +++ b/crates/router/src/connector/airwallex/transformers.rs @@ -205,7 +205,8 @@ impl TryFrom<&AirwallexRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("airwallex"), )) diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 33570a782fd8..57f724920f79 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -343,7 +343,8 @@ impl TryFrom<&types::SetupMandateRouterData> for CreateCustomerProfileRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("authorizedotnet"), ))? @@ -522,7 +523,8 @@ impl TryFrom<&AuthorizedotnetRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( "authorizedotnet", @@ -581,7 +583,8 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("authorizedotnet"), ))? diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index 34244bbb819b..23c018d41e7c 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -322,7 +322,8 @@ impl TryFrom<&types::SetupMandateRouterData> for BankOfAmericaPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("BankOfAmerica"), ))? @@ -1073,7 +1074,8 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( "Bank of America", diff --git a/crates/router/src/connector/billwerk/transformers.rs b/crates/router/src/connector/billwerk/transformers.rs index e91184c878cb..93289aa767cf 100644 --- a/crates/router/src/connector/billwerk/transformers.rs +++ b/crates/router/src/connector/billwerk/transformers.rs @@ -103,7 +103,8 @@ impl TryFrom<&types::TokenizationRouterData> for BillwerkTokenRequest { | domain::payments::PaymentMethodData::Voucher(_) | domain::payments::PaymentMethodData::GiftCard(_) | domain::payments::PaymentMethodData::OpenBanking(_) - | domain::payments::PaymentMethodData::CardToken(_) => { + | domain::payments::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("billwerk"), ) diff --git a/crates/router/src/connector/bluesnap/transformers.rs b/crates/router/src/connector/bluesnap/transformers.rs index dc67c1961bef..431cab84b272 100644 --- a/crates/router/src/connector/bluesnap/transformers.rs +++ b/crates/router/src/connector/bluesnap/transformers.rs @@ -228,7 +228,8 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( "Selected payment method via Token flow through bluesnap".to_string(), ) @@ -393,7 +394,8 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> for Blues | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("bluesnap"), )) diff --git a/crates/router/src/connector/boku/transformers.rs b/crates/router/src/connector/boku/transformers.rs index e6c6e3b82396..c9538ea2f54c 100644 --- a/crates/router/src/connector/boku/transformers.rs +++ b/crates/router/src/connector/boku/transformers.rs @@ -93,7 +93,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BokuPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("boku"), ))? diff --git a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs index af50bd10ce77..ed2b0e84a440 100644 --- a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs +++ b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs @@ -210,7 +210,8 @@ impl TryFrom<&BraintreeRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), ) @@ -997,7 +998,8 @@ impl TryFrom<&types::TokenizationRouterData> for BraintreeTokenRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), ) @@ -1588,7 +1590,8 @@ fn get_braintree_redirect_form( | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => Err( + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => Err( errors::ConnectorError::NotImplemented("given payment method".to_owned()), )?, }, diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index 717c4afc1956..de690d9e90bd 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -176,7 +176,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), )) diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index be48d2a864f6..4771a29ad5a2 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -133,7 +133,8 @@ impl TryFrom<&types::TokenizationRouterData> for TokenRequest { | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("checkout"), ) @@ -370,7 +371,8 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("checkout"), )) diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs index c15afd7fd444..ac9c9d4752ea 100644 --- a/crates/router/src/connector/cryptopay/transformers.rs +++ b/crates/router/src/connector/cryptopay/transformers.rs @@ -79,7 +79,8 @@ impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("CryptoPay"), )) diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 86960b43ca40..cf973649f349 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -215,7 +215,8 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), ))? @@ -1400,7 +1401,8 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), ) @@ -1507,7 +1509,8 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), ) @@ -2226,7 +2229,8 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsPreProcessingRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), )) @@ -2336,7 +2340,8 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), ) diff --git a/crates/router/src/connector/datatrans/transformers.rs b/crates/router/src/connector/datatrans/transformers.rs index 96c7bcd8b2c0..6848df883194 100644 --- a/crates/router/src/connector/datatrans/transformers.rs +++ b/crates/router/src/connector/datatrans/transformers.rs @@ -188,7 +188,8 @@ impl TryFrom<&DatatransRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message("Datatrans"), ))? diff --git a/crates/router/src/connector/dlocal/transformers.rs b/crates/router/src/connector/dlocal/transformers.rs index ca18d12a0883..e07b45366d63 100644 --- a/crates/router/src/connector/dlocal/transformers.rs +++ b/crates/router/src/connector/dlocal/transformers.rs @@ -167,7 +167,8 @@ impl TryFrom<&DlocalRouterData<&types::PaymentsAuthorizeRouterData>> for DlocalP | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( crate::connector::utils::get_unimplemented_payment_method_error_message( "Dlocal", diff --git a/crates/router/src/connector/forte/transformers.rs b/crates/router/src/connector/forte/transformers.rs index 0533d71ffeb3..6f14541db88a 100644 --- a/crates/router/src/connector/forte/transformers.rs +++ b/crates/router/src/connector/forte/transformers.rs @@ -116,7 +116,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for FortePaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Forte"), ))? diff --git a/crates/router/src/connector/globepay/transformers.rs b/crates/router/src/connector/globepay/transformers.rs index 8974c9f94b97..589f5b721b29 100644 --- a/crates/router/src/connector/globepay/transformers.rs +++ b/crates/router/src/connector/globepay/transformers.rs @@ -74,7 +74,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("globepay"), ))? diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index fb27f5138a9c..843352867d0d 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -247,7 +247,8 @@ impl TryFrom<&types::TokenizationRouterData> for CustomerBankAccount { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Gocardless"), ) @@ -417,7 +418,8 @@ impl TryFrom<&types::SetupMandateRouterData> for GocardlessMandateRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( "Setup Mandate flow for selected payment method through Gocardless".to_string(), )) diff --git a/crates/router/src/connector/iatapay/transformers.rs b/crates/router/src/connector/iatapay/transformers.rs index 1ce07fb49e32..8221ff23de16 100644 --- a/crates/router/src/connector/iatapay/transformers.rs +++ b/crates/router/src/connector/iatapay/transformers.rs @@ -205,7 +205,8 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::OpenBanking(_) => { + | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("iatapay"), ))? diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs index fbfb8b71974f..41048edfb63f 100644 --- a/crates/router/src/connector/klarna.rs +++ b/crates/router/src/connector/klarna.rs @@ -652,7 +652,8 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(report!(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message( req.connector.as_str(), diff --git a/crates/router/src/connector/mifinity/transformers.rs b/crates/router/src/connector/mifinity/transformers.rs index 20885d08955e..4781ecba115c 100644 --- a/crates/router/src/connector/mifinity/transformers.rs +++ b/crates/router/src/connector/mifinity/transformers.rs @@ -197,7 +197,8 @@ impl TryFrom<&MifinityRouterData<&types::PaymentsAuthorizeRouterData>> for Mifin | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Mifinity"), ) diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index ac0a04de182d..7c74f00560b8 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -607,7 +607,8 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("multisafepay"), ))? @@ -788,7 +789,8 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::OpenBanking(_) => { + | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("multisafepay"), ))? diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index 7e32ae5ebedb..9692d39725be 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -626,7 +626,8 @@ fn get_payment_details_and_product( | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nexinets"), ))?, } diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs index e0858e16a4a6..d639c99c8684 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -585,7 +585,8 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nmi"), ) diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index 8336cbf13188..299acc523a3e 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -353,7 +353,8 @@ impl TryFrom<&NoonRouterData<&types::PaymentsAuthorizeRouterData>> for NoonPayme | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( conn_utils::get_unimplemented_payment_method_error_message("Noon"), )) diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs index 1aa345173570..0d9ef8f3366d 100644 --- a/crates/router/src/connector/nuvei/transformers.rs +++ b/crates/router/src/connector/nuvei/transformers.rs @@ -996,7 +996,8 @@ where | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nuvei"), ) @@ -1199,6 +1200,7 @@ impl TryFrom<(&types::PaymentsCompleteAuthorizeRouterData, Secret)> | Some(domain::PaymentMethodData::Upi(..)) | Some(domain::PaymentMethodData::OpenBanking(_)) | Some(domain::PaymentMethodData::CardToken(..)) + | Some(domain::PaymentMethodData::NetworkToken(..)) | None => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nuvei"), )), diff --git a/crates/router/src/connector/opayo/transformers.rs b/crates/router/src/connector/opayo/transformers.rs index 5771b45100bd..7021bbd89207 100644 --- a/crates/router/src/connector/opayo/transformers.rs +++ b/crates/router/src/connector/opayo/transformers.rs @@ -57,7 +57,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for OpayoPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Opayo"), ) diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index 8da3e7502bed..5e8c941693b0 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -261,7 +261,8 @@ fn get_payment_method_data( | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Payeezy"), ))?, } diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index 2ec35b7a7de0..f6591a532221 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -430,7 +430,8 @@ impl TryFrom<&PaymentMethodData> for SalePaymentMethod { | PaymentMethodData::Upi(_) | PaymentMethodData::Voucher(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => { + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } } @@ -675,7 +676,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest { | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("payme"), ))?, } @@ -736,6 +738,7 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for Pay3dsRequest { | Some(PaymentMethodData::GiftCard(_)) | Some(PaymentMethodData::OpenBanking(_)) | Some(PaymentMethodData::CardToken(_)) + | Some(domain::PaymentMethodData::NetworkToken(_)) | None => { Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) } @@ -775,7 +778,8 @@ impl TryFrom<&types::TokenizationRouterData> for CaptureBuyerRequest { | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) => { + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) =>{ Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) } } diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs index fb70581cd972..88d957d37e6c 100644 --- a/crates/router/src/connector/paypal/transformers.rs +++ b/crates/router/src/connector/paypal/transformers.rs @@ -552,7 +552,8 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP | domain::PaymentMethodData::Crypto(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Paypal"), ) diff --git a/crates/router/src/connector/placetopay/transformers.rs b/crates/router/src/connector/placetopay/transformers.rs index 50c897653dac..47ffdb2f5546 100644 --- a/crates/router/src/connector/placetopay/transformers.rs +++ b/crates/router/src/connector/placetopay/transformers.rs @@ -142,7 +142,8 @@ impl TryFrom<&PlacetopayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Placetopay"), ) diff --git a/crates/router/src/connector/powertranz/transformers.rs b/crates/router/src/connector/powertranz/transformers.rs index fedaabb5e750..de0657ee011b 100644 --- a/crates/router/src/connector/powertranz/transformers.rs +++ b/crates/router/src/connector/powertranz/transformers.rs @@ -117,7 +117,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PowertranzPaymentsRequest | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotSupported { message: utils::SELECTED_PAYMENT_METHOD.to_string(), connector: "powertranz", diff --git a/crates/router/src/connector/razorpay/transformers.rs b/crates/router/src/connector/razorpay/transformers.rs index 8cf328918868..845e5ef28832 100644 --- a/crates/router/src/connector/razorpay/transformers.rs +++ b/crates/router/src/connector/razorpay/transformers.rs @@ -399,7 +399,8 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } }?; diff --git a/crates/router/src/connector/shift4/transformers.rs b/crates/router/src/connector/shift4/transformers.rs index f384d8d296d5..b34645eafb70 100644 --- a/crates/router/src/connector/shift4/transformers.rs +++ b/crates/router/src/connector/shift4/transformers.rs @@ -248,7 +248,8 @@ where | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Shift4"), ) @@ -472,6 +473,7 @@ impl TryFrom<&types::RouterData Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Shift4"), ) diff --git a/crates/router/src/connector/square/transformers.rs b/crates/router/src/connector/square/transformers.rs index 67be82bb2d91..d07164b93f1d 100644 --- a/crates/router/src/connector/square/transformers.rs +++ b/crates/router/src/connector/square/transformers.rs @@ -175,7 +175,8 @@ impl TryFrom<&types::TokenizationRouterData> for SquareTokenRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Square"), ))? @@ -292,7 +293,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for SquarePaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Square"), ))? diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index 90ddc9f0b903..ba6da9f55575 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1334,7 +1334,8 @@ fn create_stripe_payment_method( | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::MandatePayment | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), ) .into()), @@ -1711,7 +1712,8 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent | domain::payments::PaymentMethodData::Voucher(_) | domain::payments::PaymentMethodData::GiftCard(_) | domain::payments::PaymentMethodData::OpenBanking(_) - | domain::payments::PaymentMethodData::CardToken(_) => { + | domain::payments::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotSupported { message: "Network tokenization for payment method".to_string(), connector: "Stripe", @@ -3299,6 +3301,7 @@ impl | Some(domain::PaymentMethodData::Voucher(..)) | Some(domain::PaymentMethodData::OpenBanking(..)) | Some(domain::PaymentMethodData::CardToken(..)) + | Some(domain::PaymentMethodData::NetworkToken(..)) | None => Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), ) @@ -3752,7 +3755,8 @@ impl | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), ))? diff --git a/crates/router/src/connector/trustpay/transformers.rs b/crates/router/src/connector/trustpay/transformers.rs index 85539e253b7b..47a629cda955 100644 --- a/crates/router/src/connector/trustpay/transformers.rs +++ b/crates/router/src/connector/trustpay/transformers.rs @@ -434,7 +434,8 @@ impl TryFrom<&TrustpayRouterData<&types::PaymentsAuthorizeRouterData>> for Trust | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("trustpay"), ) diff --git a/crates/router/src/connector/tsys/transformers.rs b/crates/router/src/connector/tsys/transformers.rs index 53561d93a926..1cd0188f0c5f 100644 --- a/crates/router/src/connector/tsys/transformers.rs +++ b/crates/router/src/connector/tsys/transformers.rs @@ -77,7 +77,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for TsysPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("tsys"), ))? diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index d61d78d37ff4..d5c01503ed83 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -2677,12 +2677,14 @@ pub enum PaymentMethodDataType { PromptPay, VietQr, OpenBanking, + NetworkToken, } impl From for PaymentMethodDataType { fn from(pm_data: domain::payments::PaymentMethodData) -> Self { match pm_data { domain::payments::PaymentMethodData::Card(_) => Self::Card, + domain::payments::PaymentMethodData::NetworkToken(_) => Self::NetworkToken, domain::payments::PaymentMethodData::CardRedirect(card_redirect_data) => { match card_redirect_data { domain::CardRedirectData::Knet {} => Self::Knet, diff --git a/crates/router/src/connector/volt/transformers.rs b/crates/router/src/connector/volt/transformers.rs index fcc95cd0d4b4..91f4f3304d33 100644 --- a/crates/router/src/connector/volt/transformers.rs +++ b/crates/router/src/connector/volt/transformers.rs @@ -141,7 +141,8 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Volt"), ) diff --git a/crates/router/src/connector/worldline/transformers.rs b/crates/router/src/connector/worldline/transformers.rs index 11d9da7785a1..a76459af23cf 100644 --- a/crates/router/src/connector/worldline/transformers.rs +++ b/crates/router/src/connector/worldline/transformers.rs @@ -250,7 +250,8 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("worldline"), ))? diff --git a/crates/router/src/connector/worldpay/transformers.rs b/crates/router/src/connector/worldpay/transformers.rs index cab949789b97..32af7feb6198 100644 --- a/crates/router/src/connector/worldpay/transformers.rs +++ b/crates/router/src/connector/worldpay/transformers.rs @@ -109,7 +109,8 @@ fn fetch_payment_instrument( | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("worldpay"), ) .into()), diff --git a/crates/router/src/connector/zen/transformers.rs b/crates/router/src/connector/zen/transformers.rs index 2562277ee9b4..7d7104e4bd80 100644 --- a/crates/router/src/connector/zen/transformers.rs +++ b/crates/router/src/connector/zen/transformers.rs @@ -695,7 +695,8 @@ impl TryFrom<&ZenRouterData<&types::PaymentsAuthorizeRouterData>> for ZenPayment | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Zen"), ))? diff --git a/crates/router/src/connector/zsl/transformers.rs b/crates/router/src/connector/zsl/transformers.rs index 19a79ab48a35..a4c461f241ff 100644 --- a/crates/router/src/connector/zsl/transformers.rs +++ b/crates/router/src/connector/zsl/transformers.rs @@ -182,6 +182,7 @@ impl TryFrom<&ZslRouterData<&types::PaymentsAuthorizeRouterData>> for ZslPayment | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) | domain::PaymentMethodData::OpenBanking(_) => { Err(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message( diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 988d6beeb8b2..4474b4925e5c 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -46,15 +46,15 @@ const PAYMENT_METHOD_STATUS_TAG: &str = "PAYMENT_METHOD_STATUS"; #[instrument(skip_all)] pub async fn retrieve_payment_method( - pm_data: &Option, + pm_data: &Option, state: &SessionState, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, merchant_key_store: &domain::MerchantKeyStore, business_profile: Option<&diesel_models::business_profile::BusinessProfile>, -) -> RouterResult<(Option, Option)> { +) -> RouterResult<(Option, Option)> { match pm_data { - pm_opt @ Some(pm @ api::PaymentMethodData::Card(_)) => { + pm_opt @ Some(pm @ domain::PaymentMethodData::Card(_)) => { let payment_token = helpers::store_payment_method_data_in_vault( state, payment_attempt, @@ -68,17 +68,17 @@ pub async fn retrieve_payment_method( Ok((pm_opt.to_owned(), payment_token)) } - pm @ Some(api::PaymentMethodData::PayLater(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::Crypto(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::BankDebit(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::Upi(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::Voucher(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::Reward) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::RealTimePayment(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::CardRedirect(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::GiftCard(_)) => Ok((pm.to_owned(), None)), - pm @ Some(api::PaymentMethodData::OpenBanking(_)) => Ok((pm.to_owned(), None)), - pm_opt @ Some(pm @ api::PaymentMethodData::BankTransfer(_)) => { + pm @ Some(domain::PaymentMethodData::PayLater(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::Crypto(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::BankDebit(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::Upi(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::Voucher(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::Reward) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::RealTimePayment(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::CardRedirect(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::GiftCard(_)) => Ok((pm.to_owned(), None)), + pm @ Some(domain::PaymentMethodData::OpenBanking(_)) => Ok((pm.to_owned(), None)), + pm_opt @ Some(pm @ domain::PaymentMethodData::BankTransfer(_)) => { let payment_token = helpers::store_payment_method_data_in_vault( state, payment_attempt, @@ -92,7 +92,7 @@ pub async fn retrieve_payment_method( Ok((pm_opt.to_owned(), payment_token)) } - pm_opt @ Some(pm @ api::PaymentMethodData::Wallet(_)) => { + pm_opt @ Some(pm @ domain::PaymentMethodData::Wallet(_)) => { let payment_token = helpers::store_payment_method_data_in_vault( state, payment_attempt, @@ -106,7 +106,7 @@ pub async fn retrieve_payment_method( Ok((pm_opt.to_owned(), payment_token)) } - pm_opt @ Some(pm @ api::PaymentMethodData::BankRedirect(_)) => { + pm_opt @ Some(pm @ domain::PaymentMethodData::BankRedirect(_)) => { let payment_token = helpers::store_payment_method_data_in_vault( state, payment_attempt, diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index 4e17ec6abd3a..3932a5a23e4f 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -47,20 +47,19 @@ pub trait Vaultable: Sized { ) -> CustomResult<(Self, SupplementaryVaultData), errors::VaultError>; } -impl Vaultable for api::Card { +impl Vaultable for domain::Card { fn get_value1( &self, _customer_id: Option, ) -> CustomResult { - let value1 = api::TokenizedCardValue1 { + let value1 = domain::TokenizedCardValue1 { card_number: self.card_number.peek().clone(), exp_year: self.card_exp_year.peek().clone(), exp_month: self.card_exp_month.peek().clone(), - name_on_card: self - .card_holder_name - .as_ref() - .map(|name| name.peek().clone()), - nickname: None, + nickname: self + .nick_name + .as_ref() + .map(|name| name.peek().clone()), card_last_four: None, card_token: None, }; @@ -75,7 +74,7 @@ impl Vaultable for api::Card { &self, customer_id: Option, ) -> CustomResult { - let value2 = api::TokenizedCardValue2 { + let value2 = domain::TokenizedCardValue2 { card_security_code: Some(self.card_cvc.peek().clone()), card_fingerprint: None, external_id: None, @@ -93,12 +92,12 @@ impl Vaultable for api::Card { value1: String, value2: String, ) -> CustomResult<(Self, SupplementaryVaultData), errors::VaultError> { - let value1: api::TokenizedCardValue1 = value1 + let value1: domain::TokenizedCardValue1 = value1 .parse_struct("TokenizedCardValue1") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into card value1")?; - let value2: api::TokenizedCardValue2 = value2 + let value2: domain::TokenizedCardValue2 = value2 .parse_struct("TokenizedCardValue2") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into card value2")?; @@ -109,7 +108,6 @@ impl Vaultable for api::Card { .attach_printable("Invalid card number format from the mock locker")?, card_exp_month: value1.exp_month.into(), card_exp_year: value1.exp_year.into(), - card_holder_name: value1.name_on_card.map(masking::Secret::new), card_cvc: value2.card_security_code.unwrap_or_default().into(), card_issuer: None, card_network: None, @@ -128,12 +126,12 @@ impl Vaultable for api::Card { } } -impl Vaultable for api_models::payments::BankTransferData { +impl Vaultable for domain::BankTransferData { fn get_value1( &self, _customer_id: Option, ) -> CustomResult { - let value1 = api_models::payment_methods::TokenizedBankTransferValue1 { + let value1 = domain::TokenizedBankTransferValue1 { data: self.to_owned(), }; @@ -147,7 +145,7 @@ impl Vaultable for api_models::payments::BankTransferData { &self, customer_id: Option, ) -> CustomResult { - let value2 = api_models::payment_methods::TokenizedBankTransferValue2 { customer_id }; + let value2 = domain::TokenizedBankTransferValue2 { customer_id }; value2 .encode_to_string_of_json() @@ -159,12 +157,12 @@ impl Vaultable for api_models::payments::BankTransferData { value1: String, value2: String, ) -> CustomResult<(Self, SupplementaryVaultData), errors::VaultError> { - let value1: api_models::payment_methods::TokenizedBankTransferValue1 = value1 + let value1: domain::TokenizedBankTransferValue1 = value1 .parse_struct("TokenizedBankTransferValue1") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into bank transfer data")?; - let value2: api_models::payment_methods::TokenizedBankTransferValue2 = value2 + let value2: domain::TokenizedBankTransferValue2 = value2 .parse_struct("TokenizedBankTransferValue2") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into supplementary bank transfer data")?; @@ -180,12 +178,12 @@ impl Vaultable for api_models::payments::BankTransferData { } } -impl Vaultable for api::WalletData { +impl Vaultable for domain::WalletData { fn get_value1( &self, _customer_id: Option, ) -> CustomResult { - let value1 = api::TokenizedWalletValue1 { + let value1 = domain::TokenizedWalletValue1 { data: self.to_owned(), }; @@ -199,7 +197,7 @@ impl Vaultable for api::WalletData { &self, customer_id: Option, ) -> CustomResult { - let value2 = api::TokenizedWalletValue2 { customer_id }; + let value2 = domain::TokenizedWalletValue2 { customer_id }; value2 .encode_to_string_of_json() @@ -211,12 +209,12 @@ impl Vaultable for api::WalletData { value1: String, value2: String, ) -> CustomResult<(Self, SupplementaryVaultData), errors::VaultError> { - let value1: api::TokenizedWalletValue1 = value1 + let value1: domain::TokenizedWalletValue1 = value1 .parse_struct("TokenizedWalletValue1") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into wallet data value1")?; - let value2: api::TokenizedWalletValue2 = value2 + let value2: domain::TokenizedWalletValue2 = value2 .parse_struct("TokenizedWalletValue2") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into wallet data value2")?; @@ -232,12 +230,12 @@ impl Vaultable for api::WalletData { } } -impl Vaultable for api_models::payments::BankRedirectData { +impl Vaultable for domain::BankRedirectData { fn get_value1( &self, _customer_id: Option, ) -> CustomResult { - let value1 = api_models::payment_methods::TokenizedBankRedirectValue1 { + let value1 = domain::TokenizedBankRedirectValue1 { data: self.to_owned(), }; @@ -251,7 +249,7 @@ impl Vaultable for api_models::payments::BankRedirectData { &self, customer_id: Option, ) -> CustomResult { - let value2 = api_models::payment_methods::TokenizedBankRedirectValue2 { customer_id }; + let value2 = domain::TokenizedBankRedirectValue2 { customer_id }; value2 .encode_to_string_of_json() @@ -263,12 +261,12 @@ impl Vaultable for api_models::payments::BankRedirectData { value1: String, value2: String, ) -> CustomResult<(Self, SupplementaryVaultData), errors::VaultError> { - let value1: api_models::payment_methods::TokenizedBankRedirectValue1 = value1 + let value1: domain::TokenizedBankRedirectValue1 = value1 .parse_struct("TokenizedBankRedirectValue1") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into bank redirect data")?; - let value2: api_models::payment_methods::TokenizedBankRedirectValue2 = value2 + let value2: domain::TokenizedBankRedirectValue2 = value2 .parse_struct("TokenizedBankRedirectValue2") .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Could not deserialize into supplementary bank redirect data")?; @@ -293,7 +291,7 @@ pub enum VaultPaymentMethod { BankRedirect(String), } -impl Vaultable for api::PaymentMethodData { +impl Vaultable for domain::PaymentMethodData { fn get_value1( &self, customer_id: Option, @@ -356,11 +354,11 @@ impl Vaultable for api::PaymentMethodData { match (value1, value2) { (VaultPaymentMethod::Card(mvalue1), VaultPaymentMethod::Card(mvalue2)) => { - let (card, supp_data) = api::Card::from_values(mvalue1, mvalue2)?; + let (card, supp_data) = domain::Card::from_values(mvalue1, mvalue2)?; Ok((Self::Card(card), supp_data)) } (VaultPaymentMethod::Wallet(mvalue1), VaultPaymentMethod::Wallet(mvalue2)) => { - let (wallet, supp_data) = api::WalletData::from_values(mvalue1, mvalue2)?; + let (wallet, supp_data) = domain::WalletData::from_values(mvalue1, mvalue2)?; Ok((Self::Wallet(wallet), supp_data)) } ( @@ -368,7 +366,7 @@ impl Vaultable for api::PaymentMethodData { VaultPaymentMethod::BankTransfer(mvalue2), ) => { let (bank_transfer, supp_data) = - api_models::payments::BankTransferData::from_values(mvalue1, mvalue2)?; + domain::BankTransferData::from_values(mvalue1, mvalue2)?; Ok((Self::BankTransfer(Box::new(bank_transfer)), supp_data)) } ( @@ -376,7 +374,7 @@ impl Vaultable for api::PaymentMethodData { VaultPaymentMethod::BankRedirect(mvalue2), ) => { let (bank_redirect, supp_data) = - api_models::payments::BankRedirectData::from_values(mvalue1, mvalue2)?; + domain::BankRedirectData::from_values(mvalue1, mvalue2)?; Ok((Self::BankRedirect(bank_redirect), supp_data)) } @@ -821,11 +819,11 @@ impl Vault { state: &routes::SessionState, lookup_key: &str, merchant_key_store: &domain::MerchantKeyStore, - ) -> RouterResult<(Option, SupplementaryVaultData)> { + ) -> RouterResult<(Option, SupplementaryVaultData)> { let de_tokenize = get_tokenized_data(state, lookup_key, true, merchant_key_store.key.get_inner()).await?; let (payment_method, customer_id) = - api::PaymentMethodData::from_values(de_tokenize.value1, de_tokenize.value2) + domain::PaymentMethodData::from_values(de_tokenize.value1, de_tokenize.value2) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Error parsing Payment Method from Values")?; @@ -836,7 +834,7 @@ impl Vault { pub async fn store_payment_method_data_in_locker( state: &routes::SessionState, token_id: Option, - payment_method: &api::PaymentMethodData, + payment_method: &domain::PaymentMethodData, customer_id: Option, pm: enums::PaymentMethod, merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index f1dce992d171..ec60bf34757a 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2047,9 +2047,9 @@ where } //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() { - api_models::payments::BankTransferData::AchBankTransfer { .. } - | api_models::payments::BankTransferData::MultibancoBankTransfer { .. } + Some(domain::PaymentMethodData::BankTransfer(data)) => match data.deref() { + domain::BankTransferData::AchBankTransfer { .. } + | domain::BankTransferData::MultibancoBankTransfer { .. } if connector.connector_name == router_types::Connector::Stripe => { if payment_data.payment_attempt.preprocessing_step_id.is_none() { @@ -2063,7 +2063,7 @@ where } _ => (router_data, should_continue_payment), }, - Some(api_models::payments::PaymentMethodData::Wallet(_)) => { + Some(domain::PaymentMethodData::Wallet(_)) => { if is_preprocessing_required_for_wallets(connector.connector_name.to_string()) { ( router_data.preprocessing_steps(state, connector).await?, @@ -2073,7 +2073,7 @@ where (router_data, should_continue_payment) } } - Some(api_models::payments::PaymentMethodData::Card(_)) => { + Some(domain::PaymentMethodData::Card(_)) => { if connector.connector_name == router_types::Connector::Payme && !matches!(format!("{operation:?}").as_str(), "CompleteAuthorize") { @@ -2122,7 +2122,7 @@ where (router_data, should_continue_payment) } } - Some(api_models::payments::PaymentMethodData::GiftCard(_)) => { + Some(domain::PaymentMethodData::GiftCard(_)) => { if connector.connector_name == router_types::Connector::Adyen { router_data = router_data.preprocessing_steps(state, connector).await?; @@ -2133,7 +2133,7 @@ where (router_data, should_continue_payment) } } - Some(api_models::payments::PaymentMethodData::BankDebit(_)) => { + Some(domain::PaymentMethodData::BankDebit(_)) => { if connector.connector_name == router_types::Connector::Gocardless { router_data = router_data.preprocessing_steps(state, connector).await?; let is_error_in_response = router_data.response.is_err(); @@ -2196,8 +2196,8 @@ where .await?; match payment_data.payment_method_data.clone() { - Some(api_models::payments::PaymentMethodData::OpenBanking( - api_models::payments::OpenBankingData::OpenBankingPIS { .. }, + Some(domain::PaymentMethodData::OpenBanking( + domain::OpenBankingData::OpenBankingPIS { .. }, )) => { if connector.connector_name == router_types::Connector::Plaid { router_data = router_data.postprocessing_steps(state, connector).await?; @@ -2688,7 +2688,7 @@ where pub token_data: Option, pub confirm: Option, pub force_sync: Option, - pub payment_method_data: Option, + pub payment_method_data: Option, pub payment_method_info: Option, pub refunds: Vec, pub disputes: Vec, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 6c598e67a348..572f86fe05e9 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1734,7 +1734,7 @@ pub async fn retrieve_payment_method_with_temporary_token( payment_intent: &PaymentIntent, merchant_key_store: &domain::MerchantKeyStore, card_token_data: Option<&CardToken>, -) -> RouterResult> { +) -> RouterResult> { let (pm, supplementary_data) = vault::Vault::get_payment_method_data_from_locker(state, token, merchant_key_store) .await @@ -1752,7 +1752,7 @@ pub async fn retrieve_payment_method_with_temporary_token( )?; Ok::<_, error_stack::Report>(match pm { - Some(api::PaymentMethodData::Card(card)) => { + Some(domain::PaymentMethodData::Card(card)) => { let mut updated_card = card.clone(); let mut is_card_updated = false; @@ -1800,21 +1800,21 @@ pub async fn retrieve_payment_method_with_temporary_token( Some((updated_pm, enums::PaymentMethod::Card)) } else { Some(( - api::PaymentMethodData::Card(card), + domain::Card(card), enums::PaymentMethod::Card, )) } } - Some(the_pm @ api::PaymentMethodData::Wallet(_)) => { + Some(the_pm @ domain::Wallet(_)) => { Some((the_pm, enums::PaymentMethod::Wallet)) } - Some(the_pm @ api::PaymentMethodData::BankTransfer(_)) => { + Some(the_pm @ domain::BankTransfer(_)) => { Some((the_pm, enums::PaymentMethod::BankTransfer)) } - Some(the_pm @ api::PaymentMethodData::BankRedirect(_)) => { + Some(the_pm @ domain::BankRedirect(_)) => { Some((the_pm, enums::PaymentMethod::BankRedirect)) } @@ -1831,9 +1831,9 @@ pub async fn retrieve_card_with_permanent_token( _payment_method_id: &str, payment_intent: &PaymentIntent, card_token_data: Option<&CardToken>, - _merchant_key_store: &domain::MerchantKeyStore, + merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, -) -> RouterResult { +) -> RouterResult { let customer_id = payment_intent .customer_id .as_ref() @@ -1887,7 +1887,7 @@ pub async fn retrieve_card_with_permanent_token( bank_code: None, }; - Ok(api::PaymentMethodData::Card(api_card)) + Ok(domain::Card(api_card).into()) } pub async fn retrieve_payment_method_from_db_with_token_data( @@ -1985,16 +1985,16 @@ pub async fn make_pm_data<'a, F: Clone, R>( business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, R>, - Option, + Option, Option, )> { - let request = &payment_data.payment_method_data.clone(); + let request = payment_data.payment_method_data.clone(); let mut card_token_data = payment_data .payment_method_data .clone() .and_then(|pmd| match pmd { - api_models::payments::PaymentMethodData::CardToken(token_data) => Some(token_data), + domain::PaymentMethodData::CardToken(token_data) => Some(token_data), _ => None, }) .or(Some(CardToken::default())); @@ -2025,7 +2025,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( } // TODO: Handle case where payment method and token both are present in request properly. - let (payment_method, pm_id) = match (request, payment_data.token_data.as_ref()) { + let (payment_method, pm_id) = match (request.clone(), payment_data.token_data.as_ref()) { (_, Some(hyperswitch_token)) => { let pm_data = payment_methods::retrieve_payment_method_with_token( state, @@ -2056,7 +2056,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( (Some(_), _) => { let (payment_method_data, payment_token) = payment_methods::retrieve_payment_method( - request, + &request.map(Into::into), state, &payment_data.payment_intent, &payment_data.payment_attempt, @@ -2077,7 +2077,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( pub async fn store_in_vault_and_generate_ppmt( state: &SessionState, - payment_method_data: &api_models::payments::PaymentMethodData, + payment_method_data: &domain::PaymentMethodData, payment_intent: &PaymentIntent, payment_attempt: &PaymentAttempt, payment_method: enums::PaymentMethod, @@ -2122,7 +2122,7 @@ pub async fn store_payment_method_data_in_vault( payment_attempt: &PaymentAttempt, payment_intent: &PaymentIntent, payment_method: enums::PaymentMethod, - payment_method_data: &api::PaymentMethodData, + payment_method_data: &domain::PaymentMethodData, merchant_key_store: &domain::MerchantKeyStore, business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult> { diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 336755c6e3da..1a467576b80f 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -134,7 +134,7 @@ pub trait Domain: Send + Sync { business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, R>, - Option, + Option, Option, )>; @@ -194,7 +194,7 @@ pub trait Domain: Send + Sync { _state: &SessionState, _payment_id: &str, _business_profile: &storage::BusinessProfile, - _payment_method_data: &Option, + _payment_method_data: &Option, ) -> CustomResult<(), errors::ApiErrorResponse> { Ok(()) } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 52d76289d883..139ada2be24c 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -648,7 +648,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ), token_data, confirm: request.confirm, - payment_method_data: payment_method_data_after_card_bin_call, + payment_method_data: payment_method_data_after_card_bin_call.map(Into::into), payment_method_info, force_sync: None, refunds: vec![], @@ -725,7 +725,7 @@ impl Domain for PaymentConfirm { business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, - Option, + Option, Option, )> { let (op, payment_method_data, pm_id) = helpers::make_pm_data( @@ -909,7 +909,7 @@ impl Domain for PaymentConfirm { state: &SessionState, payment_id: &str, business_profile: &storage::BusinessProfile, - payment_method_data: &Option, + payment_method_data: &Option, ) -> CustomResult<(), errors::ApiErrorResponse> { if let (Some(true), Some(api::PaymentMethodData::Card(card)), Some(merchant_config)) = ( business_profile.is_extended_card_info_enabled, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 1723084d0f19..e12f109e17bd 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -442,7 +442,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ), token_data: None, confirm: request.confirm, - payment_method_data: payment_method_data_after_card_bin_call, + payment_method_data: payment_method_data_after_card_bin_call.map(Into::into), payment_method_info, refunds: vec![], disputes: vec![], diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 3a5cd00b9c3b..89b941b9903d 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -450,7 +450,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_method_data: request .payment_method_data .as_ref() - .and_then(|pmd| pmd.payment_method_data.clone()), + .and_then(|pmd| pmd.payment_method_data.clone().into()), payment_method_info, force_sync: None, refunds: vec![], diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index f2d5d2d7d5e9..f258dc138acd 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -700,7 +700,7 @@ pub async fn retrieve_payment_method_from_auth_service( auth_token: &payment_methods::BankAccountTokenData, payment_intent: &PaymentIntent, customer: &Option, -) -> RouterResult> { +) -> RouterResult> { let db = state.store.as_ref(); let connector = PaymentAuthConnectorData::get_connector_by_name( @@ -833,7 +833,7 @@ pub async fn retrieve_payment_method_from_auth_service( let payment_method_data = match &bank_account.account_details { pm_auth_types::PaymentMethodTypeDetails::Ach(ach) => { - PaymentMethodData::BankDebit(BankDebitData::AchBankDebit { + domain::PaymentMethodData::BankDebit(domain::BankDebitData::AchBankDebit { billing_details: Some(billing_details), account_number: ach.account_number.clone(), routing_number: ach.routing_number.clone(), @@ -845,7 +845,7 @@ pub async fn retrieve_payment_method_from_auth_service( }) } pm_auth_types::PaymentMethodTypeDetails::Bacs(bacs) => { - PaymentMethodData::BankDebit(BankDebitData::BacsBankDebit { + domain::PaymentMethodData::BankDebit(domain::BankDebitData::BacsBankDebit { billing_details: Some(billing_details), account_number: bacs.account_number.clone(), sort_code: bacs.sort_code.clone(), @@ -853,7 +853,7 @@ pub async fn retrieve_payment_method_from_auth_service( }) } pm_auth_types::PaymentMethodTypeDetails::Sepa(sepa) => { - PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { + domain::PaymentMethodData::BankDebit(domain::BankDebitData::SepaBankDebit { billing_details: Some(billing_details), iban: sepa.iban.clone(), bank_account_holder_name: None, diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index 1d3275985f6e..d6eacd8a541f 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -7,5 +7,7 @@ pub use hyperswitch_domain_models::payment_method_data::{ KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData, PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, - WalletData, WeChatPayQr, + WalletData, WeChatPayQr,TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, + TokenizedWalletValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedBankRedirectValue1, + TokenizedBankRedirectValue2 }; diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 8a8f89f90171..80d5e309ecf6 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -10,7 +10,13 @@ pub use diesel_models::payment_method::{ TokenizeCoreWorkflow, }; -use crate::types::api::{self, payments}; +use crate::types::{ + api::{self, payments}, + domain::{ + self, + types::{self, AsyncLift}, + }, +}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "snake_case")] @@ -29,7 +35,7 @@ pub struct CardTokenData { #[derive(Debug, Clone, serde::Serialize, Default, serde::Deserialize)] pub struct PaymentMethodDataWithId { pub payment_method: Option, - pub payment_method_data: Option, + pub payment_method_data: Option, pub payment_method_id: Option, } From 85061f241a22dfa0ac38639dac87a672b905f44b Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 2 Aug 2024 02:36:54 +0530 Subject: [PATCH 04/59] fix clippy --- crates/hyperswitch_domain_models/src/api.rs | 4 +- .../src/payment_method_data.rs | 207 ++++++++++++++++++ .../src/connector/itaubank/transformers.rs | 3 +- crates/router/src/core/authentication.rs | 2 +- .../src/core/authentication/transformers.rs | 5 +- crates/router/src/core/blocklist/utils.rs | 10 +- crates/router/src/core/payment_methods.rs | 2 +- crates/router/src/core/payments/helpers.rs | 175 ++++++++------- crates/router/src/core/payments/operations.rs | 8 +- .../operations/payment_complete_authorize.rs | 4 +- .../payments/operations/payment_confirm.rs | 40 ++-- .../payments/operations/payment_create.rs | 41 ++-- .../payments/operations/payment_session.rs | 2 +- .../core/payments/operations/payment_start.rs | 2 +- .../payments/operations/payment_status.rs | 2 +- .../payments/operations/payment_update.rs | 4 +- .../payments_incremental_authorization.rs | 2 +- crates/router/src/core/payments/routing.rs | 4 +- .../router/src/core/payments/transformers.rs | 12 +- crates/router/src/core/pm_auth.rs | 14 +- crates/router/src/services/api.rs | 5 +- crates/router/src/types/domain/payments.rs | 2 +- 22 files changed, 391 insertions(+), 159 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/api.rs b/crates/hyperswitch_domain_models/src/api.rs index d62516234eae..b013d2b79027 100644 --- a/crates/hyperswitch_domain_models/src/api.rs +++ b/crates/hyperswitch_domain_models/src/api.rs @@ -5,6 +5,8 @@ use common_utils::{ impl_api_event_type, }; +use super::payment_method_data::PaymentMethodData; + #[derive(Debug, Eq, PartialEq)] pub enum ApplicationResponse { Json(R), @@ -33,7 +35,7 @@ impl_api_event_type!(Miscellaneous, (PaymentLinkFormData, GenericLinkFormData)); #[derive(Debug, Eq, PartialEq)] pub struct RedirectionFormData { pub redirect_form: crate::router_response_types::RedirectForm, - pub payment_method_data: Option, + pub payment_method_data: Option, pub amount: String, pub currency: String, } diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 01a4fbb4aa50..203a8e2e72ab 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -1,7 +1,9 @@ +use api_models::payments::ExtendedCardInfo; use common_utils::{pii::{self, Email}, id_type}; use masking::Secret; use serde::{Deserialize, Serialize}; use time::Date; +use common_enums::enums as api_enums; // We need to derive Serialize and Deserialize because some parts of payment method data are being // stored in the database as serde_json::Value @@ -1049,4 +1051,209 @@ pub struct TokenizedBankRedirectValue1 { #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedBankRedirectValue2 { pub customer_id: Option, +} + +pub trait GetPaymentMethodType { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType; +} + +impl GetPaymentMethodType for CardRedirectData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::Knet {} => api_enums::PaymentMethodType::Knet, + Self::Benefit {} => api_enums::PaymentMethodType::Benefit, + Self::MomoAtm {} => api_enums::PaymentMethodType::MomoAtm, + Self::CardRedirect {} => api_enums::PaymentMethodType::CardRedirect, + } + } +} + +impl GetPaymentMethodType for WalletData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::AliPayQr(_) | Self::AliPayRedirect(_) => api_enums::PaymentMethodType::AliPay, + Self::AliPayHkRedirect(_) => api_enums::PaymentMethodType::AliPayHk, + Self::MomoRedirect(_) => api_enums::PaymentMethodType::Momo, + Self::KakaoPayRedirect(_) => api_enums::PaymentMethodType::KakaoPay, + Self::GoPayRedirect(_) => api_enums::PaymentMethodType::GoPay, + Self::GcashRedirect(_) => api_enums::PaymentMethodType::Gcash, + Self::ApplePay(_) | Self::ApplePayRedirect(_) | Self::ApplePayThirdPartySdk(_) => { + api_enums::PaymentMethodType::ApplePay + } + Self::DanaRedirect {} => api_enums::PaymentMethodType::Dana, + Self::GooglePay(_) | Self::GooglePayRedirect(_) | Self::GooglePayThirdPartySdk(_) => { + api_enums::PaymentMethodType::GooglePay + } + Self::MbWayRedirect(_) => api_enums::PaymentMethodType::MbWay, + Self::MobilePayRedirect(_) => api_enums::PaymentMethodType::MobilePay, + Self::PaypalRedirect(_) | Self::PaypalSdk(_) => api_enums::PaymentMethodType::Paypal, + Self::SamsungPay(_) => api_enums::PaymentMethodType::SamsungPay, + Self::TwintRedirect {} => api_enums::PaymentMethodType::Twint, + Self::VippsRedirect {} => api_enums::PaymentMethodType::Vipps, + Self::TouchNGoRedirect(_) => api_enums::PaymentMethodType::TouchNGo, + Self::WeChatPayRedirect(_) | Self::WeChatPayQr(_) => { + api_enums::PaymentMethodType::WeChatPay + } + Self::CashappQr(_) => api_enums::PaymentMethodType::Cashapp, + Self::SwishQr(_) => api_enums::PaymentMethodType::Swish, + Self::Mifinity(_) => api_enums::PaymentMethodType::Mifinity, + } + } +} + +impl GetPaymentMethodType for PayLaterData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::KlarnaRedirect { .. } => api_enums::PaymentMethodType::Klarna, + Self::KlarnaSdk { .. } => api_enums::PaymentMethodType::Klarna, + Self::AffirmRedirect {} => api_enums::PaymentMethodType::Affirm, + Self::AfterpayClearpayRedirect { .. } => api_enums::PaymentMethodType::AfterpayClearpay, + Self::PayBrightRedirect {} => api_enums::PaymentMethodType::PayBright, + Self::WalleyRedirect {} => api_enums::PaymentMethodType::Walley, + Self::AlmaRedirect {} => api_enums::PaymentMethodType::Alma, + Self::AtomeRedirect {} => api_enums::PaymentMethodType::Atome, + } + } +} + +impl GetPaymentMethodType for BankRedirectData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::BancontactCard { .. } => api_enums::PaymentMethodType::BancontactCard, + Self::Bizum {} => api_enums::PaymentMethodType::Bizum, + Self::Blik { .. } => api_enums::PaymentMethodType::Blik, + Self::Eps { .. } => api_enums::PaymentMethodType::Eps, + Self::Giropay { .. } => api_enums::PaymentMethodType::Giropay, + Self::Ideal { .. } => api_enums::PaymentMethodType::Ideal, + Self::Interac { .. } => api_enums::PaymentMethodType::Interac, + Self::OnlineBankingCzechRepublic { .. } => { + api_enums::PaymentMethodType::OnlineBankingCzechRepublic + } + Self::OnlineBankingFinland { .. } => api_enums::PaymentMethodType::OnlineBankingFinland, + Self::OnlineBankingPoland { .. } => api_enums::PaymentMethodType::OnlineBankingPoland, + Self::OnlineBankingSlovakia { .. } => { + api_enums::PaymentMethodType::OnlineBankingSlovakia + } + Self::OpenBankingUk { .. } => api_enums::PaymentMethodType::OpenBankingUk, + Self::Przelewy24 { .. } => api_enums::PaymentMethodType::Przelewy24, + Self::Sofort { .. } => api_enums::PaymentMethodType::Sofort, + Self::Trustly { .. } => api_enums::PaymentMethodType::Trustly, + Self::OnlineBankingFpx { .. } => api_enums::PaymentMethodType::OnlineBankingFpx, + Self::OnlineBankingThailand { .. } => { + api_enums::PaymentMethodType::OnlineBankingThailand + } + Self::LocalBankRedirect { .. } => api_enums::PaymentMethodType::LocalBankRedirect, + } + } +} + +impl GetPaymentMethodType for BankDebitData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::AchBankDebit { .. } => api_enums::PaymentMethodType::Ach, + Self::SepaBankDebit { .. } => api_enums::PaymentMethodType::Sepa, + Self::BecsBankDebit { .. } => api_enums::PaymentMethodType::Becs, + Self::BacsBankDebit { .. } => api_enums::PaymentMethodType::Bacs, + } + } +} + +impl GetPaymentMethodType for BankTransferData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::AchBankTransfer { .. } => api_enums::PaymentMethodType::Ach, + Self::SepaBankTransfer { .. } => api_enums::PaymentMethodType::Sepa, + Self::BacsBankTransfer { .. } => api_enums::PaymentMethodType::Bacs, + Self::MultibancoBankTransfer { .. } => api_enums::PaymentMethodType::Multibanco, + Self::PermataBankTransfer { .. } => api_enums::PaymentMethodType::PermataBankTransfer, + Self::BcaBankTransfer { .. } => api_enums::PaymentMethodType::BcaBankTransfer, + Self::BniVaBankTransfer { .. } => api_enums::PaymentMethodType::BniVa, + Self::BriVaBankTransfer { .. } => api_enums::PaymentMethodType::BriVa, + Self::CimbVaBankTransfer { .. } => api_enums::PaymentMethodType::CimbVa, + Self::DanamonVaBankTransfer { .. } => api_enums::PaymentMethodType::DanamonVa, + Self::MandiriVaBankTransfer { .. } => api_enums::PaymentMethodType::MandiriVa, + Self::Pix { .. } => api_enums::PaymentMethodType::Pix, + Self::Pse {} => api_enums::PaymentMethodType::Pse, + Self::LocalBankTransfer { .. } => api_enums::PaymentMethodType::LocalBankTransfer, + } + } +} + +impl GetPaymentMethodType for CryptoData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + api_enums::PaymentMethodType::CryptoCurrency + } +} + +impl GetPaymentMethodType for RealTimePaymentData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::Fps {} => api_enums::PaymentMethodType::Fps, + Self::DuitNow {} => api_enums::PaymentMethodType::DuitNow, + Self::PromptPay {} => api_enums::PaymentMethodType::PromptPay, + Self::VietQr {} => api_enums::PaymentMethodType::VietQr, + } + } +} + +impl GetPaymentMethodType for UpiData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::UpiCollect(_) => api_enums::PaymentMethodType::UpiCollect, + Self::UpiIntent(_) => api_enums::PaymentMethodType::UpiIntent, + } + } +} +impl GetPaymentMethodType for VoucherData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::Boleto(_) => api_enums::PaymentMethodType::Boleto, + Self::Efecty => api_enums::PaymentMethodType::Efecty, + Self::PagoEfectivo => api_enums::PaymentMethodType::PagoEfectivo, + Self::RedCompra => api_enums::PaymentMethodType::RedCompra, + Self::RedPagos => api_enums::PaymentMethodType::RedPagos, + Self::Alfamart(_) => api_enums::PaymentMethodType::Alfamart, + Self::Indomaret(_) => api_enums::PaymentMethodType::Indomaret, + Self::Oxxo => api_enums::PaymentMethodType::Oxxo, + Self::SevenEleven(_) => api_enums::PaymentMethodType::SevenEleven, + Self::Lawson(_) => api_enums::PaymentMethodType::Lawson, + Self::MiniStop(_) => api_enums::PaymentMethodType::MiniStop, + Self::FamilyMart(_) => api_enums::PaymentMethodType::FamilyMart, + Self::Seicomart(_) => api_enums::PaymentMethodType::Seicomart, + Self::PayEasy(_) => api_enums::PaymentMethodType::PayEasy, + } + } +} +impl GetPaymentMethodType for GiftCardData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::Givex(_) => api_enums::PaymentMethodType::Givex, + Self::PaySafeCard {} => api_enums::PaymentMethodType::PaySafeCard, + } + } +} + +impl GetPaymentMethodType for OpenBankingData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::OpenBankingPIS {} => api_enums::PaymentMethodType::OpenBankingPIS, + } + } +} + +impl From for ExtendedCardInfo { + fn from(value: Card) -> Self { + Self { + card_number: value.card_number, + card_exp_month: value.card_exp_month, + card_exp_year: value.card_exp_year, + card_holder_name: None, + card_cvc: value.card_cvc, + card_issuer: value.card_issuer, + card_network: value.card_network, + card_type: value.card_type, + card_issuing_country: value.card_issuing_country, + bank_code: value.bank_code, + } + } } \ No newline at end of file diff --git a/crates/router/src/connector/itaubank/transformers.rs b/crates/router/src/connector/itaubank/transformers.rs index 5c53cfbf1602..de788054a9ba 100644 --- a/crates/router/src/connector/itaubank/transformers.rs +++ b/crates/router/src/connector/itaubank/transformers.rs @@ -119,7 +119,8 @@ impl TryFrom<&ItaubankRouterData<&types::PaymentsAuthorizeRouterData>> for Itaub | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::OpenBanking(_) => { + | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( "Selected payment method through itaubank".to_string(), ) diff --git a/crates/router/src/core/authentication.rs b/crates/router/src/core/authentication.rs index 7cc03ab9ea45..b05b13c726a8 100644 --- a/crates/router/src/core/authentication.rs +++ b/crates/router/src/core/authentication.rs @@ -22,7 +22,7 @@ pub async fn perform_authentication( state: &SessionState, merchant_id: common_utils::id_type::MerchantId, authentication_connector: String, - payment_method_data: payments::PaymentMethodData, + payment_method_data: domain::PaymentMethodData, payment_method: common_enums::PaymentMethod, billing_address: payments::Address, shipping_address: Option, diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 569f2beea5e0..88c31581807c 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -13,6 +13,7 @@ use crate::{ types::{ self, storage, transformers::{ForeignFrom, ForeignTryFrom}, + domain, }, utils::ext_traits::OptionExt, }; @@ -28,7 +29,7 @@ const IRRELEVANT_CONNECTOR_REQUEST_REFERENCE_ID_IN_AUTHENTICATION_FLOW: &str = pub fn construct_authentication_router_data( merchant_id: common_utils::id_type::MerchantId, authentication_connector: String, - payment_method_data: payments::PaymentMethodData, + payment_method_data: domain::PaymentMethodData, payment_method: PaymentMethod, billing_address: payments::Address, shipping_address: Option, @@ -47,7 +48,7 @@ pub fn construct_authentication_router_data( three_ds_requestor_url: String, ) -> RouterResult { let router_request = types::authentication::ConnectorAuthenticationRequestData { - payment_method_data: From::from(payment_method_data), + payment_method_data: payment_method_data, billing_address, shipping_address, browser_details, diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs index 8edd75d3df7f..6a101f506f36 100644 --- a/crates/router/src/core/blocklist/utils.rs +++ b/crates/router/src/core/blocklist/utils.rs @@ -304,7 +304,7 @@ where let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, merchant_id).await?; // Hashed Fingerprint to check whether or not this payment should be blocked. - let card_number_fingerprint = if let Some(api_models::payments::PaymentMethodData::Card(card)) = + let card_number_fingerprint = if let Some(domain::PaymentMethodData::Card(card)) = payment_data.payment_method_data.as_ref() { generate_fingerprint( @@ -332,7 +332,7 @@ where .payment_method_data .as_ref() .and_then(|pm_data| match pm_data { - api_models::payments::PaymentMethodData::Card(card) => { + domain::PaymentMethodData::Card(card) => { Some(card.card_number.get_card_isin()) } _ => None, @@ -344,7 +344,7 @@ where .payment_method_data .as_ref() .and_then(|pm_data| match pm_data { - api_models::payments::PaymentMethodData::Card(card) => { + domain::PaymentMethodData::Card(card) => { Some(card.card_number.get_extended_card_bin()) } _ => None, @@ -446,12 +446,12 @@ where pub async fn generate_payment_fingerprint( state: &SessionState, merchant_id: common_utils::id_type::MerchantId, - payment_method_data: Option, + payment_method_data: Option, ) -> CustomResult, errors::ApiErrorResponse> { let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, &merchant_id).await?; Ok( - if let Some(api_models::payments::PaymentMethodData::Card(card)) = + if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data.as_ref() { generate_fingerprint( diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 4474b4925e5c..70461dec185c 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -434,7 +434,7 @@ pub async fn retrieve_payment_method_with_token( merchant_key_store: &domain::MerchantKeyStore, token_data: &storage::PaymentTokenData, payment_intent: &PaymentIntent, - card_token_data: Option<&CardToken>, + card_token_data: Option<&domain::CardToken>, customer: &Option, storage_scheme: common_enums::enums::MerchantStorageScheme, ) -> RouterResult { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 572f86fe05e9..9f4bbd5580f0 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, str::FromStr}; use api_models::{ customers::CustomerRequestWithEmail, mandates::RecurringDetails, - payments::{AddressDetailsWithPhone, CardToken, GetPaymentMethodType, RequestSurchargeDetails}, + payments::{AddressDetailsWithPhone, RequestSurchargeDetails}, }; use base64::Engine; use common_enums::ConnectorType; @@ -21,9 +21,7 @@ use diesel_models::enums::{self}; use error_stack::{report, ResultExt}; use futures::future::Either; use hyperswitch_domain_models::{ - mandates::MandateData, - payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent}, - router_data::KlarnaSdkResponse, + mandates::MandateData, payment_method_data::GetPaymentMethodType, payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent}, router_data::KlarnaSdkResponse }; use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject}; use josekit::jwe; @@ -1733,7 +1731,7 @@ pub async fn retrieve_payment_method_with_temporary_token( token: &str, payment_intent: &PaymentIntent, merchant_key_store: &domain::MerchantKeyStore, - card_token_data: Option<&CardToken>, + card_token_data: Option<&domain::CardToken>, ) -> RouterResult> { let (pm, supplementary_data) = vault::Vault::get_payment_method_data_from_locker(state, token, merchant_key_store) @@ -1758,7 +1756,7 @@ pub async fn retrieve_payment_method_with_temporary_token( // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked // from payment_method_data.card_token object - let name_on_card = if let Some(name) = card.card_holder_name.clone() { + let name_on_card = if let Some(name) = card.nick_name.clone() { //todo! if name.clone().expose().is_empty() { card_token_data .and_then(|token_data| { @@ -1767,7 +1765,7 @@ pub async fn retrieve_payment_method_with_temporary_token( }) .or(Some(name)) } else { - card.card_holder_name.clone() + card.nick_name.clone() //todo! } } else { card_token_data.and_then(|token_data| { @@ -1776,7 +1774,7 @@ pub async fn retrieve_payment_method_with_temporary_token( }) }; - updated_card.card_holder_name = name_on_card; + updated_card.nick_name = name_on_card; //todo! if let Some(token_data) = card_token_data { if let Some(cvc) = token_data.card_cvc.clone() { @@ -1786,7 +1784,7 @@ pub async fn retrieve_payment_method_with_temporary_token( } if is_card_updated { - let updated_pm = api::PaymentMethodData::Card(updated_card); + let updated_pm = domain::PaymentMethodData::Card(updated_card); vault::Vault::store_payment_method_data_in_locker( state, Some(token.to_owned()), @@ -1800,21 +1798,21 @@ pub async fn retrieve_payment_method_with_temporary_token( Some((updated_pm, enums::PaymentMethod::Card)) } else { Some(( - domain::Card(card), + domain::PaymentMethodData::Card(card), enums::PaymentMethod::Card, )) } } - Some(the_pm @ domain::Wallet(_)) => { + Some(the_pm @ domain::PaymentMethodData::Wallet(_)) => { Some((the_pm, enums::PaymentMethod::Wallet)) } - Some(the_pm @ domain::BankTransfer(_)) => { + Some(the_pm @ domain::PaymentMethodData::BankTransfer(_)) => { Some((the_pm, enums::PaymentMethod::BankTransfer)) } - Some(the_pm @ domain::BankRedirect(_)) => { + Some(the_pm @ domain::PaymentMethodData::BankRedirect(_)) => { Some((the_pm, enums::PaymentMethod::BankRedirect)) } @@ -1830,7 +1828,7 @@ pub async fn retrieve_card_with_permanent_token( locker_id: &str, _payment_method_id: &str, payment_intent: &PaymentIntent, - card_token_data: Option<&CardToken>, + card_token_data: Option<&domain::CardToken>, merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult { @@ -1887,7 +1885,7 @@ pub async fn retrieve_card_with_permanent_token( bank_code: None, }; - Ok(domain::Card(api_card).into()) + Ok(domain::PaymentMethodData::Card(api_card.into())) } pub async fn retrieve_payment_method_from_db_with_token_data( @@ -1997,7 +1995,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( domain::PaymentMethodData::CardToken(token_data) => Some(token_data), _ => None, }) - .or(Some(CardToken::default())); + .or(Some(domain::CardToken::default())); if let Some(cvc) = payment_data.card_cvc.clone() { if let Some(token_data) = card_token_data.as_mut() { @@ -3855,12 +3853,12 @@ mod test { #[instrument(skip_all)] pub async fn get_additional_payment_data( - pm_data: &api_models::payments::PaymentMethodData, + pm_data: &domain::PaymentMethodData, db: &dyn StorageInterface, profile_id: &str, -) -> api_models::payments::AdditionalPaymentData { +) -> Option { match pm_data { - api_models::payments::PaymentMethodData::Card(card_data) => { + domain::PaymentMethodData::Card(card_data) => { //todo! let card_isin = Some(card_data.card_number.get_card_isin()); let enable_extended_bin =db .find_config_by_key_unwrap_or( @@ -3881,7 +3879,7 @@ pub async fn get_additional_payment_data( && card_data.card_issuing_country.is_some() && card_data.bank_code.is_some() { - api_models::payments::AdditionalPaymentData::Card(Box::new( + Some(api_models::payments::AdditionalPaymentData::Card(Box::new( api_models::payments::AdditionalCardInfo { card_issuer: card_data.card_issuer.to_owned(), card_network: card_data.card_network.clone(), @@ -3890,7 +3888,7 @@ pub async fn get_additional_payment_data( bank_code: card_data.bank_code.to_owned(), card_exp_month: Some(card_data.card_exp_month.clone()), card_exp_year: Some(card_data.card_exp_year.clone()), - card_holder_name: card_data.card_holder_name.clone(), + card_holder_name: card_data.nick_name.clone(), //todo! last4: last4.clone(), card_isin: card_isin.clone(), card_extended_bin: card_extended_bin.clone(), @@ -3898,7 +3896,7 @@ pub async fn get_additional_payment_data( payment_checks: None, authentication_data: None, }, - )) + ))) } else { let card_info = card_isin .clone() @@ -3923,14 +3921,14 @@ pub async fn get_additional_payment_data( card_extended_bin: card_extended_bin.clone(), card_exp_month: Some(card_data.card_exp_month.clone()), card_exp_year: Some(card_data.card_exp_year.clone()), - card_holder_name: card_data.card_holder_name.clone(), + card_holder_name: card_data.nick_name.clone(), //todo! // These are filled after calling the processor / connector payment_checks: None, authentication_data: None, }, )) }); - card_info.unwrap_or_else(|| { + Some(card_info.unwrap_or_else(|| { api_models::payments::AdditionalPaymentData::Card(Box::new( api_models::payments::AdditionalCardInfo { card_issuer: None, @@ -3943,77 +3941,83 @@ pub async fn get_additional_payment_data( card_extended_bin, card_exp_month: Some(card_data.card_exp_month.clone()), card_exp_year: Some(card_data.card_exp_year.clone()), - card_holder_name: card_data.card_holder_name.clone(), + card_holder_name: card_data.nick_name.clone(), //todo! // These are filled after calling the processor / connector payment_checks: None, authentication_data: None, }, )) - }) + })) } } - api_models::payments::PaymentMethodData::BankRedirect(bank_redirect_data) => { + domain::PaymentMethodData::BankRedirect(bank_redirect_data) => { match bank_redirect_data { - api_models::payments::BankRedirectData::Eps { bank_name, .. } => { - api_models::payments::AdditionalPaymentData::BankRedirect { + domain::BankRedirectData::Eps { bank_name, .. } => { + Some(api_models::payments::AdditionalPaymentData::BankRedirect { bank_name: bank_name.to_owned(), - } + }) } - api_models::payments::BankRedirectData::Ideal { bank_name, .. } => { - api_models::payments::AdditionalPaymentData::BankRedirect { + domain::BankRedirectData::Ideal { bank_name, .. } => { + Some(api_models::payments::AdditionalPaymentData::BankRedirect { bank_name: bank_name.to_owned(), - } + }) } - _ => api_models::payments::AdditionalPaymentData::BankRedirect { bank_name: None }, + _ => Some(api_models::payments::AdditionalPaymentData::BankRedirect { bank_name: None }) } } - api_models::payments::PaymentMethodData::Wallet(wallet) => match wallet { - api_models::payments::WalletData::ApplePay(apple_pay_wallet_data) => { - api_models::payments::AdditionalPaymentData::Wallet { - apple_pay: Some(apple_pay_wallet_data.payment_method.to_owned()), - } + domain::PaymentMethodData::Wallet(wallet) => match wallet { + domain::WalletData::ApplePay(apple_pay_wallet_data) => { + Some(api_models::payments::AdditionalPaymentData::Wallet { + apple_pay: Some(api_models::payments::ApplepayPaymentMethod{ + display_name: apple_pay_wallet_data.payment_method.display_name.clone(), + network: apple_pay_wallet_data.payment_method.network.clone(), + pm_type: apple_pay_wallet_data.payment_method.pm_type.clone(), + + }) + }) } - _ => api_models::payments::AdditionalPaymentData::Wallet { apple_pay: None }, + _ => Some(api_models::payments::AdditionalPaymentData::Wallet { apple_pay: None }) }, - api_models::payments::PaymentMethodData::PayLater(_) => { - api_models::payments::AdditionalPaymentData::PayLater { klarna_sdk: None } + domain::PaymentMethodData::PayLater(_) => { + Some(api_models::payments::AdditionalPaymentData::PayLater { klarna_sdk: None }) } - api_models::payments::PaymentMethodData::BankTransfer(_) => { - api_models::payments::AdditionalPaymentData::BankTransfer {} + domain::PaymentMethodData::BankTransfer(_) => { + Some(api_models::payments::AdditionalPaymentData::BankTransfer {}) } - api_models::payments::PaymentMethodData::Crypto(_) => { - api_models::payments::AdditionalPaymentData::Crypto {} + domain::PaymentMethodData::Crypto(_) => { + Some(api_models::payments::AdditionalPaymentData::Crypto {}) } - api_models::payments::PaymentMethodData::BankDebit(_) => { - api_models::payments::AdditionalPaymentData::BankDebit {} + domain::PaymentMethodData::BankDebit(_) => { + Some(api_models::payments::AdditionalPaymentData::BankDebit {}) } - api_models::payments::PaymentMethodData::MandatePayment => { - api_models::payments::AdditionalPaymentData::MandatePayment {} + domain::PaymentMethodData::MandatePayment => { + Some(api_models::payments::AdditionalPaymentData::MandatePayment {}) } - api_models::payments::PaymentMethodData::Reward => { - api_models::payments::AdditionalPaymentData::Reward {} + domain::PaymentMethodData::Reward => { + Some(api_models::payments::AdditionalPaymentData::Reward {}) } - api_models::payments::PaymentMethodData::RealTimePayment(_) => { - api_models::payments::AdditionalPaymentData::RealTimePayment {} + domain::PaymentMethodData::RealTimePayment(_) => { + Some(api_models::payments::AdditionalPaymentData::RealTimePayment {}) } - api_models::payments::PaymentMethodData::Upi(_) => { - api_models::payments::AdditionalPaymentData::Upi {} + domain::PaymentMethodData::Upi(_) => { + Some(api_models::payments::AdditionalPaymentData::Upi {}) } - api_models::payments::PaymentMethodData::CardRedirect(_) => { - api_models::payments::AdditionalPaymentData::CardRedirect {} + domain::PaymentMethodData::CardRedirect(_) => { + Some(api_models::payments::AdditionalPaymentData::CardRedirect {}) } - api_models::payments::PaymentMethodData::Voucher(_) => { - api_models::payments::AdditionalPaymentData::Voucher {} + domain::PaymentMethodData::Voucher(_) => { + Some(api_models::payments::AdditionalPaymentData::Voucher {}) } - api_models::payments::PaymentMethodData::GiftCard(_) => { - api_models::payments::AdditionalPaymentData::GiftCard {} + domain::PaymentMethodData::GiftCard(_) => { + Some(api_models::payments::AdditionalPaymentData::GiftCard {}) } - api_models::payments::PaymentMethodData::CardToken(_) => { - api_models::payments::AdditionalPaymentData::CardToken {} + domain::PaymentMethodData::CardToken(_) => { + Some(api_models::payments::AdditionalPaymentData::CardToken {}) } - api_models::payments::PaymentMethodData::OpenBanking(_) => { - api_models::payments::AdditionalPaymentData::OpenBanking {} + domain::PaymentMethodData::OpenBanking(_) => { + Some(api_models::payments::AdditionalPaymentData::OpenBanking {}) } + domain::PaymentMethodData::NetworkToken(_) => None } } @@ -4496,14 +4500,14 @@ impl ApplePayData { } pub fn get_key_params_for_surcharge_details( - payment_method_data: &api_models::payments::PaymentMethodData, + payment_method_data: &domain::PaymentMethodData, ) -> Option<( common_enums::PaymentMethod, common_enums::PaymentMethodType, Option, )> { match payment_method_data { - api_models::payments::PaymentMethodData::Card(card) => { + domain::PaymentMethodData::Card(card) => { // surcharge generated will always be same for credit as well as debit // since surcharge conditions cannot be defined on card_type Some(( @@ -4512,69 +4516,70 @@ pub fn get_key_params_for_surcharge_details( card.card_network.clone(), )) } - api_models::payments::PaymentMethodData::CardRedirect(card_redirect_data) => Some(( + domain::PaymentMethodData::CardRedirect(card_redirect_data) => Some(( common_enums::PaymentMethod::CardRedirect, card_redirect_data.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::Wallet(wallet) => Some(( + domain::PaymentMethodData::Wallet(wallet) => Some(( common_enums::PaymentMethod::Wallet, wallet.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::PayLater(pay_later) => Some(( + domain::PaymentMethodData::PayLater(pay_later) => Some(( common_enums::PaymentMethod::PayLater, pay_later.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::BankRedirect(bank_redirect) => Some(( + domain::PaymentMethodData::BankRedirect(bank_redirect) => Some(( common_enums::PaymentMethod::BankRedirect, bank_redirect.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::BankDebit(bank_debit) => Some(( + domain::PaymentMethodData::BankDebit(bank_debit) => Some(( common_enums::PaymentMethod::BankDebit, bank_debit.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::BankTransfer(bank_transfer) => Some(( + domain::PaymentMethodData::BankTransfer(bank_transfer) => Some(( common_enums::PaymentMethod::BankTransfer, bank_transfer.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::Crypto(crypto) => Some(( + domain::PaymentMethodData::Crypto(crypto) => Some(( common_enums::PaymentMethod::Crypto, crypto.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::MandatePayment => None, - api_models::payments::PaymentMethodData::Reward => None, - api_models::payments::PaymentMethodData::RealTimePayment(real_time_payment) => Some(( + domain::PaymentMethodData::MandatePayment => None, + domain::PaymentMethodData::Reward => None, + domain::PaymentMethodData::RealTimePayment(real_time_payment) => Some(( common_enums::PaymentMethod::RealTimePayment, real_time_payment.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::Upi(upi_data) => Some(( + domain::PaymentMethodData::Upi(upi_data) => Some(( common_enums::PaymentMethod::Upi, upi_data.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::Voucher(voucher) => Some(( + domain::PaymentMethodData::Voucher(voucher) => Some(( common_enums::PaymentMethod::Voucher, voucher.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::GiftCard(gift_card) => Some(( + domain::PaymentMethodData::GiftCard(gift_card) => Some(( common_enums::PaymentMethod::GiftCard, gift_card.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::OpenBanking(ob_data) => Some(( + domain::PaymentMethodData::OpenBanking(ob_data) => Some(( common_enums::PaymentMethod::OpenBanking, ob_data.get_payment_method_type(), None, )), - api_models::payments::PaymentMethodData::CardToken(_) => None, + domain::PaymentMethodData::CardToken(_) => None, + domain::PaymentMethodData::NetworkToken(_) => None, } } @@ -4795,7 +4800,7 @@ pub async fn get_payment_method_details_from_payment_token( payment_intent: &PaymentIntent, key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, -) -> RouterResult> { +) -> RouterResult> { let hyperswitch_token = if let Some(token) = payment_attempt.payment_token.clone() { let redis_conn = state .store @@ -4970,7 +4975,7 @@ pub async fn get_payment_external_authentication_flow_during_confirm( connector_supports_separate_authn.is_some() ); let card_number = payment_data.payment_method_data.as_ref().and_then(|pmd| { - if let api_models::payments::PaymentMethodData::Card(card) = pmd { + if let domain::PaymentMethodData::Card(card) = pmd { Some(card.card_number.clone()) } else { None diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index 1a467576b80f..55690733745e 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -306,7 +306,7 @@ where _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest>, - Option, + Option, Option, )> { Ok((Box::new(self), None, None)) @@ -369,7 +369,7 @@ where _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCaptureRequest>, - Option, + Option, Option, )> { Ok((Box::new(self), None, None)) @@ -444,7 +444,7 @@ where _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCancelRequest>, - Option, + Option, Option, )> { Ok((Box::new(self), None, None)) @@ -508,7 +508,7 @@ where _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRejectRequest>, - Option, + Option, Option, )> { Ok((Box::new(self), None, None)) 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 664a7c9bb032..3dcd1e5cb5dd 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -314,7 +314,7 @@ impl GetTracker, api::PaymentsRequest> for Co payment_method_data: request .payment_method_data .as_ref() - .and_then(|pmd| pmd.payment_method_data.clone()), + .and_then(|pmd| pmd.payment_method_data.clone().map(Into::into).clone()), payment_method_info, force_sync: None, refunds: vec![], @@ -399,7 +399,7 @@ impl Domain for CompleteAuthorize { business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, - Option, + Option, Option, )> { let (op, payment_method_data, pm_id) = helpers::make_pm_data( diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 139ada2be24c..d685614ecda2 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -4,7 +4,7 @@ use api_models::{ admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payment_methods::PaymentMethodsData, - payments::{AdditionalPaymentData, ExtendedCardInfo}, + payments::{AdditionalPaymentData, ExtendedCardInfo, GetAddressFromPaymentMethodData}, }; use async_trait::async_trait; use common_utils::{ @@ -457,9 +457,9 @@ impl GetTracker, api::PaymentsRequest> for Pa let additional_pm_data_fut = tokio::spawn( async move { Ok(n_request_payment_method_data - .async_map(|payment_method_data| async move { - helpers::get_additional_payment_data( - &payment_method_data, + .async_and_then(|payment_method_data| async move { + helpers::get_additional_payment_data( + &payment_method_data.into(), store.as_ref(), profile_id.as_ref(), ) @@ -628,6 +628,21 @@ impl GetTracker, api::PaymentsRequest> for Pa .as_ref() .map(|payment_method_billing| payment_method_billing.address_id.clone()); + let address = PaymentAddress::new( + shipping_address.as_ref().map(From::from), + billing_address.as_ref().map(From::from), + payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, + ); + + let payment_method_data_billing = request + .payment_method_data + .as_ref() + .and_then(|pmd| pmd.payment_method_data.as_ref()) + .and_then(|payment_method_data_billing| payment_method_data_billing.get_billing_address()); + + let unified_address = address.unify_with_payment_method_data_billing(payment_method_data_billing); + let payment_data = PaymentData { flow: PhantomData, payment_intent, @@ -640,12 +655,7 @@ impl GetTracker, api::PaymentsRequest> for Pa setup_mandate, customer_acceptance: customer_acceptance.map(From::from), token, - address: PaymentAddress::new( - shipping_address.as_ref().map(From::from), - billing_address.as_ref().map(From::from), - payment_method_billing.as_ref().map(From::from), - business_profile.use_billing_as_payment_method_billing, - ), + address: unified_address, token_data, confirm: request.confirm, payment_method_data: payment_method_data_after_card_bin_call.map(Into::into), @@ -911,7 +921,7 @@ impl Domain for PaymentConfirm { business_profile: &storage::BusinessProfile, payment_method_data: &Option, ) -> CustomResult<(), errors::ApiErrorResponse> { - if let (Some(true), Some(api::PaymentMethodData::Card(card)), Some(merchant_config)) = ( + if let (Some(true), Some(domain::PaymentMethodData::Card(card)), Some(merchant_config)) = ( business_profile.is_extended_card_info_enabled, payment_method_data, business_profile.extended_card_info_config.clone(), @@ -1065,6 +1075,9 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .as_ref() .get_required_value("profile_id") .change_context(errors::ApiErrorResponse::InternalServerError)?; + + payment_data.payment_attempt.clone().payment_method_data; + let payment_experience = payment_data.payment_attempt.payment_experience; let additional_pm_data = payment_data .payment_method_data @@ -1117,14 +1130,15 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen }; let customer_details = payment_data.payment_intent.customer_details.clone(); - let business_sub_label = payment_data.payment_attempt.business_sub_label.clone(); - let authentication_type = payment_data.payment_attempt.authentication_type; + let business_sub_label = payment_data.payment_attempt.clone().business_sub_label.clone(); + let authentication_type = payment_data.payment_attempt.clone().authentication_type; let (shipping_address_id, billing_address_id, payment_method_billing_address_id) = ( payment_data.payment_intent.shipping_address_id.clone(), payment_data.payment_intent.billing_address_id.clone(), payment_data .payment_attempt + .clone() .payment_method_billing_address_id .clone(), ); diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index e12f109e17bd..145641e591b1 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use api_models::{ - enums::FrmSuggestion, mandates::RecurringDetails, payment_methods::PaymentMethodsData, + enums::FrmSuggestion, mandates::RecurringDetails, payment_methods::PaymentMethodsData, payments::GetAddressFromPaymentMethodData, }; use async_trait::async_trait; use common_utils::{ @@ -422,6 +422,21 @@ impl GetTracker, api::PaymentsRequest> for Pa let amount = payment_attempt.get_total_amount().into(); + let address = PaymentAddress::new( + shipping_address.as_ref().map(From::from), + billing_address.as_ref().map(From::from), + payment_method_billing_address.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, + ); + + let payment_method_data_billing = request + .payment_method_data + .as_ref() + .and_then(|pmd| pmd.payment_method_data.as_ref()) + .and_then(|payment_method_data_billing| payment_method_data_billing.get_billing_address()); + + let unified_address = address.unify_with_payment_method_data_billing(payment_method_data_billing); + let payment_data = PaymentData { flow: PhantomData, payment_intent, @@ -434,12 +449,7 @@ impl GetTracker, api::PaymentsRequest> for Pa setup_mandate, customer_acceptance, token, - address: PaymentAddress::new( - shipping_address.as_ref().map(From::from), - billing_address.as_ref().map(From::from), - payment_method_billing_address.as_ref().map(From::from), - business_profile.use_billing_as_payment_method_billing, - ), + address: unified_address, token_data: None, confirm: request.confirm, payment_method_data: payment_method_data_after_card_bin_call.map(Into::into), @@ -519,7 +529,7 @@ impl Domain for PaymentCreate { business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, - Option, + Option, Option, )> { helpers::make_pm_data( @@ -829,13 +839,11 @@ impl PaymentCreate { let mut additional_pm_data = request .payment_method_data - .as_ref() - .and_then(|payment_method_data_request| { - payment_method_data_request.payment_method_data.as_ref() - }) - .async_map(|payment_method_data| async { + .clone() + .and_then(|payment_method_data_request| payment_method_data_request.payment_method_data) + .async_and_then(|payment_method_data| async { helpers::get_additional_payment_data( - payment_method_data, + &payment_method_data.into(), &*state.store, &profile_id, ) @@ -847,7 +855,7 @@ impl PaymentCreate { // If recurring payment is made using payment_method_id, then fetch payment_method_data from retrieved payment_method object additional_pm_data = payment_method_info .as_ref() - .async_map(|pm_info| async { + .async_and_then(|pm_info| async { decrypt_optional::( &state.into(), pm_info.payment_method_data.clone(), @@ -875,10 +883,9 @@ impl PaymentCreate { }) }) .await - .flatten() .map(|card| { api_models::payments::AdditionalPaymentData::Card(Box::new(card.into())) - }) + }); }; let additional_pm_data_value = additional_pm_data diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index e65fcbc390b8..95a71e44b588 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -331,7 +331,7 @@ where _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsSessionRequest>, - Option, + Option, Option, )> { //No payment method data for this operation diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 0172b5f26443..a31079219a13 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -303,7 +303,7 @@ where business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsStartRequest>, - Option, + Option, Option, )> { if payment_data diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index a7ba54a377d4..76f53d23d7a2 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -92,7 +92,7 @@ impl Domain for PaymentStatus { _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, - Option, + Option, Option, )> { Ok((Box::new(self), None, None)) diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 89b941b9903d..0d768e59327c 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -450,7 +450,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_method_data: request .payment_method_data .as_ref() - .and_then(|pmd| pmd.payment_method_data.clone().into()), + .and_then(|pmd| pmd.payment_method_data.clone().map(Into::into)), payment_method_info, force_sync: None, refunds: vec![], @@ -527,7 +527,7 @@ impl Domain for PaymentUpdate { business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, - Option, + Option, Option, )> { helpers::make_pm_data( 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 c7311059ba2d..b34599821b16 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -320,7 +320,7 @@ impl Domain _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, PaymentsIncrementalAuthorizationRequest>, - Option, + Option, Option, )> { Ok((Box::new(self), None, None)) diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 151192fb8b79..19e08c5f6247 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -195,7 +195,7 @@ where .payment_method_data .as_ref() .and_then(|pm_data| match pm_data { - api::PaymentMethodData::Card(card) => card.card_network.clone(), + domain::PaymentMethodData::Card(card) => card.card_network.clone(), _ => None, }), @@ -207,7 +207,7 @@ where .payment_method_data .as_ref() .and_then(|pm_data| match pm_data { - api::PaymentMethodData::Card(card) => { + domain::PaymentMethodData::Card(card) => { Some(card.card_number.peek().chars().take(6).collect()) } _ => None, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 35d72ac6ef1c..ff64f7c7a2d1 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -135,10 +135,6 @@ where Some(merchant_connector_account), ); - let payment_method_data_billing = payment_data - .payment_method_data - .as_ref() - .and_then(|payment_method_data| payment_method_data.get_billing_address()); router_data = types::RouterData { flow: PhantomData, @@ -152,9 +148,7 @@ where connector_auth_type: auth_type, description: payment_data.payment_intent.description.clone(), return_url: payment_data.payment_intent.return_url.clone(), - address: payment_data - .address - .unify_with_payment_method_data_billing(payment_method_data_billing), + address: payment_data.address.clone(), auth_type: payment_data .payment_attempt .authentication_type @@ -582,7 +576,7 @@ where services::ApplicationResponse::Form(Box::new(services::RedirectionFormData { redirect_form: form, - payment_method_data: payment_data.payment_method_data, + payment_method_data: payment_data.payment_method_data.map(Into::into), amount, currency: currency.to_string(), })) @@ -1348,7 +1342,7 @@ impl TryFrom> for types::PaymentsAuthoriz // payment_method_data is not required during recurring mandate payment, in such case keep default PaymentMethodData as MandatePayment let payment_method_data = payment_data.payment_method_data.or_else(|| { if payment_data.mandate_id.is_some() { - Some(api_models::payments::PaymentMethodData::MandatePayment) + Some(domain::PaymentMethodData::MandatePayment) } else { None } diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index f258dc138acd..1e3b246679d8 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -834,11 +834,11 @@ pub async fn retrieve_payment_method_from_auth_service( let payment_method_data = match &bank_account.account_details { pm_auth_types::PaymentMethodTypeDetails::Ach(ach) => { domain::PaymentMethodData::BankDebit(domain::BankDebitData::AchBankDebit { - billing_details: Some(billing_details), + // billing_details: Some(billing_details), account_number: ach.account_number.clone(), routing_number: ach.routing_number.clone(), - card_holder_name: None, - bank_account_holder_name: None, + // card_holder_name: None, + // bank_account_holder_name: None, bank_name: None, bank_type, bank_holder_type: None, @@ -846,17 +846,17 @@ pub async fn retrieve_payment_method_from_auth_service( } pm_auth_types::PaymentMethodTypeDetails::Bacs(bacs) => { domain::PaymentMethodData::BankDebit(domain::BankDebitData::BacsBankDebit { - billing_details: Some(billing_details), + // billing_details: Some(billing_details), account_number: bacs.account_number.clone(), sort_code: bacs.sort_code.clone(), - bank_account_holder_name: None, + // bank_account_holder_name: None, }) } pm_auth_types::PaymentMethodTypeDetails::Sepa(sepa) => { domain::PaymentMethodData::BankDebit(domain::BankDebitData::SepaBankDebit { - billing_details: Some(billing_details), + // billing_details: Some(billing_details), iban: sepa.iban.clone(), - bank_account_holder_name: None, + // bank_account_holder_name: None, }) } }; diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 99393e6cd1b9..00a49dcda4c3 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -34,6 +34,7 @@ pub use hyperswitch_domain_models::{ RedirectionFormData, }, router_response_types::RedirectForm, + payment_method_data::PaymentMethodData, }; pub use hyperswitch_interfaces::{ api::{ @@ -1251,7 +1252,7 @@ impl Authenticate for api_models::payments::PaymentsRejectRequest {} pub fn build_redirection_form( form: &RedirectForm, - payment_method_data: Option, + payment_method_data: Option, amount: String, currency: String, config: Settings, @@ -1327,7 +1328,7 @@ pub fn build_redirection_form( payment_fields_token, } => { let card_details = - if let Some(api::PaymentMethodData::Card(ccard)) = payment_method_data { + if let Some(PaymentMethodData::Card(ccard)) = payment_method_data { format!( "var saveCardDirectly={{cvv: \"{}\",amount: {},currency: \"{}\"}};", ccard.card_cvc.peek(), diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index d6eacd8a541f..490412132aa6 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -9,5 +9,5 @@ pub use hyperswitch_domain_models::payment_method_data::{ SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, WeChatPayQr,TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedBankRedirectValue1, - TokenizedBankRedirectValue2 + TokenizedBankRedirectValue2, }; From 55eaaaee12d7f1de7945bd14552bd75a6b7bc417 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 2 Aug 2024 02:42:37 +0530 Subject: [PATCH 05/59] fix formatting --- .../src/connectors/bambora/transformers.rs | 2 +- .../src/connectors/fiserv/transformers.rs | 2 +- .../src/connectors/helcim/transformers.rs | 4 +- .../src/connectors/stax/transformers.rs | 12 ++--- .../src/payment_method_data.rs | 10 ++-- .../router/src/connector/aci/transformers.rs | 2 +- .../src/connector/adyen/transformers.rs | 4 +- .../src/connector/airwallex/transformers.rs | 2 +- .../connector/authorizedotnet/transformers.rs | 6 +-- .../connector/bankofamerica/transformers.rs | 4 +- .../src/connector/billwerk/transformers.rs | 2 +- .../src/connector/bluesnap/transformers.rs | 4 +- .../router/src/connector/boku/transformers.rs | 2 +- .../braintree_graphql_transformers.rs | 6 +-- .../src/connector/braintree/transformers.rs | 2 +- .../src/connector/checkout/transformers.rs | 4 +- .../src/connector/cryptopay/transformers.rs | 2 +- .../src/connector/cybersource/transformers.rs | 10 ++-- .../src/connector/datatrans/transformers.rs | 2 +- .../src/connector/dlocal/transformers.rs | 2 +- .../src/connector/forte/transformers.rs | 2 +- .../src/connector/globepay/transformers.rs | 2 +- .../src/connector/gocardless/transformers.rs | 4 +- .../src/connector/iatapay/transformers.rs | 2 +- .../src/connector/itaubank/transformers.rs | 2 +- crates/router/src/connector/klarna.rs | 2 +- .../src/connector/mifinity/transformers.rs | 2 +- .../connector/multisafepay/transformers.rs | 4 +- .../src/connector/nexinets/transformers.rs | 10 ++-- .../router/src/connector/nmi/transformers.rs | 2 +- .../router/src/connector/noon/transformers.rs | 2 +- .../src/connector/nuvei/transformers.rs | 2 +- .../src/connector/opayo/transformers.rs | 2 +- .../src/connector/payeezy/transformers.rs | 10 ++-- .../src/connector/payme/transformers.rs | 6 +-- .../src/connector/paypal/transformers.rs | 2 +- .../src/connector/placetopay/transformers.rs | 2 +- .../src/connector/powertranz/transformers.rs | 2 +- .../src/connector/razorpay/transformers.rs | 2 +- .../src/connector/shift4/transformers.rs | 2 +- .../src/connector/square/transformers.rs | 4 +- .../src/connector/stripe/transformers.rs | 16 +++--- .../src/connector/trustpay/transformers.rs | 2 +- .../router/src/connector/tsys/transformers.rs | 2 +- .../router/src/connector/volt/transformers.rs | 2 +- .../src/connector/worldline/transformers.rs | 2 +- .../src/connector/worldpay/transformers.rs | 12 +++-- .../router/src/connector/zen/transformers.rs | 2 +- .../router/src/connector/zsl/transformers.rs | 2 +- .../src/core/authentication/transformers.rs | 3 +- crates/router/src/core/blocklist/utils.rs | 8 +-- .../router/src/core/payment_methods/vault.rs | 5 +- crates/router/src/core/payments.rs | 6 +-- crates/router/src/core/payments/helpers.rs | 50 ++++++++++--------- .../payments/operations/payment_confirm.rs | 39 +++++++++------ .../payments/operations/payment_create.rs | 10 ++-- .../router/src/core/payments/transformers.rs | 1 - crates/router/src/services/api.rs | 23 ++++----- crates/router/src/types/domain/payments.rs | 8 +-- 59 files changed, 179 insertions(+), 166 deletions(-) diff --git a/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs b/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs index 57bdac8b7f8b..8499aecaed77 100644 --- a/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs @@ -196,7 +196,7 @@ impl TryFrom> for Bambora | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("bambora"), ) diff --git a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs index 0cfcbfe56e16..b2cbd570b4f0 100644 --- a/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/fiserv/transformers.rs @@ -198,7 +198,7 @@ impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservP | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("fiserv"), diff --git a/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs b/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs index 5558cd507794..90380d023215 100644 --- a/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/helcim/transformers.rs @@ -180,7 +180,7 @@ impl TryFrom<&SetupMandateRouterData> for HelcimVerifyRequest { | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( crate::utils::get_unimplemented_payment_method_error_message("Helcim"), ))?, @@ -274,7 +274,7 @@ impl TryFrom<&HelcimRouterData<&PaymentsAuthorizeRouterData>> for HelcimPayments | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( crate::utils::get_unimplemented_payment_method_error_message("Helcim"), ))?, diff --git a/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs b/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs index 9e7dd968fd0f..f7b2016a3705 100644 --- a/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs +++ b/crates/hyperswitch_connectors/src/connectors/stax/transformers.rs @@ -117,7 +117,7 @@ impl TryFrom<&StaxRouterData<&types::PaymentsAuthorizeRouterData>> for StaxPayme | PaymentMethodData::CardRedirect(_) | PaymentMethodData::Upi(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Stax"), ))?, @@ -268,12 +268,10 @@ impl TryFrom<&types::TokenizationRouterData> for StaxTokenRequest { | PaymentMethodData::CardRedirect(_) | PaymentMethodData::Upi(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) - | PaymentMethodData::NetworkToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Stax"), - ))? - } + | PaymentMethodData::CardToken(_) + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Stax"), + ))?, } } } diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 203a8e2e72ab..cea72fc45d8a 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -1,9 +1,12 @@ use api_models::payments::ExtendedCardInfo; -use common_utils::{pii::{self, Email}, id_type}; +use common_enums::enums as api_enums; +use common_utils::{ + id_type, + pii::{self, Email}, +}; use masking::Secret; use serde::{Deserialize, Serialize}; use time::Date; -use common_enums::enums as api_enums; // We need to derive Serialize and Deserialize because some parts of payment method data are being // stored in the database as serde_json::Value @@ -1022,7 +1025,6 @@ pub struct TokenizedCardValue2 { pub payment_method_id: Option, } - #[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TokenizedWalletValue1 { pub data: WalletData, @@ -1256,4 +1258,4 @@ impl From for ExtendedCardInfo { bank_code: value.bank_code, } } -} \ No newline at end of file +} diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index fb5f76f1b11c..933b4326350e 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -448,7 +448,7 @@ impl TryFrom<&AciRouterData<&types::PaymentsAuthorizeRouterData>> for AciPayment | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Aci"), diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 3304496be496..71c5bb438d99 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1556,7 +1556,7 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyen"), @@ -2559,7 +2559,7 @@ impl<'a> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotSupported { message: "Network tokenization for payment method".to_string(), diff --git a/crates/router/src/connector/airwallex/transformers.rs b/crates/router/src/connector/airwallex/transformers.rs index a83fb6fbd9d8..4d84f64534e8 100644 --- a/crates/router/src/connector/airwallex/transformers.rs +++ b/crates/router/src/connector/airwallex/transformers.rs @@ -205,7 +205,7 @@ impl TryFrom<&AirwallexRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("airwallex"), diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 57f724920f79..14f61d1ded79 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -343,7 +343,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CreateCustomerProfileRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("authorizedotnet"), @@ -523,7 +523,7 @@ impl TryFrom<&AuthorizedotnetRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( @@ -583,7 +583,7 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("authorizedotnet"), diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index 23c018d41e7c..46a522c73782 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -322,7 +322,7 @@ impl TryFrom<&types::SetupMandateRouterData> for BankOfAmericaPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("BankOfAmerica"), @@ -1074,7 +1074,7 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( diff --git a/crates/router/src/connector/billwerk/transformers.rs b/crates/router/src/connector/billwerk/transformers.rs index 93289aa767cf..c6f2be9dbfc3 100644 --- a/crates/router/src/connector/billwerk/transformers.rs +++ b/crates/router/src/connector/billwerk/transformers.rs @@ -103,7 +103,7 @@ impl TryFrom<&types::TokenizationRouterData> for BillwerkTokenRequest { | domain::payments::PaymentMethodData::Voucher(_) | domain::payments::PaymentMethodData::GiftCard(_) | domain::payments::PaymentMethodData::OpenBanking(_) - | domain::payments::PaymentMethodData::CardToken(_) + | domain::payments::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("billwerk"), diff --git a/crates/router/src/connector/bluesnap/transformers.rs b/crates/router/src/connector/bluesnap/transformers.rs index 431cab84b272..6ebcdf0e765b 100644 --- a/crates/router/src/connector/bluesnap/transformers.rs +++ b/crates/router/src/connector/bluesnap/transformers.rs @@ -228,7 +228,7 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( "Selected payment method via Token flow through bluesnap".to_string(), @@ -394,7 +394,7 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> for Blues | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("bluesnap"), diff --git a/crates/router/src/connector/boku/transformers.rs b/crates/router/src/connector/boku/transformers.rs index c9538ea2f54c..5ff12d81bbfb 100644 --- a/crates/router/src/connector/boku/transformers.rs +++ b/crates/router/src/connector/boku/transformers.rs @@ -93,7 +93,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BokuPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("boku"), diff --git a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs index ed2b0e84a440..578ebd9c16aa 100644 --- a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs +++ b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs @@ -210,7 +210,7 @@ impl TryFrom<&BraintreeRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), @@ -998,7 +998,7 @@ impl TryFrom<&types::TokenizationRouterData> for BraintreeTokenRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), @@ -1590,7 +1590,7 @@ fn get_braintree_redirect_form( | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => Err( errors::ConnectorError::NotImplemented("given payment method".to_owned()), )?, diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index de690d9e90bd..e40bc7b34dd2 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -176,7 +176,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index 4771a29ad5a2..223256155cfd 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -133,7 +133,7 @@ impl TryFrom<&types::TokenizationRouterData> for TokenRequest { | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("checkout"), @@ -372,7 +372,7 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("checkout"), )) diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs index ac9c9d4752ea..cd22890ca526 100644 --- a/crates/router/src/connector/cryptopay/transformers.rs +++ b/crates/router/src/connector/cryptopay/transformers.rs @@ -79,7 +79,7 @@ impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("CryptoPay"), diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index cf973649f349..704507b85a6e 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -215,7 +215,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -1401,7 +1401,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -1509,7 +1509,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -2229,7 +2229,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsPreProcessingRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -2340,7 +2340,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), diff --git a/crates/router/src/connector/datatrans/transformers.rs b/crates/router/src/connector/datatrans/transformers.rs index 6848df883194..afc9288b6564 100644 --- a/crates/router/src/connector/datatrans/transformers.rs +++ b/crates/router/src/connector/datatrans/transformers.rs @@ -188,7 +188,7 @@ impl TryFrom<&DatatransRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message("Datatrans"), diff --git a/crates/router/src/connector/dlocal/transformers.rs b/crates/router/src/connector/dlocal/transformers.rs index e07b45366d63..a613cdb2054e 100644 --- a/crates/router/src/connector/dlocal/transformers.rs +++ b/crates/router/src/connector/dlocal/transformers.rs @@ -167,7 +167,7 @@ impl TryFrom<&DlocalRouterData<&types::PaymentsAuthorizeRouterData>> for DlocalP | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( crate::connector::utils::get_unimplemented_payment_method_error_message( diff --git a/crates/router/src/connector/forte/transformers.rs b/crates/router/src/connector/forte/transformers.rs index 6f14541db88a..b039282b719f 100644 --- a/crates/router/src/connector/forte/transformers.rs +++ b/crates/router/src/connector/forte/transformers.rs @@ -116,7 +116,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for FortePaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Forte"), diff --git a/crates/router/src/connector/globepay/transformers.rs b/crates/router/src/connector/globepay/transformers.rs index 589f5b721b29..df89d3deb9fd 100644 --- a/crates/router/src/connector/globepay/transformers.rs +++ b/crates/router/src/connector/globepay/transformers.rs @@ -74,7 +74,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("globepay"), diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index 843352867d0d..572e4efa33fd 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -247,7 +247,7 @@ impl TryFrom<&types::TokenizationRouterData> for CustomerBankAccount { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Gocardless"), @@ -418,7 +418,7 @@ impl TryFrom<&types::SetupMandateRouterData> for GocardlessMandateRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( "Setup Mandate flow for selected payment method through Gocardless".to_string(), diff --git a/crates/router/src/connector/iatapay/transformers.rs b/crates/router/src/connector/iatapay/transformers.rs index 8221ff23de16..0b48d793a95c 100644 --- a/crates/router/src/connector/iatapay/transformers.rs +++ b/crates/router/src/connector/iatapay/transformers.rs @@ -205,7 +205,7 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("iatapay"), diff --git a/crates/router/src/connector/itaubank/transformers.rs b/crates/router/src/connector/itaubank/transformers.rs index de788054a9ba..9af0a25dd862 100644 --- a/crates/router/src/connector/itaubank/transformers.rs +++ b/crates/router/src/connector/itaubank/transformers.rs @@ -119,7 +119,7 @@ impl TryFrom<&ItaubankRouterData<&types::PaymentsAuthorizeRouterData>> for Itaub | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( "Selected payment method through itaubank".to_string(), diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs index 41048edfb63f..0e6fab805848 100644 --- a/crates/router/src/connector/klarna.rs +++ b/crates/router/src/connector/klarna.rs @@ -652,7 +652,7 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(report!(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message( diff --git a/crates/router/src/connector/mifinity/transformers.rs b/crates/router/src/connector/mifinity/transformers.rs index 4781ecba115c..bdd67e525d4b 100644 --- a/crates/router/src/connector/mifinity/transformers.rs +++ b/crates/router/src/connector/mifinity/transformers.rs @@ -197,7 +197,7 @@ impl TryFrom<&MifinityRouterData<&types::PaymentsAuthorizeRouterData>> for Mifin | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Mifinity"), diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 7c74f00560b8..0fd483a421d2 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -607,7 +607,7 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("multisafepay"), @@ -789,7 +789,7 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("multisafepay"), diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index 9692d39725be..ec44f8381182 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -626,10 +626,12 @@ fn get_payment_details_and_product( | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("nexinets"), - ))?, + | PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("nexinets"), + ))? + } } } diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs index d639c99c8684..069a51ae52e7 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -585,7 +585,7 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nmi"), diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index 299acc523a3e..b7df6829da47 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -353,7 +353,7 @@ impl TryFrom<&NoonRouterData<&types::PaymentsAuthorizeRouterData>> for NoonPayme | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( conn_utils::get_unimplemented_payment_method_error_message("Noon"), diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs index 0d9ef8f3366d..e67116408267 100644 --- a/crates/router/src/connector/nuvei/transformers.rs +++ b/crates/router/src/connector/nuvei/transformers.rs @@ -996,7 +996,7 @@ where | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nuvei"), diff --git a/crates/router/src/connector/opayo/transformers.rs b/crates/router/src/connector/opayo/transformers.rs index 7021bbd89207..dcb4e201e8bb 100644 --- a/crates/router/src/connector/opayo/transformers.rs +++ b/crates/router/src/connector/opayo/transformers.rs @@ -57,7 +57,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for OpayoPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Opayo"), diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index 5e8c941693b0..0bb10c9845a3 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -261,10 +261,12 @@ fn get_payment_method_data( | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("Payeezy"), - ))?, + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("Payeezy"), + ))? + } } } diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index f6591a532221..b037114cbddf 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -430,7 +430,7 @@ impl TryFrom<&PaymentMethodData> for SalePaymentMethod { | PaymentMethodData::Upi(_) | PaymentMethodData::Voucher(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } @@ -676,7 +676,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest { | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) - | PaymentMethodData::CardToken(_) + | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("payme"), ))?, @@ -779,7 +779,7 @@ impl TryFrom<&types::TokenizationRouterData> for CaptureBuyerRequest { | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) - | PaymentMethodData::NetworkToken(_) =>{ + | PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) } } diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs index 88d957d37e6c..24b2f2b88e98 100644 --- a/crates/router/src/connector/paypal/transformers.rs +++ b/crates/router/src/connector/paypal/transformers.rs @@ -552,7 +552,7 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP | domain::PaymentMethodData::Crypto(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Paypal"), diff --git a/crates/router/src/connector/placetopay/transformers.rs b/crates/router/src/connector/placetopay/transformers.rs index 47ffdb2f5546..919fe25bdd8f 100644 --- a/crates/router/src/connector/placetopay/transformers.rs +++ b/crates/router/src/connector/placetopay/transformers.rs @@ -142,7 +142,7 @@ impl TryFrom<&PlacetopayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Placetopay"), diff --git a/crates/router/src/connector/powertranz/transformers.rs b/crates/router/src/connector/powertranz/transformers.rs index de0657ee011b..97c2994b4c77 100644 --- a/crates/router/src/connector/powertranz/transformers.rs +++ b/crates/router/src/connector/powertranz/transformers.rs @@ -117,7 +117,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PowertranzPaymentsRequest | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotSupported { message: utils::SELECTED_PAYMENT_METHOD.to_string(), diff --git a/crates/router/src/connector/razorpay/transformers.rs b/crates/router/src/connector/razorpay/transformers.rs index 845e5ef28832..69d6bdce3848 100644 --- a/crates/router/src/connector/razorpay/transformers.rs +++ b/crates/router/src/connector/razorpay/transformers.rs @@ -399,7 +399,7 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } diff --git a/crates/router/src/connector/shift4/transformers.rs b/crates/router/src/connector/shift4/transformers.rs index b34645eafb70..13c0395cf9f4 100644 --- a/crates/router/src/connector/shift4/transformers.rs +++ b/crates/router/src/connector/shift4/transformers.rs @@ -248,7 +248,7 @@ where | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Shift4"), diff --git a/crates/router/src/connector/square/transformers.rs b/crates/router/src/connector/square/transformers.rs index d07164b93f1d..db7058f33847 100644 --- a/crates/router/src/connector/square/transformers.rs +++ b/crates/router/src/connector/square/transformers.rs @@ -175,7 +175,7 @@ impl TryFrom<&types::TokenizationRouterData> for SquareTokenRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Square"), @@ -293,7 +293,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for SquarePaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Square"), diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index ba6da9f55575..6b26818c2927 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -1334,11 +1334,13 @@ fn create_stripe_payment_method( | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::MandatePayment | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( - connector_util::get_unimplemented_payment_method_error_message("stripe"), - ) - .into()), + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { + Err(errors::ConnectorError::NotImplemented( + connector_util::get_unimplemented_payment_method_error_message("stripe"), + ) + .into()) + } } } @@ -1712,7 +1714,7 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent | domain::payments::PaymentMethodData::Voucher(_) | domain::payments::PaymentMethodData::GiftCard(_) | domain::payments::PaymentMethodData::OpenBanking(_) - | domain::payments::PaymentMethodData::CardToken(_) + | domain::payments::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotSupported { message: "Network tokenization for payment method".to_string(), @@ -3755,7 +3757,7 @@ impl | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), diff --git a/crates/router/src/connector/trustpay/transformers.rs b/crates/router/src/connector/trustpay/transformers.rs index 47a629cda955..d05db4d28ab5 100644 --- a/crates/router/src/connector/trustpay/transformers.rs +++ b/crates/router/src/connector/trustpay/transformers.rs @@ -434,7 +434,7 @@ impl TryFrom<&TrustpayRouterData<&types::PaymentsAuthorizeRouterData>> for Trust | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("trustpay"), diff --git a/crates/router/src/connector/tsys/transformers.rs b/crates/router/src/connector/tsys/transformers.rs index 1cd0188f0c5f..347a95151d50 100644 --- a/crates/router/src/connector/tsys/transformers.rs +++ b/crates/router/src/connector/tsys/transformers.rs @@ -77,7 +77,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for TsysPaymentsRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("tsys"), diff --git a/crates/router/src/connector/volt/transformers.rs b/crates/router/src/connector/volt/transformers.rs index 91f4f3304d33..451c70279cc4 100644 --- a/crates/router/src/connector/volt/transformers.rs +++ b/crates/router/src/connector/volt/transformers.rs @@ -141,7 +141,7 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Volt"), diff --git a/crates/router/src/connector/worldline/transformers.rs b/crates/router/src/connector/worldline/transformers.rs index a76459af23cf..e0278dc4ccb8 100644 --- a/crates/router/src/connector/worldline/transformers.rs +++ b/crates/router/src/connector/worldline/transformers.rs @@ -250,7 +250,7 @@ impl | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("worldline"), diff --git a/crates/router/src/connector/worldpay/transformers.rs b/crates/router/src/connector/worldpay/transformers.rs index 32af7feb6198..de82e11548ca 100644 --- a/crates/router/src/connector/worldpay/transformers.rs +++ b/crates/router/src/connector/worldpay/transformers.rs @@ -109,11 +109,13 @@ fn fetch_payment_instrument( | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("worldpay"), - ) - .into()), + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("worldpay"), + ) + .into()) + } } } diff --git a/crates/router/src/connector/zen/transformers.rs b/crates/router/src/connector/zen/transformers.rs index 7d7104e4bd80..d2e8ce40283b 100644 --- a/crates/router/src/connector/zen/transformers.rs +++ b/crates/router/src/connector/zen/transformers.rs @@ -695,7 +695,7 @@ impl TryFrom<&ZenRouterData<&types::PaymentsAuthorizeRouterData>> for ZenPayment | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::CardToken(_) | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Zen"), diff --git a/crates/router/src/connector/zsl/transformers.rs b/crates/router/src/connector/zsl/transformers.rs index a4c461f241ff..60aed55b1f49 100644 --- a/crates/router/src/connector/zsl/transformers.rs +++ b/crates/router/src/connector/zsl/transformers.rs @@ -182,7 +182,7 @@ impl TryFrom<&ZslRouterData<&types::PaymentsAuthorizeRouterData>> for ZslPayment | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) + | domain::PaymentMethodData::NetworkToken(_) | domain::PaymentMethodData::OpenBanking(_) => { Err(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message( diff --git a/crates/router/src/core/authentication/transformers.rs b/crates/router/src/core/authentication/transformers.rs index 88c31581807c..1b8b3a6aab1a 100644 --- a/crates/router/src/core/authentication/transformers.rs +++ b/crates/router/src/core/authentication/transformers.rs @@ -11,9 +11,8 @@ use crate::{ payments::helpers as payments_helpers, }, types::{ - self, storage, + self, domain, storage, transformers::{ForeignFrom, ForeignTryFrom}, - domain, }, utils::ext_traits::OptionExt, }; diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs index 6a101f506f36..555be16a1c6e 100644 --- a/crates/router/src/core/blocklist/utils.rs +++ b/crates/router/src/core/blocklist/utils.rs @@ -332,9 +332,7 @@ where .payment_method_data .as_ref() .and_then(|pm_data| match pm_data { - domain::PaymentMethodData::Card(card) => { - Some(card.card_number.get_card_isin()) - } + domain::PaymentMethodData::Card(card) => Some(card.card_number.get_card_isin()), _ => None, }); @@ -451,9 +449,7 @@ pub async fn generate_payment_fingerprint( let merchant_fingerprint_secret = get_merchant_fingerprint_secret(state, &merchant_id).await?; Ok( - if let Some(domain::PaymentMethodData::Card(card)) = - payment_method_data.as_ref() - { + if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data.as_ref() { generate_fingerprint( state, StrongSecret::new(card.card_number.get_card_no()), diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index 3932a5a23e4f..918522e7a9e5 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -56,10 +56,7 @@ impl Vaultable for domain::Card { card_number: self.card_number.peek().clone(), exp_year: self.card_exp_year.peek().clone(), exp_month: self.card_exp_month.peek().clone(), - nickname: self - .nick_name - .as_ref() - .map(|name| name.peek().clone()), + nickname: self.nick_name.as_ref().map(|name| name.peek().clone()), card_last_four: None, card_token: None, }; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index ec60bf34757a..ac5adf6cf1a6 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2196,9 +2196,9 @@ where .await?; match payment_data.payment_method_data.clone() { - Some(domain::PaymentMethodData::OpenBanking( - domain::OpenBankingData::OpenBankingPIS { .. }, - )) => { + Some(domain::PaymentMethodData::OpenBanking(domain::OpenBankingData::OpenBankingPIS { + .. + })) => { if connector.connector_name == router_types::Connector::Plaid { router_data = router_data.postprocessing_steps(state, connector).await?; let token = if let Ok(ref res) = router_data.response { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 9f4bbd5580f0..4f3d9b381336 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -21,7 +21,10 @@ use diesel_models::enums::{self}; use error_stack::{report, ResultExt}; use futures::future::Either; use hyperswitch_domain_models::{ - mandates::MandateData, payment_method_data::GetPaymentMethodType, payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent}, router_data::KlarnaSdkResponse + mandates::MandateData, + payment_method_data::GetPaymentMethodType, + payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent}, + router_data::KlarnaSdkResponse, }; use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject}; use josekit::jwe; @@ -1756,7 +1759,8 @@ pub async fn retrieve_payment_method_with_temporary_token( // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked // from payment_method_data.card_token object - let name_on_card = if let Some(name) = card.nick_name.clone() { //todo! + let name_on_card = if let Some(name) = card.nick_name.clone() { + //todo! if name.clone().expose().is_empty() { card_token_data .and_then(|token_data| { @@ -1765,7 +1769,7 @@ pub async fn retrieve_payment_method_with_temporary_token( }) .or(Some(name)) } else { - card.nick_name.clone() //todo! + card.nick_name.clone() //todo! } } else { card_token_data.and_then(|token_data| { @@ -3858,7 +3862,8 @@ pub async fn get_additional_payment_data( profile_id: &str, ) -> Option { match pm_data { - domain::PaymentMethodData::Card(card_data) => { //todo! + domain::PaymentMethodData::Card(card_data) => { + //todo! let card_isin = Some(card_data.card_number.get_card_isin()); let enable_extended_bin =db .find_config_by_key_unwrap_or( @@ -3950,33 +3955,32 @@ pub async fn get_additional_payment_data( })) } } - domain::PaymentMethodData::BankRedirect(bank_redirect_data) => { - match bank_redirect_data { - domain::BankRedirectData::Eps { bank_name, .. } => { - Some(api_models::payments::AdditionalPaymentData::BankRedirect { - bank_name: bank_name.to_owned(), - }) - } - domain::BankRedirectData::Ideal { bank_name, .. } => { - Some(api_models::payments::AdditionalPaymentData::BankRedirect { - bank_name: bank_name.to_owned(), - }) - } - _ => Some(api_models::payments::AdditionalPaymentData::BankRedirect { bank_name: None }) + domain::PaymentMethodData::BankRedirect(bank_redirect_data) => match bank_redirect_data { + domain::BankRedirectData::Eps { bank_name, .. } => { + Some(api_models::payments::AdditionalPaymentData::BankRedirect { + bank_name: bank_name.to_owned(), + }) } - } + domain::BankRedirectData::Ideal { bank_name, .. } => { + Some(api_models::payments::AdditionalPaymentData::BankRedirect { + bank_name: bank_name.to_owned(), + }) + } + _ => { + Some(api_models::payments::AdditionalPaymentData::BankRedirect { bank_name: None }) + } + }, domain::PaymentMethodData::Wallet(wallet) => match wallet { domain::WalletData::ApplePay(apple_pay_wallet_data) => { Some(api_models::payments::AdditionalPaymentData::Wallet { - apple_pay: Some(api_models::payments::ApplepayPaymentMethod{ + apple_pay: Some(api_models::payments::ApplepayPaymentMethod { display_name: apple_pay_wallet_data.payment_method.display_name.clone(), network: apple_pay_wallet_data.payment_method.network.clone(), pm_type: apple_pay_wallet_data.payment_method.pm_type.clone(), - - }) + }), }) } - _ => Some(api_models::payments::AdditionalPaymentData::Wallet { apple_pay: None }) + _ => Some(api_models::payments::AdditionalPaymentData::Wallet { apple_pay: None }), }, domain::PaymentMethodData::PayLater(_) => { Some(api_models::payments::AdditionalPaymentData::PayLater { klarna_sdk: None }) @@ -4017,7 +4021,7 @@ pub async fn get_additional_payment_data( domain::PaymentMethodData::OpenBanking(_) => { Some(api_models::payments::AdditionalPaymentData::OpenBanking {}) } - domain::PaymentMethodData::NetworkToken(_) => None + domain::PaymentMethodData::NetworkToken(_) => None, } } diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index d685614ecda2..9c248958b66b 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -458,7 +458,7 @@ impl GetTracker, api::PaymentsRequest> for Pa async move { Ok(n_request_payment_method_data .async_and_then(|payment_method_data| async move { - helpers::get_additional_payment_data( + helpers::get_additional_payment_data( &payment_method_data.into(), store.as_ref(), profile_id.as_ref(), @@ -628,20 +628,23 @@ impl GetTracker, api::PaymentsRequest> for Pa .as_ref() .map(|payment_method_billing| payment_method_billing.address_id.clone()); - let address = PaymentAddress::new( - shipping_address.as_ref().map(From::from), - billing_address.as_ref().map(From::from), - payment_method_billing.as_ref().map(From::from), - business_profile.use_billing_as_payment_method_billing, - ); - - let payment_method_data_billing = request - .payment_method_data - .as_ref() - .and_then(|pmd| pmd.payment_method_data.as_ref()) - .and_then(|payment_method_data_billing| payment_method_data_billing.get_billing_address()); - - let unified_address = address.unify_with_payment_method_data_billing(payment_method_data_billing); + let address = PaymentAddress::new( + shipping_address.as_ref().map(From::from), + billing_address.as_ref().map(From::from), + payment_method_billing.as_ref().map(From::from), + business_profile.use_billing_as_payment_method_billing, + ); + + let payment_method_data_billing = request + .payment_method_data + .as_ref() + .and_then(|pmd| pmd.payment_method_data.as_ref()) + .and_then(|payment_method_data_billing| { + payment_method_data_billing.get_billing_address() + }); + + let unified_address = + address.unify_with_payment_method_data_billing(payment_method_data_billing); let payment_data = PaymentData { flow: PhantomData, @@ -1130,7 +1133,11 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen }; let customer_details = payment_data.payment_intent.customer_details.clone(); - let business_sub_label = payment_data.payment_attempt.clone().business_sub_label.clone(); + let business_sub_label = payment_data + .payment_attempt + .clone() + .business_sub_label + .clone(); let authentication_type = payment_data.payment_attempt.clone().authentication_type; let (shipping_address_id, billing_address_id, payment_method_billing_address_id) = ( diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 145641e591b1..01f6c1e31662 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -1,7 +1,8 @@ use std::marker::PhantomData; use api_models::{ - enums::FrmSuggestion, mandates::RecurringDetails, payment_methods::PaymentMethodsData, payments::GetAddressFromPaymentMethodData, + enums::FrmSuggestion, mandates::RecurringDetails, payment_methods::PaymentMethodsData, + payments::GetAddressFromPaymentMethodData, }; use async_trait::async_trait; use common_utils::{ @@ -433,9 +434,12 @@ impl GetTracker, api::PaymentsRequest> for Pa .payment_method_data .as_ref() .and_then(|pmd| pmd.payment_method_data.as_ref()) - .and_then(|payment_method_data_billing| payment_method_data_billing.get_billing_address()); + .and_then(|payment_method_data_billing| { + payment_method_data_billing.get_billing_address() + }); - let unified_address = address.unify_with_payment_method_data_billing(payment_method_data_billing); + let unified_address = + address.unify_with_payment_method_data_billing(payment_method_data_billing); let payment_data = PaymentData { flow: PhantomData, diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index ff64f7c7a2d1..c0d83bb86d74 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -135,7 +135,6 @@ where Some(merchant_connector_account), ); - router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_account.get_id().clone(), diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 00a49dcda4c3..5f75a6a8f92c 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -33,8 +33,8 @@ pub use hyperswitch_domain_models::{ GenericLinks, PaymentLinkAction, PaymentLinkFormData, PaymentLinkStatusData, RedirectionFormData, }, - router_response_types::RedirectForm, payment_method_data::PaymentMethodData, + router_response_types::RedirectForm, }; pub use hyperswitch_interfaces::{ api::{ @@ -1327,17 +1327,16 @@ pub fn build_redirection_form( RedirectForm::BlueSnap { payment_fields_token, } => { - let card_details = - if let Some(PaymentMethodData::Card(ccard)) = payment_method_data { - format!( - "var saveCardDirectly={{cvv: \"{}\",amount: {},currency: \"{}\"}};", - ccard.card_cvc.peek(), - amount, - currency - ) - } else { - "".to_string() - }; + let card_details = if let Some(PaymentMethodData::Card(ccard)) = payment_method_data { + format!( + "var saveCardDirectly={{cvv: \"{}\",amount: {},currency: \"{}\"}};", + ccard.card_cvc.peek(), + amount, + currency + ) + } else { + "".to_string() + }; let bluesnap_sdk_url = config.connectors.bluesnap.secondary_base_url; maud::html! { (maud::DOCTYPE) diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index 490412132aa6..1cf36bc44281 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -6,8 +6,8 @@ pub use hyperswitch_domain_models::payment_method_data::{ GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData, PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, - SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, - WalletData, WeChatPayQr,TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, - TokenizedWalletValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedBankRedirectValue1, - TokenizedBankRedirectValue2, + SwishQrData, TokenizedBankRedirectValue1, TokenizedBankRedirectValue2, + TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedCardValue1, + TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, TouchNGoRedirection, + UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, WeChatPayQr, }; From b4c55d283eb86e2c20fca02cf627731e2f5f0612 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Sun, 4 Aug 2024 20:23:55 +0530 Subject: [PATCH 06/59] add network tokenization support for cybersource --- crates/api_models/src/payments.rs | 1 + .../src/connector/cybersource/transformers.rs | 105 +++++++++++++++++- crates/router/src/connector/utils.rs | 9 ++ .../payment_methods/network_tokenization.rs | 32 +++--- crates/router/src/core/payments/helpers.rs | 78 +++++++------ crates/router/src/types/domain/payments.rs | 2 +- 6 files changed, 169 insertions(+), 58 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 570b76f3b510..10cbe6e703e6 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -984,6 +984,7 @@ pub struct MandateIds { pub enum MandateReferenceId { ConnectorMandateId(ConnectorMandateReferenceId), // mandate_id send by connector NetworkMandateId(String), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns + } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 704507b85a6e..53689ac774b6 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -16,9 +16,7 @@ use serde_json::Value; use crate::connector::utils::PayoutsData; use crate::{ connector::utils::{ - self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData, - PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData, - PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData, + self, AddressDetailsData, ApplePayDecrypt, CardData, NetworkTokenData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData }, consts, core::errors, @@ -344,6 +342,22 @@ pub struct CaptureOptions { total_capture_count: u32, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NetworkTokenizedCard { + number: cards::CardNumber, + expiration_month: Secret, + expiration_year: Secret, + cryptogram: Secret, + transaction_type: String, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NetworkTokenPaymentInformation { + tokenized_card: NetworkTokenizedCard, +} + #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct CardPaymentInformation { @@ -409,6 +423,7 @@ pub enum PaymentInformation { ApplePay(Box), ApplePayToken(Box), MandatePayment(Box), + NetworkToken(Box), } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -1052,6 +1067,86 @@ impl } } +impl + TryFrom<( + &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, + domain::NetworkTokenData, + )> for CybersourcePaymentsRequest +{ + type Error = error_stack::Report; + fn try_from( + (item, token_data): ( + &CybersourceRouterData<&types::PaymentsAuthorizeRouterData>, + domain::NetworkTokenData, + ), + ) -> Result { + let email = item.router_data.request.get_email()?; + let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?; + let order_information = OrderInformationWithBill::from((item, Some(bill_to))); + + let card_issuer = token_data.get_card_issuer(); + let card_type = match card_issuer { + Ok(issuer) => Some(String::from(issuer)), + Err(_) => None, + }; + + let payment_information = PaymentInformation::NetworkToken(Box::new(NetworkTokenPaymentInformation { + tokenized_card: NetworkTokenizedCard { + number: token_data.token_number, + expiration_month: token_data.token_exp_month, + expiration_year: token_data.token_exp_year, + cryptogram: token_data.token_cryptogram, + transaction_type: "1".to_string(), + }, + })); + + let processing_information = ProcessingInformation::try_from((item, None, card_type))?; + let client_reference_information = ClientReferenceInformation::from(item); + let merchant_defined_information = item + .router_data + .request + .metadata + .clone() + .map(Vec::::foreign_from); + + let consumer_authentication_information = item + .router_data + .request + .authentication_data + .as_ref() + .map(|authn_data| { + let (ucaf_authentication_data, cavv) = + if token_data.card_network == Some(common_enums::CardNetwork::Mastercard) { + (Some(Secret::new(authn_data.cavv.clone())), None) + } else { + (None, Some(authn_data.cavv.clone())) + }; + CybersourceConsumerAuthInformation { + ucaf_collection_indicator: None, + cavv, + ucaf_authentication_data, + xid: Some(authn_data.threeds_server_transaction_id.clone()), + directory_server_transaction_id: authn_data + .ds_trans_id + .clone() + .map(Secret::new), + specification_version: None, + pa_specification_version: Some(authn_data.message_version.clone()), + veres_enrolled: Some("Y".to_string()), + } + }); + + Ok(Self { + processing_information, + payment_information, + order_information, + client_reference_information, + consumer_authentication_information, + merchant_defined_information, + }) + } +} + impl TryFrom<( &CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData>, @@ -1389,6 +1484,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> )?; Self::try_from((item, connector_mandate_id)) } + domain::PaymentMethodData::NetworkToken(token_data) => Self::try_from((item, token_data)), domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::PayLater(_) | domain::PaymentMethodData::BankRedirect(_) @@ -1401,8 +1497,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { + | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), ) diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index d5c01503ed83..a8ae0fc4df6d 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -2974,3 +2974,12 @@ pub fn get_refund_integrity_object( refund_amount: refund_amount_in_minor_unit, }) } +pub trait NetworkTokenData { + fn get_card_issuer(&self) -> Result; +} + +impl NetworkTokenData for domain::NetworkTokenData { + fn get_card_issuer(&self) -> Result { + get_card_issuer(self.token_number.peek()) + } +} diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 183ca0bb532c..f6c3f4f06122 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -28,6 +28,9 @@ use strum::IntoEnumIterator; use cards::CardNumber; use josekit::jwe; use serde::{Deserialize, Serialize}; +use hyperswitch_domain_models::{ + payment_method_data::NetworkTokenData, +}; #[cfg(feature = "payouts")] use crate::{ @@ -242,8 +245,8 @@ pub struct GetCardToken { } #[derive(Debug, Serialize, Deserialize)] pub struct AuthenticationDetails { - cryptogram: String, - token: Secret, + cryptogram: Secret, + token: CardNumber, } #[derive(Debug, Serialize, Deserialize)] pub struct TokenResponse { @@ -255,7 +258,7 @@ pub async fn get_token_from_tokenization_service( merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, pm_id: String, //to fetch from pm table -) -> errors::RouterResult { +) -> errors::RouterResult { let db = state.store.as_ref(); let key = key_store.key.get_inner().peek(); let pm_data = db @@ -316,26 +319,25 @@ pub async fn get_token_from_tokenization_service( _ => None, }); println!("card_decrypted: {:?}", card_decrypted); - let card_data = Card { - card_number: card_decrypted - .clone() - .unwrap() - .card_number - .unwrap_or_default(), - name_on_card: card_decrypted.clone().unwrap().card_holder_name, - card_exp_month: card_decrypted + let card_data = NetworkTokenData { + token_number: res.authentication_details.token, + token_cryptogram:res.authentication_details.cryptogram, + token_exp_month: card_decrypted .clone() .unwrap() .expiry_month .unwrap_or_default(), - card_exp_year: card_decrypted + token_exp_year: card_decrypted .clone() .unwrap() .expiry_year .unwrap_or_default(), - card_brand: Some("Visa".to_string()), - card_isin: card_decrypted.clone().unwrap().card_isin, - nick_name: None, + nick_name: card_decrypted.clone().unwrap().card_holder_name, + card_issuer: None, + card_network: Some(common_enums::CardNetwork::Visa), + card_type: None, + card_issuing_country: None, + bank_code: None, }; Ok(card_data) } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 8969ab4524b7..43d8762d39f4 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -22,7 +22,7 @@ use error_stack::{report, ResultExt}; use futures::future::Either; use hyperswitch_domain_models::{ mandates::MandateData, - payment_method_data::GetPaymentMethodType, + payment_method_data::{GetPaymentMethodType,NetworkTokenData}, payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent}, router_data::KlarnaSdkResponse, }; @@ -1853,56 +1853,60 @@ pub async fn retrieve_card_with_permanent_token( // .change_context(errors::ApiErrorResponse::InternalServerError) // .attach_printable("failed to fetch card information from the permanent locker")?; + // let name_on_card = if let Some(name) = card.name_on_card.clone() { + // if name.clone().expose().is_empty() { + // card_token_data + // .and_then(|token_data| token_data.card_holder_name.clone()) + // .or(Some(name)) + // } else { + // card.clone().name_on_card + // } + // } else { + // card_token_data.and_then(|token_data| token_data.card_holder_name.clone()) + // }; + let db = state.store.as_ref(); let key_manager_state = &state.into(); let merchant_account = db.find_merchant_account_by_merchant_id(key_manager_state, &payment_intent.merchant_id, merchant_key_store).await.change_context(errors::ApiErrorResponse::InternalServerError)?; - let card = if merchant_account.is_network_tokenization_enabled{ - network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)? + if merchant_account.is_network_tokenization_enabled{ + let network_token_data = network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)?; + Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) } else{ - cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) + let card = cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch card information from the permanent locker")? + .attach_printable("failed to fetch card information from the permanent locker")?; + let api_card = api::Card { + card_number: card.card_number, + card_holder_name: None, + card_exp_month: card.card_exp_month, + card_exp_year: card.card_exp_year, + card_cvc: card_token_data + .cloned() + .unwrap_or_default() + .card_cvc + .unwrap_or_default(), + card_issuer: None, + nick_name: card.nick_name.map(masking::Secret::new), + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + + Ok(domain::PaymentMethodData::Card(api_card.into())) - }; + } // let key = merchant_key_store.key.get_inner().peek(); // let card = network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)?; // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked // from payment_method_data.card_token object - let name_on_card = if let Some(name) = card.name_on_card.clone() { - if name.clone().expose().is_empty() { - card_token_data - .and_then(|token_data| token_data.card_holder_name.clone()) - .or(Some(name)) - } else { - card.clone().name_on_card - } - } else { - card_token_data.and_then(|token_data| token_data.card_holder_name.clone()) - }; - println!("carddd: {:?}", card.clone()); - - let api_card = api::Card { - card_number: card.card_number, - card_holder_name: name_on_card, - card_exp_month: card.card_exp_month, - card_exp_year: card.card_exp_year, - card_cvc: card_token_data - .cloned() - .unwrap_or_default() - .card_cvc - .unwrap_or_default(), - card_issuer: None, - nick_name: card.nick_name.map(masking::Secret::new), - card_network: None, - card_type: None, - card_issuing_country: None, - bank_code: None, - }; + + // println!("carddd: {:?}", card.clone()); - Ok(domain::PaymentMethodData::Card(api_card.into())) + } pub async fn retrieve_payment_method_from_db_with_token_data( diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index 1cf36bc44281..4db8f1759436 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -9,5 +9,5 @@ pub use hyperswitch_domain_models::payment_method_data::{ SwishQrData, TokenizedBankRedirectValue1, TokenizedBankRedirectValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, TouchNGoRedirection, - UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, WeChatPayQr, + UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, WeChatPayQr,NetworkTokenData, }; From d230f97e314258df0bc926c13fb2d3b95b716c64 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 6 Aug 2024 00:21:10 +0530 Subject: [PATCH 07/59] fix cit repeat --- .../payments/operations/payment_response.rs | 42 ++++++++++++++----- .../router/src/core/payments/transformers.rs | 39 +++++++++++++---- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index a9449e38d2e8..93add66d5b09 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -759,16 +759,38 @@ async fn payment_response_update_tracker( storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> { // Update additional payment data with the payment method response that we received from connector - let additional_payment_method_data = - update_additional_payment_data_with_connector_response_pm_data( - payment_data.payment_attempt.payment_method_data.clone(), - router_data - .connector_response - .as_ref() - .and_then(|connector_response| { - connector_response.additional_payment_method_data.clone() - }), - )?; + let additional_payment_method_data = match payment_data.payment_method_data.clone(){ + Some(payment_method_data) => match payment_method_data{ + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardRedirect(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::PayLater(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankRedirect(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankDebit(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankTransfer(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Crypto(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::MandatePayment | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::RealTimePayment(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Upi(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Voucher(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::GiftCard(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardToken(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::OpenBanking(_) => update_additional_payment_data_with_connector_response_pm_data( + payment_data.payment_attempt.payment_method_data.clone(), + router_data + .connector_response + .as_ref() + .and_then(|connector_response| { + connector_response.additional_payment_method_data.clone() + }), + )?, + hyperswitch_domain_models::payment_method_data::PaymentMethodData::NetworkToken(_) => payment_data.payment_attempt.payment_method_data.clone(), + } + None => None + + }; + router_data.payment_method_status.and_then(|status| { payment_data diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index c0d83bb86d74..da78bec61073 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -461,14 +461,37 @@ where .map(ToString::to_string) .unwrap_or("".to_owned()); let additional_payment_method_data: Option = - payment_attempt - .payment_method_data - .clone() - .map(|data| data.parse_value("payment_method_data")) - .transpose() - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "payment_method_data", - })?; + match payment_data.payment_method_data.clone(){ + Some(payment_method_data) => match payment_method_data{ + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardRedirect(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::PayLater(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankRedirect(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankDebit(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankTransfer(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Crypto(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::MandatePayment | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::RealTimePayment(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Upi(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Voucher(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::GiftCard(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardToken(_) | + hyperswitch_domain_models::payment_method_data::PaymentMethodData::OpenBanking(_) => {payment_attempt + .payment_method_data + .clone() + .map(|data| data.parse_value("payment_method_data")) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "payment_method_data", + })?}, + hyperswitch_domain_models::payment_method_data::PaymentMethodData::NetworkToken(_) => None, + } + None => None + + }; + let surcharge_details = payment_attempt .surcharge_amount From 3d4707f164dd9655b13929717649c288c1483c70 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 13 Aug 2024 02:16:42 +0530 Subject: [PATCH 08/59] add token_payment_method_data in pm table, add delete api for token deletion, and code refactoring --- config/development.toml | 1 + crates/api_models/src/payments.rs | 11 +- crates/diesel_models/src/payment_method.rs | 16 +- .../diesel_models/src/query/payment_method.rs | 2 - crates/diesel_models/src/schema.rs | 1 + crates/hyperswitch_connectors/src/utils.rs | 4 +- .../src/configs/secrets_transformers.rs | 4 +- crates/router/src/configs/settings.rs | 1 + .../src/connector/adyen/transformers.rs | 176 +++++++++- .../connector/authorizedotnet/transformers.rs | 1 + .../src/connector/cybersource/transformers.rs | 29 +- crates/router/src/connector/utils.rs | 17 +- crates/router/src/core/customers.rs | 16 +- crates/router/src/core/payment_methods.rs | 9 +- .../router/src/core/payment_methods/cards.rs | 38 ++- .../payment_methods/network_tokenization.rs | 314 +++++++++++++----- crates/router/src/core/payments.rs | 56 +++- crates/router/src/core/payments/helpers.rs | 50 +-- .../payments/operations/payment_response.rs | 68 ++-- .../router/src/core/payments/tokenization.rs | 54 ++- crates/router/src/core/payouts/helpers.rs | 1 + crates/router/src/core/pm_auth.rs | 1 + crates/router/src/db/payment_method.rs | 1 + crates/router/src/services/encryption.rs | 11 +- crates/router/src/types/api/mandates.rs | 87 +++-- crates/router/src/types/domain/payments.rs | 13 +- .../down.sql | 2 + .../up.sql | 2 + 28 files changed, 770 insertions(+), 216 deletions(-) create mode 100644 migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql create mode 100644 migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql diff --git a/config/development.toml b/config/development.toml index 2926ce7aa411..0d25b6e1dabe 100644 --- a/config/development.toml +++ b/config/development.toml @@ -725,3 +725,4 @@ token_service_api_key= "" public_key= "" private_key= "" key_id= "" +delete_token_url= "" diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 10cbe6e703e6..60806d1af3e3 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -984,7 +984,16 @@ pub struct MandateIds { pub enum MandateReferenceId { ConnectorMandateId(ConnectorMandateReferenceId), // mandate_id send by connector NetworkMandateId(String), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns - + NetworkTokenWithNTI(String), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns along with network token +} + +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] +pub struct NetworkTokenWithNTIRef { + pub network_token: CardNumber, + pub network_transaction_id: String, + pub payment_method_id: Option, + pub token_exp_month: Option, + pub token_exp_year: Option, } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 45a22f8227c5..87728c569e97 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -46,6 +46,7 @@ pub struct PaymentMethod { pub updated_by: Option, pub network_token_reference_id: Option, pub token_locker_id: Option, + pub token_payment_method_data: Option, } #[derive( @@ -85,6 +86,7 @@ pub struct PaymentMethodNew { pub updated_by: Option, pub network_token_reference_id: Option, pub token_locker_id: Option, + pub token_payment_method_data: Option, } impl PaymentMethodNew { @@ -131,6 +133,7 @@ pub enum PaymentMethodUpdate { payment_method_type: Option, payment_method_issuer: Option, token_locker_id: Option, + token_payment_method_data: Option, }, ConnectorMandateDetailsUpdate { connector_mandate_details: Option, @@ -166,6 +169,7 @@ pub struct PaymentMethodUpdateInternal { payment_method_type: Option, payment_method_issuer: Option, token_locker_id: Option, + token_payment_method_data: Option, } impl PaymentMethodUpdateInternal { @@ -215,13 +219,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, - network_token_reference_id:None, + network_token_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data, @@ -239,6 +244,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { metadata: None, @@ -254,6 +260,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { payment_method_data, @@ -272,6 +279,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { network_transaction_id, @@ -290,6 +298,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { metadata: None, @@ -305,6 +314,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, PaymentMethodUpdate::AdditionalDataUpdate { payment_method_data, @@ -315,6 +325,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type, payment_method_issuer, token_locker_id, + token_payment_method_data, } => Self { metadata: None, payment_method_data, @@ -329,6 +340,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer, payment_method_type, token_locker_id, + token_payment_method_data, }, PaymentMethodUpdate::ConnectorMandateDetailsUpdate { connector_mandate_details, @@ -346,6 +358,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, token_locker_id: None, + token_payment_method_data: None, }, } } @@ -388,6 +401,7 @@ impl From<&PaymentMethodNew> for PaymentMethod { .payment_method_billing_address .clone(), token_locker_id: payment_method_new.token_locker_id.clone(), + token_payment_method_data: payment_method_new.token_payment_method_data.clone(), } } } diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs index eed1de26d59e..259be1e17300 100644 --- a/crates/diesel_models/src/query/payment_method.rs +++ b/crates/diesel_models/src/query/payment_method.rs @@ -61,8 +61,6 @@ impl PaymentMethod { // .await // } - - pub async fn find_by_payment_method_id( conn: &PgPooledConn, payment_method_id: &str, diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 008f37728b7a..63cf4366231f 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -987,6 +987,7 @@ diesel::table! { network_token_reference_id -> Nullable, #[max_length = 64] token_locker_id -> Nullable, + token_payment_method_data -> Nullable, } } diff --git a/crates/hyperswitch_connectors/src/utils.rs b/crates/hyperswitch_connectors/src/utils.rs index ebd3ff347042..74ffccca35f9 100644 --- a/crates/hyperswitch_connectors/src/utils.rs +++ b/crates/hyperswitch_connectors/src/utils.rs @@ -892,7 +892,9 @@ impl PaymentsAuthorizeRequestData for PaymentsAuthorizeData { Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { connector_mandate_ids.connector_mandate_id.clone() } - Some(payments::MandateReferenceId::NetworkMandateId(_)) | None => None, + Some(payments::MandateReferenceId::NetworkMandateId(_)) + | None + | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => None, }) } fn is_mandate_payment(&self) -> bool { diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 8d733c81f4e9..9d3afc51667d 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -406,7 +406,9 @@ pub(crate) async fn fetch_raw_secrets( let network_tokenization_service = settings::NetworkTokenizationService::convert_to_raw_secret( conf.network_tokenization_service, secret_management_client, - ).await.expect("Failed to decrypt network tokenization service configs"); + ) + .await + .expect("Failed to decrypt network tokenization service configs"); Settings { server: conf.server, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index f83bceb7c902..6acf124e492d 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -409,6 +409,7 @@ pub struct NetworkTokenizationService { pub public_key: Secret, pub private_key: Secret, pub key_id: String, + pub delete_token_url: String, } #[derive(Debug, Deserialize, Clone)] diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 71c5bb438d99..79e51879c474 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -14,7 +14,7 @@ use crate::{connector::utils::PayoutsData, types::api::payouts, utils::OptionExt use crate::{ connector::utils::{ self, AddressDetailsData, BrowserInformationData, CardData, MandateReferenceData, - PaymentsAuthorizeRequestData, PhoneDetailsData, RouterData, + NetworkTokenData, PaymentsAuthorizeRequestData, PhoneDetailsData, RouterData, }, consts, core::errors, @@ -136,6 +136,15 @@ pub struct LineItem { quantity: Option, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct MpiData { + directory_response: String, + authentication_response: String, + token_authentication_verification_value: Secret, //cryptogram + eci: String, +} + #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -166,6 +175,7 @@ pub struct AdyenPaymentRequest<'a> { channel: Option, metadata: Option, merchant_order_reference: Option, + mpi_data: Option, } #[derive(Debug, Serialize)] @@ -603,6 +613,7 @@ pub enum AdyenPaymentMethod<'a> { #[serde(rename = "econtext_stores")] PayEasy(Box), Pix(Box), + NetworkToken(Box), } #[derive(Debug, Clone, Serialize)] @@ -1202,6 +1213,19 @@ pub struct AdyenAuthType { pub(super) review_key: Option>, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AdyenNetworkTokenData { + #[serde(rename = "type")] + payment_type: PaymentType, + number: CardNumber, + expiry_month: Secret, + expiry_year: Secret, + holder_name: Option>, + brand: Option, //Mandatory for mandate using network_txns_id + network_payment_reference: Option>, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum PaymentType { @@ -1550,14 +1574,16 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>> domain::PaymentMethodData::GiftCard(ref gift_card_data) => { AdyenPaymentRequest::try_from((item, gift_card_data.as_ref())) } + domain::PaymentMethodData::NetworkToken(ref token_data) => { + AdyenPaymentRequest::try_from((item, token_data)) + } domain::PaymentMethodData::Crypto(_) | domain::PaymentMethodData::MandatePayment | domain::PaymentMethodData::Reward | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { + | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyen"), ))? @@ -2568,6 +2594,49 @@ impl<'a> } } } + payments::MandateReferenceId::NetworkTokenWithNTI(network_mandate_id) => { + match item.router_data.request.payment_method_data { + domain::PaymentMethodData::NetworkToken(ref token_data) => { + let card_issuer = token_data.get_card_issuer()?; + let brand = CardBrand::try_from(&card_issuer)?; + let card_holder_name = item.router_data.get_optional_billing_full_name(); + let adyen_network_token = AdyenNetworkTokenData { + payment_type: PaymentType::NetworkToken, + number: token_data.token_number.clone(), + expiry_month: token_data.token_exp_month.clone(), + expiry_year: token_data.get_expiry_year_4_digit(), + holder_name: card_holder_name, + brand: Some(brand), // FIXME: Remove hardcoding + network_payment_reference: Some(Secret::new(network_mandate_id)), + }; + Ok(AdyenPaymentMethod::NetworkToken(Box::new( + adyen_network_token, + ))) + } + + domain::PaymentMethodData::Card(_) + | domain::PaymentMethodData::CardRedirect(_) + | domain::PaymentMethodData::Wallet(_) + | domain::PaymentMethodData::PayLater(_) + | domain::PaymentMethodData::BankRedirect(_) + | domain::PaymentMethodData::BankDebit(_) + | domain::PaymentMethodData::BankTransfer(_) + | domain::PaymentMethodData::Crypto(_) + | domain::PaymentMethodData::MandatePayment + | domain::PaymentMethodData::Reward + | domain::PaymentMethodData::RealTimePayment(_) + | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::Voucher(_) + | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) + | domain::PaymentMethodData::CardToken(_) => { + Err(errors::ConnectorError::NotSupported { + message: "Network tokenization for payment method".to_string(), + connector: "Adyen", + })? + } + } + } // }?; Ok(AdyenPaymentRequest { amount, @@ -2595,6 +2664,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }) } } @@ -2658,6 +2728,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }) } } @@ -2713,6 +2784,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }; Ok(request) } @@ -2768,6 +2840,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }; Ok(request) } @@ -2819,6 +2892,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }; Ok(request) } @@ -2870,6 +2944,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }; Ok(request) } @@ -2931,6 +3006,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }) } } @@ -3027,6 +3103,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }) } } @@ -3103,6 +3180,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }) } } @@ -3162,6 +3240,7 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: None, }) } } @@ -4855,3 +4934,94 @@ impl From for storage_enums::PayoutStatus { } } } + +impl<'a> TryFrom<(&domain::NetworkTokenData, Option>)> for AdyenPaymentMethod<'a> { + type Error = Error; + fn try_from( + (token_data, card_holder_name): (&domain::NetworkTokenData, Option>), + ) -> Result { + let adyen_network_token = AdyenNetworkTokenData { + payment_type: PaymentType::NetworkToken, + number: token_data.token_number.clone(), + expiry_month: token_data.token_exp_month.clone(), + expiry_year: token_data.get_expiry_year_4_digit(), + holder_name: card_holder_name, + brand: Some(CardBrand::Visa), // FIXME: Remove hardcoding + network_payment_reference: None, + }; + Ok(AdyenPaymentMethod::NetworkToken(Box::new( + adyen_network_token, + ))) + } +} + +impl<'a> + TryFrom<( + &AdyenRouterData<&types::PaymentsAuthorizeRouterData>, + &domain::NetworkTokenData, + )> for AdyenPaymentRequest<'a> +{ + type Error = Error; + fn try_from( + value: ( + &AdyenRouterData<&types::PaymentsAuthorizeRouterData>, + &domain::NetworkTokenData, + ), + ) -> Result { + let (item, token_data) = value; + let amount = get_amount_data(item); + let auth_type = AdyenAuthType::try_from(&item.router_data.connector_auth_type)?; + let shopper_interaction = AdyenShopperInteraction::from(item.router_data); + let shopper_reference = build_shopper_reference( + &item.router_data.customer_id, + item.router_data.merchant_id.clone(), + ); + let (recurring_processing_model, store_payment_method, _) = + get_recurring_processing_model(item.router_data)?; + let browser_info = get_browser_info(item.router_data)?; + let billing_address = + get_address_info(item.router_data.get_optional_billing()).transpose()?; + let country_code = get_country_code(item.router_data.get_optional_billing()); + let additional_data = get_additional_data(item.router_data); + let return_url = item.router_data.request.get_return_url()?; + let card_holder_name = item.router_data.get_optional_billing_full_name(); + let payment_method = AdyenPaymentMethod::try_from((token_data, card_holder_name))?; + let shopper_email = item.router_data.request.email.clone(); + let shopper_name = get_shopper_name(item.router_data.get_optional_billing()); + let mpi_data = MpiData { + directory_response: "Y".to_string(), + authentication_response: "Y".to_string(), + token_authentication_verification_value: token_data.token_cryptogram.clone(), + eci: "07".to_string(), + }; + + Ok(AdyenPaymentRequest { + amount, + merchant_account: auth_type.merchant_account, + payment_method, + reference: item.router_data.connector_request_reference_id.clone(), + return_url, + shopper_interaction, + recurring_processing_model, + browser_info, + additional_data, + telephone_number: None, + shopper_name, + shopper_email, + shopper_locale: None, + social_security_number: None, + billing_address, + delivery_address: None, + country_code, + line_items: None, + shopper_reference, + store_payment_method, + channel: None, + shopper_statement: item.router_data.request.statement_descriptor.clone(), + shopper_ip: item.router_data.request.get_ip_address_as_optional(), + metadata: item.router_data.request.metadata.clone().map(Into::into), + merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), + mpi_data: Some(mpi_data), + }) + } +} diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 14f61d1ded79..8e363dae8ca8 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -502,6 +502,7 @@ impl TryFrom<&AuthorizedotnetRouterData<&types::PaymentsAuthorizeRouterData>> Some(api_models::payments::MandateReferenceId::ConnectorMandateId( connector_mandate_id, )) => TransactionRequest::try_from((item, connector_mandate_id))?, + Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) => todo!(), None => { match &item.router_data.request.payment_method_data { domain::PaymentMethodData::Card(ccard) => { diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 53689ac774b6..2bfc8fa96e5a 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -16,7 +16,10 @@ use serde_json::Value; use crate::connector::utils::PayoutsData; use crate::{ connector::utils::{ - self, AddressDetailsData, ApplePayDecrypt, CardData, NetworkTokenData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData + self, AddressDetailsData, ApplePayDecrypt, CardData, NetworkTokenData, + PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, + PaymentsPreProcessingData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, + RecurringMandateData, RouterData, }, consts, core::errors, @@ -708,6 +711,7 @@ impl }), ) } + Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => todo!(), // None => (None, None, None), } } else { @@ -1090,15 +1094,16 @@ impl Err(_) => None, }; - let payment_information = PaymentInformation::NetworkToken(Box::new(NetworkTokenPaymentInformation { - tokenized_card: NetworkTokenizedCard { - number: token_data.token_number, - expiration_month: token_data.token_exp_month, - expiration_year: token_data.token_exp_year, - cryptogram: token_data.token_cryptogram, - transaction_type: "1".to_string(), - }, - })); + let payment_information = + PaymentInformation::NetworkToken(Box::new(NetworkTokenPaymentInformation { + tokenized_card: NetworkTokenizedCard { + number: token_data.token_number, + expiration_month: token_data.token_exp_month, + expiration_year: token_data.token_exp_year, + cryptogram: token_data.token_cryptogram, + transaction_type: "1".to_string(), + }, + })); let processing_information = ProcessingInformation::try_from((item, None, card_type))?; let client_reference_information = ClientReferenceInformation::from(item); @@ -1484,7 +1489,9 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> )?; Self::try_from((item, connector_mandate_id)) } - domain::PaymentMethodData::NetworkToken(token_data) => Self::try_from((item, token_data)), + domain::PaymentMethodData::NetworkToken(token_data) => { + Self::try_from((item, token_data)) + } domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::PayLater(_) | domain::PaymentMethodData::BankRedirect(_) diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index a8ae0fc4df6d..7e147409f6aa 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -703,7 +703,9 @@ impl PaymentsPreProcessingData for types::PaymentsPreProcessingData { Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { connector_mandate_ids.connector_mandate_id.clone() } - Some(payments::MandateReferenceId::NetworkMandateId(_)) | None => None, + Some(payments::MandateReferenceId::NetworkMandateId(_)) + | None + | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => None, }) } } @@ -840,7 +842,9 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => { connector_mandate_ids.connector_mandate_id.clone() } - Some(payments::MandateReferenceId::NetworkMandateId(_)) | None => None, + Some(payments::MandateReferenceId::NetworkMandateId(_)) + | None + | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => None, }) } fn is_mandate_payment(&self) -> bool { @@ -2976,10 +2980,19 @@ pub fn get_refund_integrity_object( } pub trait NetworkTokenData { fn get_card_issuer(&self) -> Result; + fn get_expiry_year_4_digit(&self) -> Secret; } impl NetworkTokenData for domain::NetworkTokenData { fn get_card_issuer(&self) -> Result { get_card_issuer(self.token_number.peek()) } + + fn get_expiry_year_4_digit(&self) -> Secret { + let mut year = self.token_exp_year.peek().clone(); + if year.len() == 2 { + year = format!("20{}", year); + } + Secret::new(year) + } } diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index a2081abddca9..3e179e7908db 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -28,7 +28,7 @@ use crate::{ }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::{ - core::payment_methods::cards, + core::payment_methods::{cards, network_tokenization}, routes::metrics, types::storage::{self, enums}, utils::CustomerAddress, @@ -468,7 +468,21 @@ pub async fn delete_customer( ) .await .switch()?; + + if let Some(network_token_ref_id) = pm.network_token_reference_id { + let resp = network_tokenization::delete_network_token_from_locker_and_token_service( + &state, + &req.customer_id, + merchant_account.get_id(), + pm.payment_method_id.clone(), + pm.token_locker_id, + network_token_ref_id, + ) + .await + .switch()?; + } } + db.delete_payment_method_by_merchant_id_payment_method_id( merchant_account.get_id(), &pm.payment_method_id, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 3916f77dd0a9..dfea3f43fff1 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1,18 +1,18 @@ pub mod cards; pub mod migration; +pub mod network_tokenization; pub mod surcharge_decision_configs; pub mod transformers; pub mod utils; mod validator; pub mod vault; -pub mod network_tokenization; use std::{borrow::Cow, collections::HashSet}; pub use api_models::enums::Connector; #[cfg(feature = "payouts")] pub use api_models::{enums::PayoutConnectors, payouts as payout_types}; -use api_models::{payment_methods, payments::CardToken}; +use api_models::payment_methods; use common_utils::{ext_traits::Encode, id_type::CustomerId}; use diesel_models::{ enums, GenericLinkNew, PaymentMethodCollectLink, PaymentMethodCollectLinkData, @@ -512,7 +512,10 @@ pub async fn retrieve_payment_method_with_token( storage::PaymentTokenData::PermanentCard(card_token) => { helpers::retrieve_card_with_permanent_token( state, - card_token.token_locker_id.as_ref().unwrap_or(card_token.locker_id.as_ref().unwrap_or(&card_token.token)), + card_token + .token_locker_id + .as_ref() + .unwrap_or(card_token.locker_id.as_ref().unwrap_or(&card_token.token)), card_token .payment_method_id .as_ref() diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 4685391d30aa..432a60392e70 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -55,7 +55,8 @@ use crate::{ core::{ errors::{self, StorageErrorExt}, payment_methods::{ - add_payment_method_status_update_task, transformers as payment_methods, + add_payment_method_status_update_task, network_tokenization, + transformers as payment_methods, utils::{get_merchant_pm_filter_graph, make_pm_graph, refresh_pm_filters_cache}, vault, }, @@ -99,6 +100,7 @@ pub async fn create_payment_method( card_scheme: Option, network_token_reference_id: Option, token_locker_id: Option, + token_payment_method_data: Option, ) -> errors::CustomResult { let db = &*state.store; let customer = db @@ -154,6 +156,7 @@ pub async fn create_payment_method( updated_by: None, network_token_reference_id, token_locker_id, + token_payment_method_data, }, storage_scheme, ) @@ -266,6 +269,7 @@ pub async fn get_or_insert_payment_method( None, None, // todo! None, //todo! + None, //todo! ) .await } else { @@ -545,6 +549,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( updated_by: None, network_token_reference_id: None, token_locker_id: None, + token_payment_method_data: None, }, merchant_account.storage_scheme, ) @@ -646,6 +651,7 @@ pub async fn get_client_secret_or_add_payment_method( None, None, None, + None, ) .await?; @@ -834,7 +840,8 @@ pub async fn add_payment_method_data( payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, - token_locker_id: None, // todo! + token_locker_id: None, // todo! + token_payment_method_data: None, //todo! }; db.update_payment_method( @@ -1127,6 +1134,7 @@ pub async fn add_payment_method( payment_method_billing_address.map(Into::into), None, //todo! None, //todo! + None, ) .await?; @@ -1154,6 +1162,7 @@ pub async fn insert_payment_method( payment_method_billing_address: Option, network_token_reference_id: Option, token_locker_id: Option, + token_payment_method_data: Option, ) -> errors::RouterResult { let pm_card_details = resp .card @@ -1190,6 +1199,7 @@ pub async fn insert_payment_method( }), network_token_reference_id, token_locker_id, + token_payment_method_data, ) .await } @@ -3802,7 +3812,9 @@ pub async fn list_customer_payment_method( Some(pm.payment_method_id.clone()), pm.locker_id.clone().or(Some(pm.payment_method_id.clone())), pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()), - pm.network_token_reference_id.clone().or(Some(pm.payment_method_id.clone())), + pm.network_token_reference_id + .clone() + .or(Some(pm.payment_method_id.clone())), ), } } else { @@ -4642,6 +4654,24 @@ pub async fn delete_payment_method( ) .await?; + if let Some(network_token_ref_id) = key.network_token_reference_id { + let resp = network_tokenization::delete_network_token_from_locker_and_token_service( + &state, + &key.customer_id, + &key.merchant_id, + key.payment_method_id.clone(), + key.token_locker_id, + network_token_ref_id, + ) + .await?; + + if resp.status == "Ok" { + logger::info!("Token From locker deleted Successfully!"); + } else { + logger::error!("Error: Deleting Token From Locker!\n{:#?}", resp); + } + } + if response.status == "Ok" { logger::info!("Card From locker deleted Successfully!"); } else { @@ -4678,7 +4708,7 @@ pub async fn delete_payment_method( Ok(services::ApplicationResponse::Json( api::PaymentMethodDeleteResponse { - payment_method_id: key.payment_method_id, + payment_method_id: key.payment_method_id.clone(), deleted: true, }, )) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index f6c3f4f06122..cb52b26d382c 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -1,40 +1,27 @@ -use masking::{ExposeInterface, Mask, PeekInterface, Secret}; -use std::{ - collections::{HashMap, HashSet}, - fmt::Debug, - str::FromStr, -}; +use std::fmt::Debug; -use api_models::{ - admin::PaymentMethodsEnabled, - enums as api_enums, - payment_methods::{Card, PaymentMethodsData}, -}; -use common_enums::enums::MerchantStorageScheme; +use api_models::{enums as api_enums, payment_methods::PaymentMethodsData}; +use cards::CardNumber; use common_utils::{ - consts, - crypto::Encryptable, - encryption::Encryption, - errors::{self as common_utils_erros, CustomResult, ParsingError, ValidationError}, + errors::CustomResult, ext_traits::{Encode, OptionExt}, id_type, request::RequestContent, types::keymanager::Identifier, }; -use error_stack::{report, ResultExt}; -use euclid::dssa::graph::{AnalysisContext, CgraphExt}; -use strum::IntoEnumIterator; - -use cards::CardNumber; +use error_stack::ResultExt; +use hyperswitch_domain_models::payment_method_data::NetworkTokenData; use josekit::jwe; +use masking::{ExposeInterface, Mask, PeekInterface, Secret}; use serde::{Deserialize, Serialize}; -use hyperswitch_domain_models::{ - payment_method_data::NetworkTokenData, -}; -#[cfg(feature = "payouts")] +use super::transformers::DeleteCardResp; use crate::{ - core::errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt}, + core::{ + errors, + payment_methods::{self}, + payments::helpers, + }, headers, logger, routes::{self}, services::{ @@ -46,7 +33,7 @@ use crate::{ domain::{self, types::decrypt_optional}, storage::{self, enums as storage_enums}, }, - utils::{ ConnectorResponseExt}, + utils::ConnectorResponseExt, }; #[derive(Debug, Serialize, Deserialize)] @@ -75,6 +62,7 @@ pub struct ApiPayload { order_data: OrderData, sub_merchant_id: String, key_id: String, + should_send_token: bool, } #[derive(Debug, Deserialize, Serialize)] @@ -98,15 +86,16 @@ pub struct CardNetworkTokenResponsePayloadTemporary { pub token_status: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardNetworkTokenResponsePayload { pub card_brand: api_enums::CardNetwork, pub card_fingerprint: String, - pub card_reference: CardNumber, + pub card_reference: String, pub correlation_id: String, pub customer_id: String, pub par: String, + pub token: CardNumber, pub token_expiry_month: Secret, pub token_expiry_year: Secret, pub token_isin: String, @@ -114,6 +103,57 @@ pub struct CardNetworkTokenResponsePayload { pub token_status: String, } +#[derive(Debug, Serialize, Deserialize)] +pub struct GetCardToken { + card_reference: String, + customer_id: id_type::CustomerId, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct AuthenticationDetails { + cryptogram: Secret, + token: CardNumber, +} +#[derive(Debug, Serialize, Deserialize)] +pub struct TokenResponse { + authentication_details: AuthenticationDetails, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DeleteCardToken { + card_reference: String, + customer_id: id_type::CustomerId, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "UPPERCASE")] +pub enum DeleteNetworkTokenStatus { + Success, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +#[serde(untagged)] +pub enum DeleteNTResponse { + DeleteNetworkTokenResponse(DeleteNetworkTokenResponse), + DeleteNetworkTokenErrorResponse(NetworkTokenErrorResponse), +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct NetworkTokenErrorInfo { + code: String, + developer_message: String, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct NetworkTokenErrorResponse { + error_message: String, + error_info: NetworkTokenErrorInfo, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct DeleteNetworkTokenResponse { + status: DeleteNetworkTokenStatus, +} + pub async fn make_card_network_tokenization_request( state: &routes::SessionState, payment_method_data: Option<&domain::PaymentMethodData>, @@ -148,33 +188,37 @@ pub async fn make_card_network_tokenization_request( logger::error!(fetch_err=?e); errors::ApiErrorResponse::InternalServerError })?; - println!("payloaddd: {:?}", payload); let payload_bytes = payload.as_bytes(); - println!("payload_bytesss: {:?}", payload_bytes); let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let enc_key = tokenization_service.public_key.peek().clone(); let key_id = tokenization_service.key_id.clone(); - let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - println!("jwtt: {:?}", jwt); + + let jwt = encryption::encrypt_jwe( + payload_bytes, + enc_key, + "A128GCM", + Some(key_id.as_str()), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); let order_data = OrderData { consent_id: "test12324".to_string(), // ?? customer_id: customer_id.clone(), - amount:amount_str, - currency:currency_str, + amount: amount_str, + currency: currency_str, }; let api_payload = ApiPayload { service: "NETWORK_TOKEN".to_string(), card_data: jwt, order_data, - sub_merchant_id: "visa_sbx_working".to_string(), + sub_merchant_id: "visa_sbx_working".to_string(), //should be sent in req if env is sbx, else this is not needed todo! key_id, + should_send_token: true, }; let mut request = services::Request::new( @@ -184,10 +228,13 @@ pub async fn make_card_network_tokenization_request( request.add_header(headers::CONTENT_TYPE, "application/json".into()); request.add_header( headers::AUTHORIZATION, - tokenization_service.token_service_api_key.peek().clone().into_masked(), + tokenization_service + .token_service_api_key + .peek() + .clone() + .into_masked(), ); request.set_body(RequestContent::Json(Box::new(api_payload))); - logger::debug!("Requestt to euler: {:?}", request); let response = services::call_connector_api(state, request, "generate_token") .await @@ -211,46 +258,17 @@ pub async fn make_card_network_tokenization_request( jwe::RSA_OAEP_256, ) .await - .unwrap(); - println!( - "card_network_token_response: {:?}", - card_network_token_response - ); + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Failed to decrypt the tokenization response from the tokenization service", + )?; - let cn_response_temp: CardNetworkTokenResponsePayloadTemporary = + let cn_response: CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) .change_context(errors::VaultError::ResponseDeserializationFailed) .change_context(errors::ApiErrorResponse::InternalServerError)?; - let cn_response = CardNetworkTokenResponsePayload { - card_brand: cn_response_temp.card_brand, - card_fingerprint: cn_response_temp.card_fingerprint, - card_reference: CardNumber::from_str("xxxxxxxxxxxxx").unwrap(), - correlation_id: cn_response_temp.correlation_id, - customer_id: cn_response_temp.customer_id, - par: cn_response_temp.par, - token_expiry_month: cn_response_temp.token_expiry_month, - token_expiry_year: cn_response_temp.token_expiry_year, - token_isin: cn_response_temp.token_isin, - token_last_four: cn_response_temp.token_last_four, - token_status: cn_response_temp.token_status, - }; - Ok((cn_response, Some(cn_response_temp.card_reference))) -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct GetCardToken { - card_reference: String, - customer_id: id_type::CustomerId, -} -#[derive(Debug, Serialize, Deserialize)] -pub struct AuthenticationDetails { - cryptogram: Secret, - token: CardNumber, -} -#[derive(Debug, Serialize, Deserialize)] -pub struct TokenResponse { - authentication_details: AuthenticationDetails, + Ok((cn_response.clone(), Some(cn_response.card_reference))) } pub async fn get_token_from_tokenization_service( @@ -271,7 +289,7 @@ pub async fn get_token_from_tokenization_service( let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( services::Method::Post, - tokenization_service.clone().fetch_token_url.as_str(), + tokenization_service.fetch_token_url.as_str(), ); let payload = GetCardToken { card_reference: token_ref.unwrap(), @@ -281,16 +299,20 @@ pub async fn get_token_from_tokenization_service( request.add_header(headers::CONTENT_TYPE, "application/json".into()); request.add_header( headers::AUTHORIZATION, - tokenization_service.token_service_api_key.clone().peek().clone().into_masked(), + tokenization_service + .token_service_api_key + .clone() + .peek() + .clone() + .into_masked(), ); request.set_body(RequestContent::Json(Box::new(payload))); - logger::debug!("reqq to euler: {:?}", request); // Send the request using `call_connector_api` let response = services::call_connector_api(state, request, "get network token") .await .change_context(errors::VaultError::SaveCardFailed); - logger::debug!("Response from euler: {:?}", response); + let res: TokenResponse = response .get_response_inner("cardNetworkTokenResponse") .change_context(errors::VaultError::FetchCardFailed) @@ -298,8 +320,6 @@ pub async fn get_token_from_tokenization_service( logger::error!(fetch_err=?e); errors::ApiErrorResponse::InternalServerError })?; - println!("ressss: {:?}", res); - // let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); let card_decrypted = decrypt_optional::( &state.into(), @@ -318,10 +338,9 @@ pub async fn get_token_from_tokenization_service( PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), _ => None, }); - println!("card_decrypted: {:?}", card_decrypted); let card_data = NetworkTokenData { token_number: res.authentication_details.token, - token_cryptogram:res.authentication_details.cryptogram, + token_cryptogram: res.authentication_details.cryptogram, token_exp_month: card_decrypted .clone() .unwrap() @@ -341,3 +360,132 @@ pub async fn get_token_from_tokenization_service( }; Ok(card_data) } + +pub async fn do_status_check_for_network_token( + state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, + payment_method_info: &storage::PaymentMethod, + //input either network token ref or network token +) -> CustomResult { + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let token_data_decrypted = decrypt_optional::( + &state.into(), + payment_method_info.token_payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); + + is_token_active(token_data_decrypted) +} + +fn is_token_active( + token_data_decrypted: Option, +) -> CustomResult { + if let Some(token_data) = token_data_decrypted { + if let (Some(exp_month), Some(exp_year)) = (token_data.expiry_month, token_data.expiry_year) + { + helpers::validate_card_expiry(&exp_month, &exp_year)?; + } + Ok(true) + } else { + check_token_status_with_tokenization_service() + } +} + +fn check_token_status_with_tokenization_service() -> CustomResult { + Ok(true) +} + +pub async fn delete_network_token_from_locker_and_token_service( + state: &routes::SessionState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + payment_method_id: String, + token_locker_id: Option, + network_token_requestor_reference_id: String, +) -> errors::RouterResult { + let resp = payment_methods::cards::delete_card_from_locker( + &state, + customer_id, + merchant_id, + token_locker_id.as_ref().unwrap_or(&payment_method_id), + ) + .await?; + let delete_token_resp = delete_network_token_from_tokenization_service( + state, + network_token_requestor_reference_id, + customer_id, + ) + .await?; + + if resp.status == "Ok" { + logger::info!("Card From locker deleted Successfully!"); + } else { + logger::error!("Error: Deleting Card From Locker!\n{:#?}", resp); + Err(errors::ApiErrorResponse::InternalServerError)? + } + + Ok(resp) +} + +pub async fn delete_network_token_from_tokenization_service( + state: &routes::SessionState, + network_token_requestor_reference_id: String, + customer_id: &id_type::CustomerId, +) -> CustomResult { + let tokenization_service = &state.conf.network_tokenization_service.get_inner(); + let mut request = services::Request::new( + services::Method::Post, + tokenization_service.delete_token_url.as_str(), + ); + let payload = DeleteCardToken { + card_reference: network_token_requestor_reference_id, + customer_id: customer_id.clone(), + }; + + request.add_header(headers::CONTENT_TYPE, "application/json".into()); + request.add_header( + headers::AUTHORIZATION, + tokenization_service + .token_service_api_key + .clone() + .peek() + .clone() + .into_masked(), + ); + request.set_body(RequestContent::Json(Box::new(payload))); + + // Send the request using `call_connector_api` + let response = services::call_connector_api(state, request, "delete network token") + .await + .change_context(errors::VaultError::SaveCardFailed); + + let res: DeleteNTResponse = response + .get_response_inner("cardNetworkTokenResponse") + .change_context(errors::VaultError::FetchCardFailed) + .map_err(|e| { + logger::error!(fetch_err=?e); + errors::ApiErrorResponse::InternalServerError + })?; + + if res == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { + status: DeleteNetworkTokenStatus::Success, + }) + { + Ok(true) + } else { + Err(errors::ApiErrorResponse::InternalServerError)? + } +} diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 87c85faaed38..7c88ff167cc5 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -75,7 +75,7 @@ use crate::{ core::{ authentication as authentication_core, errors::{self, CustomResult, RouterResponse, RouterResult}, - payment_methods::cards, + payment_methods::{cards, network_tokenization::do_status_check_for_network_token}, utils, }, db::StorageInterface, @@ -2520,6 +2520,7 @@ where .map(|mandate_reference| match mandate_reference { api_models::payments::MandateReferenceId::ConnectorMandateId(_) => true, api_models::payments::MandateReferenceId::NetworkMandateId(_) => false, + api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_) => false, }) .unwrap_or(false); @@ -3522,6 +3523,8 @@ where connector_data, mandate_type, business_profile.is_connector_agnostic_mit_enabled, + merchant_account.is_network_tokenization_enabled, + key_store, ) .await; } @@ -3571,6 +3574,8 @@ where connector_data, mandate_type, business_profile.is_connector_agnostic_mit_enabled, + merchant_account.is_network_tokenization_enabled, + key_store, ) .await; } @@ -3595,6 +3600,8 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, mandate_type: Option, is_connector_agnostic_mit_enabled: Option, + is_network_tokenization_enabled: bool, + key_store: &domain::MerchantKeyStore, ) -> RouterResult { match ( payment_data.payment_intent.setup_future_usage, @@ -3645,14 +3652,40 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, + is_network_tokenization_enabled: Option, + payment_method_info: &storage::PaymentMethod, +) -> bool { + is_connector_agnostic_mit_enabled == Some(true) + && is_network_tokenization_enabled == Some(true) + && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) + && payment_method_info.network_transaction_id.is_some() + && payment_method_info.token_locker_id.is_some() + && do_status_check_for_network_token(state, key_store, payment_method_info) + .await + .is_ok() +} + pub fn should_add_task_to_process_tracker(payment_data: &PaymentData) -> bool { let connector = payment_data.payment_attempt.connector.as_deref(); @@ -3989,6 +4039,8 @@ where connector_data, mandate_type, business_profile.is_connector_agnostic_mit_enabled, + merchant_account.is_network_tokenization_enabled, + key_store, ) .await } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 43d8762d39f4..347e4df66a37 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, str::FromStr}; +use std::borrow::Cow; use api_models::{ customers::CustomerRequestWithEmail, @@ -22,7 +22,7 @@ use error_stack::{report, ResultExt}; use futures::future::Either; use hyperswitch_domain_models::{ mandates::MandateData, - payment_method_data::{GetPaymentMethodType,NetworkTokenData}, + payment_method_data::GetPaymentMethodType, payments::{payment_attempt::PaymentAttempt, payment_intent::CustomerData, PaymentIntent}, router_data::KlarnaSdkResponse, }; @@ -53,8 +53,7 @@ use crate::{ payment_methods::{ self, cards::{self, create_encrypted_data}, - network_tokenization, - vault, + network_tokenization, vault, }, payments, pm_auth::retrieve_payment_method_from_auth_service, @@ -1867,16 +1866,30 @@ pub async fn retrieve_card_with_permanent_token( let db = state.store.as_ref(); let key_manager_state = &state.into(); - let merchant_account = db.find_merchant_account_by_merchant_id(key_manager_state, &payment_intent.merchant_id, merchant_key_store).await.change_context(errors::ApiErrorResponse::InternalServerError)?; - if merchant_account.is_network_tokenization_enabled{ - let network_token_data = network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)?; + let merchant_account = db + .find_merchant_account_by_merchant_id( + key_manager_state, + &payment_intent.merchant_id, + merchant_key_store, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + if merchant_account.is_network_tokenization_enabled { + let network_token_data = network_tokenization::get_token_from_tokenization_service( + state, + &merchant_account, + merchant_key_store, + payment_method_id.to_string(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) - } - else{ - let card = cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch card information from the permanent locker")?; + } else { + let card = + cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("failed to fetch card information from the permanent locker")?; let api_card = api::Card { card_number: card.card_number, card_holder_name: None, @@ -1894,19 +1907,14 @@ pub async fn retrieve_card_with_permanent_token( card_issuing_country: None, bank_code: None, }; - - Ok(domain::PaymentMethodData::Card(api_card.into())) + Ok(domain::PaymentMethodData::Card(api_card.into())) } // let key = merchant_key_store.key.get_inner().peek(); // let card = network_tokenization::get_token_from_tokenization_service(state, &merchant_account, merchant_key_store, payment_method_id.to_string()).await.change_context(errors::ApiErrorResponse::InternalServerError)?; // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked // from payment_method_data.card_token object - - // println!("carddd: {:?}", card.clone()); - - } pub async fn retrieve_payment_method_from_db_with_token_data( @@ -2038,7 +2046,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( .locker_id .clone() .unwrap_or(payment_method_info.payment_method_id.clone()), - token_locker_id:payment_method_info + token_locker_id: payment_method_info .network_token_reference_id .clone() .or(Some(payment_method_info.payment_method_id.clone())), @@ -2061,8 +2069,6 @@ pub async fn make_pm_data<'a, F: Clone, R>( ) .await; - - let payment_method_details = pm_data.attach_printable("in 'make_pm_data'")?; Ok::<_, error_stack::Report>( diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 93add66d5b09..00d4d3f9cad4 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -759,38 +759,42 @@ async fn payment_response_update_tracker( storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult> { // Update additional payment data with the payment method response that we received from connector - let additional_payment_method_data = match payment_data.payment_method_data.clone(){ - Some(payment_method_data) => match payment_method_data{ - hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardRedirect(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::PayLater(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankRedirect(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankDebit(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankTransfer(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::Crypto(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::MandatePayment | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::RealTimePayment(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::Upi(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::Voucher(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::GiftCard(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardToken(_) | - hyperswitch_domain_models::payment_method_data::PaymentMethodData::OpenBanking(_) => update_additional_payment_data_with_connector_response_pm_data( - payment_data.payment_attempt.payment_method_data.clone(), - router_data - .connector_response - .as_ref() - .and_then(|connector_response| { - connector_response.additional_payment_method_data.clone() - }), - )?, - hyperswitch_domain_models::payment_method_data::PaymentMethodData::NetworkToken(_) => payment_data.payment_attempt.payment_method_data.clone(), - } - None => None - - }; - + let additional_payment_method_data = match payment_data.payment_method_data.clone() { + Some(payment_method_data) => match payment_method_data { + hyperswitch_domain_models::payment_method_data::PaymentMethodData::Card(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardRedirect(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Wallet(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::PayLater(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankRedirect(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankDebit(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::BankTransfer(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Crypto(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::MandatePayment + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Reward + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::RealTimePayment( + _, + ) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Upi(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::Voucher(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::GiftCard(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::CardToken(_) + | hyperswitch_domain_models::payment_method_data::PaymentMethodData::OpenBanking(_) => { + update_additional_payment_data_with_connector_response_pm_data( + payment_data.payment_attempt.payment_method_data.clone(), + router_data + .connector_response + .as_ref() + .and_then(|connector_response| { + connector_response.additional_payment_method_data.clone() + }), + )? + } + hyperswitch_domain_models::payment_method_data::PaymentMethodData::NetworkToken(_) => { + payment_data.payment_attempt.payment_method_data.clone() + } + }, + None => None, + }; router_data.payment_method_status.and_then(|status| { payment_data diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index b963b68036f0..53fdd4ef17b1 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -178,7 +178,6 @@ where .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to serialize customer acceptance to value")?; - let is_network_token_enabled = merchant_account.is_network_tokenization_enabled; let pm_id = if customer_acceptance.is_some() { let payment_method_create_request = helpers::get_payment_method_create_request( @@ -191,7 +190,7 @@ where .await?; let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = merchant_account.get_id(); - let ((mut resp, duplication_check, network_token_ref_id), token_locker_id) = + let ((mut resp, duplication_check, network_token_requestor_ref_id), token_resp) = if !state.conf.locker.locker_enabled { let (res, dc) = skip_saving_card_in_locker( merchant_account, @@ -210,11 +209,11 @@ where payment_method_create_request.to_owned(), false, amount.clone(), - currency.clone(), + currency, )) .await?; - let (res2, dc2, ref_id2) = Box::pin(save_in_locker( + let (res2, dc2, network_token_requestor_ref_id) = Box::pin(save_in_locker( state, merchant_account, Some(&save_payment_method_data.request.get_payment_method_data()), @@ -225,8 +224,12 @@ where )) .await?; - ((res, dc, ref_id2), Some(res2.payment_method_id)) + ((res, dc, network_token_requestor_ref_id), Some(res2)) }; + let token_locker_id = match token_resp { + Some(ref token_resp) => Some(token_resp.payment_method_id.clone()), + None => None, + }; let pm_card_details = resp.card.as_ref().map(|card| { PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) @@ -240,6 +243,27 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt payment method data")?; + let pm_token_data_encrypted: Option>> = + match token_resp { + Some(token_resp) => { + let pm_token_details = token_resp.card.as_ref().map(|card| { + PaymentMethodsData::Card(CardDetailsPaymentMethod::from( + card.clone(), + )) + }); + + pm_token_details + .async_map(|pm_card| { + create_encrypted_data(state, key_store, pm_card) + }) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt payment method data")? + } + None => None, + }; + let encrypted_payment_method_billing_address: Option< Encryptable>, > = payment_method_billing_address @@ -350,8 +374,9 @@ where card.card_network .map(|card_network| card_network.to_string()) }), - network_token_ref_id, //todo! + network_token_requestor_ref_id, //todo! token_locker_id, + pm_token_data_encrypted.map(Into::into), ) .await } else { @@ -443,8 +468,9 @@ where merchant_account.storage_scheme, encrypted_payment_method_billing_address .map(Into::into), - network_token_ref_id, //todo! + network_token_requestor_ref_id, //todo! token_locker_id, + pm_token_data_encrypted.map(Into::into), ) .await } else { @@ -642,8 +668,9 @@ where card.card_network .map(|card_network| card_network.to_string()) }), - network_token_ref_id, //todo! - token_locker_id, //todo! + network_token_requestor_ref_id, //todo! + token_locker_id, //todo! + pm_token_data_encrypted.map(Into::into), //todo! ) .await?; }; @@ -767,9 +794,8 @@ pub async fn save_in_locker( .customer_id .clone() .get_required_value("customer_id")?; - println!("before saveingggg"); if save_token { - let (token_response, token_ref_id) = + let (token_response, network_token_requestor_ref_id) = network_tokenization::make_card_network_tokenization_request( state, payment_method_data, @@ -780,7 +806,7 @@ pub async fn save_in_locker( ) .await?; let card_data = api::CardDetail { - card_number: token_response.card_reference.clone(), + card_number: token_response.token.clone(), card_exp_month: token_response.token_expiry_month.clone(), card_exp_year: token_response.token_expiry_year.clone(), card_holder_name: None, @@ -790,7 +816,6 @@ pub async fn save_in_locker( card_issuer: None, card_type: None, }; - println!("savinggg token"); let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( state, payment_method_request, @@ -802,9 +827,8 @@ pub async fn save_in_locker( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Add Card Failed")?; - Ok((res, dc, token_ref_id)) + Ok((res, dc, network_token_requestor_ref_id)) } else { - println!("savinggg card"); match payment_method_request.card.clone() { Some(card) => { let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index a786cd13a2be..ef7c75c3916e 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -519,6 +519,7 @@ pub async fn save_payout_data_to_locker( None, None, None, + None, ) .await?; } diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index bdfa72cc8030..c9f6625b746a 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -512,6 +512,7 @@ async fn store_bank_details_in_payment_methods( payment_method_billing_address: None, updated_by: None, token_locker_id: None, + token_payment_method_data: None, }; new_entries.push(pm_new); diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index ec889fb339dd..e5f16409c9c3 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -696,6 +696,7 @@ impl PaymentMethodInterface for MockDb { updated_by: payment_method_new.updated_by, payment_method_billing_address: payment_method_new.payment_method_billing_address, token_locker_id: payment_method_new.token_locker_id, + token_payment_method_data: payment_method_new.token_payment_method_data, }; payment_methods.push(payment_method.clone()); Ok(payment_method) diff --git a/crates/router/src/services/encryption.rs b/crates/router/src/services/encryption.rs index b5700b67aedd..13cf8f35e582 100644 --- a/crates/router/src/services/encryption.rs +++ b/crates/router/src/services/encryption.rs @@ -212,9 +212,14 @@ VuY3OeNxi+dC2r7HppP3O/MJ4gX/RJJfSrcaGP8/Ke1W5+jE97Qy #[actix_rt::test] async fn test_jwe() { - let jwt = encrypt_jwe("request_payload".as_bytes(), ENCRYPTION_KEY, "A256GCM", None) - .await - .unwrap(); + let jwt = encrypt_jwe( + "request_payload".as_bytes(), + ENCRYPTION_KEY, + "A256GCM", + None, + ) + .await + .unwrap(); let alg = jwe::RSA_OAEP_256; let payload = decrypt_jwe(&jwt, KeyIdCheck::SkipKeyIdCheck, DECRYPTION_KEY, alg) .await diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index 686f7f3ce495..5804b7d6749e 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -52,33 +52,74 @@ impl MandateResponseExt for MandateResponse { .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) .attach_printable("payment_method not found")?; + // let token_data = if payment_method.token_locker_id.is_some() && state.conf.locker.locker_enabled { + // let card = payment_methods::cards::get_card_from_locker( + // state, + // &payment_method.customer_id, + // &payment_method.merchant_id, + // payment_method + // .token_locker_id + // .as_ref() + // .unwrap_or(&payment_method.payment_method_id), + // ) + // .await?; + + // payment_methods::transformers::get_card_detail(&payment_method, card) + // .change_context(errors::ApiErrorResponse::InternalServerError) + // .attach_printable("Failed while getting card details")? + // } else { + // payment_methods::cards::get_card_details_without_locker_fallback( + // &payment_method, + // state, + // &key_store, + // ) + // .await? + // }; + let card = if pm == storage_enums::PaymentMethod::Card { // if locker is disabled , decrypt the payment method data - let card_details = if state.conf.locker.locker_enabled { - let card = payment_methods::cards::get_card_from_locker( - state, - &payment_method.customer_id, - &payment_method.merchant_id, - payment_method - .locker_id - .as_ref() - .unwrap_or(&payment_method.payment_method_id), - ) - .await?; + // let card_details = if state.conf.locker.locker_enabled { + // let card = payment_methods::cards::get_card_from_locker( + // // get network token details here? instead of that status check? + // state, + // &payment_method.customer_id, + // &payment_method.merchant_id, + // payment_method + // .locker_id + // .as_ref() + // .unwrap_or(&payment_method.payment_method_id), + // ) + // .await?; - payment_methods::transformers::get_card_detail(&payment_method, card) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while getting card details")? - } else { - payment_methods::cards::get_card_details_without_locker_fallback( - &payment_method, - state, - &key_store, - ) - .await? - }; + // payment_methods::transformers::get_card_detail(&payment_method, card) + // .change_context(errors::ApiErrorResponse::InternalServerError) + // .attach_printable("Failed while getting card details")? + let token_data = + if payment_method.token_locker_id.is_some() && state.conf.locker.locker_enabled { + let card = payment_methods::cards::get_card_from_locker( + state, + &payment_method.customer_id, + &payment_method.merchant_id, + payment_method + .token_locker_id + .as_ref() + .unwrap_or(&payment_method.payment_method_id), + ) + .await?; + + payment_methods::transformers::get_card_detail(&payment_method, card) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while getting card details")? + } else { + payment_methods::cards::get_card_details_without_locker_fallback( + &payment_method, + state, + &key_store, + ) + .await? + }; - Some(MandateCardDetails::from(card_details).into_inner()) + Some(MandateCardDetails::from(token_data).into_inner()) } else { None }; diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index 4db8f1759436..23f1a3c434fa 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -4,10 +4,11 @@ pub use hyperswitch_domain_models::payment_method_data::{ CardToken, CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails, GoPayRedirection, GooglePayPaymentMethodInfo, GooglePayRedirectData, GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, - KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData, - PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, - SwishQrData, TokenizedBankRedirectValue1, TokenizedBankRedirectValue2, - TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedCardValue1, - TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, TouchNGoRedirection, - UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, WeChatPayQr,NetworkTokenData, + KakaoPayRedirection, MbWayRedirection, MifinityData, NetworkTokenData, OpenBankingData, + PayLaterData, PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, + SepaAndBacsBillingDetails, SwishQrData, TokenizedBankRedirectValue1, + TokenizedBankRedirectValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, + TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, + TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, + WeChatPayQr, }; diff --git a/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql new file mode 100644 index 000000000000..1faeab3888a4 --- /dev/null +++ b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_methods DROP COLUMN IF EXISTS token_payment_method_data; \ No newline at end of file diff --git a/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql new file mode 100644 index 000000000000..51b44f9b7250 --- /dev/null +++ b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS token_payment_method_data BYTEA DEFAULT NULL; \ No newline at end of file From b7f9735395237386de5dba1c3f42832647a31806 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:50:55 +0000 Subject: [PATCH 09/59] docs(openapi): re-generate OpenAPI specification --- api-reference/openapi_spec.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index e16343595655..fcb4c7eae3f2 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -11555,6 +11555,9 @@ } ], "nullable": true + }, + "is_network_tokenization_enabled": { + "type": "boolean" } }, "additionalProperties": false @@ -11650,7 +11653,8 @@ "primary_business_details", "organization_id", "is_recon_enabled", - "recon_status" + "recon_status", + "is_network_tokenization_enabled" ], "properties": { "merchant_id": { @@ -11788,6 +11792,9 @@ } ], "nullable": true + }, + "is_network_tokenization_enabled": { + "type": "boolean" } } }, From 27ddfe681ff3cb28d8edbd47c8b909c6be268558 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 13 Aug 2024 20:30:40 +0530 Subject: [PATCH 10/59] fix clippy errors --- crates/api_models/src/admin.rs | 3 +++ .../src/merchant_account.rs | 7 ++++++ .../src/connector/nexinets/transformers.rs | 2 +- .../src/connector/wellsfargo/transformers.rs | 2 +- crates/router/src/core/admin.rs | 1 + .../payment_methods/network_tokenization.rs | 24 ++++++++++++------- crates/router/src/core/payments/helpers.rs | 3 ++- crates/router/src/core/routing/helpers.rs | 1 + 8 files changed, 31 insertions(+), 12 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 57a0bbc5d7c9..959f4699f009 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -337,6 +337,9 @@ pub struct MerchantAccountUpdate { /// Default payment method collect link config #[schema(value_type = Option)] pub pm_collect_link_config: Option, + + pub is_network_tokenization_enabled: Option, + } #[cfg(all( diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 3eb785604333..4610f902387c 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -237,6 +237,7 @@ pub enum MerchantAccountUpdate { default_profile: Option>, payment_link_config: Option, pm_collect_link_config: Option, + is_network_tokenization_enabled: Option, }, StorageSchemeUpdate { storage_scheme: MerchantStorageScheme, @@ -298,6 +299,7 @@ impl From for MerchantAccountUpdateInternal { default_profile, payment_link_config, pm_collect_link_config, + is_network_tokenization_enabled, } => Self { merchant_name: merchant_name.map(Encryption::from), merchant_details: merchant_details.map(Encryption::from), @@ -324,6 +326,7 @@ impl From for MerchantAccountUpdateInternal { organization_id: None, is_recon_enabled: None, recon_status: None, + is_network_tokenization_enabled }, MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self { storage_scheme: Some(storage_scheme), @@ -351,6 +354,7 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, + is_network_tokenization_enabled: None, }, MerchantAccountUpdate::ReconUpdate { recon_status } => Self { recon_status: Some(recon_status), @@ -378,6 +382,7 @@ impl From for MerchantAccountUpdateInternal { default_profile: None, payment_link_config: None, pm_collect_link_config: None, + is_network_tokenization_enabled: None, }, MerchantAccountUpdate::UnsetDefaultProfile => Self { default_profile: Some(None), @@ -405,6 +410,7 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, + is_network_tokenization_enabled: None, }, MerchantAccountUpdate::ModifiedAtUpdate => Self { modified_at: now, @@ -432,6 +438,7 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, + is_network_tokenization_enabled: None, }, } } diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index ee172601e44d..c9165e1d7edd 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -628,7 +628,7 @@ fn get_payment_details_and_product( | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { + | PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nexinets"), ))? diff --git a/crates/router/src/connector/wellsfargo/transformers.rs b/crates/router/src/connector/wellsfargo/transformers.rs index d368504a5fc6..be863b2083b9 100644 --- a/crates/router/src/connector/wellsfargo/transformers.rs +++ b/crates/router/src/connector/wellsfargo/transformers.rs @@ -690,7 +690,7 @@ impl }), ) } - None => (None, None, None), + Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) | None => (None, None, None), } } else { (None, None, None) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 9a68d6a1322c..7553e016b785 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -983,6 +983,7 @@ impl MerchantAccountUpdateBridge for api::MerchantAccountUpdate { payment_link_config: None, pm_collect_link_config, routing_algorithm: self.routing_algorithm, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } } diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index cb52b26d382c..246c24fe1bbd 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -5,7 +5,7 @@ use cards::CardNumber; use common_utils::{ errors::CustomResult, ext_traits::{Encode, OptionExt}, - id_type, + id_type, type_name, request::RequestContent, types::keymanager::Identifier, }; @@ -26,16 +26,18 @@ use crate::{ routes::{self}, services::{ self, encryption, - request::{self}, + request, }, types::{ api::{self}, - domain::{self, types::decrypt_optional}, + domain, storage::{self, enums as storage_enums}, }, utils::ConnectorResponseExt, }; +use diesel_models::payment_method; + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardData { @@ -157,11 +159,11 @@ pub struct DeleteNetworkTokenResponse { pub async fn make_card_network_tokenization_request( state: &routes::SessionState, payment_method_data: Option<&domain::PaymentMethodData>, - merchant_account: &domain::MerchantAccount, + _merchant_account: &domain::MerchantAccount, customer_id: &Option, amount: Option, currency: Option, -) -> errors::CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> +) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> { let customer_id = customer_id .clone() @@ -321,13 +323,15 @@ pub async fn get_token_from_tokenization_service( errors::ApiErrorResponse::InternalServerError })?; let identifier = Identifier::Merchant(key_store.merchant_id.clone()); - let card_decrypted = decrypt_optional::( + let card_decrypted = domain::types::crypto_operation::( &state.into(), - pm_data.payment_method_data.clone(), + type_name!(payment_method::PaymentMethod), + domain::types::CryptoOperation::DecryptOptional(pm_data.payment_method_data.clone()), identifier, key, ) .await + .and_then(|val| val.try_into_optionaloperation()) .change_context(errors::StorageError::DecryptionError) .attach_printable("unable to decrypt card details") .ok() @@ -369,13 +373,15 @@ pub async fn do_status_check_for_network_token( ) -> CustomResult { let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); - let token_data_decrypted = decrypt_optional::( + let token_data_decrypted = domain::types::crypto_operation::( &state.into(), - payment_method_info.token_payment_method_data.clone(), + type_name!(payment_method::PaymentMethod), + domain::types::CryptoOperation::DecryptOptional(payment_method_info.token_payment_method_data.clone()), identifier, key, ) .await + .and_then(|val| val.try_into_optionaloperation()) .change_context(errors::StorageError::DecryptionError) .attach_printable("unable to decrypt card details") .ok() diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 6747d299e5ed..673dc6f95596 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1862,7 +1862,7 @@ pub async fn retrieve_card_with_permanent_token( payment_method_id: &str, payment_intent: &PaymentIntent, card_token_data: Option<&domain::CardToken>, - _merchant_key_store: &domain::MerchantKeyStore, + merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult { let customer_id = payment_intent @@ -1936,6 +1936,7 @@ pub async fn retrieve_card_with_permanent_token( Ok(domain::PaymentMethodData::Card(api_card.into())) } +} pub async fn retrieve_payment_method_from_db_with_token_data( state: &SessionState, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 9255c2bad0ba..4562ed3c4d25 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -152,6 +152,7 @@ pub async fn update_merchant_active_algorithm_ref( default_profile: None, payment_link_config: None, pm_collect_link_config: None, + is_network_tokenization_enabled: None, }; let db = &*state.store; From 95dd43d1f40cda49818dcb6155c3f6c705382ba6 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:01:49 +0000 Subject: [PATCH 11/59] chore: run formatter --- crates/api_models/src/admin.rs | 1 - .../src/merchant_account.rs | 2 +- .../src/connector/nexinets/transformers.rs | 8 +-- .../src/connector/wellsfargo/transformers.rs | 4 +- .../router/src/core/payment_methods/cards.rs | 4 +- .../payment_methods/network_tokenization.rs | 69 +++++++++---------- crates/router/src/core/payments/helpers.rs | 4 +- crates/router/src/core/routing/helpers.rs | 2 +- crates/router/src/types/domain/payments.rs | 13 ++-- 9 files changed, 51 insertions(+), 56 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 959f4699f009..a97e90cc16a9 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -339,7 +339,6 @@ pub struct MerchantAccountUpdate { pub pm_collect_link_config: Option, pub is_network_tokenization_enabled: Option, - } #[cfg(all( diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 4610f902387c..fe02079d0d30 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -326,7 +326,7 @@ impl From for MerchantAccountUpdateInternal { organization_id: None, is_recon_enabled: None, recon_status: None, - is_network_tokenization_enabled + is_network_tokenization_enabled, }, MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self { storage_scheme: Some(storage_scheme), diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index c9165e1d7edd..3aaf622a0200 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -628,11 +628,9 @@ fn get_payment_details_and_product( | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) | PaymentMethodData::NetworkToken(_) - | PaymentMethodData::NetworkToken(_) => { - Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message("nexinets"), - ))? - } + | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message("nexinets"), + ))?, } } diff --git a/crates/router/src/connector/wellsfargo/transformers.rs b/crates/router/src/connector/wellsfargo/transformers.rs index be863b2083b9..761fc43982b4 100644 --- a/crates/router/src/connector/wellsfargo/transformers.rs +++ b/crates/router/src/connector/wellsfargo/transformers.rs @@ -690,7 +690,9 @@ impl }), ) } - Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) | None => (None, None, None), + Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) | None => { + (None, None, None) + } } } else { (None, None, None) diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 08624d613e13..30a9b21df0bb 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4070,8 +4070,8 @@ async fn get_pm_list_context( pm.locker_id.clone().or(Some(pm.payment_method_id.clone())), pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()), pm.network_token_reference_id - .clone() - .or(Some(pm.payment_method_id.clone())), + .clone() + .or(Some(pm.payment_method_id.clone())), ), ), }) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 246c24fe1bbd..222ee97774ca 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -5,10 +5,12 @@ use cards::CardNumber; use common_utils::{ errors::CustomResult, ext_traits::{Encode, OptionExt}, - id_type, type_name, + id_type, request::RequestContent, + type_name, types::keymanager::Identifier, }; +use diesel_models::payment_method; use error_stack::ResultExt; use hyperswitch_domain_models::payment_method_data::NetworkTokenData; use josekit::jwe; @@ -24,10 +26,7 @@ use crate::{ }, headers, logger, routes::{self}, - services::{ - self, encryption, - request, - }, + services::{self, encryption, request}, types::{ api::{self}, domain, @@ -36,8 +35,6 @@ use crate::{ utils::ConnectorResponseExt, }; -use diesel_models::payment_method; - #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardData { @@ -163,8 +160,7 @@ pub async fn make_card_network_tokenization_request( customer_id: &Option, amount: Option, currency: Option, -) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> -{ +) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> { let customer_id = customer_id .clone() .get_required_value("customer_id") @@ -197,14 +193,9 @@ pub async fn make_card_network_tokenization_request( let key_id = tokenization_service.key_id.clone(); - let jwt = encryption::encrypt_jwe( - payload_bytes, - enc_key, - "A128GCM", - Some(key_id.as_str()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; + let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); let order_data = OrderData { @@ -373,25 +364,28 @@ pub async fn do_status_check_for_network_token( ) -> CustomResult { let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); - let token_data_decrypted = domain::types::crypto_operation::( - &state.into(), - type_name!(payment_method::PaymentMethod), - domain::types::CryptoOperation::DecryptOptional(payment_method_info.token_payment_method_data.clone()), - identifier, - key, - ) - .await - .and_then(|val| val.try_into_optionaloperation()) - .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") - .ok() - .flatten() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); + let token_data_decrypted = + domain::types::crypto_operation::( + &state.into(), + type_name!(payment_method::PaymentMethod), + domain::types::CryptoOperation::DecryptOptional( + payment_method_info.token_payment_method_data.clone(), + ), + identifier, + key, + ) + .await + .and_then(|val| val.try_into_optionaloperation()) + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); is_token_active(token_data_decrypted) } @@ -486,7 +480,8 @@ pub async fn delete_network_token_from_tokenization_service( errors::ApiErrorResponse::InternalServerError })?; - if res == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { + if res + == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { status: DeleteNetworkTokenStatus::Success, }) { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 673dc6f95596..3f77ed35c21f 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1934,8 +1934,8 @@ pub async fn retrieve_card_with_permanent_token( bank_code: None, }; - Ok(domain::PaymentMethodData::Card(api_card.into())) -} + Ok(domain::PaymentMethodData::Card(api_card.into())) + } } pub async fn retrieve_payment_method_from_db_with_token_data( diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 4562ed3c4d25..75a88275f868 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -152,7 +152,7 @@ pub async fn update_merchant_active_algorithm_ref( default_profile: None, payment_link_config: None, pm_collect_link_config: None, - is_network_tokenization_enabled: None, + is_network_tokenization_enabled: None, }; let db = &*state.store; diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index 6efbe2ef2f3f..23f1a3c434fa 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -4,10 +4,11 @@ pub use hyperswitch_domain_models::payment_method_data::{ CardToken, CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails, GoPayRedirection, GooglePayPaymentMethodInfo, GooglePayRedirectData, GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, - KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData, - PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, - SwishQrData, TokenizedBankRedirectValue1, TokenizedBankRedirectValue2, - TokenizedBankTransferValue1, TokenizedBankTransferValue2, TokenizedCardValue1, - TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, TouchNGoRedirection, - UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, WeChatPayQr, NetworkTokenData, + KakaoPayRedirection, MbWayRedirection, MifinityData, NetworkTokenData, OpenBankingData, + PayLaterData, PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, + SepaAndBacsBillingDetails, SwishQrData, TokenizedBankRedirectValue1, + TokenizedBankRedirectValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, + TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, + TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, + WeChatPayQr, }; From bd9e6dac9ec0f5718e97c61134d7d99f150e4ed2 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:04:48 +0000 Subject: [PATCH 12/59] docs(openapi): re-generate OpenAPI specification --- api-reference/openapi_spec.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index fcb4c7eae3f2..5abc03b8a2af 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -11923,6 +11923,10 @@ } ], "nullable": true + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "nullable": true } }, "additionalProperties": false From 079cc3deeae7ae1475aec6340b3d3864785222f2 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 14 Aug 2024 01:22:17 +0530 Subject: [PATCH 13/59] code refactoring --- .../payment_methods/network_tokenization.rs | 77 +++---- .../router/src/core/payments/tokenization.rs | 213 ++++++++++++------ 2 files changed, 178 insertions(+), 112 deletions(-) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 246c24fe1bbd..983f612069c6 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -5,8 +5,9 @@ use cards::CardNumber; use common_utils::{ errors::CustomResult, ext_traits::{Encode, OptionExt}, - id_type, type_name, + id_type, request::RequestContent, + type_name, types::keymanager::Identifier, }; use error_stack::ResultExt; @@ -24,10 +25,7 @@ use crate::{ }, headers, logger, routes::{self}, - services::{ - self, encryption, - request, - }, + services::{self, encryption}, types::{ api::{self}, domain, @@ -159,28 +157,24 @@ pub struct DeleteNetworkTokenResponse { pub async fn make_card_network_tokenization_request( state: &routes::SessionState, payment_method_data: Option<&domain::PaymentMethodData>, - _merchant_account: &domain::MerchantAccount, customer_id: &Option, amount: Option, currency: Option, -) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> -{ +) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> { let customer_id = customer_id .clone() .get_required_value("customer_id") .change_context(errors::ApiErrorResponse::InternalServerError)?; let card_data = match payment_method_data { - Some(pm_data) => match pm_data { - domain::PaymentMethodData::Card(card) => CardData { + Some(domain::PaymentMethodData::Card(card)) => CardData { + card_number: card.card_number.clone(), exp_month: card.card_exp_month.clone(), exp_year: card.card_exp_year.clone(), card_security_code: card.card_cvc.clone(), }, _ => todo!(), - }, - _ => todo!(), - }; + }; let payload = card_data .encode_to_string_of_json() @@ -197,14 +191,9 @@ pub async fn make_card_network_tokenization_request( let key_id = tokenization_service.key_id.clone(); - let jwt = encryption::encrypt_jwe( - payload_bytes, - enc_key, - "A128GCM", - Some(key_id.as_str()), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; + let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); let order_data = OrderData { @@ -373,25 +362,28 @@ pub async fn do_status_check_for_network_token( ) -> CustomResult { let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); - let token_data_decrypted = domain::types::crypto_operation::( - &state.into(), - type_name!(payment_method::PaymentMethod), - domain::types::CryptoOperation::DecryptOptional(payment_method_info.token_payment_method_data.clone()), - identifier, - key, - ) - .await - .and_then(|val| val.try_into_optionaloperation()) - .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") - .ok() - .flatten() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); + let token_data_decrypted = + domain::types::crypto_operation::( + &state.into(), + type_name!(payment_method::PaymentMethod), + domain::types::CryptoOperation::DecryptOptional( + payment_method_info.token_payment_method_data.clone(), + ), + identifier, + key, + ) + .await + .and_then(|val| val.try_into_optionaloperation()) + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); is_token_active(token_data_decrypted) } @@ -429,7 +421,7 @@ pub async fn delete_network_token_from_locker_and_token_service( token_locker_id.as_ref().unwrap_or(&payment_method_id), ) .await?; - let delete_token_resp = delete_network_token_from_tokenization_service( + let _delete_token_resp = delete_network_token_from_tokenization_service( state, network_token_requestor_reference_id, customer_id, @@ -486,7 +478,8 @@ pub async fn delete_network_token_from_tokenization_service( errors::ApiErrorResponse::InternalServerError })?; - if res == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { + if res + == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { status: DeleteNetworkTokenStatus::Success, }) { diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index e508713b855e..644a87a82aef 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -796,84 +796,157 @@ pub async fn save_in_locker( Option, )> { payment_method_request.validate()?; + if save_token { + save_token_in_locker( + state, + merchant_account, + payment_method_data, + payment_method_request.clone(), + amount, + currency, + ).await + + } else { + save_card_in_locker( + state, + merchant_account, + payment_method_request.clone(), + ).await + } +} + +pub async fn save_card_in_locker( + state: &SessionState, + merchant_account: &domain::MerchantAccount, + payment_method_request: api::PaymentMethodCreate, +) -> RouterResult<( + api_models::payment_methods::PaymentMethodResponse, + Option, + Option, +)> { let merchant_id = merchant_account.get_id(); let customer_id = payment_method_request .customer_id .clone() .get_required_value("customer_id")?; - if save_token { - let (token_response, network_token_requestor_ref_id) = - network_tokenization::make_card_network_tokenization_request( + match payment_method_request.card.clone() { + Some(card) => { + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( state, - payment_method_data, + payment_method_request, + &card, + &customer_id, merchant_account, - &payment_method_request.customer_id, - amount, - currency, - ) - .await?; - let card_data = api::CardDetail { - card_number: token_response.token.clone(), - card_exp_month: token_response.token_expiry_month.clone(), - card_exp_year: token_response.token_expiry_year.clone(), - card_holder_name: None, - nick_name: None, - card_issuing_country: None, - card_network: Some(token_response.card_brand.clone()), - card_issuer: None, - card_type: None, - }; - let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( - state, - payment_method_request, - &card_data, - &customer_id, - merchant_account, - None, - )) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Card Failed")?; - Ok((res, dc, network_token_requestor_ref_id)) - } else { - match payment_method_request.card.clone() { - Some(card) => { - let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( + None, + )) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Add Card Failed")?; + Ok((res, dc, None)) + } + None => { + let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); + let payment_method_response = api::PaymentMethodResponse { + merchant_id: merchant_id.clone(), + customer_id: Some(customer_id), + payment_method_id: pm_id, + payment_method: payment_method_request.payment_method, + payment_method_type: payment_method_request.payment_method_type, + #[cfg(feature = "payouts")] + bank_transfer: None, + card: None, + metadata: None, + created: Some(common_utils::date_time::now()), + recurring_enabled: false, //[#219] + installment_payment_enabled: false, //[#219] + payment_experience: Some(vec![ + api_models::enums::PaymentExperience::RedirectToUrl, + ]), //[#219] + last_used_at: Some(common_utils::date_time::now()), + client_secret: None, + }; + Ok((payment_method_response, None, None)) + } + } + + +} + +pub async fn save_token_in_locker( + state: &SessionState, + merchant_account: &domain::MerchantAccount, + payment_method_data: Option<&domain::PaymentMethodData>, + payment_method_request: api::PaymentMethodCreate, + amount: Option, + currency: Option, +) -> RouterResult<( + api_models::payment_methods::PaymentMethodResponse, + Option, + Option, +)> { + let merchant_id = merchant_account.get_id(); + let customer_id = payment_method_request + .customer_id + .clone() + .get_required_value("customer_id")?; + match payment_method_data { + Some(domain::PaymentMethodData::Card(card)) => { + let (token_response, network_token_requestor_ref_id) = + network_tokenization::make_card_network_tokenization_request( state, - payment_method_request, - &card, - &customer_id, - merchant_account, - None, - )) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Card Failed")?; - Ok((res, dc, None)) - } - None => { - let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); - let payment_method_response = api::PaymentMethodResponse { - merchant_id: merchant_id.clone(), - customer_id: Some(customer_id), - payment_method_id: pm_id, - payment_method: payment_method_request.payment_method, - payment_method_type: payment_method_request.payment_method_type, - #[cfg(feature = "payouts")] - bank_transfer: None, - card: None, - metadata: None, - created: Some(common_utils::date_time::now()), - recurring_enabled: false, //[#219] - installment_payment_enabled: false, //[#219] - payment_experience: Some(vec![ - api_models::enums::PaymentExperience::RedirectToUrl, - ]), //[#219] - last_used_at: Some(common_utils::date_time::now()), - client_secret: None, - }; - Ok((payment_method_response, None, None)) - } + payment_method_data, + &payment_method_request.customer_id, + amount, + currency, + ) + .await?; + let card_data = api::CardDetail { + card_number: token_response.token.clone(), + card_exp_month: token_response.token_expiry_month.clone(), + card_exp_year: token_response.token_expiry_year.clone(), + card_holder_name: None, + nick_name: None, + card_issuing_country: None, + card_network: Some(token_response.card_brand.clone()), + card_issuer: None, + card_type: None, + }; + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( + state, + payment_method_request, + &card_data, + &customer_id, + merchant_account, + None, + )) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Add Card Failed")?; + + Ok((res, dc, network_token_requestor_ref_id)) + } + _ => { + let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); + let payment_method_response = api::PaymentMethodResponse { + merchant_id: merchant_id.clone(), + customer_id: Some(customer_id), + payment_method_id: pm_id, + payment_method: payment_method_request.payment_method, + payment_method_type: payment_method_request.payment_method_type, + #[cfg(feature = "payouts")] + bank_transfer: None, + card: None, + metadata: None, + created: Some(common_utils::date_time::now()), + recurring_enabled: false, //[#219] + installment_payment_enabled: false, //[#219] + payment_experience: Some(vec![ + api_models::enums::PaymentExperience::RedirectToUrl, + ]), //[#219] + last_used_at: Some(common_utils::date_time::now()), + client_secret: None, + }; + Ok((payment_method_response, None, None)) } } } From 500fd86a332e2aa2fb9b629633a63f40d590e196 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 16 Aug 2024 19:21:14 +0530 Subject: [PATCH 14/59] code refactoring --- .../src/connector/adyen/transformers.rs | 3 +- .../src/connector/nexinets/transformers.rs | 1 - .../payment_methods/network_tokenization.rs | 43 +++++++++++-------- .../router/src/core/payments/tokenization.rs | 34 +++++++++------ 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index e64e65270d6e..79e51879c474 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1583,8 +1583,7 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { + | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyen"), ))? diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index 3aaf622a0200..43cb4cecec8d 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -627,7 +627,6 @@ fn get_payment_details_and_product( | PaymentMethodData::GiftCard(_) | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) - | PaymentMethodData::NetworkToken(_) | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nexinets"), ))?, diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index caed3eee1fe6..1172f55c54ed 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -59,16 +59,22 @@ pub struct ApiPayload { service: String, card_data: String, order_data: OrderData, - sub_merchant_id: String, key_id: String, should_send_token: bool, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] pub struct CardNetworkTokenResponse { payload: String, } +#[derive(Debug, Deserialize, Eq, PartialEq)] +#[serde(untagged)] +pub enum CardNTResponse { + CardNetworkTokenResponse(CardNetworkTokenResponse), + CardNetworkTokenErrorResponse(NetworkTokenErrorResponse), +} + #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardNetworkTokenResponsePayloadTemporary { @@ -155,7 +161,7 @@ pub struct DeleteNetworkTokenResponse { pub async fn make_card_network_tokenization_request( state: &routes::SessionState, - payment_method_data: Option<&domain::PaymentMethodData>, + card: &domain::Card, customer_id: &Option, amount: Option, currency: Option, @@ -164,14 +170,11 @@ pub async fn make_card_network_tokenization_request( .clone() .get_required_value("customer_id") .change_context(errors::ApiErrorResponse::InternalServerError)?; - let card_data = match payment_method_data { - Some(domain::PaymentMethodData::Card(card)) => CardData { - card_number: card.card_number.clone(), - exp_month: card.card_exp_month.clone(), - exp_year: card.card_exp_year.clone(), - card_security_code: card.card_cvc.clone(), - }, - _ => todo!(), + let card_data = CardData { + card_number: card.card_number.clone(), + exp_month: card.card_exp_month.clone(), + exp_year: card.card_exp_year.clone(), + card_security_code: card.card_cvc.clone(), }; let payload = card_data @@ -188,10 +191,17 @@ pub async fn make_card_network_tokenization_request( let enc_key = tokenization_service.public_key.peek().clone(); let key_id = tokenization_service.key_id.clone(); + println!("payloadd bytess {:?}", payload_bytes); - let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; + let jwt = encryption::encrypt_jwe( + payload_bytes, + enc_key, + "A128GCM", + Some(key_id.as_str()), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + println!("jwttt: {:?}", jwt); let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); let order_data = OrderData { @@ -205,7 +215,6 @@ pub async fn make_card_network_tokenization_request( service: "NETWORK_TOKEN".to_string(), card_data: jwt, order_data, - sub_merchant_id: "visa_sbx_working".to_string(), //should be sent in req if env is sbx, else this is not needed todo! key_id, should_send_token: true, }; @@ -225,12 +234,12 @@ pub async fn make_card_network_tokenization_request( ); request.set_body(RequestContent::Json(Box::new(api_payload))); + println!("reqq to eulerr: {:?}", request); + let response = services::call_connector_api(state, request, "generate_token") .await .change_context(errors::VaultError::SaveCardFailed); - logger::debug!("Responsee from euler: {:?}", response); - let res: CardNetworkTokenResponse = response .get_response_inner("cardNetworkTokenResponse") .change_context(errors::VaultError::FetchCardFailed) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 8dfe323c5dc4..41d67db80b4e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -190,6 +190,7 @@ where .await?; let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = merchant_account.get_id(); + let is_network_tokenization_enabled = merchant_account.is_network_tokenization_enabled; let ((mut resp, duplication_check, network_token_requestor_ref_id), token_resp) = if !state.conf.locker.locker_enabled { let (res, dc) = skip_saving_card_in_locker( @@ -212,19 +213,28 @@ where currency, )) .await?; + + if is_network_tokenization_enabled { + let (res2, dc2, network_token_requestor_ref_id) = Box::pin(save_in_locker( + state, + merchant_account, + Some(&save_payment_method_data.request.get_payment_method_data()), + payment_method_create_request.to_owned(), + true, + amount, + currency, + )) + .await?; - let (res2, dc2, network_token_requestor_ref_id) = Box::pin(save_in_locker( - state, - merchant_account, - Some(&save_payment_method_data.request.get_payment_method_data()), - payment_method_create_request.to_owned(), - true, - amount, - currency, - )) - .await?; + ((res, dc, network_token_requestor_ref_id), Some(res2)) + + } + else{ + + ((res, dc, None), None) - ((res, dc, network_token_requestor_ref_id), Some(res2)) + } + }; let token_locker_id = match token_resp { Some(ref token_resp) => Some(token_resp.payment_method_id.clone()), @@ -886,7 +896,7 @@ pub async fn save_token_in_locker( let (token_response, network_token_requestor_ref_id) = network_tokenization::make_card_network_tokenization_request( state, - payment_method_data, + &card, &payment_method_request.customer_id, amount, currency, From 069ced9937a9754799d36d1d34e97edbfc300d4e Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Sun, 18 Aug 2024 19:44:38 +0530 Subject: [PATCH 15/59] code refactoring --- crates/router/src/core/errors.rs | 28 ++++ .../payment_methods/network_tokenization.rs | 137 ++++++++++++------ .../router/src/core/payments/tokenization.rs | 109 ++++++++------ 3 files changed, 182 insertions(+), 92 deletions(-) diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index c22465c3a321..f9d77148b06b 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -329,3 +329,31 @@ pub enum ConditionalConfigError { #[error("Error constructing the Input")] InputConstructionError, } + +#[derive(Debug, thiserror::Error)] +pub enum NetworkTokenizationError { + #[error("Failed to save card in card vault")] + SaveTokenFailed, + #[error("Failed to fetch card details from card vault")] + FetchTokenFailed, + #[error("Failed to encode card vault request")] + RequestEncodingFailed, + #[error("Failed to deserialize token service response")] + ResponseDeserializationFailed, + #[error("Failed to create payment method")] + PaymentMethodCreationFailed, + #[error("The given payment method is currently not supported in vault")] + PaymentMethodNotSupported, + #[error("The given payout method is currently not supported in vault")] + PayoutMethodNotSupported, + #[error("Missing required field: {field_name}")] + MissingRequiredField { field_name: &'static str }, + #[error("The card vault returned an unexpected response: {0:?}")] + UnexpectedResponseError(bytes::Bytes), + #[error("Failed to update in PMD table")] + UpdateInPaymentMethodDataTableFailed, + #[error("Failed to fetch payment method in vault")] + FetchPaymentMethodFailed, + #[error("Failed to save payment method in vault")] + SavePaymentMethodFailed, +} diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 1172f55c54ed..b58c0457413c 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -4,7 +4,7 @@ use api_models::{enums as api_enums, payment_methods::PaymentMethodsData}; use cards::CardNumber; use common_utils::{ errors::CustomResult, - ext_traits::{Encode, OptionExt}, + ext_traits::{BytesExt, Encode}, id_type, request::RequestContent, type_name, @@ -121,6 +121,7 @@ pub struct AuthenticationDetails { #[derive(Debug, Serialize, Deserialize)] pub struct TokenResponse { authentication_details: AuthenticationDetails, + network: api_enums::CardNetwork, } #[derive(Debug, Serialize, Deserialize)] @@ -159,33 +160,14 @@ pub struct DeleteNetworkTokenResponse { status: DeleteNetworkTokenStatus, } -pub async fn make_card_network_tokenization_request( +pub async fn mk_tokenization_req( state: &routes::SessionState, - card: &domain::Card, - customer_id: &Option, - amount: Option, - currency: Option, -) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::ApiErrorResponse> { - let customer_id = customer_id - .clone() - .get_required_value("customer_id") - .change_context(errors::ApiErrorResponse::InternalServerError)?; - let card_data = CardData { - card_number: card.card_number.clone(), - exp_month: card.card_exp_month.clone(), - exp_year: card.card_exp_year.clone(), - card_security_code: card.card_cvc.clone(), - }; - - let payload = card_data - .encode_to_string_of_json() - .and_then(|x| x.encode_to_string_of_json()) - .change_context(errors::VaultError::FetchCardFailed) - .map_err(|e| { - logger::error!(fetch_err=?e); - errors::ApiErrorResponse::InternalServerError - })?; - let payload_bytes = payload.as_bytes(); + payload_bytes: &[u8], + amount: String, + currency: String, + customer_id: id_type::CustomerId, +) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError> +{ let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let enc_key = tokenization_service.public_key.peek().clone(); @@ -200,15 +182,14 @@ pub async fn make_card_network_tokenization_request( Some(key_id.as_str()), ) .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - println!("jwttt: {:?}", jwt); - let amount_str = amount.map_or_else(String::new, |a| a.to_string()); - let currency_str = currency.map_or_else(String::new, |c| c.to_string()); + .change_context(errors::NetworkTokenizationError::SaveTokenFailed) + .attach_printable("Error on jwe encrypt")?; + let order_data = OrderData { consent_id: "test12324".to_string(), // ?? - customer_id: customer_id.clone(), - amount: amount_str, - currency: currency_str, + customer_id, + amount, + currency, }; let api_payload = ApiPayload { @@ -238,37 +219,97 @@ pub async fn make_card_network_tokenization_request( let response = services::call_connector_api(state, request, "generate_token") .await - .change_context(errors::VaultError::SaveCardFailed); + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); - let res: CardNetworkTokenResponse = response - .get_response_inner("cardNetworkTokenResponse") - .change_context(errors::VaultError::FetchCardFailed) - .map_err(|e| { - logger::error!(fetch_err=?e); - errors::ApiErrorResponse::InternalServerError + let res = response + .map_err(|error| { + error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) + }) + .attach_printable("Error while receiving response") + .and_then(|inner| match inner { + Err(err_res) => { + let parsed_error: NetworkTokenErrorResponse = err_res + .response + .parse_struct("Card Network Tokenization Response") + .change_context( + errors::NetworkTokenizationError::ResponseDeserializationFailed, + )?; + logger::error!( + error_code = %parsed_error.error_info.code, + developer_message = %parsed_error.error_info.developer_message, + "Network tokenization error: {}", + parsed_error.error_message + ); + Err(errors::NetworkTokenizationError::ResponseDeserializationFailed) + .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) + } + Ok(res) => Ok(res), + }) + .inspect_err(|err| { + logger::error!("Error while deserializing response: {:?}", err); })?; + + let network_response: CardNetworkTokenResponse = res + .response + .parse_struct("Card Network Tokenization Response") + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + let dec_key = tokenization_service.private_key.peek().clone(); let card_network_token_response = services::decrypt_jwe( - &res.payload, + &network_response.payload, services::KeyIdCheck::SkipKeyIdCheck, dec_key, jwe::RSA_OAEP_256, ) .await - .change_context(errors::ApiErrorResponse::InternalServerError) + .change_context(errors::NetworkTokenizationError::SaveTokenFailed) .attach_printable( "Failed to decrypt the tokenization response from the tokenization service", )?; let cn_response: CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) - .change_context(errors::VaultError::ResponseDeserializationFailed) - .change_context(errors::ApiErrorResponse::InternalServerError)?; - + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; Ok((cn_response.clone(), Some(cn_response.card_reference))) } +pub async fn make_card_network_tokenization_request( + state: &routes::SessionState, + card: &domain::Card, + customer_id: &id_type::CustomerId, + amount: Option, + currency: Option, +) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError> +{ + let card_data = CardData { + card_number: card.card_number.clone(), + exp_month: card.card_exp_month.clone(), + exp_year: card.card_exp_year.clone(), + card_security_code: card.card_cvc.clone(), + }; + + let payload = card_data + .encode_to_string_of_json() + .and_then(|x| x.encode_to_string_of_json()) + .change_context(errors::NetworkTokenizationError::RequestEncodingFailed) + .map_err(|e| { + logger::error!(fetch_err=?e); + errors::NetworkTokenizationError::RequestEncodingFailed + })?; + println!("payloaddd: {}", payload); + let payload_bytes = payload.as_bytes(); + let amount_str = amount.map_or_else(String::new, |a| a.to_string()); + let currency_str = currency.map_or_else(String::new, |c| c.to_string()); + + mk_tokenization_req(state, payload_bytes, amount_str, currency_str, customer_id.clone()) + .await + .inspect_err(|e| logger::error!(error = %e, "Error while making tokenization request")) + + + +} + pub async fn get_token_from_tokenization_service( state: &routes::SessionState, merchant_account: &domain::MerchantAccount, @@ -309,7 +350,7 @@ pub async fn get_token_from_tokenization_service( // Send the request using `call_connector_api` let response = services::call_connector_api(state, request, "get network token") .await - .change_context(errors::VaultError::SaveCardFailed); + .change_context(errors::NetworkTokenizationError::SaveTokenFailed); let res: TokenResponse = response .get_response_inner("cardNetworkTokenResponse") @@ -353,7 +394,7 @@ pub async fn get_token_from_tokenization_service( .unwrap_or_default(), nick_name: card_decrypted.clone().unwrap().card_holder_name, card_issuer: None, - card_network: Some(common_enums::CardNetwork::Visa), + card_network: Some(res.network), card_type: None, card_issuing_country: None, bank_code: None, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 41d67db80b4e..9c5366d3a45e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -190,7 +190,8 @@ where .await?; let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = merchant_account.get_id(); - let is_network_tokenization_enabled = merchant_account.is_network_tokenization_enabled; + let is_network_tokenization_enabled = + merchant_account.is_network_tokenization_enabled; let ((mut resp, duplication_check, network_token_requestor_ref_id), token_resp) = if !state.conf.locker.locker_enabled { let (res, dc) = skip_saving_card_in_locker( @@ -213,28 +214,27 @@ where currency, )) .await?; - + if is_network_tokenization_enabled { - let (res2, dc2, network_token_requestor_ref_id) = Box::pin(save_in_locker( - state, - merchant_account, - Some(&save_payment_method_data.request.get_payment_method_data()), - payment_method_create_request.to_owned(), - true, - amount, - currency, - )) - .await?; + println!("tokennn"); + let (res2, dc2, network_token_requestor_ref_id) = + Box::pin(save_in_locker( + state, + merchant_account, + Some( + &save_payment_method_data.request.get_payment_method_data(), + ), + payment_method_create_request.to_owned(), + true, + amount, + currency, + )) + .await?; ((res, dc, network_token_requestor_ref_id), Some(res2)) - - } - else{ - + } else { ((res, dc, None), None) - } - }; let token_locker_id = match token_resp { Some(ref token_resp) => Some(token_resp.payment_method_id.clone()), @@ -891,41 +891,62 @@ pub async fn save_token_in_locker( .customer_id .clone() .get_required_value("customer_id")?; + + // if (card) { + // if let Ok(a) = try_tokenize() { + // return a + // } + // } + + // rest of the flow match payment_method_data { Some(domain::PaymentMethodData::Card(card)) => { - let (token_response, network_token_requestor_ref_id) = + let tokenization_result = network_tokenization::make_card_network_tokenization_request( state, &card, - &payment_method_request.customer_id, + &customer_id, amount, currency, ) - .await?; - let card_data = api::CardDetail { - card_number: token_response.token.clone(), - card_exp_month: token_response.token_expiry_month.clone(), - card_exp_year: token_response.token_expiry_year.clone(), - card_holder_name: None, - nick_name: None, - card_issuing_country: None, - card_network: Some(token_response.card_brand.clone()), - card_issuer: None, - card_type: None, - }; - let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( - state, - payment_method_request, - &card_data, - &customer_id, - merchant_account, - None, - )) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Card Failed")?; + .await; + match tokenization_result { + Ok((token_response, network_token_requestor_ref_id)) => { + // Only proceed if the tokenization was successful + + let card_data = api::CardDetail { + card_number: token_response.token.clone(), + card_exp_month: token_response.token_expiry_month.clone(), + card_exp_year: token_response.token_expiry_year.clone(), + card_holder_name: None, + nick_name: None, + card_issuing_country: None, + card_network: Some(token_response.card_brand.clone()), + card_issuer: None, + card_type: None, + }; + + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( + state, + payment_method_request, + &card_data, + &customer_id, + merchant_account, + None, + )) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Add Card Failed")?; + + Ok((res, dc, network_token_requestor_ref_id)) + } + Err(e) => { + // If tokenization fails, return the error without propagating further + logger::error!(error=?e, "Card network tokenization failed"); + Err(errors::ApiErrorResponse::InternalServerError).attach_printable(format!("Card network tokenization failed: {e:?}")) + } + } - Ok((res, dc, network_token_requestor_ref_id)) } _ => { let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); From e23f2c7d82a8b26775a3745ab77ad422cf945fc0 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 19 Aug 2024 00:56:44 +0530 Subject: [PATCH 16/59] tokenize the card when the card network is in the list of network_tokenization_supported_card_networks --- .../src/connector/cybersource/transformers.rs | 3 +- .../router/src/core/payments/tokenization.rs | 106 +++++++++--------- 2 files changed, 54 insertions(+), 55 deletions(-) diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 171c1767ef38..ab0468088c5c 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -1504,8 +1504,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) - | domain::PaymentMethodData::NetworkToken(_) => { + | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), ) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 9c5366d3a45e..3956b3df96ce 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -237,7 +237,13 @@ where } }; let token_locker_id = match token_resp { - Some(ref token_resp) => Some(token_resp.payment_method_id.clone()), + Some(ref token_resp) => { + if network_token_requestor_ref_id.is_some() { + Some(token_resp.payment_method_id.clone()) + } else { + None + } + } None => None, }; @@ -891,29 +897,30 @@ pub async fn save_token_in_locker( .customer_id .clone() .get_required_value("customer_id")?; - - // if (card) { - // if let Ok(a) = try_tokenize() { - // return a - // } - // } - - // rest of the flow - match payment_method_data { - Some(domain::PaymentMethodData::Card(card)) => { - let tokenization_result = - network_tokenization::make_card_network_tokenization_request( - state, - &card, - &customer_id, - amount, - currency, - ) - .await; - match tokenization_result { - Ok((token_response, network_token_requestor_ref_id)) => { + let network_tokenization_supported_card_networks = &state + .conf + .network_tokenization_supported_card_networks + .card_networks; + + if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data { + println!("tokenn entered "); + println!("cardd {:?}", card); + if let Some(card_network) = &card.card_network { + println!("tokenn cn verified"); + if network_tokenization_supported_card_networks.contains(card_network) { + println!("calling tn serivce"); + if let Ok((token_response, network_token_requestor_ref_id)) = + network_tokenization::make_card_network_tokenization_request( + state, + &card, + &customer_id, + amount, + currency, + ) + .await + { // Only proceed if the tokenization was successful - + println!("tokenn success"); let card_data = api::CardDetail { card_number: token_response.token.clone(), card_exp_month: token_response.token_expiry_month.clone(), @@ -925,7 +932,7 @@ pub async fn save_token_in_locker( card_issuer: None, card_type: None, }; - + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( state, payment_method_request, @@ -937,39 +944,32 @@ pub async fn save_token_in_locker( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Add Card Failed")?; - - Ok((res, dc, network_token_requestor_ref_id)) - } - Err(e) => { - // If tokenization fails, return the error without propagating further - logger::error!(error=?e, "Card network tokenization failed"); - Err(errors::ApiErrorResponse::InternalServerError).attach_printable(format!("Card network tokenization failed: {e:?}")) + + return Ok((res, dc, network_token_requestor_ref_id)); } } - - } - _ => { - let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); - let payment_method_response = api::PaymentMethodResponse { - merchant_id: merchant_id.clone(), - customer_id: Some(customer_id), - payment_method_id: pm_id, - payment_method: payment_method_request.payment_method, - payment_method_type: payment_method_request.payment_method_type, - #[cfg(feature = "payouts")] - bank_transfer: None, - card: None, - metadata: None, - created: Some(common_utils::date_time::now()), - recurring_enabled: false, //[#219] - installment_payment_enabled: false, //[#219] - payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), //[#219] - last_used_at: Some(common_utils::date_time::now()), - client_secret: None, - }; - Ok((payment_method_response, None, None)) } } + + let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); + let payment_method_response = api::PaymentMethodResponse { + merchant_id: merchant_id.clone(), + customer_id: Some(customer_id), + payment_method_id: pm_id, + payment_method: payment_method_request.payment_method, + payment_method_type: payment_method_request.payment_method_type, + #[cfg(feature = "payouts")] + bank_transfer: None, + card: None, + metadata: None, + created: Some(common_utils::date_time::now()), + recurring_enabled: false, //[#219] + installment_payment_enabled: false, //[#219] + payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), //[#219] + last_used_at: Some(common_utils::date_time::now()), + client_secret: None, + }; + Ok((payment_method_response, None, None)) } pub fn create_payment_method_metadata( From ac2c2e760d39a985c78bb164864aa761cf47d64c Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 19 Aug 2024 00:58:12 +0530 Subject: [PATCH 17/59] Merge main --- CHANGELOG.md | 67 ++ README.md | 11 + api-reference-v2/openapi_spec.json | 27 +- .../event/events--delivery-attempt-list.mdx | 2 +- .../api-reference/event/events--list.mdx | 2 +- .../event/events--manual-retry.mdx | 2 +- api-reference/openapi_spec.json | 43 +- config/config.example.toml | 1 + config/deployments/integration_test.toml | 1 + config/deployments/production.toml | 3 +- config/deployments/sandbox.toml | 1 + config/development.toml | 2 + config/docker_compose.toml | 2 + crates/analytics/src/opensearch.rs | 14 +- crates/api_models/src/admin.rs | 14 +- crates/api_models/src/customers.rs | 114 +++ crates/api_models/src/enums.rs | 8 +- crates/api_models/src/events/customer.rs | 13 +- crates/api_models/src/payouts.rs | 5 +- crates/api_models/src/webhook_events.rs | 21 +- crates/common_enums/src/enums.rs | 13 +- crates/common_utils/src/events.rs | 2 +- crates/connector_configs/src/connector.rs | 4 +- .../src/response_modifier.rs | 8 + .../connector_configs/toml/development.toml | 40 + crates/connector_configs/toml/production.toml | 40 + crates/connector_configs/toml/sandbox.toml | 40 + crates/diesel_models/src/business_profile.rs | 12 +- crates/diesel_models/src/customers.rs | 8 + crates/diesel_models/src/kv.rs | 5 + .../src/merchant_connector_account.rs | 8 +- .../src/query/business_profile.rs | 14 + crates/diesel_models/src/query/customers.rs | 153 ++- .../diesel_models/src/query/payout_attempt.rs | 21 +- crates/diesel_models/src/query/payouts.rs | 52 +- crates/diesel_models/src/query/user_role.rs | 83 +- crates/diesel_models/src/schema.rs | 1 + crates/diesel_models/src/schema_v2.rs | 4 +- .../hyperswitch_connectors/src/connectors.rs | 3 +- .../src/connectors/taxjar.rs | 563 +++++++++++ .../src/connectors/taxjar/transformers.rs | 228 +++++ .../src/default_implementations.rs | 84 +- .../src/default_implementations_v2.rs | 66 +- .../src/business_profile.rs | 55 +- .../hyperswitch_domain_models/src/consts.rs | 9 +- .../hyperswitch_domain_models/src/customer.rs | 16 +- .../src/merchant_connector_account.rs | 22 +- .../src/payment_address.rs | 24 + .../src/payouts/payout_attempt.rs | 6 +- .../src/payouts/payouts.rs | 25 +- crates/hyperswitch_interfaces/src/configs.rs | 1 + crates/kgraph_utils/benches/evaluation.rs | 4 +- crates/kgraph_utils/src/mca.rs | 4 +- .../src/routes/merchant_connector_account.rs | 8 +- crates/openapi/src/routes/webhook_events.rs | 19 +- crates/redis_interface/src/commands.rs | 64 +- crates/redis_interface/src/errors.rs | 10 + .../src/compatibility/stripe/customers.rs | 11 +- .../compatibility/stripe/customers/types.rs | 2 +- crates/router/src/connector.rs | 2 +- crates/router/src/connector/aci.rs | 45 +- .../router/src/connector/aci/transformers.rs | 26 +- crates/router/src/connector/ebanx.rs | 41 +- .../src/connector/ebanx/transformers.rs | 27 +- crates/router/src/connector/forte.rs | 39 +- .../src/connector/forte/transformers.rs | 46 +- crates/router/src/connector/globepay.rs | 35 +- .../src/connector/globepay/transformers.rs | 40 +- crates/router/src/connector/multisafepay.rs | 45 +- .../connector/multisafepay/transformers.rs | 27 +- crates/router/src/connector/paybox.rs | 146 +-- .../src/connector/paybox/transformers.rs | 710 ++++++++++++-- crates/router/src/core/admin.rs | 111 ++- crates/router/src/core/customers.rs | 531 ++++++++--- crates/router/src/core/payment_methods.rs | 27 +- .../router/src/core/payment_methods/cards.rs | 129 ++- .../src/core/payment_methods/validator.rs | 12 + crates/router/src/core/payments.rs | 22 +- .../connector_integration_v2_impls.rs | 3 + crates/router/src/core/payments/flows.rs | 4 + .../src/core/payments/flows/session_flow.rs | 17 +- crates/router/src/core/payments/helpers.rs | 15 +- .../payments/operations/payment_confirm.rs | 55 +- .../payments/operations/payment_update.rs | 8 +- .../router/src/core/payments/transformers.rs | 24 +- crates/router/src/core/payout_link.rs | 14 + crates/router/src/core/payouts.rs | 129 ++- crates/router/src/core/payouts/validator.rs | 2 +- crates/router/src/core/pm_auth.rs | 4 +- crates/router/src/core/routing.rs | 122 ++- crates/router/src/core/routing/helpers.rs | 4 +- crates/router/src/core/user_role.rs | 175 +++- crates/router/src/core/utils.rs | 17 +- crates/router/src/core/webhooks/incoming.rs | 7 +- .../src/core/webhooks/webhook_events.rs | 181 ++-- crates/router/src/db/business_profile.rs | 64 ++ crates/router/src/db/customers.rs | 882 ++++++++++-------- crates/router/src/db/events.rs | 72 -- crates/router/src/db/kafka_store.rs | 189 +++- .../src/db/merchant_connector_account.rs | 32 +- crates/router/src/db/user_role.rs | 191 +++- crates/router/src/routes/app.rs | 26 +- crates/router/src/routes/customers.rs | 48 +- crates/router/src/routes/payment_methods.rs | 12 +- crates/router/src/routes/routing.rs | 94 +- crates/router/src/routes/webhook_events.rs | 24 +- crates/router/src/services/authentication.rs | 64 -- crates/router/src/types/api.rs | 19 +- crates/router/src/types/api/customers.rs | 4 +- crates/router/src/types/transformers.rs | 3 +- crates/router/src/utils.rs | 98 ++ crates/router/tests/connectors/aci.rs | 17 +- crates/router/tests/connectors/ebanx.rs | 2 +- crates/router/tests/connectors/forte.rs | 2 +- crates/router/tests/connectors/globepay.rs | 2 +- crates/router/tests/connectors/main.rs | 1 + .../router/tests/connectors/multisafepay.rs | 2 +- .../router/tests/connectors/sample_auth.toml | 5 +- crates/router/tests/connectors/taxjar.rs | 420 +++++++++ crates/storage_impl/src/mock_db/payouts.rs | 24 + crates/storage_impl/src/payouts/payouts.rs | 194 +++- crates/storage_impl/src/redis/kv_store.rs | 20 + crates/test_utils/src/connector_auth.rs | 1 + .../e2e/PaymentTest/00020-Variations.cy.js | 2 +- .../cypress/e2e/PaymentUtils/Commons.js | 1 + .../cypress/e2e/PaymentUtils/Iatapay.js | 2 +- .../cypress/e2e/PaymentUtils/Paybox.js | 136 +++ .../cypress/e2e/PaymentUtils/Utils.js | 2 + cypress-tests/cypress/support/commands.js | 2 +- cypress-tests/package.json | 1 + loadtest/config/development.toml | 2 + .../down.sql | 5 - .../2022-09-29-093314_create_seed_data/up.sql | 27 - .../down.sql | 2 + .../up.sql | 3 + scripts/add_connector.sh | 2 +- .../2024-07-30-100323_customer_v2/up.sql | 1 + .../down.sql | 22 +- .../up.sql | 30 +- .../down.sql | 2 + .../up.sql | 3 + 141 files changed, 5959 insertions(+), 1725 deletions(-) create mode 100644 crates/hyperswitch_connectors/src/connectors/taxjar.rs create mode 100644 crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs create mode 100644 crates/router/tests/connectors/taxjar.rs create mode 100644 cypress-tests/cypress/e2e/PaymentUtils/Paybox.js delete mode 100644 migrations/2022-09-29-093314_create_seed_data/down.sql delete mode 100644 migrations/2022-09-29-093314_create_seed_data/up.sql create mode 100644 migrations/2024-08-12-104928_add_api_version_in_mca/down.sql create mode 100644 migrations/2024-08-12-104928_add_api_version_in_mca/up.sql create mode 100644 v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/down.sql create mode 100644 v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/up.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b55338ed1c..26f09bcdb8e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,73 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.08.15.0 + +### Features + +- **connector:** [Paybox] add paybox connector ([#5575](https://github.com/juspay/hyperswitch/pull/5575)) ([`e4f4fba`](https://github.com/juspay/hyperswitch/commit/e4f4fbafe643b25078ebab6d054c6fa41a081f1a)) +- **customer_v2:** Customer v2 refactor customer v2 update endpoint ([#5490](https://github.com/juspay/hyperswitch/pull/5490)) ([`17703fe`](https://github.com/juspay/hyperswitch/commit/17703fe2cbd693cf4f417c061df3c42c63ebc745)) +- **payout_link:** Return total_count in filtered payouts list API response ([#5538](https://github.com/juspay/hyperswitch/pull/5538)) ([`34f648e`](https://github.com/juspay/hyperswitch/commit/34f648e29ba91601c9d78dc95ab405dc8d6ba97e)) +- **users:** Add support for profile user delete ([#5541](https://github.com/juspay/hyperswitch/pull/5541)) ([`19a9180`](https://github.com/juspay/hyperswitch/commit/19a9180925cf1a73aac74b5dfe5b68ba658718be)) + +### Bug Fixes + +- **cypress:** + - Fix naming convention in iatapay ideal ([#5618](https://github.com/juspay/hyperswitch/pull/5618)) ([`fe19127`](https://github.com/juspay/hyperswitch/commit/fe19127135ef0ffb702b64bb3d04ea8015109f6e)) + - Fix hooks in cypress for 3ds ([#5617](https://github.com/juspay/hyperswitch/pull/5617)) ([`d43648b`](https://github.com/juspay/hyperswitch/commit/d43648b9f5f87ce2e46e9e2dc49327c8a6af0f7d)) +- **opensearch:** Sorted the global search results in descending order ([#5623](https://github.com/juspay/hyperswitch/pull/5623)) ([`27096e7`](https://github.com/juspay/hyperswitch/commit/27096e71e451c7b30d2ec0be8aea68f226984fe4)) +- **payout:** Use mca_connector_id from ConnectorData to fetch mca_account ([#5620](https://github.com/juspay/hyperswitch/pull/5620)) ([`92a07cf`](https://github.com/juspay/hyperswitch/commit/92a07cf5e4fcce6bf83f1802e07b1dd0b9cf67e2)) +- **pm_auth:** Fixed deserialize logic in pm_auth core ([#5615](https://github.com/juspay/hyperswitch/pull/5615)) ([`f66b09f`](https://github.com/juspay/hyperswitch/commit/f66b09fed50bb89cd512d01af22a51107a0c9869)) +- **router:** Allow payments update for requires_payment_method and requires_confirmation intent status only ([#5616](https://github.com/juspay/hyperswitch/pull/5616)) ([`805540a`](https://github.com/juspay/hyperswitch/commit/805540a1d02ec6ce61e21c66349dbb0fb3403e69)) + +### Refactors + +- **connector:** + - Added amount conversion framework for Ebanx ([#5452](https://github.com/juspay/hyperswitch/pull/5452)) ([`67d580c`](https://github.com/juspay/hyperswitch/commit/67d580c0ebcfc070ad6ff5cab4079ca447541ee4)) + - Added amount conversion framework for multisafepay ([#4982](https://github.com/juspay/hyperswitch/pull/4982)) ([`556dd10`](https://github.com/juspay/hyperswitch/commit/556dd103dbdf46bb84b247547dd7cca3d2eec91c)) + - Add amount conversion framework to Globepay ([#5470](https://github.com/juspay/hyperswitch/pull/5470)) ([`66cc0a3`](https://github.com/juspay/hyperswitch/commit/66cc0a3b395d95bce56c63c4a561b7b42122d28e)) + - Add amount conversion framework to Forte ([#5461](https://github.com/juspay/hyperswitch/pull/5461)) ([`2249010`](https://github.com/juspay/hyperswitch/commit/2249010ceb9fc03249d8feb428f2209b2d2ee9f4)) +- **redis:** Add redis commands required for success rate based routing ([#5610](https://github.com/juspay/hyperswitch/pull/5610)) ([`340714c`](https://github.com/juspay/hyperswitch/commit/340714ce1b8359bb908ec3873c88822a2c0054f0)) +- **router:** + - Add api_version and make profile_id mandatory in mca v2 ([#5602](https://github.com/juspay/hyperswitch/pull/5602)) ([`56791c2`](https://github.com/juspay/hyperswitch/commit/56791c27437f5df331447c617c7b2b8041775e86)) + - Use the saved billing details in the recurring payments ([#5631](https://github.com/juspay/hyperswitch/pull/5631)) ([`5fa7b14`](https://github.com/juspay/hyperswitch/commit/5fa7b147aa81832dcda767efa5c02f2e82131f73)) +- **webhook_events:** Allow listing unique webhook events based on profile ID ([#5598](https://github.com/juspay/hyperswitch/pull/5598)) ([`8bcda2c`](https://github.com/juspay/hyperswitch/commit/8bcda2cea480083179bd071e8ff466103e61efc1)) + +### Miscellaneous Tasks + +- Wasm changes required for plaid ([#5633](https://github.com/juspay/hyperswitch/pull/5633)) ([`8fa51b7`](https://github.com/juspay/hyperswitch/commit/8fa51b7b1cdf4347dc5778859597aa9ed1691790)) + +**Full Changelog:** [`2024.08.14.0...2024.08.15.0`](https://github.com/juspay/hyperswitch/compare/2024.08.14.0...2024.08.15.0) + +- - - + +## 2024.08.14.0 + +### Features + +- **connector:** Create Taxjar connector ([#5597](https://github.com/juspay/hyperswitch/pull/5597)) ([`0ab0aa1`](https://github.com/juspay/hyperswitch/commit/0ab0aa1a94fe98719d51ff89d27935a30cb33721)) + +### Bug Fixes + +- **router:** Return missing required field error when a domain is missing during apple pay session call ([#5596](https://github.com/juspay/hyperswitch/pull/5596)) ([`751ba15`](https://github.com/juspay/hyperswitch/commit/751ba15482f6e4aed403ff947eea01b75093087e)) + +### Refactors + +- **connector:** Add amount conversion framework for ACI ([#5456](https://github.com/juspay/hyperswitch/pull/5456)) ([`93aa522`](https://github.com/juspay/hyperswitch/commit/93aa522929fc8538894bd4bc934cb17a793d2e24)) +- **routing:** Refactor fallback routing apis for v2 ([#5592](https://github.com/juspay/hyperswitch/pull/5592)) ([`051086f`](https://github.com/juspay/hyperswitch/commit/051086f7b657600efe2d6a41023572610ef41ccb)) + +### Documentation + +- **README:** Add social media links ([#5600](https://github.com/juspay/hyperswitch/pull/5600)) ([`e757605`](https://github.com/juspay/hyperswitch/commit/e757605fdc287685c573d0c1c461cea556e5a5ce)) + +### Miscellaneous Tasks + +- Run pml independently ([#5604](https://github.com/juspay/hyperswitch/pull/5604)) ([`f5b2eec`](https://github.com/juspay/hyperswitch/commit/f5b2eec015bce088ed666960228769b40d76b2c0)) + +**Full Changelog:** [`2024.08.13.0...2024.08.14.0`](https://github.com/juspay/hyperswitch/compare/2024.08.13.0...2024.08.14.0) + +- - - + ## 2024.08.13.0 ### Features diff --git a/README.md b/README.md index 62e016d36203..f4729db95399 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,17 @@ The single API to access payment ecosystems across 130+ countries

+

+ + + + + + + + + +


diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index 8974f940bac1..a52ebcdf9be6 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -20,7 +20,7 @@ } ], "paths": { - "/connector_accounts": { + "/v2/connector_accounts": { "post": { "tags": [ "Merchant Connector Account" @@ -95,7 +95,7 @@ ] } }, - "/connector_accounts/{id}": { + "/v2/connector_accounts/{id}": { "get": { "tags": [ "Merchant Connector Account" @@ -3577,6 +3577,7 @@ "noon", "nuvei", "opennode", + "paybox", "payme", "payone", "paypal", @@ -7177,6 +7178,7 @@ "required": [ "connector_type", "connector_name", + "profile_id", "merchant_id" ], "properties": { @@ -7188,14 +7190,13 @@ }, "connector_label": { "type": "string", - "description": "This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports. Eg: if your profile label is `default`, connector label can be `stripe_default`", + "description": "This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports, If not passed then if will take `connector_name`_`profile_name`. Eg: if your profile label is `default`, connector label can be `stripe_default`", "example": "stripe_US_travel", "nullable": true }, "profile_id": { "type": "string", - "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", - "nullable": true + "description": "Identifier for the business profile, if not provided default will be chosen from merchant account" }, "connector_account_details": { "allOf": [ @@ -7394,6 +7395,7 @@ "connector_type", "connector_name", "id", + "profile_id", "status" ], "properties": { @@ -7417,7 +7419,6 @@ "profile_id": { "type": "string", "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", - "nullable": true, "maxLength": 64 }, "payment_methods_enabled": { @@ -7518,6 +7519,7 @@ "connector_type", "connector_name", "id", + "profile_id", "status" ], "properties": { @@ -7541,7 +7543,6 @@ "profile_id": { "type": "string", "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", - "nullable": true, "maxLength": 64 }, "connector_account_details": { @@ -7666,7 +7667,7 @@ }, "connector_label": { "type": "string", - "description": "This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports. Eg: if your profile label is `default`, connector label can be `stripe_default`", + "description": "This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports, If not passed then if will take `connector_name`_`profile_name`. Eg: if your profile label is `default`, connector label can be `stripe_default`", "example": "stripe_US_travel", "nullable": true }, @@ -14252,7 +14253,14 @@ "type": "array", "items": { "$ref": "#/components/schemas/PayoutCreateResponse" - } + }, + "description": "The list of payouts response objects" + }, + "total_count": { + "type": "integer", + "format": "int64", + "description": "The total number of available payouts for given constraints", + "nullable": true } } }, @@ -15493,6 +15501,7 @@ "noon", "nuvei", "opennode", + "paybox", "payme", "payone", "paypal", diff --git a/api-reference/api-reference/event/events--delivery-attempt-list.mdx b/api-reference/api-reference/event/events--delivery-attempt-list.mdx index 04cad3e5e17b..52ed085f9d3b 100644 --- a/api-reference/api-reference/event/events--delivery-attempt-list.mdx +++ b/api-reference/api-reference/event/events--delivery-attempt-list.mdx @@ -1,3 +1,3 @@ --- -openapi: get /events/{merchant_id_or_profile_id}/{event_id}/attempts +openapi: get /events/{merchant_id}/{event_id}/attempts --- \ No newline at end of file diff --git a/api-reference/api-reference/event/events--list.mdx b/api-reference/api-reference/event/events--list.mdx index 580b705052ce..0b02af87e61f 100644 --- a/api-reference/api-reference/event/events--list.mdx +++ b/api-reference/api-reference/event/events--list.mdx @@ -1,3 +1,3 @@ --- -openapi: get /events/{merchant_id_or_profile_id} +openapi: get /events/{merchant_id} --- \ No newline at end of file diff --git a/api-reference/api-reference/event/events--manual-retry.mdx b/api-reference/api-reference/event/events--manual-retry.mdx index 2bc2fce304a1..d52cb414bbf9 100644 --- a/api-reference/api-reference/event/events--manual-retry.mdx +++ b/api-reference/api-reference/event/events--manual-retry.mdx @@ -1,3 +1,3 @@ --- -openapi: post /events/{merchant_id_or_profile_id}/{event_id}/retry +openapi: post /events/{merchant_id}/{event_id}/retry --- \ No newline at end of file diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 5abc03b8a2af..a935a066af21 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -4658,7 +4658,7 @@ ] } }, - "/events/{merchant_id_or_profile_id}": { + "/events/{merchant_id}": { "get": { "tags": [ "Event" @@ -4668,9 +4668,9 @@ "operationId": "List all Events associated with a Merchant Account or Business Profile", "parameters": [ { - "name": "merchant_id_or_profile_id", + "name": "merchant_id", "in": "path", - "description": "The unique identifier for the Merchant Account or Business Profile", + "description": "The unique identifier for the Merchant Account.", "required": true, "schema": { "type": "string" @@ -4729,6 +4729,16 @@ "type": "string", "nullable": true } + }, + { + "name": "profile_id", + "in": "query", + "description": "Only include Events associated with the Business Profile identified by the specified Business Profile ID.", + "required": false, + "schema": { + "type": "string", + "nullable": true + } } ], "responses": { @@ -4753,7 +4763,7 @@ ] } }, - "/events/{merchant_id_or_profile_id}/{event_id}/attempts": { + "/events/{merchant_id}/{event_id}/attempts": { "get": { "tags": [ "Event" @@ -4763,9 +4773,9 @@ "operationId": "List all delivery attempts for an Event", "parameters": [ { - "name": "merchant_id_or_profile_id", + "name": "merchant_id", "in": "path", - "description": "The unique identifier for the Merchant Account or Business Profile", + "description": "The unique identifier for the Merchant Account.", "required": true, "schema": { "type": "string" @@ -4803,7 +4813,7 @@ ] } }, - "/events/{merchant_id_or_profile_id}/{event_id}/retry": { + "/events/{merchant_id}/{event_id}/retry": { "post": { "tags": [ "Event" @@ -4813,9 +4823,9 @@ "operationId": "Manually retry the delivery of an Event", "parameters": [ { - "name": "merchant_id_or_profile_id", + "name": "merchant_id", "in": "path", - "description": "The unique identifier for the Merchant Account or Business Profile", + "description": "The unique identifier for the Merchant Account.", "required": true, "schema": { "type": "string" @@ -8045,6 +8055,7 @@ "noon", "nuvei", "opennode", + "paybox", "payme", "payone", "paypal", @@ -12175,6 +12186,7 @@ "connector_type", "connector_name", "merchant_connector_id", + "profile_id", "status" ], "properties": { @@ -12198,7 +12210,6 @@ "profile_id": { "type": "string", "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", - "nullable": true, "maxLength": 64 }, "payment_methods_enabled": { @@ -12326,6 +12337,7 @@ "connector_type", "connector_name", "merchant_connector_id", + "profile_id", "status" ], "properties": { @@ -12349,7 +12361,6 @@ "profile_id": { "type": "string", "description": "Identifier for the business profile, if not provided default will be chosen from merchant account", - "nullable": true, "maxLength": 64 }, "connector_account_details": { @@ -19129,7 +19140,14 @@ "type": "array", "items": { "$ref": "#/components/schemas/PayoutCreateResponse" - } + }, + "description": "The list of payouts response objects" + }, + "total_count": { + "type": "integer", + "format": "int64", + "description": "The total number of available payouts for given constraints", + "nullable": true } } }, @@ -20766,6 +20784,7 @@ "noon", "nuvei", "opennode", + "paybox", "payme", "payone", "paypal", diff --git a/config/config.example.toml b/config/config.example.toml index 28781ffca406..9a110cbedc32 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -246,6 +246,7 @@ square.base_url = "https://connect.squareupsandbox.com/" square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" +taxjar.base_url = "https://api.sandbox.taxjar.com/v2/" threedsecureio.base_url = "https://service.sandbox.3dsecure.io" stripe.base_url_file_upload = "https://files.stripe.com/" trustpay.base_url = "https://test-tpgw.trustpay.eu/" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index 09aa3975abc3..d25ed1698286 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -86,6 +86,7 @@ square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" stripe.base_url_file_upload = "https://files.stripe.com/" +taxjar.base_url = "https://api.sandbox.taxjar.com/v2/" trustpay.base_url = "https://test-tpgw.trustpay.eu/" trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 762ecdc96cd5..bac5ec206e6e 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -70,7 +70,7 @@ noon.key_mode = "Live" nuvei.base_url = "https://ppp-test.nuvei.com/" opayo.base_url = "https://pi-live.sagepay.com/" opennode.base_url = "https://api.opennode.com" -paybox.base_url = "https://preprod-ppps.paybox.com/PPPS.php" +paybox.base_url = "https://ppps.paybox.com/PPPS.php" payeezy.base_url = "https://api.payeezy.com/" payme.base_url = "https://live.payme.io/" payone.base_url = "https://payment.payone.com/" @@ -90,6 +90,7 @@ square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" stripe.base_url_file_upload = "https://files.stripe.com/" +taxjar.base_url = "https://api.taxjar.com/v2/" trustpay.base_url = "https://tpgw.trustpay.eu/" trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://gateway.transit-pass.com/" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 0d3c1d522712..066eafec9ee8 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -90,6 +90,7 @@ square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" stripe.base_url_file_upload = "https://files.stripe.com/" +taxjar.base_url = "https://api.sandbox.taxjar.com/v2/" trustpay.base_url = "https://test-tpgw.trustpay.eu/" trustpay.base_url_bank_redirects = "https://aapi.trustpay.eu/" tsys.base_url = "https://stagegw.transnox.com/" diff --git a/config/development.toml b/config/development.toml index 99f2cd2e9185..c7c51bee035f 100644 --- a/config/development.toml +++ b/config/development.toml @@ -150,6 +150,7 @@ cards = [ "square", "stax", "stripe", + "taxjar", "threedsecureio", "trustpay", "tsys", @@ -249,6 +250,7 @@ square.base_url = "https://connect.squareupsandbox.com/" square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" +taxjar.base_url = "https://api.sandbox.taxjar.com/v2/" threedsecureio.base_url = "https://service.sandbox.3dsecure.io" stripe.base_url_file_upload = "https://files.stripe.com/" wise.base_url = "https://api.sandbox.transferwise.tech/" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index c1eb85e36346..00942e7bb520 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -175,6 +175,7 @@ square.base_url = "https://connect.squareupsandbox.com/" square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" +taxjar.base_url = "https://api.sandbox.taxjar.com/v2/" threedsecureio.base_url = "https://service.sandbox.3dsecure.io" stripe.base_url_file_upload = "https://files.stripe.com/" trustpay.base_url = "https://test-tpgw.trustpay.eu/" @@ -252,6 +253,7 @@ cards = [ "square", "stax", "stripe", + "taxjar", "threedsecureio", "trustpay", "tsys", diff --git a/crates/analytics/src/opensearch.rs b/crates/analytics/src/opensearch.rs index e74622a054a9..f3dd5eea4413 100644 --- a/crates/analytics/src/opensearch.rs +++ b/crates/analytics/src/opensearch.rs @@ -518,7 +518,19 @@ impl OpenSearchQueryBuilder { let mut final_query = Map::new(); final_query.insert("bool".to_string(), json!({ "filter": updated_query })); - let payload = json!({ "query": Value::Object(final_query) }); + let mut sort_obj = Map::new(); + sort_obj.insert( + "@timestamp".to_string(), + json!({ + "order": "desc" + }), + ); + let payload = json!({ + "query": Value::Object(final_query), + "sort": [ + Value::Object(sort_obj) + ] + }); payload }) .collect::>()) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index a97e90cc16a9..b582a1def4aa 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -698,12 +698,12 @@ pub struct MerchantConnectorCreate { /// Name of the Connector #[schema(value_type = Connector, example = "stripe")] pub connector_name: api_enums::Connector, - /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports. Eg: if your profile label is `default`, connector label can be `stripe_default` + /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports, If not passed then if will take `connector_name`_`profile_name`. Eg: if your profile label is `default`, connector label can be `stripe_default` #[schema(example = "stripe_US_travel")] pub connector_label: Option, /// Identifier for the business profile, if not provided default will be chosen from merchant account - pub profile_id: Option, + pub profile_id: String, /// An object containing the required details/credentials for a Connector account. #[schema(value_type = Option,example = json!({ "auth_type": "HeaderKey","api_key": "Basic MyVerySecretApiKey" }))] @@ -1068,7 +1068,7 @@ pub struct MerchantConnectorResponse { /// Identifier for the business profile, if not provided default will be chosen from merchant account #[schema(max_length = 64)] - pub profile_id: Option, + pub profile_id: String, /// An object containing the required details/credentials for a Connector account. #[schema(value_type = Option,example = json!({ "auth_type": "HeaderKey","api_key": "Basic MyVerySecretApiKey" }))] @@ -1175,7 +1175,7 @@ pub struct MerchantConnectorResponse { /// Identifier for the business profile, if not provided default will be chosen from merchant account #[schema(max_length = 64)] - pub profile_id: Option, + pub profile_id: String, /// An object containing the required details/credentials for a Connector account. #[schema(value_type = Option,example = json!({ "auth_type": "HeaderKey","api_key": "Basic MyVerySecretApiKey" }))] @@ -1299,7 +1299,7 @@ pub struct MerchantConnectorListResponse { /// Identifier for the business profile, if not provided default will be chosen from merchant account #[schema(max_length = 64)] - pub profile_id: Option, + pub profile_id: String, /// An object containing the details about the payment methods that need to be enabled under this merchant connector account #[schema(example = json!([ @@ -1408,7 +1408,7 @@ pub struct MerchantConnectorListResponse { /// Identifier for the business profile, if not provided default will be chosen from merchant account #[schema(max_length = 64)] - pub profile_id: Option, + pub profile_id: String, /// An object containing the details about the payment methods that need to be enabled under this merchant connector account #[schema(example = json!([ @@ -1570,7 +1570,7 @@ pub struct MerchantConnectorUpdate { #[schema(value_type = ConnectorType, example = "payment_processor")] pub connector_type: api_enums::ConnectorType, - /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports. Eg: if your profile label is `default`, connector label can be `stripe_default` + /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports, If not passed then if will take `connector_name`_`profile_name`. Eg: if your profile label is `default`, connector label can be `stripe_default` #[schema(example = "stripe_US_travel")] pub connector_label: Option, diff --git a/crates/api_models/src/customers.rs b/crates/api_models/src/customers.rs index 8f76a3f8354d..c4261d624f17 100644 --- a/crates/api_models/src/customers.rs +++ b/crates/api_models/src/customers.rs @@ -342,3 +342,117 @@ pub struct CustomerDeleteResponse { #[schema(example = false)] pub payment_methods_deleted: bool, } + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +#[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] +pub struct CustomerUpdateRequest { + /// The identifier for the customer object + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub customer_id: Option, + /// The identifier for the Merchant Account + #[schema(max_length = 255, example = "y3oqhf46pyzuxjbcn2giaqnb44")] + #[serde(skip)] + pub merchant_id: id_type::MerchantId, + /// The customer's name + #[schema(max_length = 255, value_type = Option, example = "Jon Test")] + pub name: Option>, + /// The customer's email address + #[schema(value_type = Option, max_length = 255, example = "JonTest@test.com")] + pub email: Option, + /// The customer's phone number + #[schema(value_type = Option, max_length = 255, example = "9123456789")] + pub phone: Option>, + /// An arbitrary string that you can attach to a customer object. + #[schema(max_length = 255, example = "First Customer", value_type = Option)] + pub description: Option, + /// The country code for the customer phone number + #[schema(max_length = 255, example = "+65")] + pub phone_country_code: Option, + /// The address for the customer + #[schema(value_type = Option)] + pub address: Option, + /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 + /// characters long. Metadata is useful for storing additional, structured information on an + /// object. + #[schema(value_type = Option,example = json!({ "city": "NY", "unit": "245" }))] + pub metadata: Option, +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +impl CustomerUpdateRequest { + pub fn get_merchant_reference_id(&self) -> Option { + Some( + self.customer_id + .to_owned() + .unwrap_or_else(common_utils::generate_customer_id_of_default_length), + ) + } + pub fn get_address(&self) -> Option { + self.address.clone() + } +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] +pub struct CustomerUpdateRequest { + /// The merchant identifier for the customer object. + #[schema(value_type = Option, max_length = 64, min_length = 1, example = "cus_y3oqhf46pyzuxjbcn2giaqnb44")] + pub merchant_reference_id: Option, + /// The customer's name + #[schema(max_length = 255, value_type = String, example = "Jon Test")] + pub name: Option>, + /// The customer's email address + #[schema(value_type = String, max_length = 255, example = "JonTest@test.com")] + pub email: Option, + /// The customer's phone number + #[schema(value_type = Option, max_length = 255, example = "9123456789")] + pub phone: Option>, + /// An arbitrary string that you can attach to a customer object. + #[schema(max_length = 255, example = "First Customer", value_type = Option)] + pub description: Option, + /// The country code for the customer phone number + #[schema(max_length = 255, example = "+65")] + pub phone_country_code: Option, + /// The default billing address for the customer + #[schema(value_type = Option)] + pub default_billing_address: Option, + /// The default shipping address for the customer + #[schema(value_type = Option)] + pub default_shipping_address: Option, + /// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 + /// characters long. Metadata is useful for storing additional, structured information on an + /// object. + #[schema(value_type = Option,example = json!({ "city": "NY", "unit": "245" }))] + pub metadata: Option, + /// The unique identifier of the payment method + #[schema(example = "card_rGK4Vi5iSW70MY7J2mIg")] + pub default_payment_method_id: Option, +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +impl CustomerUpdateRequest { + pub fn get_merchant_reference_id(&self) -> Option { + self.merchant_reference_id.clone() + } + + pub fn get_default_customer_billing_address(&self) -> Option { + self.default_billing_address.clone() + } + + pub fn get_default_customer_shipping_address(&self) -> Option { + self.default_shipping_address.clone() + } +} + +#[derive(Default, Debug, serde::Deserialize, serde::Serialize, Clone)] +pub struct UpdateCustomerId(String); + +impl UpdateCustomerId { + pub fn get_global_id(&self) -> String { + self.0.clone() + } + + pub fn new(id: String) -> Self { + Self(id) + } +} diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 0b2d88ee2960..a7738133ca0c 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -116,7 +116,7 @@ pub enum Connector { Nuvei, // Opayo, added as template code for future usage Opennode, - // Paybox, added as template code for future usage + Paybox, // Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage Payme, Payone, @@ -131,6 +131,7 @@ pub enum Connector { Square, Stax, Stripe, + // Taxjar, Threedsecureio, Trustpay, Tsys, @@ -208,6 +209,7 @@ impl Connector { | Self::DummyConnector7 => false, Self::Aci // Add Separate authentication support for connectors + // | Self::Taxjar // | Self::Fiservemea | Self::Adyen | Self::Adyenplatform @@ -242,8 +244,8 @@ impl Connector { | Self::Nexinets | Self::Nuvei | Self::Opennode - // | Self::Paybox added as template code for future usage - | Self::Payme + | Self::Paybox + | Self::Payme | Self::Payone | Self::Paypal | Self::Payu diff --git a/crates/api_models/src/events/customer.rs b/crates/api_models/src/events/customer.rs index 8457d6b66228..dedc1b82c88b 100644 --- a/crates/api_models/src/events/customer.rs +++ b/crates/api_models/src/events/customer.rs @@ -1,6 +1,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; -use crate::customers::{CustomerDeleteResponse, CustomerId, CustomerRequest, CustomerResponse}; +use crate::customers::{ + CustomerDeleteResponse, CustomerId, CustomerRequest, CustomerResponse, CustomerUpdateRequest, +}; impl ApiEventMetric for CustomerDeleteResponse { fn get_api_event_type(&self) -> Option { @@ -33,3 +35,12 @@ impl ApiEventMetric for CustomerId { }) } } + +impl ApiEventMetric for CustomerUpdateRequest { + fn get_api_event_type(&self) -> Option { + self.get_merchant_reference_id() + .clone() + .map(|cid| ApiEventsType::Customer { customer_id: cid }) + } +} +// These needs to be fixed for v2 diff --git a/crates/api_models/src/payouts.rs b/crates/api_models/src/payouts.rs index b90b34cdff78..60c08241d173 100644 --- a/crates/api_models/src/payouts.rs +++ b/crates/api_models/src/payouts.rs @@ -715,8 +715,11 @@ pub struct PayoutListFilterConstraints { pub struct PayoutListResponse { /// The number of payouts included in the list pub size: usize, - // The list of payouts response objects + /// The list of payouts response objects pub data: Vec, + /// The total number of available payouts for given constraints + #[serde(skip_serializing_if = "Option::is_none")] + pub total_count: Option, } #[derive(Clone, Debug, serde::Serialize, ToSchema)] diff --git a/crates/api_models/src/webhook_events.rs b/crates/api_models/src/webhook_events.rs index 2dcc6d643231..fa61cc0f253d 100644 --- a/crates/api_models/src/webhook_events.rs +++ b/crates/api_models/src/webhook_events.rs @@ -24,6 +24,9 @@ pub struct EventListConstraints { /// Filter all events associated with the specified object identifier (Payment Intent ID, /// Refund ID, etc.) pub object_id: Option, + + /// Filter all events associated with the specified business profile ID. + pub profile_id: Option, } #[derive(Debug)] @@ -97,11 +100,7 @@ pub struct EventRetrieveResponse { impl common_utils::events::ApiEventMetric for EventRetrieveResponse { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Events { - merchant_id_or_profile_id: self - .event_information - .merchant_id - .get_string_repr() - .to_owned(), + merchant_id: self.event_information.merchant_id.clone(), }) } } @@ -148,42 +147,42 @@ pub struct OutgoingWebhookResponseContent { #[derive(Debug, serde::Serialize)] pub struct EventListRequestInternal { - pub merchant_id_or_profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, pub constraints: EventListConstraints, } impl common_utils::events::ApiEventMetric for EventListRequestInternal { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Events { - merchant_id_or_profile_id: self.merchant_id_or_profile_id.clone(), + merchant_id: self.merchant_id.clone(), }) } } #[derive(Debug, serde::Serialize)] pub struct WebhookDeliveryAttemptListRequestInternal { - pub merchant_id_or_profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, pub initial_attempt_id: String, } impl common_utils::events::ApiEventMetric for WebhookDeliveryAttemptListRequestInternal { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Events { - merchant_id_or_profile_id: self.merchant_id_or_profile_id.clone(), + merchant_id: self.merchant_id.clone(), }) } } #[derive(Debug, serde::Serialize)] pub struct WebhookDeliveryRetryRequestInternal { - pub merchant_id_or_profile_id: String, + pub merchant_id: common_utils::id_type::MerchantId, pub event_id: String, } impl common_utils::events::ApiEventMetric for WebhookDeliveryRetryRequestInternal { fn get_api_event_type(&self) -> Option { Some(common_utils::events::ApiEventsType::Events { - merchant_id_or_profile_id: self.merchant_id_or_profile_id.clone(), + merchant_id: self.merchant_id.clone(), }) } } diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 87c19e996853..af17f574948c 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -229,7 +229,7 @@ pub enum RoutableConnectors { // Opayo, added as template code for future usage Opennode, // Payeezy, As psync and rsync are not supported by this connector, it is added as template code for future usage - // Paybox, added as template code for future usage + Paybox, Payme, Payone, Paypal, @@ -245,6 +245,7 @@ pub enum RoutableConnectors { Square, Stax, Stripe, + // Taxjar, Trustpay, // Tsys, Tsys, @@ -3085,6 +3086,8 @@ pub enum ApiVersion { Debug, Eq, PartialEq, + Ord, + PartialOrd, serde::Deserialize, serde::Serialize, strum::Display, @@ -3095,10 +3098,10 @@ pub enum ApiVersion { #[strum(serialize_all = "snake_case")] #[serde(rename_all = "snake_case")] pub enum EntityType { - Internal, - Organization, - Merchant, - Profile, + Internal = 3, + Organization = 2, + Merchant = 1, + Profile = 0, } #[derive(Clone, Debug, serde::Serialize)] diff --git a/crates/common_utils/src/events.rs b/crates/common_utils/src/events.rs index c2294f865ae6..ac4b9c42a41e 100644 --- a/crates/common_utils/src/events.rs +++ b/crates/common_utils/src/events.rs @@ -58,7 +58,7 @@ pub enum ApiEventsType { dispute_id: String, }, Events { - merchant_id_or_profile_id: String, + merchant_id: id_type::MerchantId, }, PaymentMethodCollectLink { link_id: String, diff --git a/crates/connector_configs/src/connector.rs b/crates/connector_configs/src/connector.rs index 6cc32fe95534..51713f6166fb 100644 --- a/crates/connector_configs/src/connector.rs +++ b/crates/connector_configs/src/connector.rs @@ -175,7 +175,7 @@ pub struct ConnectorConfig { pub nmi: Option, pub noon: Option, pub nuvei: Option, - // pub paybox: Option, added for future usage + pub paybox: Option, pub payme: Option, #[cfg(feature = "payouts")] pub payone_payout: Option, @@ -323,6 +323,7 @@ impl ConnectorConfig { Connector::Nmi => Ok(connector_data.nmi), Connector::Noon => Ok(connector_data.noon), Connector::Nuvei => Ok(connector_data.nuvei), + Connector::Paybox => Ok(connector_data.paybox), Connector::Payme => Ok(connector_data.payme), Connector::Payone => Err("Use get_payout_connector_config".to_string()), Connector::Paypal => Ok(connector_data.paypal), @@ -363,7 +364,6 @@ impl ConnectorConfig { #[cfg(feature = "dummy_connector")] Connector::DummyConnector7 => Ok(connector_data.paypal_test), Connector::Netcetera => Ok(connector_data.netcetera), - // Connector::Paybox => Ok(connector_data.paybox), added for future usage } } } diff --git a/crates/connector_configs/src/response_modifier.rs b/crates/connector_configs/src/response_modifier.rs index 06f9d25b65d2..b6b2e8555e39 100644 --- a/crates/connector_configs/src/response_modifier.rs +++ b/crates/connector_configs/src/response_modifier.rs @@ -225,6 +225,13 @@ impl ConnectorApiIntegrationPayload { } } + let open_banking = DashboardPaymentMethodPayload { + payment_method: api_models::enums::PaymentMethod::OpenBanking, + payment_method_type: api_models::enums::PaymentMethod::OpenBanking.to_string(), + provider: Some(open_banking_details), + card_provider: None, + }; + let upi = DashboardPaymentMethodPayload { payment_method: api_models::enums::PaymentMethod::Upi, payment_method_type: api_models::enums::PaymentMethod::Upi.to_string(), @@ -322,6 +329,7 @@ impl ConnectorApiIntegrationPayload { DashboardRequestPayload { connector: response.connector_name, payment_methods_enabled: Some(vec![ + open_banking, upi, voucher, reward, diff --git a/crates/connector_configs/toml/development.toml b/crates/connector_configs/toml/development.toml index 95b22f8f77f1..cc2ad85e56cb 100644 --- a/crates/connector_configs/toml/development.toml +++ b/crates/connector_configs/toml/development.toml @@ -3803,6 +3803,46 @@ key1="Public Api Key" api_key = "Passcode" key1 = "datatrans MerchantId" +[paybox] +[[paybox.credit]] + payment_method_type = "Mastercard" +[[paybox.credit]] + payment_method_type = "Visa" +[[paybox.credit]] + payment_method_type = "Interac" +[[paybox.credit]] + payment_method_type = "AmericanExpress" +[[paybox.credit]] + payment_method_type = "JCB" +[[paybox.credit]] + payment_method_type = "DinersClub" +[[paybox.credit]] + payment_method_type = "Discover" +[[paybox.credit]] + payment_method_type = "CartesBancaires" +[[paybox.credit]] + payment_method_type = "UnionPay" +[[paybox.debit]] + payment_method_type = "Mastercard" +[[paybox.debit]] + payment_method_type = "Visa" +[[paybox.debit]] + payment_method_type = "Interac" +[[paybox.debit]] + payment_method_type = "AmericanExpress" +[[paybox.debit]] + payment_method_type = "JCB" +[[paybox.debit]] + payment_method_type = "DinersClub" +[[paybox.debit]] + payment_method_type = "Discover" +[[paybox.debit]] + payment_method_type = "CartesBancaires" +[paybox.connector_auth.SignatureKey] +api_key="SITE Key" +key1="Rang Identifier" +api_secret="CLE Secret" + [wellsfargo] [[wellsfargo.credit]] payment_method_type = "Mastercard" diff --git a/crates/connector_configs/toml/production.toml b/crates/connector_configs/toml/production.toml index a2c118f572b8..a61911b9da1c 100644 --- a/crates/connector_configs/toml/production.toml +++ b/crates/connector_configs/toml/production.toml @@ -2680,6 +2680,46 @@ key1 = "Merchant ID" api_key="Private Api Key" key1="Public Api Key" +[paybox] +[[paybox.credit]] + payment_method_type = "Mastercard" +[[paybox.credit]] + payment_method_type = "Visa" +[[paybox.credit]] + payment_method_type = "Interac" +[[paybox.credit]] + payment_method_type = "AmericanExpress" +[[paybox.credit]] + payment_method_type = "JCB" +[[paybox.credit]] + payment_method_type = "DinersClub" +[[paybox.credit]] + payment_method_type = "Discover" +[[paybox.credit]] + payment_method_type = "CartesBancaires" +[[paybox.credit]] + payment_method_type = "UnionPay" +[[paybox.debit]] + payment_method_type = "Mastercard" +[[paybox.debit]] + payment_method_type = "Visa" +[[paybox.debit]] + payment_method_type = "Interac" +[[paybox.debit]] + payment_method_type = "AmericanExpress" +[[paybox.debit]] + payment_method_type = "JCB" +[[paybox.debit]] + payment_method_type = "DinersClub" +[[paybox.debit]] + payment_method_type = "Discover" +[[paybox.debit]] + payment_method_type = "CartesBancaires" +[paybox.connector_auth.SignatureKey] +api_key="SITE Key" +key1="Rang Identifier" +api_secret="CLE Secret" + [datatrans] [[datatrans.credit]] payment_method_type = "Mastercard" diff --git a/crates/connector_configs/toml/sandbox.toml b/crates/connector_configs/toml/sandbox.toml index 1c4b61db41cc..b61187def75e 100644 --- a/crates/connector_configs/toml/sandbox.toml +++ b/crates/connector_configs/toml/sandbox.toml @@ -3796,6 +3796,46 @@ key1="Public Api Key" api_key = "Passcode" key1 = "datatrans MerchantId" +[paybox] +[[paybox.credit]] + payment_method_type = "Mastercard" +[[paybox.credit]] + payment_method_type = "Visa" +[[paybox.credit]] + payment_method_type = "Interac" +[[paybox.credit]] + payment_method_type = "AmericanExpress" +[[paybox.credit]] + payment_method_type = "JCB" +[[paybox.credit]] + payment_method_type = "DinersClub" +[[paybox.credit]] + payment_method_type = "Discover" +[[paybox.credit]] + payment_method_type = "CartesBancaires" +[[paybox.credit]] + payment_method_type = "UnionPay" +[[paybox.debit]] + payment_method_type = "Mastercard" +[[paybox.debit]] + payment_method_type = "Visa" +[[paybox.debit]] + payment_method_type = "Interac" +[[paybox.debit]] + payment_method_type = "AmericanExpress" +[[paybox.debit]] + payment_method_type = "JCB" +[[paybox.debit]] + payment_method_type = "DinersClub" +[[paybox.debit]] + payment_method_type = "Discover" +[[paybox.debit]] + payment_method_type = "CartesBancaires" +[paybox.connector_auth.SignatureKey] +api_key="SITE Key" +key1="Rang Identifier" +api_secret="CLE Secret" + [wellsfargo] [[wellsfargo.credit]] payment_method_type = "Mastercard" diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index bb4aa21cda69..cf873d4b61f8 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -251,7 +251,7 @@ pub struct BusinessProfile { // pub order_fulfillment_time_origin: Option, pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, - // pub default_fallback_routing: Option, + pub default_fallback_routing: Option, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] @@ -289,7 +289,7 @@ pub struct BusinessProfileNew { // pub order_fulfillment_time_origin: Option, pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, - // pub default_fallback_routing: Option, + pub default_fallback_routing: Option, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] @@ -324,7 +324,7 @@ pub struct BusinessProfileUpdateInternal { // pub order_fulfillment_time_origin: Option, pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, - // pub default_fallback_routing: Option, + pub default_fallback_routing: Option, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] @@ -358,7 +358,7 @@ impl BusinessProfileUpdateInternal { // order_fulfillment_time_origin, frm_routing_algorithm_id, payout_routing_algorithm_id, - // default_fallback_routing, + default_fallback_routing, } = self; BusinessProfile { profile_id: source.profile_id, @@ -407,7 +407,7 @@ impl BusinessProfileUpdateInternal { frm_routing_algorithm_id: frm_routing_algorithm_id.or(source.frm_routing_algorithm_id), payout_routing_algorithm_id: payout_routing_algorithm_id .or(source.payout_routing_algorithm_id), - // default_fallback_routing: default_fallback_routing.or(source.default_fallback_routing), + default_fallback_routing: default_fallback_routing.or(source.default_fallback_routing), } } } @@ -451,7 +451,7 @@ impl From for BusinessProfile { // order_fulfillment_time_origin: new.order_fulfillment_time_origin, frm_routing_algorithm_id: new.frm_routing_algorithm_id, payout_routing_algorithm_id: new.payout_routing_algorithm_id, - // default_fallback_routing: new.default_fallback_routing, + default_fallback_routing: new.default_fallback_routing, } } } diff --git a/crates/diesel_models/src/customers.rs b/crates/diesel_models/src/customers.rs index 35595c05d94d..7e4e27a5d385 100644 --- a/crates/diesel_models/src/customers.rs +++ b/crates/diesel_models/src/customers.rs @@ -261,6 +261,8 @@ pub struct CustomerUpdateInternal { pub connector_customer: Option, pub default_payment_method_id: Option>, pub updated_by: Option, + pub default_billing_address: Option, + pub default_shipping_address: Option, } #[cfg(all(feature = "v2", feature = "customer_v2"))] @@ -276,6 +278,8 @@ impl CustomerUpdateInternal { connector_customer, // address_id, default_payment_method_id, + default_billing_address, + default_shipping_address, .. } = self; @@ -292,6 +296,10 @@ impl CustomerUpdateInternal { default_payment_method_id: default_payment_method_id .flatten() .map_or(source.default_payment_method_id, Some), + default_billing_address: default_billing_address + .map_or(source.default_billing_address, Some), + default_shipping_address: default_shipping_address + .map_or(source.default_shipping_address, Some), ..source } } diff --git a/crates/diesel_models/src/kv.rs b/crates/diesel_models/src/kv.rs index 84bc58f4e10b..1de2097b99b9 100644 --- a/crates/diesel_models/src/kv.rs +++ b/crates/diesel_models/src/kv.rs @@ -138,6 +138,7 @@ impl DBOperation { ) .await?, )), + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] Updateable::CustomerUpdate(cust) => DBResult::Customer(Box::new( Customer::update_by_customer_id_merchant_id( conn, @@ -147,6 +148,10 @@ impl DBOperation { ) .await?, )), + #[cfg(all(feature = "v2", feature = "customer_v2"))] + Updateable::CustomerUpdate(cust) => DBResult::Customer(Box::new( + Customer::update_by_id(conn, cust.orig.id.clone(), cust.update_data).await?, + )), }, }) } diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index a057f349ead5..876240f44a3d 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -55,6 +55,7 @@ pub struct MerchantConnectorAccount { pub status: storage_enums::ConnectorStatus, pub additional_merchant_data: Option, pub connector_wallets_details: Option, + pub version: common_enums::ApiVersion, } #[cfg(all( @@ -94,7 +95,7 @@ pub struct MerchantConnectorAccount { pub connector_webhook_details: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub frm_config: Option>, - pub profile_id: Option, + pub profile_id: String, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, pub pm_auth_config: Option, @@ -102,6 +103,7 @@ pub struct MerchantConnectorAccount { pub additional_merchant_data: Option, pub connector_wallets_details: Option, pub id: String, + pub version: common_enums::ApiVersion, } #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] @@ -144,6 +146,7 @@ pub struct MerchantConnectorAccountNew { pub status: storage_enums::ConnectorStatus, pub additional_merchant_data: Option, pub connector_wallets_details: Option, + pub version: common_enums::ApiVersion, } #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] @@ -163,7 +166,7 @@ pub struct MerchantConnectorAccountNew { pub connector_webhook_details: Option, #[diesel(deserialize_as = super::OptionalDieselArray)] pub frm_config: Option>, - pub profile_id: Option, + pub profile_id: String, #[diesel(deserialize_as = super::OptionalDieselArray)] pub applepay_verified_domains: Option>, pub pm_auth_config: Option, @@ -171,6 +174,7 @@ pub struct MerchantConnectorAccountNew { pub additional_merchant_data: Option, pub connector_wallets_details: Option, pub id: String, + pub version: common_enums::ApiVersion, } #[cfg(all( diff --git a/crates/diesel_models/src/query/business_profile.rs b/crates/diesel_models/src/query/business_profile.rs index 09fa700dae64..fb80449cdc65 100644 --- a/crates/diesel_models/src/query/business_profile.rs +++ b/crates/diesel_models/src/query/business_profile.rs @@ -48,6 +48,20 @@ impl BusinessProfile { .await } + pub async fn find_by_merchant_id_profile_id( + conn: &PgPooledConn, + merchant_id: &common_utils::id_type::MerchantId, + profile_id: &str, + ) -> StorageResult { + generics::generic_find_one::<::Table, _, _>( + conn, + dsl::merchant_id + .eq(merchant_id.to_owned()) + .and(dsl::profile_id.eq(profile_id.to_owned())), + ) + .await + } + pub async fn find_by_profile_name_merchant_id( conn: &PgPooledConn, profile_name: &str, diff --git a/crates/diesel_models/src/query/customers.rs b/crates/diesel_models/src/query/customers.rs index ef0fa7d7e2a3..1dc35511c9d9 100644 --- a/crates/diesel_models/src/query/customers.rs +++ b/crates/diesel_models/src/query/customers.rs @@ -1,11 +1,12 @@ -#[cfg(all(feature = "v2", feature = "customer_v2"))] use common_utils::id_type; +#[cfg(all(feature = "v2", feature = "customer_v2"))] +use diesel::BoolExpressionMethods; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use diesel::BoolExpressionMethods; use diesel::{associations::HasTable, ExpressionMethods}; use super::generics; -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +// #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::errors; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::schema::customers::dsl; @@ -22,52 +23,36 @@ impl CustomerNew { } } -#[cfg(all(feature = "v2", feature = "customer_v2"))] impl Customer { - pub async fn update_by_customer_id_merchant_id( - _conn: &PgPooledConn, - _customer_id: id_type::CustomerId, - _merchant_id: id_type::MerchantId, - _customer: CustomerUpdateInternal, + #[cfg(all(feature = "v2", feature = "customer_v2"))] + pub async fn update_by_id( + conn: &PgPooledConn, + id: String, + customer: CustomerUpdateInternal, ) -> StorageResult { - // match generics::generic_update_by_id::<::Table, _, _, _>( - // conn, - // (customer_id.clone(), merchant_id.clone()), - // customer, - // ) - // .await - // { - // Err(error) => match error.current_context() { - // errors::DatabaseError::NoFieldsToUpdate => { - // generics::generic_find_by_id::<::Table, _, _>( - // conn, - // (customer_id, merchant_id), - // ) - // .await - // } - // _ => Err(error), - // }, - // result => result, - todo!() - // } - } - pub async fn find_by_id_merchant_id(conn: &PgPooledConn, id: &str) -> StorageResult { - generics::generic_find_by_id::<::Table, _, _>(conn, id.to_owned()).await + match generics::generic_update_by_id::<::Table, _, _, _>( + conn, + id.clone(), + customer, + ) + .await + { + Err(error) => match error.current_context() { + errors::DatabaseError::NoFieldsToUpdate => { + generics::generic_find_by_id::<::Table, _, _>(conn, id).await + } + _ => Err(error), + }, + result => result, + } } - pub async fn find_by_customer_id_merchant_id( - _conn: &PgPooledConn, - _customer_id: &id_type::CustomerId, - _merchant_id: &id_type::MerchantId, - ) -> StorageResult { - // generics::generic_find_by_id::<::Table, _, _>( - // conn, - // (customer_id.to_owned(), merchant_id.to_owned()), - // ) - // .await - todo!() + #[cfg(all(feature = "v2", feature = "customer_v2"))] + pub async fn find_by_global_id(conn: &PgPooledConn, id: &String) -> StorageResult { + generics::generic_find_by_id::<::Table, _, _>(conn, id.to_owned()).await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn list_by_merchant_id( conn: &PgPooledConn, merchant_id: &id_type::MerchantId, @@ -82,26 +67,39 @@ impl Customer { .await } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + pub async fn find_optional_by_merchant_id_merchant_reference_id( + conn: &PgPooledConn, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + ) -> StorageResult> { + generics::generic_find_one_optional::<::Table, _, _>( + conn, + dsl::merchant_id + .eq(merchant_id.to_owned()) + .and(dsl::merchant_reference_id.eq(customer_id.to_owned())), + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn find_optional_by_customer_id_merchant_id( - _conn: &PgPooledConn, - _customer_id: &id_type::CustomerId, - _merchant_id: &id_type::MerchantId, + conn: &PgPooledConn, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, ) -> StorageResult> { - // generics::generic_find_by_id_optional::<::Table, _, _>( - // conn, - // (customer_id.to_owned(), merchant_id.to_owned()), - // ) - // .await - todo!() + generics::generic_find_by_id_optional::<::Table, _, _>( + conn, + (customer_id.to_owned(), merchant_id.to_owned()), + ) + .await } -} -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -impl Customer { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn update_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: common_utils::id_type::CustomerId, - merchant_id: common_utils::id_type::MerchantId, + customer_id: id_type::CustomerId, + merchant_id: id_type::MerchantId, customer: CustomerUpdateInternal, ) -> StorageResult { match generics::generic_update_by_id::<::Table, _, _, _>( @@ -125,10 +123,11 @@ impl Customer { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn delete_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: &common_utils::id_type::CustomerId, - merchant_id: &common_utils::id_type::MerchantId, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, ) -> StorageResult { generics::generic_delete::<::Table, _>( conn, @@ -139,38 +138,28 @@ impl Customer { .await } - pub async fn find_by_customer_id_merchant_id( + #[cfg(all(feature = "v2", feature = "customer_v2"))] + pub async fn find_by_merchant_reference_id_merchant_id( conn: &PgPooledConn, - customer_id: &common_utils::id_type::CustomerId, - merchant_id: &common_utils::id_type::MerchantId, + merchant_reference_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, ) -> StorageResult { - generics::generic_find_by_id::<::Table, _, _>( - conn, - (customer_id.to_owned(), merchant_id.to_owned()), - ) - .await - } - - pub async fn list_by_merchant_id( - conn: &PgPooledConn, - merchant_id: &common_utils::id_type::MerchantId, - ) -> StorageResult> { - generics::generic_filter::<::Table, _, _, _>( + generics::generic_find_one::<::Table, _, _>( conn, - dsl::merchant_id.eq(merchant_id.to_owned()), - None, - None, - Some(dsl::created_at), + dsl::merchant_id + .eq(merchant_id.to_owned()) + .and(dsl::merchant_reference_id.eq(merchant_reference_id.to_owned())), ) .await } - pub async fn find_optional_by_customer_id_merchant_id( + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] + pub async fn find_by_customer_id_merchant_id( conn: &PgPooledConn, - customer_id: &common_utils::id_type::CustomerId, - merchant_id: &common_utils::id_type::MerchantId, - ) -> StorageResult> { - generics::generic_find_by_id_optional::<::Table, _, _>( + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + ) -> StorageResult { + generics::generic_find_by_id::<::Table, _, _>( conn, (customer_id.to_owned(), merchant_id.to_owned()), ) diff --git a/crates/diesel_models/src/query/payout_attempt.rs b/crates/diesel_models/src/query/payout_attempt.rs index e415dc6735de..939336a223a5 100644 --- a/crates/diesel_models/src/query/payout_attempt.rs +++ b/crates/diesel_models/src/query/payout_attempt.rs @@ -147,7 +147,7 @@ impl PayoutAttempt { Vec, Vec, )> { - let active_attempts: Vec = payouts + let active_attempt_ids = payouts .iter() .map(|payout| { format!( @@ -156,11 +156,20 @@ impl PayoutAttempt { payout.attempt_count.clone() ) }) - .collect(); + .collect::>(); + + let active_payout_ids = payouts + .iter() + .map(|payout| payout.payout_id.clone()) + .collect::>(); let filter = ::table() .filter(dsl::merchant_id.eq(merchant_id.to_owned())) - .filter(dsl::payout_attempt_id.eq_any(active_attempts)); + .filter(dsl::payout_attempt_id.eq_any(active_attempt_ids)); + + let payouts_filter = ::table() + .filter(payout_dsl::merchant_id.eq(merchant_id.to_owned())) + .filter(payout_dsl::payout_id.eq_any(active_payout_ids)); let payout_status: Vec = payouts .iter() @@ -181,7 +190,8 @@ impl PayoutAttempt { .flatten() .collect::>(); - let filter_currency = ::table() + let filter_currency = payouts_filter + .clone() .select(payout_dsl::destination_currency) .distinct() .get_results_async::(conn) @@ -191,7 +201,8 @@ impl PayoutAttempt { .into_iter() .collect::>(); - let filter_payout_method = Payouts::table() + let filter_payout_method = payouts_filter + .clone() .select(payout_dsl::payout_type) .distinct() .get_results_async::>(conn) diff --git a/crates/diesel_models/src/query/payouts.rs b/crates/diesel_models/src/query/payouts.rs index e65062b3922c..25c7bfc4f7ed 100644 --- a/crates/diesel_models/src/query/payouts.rs +++ b/crates/diesel_models/src/query/payouts.rs @@ -1,11 +1,16 @@ -use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods}; -use error_stack::report; +use async_bb8_diesel::AsyncRunQueryDsl; +use diesel::{ + associations::HasTable, debug_query, pg::Pg, BoolExpressionMethods, ExpressionMethods, + JoinOnDsl, QueryDsl, +}; +use error_stack::{report, ResultExt}; use super::generics; use crate::{ - errors, + enums, errors, payouts::{Payouts, PayoutsNew, PayoutsUpdate, PayoutsUpdateInternal}, - schema::payouts::dsl, + query::generics::db_metrics, + schema::{payout_attempt, payouts::dsl}, PgPooledConn, StorageResult, }; @@ -87,4 +92,43 @@ impl Payouts { ) .await } + + pub async fn get_total_count_of_payouts( + conn: &PgPooledConn, + merchant_id: &common_utils::id_type::MerchantId, + active_payout_ids: &[String], + connector: Option>, + currency: Option>, + status: Option>, + payout_type: Option>, + ) -> StorageResult { + let mut filter = ::table() + .inner_join(payout_attempt::table.on(payout_attempt::dsl::payout_id.eq(dsl::payout_id))) + .count() + .filter(dsl::merchant_id.eq(merchant_id.to_owned())) + .filter(dsl::payout_id.eq_any(active_payout_ids.to_owned())) + .into_boxed(); + + if let Some(connector) = connector { + filter = filter.filter(payout_attempt::dsl::connector.eq_any(connector)); + } + if let Some(currency) = currency { + filter = filter.filter(dsl::destination_currency.eq_any(currency)); + } + if let Some(status) = status { + filter = filter.filter(dsl::status.eq_any(status)); + } + if let Some(payout_type) = payout_type { + filter = filter.filter(dsl::payout_type.eq_any(payout_type)); + } + router_env::logger::debug!(query = %debug_query::(&filter).to_string()); + + db_metrics::track_database_call::<::Table, _, _>( + filter.get_result_async::(conn), + db_metrics::DatabaseOperation::Filter, + ) + .await + .change_context(errors::DatabaseError::Others) + .attach_printable("Error filtering count of payouts") + } } diff --git a/crates/diesel_models/src/query/user_role.rs b/crates/diesel_models/src/query/user_role.rs index 2f2923db0002..b84ea63de1d0 100644 --- a/crates/diesel_models/src/query/user_role.rs +++ b/crates/diesel_models/src/query/user_role.rs @@ -82,22 +82,6 @@ impl UserRole { .await } - pub async fn delete_by_user_id_merchant_id( - conn: &PgPooledConn, - user_id: String, - merchant_id: id_type::MerchantId, - version: UserRoleVersion, - ) -> StorageResult { - generics::generic_delete_one_with_result::<::Table, _, _>( - conn, - dsl::user_id - .eq(user_id) - .and(dsl::merchant_id.eq(merchant_id)) - .and(dsl::version.eq(version)), - ) - .await - } - pub async fn list_by_user_id( conn: &PgPooledConn, user_id: String, @@ -129,4 +113,71 @@ impl UserRole { ) .await } + + pub async fn find_by_user_id_org_id_merchant_id_profile_id( + conn: &PgPooledConn, + user_id: String, + org_id: id_type::OrganizationId, + merchant_id: id_type::MerchantId, + profile_id: Option, + version: UserRoleVersion, + ) -> StorageResult { + // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level + // (org_id = ? && merchant_id = null && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = ?) + let check_lineage = dsl::org_id + .eq(org_id.clone()) + .and(dsl::merchant_id.is_null().and(dsl::profile_id.is_null())) + .or(dsl::org_id.eq(org_id.clone()).and( + dsl::merchant_id + .eq(merchant_id.clone()) + .and(dsl::profile_id.is_null()), + )) + .or(dsl::org_id.eq(org_id).and( + dsl::merchant_id + .eq(merchant_id) + //TODO: In case of None, profile_id = NULL its unexpected behaviour, after V1 profile id will not be option + .and(dsl::profile_id.eq(profile_id)), + )); + + let predicate = dsl::user_id + .eq(user_id) + .and(check_lineage) + .and(dsl::version.eq(version)); + + generics::generic_find_one::<::Table, _, _>(conn, predicate).await + } + + pub async fn delete_by_user_id_org_id_merchant_id_profile_id( + conn: &PgPooledConn, + user_id: String, + org_id: id_type::OrganizationId, + merchant_id: id_type::MerchantId, + profile_id: Option, + version: UserRoleVersion, + ) -> StorageResult { + // Checking in user roles, for a user in token hierarchy, only one of the relation will be true, either org level, merchant level or profile level + // (org_id = ? && merchant_id = null && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = null) || (org_id = ? && merchant_id = ? && profile_id = ?) + let check_lineage = dsl::org_id + .eq(org_id.clone()) + .and(dsl::merchant_id.is_null().and(dsl::profile_id.is_null())) + .or(dsl::org_id.eq(org_id.clone()).and( + dsl::merchant_id + .eq(merchant_id.clone()) + .and(dsl::profile_id.is_null()), + )) + .or(dsl::org_id.eq(org_id).and( + dsl::merchant_id + .eq(merchant_id) + //TODO: In case of None, profile_id = NULL its unexpected behaviour, after V1 profile id will not be option + .and(dsl::profile_id.eq(profile_id)), + )); + + let predicate = dsl::user_id + .eq(user_id) + .and(check_lineage) + .and(dsl::version.eq(version)); + + generics::generic_delete_one_with_result::<::Table, _, _>(conn, predicate) + .await + } } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 8cc9aff224f0..19468e49566c 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -706,6 +706,7 @@ diesel::table! { status -> ConnectorStatus, additional_merchant_data -> Nullable, connector_wallets_details -> Nullable, + version -> ApiVersion, } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index b5a4ff384adc..59822bbdfb0a 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -208,6 +208,7 @@ diesel::table! { frm_routing_algorithm_id -> Nullable, #[max_length = 64] payout_routing_algorithm_id -> Nullable, + default_fallback_routing -> Nullable, } } @@ -677,7 +678,7 @@ diesel::table! { connector_webhook_details -> Nullable, frm_config -> Nullable>>, #[max_length = 64] - profile_id -> Nullable, + profile_id -> Varchar, applepay_verified_domains -> Nullable>>, pm_auth_config -> Nullable, status -> ConnectorStatus, @@ -685,6 +686,7 @@ diesel::table! { connector_wallets_details -> Nullable, #[max_length = 64] id -> Varchar, + version -> ApiVersion, } } diff --git a/crates/hyperswitch_connectors/src/connectors.rs b/crates/hyperswitch_connectors/src/connectors.rs index 5f94c780b94c..ae0fa945799b 100644 --- a/crates/hyperswitch_connectors/src/connectors.rs +++ b/crates/hyperswitch_connectors/src/connectors.rs @@ -4,8 +4,9 @@ pub mod fiserv; pub mod fiservemea; pub mod helcim; pub mod stax; +pub mod taxjar; pub use self::{ bambora::Bambora, bitpay::Bitpay, fiserv::Fiserv, fiservemea::Fiservemea, helcim::Helcim, - stax::Stax, + stax::Stax, taxjar::Taxjar, }; diff --git a/crates/hyperswitch_connectors/src/connectors/taxjar.rs b/crates/hyperswitch_connectors/src/connectors/taxjar.rs new file mode 100644 index 000000000000..3ea453e52859 --- /dev/null +++ b/crates/hyperswitch_connectors/src/connectors/taxjar.rs @@ -0,0 +1,563 @@ +pub mod transformers; + +use common_utils::{ + errors::CustomResult, + ext_traits::BytesExt, + request::{Method, Request, RequestBuilder, RequestContent}, + types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector}, +}; +use error_stack::{report, ResultExt}; +use hyperswitch_domain_models::{ + router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData}, + router_flow_types::{ + access_token_auth::AccessTokenAuth, + payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void}, + refunds::{Execute, RSync}, + }, + router_request_types::{ + AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData, + PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, + RefundsData, SetupMandateRequestData, + }, + router_response_types::{PaymentsResponseData, RefundsResponseData}, + types::{ + PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData, + RefundSyncRouterData, RefundsRouterData, + }, +}; +use hyperswitch_interfaces::{ + api::{self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorValidation}, + configs::Connectors, + errors, + events::connector_api_logs::ConnectorEvent, + types::{self, Response}, + webhooks, +}; +use masking::{ExposeInterface, Mask}; +use transformers as taxjar; + +use crate::{constants::headers, types::ResponseRouterData, utils}; + +#[derive(Clone)] +pub struct Taxjar { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Taxjar { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMinorUnitForConnector, + } + } +} + +impl api::Payment for Taxjar {} +impl api::PaymentSession for Taxjar {} +impl api::ConnectorAccessToken for Taxjar {} +impl api::MandateSetup for Taxjar {} +impl api::PaymentAuthorize for Taxjar {} +impl api::PaymentSync for Taxjar {} +impl api::PaymentCapture for Taxjar {} +impl api::PaymentVoid for Taxjar {} +impl api::Refund for Taxjar {} +impl api::RefundExecute for Taxjar {} +impl api::RefundSync for Taxjar {} +impl api::PaymentToken for Taxjar {} + +impl ConnectorIntegration + for Taxjar +{ + // Not Implemented (R) +} + +impl ConnectorCommonExt for Taxjar +where + Self: ConnectorIntegration, +{ + fn build_headers( + &self, + req: &RouterData, + _connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) + } +} + +impl ConnectorCommon for Taxjar { + fn id(&self) -> &'static str { + "taxjar" + } + + fn get_currency_unit(&self) -> api::CurrencyUnit { + api::CurrencyUnit::Base + // TODO! Check connector documentation, on which unit they are processing the currency. + // If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor, + // if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base + } + + fn common_get_content_type(&self) -> &'static str { + "application/json" + } + + fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str { + connectors.taxjar.base_url.as_ref() + } + + fn get_auth_header( + &self, + auth_type: &ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + let auth = taxjar::TaxjarAuthType::try_from(auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(vec![( + headers::AUTHORIZATION.to_string(), + auth.api_key.expose().into_masked(), + )]) + } + + fn build_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + let response: taxjar::TaxjarErrorResponse = res + .response + .parse_struct("TaxjarErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + Ok(ErrorResponse { + status_code: res.status_code, + code: response.code, + message: response.message, + reason: response.reason, + attempt_status: None, + connector_transaction_id: None, + }) + } +} + +impl ConnectorValidation for Taxjar { + //TODO: implement functions when support enabled +} + +impl ConnectorIntegration for Taxjar { + //TODO: implement sessions flow +} + +impl ConnectorIntegration for Taxjar {} + +impl ConnectorIntegration for Taxjar {} + +impl ConnectorIntegration for Taxjar { + fn get_headers( + &self, + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsAuthorizeRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &PaymentsAuthorizeRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let amount = utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = taxjar::TaxjarRouterData::from((amount, req)); + let connector_req = taxjar::TaxjarPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &PaymentsAuthorizeRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsAuthorizeType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: taxjar::TaxjarPaymentsResponse = res + .response + .parse_struct("Taxjar PaymentsAuthorizeResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Taxjar { + fn get_headers( + &self, + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsSyncRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &PaymentsSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Get) + .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: taxjar::TaxjarPaymentsResponse = res + .response + .parse_struct("taxjar PaymentsSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Taxjar { + fn get_headers( + &self, + req: &PaymentsCaptureRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &PaymentsCaptureRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + _req: &PaymentsCaptureRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + } + + fn build_request( + &self, + req: &PaymentsCaptureRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Post) + .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsCaptureType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &PaymentsCaptureRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: taxjar::TaxjarPaymentsResponse = res + .response + .parse_struct("Taxjar PaymentsCaptureResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Taxjar {} + +impl ConnectorIntegration for Taxjar { + fn get_headers( + &self, + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &RefundsRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &RefundsRouterData, + _connectors: &Connectors, + ) -> CustomResult { + let refund_amount = utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = taxjar::TaxjarRouterData::from((refund_amount, req)); + let connector_req = taxjar::TaxjarRefundRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &RefundsRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = RequestBuilder::new() + .method(Method::Post) + .url(&types::RefundExecuteType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundExecuteType::get_headers( + self, req, connectors, + )?) + .set_body(types::RefundExecuteType::get_request_body( + self, req, connectors, + )?) + .build(); + Ok(Some(request)) + } + + fn handle_response( + &self, + data: &RefundsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: taxjar::RefundResponse = + res.response + .parse_struct("taxjar RefundResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Taxjar { + fn get_headers( + &self, + req: &RefundSyncRouterData, + connectors: &Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &RefundSyncRouterData, + _connectors: &Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &RefundSyncRouterData, + connectors: &Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + RequestBuilder::new() + .method(Method::Get) + .url(&types::RefundSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundSyncType::get_headers(self, req, connectors)?) + .set_body(types::RefundSyncType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &RefundSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: taxjar::RefundResponse = res + .response + .parse_struct("taxjar RefundSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + RouterData::try_from(ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +#[async_trait::async_trait] +impl webhooks::IncomingWebhook for Taxjar { + fn get_webhook_object_reference_id( + &self, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_event_type( + &self, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_resource_object( + &self, + _request: &webhooks::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } +} diff --git a/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs b/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs new file mode 100644 index 000000000000..81493c8f5d9f --- /dev/null +++ b/crates/hyperswitch_connectors/src/connectors/taxjar/transformers.rs @@ -0,0 +1,228 @@ +use common_enums::enums; +use common_utils::types::StringMinorUnit; +use hyperswitch_domain_models::{ + payment_method_data::PaymentMethodData, + router_data::{ConnectorAuthType, RouterData}, + router_flow_types::refunds::{Execute, RSync}, + router_request_types::ResponseId, + router_response_types::{PaymentsResponseData, RefundsResponseData}, + types::{PaymentsAuthorizeRouterData, RefundsRouterData}, +}; +use hyperswitch_interfaces::errors; +use masking::Secret; +use serde::{Deserialize, Serialize}; + +use crate::{ + types::{RefundsResponseRouterData, ResponseRouterData}, + utils::PaymentsAuthorizeRequestData, +}; + +//TODO: Fill the struct with respective fields +pub struct TaxjarRouterData { + pub amount: StringMinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub router_data: T, +} + +impl From<(StringMinorUnit, T)> for TaxjarRouterData { + fn from((amount, item): (StringMinorUnit, T)) -> Self { + //Todo : use utils to convert the amount to the type of amount that a connector accepts + Self { + amount, + router_data: item, + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Serialize, PartialEq)] +pub struct TaxjarPaymentsRequest { + amount: StringMinorUnit, + card: TaxjarCard, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct TaxjarCard { + number: cards::CardNumber, + expiry_month: Secret, + expiry_year: Secret, + cvc: Secret, + complete: bool, +} + +impl TryFrom<&TaxjarRouterData<&PaymentsAuthorizeRouterData>> for TaxjarPaymentsRequest { + type Error = error_stack::Report; + fn try_from( + item: &TaxjarRouterData<&PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { + PaymentMethodData::Card(req_card) => { + let card = TaxjarCard { + number: req_card.card_number, + expiry_month: req_card.card_exp_month, + expiry_year: req_card.card_exp_year, + cvc: req_card.card_cvc, + complete: item.router_data.request.is_auto_capture()?, + }; + Ok(Self { + amount: item.amount.clone(), + card, + }) + } + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + +//TODO: Fill the struct with respective fields +// Auth Struct +pub struct TaxjarAuthType { + pub(super) api_key: Secret, +} + +impl TryFrom<&ConnectorAuthType> for TaxjarAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &ConnectorAuthType) -> Result { + match auth_type { + ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + api_key: api_key.to_owned(), + }), + _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + } + } +} +// PaymentsResponse +//TODO: Append the remaining status flags +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum TaxjarPaymentStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for common_enums::AttemptStatus { + fn from(item: TaxjarPaymentStatus) -> Self { + match item { + TaxjarPaymentStatus::Succeeded => Self::Charged, + TaxjarPaymentStatus::Failed => Self::Failure, + TaxjarPaymentStatus::Processing => Self::Authorizing, + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct TaxjarPaymentsResponse { + status: TaxjarPaymentStatus, + id: String, +} + +impl TryFrom> + for RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: ResponseRouterData, + ) -> Result { + Ok(Self { + status: common_enums::AttemptStatus::from(item.response.status), + response: Ok(PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(item.response.id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +// REFUND : +// Type definition for RefundRequest +#[derive(Default, Debug, Serialize)] +pub struct TaxjarRefundRequest { + pub amount: StringMinorUnit, +} + +impl TryFrom<&TaxjarRouterData<&RefundsRouterData>> for TaxjarRefundRequest { + type Error = error_stack::Report; + fn try_from(item: &TaxjarRouterData<&RefundsRouterData>) -> Result { + Ok(Self { + amount: item.amount.to_owned(), + }) + } +} + +// Type definition for Refund Response + +#[allow(dead_code)] +#[derive(Debug, Serialize, Default, Deserialize, Clone)] +pub enum RefundStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::RefundStatus { + fn from(item: RefundStatus) -> Self { + match item { + RefundStatus::Succeeded => Self::Success, + RefundStatus::Failed => Self::Failure, + RefundStatus::Processing => Self::Pending, + //TODO: Review mapping + } + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RefundResponse { + id: String, + status: RefundStatus, +} + +impl TryFrom> for RefundsRouterData { + type Error = error_stack::Report; + fn try_from( + item: RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +impl TryFrom> for RefundsRouterData { + type Error = error_stack::Report; + fn try_from( + item: RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +//TODO: Fill the struct with respective fields +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct TaxjarErrorResponse { + pub status_code: u16, + pub code: String, + pub message: String, + pub reason: Option, +} diff --git a/crates/hyperswitch_connectors/src/default_implementations.rs b/crates/hyperswitch_connectors/src/default_implementations.rs index 5851443a2c4e..9165a1cba531 100644 --- a/crates/hyperswitch_connectors/src/default_implementations.rs +++ b/crates/hyperswitch_connectors/src/default_implementations.rs @@ -91,7 +91,8 @@ default_imp_for_authorize_session_token!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); use crate::connectors; @@ -115,7 +116,8 @@ default_imp_for_complete_authorize!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_incremental_authorization { @@ -139,7 +141,8 @@ default_imp_for_incremental_authorization!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_create_customer { @@ -162,7 +165,8 @@ default_imp_for_create_customer!( connectors::Bitpay, connectors::Fiserv, connectors::Fiservemea, - connectors::Helcim + connectors::Helcim, + connectors::Taxjar ); macro_rules! default_imp_for_connector_redirect_response { @@ -187,7 +191,8 @@ default_imp_for_connector_redirect_response!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_pre_processing_steps{ @@ -211,7 +216,8 @@ default_imp_for_pre_processing_steps!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_post_processing_steps{ @@ -235,7 +241,8 @@ default_imp_for_post_processing_steps!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_approve { @@ -259,7 +266,8 @@ default_imp_for_approve!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_reject { @@ -283,7 +291,8 @@ default_imp_for_reject!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_webhook_source_verification { @@ -307,7 +316,8 @@ default_imp_for_webhook_source_verification!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_accept_dispute { @@ -332,7 +342,8 @@ default_imp_for_accept_dispute!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_submit_evidence { @@ -356,7 +367,8 @@ default_imp_for_submit_evidence!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_defend_dispute { @@ -380,7 +392,8 @@ default_imp_for_defend_dispute!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_file_upload { @@ -413,7 +426,8 @@ default_imp_for_file_upload!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -439,7 +453,8 @@ default_imp_for_payouts_create!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -465,7 +480,8 @@ default_imp_for_payouts_retrieve!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -491,7 +507,8 @@ default_imp_for_payouts_eligibility!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -517,7 +534,8 @@ default_imp_for_payouts_fulfill!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -543,7 +561,8 @@ default_imp_for_payouts_cancel!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -569,7 +588,8 @@ default_imp_for_payouts_quote!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -595,7 +615,8 @@ default_imp_for_payouts_recipient!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -621,7 +642,8 @@ default_imp_for_payouts_recipient_account!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -647,7 +669,8 @@ default_imp_for_frm_sale!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -673,7 +696,8 @@ default_imp_for_frm_checkout!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -699,7 +723,8 @@ default_imp_for_frm_transaction!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -725,7 +750,8 @@ default_imp_for_frm_fulfillment!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -751,7 +777,8 @@ default_imp_for_frm_record_return!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_revoking_mandates { @@ -774,5 +801,6 @@ default_imp_for_revoking_mandates!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); diff --git a/crates/hyperswitch_connectors/src/default_implementations_v2.rs b/crates/hyperswitch_connectors/src/default_implementations_v2.rs index 32e89b3f8bb9..c9a1cfde7454 100644 --- a/crates/hyperswitch_connectors/src/default_implementations_v2.rs +++ b/crates/hyperswitch_connectors/src/default_implementations_v2.rs @@ -186,7 +186,8 @@ default_imp_for_new_connector_integration_payment!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_refund { @@ -211,7 +212,8 @@ default_imp_for_new_connector_integration_refund!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_connector_access_token { @@ -231,7 +233,8 @@ default_imp_for_new_connector_integration_connector_access_token!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_accept_dispute { @@ -257,7 +260,8 @@ default_imp_for_new_connector_integration_accept_dispute!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_submit_evidence { @@ -282,7 +286,8 @@ default_imp_for_new_connector_integration_submit_evidence!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_defend_dispute { @@ -307,7 +312,8 @@ default_imp_for_new_connector_integration_defend_dispute!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_file_upload { @@ -342,7 +348,8 @@ default_imp_for_new_connector_integration_file_upload!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -369,7 +376,8 @@ default_imp_for_new_connector_integration_payouts_create!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -396,7 +404,8 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -423,7 +432,8 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -450,7 +460,8 @@ default_imp_for_new_connector_integration_payouts_cancel!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -477,7 +488,8 @@ default_imp_for_new_connector_integration_payouts_quote!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -504,7 +516,8 @@ default_imp_for_new_connector_integration_payouts_recipient!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -531,7 +544,8 @@ default_imp_for_new_connector_integration_payouts_sync!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "payouts")] @@ -558,7 +572,8 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_webhook_source_verification { @@ -583,7 +598,8 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -610,7 +626,8 @@ default_imp_for_new_connector_integration_frm_sale!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -637,7 +654,8 @@ default_imp_for_new_connector_integration_frm_checkout!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -664,7 +682,8 @@ default_imp_for_new_connector_integration_frm_transaction!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -691,7 +710,8 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); #[cfg(feature = "frm")] @@ -718,7 +738,8 @@ default_imp_for_new_connector_integration_frm_record_return!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); macro_rules! default_imp_for_new_connector_integration_revoking_mandates { @@ -742,5 +763,6 @@ default_imp_for_new_connector_integration_revoking_mandates!( connectors::Fiserv, connectors::Fiservemea, connectors::Helcim, - connectors::Stax + connectors::Stax, + connectors::Taxjar ); diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index dcc6a88c8242..fa9af562cf3f 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -439,7 +439,7 @@ pub struct BusinessProfile { // pub order_fulfillment_time_origin: Option, pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, - // pub default_fallback_routing: Option, + pub default_fallback_routing: Option, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] @@ -470,7 +470,7 @@ pub struct BusinessProfileGeneralUpdate { // pub order_fulfillment_time_origin: Option, pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, - // pub default_fallback_routing: Option, + pub default_fallback_routing: Option, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] @@ -481,6 +481,9 @@ pub enum BusinessProfileUpdate { routing_algorithm_id: Option, payout_routing_algorithm_id: Option, }, + DefaultRoutingFallbackUpdate { + default_fallback_routing: Option, + }, ExtendedCardInfoUpdate { is_extended_card_info_enabled: Option, }, @@ -522,7 +525,7 @@ impl From for BusinessProfileUpdateInternal { // order_fulfillment_time_origin, frm_routing_algorithm_id, payout_routing_algorithm_id, - // default_fallback_routing, + default_fallback_routing, } = *update; Self { profile_name, @@ -553,7 +556,7 @@ impl From for BusinessProfileUpdateInternal { // order_fulfillment_time_origin, frm_routing_algorithm_id, payout_routing_algorithm_id, - // default_fallback_routing, + default_fallback_routing, } } BusinessProfileUpdate::RoutingAlgorithmUpdate { @@ -587,7 +590,7 @@ impl From for BusinessProfileUpdateInternal { // order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, payout_routing_algorithm_id, - // default_fallback_routing: None, + default_fallback_routing: None, }, BusinessProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, @@ -619,7 +622,7 @@ impl From for BusinessProfileUpdateInternal { // order_fulfillment_time: None, // order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, - // default_fallback_routing: None, + default_fallback_routing: None, }, BusinessProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, @@ -651,7 +654,39 @@ impl From for BusinessProfileUpdateInternal { // order_fulfillment_time: None, // order_fulfillment_time_origin: None, frm_routing_algorithm_id: None, - // default_fallback_routing: None, + default_fallback_routing: None, + }, + BusinessProfileUpdate::DefaultRoutingFallbackUpdate { + default_fallback_routing, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + routing_algorithm_id: None, + payout_routing_algorithm_id: None, + intent_fulfillment_time: None, + // order_fulfillment_time: None, + // order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + default_fallback_routing, }, } } @@ -699,7 +734,7 @@ impl super::behaviour::Conversion for BusinessProfile { // order_fulfillment_time: self.order_fulfillment_time, // order_fulfillment_time_origin: self.order_fulfillment_time_origin, frm_routing_algorithm_id: self.frm_routing_algorithm_id, - // default_fallback_routing: self.default_fallback_routing, + default_fallback_routing: self.default_fallback_routing, }) } @@ -760,7 +795,7 @@ impl super::behaviour::Conversion for BusinessProfile { // order_fulfillment_time_origin: item.order_fulfillment_time_origin, frm_routing_algorithm_id: item.frm_routing_algorithm_id, payout_routing_algorithm_id: item.payout_routing_algorithm_id, - // default_fallback_routing: item.default_fallback_routing, + default_fallback_routing: item.default_fallback_routing, }) } .await @@ -805,7 +840,7 @@ impl super::behaviour::Conversion for BusinessProfile { // order_fulfillment_time_origin: self.order_fulfillment_time_origin, frm_routing_algorithm_id: self.frm_routing_algorithm_id, payout_routing_algorithm_id: self.payout_routing_algorithm_id, - // default_fallback_routing: self.default_fallback_routing, + default_fallback_routing: self.default_fallback_routing, }) } } diff --git a/crates/hyperswitch_domain_models/src/consts.rs b/crates/hyperswitch_domain_models/src/consts.rs index 8499cd3d82f0..c19d813cfea3 100644 --- a/crates/hyperswitch_domain_models/src/consts.rs +++ b/crates/hyperswitch_domain_models/src/consts.rs @@ -3,12 +3,17 @@ #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "customer_v2"), - not(feature = "merchant_account_v2") + not(feature = "merchant_account_v2"), + not(feature = "merchant_connector_account_v2") ))] pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V1; #[cfg(all( feature = "v2", - any(feature = "customer_v2", feature = "merchant_account_v2") + any( + feature = "customer_v2", + feature = "merchant_account_v2", + feature = "merchant_connector_account_v2" + ) ))] pub const API_VERSION: common_enums::ApiVersion = common_enums::ApiVersion::V2; diff --git a/crates/hyperswitch_domain_models/src/customer.rs b/crates/hyperswitch_domain_models/src/customer.rs index 7f217632def4..5559a8c0e788 100644 --- a/crates/hyperswitch_domain_models/src/customer.rs +++ b/crates/hyperswitch_domain_models/src/customer.rs @@ -290,6 +290,9 @@ pub enum CustomerUpdate { phone_country_code: Option, metadata: Option, connector_customer: Option, + default_billing_address: Option, + default_shipping_address: Option, + default_payment_method_id: Option>, }, ConnectorCustomer { connector_customer: Option, @@ -311,6 +314,9 @@ impl From for CustomerUpdateInternal { phone_country_code, metadata, connector_customer, + default_billing_address, + default_shipping_address, + default_payment_method_id, } => Self { name: name.map(Encryption::from), email: email.map(Encryption::from), @@ -320,20 +326,24 @@ impl From for CustomerUpdateInternal { metadata, connector_customer, modified_at: date_time::now(), - default_payment_method_id: None, + default_billing_address, + default_shipping_address, + default_payment_method_id, updated_by: None, }, CustomerUpdate::ConnectorCustomer { connector_customer } => Self { connector_customer, - modified_at: date_time::now(), name: None, email: None, phone: None, description: None, phone_country_code: None, metadata: None, + modified_at: date_time::now(), default_payment_method_id: None, updated_by: None, + default_billing_address: None, + default_shipping_address: None, }, CustomerUpdate::UpdateDefaultPaymentMethod { default_payment_method_id, @@ -348,6 +358,8 @@ impl From for CustomerUpdateInternal { metadata: None, connector_customer: None, updated_by: None, + default_billing_address: None, + default_shipping_address: None, }, } } diff --git a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs index 006fd5d7755f..9e2f6918b43f 100644 --- a/crates/hyperswitch_domain_models/src/merchant_connector_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_connector_account.rs @@ -36,12 +36,13 @@ pub struct MerchantConnectorAccount { pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, pub connector_webhook_details: Option, - pub profile_id: Option, + pub profile_id: String, pub applepay_verified_domains: Option>, pub pm_auth_config: Option, pub status: enums::ConnectorStatus, pub connector_wallets_details: Option>, pub additional_merchant_data: Option>, + pub version: common_enums::ApiVersion, } #[cfg(all( @@ -70,12 +71,13 @@ pub struct MerchantConnectorAccount { pub created_at: time::PrimitiveDateTime, pub modified_at: time::PrimitiveDateTime, pub connector_webhook_details: Option, - pub profile_id: Option, + pub profile_id: String, pub applepay_verified_domains: Option>, pub pm_auth_config: Option, pub status: enums::ConnectorStatus, pub connector_wallets_details: Option>, pub additional_merchant_data: Option>, + pub version: common_enums::ApiVersion, } #[cfg(all(feature = "v2", feature = "merchant_connector_account_v2"))] @@ -165,12 +167,13 @@ impl behaviour::Conversion for MerchantConnectorAccount { created_at: self.created_at, modified_at: self.modified_at, connector_webhook_details: self.connector_webhook_details, - profile_id: self.profile_id, + profile_id: Some(self.profile_id), applepay_verified_domains: self.applepay_verified_domains, pm_auth_config: self.pm_auth_config, status: self.status, connector_wallets_details: self.connector_wallets_details.map(Encryption::from), additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + version: self.version, }, ) } @@ -212,7 +215,11 @@ impl behaviour::Conversion for MerchantConnectorAccount { created_at: other.created_at, modified_at: other.modified_at, connector_webhook_details: other.connector_webhook_details, - profile_id: other.profile_id, + profile_id: other + .profile_id + .ok_or(ValidationError::MissingRequiredField { + field_name: "profile_id".to_string(), + })?, applepay_verified_domains: other.applepay_verified_domains, pm_auth_config: other.pm_auth_config, status: other.status, @@ -251,6 +258,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { } else { None }, + version: other.version, }) } @@ -275,12 +283,13 @@ impl behaviour::Conversion for MerchantConnectorAccount { created_at: now, modified_at: now, connector_webhook_details: self.connector_webhook_details, - profile_id: self.profile_id, + profile_id: Some(self.profile_id), applepay_verified_domains: self.applepay_verified_domains, pm_auth_config: self.pm_auth_config, status: self.status, connector_wallets_details: self.connector_wallets_details.map(Encryption::from), additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + version: self.version, }) } } @@ -313,6 +322,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { status: self.status, connector_wallets_details: self.connector_wallets_details.map(Encryption::from), additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + version: self.version, }, ) } @@ -389,6 +399,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { } else { None }, + version: other.version, }) } @@ -414,6 +425,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { status: self.status, connector_wallets_details: self.connector_wallets_details.map(Encryption::from), additional_merchant_data: self.additional_merchant_data.map(|data| data.into()), + version: self.version, }) } } diff --git a/crates/hyperswitch_domain_models/src/payment_address.rs b/crates/hyperswitch_domain_models/src/payment_address.rs index d4f2cf9f350f..548dd0c54166 100644 --- a/crates/hyperswitch_domain_models/src/payment_address.rs +++ b/crates/hyperswitch_domain_models/src/payment_address.rs @@ -53,6 +53,7 @@ impl PaymentAddress { } /// Unify the billing details from `payment_method_data.[payment_method_data].billing details`. + /// Here the fields passed in payment_method_data_billing takes precedence pub fn unify_with_payment_method_data_billing( self, payment_method_data_billing: Option
, @@ -72,6 +73,29 @@ impl PaymentAddress { } } + /// Unify the billing details from `payment_method_data.[payment_method_data].billing details`. + /// Here the `self` takes precedence + pub fn unify_with_payment_data_billing( + self, + other_payment_method_billing: Option
, + ) -> Self { + let unified_payment_method_billing = self + .get_payment_method_billing() + .map(|payment_method_billing| { + payment_method_billing + .clone() + .unify_address(other_payment_method_billing.as_ref()) + }) + .or(other_payment_method_billing); + + Self { + shipping: self.shipping, + billing: self.billing, + unified_payment_method_billing, + payment_method_billing: self.payment_method_billing, + } + } + pub fn get_request_payment_method_billing(&self) -> Option<&Address> { self.payment_method_billing.as_ref() } diff --git a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs index adba454c798f..b863382b7cda 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payout_attempt.rs @@ -41,9 +41,9 @@ pub trait PayoutAttemptInterface { async fn get_filters_for_payouts( &self, - payout: &[Payouts], - merchant_id: &id_type::MerchantId, - storage_scheme: MerchantStorageScheme, + _payout: &[Payouts], + _merchant_id: &id_type::MerchantId, + _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result; } diff --git a/crates/hyperswitch_domain_models/src/payouts/payouts.rs b/crates/hyperswitch_domain_models/src/payouts/payouts.rs index 0c0fbe8ae28c..834b6bb3f76d 100644 --- a/crates/hyperswitch_domain_models/src/payouts/payouts.rs +++ b/crates/hyperswitch_domain_models/src/payouts/payouts.rs @@ -61,10 +61,29 @@ pub trait PayoutsInterface { #[cfg(feature = "olap")] async fn filter_payouts_by_time_range_constraints( &self, - merchant_id: &id_type::MerchantId, - time_range: &api_models::payments::TimeRange, - storage_scheme: MerchantStorageScheme, + _merchant_id: &id_type::MerchantId, + _time_range: &api_models::payments::TimeRange, + _storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, errors::StorageError>; + + #[cfg(feature = "olap")] + #[allow(clippy::too_many_arguments)] + async fn get_total_count_of_filtered_payouts( + &self, + _merchant_id: &id_type::MerchantId, + _active_payout_ids: &[String], + _connector: Option>, + _currency: Option>, + _status: Option>, + _payout_method: Option>, + ) -> error_stack::Result; + + #[cfg(feature = "olap")] + async fn filter_active_payout_ids_by_constraints( + &self, + _merchant_id: &id_type::MerchantId, + _constraints: &PayoutFetchConstraints, + ) -> error_stack::Result, errors::StorageError>; } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/crates/hyperswitch_interfaces/src/configs.rs b/crates/hyperswitch_interfaces/src/configs.rs index a2dd4675c8ce..2a30df3b2cbf 100644 --- a/crates/hyperswitch_interfaces/src/configs.rs +++ b/crates/hyperswitch_interfaces/src/configs.rs @@ -75,6 +75,7 @@ pub struct Connectors { pub square: ConnectorParams, pub stax: ConnectorParams, pub stripe: ConnectorParamsWithFileUploadUrl, + pub taxjar: ConnectorParams, pub threedsecureio: ConnectorParams, pub trustpay: ConnectorParamsWithMoreUrls, pub tsys: ConnectorParams, diff --git a/crates/kgraph_utils/benches/evaluation.rs b/crates/kgraph_utils/benches/evaluation.rs index 5fdab57aac0c..ab2e8c80ca43 100644 --- a/crates/kgraph_utils/benches/evaluation.rs +++ b/crates/kgraph_utils/benches/evaluation.rs @@ -64,7 +64,7 @@ fn build_test_data( connector_label: Some("something".to_string()), frm_configs: None, connector_webhook_details: None, - profile_id: None, + profile_id: "profile_id".to_string(), applepay_verified_domains: None, pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, @@ -90,7 +90,7 @@ fn build_test_data( business_sub_label: Some("something".to_string()), frm_configs: None, connector_webhook_details: None, - profile_id: None, + profile_id: "profile_id".to_string(), applepay_verified_domains: None, pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 2c61bdfe19b4..d4e046697140 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -752,7 +752,7 @@ mod tests { }]), frm_configs: None, connector_webhook_details: None, - profile_id: None, + profile_id: "profile_id".to_string(), applepay_verified_domains: None, pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, @@ -813,7 +813,7 @@ mod tests { }]), frm_configs: None, connector_webhook_details: None, - profile_id: None, + profile_id: "profile_id".to_string(), applepay_verified_domains: None, pm_auth_config: None, status: api_enums::ConnectorStatus::Inactive, diff --git a/crates/openapi/src/routes/merchant_connector_account.rs b/crates/openapi/src/routes/merchant_connector_account.rs index d09414d16f64..d4bb0c1bd46b 100644 --- a/crates/openapi/src/routes/merchant_connector_account.rs +++ b/crates/openapi/src/routes/merchant_connector_account.rs @@ -64,7 +64,7 @@ pub async fn connector_create() {} #[cfg(feature = "v2")] #[utoipa::path( post, - path = "/connector_accounts", + path = "/v2/connector_accounts", request_body( content = MerchantConnectorCreate, examples( @@ -146,7 +146,7 @@ pub async fn connector_retrieve() {} #[cfg(feature = "v2")] #[utoipa::path( get, - path = "/connector_accounts/{id}", + path = "/v2/connector_accounts/{id}", params( ("id" = i32, Path, description = "The unique identifier for the Merchant Connector") ), @@ -232,7 +232,7 @@ pub async fn connector_update() {} #[cfg(feature = "v2")] #[utoipa::path( post, - path = "/connector_accounts/{id}", + path = "/v2/connector_accounts/{id}", request_body( content = MerchantConnectorUpdate, examples( @@ -298,7 +298,7 @@ pub async fn connector_delete() {} #[cfg(feature = "v2")] #[utoipa::path( delete, - path = "/connector_accounts/{id}", + path = "/v2/connector_accounts/{id}", params( ("id" = i32, Path, description = "The unique identifier for the Merchant Connector") ), diff --git a/crates/openapi/src/routes/webhook_events.rs b/crates/openapi/src/routes/webhook_events.rs index fc06ffee5808..c754f59e517a 100644 --- a/crates/openapi/src/routes/webhook_events.rs +++ b/crates/openapi/src/routes/webhook_events.rs @@ -3,12 +3,12 @@ /// List all Events associated with a Merchant Account or Business Profile. #[utoipa::path( get, - path = "/events/{merchant_id_or_profile_id}", + path = "/events/{merchant_id}", params( ( - "merchant_id_or_profile_id" = String, + "merchant_id" = String, Path, - description = "The unique identifier for the Merchant Account or Business Profile" + description = "The unique identifier for the Merchant Account." ), ( "created_after" = Option, @@ -40,6 +40,11 @@ description = "Only include Events associated with the specified object (Payment Intent ID, Refund ID, etc.). \ Either only `object_id` must be specified, or one or more of `created_after`, `created_before`, `limit` and `offset` must be specified." ), + ( + "profile_id" = Option, + Query, + description = "Only include Events associated with the Business Profile identified by the specified Business Profile ID." + ), ), responses( (status = 200, description = "List of Events retrieved successfully", body = Vec), @@ -55,9 +60,9 @@ pub fn list_initial_webhook_delivery_attempts() {} /// List all delivery attempts for the specified Event. #[utoipa::path( get, - path = "/events/{merchant_id_or_profile_id}/{event_id}/attempts", + path = "/events/{merchant_id}/{event_id}/attempts", params( - ("merchant_id_or_profile_id" = String, Path, description = "The unique identifier for the Merchant Account or Business Profile"), + ("merchant_id" = String, Path, description = "The unique identifier for the Merchant Account."), ("event_id" = String, Path, description = "The unique identifier for the Event"), ), responses( @@ -74,9 +79,9 @@ pub fn list_webhook_delivery_attempts() {} /// Manually retry the delivery of the specified Event. #[utoipa::path( post, - path = "/events/{merchant_id_or_profile_id}/{event_id}/retry", + path = "/events/{merchant_id}/{event_id}/retry", params( - ("merchant_id_or_profile_id" = String, Path, description = "The unique identifier for the Merchant Account or Business Profile"), + ("merchant_id" = String, Path, description = "The unique identifier for the Merchant Account."), ("event_id" = String, Path, description = "The unique identifier for the Event"), ), responses( diff --git a/crates/redis_interface/src/commands.rs b/crates/redis_interface/src/commands.rs index ccfeb3e5b12e..9b76d8d9c816 100644 --- a/crates/redis_interface/src/commands.rs +++ b/crates/redis_interface/src/commands.rs @@ -15,7 +15,7 @@ use common_utils::{ }; use error_stack::{report, ResultExt}; use fred::{ - interfaces::{HashesInterface, KeysInterface, SetsInterface, StreamsInterface}, + interfaces::{HashesInterface, KeysInterface, ListInterface, SetsInterface, StreamsInterface}, prelude::RedisErrorKind, types::{ Expiration, FromRedis, MultipleIDs, MultipleKeys, MultipleOrderedPairs, MultipleStrings, @@ -371,6 +371,19 @@ impl super::RedisConnectionPool { Ok(hsetnx) } + #[instrument(level = "DEBUG", skip(self))] + pub async fn increment_field_in_hash( + &self, + key: &str, + field: &str, + increment: i64, + ) -> CustomResult { + self.pool + .hincrby(self.add_prefix(key), field, increment) + .await + .change_context(errors::RedisError::IncrementHashFieldFailed) + } + #[instrument(level = "DEBUG", skip(self))] pub async fn hscan( &self, @@ -630,6 +643,55 @@ impl super::RedisConnectionPool { }) } + #[instrument(level = "DEBUG", skip(self))] + pub async fn append_elements_to_list( + &self, + key: &str, + elements: V, + ) -> CustomResult<(), errors::RedisError> + where + V: TryInto + Debug + Send, + V::Error: Into + Send, + { + self.pool + .rpush(self.add_prefix(key), elements) + .await + .change_context(errors::RedisError::AppendElementsToListFailed) + } + + #[instrument(level = "DEBUG", skip(self))] + pub async fn get_list_elements( + &self, + key: &str, + start: i64, + stop: i64, + ) -> CustomResult, errors::RedisError> { + self.pool + .lrange(self.add_prefix(key), start, stop) + .await + .change_context(errors::RedisError::GetListElementsFailed) + } + + #[instrument(level = "DEBUG", skip(self))] + pub async fn get_list_length(&self, key: &str) -> CustomResult { + self.pool + .llen(self.add_prefix(key)) + .await + .change_context(errors::RedisError::GetListLengthFailed) + } + + #[instrument(level = "DEBUG", skip(self))] + pub async fn lpop_list_elements( + &self, + key: &str, + count: Option, + ) -> CustomResult, errors::RedisError> { + self.pool + .lpop(self.add_prefix(key), count) + .await + .change_context(errors::RedisError::PopListElementsFailed) + } + // Consumer Group API #[instrument(level = "DEBUG", skip(self))] diff --git a/crates/redis_interface/src/errors.rs b/crates/redis_interface/src/errors.rs index cc7f8bdd21fe..0e2a4b8d63b0 100644 --- a/crates/redis_interface/src/errors.rs +++ b/crates/redis_interface/src/errors.rs @@ -68,4 +68,14 @@ pub enum RedisError { OnMessageError, #[error("Got an unknown result from redis")] UnknownResult, + #[error("Failed to append elements to list in Redis")] + AppendElementsToListFailed, + #[error("Failed to get list elements in Redis")] + GetListElementsFailed, + #[error("Failed to get length of list")] + GetListLengthFailed, + #[error("Failed to pop list elements in Redis")] + PopListElementsFailed, + #[error("Failed to increment hash field in Redis")] + IncrementHashFieldFailed, } diff --git a/crates/router/src/compatibility/stripe/customers.rs b/crates/router/src/compatibility/stripe/customers.rs index 39616e007bca..330610a9a36e 100644 --- a/crates/router/src/compatibility/stripe/customers.rs +++ b/crates/router/src/compatibility/stripe/customers.rs @@ -110,8 +110,9 @@ pub async fn customer_update( }; let customer_id = path.into_inner(); - let mut cust_update_req: customer_types::CustomerRequest = payload.into(); + let mut cust_update_req: customer_types::CustomerUpdateRequest = payload.into(); cust_update_req.customer_id = Some(customer_id); + let customer_update_id = customer_types::UpdateCustomerId::new("temp_global_id".to_string()); let flow = Flow::CustomersUpdate; @@ -130,7 +131,13 @@ pub async fn customer_update( &req, cust_update_req, |state, auth, req, _| { - customers::update_customer(state, auth.merchant_account, req, auth.key_store) + customers::update_customer( + state, + auth.merchant_account, + req, + auth.key_store, + customer_update_id.clone(), + ) }, &auth::HeaderAuth(auth::ApiKeyAuth), api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/compatibility/stripe/customers/types.rs b/crates/router/src/compatibility/stripe/customers/types.rs index 82b787445cf2..9f5c546a18ed 100644 --- a/crates/router/src/compatibility/stripe/customers/types.rs +++ b/crates/router/src/compatibility/stripe/customers/types.rs @@ -135,7 +135,7 @@ impl From for api::CustomerRequest { } #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -impl From for api::CustomerRequest { +impl From for api::CustomerUpdateRequest { fn from(req: CustomerUpdateRequest) -> Self { Self { name: req.name, diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 7ca10f758555..eb83e2c4ba66 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -69,7 +69,7 @@ pub mod zsl; pub use hyperswitch_connectors::connectors::{ bambora, bambora::Bambora, bitpay, bitpay::Bitpay, fiserv, fiserv::Fiserv, fiservemea, - fiservemea::Fiservemea, helcim, helcim::Helcim, stax, stax::Stax, + fiservemea::Fiservemea, helcim, helcim::Helcim, stax, stax::Stax, taxjar, taxjar::Taxjar, }; #[cfg(feature = "dummy_connector")] diff --git a/crates/router/src/connector/aci.rs b/crates/router/src/connector/aci.rs index 98fd0d92ee74..6f800194a476 100644 --- a/crates/router/src/connector/aci.rs +++ b/crates/router/src/connector/aci.rs @@ -1,13 +1,15 @@ mod result_codes; pub mod transformers; -use std::fmt::Debug; -use common_utils::request::RequestContent; +use common_utils::{ + request::RequestContent, + types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector}, +}; use error_stack::{report, ResultExt}; use masking::PeekInterface; use transformers as aci; -use super::utils::{is_mandate_supported, PaymentsAuthorizeRequestData}; +use super::utils::{convert_amount, is_mandate_supported, PaymentsAuthorizeRequestData}; use crate::{ configs::settings, connector::utils::PaymentMethodDataType, @@ -26,8 +28,18 @@ use crate::{ utils::BytesExt, }; -#[derive(Debug, Clone)] -pub struct Aci; +#[derive(Clone)] +pub struct Aci { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Aci { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMajorUnitForConnector, + } + } +} impl ConnectorCommon for Aci { fn id(&self) -> &'static str { @@ -312,12 +324,14 @@ impl _connectors: &settings::Connectors, ) -> CustomResult { // encode only for for urlencoded things. - let connector_router_data = aci::AciRouterData::try_from(( - &self.get_currency_unit(), + + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + + let connector_router_data = aci::AciRouterData::from((amount, req)); let connector_req = aci::AciPaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) @@ -514,12 +528,13 @@ impl services::ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = aci::AciRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; + )?; + + let connector_router_data = aci::AciRouterData::from((amount, req)); let connector_req = aci::AciRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 933b4326350e..da31eae22a1e 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use common_utils::{id_type, pii::Email}; +use common_utils::{id_type, pii::Email, types::StringMajorUnit}; use error_stack::report; use masking::{ExposeInterface, Secret}; use reqwest::Url; @@ -18,26 +18,16 @@ type Error = error_stack::Report; #[derive(Debug, Serialize)] pub struct AciRouterData { - amount: String, + amount: StringMajorUnit, router_data: T, } -impl TryFrom<(&types::api::CurrencyUnit, enums::Currency, i64, T)> for AciRouterData { - type Error = error_stack::Report; - - fn try_from( - (currency_unit, currency, amount, item): ( - &types::api::CurrencyUnit, - enums::Currency, - i64, - T, - ), - ) -> Result { - let amount = utils::get_amount_as_string(currency_unit, amount, currency)?; - Ok(Self { +impl From<(StringMajorUnit, T)> for AciRouterData { + fn from((amount, item): (StringMajorUnit, T)) -> Self { + Self { amount, router_data: item, - }) + } } } @@ -76,7 +66,7 @@ pub struct AciPaymentsRequest { #[serde(rename_all = "camelCase")] pub struct TransactionDetails { pub entity_id: Secret, - pub amount: String, + pub amount: StringMajorUnit, pub currency: String, pub payment_type: AciPaymentType, } @@ -786,7 +776,7 @@ impl #[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct AciRefundRequest { - pub amount: String, + pub amount: StringMajorUnit, pub currency: String, pub payment_type: AciPaymentType, pub entity_id: Secret, diff --git a/crates/router/src/connector/ebanx.rs b/crates/router/src/connector/ebanx.rs index 69cfc501f111..e5e7f2e259aa 100644 --- a/crates/router/src/connector/ebanx.rs +++ b/crates/router/src/connector/ebanx.rs @@ -1,14 +1,16 @@ pub mod transformers; -use std::fmt::Debug; - #[cfg(feature = "payouts")] use common_utils::request::RequestContent; +#[cfg(feature = "payouts")] +use common_utils::types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}; use error_stack::{report, ResultExt}; #[cfg(feature = "payouts")] use router_env::{instrument, tracing}; use transformers as ebanx; +#[cfg(feature = "payouts")] +use crate::connector::utils::convert_amount; use crate::{ configs::settings, core::errors::{self, CustomResult}, @@ -25,9 +27,20 @@ use crate::{ headers, services::{self, request}, }; +#[derive(Clone)] +pub struct Ebanx { + #[cfg(feature = "payouts")] + amount_converter: &'static (dyn AmountConvertor + Sync), +} -#[derive(Debug, Clone)] -pub struct Ebanx; +impl Ebanx { + pub fn new() -> &'static Self { + &Self { + #[cfg(feature = "payouts")] + amount_converter: &FloatMajorUnitForConnector, + } + } +} impl api::Payment for Ebanx {} impl api::PaymentSession for Ebanx {} @@ -138,12 +151,12 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = ebanx::EbanxRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.source_currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = ebanx::EbanxRouterData::from((amount, req)); let connector_req = ebanx::EbanxPayoutCreateRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -221,12 +234,12 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = ebanx::EbanxRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.source_currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = ebanx::EbanxRouterData::from((amount, req)); let connector_req = ebanx::EbanxPayoutFulfillRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/ebanx/transformers.rs b/crates/router/src/connector/ebanx/transformers.rs index 8cc1e269f7ff..f5f096b7307f 100644 --- a/crates/router/src/connector/ebanx/transformers.rs +++ b/crates/router/src/connector/ebanx/transformers.rs @@ -1,39 +1,34 @@ #[cfg(feature = "payouts")] +use api_models::enums::Currency; +#[cfg(feature = "payouts")] use api_models::payouts::{Bank, PayoutMethodData}; -use common_enums::Currency; #[cfg(feature = "payouts")] use common_utils::pii::Email; +use common_utils::types::FloatMajorUnit; #[cfg(feature = "payouts")] use masking::ExposeInterface; use masking::Secret; use serde::{Deserialize, Serialize}; -use crate::{ - connector::utils, - core::errors, - types::{self, api::CurrencyUnit}, -}; #[cfg(feature = "payouts")] use crate::{ connector::utils::{AddressDetailsData, RouterData}, connector::utils::{CustomerDetails, PayoutsData}, types::{api, storage::enums as storage_enums}, }; +use crate::{core::errors, types}; pub struct EbanxRouterData { - pub amount: f64, // The type of amount that a connector accepts, for example, String, i64, f64, etc. + pub amount: FloatMajorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. pub router_data: T, } -impl TryFrom<(&CurrencyUnit, Currency, i64, T)> for EbanxRouterData { - type Error = error_stack::Report; - fn try_from( - (_currency_unit, currency, amount, item): (&CurrencyUnit, Currency, i64, T), - ) -> Result { - Ok(Self { - amount: utils::to_currency_base_unit_asf64(amount, currency)?, +impl From<(FloatMajorUnit, T)> for EbanxRouterData { + fn from((amount, item): (FloatMajorUnit, T)) -> Self { + Self { + amount, router_data: item, - }) + } } } @@ -43,7 +38,7 @@ pub struct EbanxPayoutCreateRequest { integration_key: Secret, external_reference: String, country: String, - amount: f64, + amount: FloatMajorUnit, currency: Currency, target: EbanxPayoutType, target_account: Secret, diff --git a/crates/router/src/connector/forte.rs b/crates/router/src/connector/forte.rs index 8b858e5553ed..5ffa3a589c31 100644 --- a/crates/router/src/connector/forte.rs +++ b/crates/router/src/connector/forte.rs @@ -1,14 +1,16 @@ pub mod transformers; -use std::fmt::Debug; - use base64::Engine; -use common_utils::request::RequestContent; +use common_utils::{ + request::RequestContent, + types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}, +}; use diesel_models::enums; use error_stack::{report, ResultExt}; use masking::PeekInterface; use transformers as forte; +use super::utils::convert_amount; use crate::{ configs::settings, connector::{ @@ -31,9 +33,18 @@ use crate::{ }, utils::BytesExt, }; +#[derive(Clone)] +pub struct Forte { + amount_converter: &'static (dyn AmountConvertor + Sync), +} -#[derive(Debug, Clone)] -pub struct Forte; +impl Forte { + pub fn new() -> &'static Self { + &Self { + amount_converter: &FloatMajorUnitForConnector, + } + } +} impl api::Payment for Forte {} impl api::PaymentSession for Forte {} @@ -225,7 +236,14 @@ impl ConnectorIntegration CustomResult { - let connector_req = forte::FortePaymentsRequest::try_from(req)?; + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = forte::ForteRouterData::from((amount, req)); + let connector_req = forte::FortePaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -562,7 +580,14 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_req = forte::ForteRefundRequest::try_from(req)?; + let refund_amount = convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = forte::ForteRouterData::from((refund_amount, req)); + let connector_req = forte::ForteRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/forte/transformers.rs b/crates/router/src/connector/forte/transformers.rs index b039282b719f..d9407b410115 100644 --- a/crates/router/src/connector/forte/transformers.rs +++ b/crates/router/src/connector/forte/transformers.rs @@ -1,4 +1,5 @@ use cards::CardNumber; +use common_utils::types::FloatMajorUnit; use masking::{PeekInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -10,10 +11,25 @@ use crate::{ types::{self, api, domain, storage::enums, transformers::ForeignFrom}, }; +#[derive(Debug, Serialize)] +pub struct ForteRouterData { + pub amount: FloatMajorUnit, + pub router_data: T, +} + +impl From<(FloatMajorUnit, T)> for ForteRouterData { + fn from((amount, router_data): (FloatMajorUnit, T)) -> Self { + Self { + amount, + router_data, + } + } +} + #[derive(Debug, Serialize)] pub struct FortePaymentsRequest { action: ForteAction, - authorization_amount: f64, + authorization_amount: FloatMajorUnit, billing_address: BillingAddress, card: Card, } @@ -62,9 +78,12 @@ impl TryFrom for ForteCardType { } } -impl TryFrom<&types::PaymentsAuthorizeRouterData> for FortePaymentsRequest { +impl TryFrom<&ForteRouterData<&types::PaymentsAuthorizeRouterData>> for FortePaymentsRequest { type Error = error_stack::Report; - fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { + fn try_from( + item_data: &ForteRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + let item = item_data.router_data; if item.request.currency != enums::Currency::USD { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Forte"), @@ -93,8 +112,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for FortePaymentsRequest { first_name: first_name.clone(), last_name: address.get_last_name().unwrap_or(first_name).clone(), }; - let authorization_amount = - utils::to_currency_base_unit_asf64(item.request.amount, item.request.currency)?; + let authorization_amount = item_data.amount; Ok(Self { action, authorization_amount, @@ -245,7 +263,7 @@ pub struct FortePaymentsResponse { pub transaction_id: String, pub location_id: Secret, pub action: ForteAction, - pub authorization_amount: Option, + pub authorization_amount: Option, pub authorization_code: String, pub entered_by: String, pub billing_address: Option, @@ -296,7 +314,7 @@ pub struct FortePaymentsSyncResponse { pub location_id: Secret, pub status: FortePaymentStatus, pub action: ForteAction, - pub authorization_amount: Option, + pub authorization_amount: Option, pub authorization_code: String, pub entered_by: String, pub billing_address: Option, @@ -479,20 +497,22 @@ impl #[derive(Default, Debug, Serialize)] pub struct ForteRefundRequest { action: String, - authorization_amount: f64, + authorization_amount: FloatMajorUnit, original_transaction_id: String, authorization_code: String, } -impl TryFrom<&types::RefundsRouterData> for ForteRefundRequest { +impl TryFrom<&ForteRouterData<&types::RefundsRouterData>> for ForteRefundRequest { type Error = error_stack::Report; - fn try_from(item: &types::RefundsRouterData) -> Result { + fn try_from( + item_data: &ForteRouterData<&types::RefundsRouterData>, + ) -> Result { + let item = item_data.router_data; let trn_id = item.request.connector_transaction_id.clone(); let connector_auth_id: ForteMeta = utils::to_connector_meta(item.request.connector_metadata.clone())?; let auth_code = connector_auth_id.auth_id; - let authorization_amount = - utils::to_currency_base_unit_asf64(item.request.refund_amount, item.request.currency)?; + let authorization_amount = item_data.amount; Ok(Self { action: "reverse".to_string(), authorization_amount, @@ -535,7 +555,7 @@ pub struct RefundResponse { pub transaction_id: String, pub original_transaction_id: String, pub action: String, - pub authorization_amount: Option, + pub authorization_amount: Option, pub authorization_code: String, pub response: ResponseStatus, } diff --git a/crates/router/src/connector/globepay.rs b/crates/router/src/connector/globepay.rs index 6570351f9e2c..ce9755ddb73f 100644 --- a/crates/router/src/connector/globepay.rs +++ b/crates/router/src/connector/globepay.rs @@ -1,10 +1,9 @@ pub mod transformers; -use std::fmt::Debug; - use common_utils::{ crypto::{self, GenerateDigest}, request::RequestContent, + types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, }; use diesel_models::enums; use error_stack::{report, ResultExt}; @@ -16,6 +15,7 @@ use transformers as globepay; use crate::{ configs::settings, + connector::utils::convert_amount, consts, core::errors::{self, CustomResult}, events::connector_api_logs::ConnectorEvent, @@ -28,9 +28,18 @@ use crate::{ }, utils::BytesExt, }; +#[derive(Clone)] +pub struct Globepay { + amount_converter: &'static (dyn AmountConvertor + Sync), +} -#[derive(Debug, Clone)] -pub struct Globepay; +impl Globepay { + pub fn new() -> &'static Self { + &Self { + amount_converter: &MinorUnitForConnector, + } + } +} impl api::Payment for Globepay {} impl api::PaymentSession for Globepay {} @@ -213,7 +222,14 @@ impl ConnectorIntegration CustomResult { - let connector_req = globepay::GlobepayPaymentsRequest::try_from(req)?; + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = globepay::GlobepayRouterData::from((amount, req)); + let connector_req = globepay::GlobepayPaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -399,7 +415,14 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_req = globepay::GlobepayRefundRequest::try_from(req)?; + let refund_amount = convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = globepay::GlobepayRouterData::from((refund_amount, req)); + let connector_req = globepay::GlobepayRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/globepay/transformers.rs b/crates/router/src/connector/globepay/transformers.rs index df89d3deb9fd..2d5991c7160f 100644 --- a/crates/router/src/connector/globepay/transformers.rs +++ b/crates/router/src/connector/globepay/transformers.rs @@ -1,4 +1,4 @@ -use common_utils::ext_traits::Encode; +use common_utils::{ext_traits::Encode, types::MinorUnit}; use error_stack::ResultExt; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -11,9 +11,24 @@ use crate::{ }; type Error = error_stack::Report; +#[derive(Debug, Serialize)] +pub struct GlobepayRouterData { + pub amount: MinorUnit, + pub router_data: T, +} + +impl From<(MinorUnit, T)> for GlobepayRouterData { + fn from((amount, router_data): (MinorUnit, T)) -> Self { + Self { + amount, + router_data, + } + } +} + #[derive(Debug, Serialize)] pub struct GlobepayPaymentsRequest { - price: i64, + price: MinorUnit, description: String, currency: enums::Currency, channel: GlobepayChannel, @@ -25,9 +40,12 @@ pub enum GlobepayChannel { Wechat, } -impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest { +impl TryFrom<&GlobepayRouterData<&types::PaymentsAuthorizeRouterData>> for GlobepayPaymentsRequest { type Error = Error; - fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { + fn try_from( + item_data: &GlobepayRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + let item = item_data.router_data.clone(); let channel: GlobepayChannel = match &item.request.payment_method_data { domain::PaymentMethodData::Wallet(ref wallet_data) => match wallet_data { domain::WalletData::AliPayQr(_) => GlobepayChannel::Alipay, @@ -83,7 +101,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest { }; let description = item.get_description()?; Ok(Self { - price: item.request.amount, + price: item_data.amount, description, currency: item.request.currency, channel, @@ -308,15 +326,15 @@ fn get_error_response( #[derive(Debug, Serialize)] pub struct GlobepayRefundRequest { - pub fee: i64, + pub fee: MinorUnit, } -impl TryFrom<&types::RefundsRouterData> for GlobepayRefundRequest { +impl TryFrom<&GlobepayRouterData<&types::RefundsRouterData>> for GlobepayRefundRequest { type Error = Error; - fn try_from(item: &types::RefundsRouterData) -> Result { - Ok(Self { - fee: item.request.refund_amount, - }) + fn try_from( + item: &GlobepayRouterData<&types::RefundsRouterData>, + ) -> Result { + Ok(Self { fee: item.amount }) } } diff --git a/crates/router/src/connector/multisafepay.rs b/crates/router/src/connector/multisafepay.rs index fd3edd7c0d93..6f35587f72ac 100644 --- a/crates/router/src/connector/multisafepay.rs +++ b/crates/router/src/connector/multisafepay.rs @@ -1,13 +1,15 @@ pub mod transformers; -use std::fmt::Debug; - use common_enums::AttemptStatus; -use common_utils::request::RequestContent; +use common_utils::{ + request::RequestContent, + types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, +}; use error_stack::{report, ResultExt}; use masking::ExposeInterface; use transformers as multisafepay; +use super::utils::{self as connector_utils}; use crate::{ configs::settings, connector::utils::{is_mandate_supported, PaymentMethodDataType}, @@ -29,8 +31,18 @@ use crate::{ utils::BytesExt, }; -#[derive(Debug, Clone)] -pub struct Multisafepay; +#[derive(Clone)] +pub struct Multisafepay { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Multisafepay { + pub fn new() -> &'static Self { + &Self { + amount_converter: &MinorUnitForConnector, + } + } +} impl ConnectorCommonExt for Multisafepay where @@ -308,12 +320,12 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = multisafepay::MultisafepayRouterData::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 = multisafepay::MultisafepayRouterData::from((amount, req)); let connector_req = multisafepay::MultisafepayPaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -412,13 +424,14 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_req = multisafepay::MultisafepayRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; - let connector_req = multisafepay::MultisafepayRefundRequest::try_from(&connector_req)?; + )?; + let connector_router_data = multisafepay::MultisafepayRouterData::from((amount, req)); + let connector_req = + multisafepay::MultisafepayRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 0fd483a421d2..5466d56c6429 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -1,6 +1,9 @@ use api_models::enums::BankNames; use common_enums::AttemptStatus; -use common_utils::pii::{Email, IpAddress}; +use common_utils::{ + pii::{Email, IpAddress}, + types::{FloatMajorUnit, MinorUnit}, +}; use masking::ExposeInterface; use serde::{Deserialize, Serialize}; use url::Url; @@ -17,20 +20,16 @@ use crate::{ #[derive(Debug, Serialize)] pub struct MultisafepayRouterData { - amount: i64, + amount: MinorUnit, router_data: T, } -impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for MultisafepayRouterData { - type Error = error_stack::Report; - - fn try_from( - (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), - ) -> Result { - Ok(Self { +impl From<(MinorUnit, T)> for MultisafepayRouterData { + fn from((amount, item): (MinorUnit, T)) -> Self { + Self { amount, router_data: item, - }) + } } } @@ -408,7 +407,7 @@ pub struct CheckoutOptions { #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct Item { pub name: String, - pub unit_price: f64, + pub unit_price: FloatMajorUnit, pub description: Option, pub quantity: i64, } @@ -426,7 +425,7 @@ pub struct MultisafepayPaymentsRequest { pub gateway: Option, pub order_id: String, pub currency: String, - pub amount: i64, + pub amount: MinorUnit, pub description: String, pub payment_options: Option, pub customer: Option, @@ -890,7 +889,7 @@ pub struct Data { pub payment_type: Option, pub order_id: String, pub currency: Option, - pub amount: Option, + pub amount: Option, pub description: Option, pub capture: Option, pub payment_url: Option, @@ -1018,7 +1017,7 @@ impl #[derive(Debug, Serialize)] pub struct MultisafepayRefundRequest { pub currency: diesel_models::enums::Currency, - pub amount: i64, + pub amount: MinorUnit, pub description: Option, pub refund_order_id: Option, pub checkout_data: Option, diff --git a/crates/router/src/connector/paybox.rs b/crates/router/src/connector/paybox.rs index 720f0a00c6ac..0a7f74a048fb 100644 --- a/crates/router/src/connector/paybox.rs +++ b/crates/router/src/connector/paybox.rs @@ -1,5 +1,6 @@ pub mod transformers; +use common_enums::enums; use common_utils::types::{AmountConvertor, MinorUnit, MinorUnitForConnector}; use error_stack::{report, ResultExt}; use masking::ExposeInterface; @@ -8,6 +9,7 @@ use transformers as paybox; use super::utils::{self as connector_utils}; use crate::{ configs::settings, + connector::utils, core::errors::{self, CustomResult}, events::connector_api_logs::ConnectorEvent, headers, @@ -49,6 +51,10 @@ impl api::Refund for Paybox {} impl api::RefundExecute for Paybox {} impl api::RefundSync for Paybox {} impl api::PaymentToken for Paybox {} +impl ConnectorIntegration + for Paybox +{ +} impl ConnectorIntegration< @@ -57,7 +63,6 @@ impl types::PaymentsResponseData, > for Paybox { - // Not Implemented (R) } impl ConnectorCommonExt for Paybox @@ -66,15 +71,13 @@ where { fn build_headers( &self, - req: &types::RouterData, + _req: &types::RouterData, _connectors: &settings::Connectors, ) -> CustomResult)>, errors::ConnectorError> { - let mut header = vec![( + let header = vec![( headers::CONTENT_TYPE.to_string(), self.get_content_type().to_string().into(), )]; - let mut api_key = self.get_auth_header(&req.connector_auth_type)?; - header.append(&mut api_key); Ok(header) } } @@ -84,15 +87,8 @@ impl ConnectorCommon for Paybox { "paybox" } - // fn get_currency_unit(&self) -> api::CurrencyUnit { - // // todo!() - // // TODO! Check connector documentation, on which unit they are processing the currency. - // // If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor, - // // if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base - // } - fn common_get_content_type(&self) -> &'static str { - "application/json" + "application/x-www-form-urlencoded" } fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { @@ -107,7 +103,7 @@ impl ConnectorCommon for Paybox { .change_context(errors::ConnectorError::FailedToObtainAuthType)?; Ok(vec![( headers::AUTHORIZATION.to_string(), - auth.api_key.expose().into_masked(), + auth.cle.expose().into_masked(), )]) } @@ -136,13 +132,24 @@ impl ConnectorCommon for Paybox { } impl ConnectorValidation for Paybox { - //TODO: implement functions when support enabled + fn validate_capture_method( + &self, + capture_method: Option, + _pmt: Option, + ) -> CustomResult<(), errors::ConnectorError> { + let capture_method = capture_method.unwrap_or_default(); + match capture_method { + enums::CaptureMethod::Automatic | enums::CaptureMethod::Manual => Ok(()), + enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => Err( + utils::construct_not_implemented_error_report(capture_method, self.id()), + ), + } + } } impl ConnectorIntegration for Paybox { - //TODO: implement sessions flow } impl ConnectorIntegration @@ -158,7 +165,6 @@ impl > for Paybox { } - impl ConnectorIntegration for Paybox { @@ -177,9 +183,9 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(self.base_url(connectors).to_string()) } fn get_request_body( @@ -195,7 +201,7 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - let response: paybox::PayboxPaymentsResponse = res - .response - .parse_struct("Paybox PaymentsAuthorizeResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: paybox::PayboxResponse = paybox::parse_url_encoded_to_struct(res.response)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { @@ -262,13 +262,20 @@ impl ConnectorIntegration &'static str { self.common_get_content_type() } - + fn get_request_body( + &self, + req: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_req = paybox::PayboxPSyncRequest::try_from(req)?; + Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) + } fn get_url( &self, _req: &types::PaymentsSyncRouterData, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(self.base_url(connectors).to_string()) } fn build_request( @@ -278,10 +285,12 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() - .method(services::Method::Get) + .method(services::Method::Post) .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) .attach_default_headers() - .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .set_body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) .build(), )) } @@ -292,10 +301,8 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - let response: paybox::PayboxPaymentsResponse = res - .response - .parse_struct("paybox PaymentsSyncResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: paybox::PayboxSyncResponse = + paybox::parse_url_encoded_to_struct(res.response)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { @@ -332,17 +339,25 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(self.base_url(connectors).to_string()) } fn get_request_body( &self, - _req: &types::PaymentsCaptureRouterData, + req: &types::PaymentsCaptureRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, + req.request.currency, + )?; + + let connector_router_data = paybox::PayboxRouterData::from((amount, req)); + let connector_req = paybox::PayboxCaptureRequest::try_from(&connector_router_data)?; + Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } fn build_request( @@ -355,9 +370,6 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - let response: paybox::PayboxPaymentsResponse = res - .response - .parse_struct("Paybox PaymentsCaptureResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: paybox::PayboxCaptureResponse = + paybox::parse_url_encoded_to_struct(res.response)?; + event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { @@ -393,11 +404,6 @@ impl ConnectorIntegration - for Paybox -{ -} - impl ConnectorIntegration for Paybox { fn get_headers( &self, @@ -414,9 +420,9 @@ impl ConnectorIntegration, - _connectors: &settings::Connectors, + connectors: &settings::Connectors, ) -> CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(self.base_url(connectors).to_string()) } fn get_request_body( @@ -432,7 +438,7 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult, errors::ConnectorError> { - let response: paybox::RefundResponse = - res.response - .parse_struct("paybox RefundResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: paybox::PayboxResponse = paybox::parse_url_encoded_to_struct(res.response)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { @@ -498,9 +498,18 @@ impl ConnectorIntegration CustomResult { - Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + Ok(self.base_url(connectors).to_string()) + } + + fn get_request_body( + &self, + req: &types::RefundSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_req = paybox::PayboxRsyncRequest::try_from(req)?; + Ok(RequestContent::FormUrlEncoded(Box::new(connector_req))) } fn build_request( @@ -510,10 +519,9 @@ impl ConnectorIntegration CustomResult, errors::ConnectorError> { Ok(Some( services::RequestBuilder::new() - .method(services::Method::Get) + .method(services::Method::Post) .url(&types::RefundSyncType::get_url(self, req, connectors)?) .attach_default_headers() - .headers(types::RefundSyncType::get_headers(self, req, connectors)?) .set_body(types::RefundSyncType::get_request_body( self, req, connectors, )?) @@ -527,10 +535,8 @@ impl ConnectorIntegration, res: Response, ) -> CustomResult { - let response: paybox::RefundResponse = res - .response - .parse_struct("paybox RefundSyncResponse") - .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + let response: paybox::PayboxSyncResponse = + paybox::parse_url_encoded_to_struct(res.response)?; event_builder.map(|i| i.set_response_body(&response)); router_env::logger::info!(connector_response=?response); types::RouterData::try_from(types::ResponseRouterData { diff --git a/crates/router/src/connector/paybox/transformers.rs b/crates/router/src/connector/paybox/transformers.rs index d83c4261c19b..fd9129c8a84f 100644 --- a/crates/router/src/connector/paybox/transformers.rs +++ b/crates/router/src/connector/paybox/transformers.rs @@ -1,14 +1,17 @@ -use common_utils::types::MinorUnit; +use bytes::Bytes; +use common_utils::{date_time::DateFormat, errors::CustomResult, types::MinorUnit}; +use error_stack::ResultExt; +use hyperswitch_connectors::utils::CardData; +use hyperswitch_domain_models::router_data::ConnectorAuthType; use masking::Secret; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ - connector::utils::PaymentsAuthorizeRequestData, + connector::utils, core::errors, types::{self, api, domain, storage::enums}, }; -//TODO: Fill the struct with respective fields pub struct PayboxRouterData { pub amount: MinorUnit, // The type of amount that a connector accepts, for example, String, i64, f64, etc. pub router_data: T, @@ -16,7 +19,6 @@ pub struct PayboxRouterData { impl From<(MinorUnit, T)> for PayboxRouterData { fn from((amount, item): (MinorUnit, T)) -> Self { - //Todo : use utils to convert the amount to the type of amount that a connector accepts Self { amount, router_data: item, @@ -24,20 +26,294 @@ impl From<(MinorUnit, T)> for PayboxRouterData { } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, PartialEq)] +const AUTH_REQUEST: &str = "00001"; +const CAPTURE_REQUEST: &str = "00002"; +const AUTH_AND_CAPTURE_REQUEST: &str = "00003"; +const SYNC_REQUEST: &str = "00017"; +const REFUND_REQUEST: &str = "00014"; + +const SUCCESS_CODE: &str = "00000"; + +const VERSION_PAYBOX: &str = "00104"; + +const PAY_ORIGIN_INTERNET: &str = "024"; + +type Error = error_stack::Report; + +#[derive(Debug, Serialize, Eq, PartialEq)] pub struct PayboxPaymentsRequest { - amount: MinorUnit, - card: PayboxCard, + #[serde(rename = "DATEQ")] + pub date: String, + + #[serde(rename = "TYPE")] + pub transaction_type: String, + + #[serde(rename = "NUMQUESTION")] + pub paybox_request_number: String, + + #[serde(rename = "MONTANT")] + pub amount: MinorUnit, + + #[serde(rename = "REFERENCE")] + pub description_reference: String, + + #[serde(rename = "VERSION")] + pub version: String, + + #[serde(rename = "DEVISE")] + pub currency: String, + + #[serde(rename = "PORTEUR")] + pub card_number: cards::CardNumber, + + #[serde(rename = "DATEVAL")] + pub expiration_date: Secret, + + #[serde(rename = "CVV")] + pub cvv: Secret, + + #[serde(rename = "ACTIVITE")] + pub activity: String, + + #[serde(rename = "SITE")] + pub site: Secret, + + #[serde(rename = "RANG")] + pub rank: Secret, + + #[serde(rename = "CLE")] + pub key: Secret, +} + +#[derive(Debug, Serialize, Eq, PartialEq)] +pub struct PayboxCaptureRequest { + #[serde(rename = "DATEQ")] + pub date: String, + + #[serde(rename = "TYPE")] + pub transaction_type: String, + + #[serde(rename = "NUMQUESTION")] + pub paybox_request_number: String, + + #[serde(rename = "MONTANT")] + pub amount: MinorUnit, + + #[serde(rename = "REFERENCE")] + pub reference: String, + + #[serde(rename = "VERSION")] + pub version: String, + + #[serde(rename = "DEVISE")] + pub currency: String, + + #[serde(rename = "SITE")] + pub site: Secret, + + #[serde(rename = "RANG")] + pub rank: Secret, + + #[serde(rename = "CLE")] + pub key: Secret, + + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, +} + +impl TryFrom<&PayboxRouterData<&types::PaymentsCaptureRouterData>> for PayboxCaptureRequest { + type Error = error_stack::Report; + fn try_from( + item: &PayboxRouterData<&types::PaymentsCaptureRouterData>, + ) -> Result { + let auth_data: PayboxAuthType = + PayboxAuthType::try_from(&item.router_data.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let currency = diesel_models::enums::Currency::iso_4217(&item.router_data.request.currency) + .to_string(); + let paybox_meta_data: PayboxMeta = + utils::to_connector_meta(item.router_data.request.connector_meta.clone())?; + let format_time = common_utils::date_time::format_date( + common_utils::date_time::now(), + DateFormat::YYYYMMDDHHmmss, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + date: format_time.clone(), + transaction_type: CAPTURE_REQUEST.to_string(), + paybox_request_number: get_paybox_request_number()?, + version: VERSION_PAYBOX.to_string(), + currency, + site: auth_data.site, + rank: auth_data.rang, + key: auth_data.cle, + transaction_number: paybox_meta_data.connector_request_id, + paybox_order_id: item.router_data.request.connector_transaction_id.clone(), + amount: item.amount, + reference: item.router_data.connector_request_reference_id.to_string(), + }) + } } -#[derive(Default, Debug, Serialize, Eq, PartialEq)] -pub struct PayboxCard { - number: cards::CardNumber, - expiry_month: Secret, - expiry_year: Secret, - cvc: Secret, - complete: bool, +#[derive(Debug, Serialize, Eq, PartialEq)] +pub struct PayboxRsyncRequest { + #[serde(rename = "DATEQ")] + pub date: String, + + #[serde(rename = "TYPE")] + pub transaction_type: String, + + #[serde(rename = "NUMQUESTION")] + pub paybox_request_number: String, + + #[serde(rename = "VERSION")] + pub version: String, + + #[serde(rename = "SITE")] + pub site: Secret, + + #[serde(rename = "RANG")] + pub rank: Secret, + + #[serde(rename = "CLE")] + pub key: Secret, + + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, +} + +impl TryFrom<&types::RefundSyncRouterData> for PayboxRsyncRequest { + type Error = error_stack::Report; + fn try_from(item: &types::RefundSyncRouterData) -> Result { + let auth_data: PayboxAuthType = PayboxAuthType::try_from(&item.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let format_time = common_utils::date_time::format_date( + common_utils::date_time::now(), + DateFormat::YYYYMMDDHHmmss, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + date: format_time.clone(), + transaction_type: SYNC_REQUEST.to_string(), + paybox_request_number: get_paybox_request_number()?, + version: VERSION_PAYBOX.to_string(), + site: auth_data.site, + rank: auth_data.rang, + key: auth_data.cle, + transaction_number: item + .request + .connector_refund_id + .clone() + .ok_or(errors::ConnectorError::RequestEncodingFailed)?, + paybox_order_id: item.request.connector_transaction_id.clone(), + }) + } +} +#[derive(Debug, Serialize, Eq, PartialEq)] +pub struct PayboxPSyncRequest { + #[serde(rename = "DATEQ")] + pub date: String, + + #[serde(rename = "TYPE")] + pub transaction_type: String, + + #[serde(rename = "NUMQUESTION")] + pub paybox_request_number: String, + + #[serde(rename = "VERSION")] + pub version: String, + + #[serde(rename = "SITE")] + pub site: Secret, + + #[serde(rename = "RANG")] + pub rank: Secret, + + #[serde(rename = "CLE")] + pub key: Secret, + + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, +} + +impl TryFrom<&types::PaymentsSyncRouterData> for PayboxPSyncRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsSyncRouterData) -> Result { + let auth_data: PayboxAuthType = PayboxAuthType::try_from(&item.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let format_time = common_utils::date_time::format_date( + common_utils::date_time::now(), + DateFormat::YYYYMMDDHHmmss, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let paybox_meta_data: PayboxMeta = + utils::to_connector_meta(item.request.connector_meta.clone())?; + Ok(Self { + date: format_time.clone(), + transaction_type: SYNC_REQUEST.to_string(), + paybox_request_number: get_paybox_request_number()?, + version: VERSION_PAYBOX.to_string(), + site: auth_data.site, + rank: auth_data.rang, + key: auth_data.cle, + transaction_number: paybox_meta_data.connector_request_id, + paybox_order_id: item + .request + .connector_transaction_id + .get_connector_transaction_id() + .change_context(errors::ConnectorError::MissingConnectorTransactionID)?, + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PayboxMeta { + pub connector_request_id: String, +} + +#[derive(Debug, Serialize, Eq, PartialEq)] +pub struct PayboxRefundRequest { + #[serde(rename = "DATEQ")] + pub date: String, + + #[serde(rename = "TYPE")] + pub transaction_type: String, + + #[serde(rename = "NUMQUESTION")] + pub paybox_request_number: String, + + #[serde(rename = "MONTANT")] + pub amount: MinorUnit, + + #[serde(rename = "VERSION")] + pub version: String, + + #[serde(rename = "DEVISE")] + pub currency: String, + + #[serde(rename = "SITE")] + pub site: Secret, + + #[serde(rename = "RANG")] + pub rank: Secret, + + #[serde(rename = "CLE")] + pub key: Secret, + + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, } impl TryFrom<&PayboxRouterData<&types::PaymentsAuthorizeRouterData>> for PayboxPaymentsRequest { @@ -47,16 +323,37 @@ impl TryFrom<&PayboxRouterData<&types::PaymentsAuthorizeRouterData>> for PayboxP ) -> Result { match item.router_data.request.payment_method_data.clone() { domain::PaymentMethodData::Card(req_card) => { - let card = PayboxCard { - number: req_card.card_number, - expiry_month: req_card.card_exp_month, - expiry_year: req_card.card_exp_year, - cvc: req_card.card_cvc, - complete: item.router_data.request.is_auto_capture()?, - }; + let auth_data: PayboxAuthType = + PayboxAuthType::try_from(&item.router_data.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let transaction_type = + get_transaction_type(item.router_data.request.capture_method)?; + let currency = + diesel_models::enums::Currency::iso_4217(&item.router_data.request.currency) + .to_string(); + let expiration_date = + req_card.get_card_expiry_month_year_2_digit_with_delimiter("".to_owned())?; + let format_time = common_utils::date_time::format_date( + common_utils::date_time::now(), + DateFormat::YYYYMMDDHHmmss, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + Ok(Self { + date: format_time.clone(), + transaction_type, + paybox_request_number: get_paybox_request_number()?, amount: item.amount, - card, + description_reference: item.router_data.connector_request_reference_id.clone(), + version: VERSION_PAYBOX.to_string(), + currency, + card_number: req_card.card_number, + expiration_date, + cvv: req_card.card_cvc, + activity: PAY_ORIGIN_INTERNET.to_string(), + site: auth_data.site, + rank: auth_data.rang, + key: auth_data.cle, }) } _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), @@ -64,31 +361,59 @@ impl TryFrom<&PayboxRouterData<&types::PaymentsAuthorizeRouterData>> for PayboxP } } -//TODO: Fill the struct with respective fields -// Auth Struct +fn get_transaction_type(capture_method: Option) -> Result { + match capture_method { + Some(enums::CaptureMethod::Automatic) => Ok(AUTH_AND_CAPTURE_REQUEST.to_string()), + Some(enums::CaptureMethod::Manual) => Ok(AUTH_REQUEST.to_string()), + _ => Err(errors::ConnectorError::CaptureMethodNotSupported)?, + } +} +fn get_paybox_request_number() -> Result { + let time_stamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .ok() + .ok_or(errors::ConnectorError::RequestEncodingFailed)? + .as_millis() + .to_string(); + // unix time (in milliseconds) has 13 digits.if we consider 8 digits(the number digits to make day deterministic) there is no collision in the paybox_request_number as it will reset the paybox_request_number for each day and paybox accepting maximum length is 10 so we gonna take 9 (13-9) + let request_number = time_stamp + .get(4..) + .ok_or(errors::ConnectorError::ParsingFailed)?; + Ok(request_number.to_string()) +} + +#[derive(Debug, Serialize, Eq, PartialEq)] pub struct PayboxAuthType { - pub(super) api_key: Secret, + pub(super) site: Secret, + pub(super) rang: Secret, + pub(super) cle: Secret, } -impl TryFrom<&types::ConnectorAuthType> for PayboxAuthType { +impl TryFrom<&ConnectorAuthType> for PayboxAuthType { type Error = error_stack::Report; - fn try_from(auth_type: &types::ConnectorAuthType) -> Result { - match auth_type { - types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { - api_key: api_key.to_owned(), - }), - _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + fn try_from(auth_type: &ConnectorAuthType) -> Result { + if let ConnectorAuthType::SignatureKey { + api_key, + key1, + api_secret, + } = auth_type + { + Ok(Self { + site: api_key.to_owned(), + rang: key1.to_owned(), + cle: api_secret.to_owned(), + }) + } else { + Err(errors::ConnectorError::FailedToObtainAuthType)? } } } -// PaymentsResponse -//TODO: Append the remaining status flags -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum PayboxPaymentStatus { Succeeded, Failed, - #[default] Processing, } @@ -102,96 +427,279 @@ impl From for enums::AttemptStatus { } } -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct PayboxPaymentsResponse { - status: PayboxPaymentStatus, - id: String, +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct PayboxResponse { + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, + + #[serde(rename = "CODEREPONSE")] + pub response_code: String, + + #[serde(rename = "COMMENTAIRE")] + pub response_message: String, +} + +pub fn parse_url_encoded_to_struct( + query_bytes: Bytes, +) -> CustomResult { + let (cow, _, _) = encoding_rs::ISO_8859_10.decode(&query_bytes); + serde_qs::from_str::(cow.as_ref()).change_context(errors::ConnectorError::ParsingFailed) +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum PayboxStatus { + #[serde(rename = "Remboursé")] + Refunded, + + #[serde(rename = "Annulé")] + Cancelled, + + #[serde(rename = "Autorisé")] + Authorised, + + #[serde(rename = "Capturé")] + Captured, + + #[serde(rename = "Refusé")] + Rejected, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct PayboxSyncResponse { + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, + + #[serde(rename = "CODEREPONSE")] + pub response_code: String, + + #[serde(rename = "COMMENTAIRE")] + pub response_message: String, + + #[serde(rename = "STATUS")] + pub status: PayboxStatus, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct PayboxCaptureResponse { + #[serde(rename = "NUMTRANS")] + pub transaction_number: String, + + #[serde(rename = "NUMAPPEL")] + pub paybox_order_id: String, + + #[serde(rename = "CODEREPONSE")] + pub response_code: String, + + #[serde(rename = "COMMENTAIRE")] + pub response_message: String, } impl - TryFrom> + TryFrom> for types::RouterData { type Error = error_stack::Report; fn try_from( - item: types::ResponseRouterData, + item: types::ResponseRouterData, ) -> Result { - Ok(Self { - status: enums::AttemptStatus::from(item.response.status), - response: Ok(types::PaymentsResponseData::TransactionResponse { - resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), - redirection_data: None, - mandate_reference: None, - connector_metadata: None, - network_txn_id: None, - connector_response_reference_id: None, - incremental_authorization_allowed: None, - charge_id: None, + let response = item.response.clone(); + let status = get_status_of_request(response.response_code.clone()); + match status { + true => Ok(Self { + status: enums::AttemptStatus::Pending, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + response.paybox_order_id, + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: Some(serde_json::json!(PayboxMeta { + connector_request_id: response.transaction_number.clone() + })), + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + amount_captured: None, + ..item.data }), - ..item.data - }) + false => Ok(Self { + response: Err(types::ErrorResponse { + code: response.response_code.clone(), + message: response.response_message.clone(), + reason: Some(response.response_message), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: Some(item.response.transaction_number), + }), + ..item.data + }), + } } } -//TODO: Fill the struct with respective fields -// REFUND : -// Type definition for RefundRequest -#[derive(Default, Debug, Serialize)] -pub struct PayboxRefundRequest { - pub amount: MinorUnit, -} - -impl TryFrom<&PayboxRouterData<&types::RefundsRouterData>> for PayboxRefundRequest { +impl TryFrom> + for types::RouterData +{ type Error = error_stack::Report; fn try_from( - item: &PayboxRouterData<&types::RefundsRouterData>, + item: types::ResponseRouterData, ) -> Result { - Ok(Self { - amount: item.amount.to_owned(), - }) + let response = item.response.clone(); + let status = get_status_of_request(response.response_code.clone()); + match status { + true => Ok(Self { + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + response.paybox_order_id, + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: Some(serde_json::json!(PayboxMeta { + connector_request_id: response.transaction_number.clone() + })), + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }), + false => Ok(Self { + response: Err(types::ErrorResponse { + code: response.response_code.clone(), + message: response.response_message.clone(), + reason: Some(response.response_message), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: Some(item.response.transaction_number), + }), + ..item.data + }), + } } } -// Type definition for Refund Response +impl TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData, + ) -> Result { + let response = item.response.clone(); + let status = get_status_of_request(response.response_code.clone()); + let connector_payment_status = item.response.status; + match status { + true => Ok(Self { + status: enums::AttemptStatus::from(connector_payment_status), -#[allow(dead_code)] -#[derive(Debug, Serialize, Default, Deserialize, Clone)] -pub enum RefundStatus { - Succeeded, - Failed, - #[default] - Processing, + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + response.paybox_order_id, + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: Some(serde_json::json!(PayboxMeta { + connector_request_id: response.transaction_number.clone() + })), + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }), + false => Ok(Self { + response: Err(types::ErrorResponse { + code: response.response_code.clone(), + message: response.response_message.clone(), + reason: Some(response.response_message), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: Some(item.response.transaction_number), + }), + ..item.data + }), + } + } } -impl From for enums::RefundStatus { - fn from(item: RefundStatus) -> Self { +impl From for common_enums::RefundStatus { + fn from(item: PayboxStatus) -> Self { + match item { + PayboxStatus::Refunded => Self::Success, + PayboxStatus::Cancelled + | PayboxStatus::Authorised + | PayboxStatus::Captured + | PayboxStatus::Rejected => Self::Failure, + } + } +} +impl From for enums::AttemptStatus { + fn from(item: PayboxStatus) -> Self { match item { - RefundStatus::Succeeded => Self::Success, - RefundStatus::Failed => Self::Failure, - RefundStatus::Processing => Self::Pending, - //TODO: Review mapping + PayboxStatus::Cancelled => Self::Voided, + PayboxStatus::Authorised => Self::Authorized, + PayboxStatus::Captured | PayboxStatus::Refunded => Self::Charged, + PayboxStatus::Rejected => Self::Failure, } } } +fn get_status_of_request(item: String) -> bool { + item == *SUCCESS_CODE +} -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Clone, Serialize, Deserialize)] -pub struct RefundResponse { - id: String, - status: RefundStatus, +impl TryFrom<&PayboxRouterData<&types::RefundsRouterData>> for PayboxRefundRequest { + type Error = error_stack::Report; + fn try_from( + item: &PayboxRouterData<&types::RefundsRouterData>, + ) -> Result { + let auth_data: PayboxAuthType = + PayboxAuthType::try_from(&item.router_data.connector_auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let currency = diesel_models::enums::Currency::iso_4217(&item.router_data.request.currency) + .to_string(); + let format_time = common_utils::date_time::format_date( + common_utils::date_time::now(), + DateFormat::YYYYMMDDHHmmss, + ) + .change_context(errors::ConnectorError::RequestEncodingFailed)?; + let paybox_meta_data: PayboxMeta = + utils::to_connector_meta(item.router_data.request.connector_metadata.clone())?; + Ok(Self { + date: format_time.clone(), + transaction_type: REFUND_REQUEST.to_string(), + paybox_request_number: get_paybox_request_number()?, + version: VERSION_PAYBOX.to_string(), + currency, + site: auth_data.site, + rank: auth_data.rang, + key: auth_data.cle, + transaction_number: paybox_meta_data.connector_request_id, + paybox_order_id: item.router_data.request.connector_transaction_id.clone(), + amount: item.amount, + }) + } } -impl TryFrom> - for types::RefundsRouterData +impl TryFrom> + for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), + connector_refund_id: item.response.transaction_number, refund_status: enums::RefundStatus::from(item.response.status), }), ..item.data @@ -199,25 +707,23 @@ impl TryFrom> } } -impl TryFrom> - for types::RefundsRouterData +impl TryFrom> + for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( - item: types::RefundsResponseRouterData, + item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { - connector_refund_id: item.response.id.to_string(), - refund_status: enums::RefundStatus::from(item.response.status), + connector_refund_id: item.response.transaction_number, + refund_status: common_enums::RefundStatus::Pending, }), ..item.data }) } } - -//TODO: Fill the struct with respective fields -#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct PayboxErrorResponse { pub status_code: u16, pub code: String, diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 7553e016b785..4f7b3f872ae8 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1436,7 +1436,10 @@ impl<'a> ConnectorAuthTypeAndMetadataValidation<'a> { opennode::transformers::OpennodeAuthType::try_from(self.auth_type)?; Ok(()) } - // api_enums::Connector::Paybox => todo!(), added for future usage + api_enums::Connector::Paybox => { + paybox::transformers::PayboxAuthType::try_from(self.auth_type)?; + Ok(()) + } api_enums::Connector::Payme => { payme::transformers::PaymeAuthType::try_from(self.auth_type)?; Ok(()) @@ -1767,7 +1770,7 @@ struct PMAuthConfigValidation<'a> { pm_auth_config: &'a Option, db: &'a dyn StorageInterface, merchant_id: &'a id_type::MerchantId, - profile_id: &'a Option, + profile_id: &'a String, key_store: &'a domain::MerchantKeyStore, key_manager_state: &'a KeyManagerState, } @@ -2335,7 +2338,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { } None => None, }, - profile_id: Some(business_profile.profile_id.clone()), + profile_id: business_profile.profile_id.clone(), applepay_verified_domains: None, pm_auth_config: self.pm_auth_config.clone(), status: connector_status, @@ -2355,11 +2358,10 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { } else { None }, + version: hyperswitch_domain_models::consts::API_VERSION, }) } - /// If profile_id is not passed, use default profile if available - /// or return a `MissingRequiredField` error async fn validate_and_get_profile_id( self, merchant_account: &domain::MerchantAccount, @@ -2368,25 +2370,19 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { key_store: &domain::MerchantKeyStore, should_validate: bool, ) -> RouterResult { - match self.profile_id { - Some(profile_id) => { - // Check whether this business profile belongs to the merchant - if should_validate { - let _ = core_utils::validate_and_get_business_profile( - db, - key_manager_state, - key_store, - Some(&profile_id), - merchant_account.get_id(), - ) - .await?; - } - Ok(profile_id.clone()) - } - None => Err(report!(errors::ApiErrorResponse::MissingRequiredField { - field_name: "profile_id" - })), + let profile_id = self.profile_id; + // Check whether this business profile belongs to the merchant + if should_validate { + let _ = core_utils::validate_and_get_business_profile( + db, + key_manager_state, + key_store, + Some(&profile_id), + merchant_account.get_id(), + ) + .await?; } + Ok(profile_id.clone()) } } @@ -2508,7 +2504,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { } None => None, }, - profile_id: Some(business_profile.profile_id.clone()), + profile_id: business_profile.profile_id.clone(), applepay_verified_domains: None, pm_auth_config: self.pm_auth_config.clone(), status: connector_status, @@ -2532,6 +2528,7 @@ impl MerchantConnectorAccountCreateBridge for api::MerchantConnectorCreate { } else { None }, + version: hyperswitch_domain_models::consts::API_VERSION, }) } @@ -2648,7 +2645,7 @@ pub async fn create_connector( pm_auth_config: &req.pm_auth_config, db: store, merchant_id, - profile_id: &Some(profile_id.clone()), + profile_id: &profile_id.clone(), key_store: &key_store, key_manager_state, }; @@ -2764,7 +2761,7 @@ async fn validate_pm_auth( merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, - profile_id: &Option, + profile_id: &String, ) -> RouterResponse<()> { let config = serde_json::from_value::(val.expose()) @@ -2983,11 +2980,7 @@ pub async fn update_connector( .await?; // Profile id should always be present - let profile_id = mca - .profile_id - .clone() - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Missing `profile_id` in merchant connector account")?; + let profile_id = mca.profile_id.clone(); let request_connector_label = req.connector_label; @@ -3720,6 +3713,62 @@ impl BusinessProfileWrapper { ), } } + pub fn get_default_fallback_list_of_connector_under_profile( + &self, + ) -> RouterResult> { + use common_utils::ext_traits::OptionExt; + use masking::ExposeOptionInterface; + + self.profile + .default_fallback_routing + .clone() + .expose_option() + .parse_value::>( + "Vec", + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Merchant default config has invalid structure") + } + pub fn get_default_routing_configs_from_profile( + &self, + ) -> RouterResult { + let profile_id = self.profile.profile_id.clone(); + let connectors = self.get_default_fallback_list_of_connector_under_profile()?; + + Ok(routing_types::ProfileDefaultRoutingConfig { + profile_id, + connectors, + }) + } + + pub async fn update_default_routing_for_profile( + self, + db: &dyn StorageInterface, + updated_config: &Vec, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + ) -> RouterResult<()> { + let default_fallback_routing = Secret::from( + updated_config + .encode_to_value() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to convert routing ref to value")?, + ); + let business_profile_update = domain::BusinessProfileUpdate::DefaultRoutingFallbackUpdate { + default_fallback_routing: Some(default_fallback_routing), + }; + + db.update_business_profile_by_profile_id( + key_manager_state, + merchant_key_store, + self.profile, + business_profile_update, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to update routing algorithm ref in business profile")?; + Ok(()) + } } pub async fn extended_card_info_toggle( diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 97cc3d6f2767..594842039c52 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -1,9 +1,9 @@ use api_models::customers::CustomerRequestWithEmail; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -use common_utils::{crypto::Encryptable, ext_traits::OptionExt, types::Description}; +use common_utils::{crypto::Encryptable, types::Description}; use common_utils::{ errors::ReportSwitchExt, - ext_traits::AsyncExt, + ext_traits::{AsyncExt, OptionExt}, id_type, type_name, types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, }; @@ -23,15 +23,13 @@ use crate::{ types::{ api::customers, domain::{self, types}, + storage::{self}, transformers::ForeignFrom, }, }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::{ - core::payment_methods::{cards, network_tokenization}, - routes::metrics, - types::storage::{self, enums}, - utils::CustomerAddress, + core::payment_methods::{cards, network_tokenization}, routes::metrics, types::storage::enums, utils::CustomerAddress, }; pub const REDACTED: &str = "Redacted"; @@ -51,7 +49,7 @@ pub async fn create_customer( let merchant_id = merchant_account.get_id(); let merchant_reference_id_customer = MerchantReferenceIdForCustomer { - customer_id: merchant_reference_id.as_ref(), + merchant_reference_id: merchant_reference_id.as_ref(), merchant_id, merchant_account: &merchant_account, key_store: &key_store, @@ -65,7 +63,7 @@ pub async fn create_customer( // it errors out, now the address that was inserted is not deleted merchant_reference_id_customer - .verify_if_customer_not_present_by_optional_merchant_reference_id(db) + .verify_if_merchant_reference_not_present_by_optional_merchant_reference_id(db) .await?; let domain_customer = customer_data @@ -332,28 +330,29 @@ impl<'a> AddressStructForDbEntry<'a> { } struct MerchantReferenceIdForCustomer<'a> { - customer_id: Option<&'a id_type::CustomerId>, + merchant_reference_id: Option<&'a id_type::CustomerId>, merchant_id: &'a id_type::MerchantId, merchant_account: &'a domain::MerchantAccount, key_store: &'a domain::MerchantKeyStore, key_manager_state: &'a KeyManagerState, } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] impl<'a> MerchantReferenceIdForCustomer<'a> { - async fn verify_if_customer_not_present_by_optional_merchant_reference_id( + async fn verify_if_merchant_reference_not_present_by_optional_merchant_reference_id( &self, db: &dyn StorageInterface, ) -> Result, error_stack::Report> { - self.customer_id + self.merchant_reference_id .async_map(|cust| async { - self.verify_if_customer_not_present_by_merchant_reference(cust, db) + self.verify_if_merchant_reference_not_present_by_merchant_reference_id(cust, db) .await }) .await .transpose() } - async fn verify_if_customer_not_present_by_merchant_reference( + async fn verify_if_merchant_reference_not_present_by_merchant_reference_id( &self, cus: &'a id_type::CustomerId, db: &dyn StorageInterface, @@ -382,6 +381,53 @@ impl<'a> MerchantReferenceIdForCustomer<'a> { } } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +impl<'a> MerchantReferenceIdForCustomer<'a> { + async fn verify_if_merchant_reference_not_present_by_optional_merchant_reference_id( + &self, + db: &dyn StorageInterface, + ) -> Result, error_stack::Report> { + self.merchant_reference_id + .async_map(|merchant_ref| async { + self.verify_if_merchant_reference_not_present_by_merchant_reference( + merchant_ref, + db, + ) + .await + }) + .await + .transpose() + } + + async fn verify_if_merchant_reference_not_present_by_merchant_reference( + &self, + merchant_ref: &'a id_type::CustomerId, + db: &dyn StorageInterface, + ) -> Result<(), error_stack::Report> { + match db + .find_customer_by_merchant_reference_id_merchant_id( + self.key_manager_state, + merchant_ref, + self.merchant_id, + self.key_store, + self.merchant_account.storage_scheme, + ) + .await + { + Err(err) => { + if !err.current_context().is_db_not_found() { + Err(err).switch() + } else { + Ok(()) + } + } + Ok(_) => Err(report!( + errors::CustomersErrorResponse::CustomerAlreadyExists + )), + } + } +} + #[cfg(all(any(feature = "v1", feature = "v2",), not(feature = "customer_v2")))] #[instrument(skip(state))] pub async fn retrieve_customer( @@ -627,143 +673,390 @@ pub async fn delete_customer( Ok(services::ApplicationResponse::Json(response)) } -#[cfg(all( - any(feature = "v1", feature = "v2", feature = "oltp"), - not(feature = "customer_v2") -))] #[instrument(skip(state))] pub async fn update_customer( state: SessionState, merchant_account: domain::MerchantAccount, - update_customer: customers::CustomerRequest, + update_customer: customers::CustomerUpdateRequest, key_store: domain::MerchantKeyStore, + id: customers::UpdateCustomerId, ) -> errors::CustomerResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); //Add this in update call if customer can be updated anywhere else - let customer_id = update_customer - .customer_id - .as_ref() - .get_required_value("customer_id") - .change_context(errors::CustomersErrorResponse::InternalServerError) - .attach("Missing required field `customer_id`")?; - let key_manager_state = &(&state).into(); - let customer = db - .find_customer_by_customer_id_merchant_id( - key_manager_state, - customer_id, - merchant_account.get_id(), + let merchant_reference_id = update_customer.get_merchant_reference_id(); + + let verify_id_for_update_customer = VerifyIdForUpdateCustomer { + merchant_reference_id: merchant_reference_id.as_ref(), + id: &id, + merchant_account: &merchant_account, + key_store: &key_store, + key_manager_state, + }; + + let customer = verify_id_for_update_customer + .verify_id_and_get_customer_object(db) + .await?; + + let updated_customer = update_customer + .create_domain_model_from_request( + db, &key_store, - merchant_account.storage_scheme, + &merchant_account, + key_manager_state, + &state, + &customer, ) - .await - .switch()?; + .await?; - let key = key_store.key.get_inner().peek(); + update_customer.generate_response(&updated_customer) +} - let address = if let Some(addr) = &update_customer.address { - match customer.address_id.clone() { - Some(address_id) => { - let customer_address: api_models::payments::AddressDetails = addr.clone(); - let update_address = update_customer - .get_address_update( - &state, - customer_address, - key, - merchant_account.storage_scheme, - merchant_account.get_id().clone(), - ) - .await - .switch() - .attach_printable("Failed while encrypting Address while Update")?; - Some( - db.update_address(key_manager_state, address_id, update_address, &key_store) +#[async_trait::async_trait] +trait CustomerUpdateBridge { + async fn create_domain_model_from_request<'a>( + &'a self, + db: &'a dyn StorageInterface, + key_store: &'a domain::MerchantKeyStore, + merchant_account: &'a domain::MerchantAccount, + key_manager_state: &'a KeyManagerState, + state: &'a SessionState, + domain_customer: &'a domain::Customer, + ) -> errors::CustomResult; + + fn generate_response<'a>( + &'a self, + customer: &'a domain::Customer, + ) -> errors::CustomerResponse; +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +struct AddressStructForDbUpdate<'a> { + update_customer: &'a customers::CustomerUpdateRequest, + merchant_account: &'a domain::MerchantAccount, + key_store: &'a domain::MerchantKeyStore, + key_manager_state: &'a KeyManagerState, + state: &'a SessionState, + domain_customer: &'a domain::Customer, +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +impl<'a> AddressStructForDbUpdate<'a> { + async fn update_address_if_sent( + &self, + db: &dyn StorageInterface, + ) -> errors::CustomResult, errors::CustomersErrorResponse> { + let address = if let Some(addr) = &self.update_customer.address { + match self.domain_customer.address_id.clone() { + Some(address_id) => { + let customer_address: api_models::payments::AddressDetails = addr.clone(); + let update_address = self + .update_customer + .get_address_update( + self.state, + customer_address, + self.key_store.key.get_inner().peek(), + self.merchant_account.storage_scheme, + self.merchant_account.get_id().clone(), + ) + .await + .switch() + .attach_printable("Failed while encrypting Address while Update")?; + Some( + db.update_address( + self.key_manager_state, + address_id, + update_address, + self.key_store, + ) .await .switch() .attach_printable(format!( "Failed while updating address: merchant_id: {:?}, customer_id: {:?}", - merchant_account.get_id(), - customer_id + self.merchant_account.get_id(), + self.domain_customer.customer_id ))?, - ) - } - None => { - let customer_address: api_models::payments::AddressDetails = addr.clone(); - - let address = update_customer - .get_domain_address( - &state, - customer_address, - merchant_account.get_id(), - &customer.customer_id, - key, - merchant_account.storage_scheme, ) - .await - .switch() - .attach_printable("Failed while encrypting address")?; - Some( - db.insert_address_for_customers(key_manager_state, address, &key_store) + } + None => { + let customer_address: api_models::payments::AddressDetails = addr.clone(); + + let address = self + .update_customer + .get_domain_address( + self.state, + customer_address, + self.merchant_account.get_id(), + &self.domain_customer.customer_id, + self.key_store.key.get_inner().peek(), + self.merchant_account.storage_scheme, + ) + .await + .switch() + .attach_printable("Failed while encrypting address")?; + Some( + db.insert_address_for_customers( + self.key_manager_state, + address, + self.key_store, + ) .await .switch() .attach_printable("Failed while inserting new address")?, - ) + ) + } } - } - } else { - match &customer.address_id { - Some(address_id) => Some( - db.find_address_by_address_id(key_manager_state, address_id, &key_store) + } else { + match &self.domain_customer.address_id { + Some(address_id) => Some( + db.find_address_by_address_id( + self.key_manager_state, + address_id, + self.key_store, + ) .await .switch()?, - ), - None => None, - } - }; - let encrypted_data = types::crypto_operation( - &(&state).into(), - type_name!(domain::Customer), - types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable( - CustomerRequestWithEmail { - name: update_customer.name.clone(), - email: update_customer.email.clone(), - phone: update_customer.phone.clone(), - }, - )), - Identifier::Merchant(key_store.merchant_id.clone()), - key, - ) - .await - .and_then(|val| val.try_into_batchoperation()) - .switch()?; - let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) - .change_context(errors::CustomersErrorResponse::InternalServerError)?; + ), + None => None, + } + }; + Ok(address) + } +} - let response = db - .update_customer_by_customer_id_merchant_id( +struct VerifyIdForUpdateCustomer<'a> { + merchant_reference_id: Option<&'a id_type::CustomerId>, + id: &'a customers::UpdateCustomerId, + merchant_account: &'a domain::MerchantAccount, + key_store: &'a domain::MerchantKeyStore, + key_manager_state: &'a KeyManagerState, +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +impl<'a> VerifyIdForUpdateCustomer<'a> { + async fn verify_id_and_get_customer_object( + &self, + db: &dyn StorageInterface, + ) -> Result> { + let customer_id = self + .merchant_reference_id + .get_required_value("customer_id") + .change_context(errors::CustomersErrorResponse::InternalServerError) + .attach("Missing required field `customer_id`")?; + + let _id = self.id; + + let customer = db + .find_customer_by_customer_id_merchant_id( + self.key_manager_state, + customer_id, + self.merchant_account.get_id(), + self.key_store, + self.merchant_account.storage_scheme, + ) + .await + .switch()?; + + Ok(customer) + } +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +impl<'a> VerifyIdForUpdateCustomer<'a> { + async fn verify_id_and_get_customer_object( + &self, + db: &dyn StorageInterface, + ) -> Result> { + let id = self.id.get_global_id(); + + let _merchant_reference_id = self.merchant_reference_id; + let customer = db + .find_customer_by_global_id( + self.key_manager_state, + &id, + &self.merchant_account.get_id(), + self.key_store, + self.merchant_account.storage_scheme, + ) + .await + .switch()?; + + Ok(customer) + } +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +#[async_trait::async_trait] +impl CustomerUpdateBridge for customers::CustomerUpdateRequest { + async fn create_domain_model_from_request<'a>( + &'a self, + db: &'a dyn StorageInterface, + key_store: &'a domain::MerchantKeyStore, + merchant_account: &'a domain::MerchantAccount, + key_manager_state: &'a KeyManagerState, + state: &'a SessionState, + domain_customer: &'a domain::Customer, + ) -> errors::CustomResult { + let update_address_for_update_customer = AddressStructForDbUpdate { + update_customer: self, + merchant_account, + key_store, key_manager_state, - customer_id.to_owned(), - merchant_account.get_id().to_owned(), - customer, - storage::CustomerUpdate::Update { - name: encryptable_customer.name, - email: encryptable_customer.email, - phone: Box::new(encryptable_customer.phone), - phone_country_code: update_customer.phone_country_code, - metadata: update_customer.metadata, - description: update_customer.description, - connector_customer: None, - address_id: address.clone().map(|addr| addr.address_id), - }, - &key_store, - merchant_account.storage_scheme, + state, + domain_customer, + }; + + let address = update_address_for_update_customer + .update_address_if_sent(db) + .await?; + + let key = key_store.key.get_inner().peek(); + + let encrypted_data = types::crypto_operation( + key_manager_state, + type_name!(domain::Customer), + types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable( + CustomerRequestWithEmail { + name: self.name.clone(), + email: self.email.clone(), + phone: self.phone.clone(), + }, + )), + Identifier::Merchant(key_store.merchant_id.clone()), + key, ) .await + .and_then(|val| val.try_into_batchoperation()) .switch()?; - Ok(services::ApplicationResponse::Json( - customers::CustomerResponse::foreign_from((response, update_customer.address)), - )) + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::CustomersErrorResponse::InternalServerError)?; + + let response = db + .update_customer_by_customer_id_merchant_id( + key_manager_state, + domain_customer.customer_id.to_owned(), + merchant_account.get_id().to_owned(), + domain_customer.to_owned(), + storage::CustomerUpdate::Update { + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: Box::new(encryptable_customer.phone), + phone_country_code: self.phone_country_code.clone(), + metadata: self.metadata.clone(), + description: self.description.clone(), + connector_customer: None, + address_id: address.clone().map(|addr| addr.address_id), + }, + key_store, + merchant_account.storage_scheme, + ) + .await + .switch()?; + + Ok(response) + } + + fn generate_response<'a>( + &'a self, + customer: &'a domain::Customer, + ) -> errors::CustomerResponse { + let address = self.get_address(); + let address_details = address.map(api_models::payments::AddressDetails::from); + + Ok(services::ApplicationResponse::Json( + customers::CustomerResponse::foreign_from((customer.clone(), address_details)), + )) + } +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[async_trait::async_trait] +impl CustomerUpdateBridge for customers::CustomerUpdateRequest { + async fn create_domain_model_from_request<'a>( + &'a self, + db: &'a dyn StorageInterface, + key_store: &'a domain::MerchantKeyStore, + merchant_account: &'a domain::MerchantAccount, + key_manager_state: &'a KeyManagerState, + state: &'a SessionState, + domain_customer: &'a domain::Customer, + ) -> errors::CustomResult { + let default_billing_address = self.get_default_customer_billing_address(); + + let encrypted_customer_billing_address = default_billing_address + .async_map(|billing_address| create_encrypted_data(state, key_store, billing_address)) + .await + .transpose() + .change_context(errors::CustomersErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt default customer billing address")?; + + let default_shipping_address = self.get_default_customer_shipping_address(); + + let encrypted_customer_shipping_address = default_shipping_address + .async_map(|shipping_address| create_encrypted_data(state, key_store, shipping_address)) + .await + .transpose() + .change_context(errors::CustomersErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt default customer shipping address")?; + + let key = key_store.key.get_inner().peek(); + + let encrypted_data = types::crypto_operation( + key_manager_state, + type_name!(domain::Customer), + types::CryptoOperation::BatchEncrypt(CustomerRequestWithEmail::to_encryptable( + CustomerRequestWithEmail { + name: self.name.clone(), + email: self.email.clone(), + phone: self.phone.clone(), + }, + )), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .and_then(|val| val.try_into_batchoperation()) + .switch()?; + + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::CustomersErrorResponse::InternalServerError)?; + + let response = db + .update_customer_by_global_id( + key_manager_state, + domain_customer.id.to_owned(), + domain_customer.to_owned(), + merchant_account.get_id(), + storage::CustomerUpdate::Update { + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: Box::new(encryptable_customer.phone), + phone_country_code: self.phone_country_code.clone(), + metadata: self.metadata.clone(), + description: self.description.clone(), + connector_customer: None, + default_billing_address: encrypted_customer_billing_address.map(Into::into), + default_shipping_address: encrypted_customer_shipping_address.map(Into::into), + default_payment_method_id: Some(self.default_payment_method_id.clone()), + }, + &key_store, + merchant_account.storage_scheme, + ) + .await + .switch()?; + Ok(response) + } + + fn generate_response<'a>( + &'a self, + customer: &'a domain::Customer, + ) -> errors::CustomerResponse { + Ok(services::ApplicationResponse::Json( + customers::CustomerResponse::foreign_from(customer.clone()), + )) + } } pub async fn migrate_customers( diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index be22108e3782..ce7495af2736 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -7,21 +7,24 @@ pub mod utils; mod validator; pub mod vault; -use std::{borrow::Cow, collections::HashSet}; +use std::borrow::Cow; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use std::collections::HashSet; pub use api_models::enums::Connector; use api_models::payment_methods; #[cfg(feature = "payouts")] pub use api_models::{enums::PayoutConnectors, payouts as payout_types}; -use common_utils::{consts::DEFAULT_LOCALE, ext_traits::Encode, id_type::CustomerId}; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use common_utils::ext_traits::Encode; +use common_utils::{consts::DEFAULT_LOCALE, id_type::CustomerId}; use diesel_models::{ enums, GenericLinkNew, PaymentMethodCollectLink, PaymentMethodCollectLinkData, }; use error_stack::{report, ResultExt}; -use hyperswitch_domain_models::{ - api::{GenericLinks, GenericLinksData}, - payments::{payment_attempt::PaymentAttempt, PaymentIntent}, -}; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData}; +use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; use masking::PeekInterface; use router_env::{instrument, tracing}; use time::Duration; @@ -205,6 +208,17 @@ pub async fn create_pm_collect_db_entry( }) } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn render_pm_collect_link( + _state: SessionState, + _merchant_account: domain::MerchantAccount, + _key_store: domain::MerchantKeyStore, + _req: payment_methods::PaymentMethodCollectLinkRenderRequest, +) -> RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn render_pm_collect_link( state: SessionState, merchant_account: domain::MerchantAccount, @@ -264,6 +278,7 @@ pub async fn render_pm_collect_link( field_name: "customer_id", })?; // Fetch customer + let customer = db .find_customer_by_customer_id_merchant_id( &(&state).into(), diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 30a9b21df0bb..d7e83246d1c5 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -32,13 +32,14 @@ use common_utils::{ }, }; use diesel_models::payment_method; -use domain::CustomerUpdate; use error_stack::{report, ResultExt}; use euclid::{ dssa::graph::{AnalysisContext, CgraphExt}, frontend::dir, }; use hyperswitch_constraint_graph as cgraph; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use hyperswitch_domain_models::customer::CustomerUpdate; use kgraph_utils::transformers::IntoDirValue; use masking::Secret; use router_env::{instrument, metrics::add_attributes, tracing}; @@ -50,11 +51,14 @@ use super::surcharge_decision_configs::{ }; #[cfg(all( any(feature = "v2", feature = "v1"), - not(feature = "payment_methods_v2") + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") ))] use crate::routes::app::SessionStateInfo; #[cfg(feature = "payouts")] use crate::types::domain::types::AsyncLift; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use crate::utils::{self}; use crate::{ configs::settings, core::{ @@ -81,9 +85,34 @@ use crate::{ storage::{self, enums, PaymentMethodListContext, PaymentTokenData}, transformers::{ForeignFrom, ForeignTryFrom}, }, - utils::{self, ConnectorResponseExt, OptionExt}, + utils::{ConnectorResponseExt, OptionExt}, }; +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] +pub async fn create_payment_method( + state: &routes::SessionState, + req: &api::PaymentMethodCreate, + customer_id: &id_type::CustomerId, + payment_method_id: &str, + locker_id: Option, + merchant_id: &id_type::MerchantId, + pm_metadata: Option, + customer_acceptance: Option, + payment_method_data: Option, + key_store: &domain::MerchantKeyStore, + connector_mandate_details: Option, + status: Option, + network_transaction_id: Option, + storage_scheme: MerchantStorageScheme, + payment_method_billing_address: Option, + card_scheme: Option, +) -> errors::CustomResult { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_payment_method( @@ -461,6 +490,19 @@ impl } } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn skip_locker_call_and_migrate_payment_method( + _state: routes::SessionState, + _req: &api::PaymentMethodMigrate, + _merchant_id: id_type::MerchantId, + _key_store: &domain::MerchantKeyStore, + _merchant_account: &domain::MerchantAccount, + _card: api_models::payment_methods::CardDetailFromLocker, +) -> errors::RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn skip_locker_call_and_migrate_payment_method( state: routes::SessionState, req: &api::PaymentMethodMigrate, @@ -709,6 +751,19 @@ pub fn authenticate_pm_client_secret_and_check_expiry( } } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[instrument(skip_all)] +pub async fn add_payment_method_data( + _state: routes::SessionState, + _req: api::PaymentMethodCreate, + _merchant_account: domain::MerchantAccount, + _key_store: domain::MerchantKeyStore, + _pm_id: String, +) -> errors::RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] pub async fn add_payment_method_data( state: routes::SessionState, @@ -739,6 +794,7 @@ pub async fn add_payment_method_data( } let customer_id = payment_method.customer_id.clone(); + let customer = db .find_customer_by_customer_id_merchant_id( &(&state).into(), @@ -2254,7 +2310,17 @@ fn get_val(str: String, val: &serde_json::Value) -> Option { .and_then(|v| v.as_str()) .map(|s| s.to_string()) } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn list_payment_methods( + _state: routes::SessionState, + _merchant_account: domain::MerchantAccount, + _key_store: domain::MerchantKeyStore, + mut _req: api::PaymentMethodListRequest, +) -> errors::RouterResponse { + todo!() +} +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn list_payment_methods( state: routes::SessionState, merchant_account: domain::MerchantAccount, @@ -3674,7 +3740,11 @@ fn filter_recurring_based( recurring_enabled.map_or(true, |enabled| payment_method.recurring_enabled == enabled) } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] pub async fn list_customer_payment_method_util( state: routes::SessionState, merchant_account: domain::MerchantAccount, @@ -3729,7 +3799,8 @@ pub async fn list_customer_payment_method_util( #[cfg(all( any(feature = "v2", feature = "v1"), - not(feature = "payment_methods_v2") + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") ))] pub async fn do_list_customer_pm_fetch_customer_if_not_passed( state: routes::SessionState, @@ -3804,7 +3875,8 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( #[cfg(all( any(feature = "v2", feature = "v1"), - not(feature = "payment_methods_v2") + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") ))] pub async fn list_customer_payment_method( state: &routes::SessionState, @@ -4307,7 +4379,11 @@ impl SavedPMLPaymentsInfo { } } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] pub async fn list_customer_payment_method( state: &routes::SessionState, merchant_account: domain::MerchantAccount, @@ -4322,7 +4398,7 @@ pub async fn list_customer_payment_method( // let key = key_store.key.get_inner().peek(); let customer = db - .find_customer_by_customer_id_merchant_id( + .find_customer_by_merchant_reference_id_merchant_id( key_manager_state, customer_id, merchant_account.get_id(), @@ -4566,7 +4642,9 @@ pub async fn get_mca_status( let mut mca_ids = HashSet::new(); let mcas = mcas .into_iter() - .filter(|mca| mca.disabled == Some(false) && profile_id.clone() == mca.profile_id) + .filter(|mca| { + mca.disabled == Some(false) && profile_id.clone() == Some(mca.profile_id.clone()) + }) .collect::>(); for mca in mcas { @@ -4834,6 +4912,20 @@ async fn get_bank_account_connector_details( None => Ok(None), } } + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn set_default_payment_method( + _state: &routes::SessionState, + _merchant_id: &id_type::MerchantId, + _key_store: domain::MerchantKeyStore, + _customer_id: &id_type::CustomerId, + _payment_method_id: String, + _storage_scheme: MerchantStorageScheme, +) -> errors::RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn set_default_payment_method( state: &routes::SessionState, merchant_id: &id_type::MerchantId, @@ -4883,13 +4975,13 @@ pub async fn set_default_payment_method( }, )?; + let customer_id = customer.get_customer_id().clone(); + let customer_update = CustomerUpdate::UpdateDefaultPaymentMethod { default_payment_method_id: Some(Some(payment_method_id.to_owned())), }; - - let customer_id = customer.get_customer_id().clone(); - // update the db with the default payment method id + let updated_customer_details = db .update_customer_by_customer_id_merchant_id( key_manager_state, @@ -5124,6 +5216,18 @@ pub async fn retrieve_payment_method( )) } +#[instrument(skip_all)] +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn delete_payment_method( + _state: routes::SessionState, + _merchant_account: domain::MerchantAccount, + _pm_id: api::PaymentMethodId, + _key_store: domain::MerchantKeyStore, +) -> errors::RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] pub async fn delete_payment_method( state: routes::SessionState, @@ -5199,7 +5303,6 @@ pub async fn delete_payment_method( let customer_update = CustomerUpdate::UpdateDefaultPaymentMethod { default_payment_method_id: Some(None), }; - db.update_customer_by_customer_id_merchant_id( key_manager_state, key.customer_id, diff --git a/crates/router/src/core/payment_methods/validator.rs b/crates/router/src/core/payment_methods/validator.rs index d9ec794151c8..f32e337a8c81 100644 --- a/crates/router/src/core/payment_methods/validator.rs +++ b/crates/router/src/core/payment_methods/validator.rs @@ -15,6 +15,17 @@ use crate::{ utils, }; +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn validate_request_and_initiate_payment_method_collect_link( + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _req: &PaymentMethodCollectLinkRequest, +) -> RouterResult { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn validate_request_and_initiate_payment_method_collect_link( state: &SessionState, merchant_account: &domain::MerchantAccount, @@ -25,6 +36,7 @@ pub async fn validate_request_and_initiate_payment_method_collect_link( let db: &dyn StorageInterface = &*state.store; let customer_id = req.customer_id.clone(); let merchant_id = merchant_account.get_id().clone(); + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] match db .find_customer_by_customer_id_merchant_id( &state.into(), diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index c336e2808100..3f627de7dbc4 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -73,7 +73,6 @@ use crate::{ configs::settings::{ApplePayPreDecryptFlow, PaymentMethodTypeTokenFilter}, connector::utils::missing_field_err, core::{ - authentication as authentication_core, errors::{self, CustomResult, RouterResponse, RouterResult}, payment_methods::{cards, network_tokenization::do_status_check_for_network_token}, utils, @@ -84,11 +83,10 @@ use crate::{ services::{self, api::Authenticate, ConnectorRedirectResponse}, types::{ self as router_types, - api::{self, authentication, ConnectorCallType, ConnectorCommon}, + api::{self, ConnectorCallType, ConnectorCommon}, domain, storage::{self, enums as storage_enums, payment_attempt::PaymentAttemptExt}, transformers::{ForeignInto, ForeignTryInto}, - BrowserInformation, }, utils::{ add_apple_pay_flow_metrics, add_connector_http_status_code_metrics, Encode, OptionExt, @@ -96,6 +94,11 @@ use crate::{ }, workflows::payment_sync, }; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use crate::{ + core::authentication as authentication_core, + types::{api::authentication, BrowserInformation}, +}; #[allow(clippy::too_many_arguments, clippy::type_complexity)] #[instrument(skip_all, fields(payment_id, merchant_id))] @@ -4186,6 +4189,17 @@ where } } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn payment_external_authentication( + _state: SessionState, + _merchant_account: domain::MerchantAccount, + _key_store: domain::MerchantKeyStore, + _req: api_models::payments::PaymentsExternalAuthenticationRequest, +) -> RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] pub async fn payment_external_authentication( state: SessionState, @@ -4230,6 +4244,7 @@ pub async fn payment_external_authentication( &[storage_enums::IntentStatus::RequiresCustomerAction], "authenticate", )?; + let optional_customer = match &payment_intent.customer_id { Some(customer_id) => Some( state @@ -4249,6 +4264,7 @@ pub async fn payment_external_authentication( ), None => None, }; + let profile_id = payment_intent .profile_id .as_ref() diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs index b79e62d11ba7..3ccd5f2577a6 100644 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ b/crates/router/src/core/payments/connector_integration_v2_impls.rs @@ -1271,6 +1271,7 @@ default_imp_for_new_connector_integration_payouts!( connector::Stax, connector::Stripe, connector::Shift4, + connector::Taxjar, connector::Trustpay, connector::Threedsecureio, connector::Tsys, @@ -2115,6 +2116,7 @@ default_imp_for_new_connector_integration_frm!( connector::Stax, connector::Stripe, connector::Shift4, + connector::Taxjar, connector::Trustpay, connector::Threedsecureio, connector::Tsys, @@ -2738,6 +2740,7 @@ default_imp_for_new_connector_integration_connector_authentication!( connector::Stax, connector::Stripe, connector::Shift4, + connector::Taxjar, connector::Trustpay, connector::Threedsecureio, connector::Tsys, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 7b531aa09e9b..228c7e208cc1 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -577,6 +577,7 @@ default_imp_for_connector_request_id!( connector::Square, connector::Stax, connector::Stripe, + connector::Taxjar, connector::Threedsecureio, connector::Trustpay, connector::Tsys, @@ -1231,6 +1232,7 @@ default_imp_for_payouts!( connector::Square, connector::Stax, connector::Shift4, + connector::Taxjar, connector::Threedsecureio, connector::Trustpay, connector::Tsys, @@ -2250,6 +2252,7 @@ default_imp_for_fraud_check!( connector::Stax, connector::Stripe, connector::Shift4, + connector::Taxjar, connector::Threedsecureio, connector::Trustpay, connector::Tsys, @@ -3065,6 +3068,7 @@ default_imp_for_connector_authentication!( connector::Stax, connector::Stripe, connector::Shift4, + connector::Taxjar, connector::Trustpay, connector::Tsys, connector::Volt, diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index cdd8452fdb32..4a1d57cda347 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -411,10 +411,8 @@ async fn create_applepay_session_token( "Retry apple pay session call with the merchant configured domain {error:?}" ); let merchant_configured_domain = merchant_configured_domain_optional - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Failed to get initiative_context for apple pay session call retry", - )?; + .get_required_value("apple pay domain") + .attach_printable("Failed to get domain for apple pay session call")?; let apple_pay_retry_session_request = payment_types::ApplepaySessionRequest { initiative_context: merchant_configured_domain, @@ -496,15 +494,16 @@ fn get_session_request_for_manual_apple_pay( session_token_data: payment_types::SessionTokenInfo, merchant_domain: Option, ) -> RouterResult { - let initiative_context = session_token_data - .initiative_context - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to get initiative_context for apple pay session call")?; + let initiative_context = merchant_domain + .or_else(|| session_token_data.initiative_context.clone()) + .get_required_value("apple pay domain") + .attach_printable("Failed to get domain for apple pay session call")?; + Ok(payment_types::ApplepaySessionRequest { merchant_identifier: session_token_data.merchant_identifier.clone(), display_name: session_token_data.display_name.clone(), initiative: session_token_data.initiative.to_string(), - initiative_context: merchant_domain.unwrap_or(initiative_context), + initiative_context, }) } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 3f77ed35c21f..ce8954c72d11 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -130,7 +130,7 @@ pub fn filter_mca_based_on_profile_and_connector_type( merchant_connector_accounts .into_iter() .filter(|mca| { - profile_id.map_or(true, |id| mca.profile_id.as_ref() == Some(id)) + profile_id.map_or(true, |id| &mca.profile_id == id) && mca.connector_type == connector_type }) .collect() @@ -1446,6 +1446,7 @@ pub async fn get_customer_from_details( None => Ok(None), Some(customer_id) => { let db = &*state.store; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] let customer = db .find_customer_optional_by_customer_id_merchant_id( &state.into(), @@ -1455,6 +1456,18 @@ pub async fn get_customer_from_details( storage_scheme, ) .await?; + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + let customer = db + .find_optional_by_merchant_id_merchant_reference_id( + &state.into(), + &customer_id, + merchant_id, + merchant_key_store, + storage_scheme, + ) + .await?; + payment_data.email = payment_data.email.clone().or_else(|| { customer.as_ref().and_then(|inner| { inner diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index fc4d15cd1ba0..76b8b1e5d746 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -3,17 +3,19 @@ use std::marker::PhantomData; use api_models::{ admin::ExtendedCardInfoConfig, enums::FrmSuggestion, - payment_methods::PaymentMethodsData, - payments::{AdditionalPaymentData, ExtendedCardInfo, GetAddressFromPaymentMethodData}, + // payment_methods::PaymentMethodsData, + payments::{ExtendedCardInfo, GetAddressFromPaymentMethodData}, }; +// use api_models::{admin::ExtendedCardInfoConfig, enums::FrmSuggestion, payments::ExtendedCardInfo}; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use api_models::{payment_methods::PaymentMethodsData, payments::AdditionalPaymentData}; use async_trait::async_trait; -use common_utils::{ - ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, - type_name, - types::keymanager::Identifier, -}; +use common_utils::ext_traits::{AsyncExt, Encode, StringExt, ValueExt}; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use common_utils::{type_name, types::keymanager::Identifier}; use error_stack::{report, ResultExt}; use futures::FutureExt; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdateFields; use masking::{ExposeInterface, PeekInterface}; use router_derive::PaymentOperation; @@ -21,29 +23,30 @@ use router_env::{instrument, logger, tracing}; use tracing_futures::Instrument; use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest}; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +use crate::{ + core::payment_methods::cards::create_encrypted_data, + events::audit_events::{AuditEvent, AuditEventType}, + types::domain::types::{crypto_operation, CryptoOperation}, +}; use crate::{ core::{ authentication, blocklist::utils as blocklist_utils, errors::{self, CustomResult, RouterResult, StorageErrorExt}, mandate::helpers as m_helpers, - payment_methods::cards::create_encrypted_data, payments::{ self, helpers, operations, populate_surcharge_details, CustomerDetails, PaymentAddress, PaymentData, }, utils as core_utils, }, - events::audit_events::{AuditEvent, AuditEventType}, routes::{app::ReqState, SessionState}, services, types::{ self, api::{self, ConnectorCallType, PaymentIdTypeExt}, - domain::{ - self, - types::{crypto_operation, CryptoOperation}, - }, + domain::{self}, storage::{self, enums as storage_enums}, }, utils::{self, OptionExt}, @@ -1018,6 +1021,30 @@ impl Domain for PaymentConfirm { } } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[async_trait] +impl UpdateTracker, api::PaymentsRequest> for PaymentConfirm { + #[instrument(skip_all)] + async fn update_trackers<'b>( + &'b self, + _state: &'b SessionState, + _req_state: ReqState, + mut _payment_data: PaymentData, + _customer: Option, + _storage_scheme: storage_enums::MerchantStorageScheme, + _updated_customer: Option, + _key_store: &domain::MerchantKeyStore, + _frm_suggestion: Option, + _header_payload: api::HeaderPayload, + ) -> RouterResult<(BoxedOperation<'b, F, api::PaymentsRequest>, PaymentData)> + where + F: 'b + Send, + { + todo!() + } +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[async_trait] impl UpdateTracker, api::PaymentsRequest> for PaymentConfirm { #[instrument(skip_all)] @@ -1384,7 +1411,6 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let customer_fut = if let Some((updated_customer, customer)) = updated_customer.zip(customer) { - let m_customer_customer_id = customer.get_customer_id().to_owned(); let m_customer_merchant_id = customer.merchant_id.to_owned(); let m_key_store = key_store.clone(); let m_updated_customer = updated_customer.clone(); @@ -1393,6 +1419,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let key_manager_state = state.into(); tokio::spawn( async move { + let m_customer_customer_id = customer.get_customer_id().to_owned(); m_db.update_customer_by_customer_id_merchant_id( &key_manager_state, m_customer_customer_id, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index b2ad13ee0d85..e7045116f23c 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -95,13 +95,11 @@ impl GetTracker, api::PaymentsRequest> for Pa .and_then(|pmd| pmd.payment_method_data.clone()), )?; - helpers::validate_payment_status_against_not_allowed_statuses( + helpers::validate_payment_status_against_allowed_statuses( &payment_intent.status, &[ - storage_enums::IntentStatus::Failed, - storage_enums::IntentStatus::Succeeded, - storage_enums::IntentStatus::PartiallyCaptured, - storage_enums::IntentStatus::RequiresCapture, + storage_enums::IntentStatus::RequiresPaymentMethod, + storage_enums::IntentStatus::RequiresConfirmation, ], "update", )?; diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 2fc420348f71..4202e0ac4407 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -42,7 +42,7 @@ pub async fn construct_payment_router_data<'a, F, T>( payment_data: PaymentData, connector_id: &str, merchant_account: &domain::MerchantAccount, - _key_store: &domain::MerchantKeyStore, + key_store: &domain::MerchantKeyStore, customer: &'a Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_recipient_data: Option, @@ -135,6 +135,26 @@ where Some(merchant_connector_account), ); + let unified_address = + if let Some(payment_method_info) = payment_data.payment_method_info.clone() { + let payment_method_billing = + crate::core::payment_methods::cards::decrypt_generic_data::
( + state, + payment_method_info.payment_method_billing_address, + key_store, + ) + .await + .attach_printable("unable to decrypt payment method billing address details")?; + payment_data + .address + .clone() + .unify_with_payment_data_billing(payment_method_billing) + } else { + payment_data.address + }; + + crate::logger::debug!("unified address details {:?}", unified_address); + router_data = types::RouterData { flow: PhantomData, merchant_id: merchant_account.get_id().clone(), @@ -147,7 +167,7 @@ where connector_auth_type: auth_type, description: payment_data.payment_intent.description.clone(), return_url: payment_data.payment_intent.return_url.clone(), - address: payment_data.address.clone(), + address: unified_address, auth_type: payment_data .payment_attempt .authentication_type diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 1b9445f180e7..0e4806051125 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -24,6 +24,19 @@ use crate::{ types::domain, }; +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn initiate_payout_link( + _state: SessionState, + _merchant_account: domain::MerchantAccount, + _key_store: domain::MerchantKeyStore, + _req: payouts::PayoutLinkInitiateRequest, + _request_headers: &header::HeaderMap, + _locale: String, +) -> RouterResponse { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn initiate_payout_link( state: SessionState, merchant_account: domain::MerchantAccount, @@ -140,6 +153,7 @@ pub async fn initiate_payout_link( .attach_printable_lazy(|| { format!("customer [{}] not found", payout_link.primary_reference) })?; + let enabled_payout_methods = filter_payout_methods(&state, &merchant_account, &key_store, &payout).await?; // Fetch default enabled_payout_methods diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 07b50d3a1543..5b6ca5d67b7a 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -743,7 +743,22 @@ pub async fn payouts_fulfill_core( response_handler(&merchant_account, &payout_data).await } -#[cfg(feature = "olap")] +#[cfg(all(feature = "olap", feature = "v2", feature = "customer_v2"))] +pub async fn payouts_list_core( + _state: SessionState, + _merchant_account: domain::MerchantAccount, + _profile_id_list: Option>, + _key_store: domain::MerchantKeyStore, + _constraints: payouts::PayoutListConstraints, +) -> RouterResponse { + todo!() +} + +#[cfg(all( + feature = "olap", + any(feature = "v1", feature = "v2"), + not(feature = "customer_v2") +))] pub async fn payouts_list_core( state: SessionState, merchant_account: domain::MerchantAccount, @@ -775,6 +790,7 @@ pub async fn payouts_list_core( { Ok(ref payout_attempt) => match payout.customer_id.clone() { Some(ref customer_id) => { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] match db .find_customer_by_customer_id_merchant_id( &(&state).into(), @@ -846,6 +862,7 @@ pub async fn payouts_list_core( api::PayoutListResponse { size: data.len(), data, + total_count: None, }, )) } @@ -861,6 +878,7 @@ pub async fn payouts_filtered_list_core( let limit = &filters.limit; validator::validate_payout_list_request_for_joins(*limit)?; let db = state.store.as_ref(); + let constraints = filters.clone().into(); let list: Vec<( storage::Payouts, storage::PayoutAttempt, @@ -868,7 +886,7 @@ pub async fn payouts_filtered_list_core( )> = db .filter_payouts_and_attempts( merchant_account.get_id(), - &filters.clone().into(), + &constraints, merchant_account.storage_scheme, ) .await @@ -899,10 +917,35 @@ pub async fn payouts_filtered_list_core( .map(ForeignFrom::foreign_from) .collect(); + let active_payout_ids = db + .filter_active_payout_ids_by_constraints(merchant_account.get_id(), &constraints) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to filter active payout ids based on the constraints")?; + + let total_count = db + .get_total_count_of_filtered_payouts( + merchant_account.get_id(), + &active_payout_ids, + filters.connector.clone(), + filters.currency.clone(), + filters.status.clone(), + filters.payout_method.clone(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| { + format!( + "Failed to fetch total count of filtered payouts for the given constraints - {:?}", + filters + ) + })?; + Ok(services::ApplicationResponse::Json( api::PayoutListResponse { size: data.len(), data, + total_count: Some(total_count), }, )) } @@ -1106,7 +1149,7 @@ pub async fn create_recipient( // 1. Form router data let router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -1135,8 +1178,6 @@ pub async fn create_recipient( Ok(recipient_create_data) => { let db = &*state.store; if let Some(customer) = customer_details { - let customer_id = customer.get_customer_id().to_owned(); - let merchant_id = merchant_account.get_id().to_owned(); if let Some(updated_customer) = customers::update_connector_customer_in_customers( &connector_label, @@ -1145,20 +1186,46 @@ pub async fn create_recipient( ) .await { - payout_data.customer_details = Some( - db.update_customer_by_customer_id_merchant_id( - &state.into(), - customer_id, - merchant_id, - customer, - updated_customer, - key_store, - merchant_account.storage_scheme, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error updating customers in db")?, - ) + #[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "customer_v2") + ))] + { + let customer_id = customer.get_customer_id().to_owned(); + payout_data.customer_details = Some( + db.update_customer_by_customer_id_merchant_id( + &state.into(), + customer_id, + merchant_account.get_id().to_owned(), + customer, + updated_customer, + key_store, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error updating customers in db")?, + ); + } + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + { + let global_id = "temp_id".to_string(); + payout_data.customer_details = Some( + db.update_customer_by_global_id( + &state.into(), + global_id, + customer, + &merchant_account.get_id(), + updated_customer, + key_store, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Error updating customers in db")?, + ); + } } } @@ -1273,7 +1340,7 @@ pub async fn check_payout_eligibility( // 1. Form Router data let router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -1453,7 +1520,7 @@ pub async fn create_payout( // 1. Form Router data let mut router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -1645,7 +1712,7 @@ pub async fn create_payout_retrieve( // 1. Form Router data let mut router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -1787,7 +1854,7 @@ pub async fn create_recipient_disburse_account( // 1. Form Router data let router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -1872,7 +1939,7 @@ pub async fn cancel_payout( // 1. Form Router data let router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -1977,7 +2044,7 @@ pub async fn fulfill_payout( // 1. Form Router data let mut router_data = core_utils::construct_payout_router_data( state, - &connector_data.connector_name, + connector_data, merchant_account, key_store, payout_data, @@ -2356,6 +2423,18 @@ pub async fn payout_create_db_entries( }) } +#[cfg(all(feature = "v2", feature = "customer_v2"))] +pub async fn make_payout_data( + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _auth_profile_id: Option, + _key_store: &domain::MerchantKeyStore, + _req: &payouts::PayoutRequest, +) -> RouterResult { + todo!() +} + +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] pub async fn make_payout_data( state: &SessionState, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index d9ab7d12bdb0..d425725645b8 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -129,7 +129,7 @@ pub async fn validate_create_request( state, req.payout_method_data.as_ref(), Some(payout_token), - &customer.customer_id, + &customer.get_customer_id(), merchant_account.get_id(), req.payout_type, merchant_key_store, diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 24577aa74f46..564b5b9e1fc5 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -346,7 +346,9 @@ async fn store_bank_details_in_payment_methods( > = HashMap::new(); for pm in payment_methods { - if pm.payment_method == Some(enums::PaymentMethod::BankDebit) { + if pm.payment_method == Some(enums::PaymentMethod::BankDebit) + && pm.payment_method_data.is_some() + { let bank_details_pm_data = crypto_operation::( &(&state).into(), type_name!(storage::PaymentMethod), diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 9d819f8df9f9..0c50a01e36d0 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -640,7 +640,90 @@ pub async fn unlink_routing_config( } } -//feature update +#[cfg(all( + feature = "v2", + feature = "routing_v2", + feature = "business_profile_v2" +))] +pub async fn update_default_fallback_routing( + state: SessionState, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + profile_id: String, + updated_list_of_connectors: Vec, +) -> RouterResponse> { + metrics::ROUTING_UPDATE_CONFIG.add(&metrics::CONTEXT, 1, &[]); + let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); + let profile = core_utils::validate_and_get_business_profile( + db, + key_manager_state, + &key_store, + Some(&profile_id), + merchant_account.get_id(), + ) + .await? + .get_required_value("BusinessProfile")?; + let profile_wrapper = admin::BusinessProfileWrapper::new(profile); + let default_list_of_connectors = + profile_wrapper.get_default_fallback_list_of_connector_under_profile()?; + + utils::when( + default_list_of_connectors.len() != updated_list_of_connectors.len(), + || { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: "current config and updated config have different lengths".to_string(), + }) + }, + )?; + + let existing_set_of_default_connectors: FxHashSet = FxHashSet::from_iter( + default_list_of_connectors + .iter() + .map(|conn_choice| conn_choice.to_string()), + ); + let updated_set_of_default_connectors: FxHashSet = FxHashSet::from_iter( + updated_list_of_connectors + .iter() + .map(|conn_choice| conn_choice.to_string()), + ); + + let symmetric_diff_between_existing_and_updated_connectors: Vec = + existing_set_of_default_connectors + .symmetric_difference(&updated_set_of_default_connectors) + .cloned() + .collect(); + + utils::when( + !symmetric_diff_between_existing_and_updated_connectors.is_empty(), + || { + Err(errors::ApiErrorResponse::InvalidRequestData { + message: format!( + "connector mismatch between old and new configs ({})", + symmetric_diff_between_existing_and_updated_connectors.join(", ") + ), + }) + }, + )?; + profile_wrapper + .update_default_routing_for_profile( + db, + &updated_list_of_connectors, + key_manager_state, + &key_store, + ) + .await?; + + metrics::ROUTING_UPDATE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json( + updated_list_of_connectors, + )) +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(any(feature = "routing_v2", feature = "business_profile_v2")) +))] pub async fn update_default_routing_config( state: SessionState, merchant_account: domain::MerchantAccount, @@ -693,6 +776,42 @@ pub async fn update_default_routing_config( Ok(service_api::ApplicationResponse::Json(updated_config)) } +#[cfg(all( + feature = "v2", + feature = "routing_v2", + feature = "business_profile_v2" +))] +pub async fn retrieve_default_fallback_algorithm_for_profile( + state: SessionState, + merchant_account: domain::MerchantAccount, + key_store: domain::MerchantKeyStore, + profile_id: String, +) -> RouterResponse> { + metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG.add(&metrics::CONTEXT, 1, &[]); + let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); + let profile = core_utils::validate_and_get_business_profile( + db, + key_manager_state, + &key_store, + Some(&profile_id), + merchant_account.get_id(), + ) + .await? + .get_required_value("BusinessProfile")?; + + let connectors_choice = admin::BusinessProfileWrapper::new(profile) + .get_default_fallback_list_of_connector_under_profile()?; + + metrics::ROUTING_RETRIEVE_DEFAULT_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json(connectors_choice)) +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(any(feature = "routing_v2", feature = "business_profile_v2")) +))] + pub async fn retrieve_default_routing_config( state: SessionState, merchant_account: domain::MerchantAccount, @@ -712,7 +831,6 @@ pub async fn retrieve_default_routing_config( service_api::ApplicationResponse::Json(conn_choice) }) } - #[cfg(all( feature = "v2", feature = "routing_v2", diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 75a88275f868..e61a7544b167 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -414,13 +414,13 @@ pub async fn validate_connectors_in_routing_config( })?; let name_mca_id_set = all_mcas .iter() - .filter(|mca| mca.profile_id.as_deref() == Some(profile_id)) + .filter(|mca| mca.profile_id == profile_id) .map(|mca| (&mca.connector_name, mca.get_id())) .collect::>(); let name_set = all_mcas .iter() - .filter(|mca| mca.profile_id.as_deref() == Some(profile_id)) + .filter(|mca| mca.profile_id == profile_id) .map(|mca| &mca.connector_name) .collect::>(); diff --git a/crates/router/src/core/user_role.rs b/crates/router/src/core/user_role.rs index b5c7f6db0168..f2fddd61bb62 100644 --- a/crates/router/src/core/user_role.rs +++ b/crates/router/src/core/user_role.rs @@ -342,68 +342,169 @@ pub async fn delete_user_role( .attach_printable("User deleting himself"); } - let user_roles = state + let deletion_requestor_role_info = roles::RoleInfo::from_role_id( + &state, + &user_from_token.role_id, + &user_from_token.merchant_id, + &user_from_token.org_id, + ) + .await + .change_context(UserErrors::InternalServerError)?; + + let mut user_role_deleted_flag = false; + + // Find in V2 + let user_role_v2 = match state .store - .list_user_roles_by_user_id(user_from_db.get_user_id(), UserRoleVersion::V1) + .find_user_role_by_user_id_and_lineage( + user_from_db.get_user_id(), + &user_from_token.org_id, + &user_from_token.merchant_id, + user_from_token.profile_id.as_ref(), + UserRoleVersion::V2, + ) + .await + { + Ok(user_role) => Some(user_role), + Err(e) => { + if e.current_context().is_db_not_found() { + None + } else { + return Err(UserErrors::InternalServerError.into()); + } + } + }; + + if let Some(role_to_be_deleted) = user_role_v2 { + let target_role_info = roles::RoleInfo::from_role_id( + &state, + &role_to_be_deleted.role_id, + &user_from_token.merchant_id, + &user_from_token.org_id, + ) .await .change_context(UserErrors::InternalServerError)?; - for user_role in user_roles.iter() { - let Some(merchant_id) = user_role.merchant_id.as_ref() else { - return Err(report!(UserErrors::InternalServerError)) - .attach_printable("merchant_id not found for user_role"); - }; + if !target_role_info.is_deletable() { + return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!( + "Invalid operation, role_id = {} is not deletable", + role_to_be_deleted.role_id + )); + } - if merchant_id == &user_from_token.merchant_id { - let role_info = roles::RoleInfo::from_role_id( - &state, - &user_role.role_id, - &user_from_token.merchant_id, + if deletion_requestor_role_info.get_entity_type() < target_role_info.get_entity_type() { + return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!( + "Invalid operation, deletion requestor = {} cannot delete target = {}", + deletion_requestor_role_info.get_entity_type(), + target_role_info.get_entity_type() + )); + } + + user_role_deleted_flag = true; + state + .store + .delete_user_role_by_user_id_and_lineage( + user_from_db.get_user_id(), &user_from_token.org_id, + &user_from_token.merchant_id, + user_from_token.profile_id.as_ref(), + UserRoleVersion::V2, ) .await - .change_context(UserErrors::InternalServerError)?; - if !role_info.is_deletable() { - return Err(report!(UserErrors::InvalidDeleteOperation)) - .attach_printable(format!("role_id = {} is not deletable", user_role.role_id)); + .change_context(UserErrors::InternalServerError) + .attach_printable("Error while deleting user role")?; + } + + // Find in V1 + let user_role_v1 = match state + .store + .find_user_role_by_user_id_and_lineage( + user_from_db.get_user_id(), + &user_from_token.org_id, + &user_from_token.merchant_id, + user_from_token.profile_id.as_ref(), + UserRoleVersion::V1, + ) + .await + { + Ok(user_role) => Some(user_role), + Err(e) => { + if e.current_context().is_db_not_found() { + None + } else { + return Err(UserErrors::InternalServerError.into()); } - } else { - return Err(report!(UserErrors::InvalidDeleteOperation)) - .attach_printable("User is not associated with the merchant"); } - } + }; + + if let Some(role_to_be_deleted) = user_role_v1 { + let target_role_info = roles::RoleInfo::from_role_id( + &state, + &role_to_be_deleted.role_id, + &user_from_token.merchant_id, + &user_from_token.org_id, + ) + .await + .change_context(UserErrors::InternalServerError)?; + + if !target_role_info.is_deletable() { + return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!( + "Invalid operation, role_id = {} is not deletable", + role_to_be_deleted.role_id + )); + } - let deleted_user_role = if user_roles.len() > 1 { + if deletion_requestor_role_info.get_entity_type() < target_role_info.get_entity_type() { + return Err(report!(UserErrors::InvalidDeleteOperation)).attach_printable(format!( + "Invalid operation, deletion requestor = {} cannot delete target = {}", + deletion_requestor_role_info.get_entity_type(), + target_role_info.get_entity_type() + )); + } + + user_role_deleted_flag = true; state .store - .delete_user_role_by_user_id_merchant_id( + .delete_user_role_by_user_id_and_lineage( user_from_db.get_user_id(), + &user_from_token.org_id, &user_from_token.merchant_id, + user_from_token.profile_id.as_ref(), UserRoleVersion::V1, ) .await .change_context(UserErrors::InternalServerError) - .attach_printable("Error while deleting user role")? - } else { + .attach_printable("Error while deleting user role")?; + } + + if !user_role_deleted_flag { + return Err(report!(UserErrors::InvalidDeleteOperation)) + .attach_printable("User is not associated with the merchant"); + } + + // Check if user has any more role associations + let user_roles_v2 = state + .store + .list_user_roles_by_user_id(user_from_db.get_user_id(), UserRoleVersion::V2) + .await + .change_context(UserErrors::InternalServerError)?; + + let user_roles_v1 = state + .store + .list_user_roles_by_user_id(user_from_db.get_user_id(), UserRoleVersion::V1) + .await + .change_context(UserErrors::InternalServerError)?; + + // If user has no more role associated with him then deleting user + if user_roles_v2.is_empty() && user_roles_v1.is_empty() { state .global_store .delete_user_by_user_id(user_from_db.get_user_id()) .await .change_context(UserErrors::InternalServerError) .attach_printable("Error while deleting user entry")?; + } - state - .store - .delete_user_role_by_user_id_merchant_id( - user_from_db.get_user_id(), - &user_from_token.merchant_id, - UserRoleVersion::V1, - ) - .await - .change_context(UserErrors::InternalServerError) - .attach_printable("Error while deleting user role")? - }; - - auth::blacklist::insert_user_in_blacklist(&state, &deleted_user_role.user_id).await?; + auth::blacklist::insert_user_in_blacklist(&state, user_from_db.get_user_id()).await?; Ok(ApplicationResponse::StatusOk) } diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs index 7bc4505b9738..980c983e050e 100644 --- a/crates/router/src/core/utils.rs +++ b/crates/router/src/core/utils.rs @@ -30,7 +30,7 @@ use crate::{ db::StorageInterface, routes::SessionState, types::{ - self, domain, + self, api, domain, storage::{self, enums}, PollConfig, }, @@ -49,7 +49,7 @@ const IRRELEVANT_ATTEMPT_ID_IN_DISPUTE_FLOW: &str = "irrelevant_attempt_id_in_di #[instrument(skip_all)] pub async fn get_mca_for_payout<'a>( state: &'a SessionState, - connector_id: &str, + connector_data: &api::ConnectorData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payout_data: &PayoutData, @@ -63,8 +63,8 @@ pub async fn get_mca_for_payout<'a>( None, key_store, &payout_data.profile_id, - connector_id, - payout_data.payout_attempt.merchant_connector_id.as_ref(), + &connector_data.connector_name.to_string(), + connector_data.merchant_connector_id.as_ref(), ) .await?; Ok(merchant_connector_account) @@ -76,19 +76,20 @@ pub async fn get_mca_for_payout<'a>( #[instrument(skip_all)] pub async fn construct_payout_router_data<'a, F>( state: &'a SessionState, - connector_name: &api_models::enums::Connector, + connector_data: &api::ConnectorData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, payout_data: &mut PayoutData, ) -> RouterResult> { let merchant_connector_account = get_mca_for_payout( state, - &connector_name.to_string(), + connector_data, merchant_account, key_store, payout_data, ) .await?; + let connector_name = connector_data.connector_name; payout_data.merchant_connector_account = Some(merchant_connector_account.clone()); let connector_auth_type: types::ConnectorAuthType = merchant_connector_account .get_connector_account_details() @@ -770,7 +771,7 @@ pub async fn construct_upload_file_router_data<'a>( payment_attempt: &storage::PaymentAttempt, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - create_file_request: &types::api::CreateFileRequest, + create_file_request: &api::CreateFileRequest, connector_id: &str, file_key: String, ) -> RouterResult { @@ -1335,7 +1336,7 @@ pub(super) trait GetProfileId { impl GetProfileId for MerchantConnectorAccount { fn get_profile_id(&self) -> Option<&String> { - self.profile_id.as_ref() + Some(&self.profile_id) } } diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 7c292d158ca5..9d16cf62c06d 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -347,12 +347,7 @@ async fn incoming_webhooks_core( )?, }; - let profile_id = merchant_connector_account - .profile_id - .as_ref() - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Could not find profile_id in merchant connector account")?; + let profile_id = merchant_connector_account.profile_id.as_ref(); let business_profile = state .store diff --git a/crates/router/src/core/webhooks/webhook_events.rs b/crates/router/src/core/webhooks/webhook_events.rs index e7d0483a76b6..cf4a8d87263c 100644 --- a/crates/router/src/core/webhooks/webhook_events.rs +++ b/crates/router/src/core/webhooks/webhook_events.rs @@ -15,23 +15,23 @@ const INITIAL_DELIVERY_ATTEMPTS_LIST_MAX_LIMIT: i64 = 100; #[derive(Debug)] enum MerchantAccountOrBusinessProfile { MerchantAccount(domain::MerchantAccount), - #[allow(dead_code)] BusinessProfile(domain::BusinessProfile), } #[instrument(skip(state))] pub async fn list_initial_delivery_attempts( state: SessionState, - merchant_id_or_profile_id: String, + merchant_id: common_utils::id_type::MerchantId, constraints: api::webhook_events::EventListConstraints, ) -> RouterResponse> { + let profile_id = constraints.profile_id.clone(); let constraints = api::webhook_events::EventListConstraintsInternal::foreign_try_from(constraints)?; let store = state.store.as_ref(); let key_manager_state = &(&state).into(); let (account, key_store) = - determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; + get_account_and_key_store(state.clone(), merchant_id, profile_id).await?; let events = match constraints { api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => { @@ -110,38 +110,31 @@ pub async fn list_initial_delivery_attempts( #[instrument(skip(state))] pub async fn list_delivery_attempts( state: SessionState, - merchant_id_or_profile_id: String, + merchant_id: common_utils::id_type::MerchantId, initial_attempt_id: String, ) -> RouterResponse> { let store = state.store.as_ref(); - - let (account, key_store) = - determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; let key_manager_state = &(&state).into(); - let events = match account { - MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => { - store - .list_events_by_merchant_id_initial_attempt_id( - key_manager_state, - merchant_account.get_id(), - &initial_attempt_id, - &key_store, - ) - .await - } - MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => { - store - .list_events_by_profile_id_initial_attempt_id( - key_manager_state, - &business_profile.profile_id, - &initial_attempt_id, - &key_store, - ) - .await - } - } - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to list delivery attempts for initial event")?; + + let key_store = store + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &store.get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + + let events = store + .list_events_by_merchant_id_initial_attempt_id( + key_manager_state, + &merchant_id, + &initial_attempt_id, + &key_store, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to list delivery attempts for initial event")?; if events.is_empty() { Err(error_stack::report!( @@ -161,13 +154,20 @@ pub async fn list_delivery_attempts( #[instrument(skip(state))] pub async fn retry_delivery_attempt( state: SessionState, - merchant_id_or_profile_id: String, + merchant_id: common_utils::id_type::MerchantId, event_id: String, ) -> RouterResponse { let store = state.store.as_ref(); let key_manager_state = &(&state).into(); - let (account, key_store) = - determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; + + let key_store = store + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &store.get_master_key().to_vec().into(), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let event_to_retry = store .find_event_by_merchant_id_event_id( @@ -179,25 +179,16 @@ pub async fn retry_delivery_attempt( .await .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?; - let business_profile = match account { - MerchantAccountOrBusinessProfile::MerchantAccount(_) => { - let business_profile_id = event_to_retry - .business_profile_id - .get_required_value("business_profile_id") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to read business profile ID from event to retry")?; - store - .find_business_profile_by_profile_id( - key_manager_state, - &key_store, - &business_profile_id, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to find business profile") - } - MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => Ok(business_profile), - }?; + let business_profile_id = event_to_retry + .business_profile_id + .get_required_value("business_profile_id") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to read business profile ID from event to retry")?; + let business_profile = store + .find_business_profile_by_profile_id(key_manager_state, &key_store, &business_profile_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to find business profile")?; let delivery_attempt = storage::enums::WebhookDeliveryAttempt::ManualRetry; let new_event_id = super::utils::generate_event_id(); @@ -271,77 +262,65 @@ pub async fn retry_delivery_attempt( )) } -async fn determine_identifier_and_get_key_store( +async fn get_account_and_key_store( state: SessionState, - merchant_id_or_profile_id: String, + merchant_id: common_utils::id_type::MerchantId, + profile_id: Option, ) -> errors::RouterResult<(MerchantAccountOrBusinessProfile, domain::MerchantKeyStore)> { let store = state.store.as_ref(); let key_manager_state = &(&state).into(); - let merchant_id = common_utils::id_type::MerchantId::try_from(std::borrow::Cow::from( - merchant_id_or_profile_id.clone(), - )) - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "merchant_id", - })?; - match store + let merchant_key_store = store .get_merchant_key_store_by_merchant_id( key_manager_state, &merchant_id, &store.get_master_key().to_vec().into(), ) .await - { - // Since a merchant key store was found with `merchant_id` = `merchant_id_or_profile_id`, - // `merchant_id_or_profile_id` is a valid merchant ID. - // Find a merchant account having `merchant_id` = `merchant_id_or_profile_id`. - Ok(key_store) => { - let merchant_account = store - .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; - - Ok(( - MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account), - key_store, - )) - } - - /* - // Since no merchant key store was found with `merchant_id` = `merchant_id_or_profile_id`, - // `merchant_id_or_profile_id` is not a valid merchant ID. - // Assuming that `merchant_id_or_profile_id` is a business profile ID, try to find a - // business profile having `profile_id` = `merchant_id_or_profile_id`. - Err(error) if error.current_context().is_db_not_found() => { - router_env::logger::debug!( - ?error, - %merchant_id_or_profile_id, - "Failed to find merchant key store for the specified merchant ID or business profile ID" - ); + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + match profile_id { + // If profile ID is specified, return business profile, since a business profile is more + // specific than a merchant account. + Some(profile_id) => { let business_profile = store - .find_business_profile_by_profile_id(&merchant_id_or_profile_id) + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + &merchant_key_store, + &merchant_id, + &profile_id, + ) .await + .attach_printable_lazy(|| { + format!( + "Failed to find business profile by merchant_id `{merchant_id:?}` and profile_id `{profile_id}`. \ + The merchant_id associated with the business profile `{profile_id}` may be \ + different than the merchant_id specified (`{merchant_id:?}`)." + ) + }) .to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound { - id: merchant_id_or_profile_id, + id: profile_id, })?; - let key_store = store - .get_merchant_key_store_by_merchant_id( + Ok(( + MerchantAccountOrBusinessProfile::BusinessProfile(business_profile), + merchant_key_store, + )) + } + + None => { + let merchant_account = store + .find_merchant_account_by_merchant_id( key_manager_state, - &business_profile.merchant_id, - &store.get_master_key().to_vec().into(), + &merchant_id, + &merchant_key_store, ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; Ok(( - MerchantAccountOrBusinessProfile::BusinessProfile(business_profile), - key_store, + MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account), + merchant_key_store, )) } - */ - Err(error) => Err(error) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to find merchant key store by merchant ID"), } } diff --git a/crates/router/src/db/business_profile.rs b/crates/router/src/db/business_profile.rs index 7b7de43d60ed..ed178f275fd6 100644 --- a/crates/router/src/db/business_profile.rs +++ b/crates/router/src/db/business_profile.rs @@ -36,6 +36,14 @@ where profile_id: &str, ) -> CustomResult; + async fn find_business_profile_by_merchant_id_profile_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + merchant_id: &common_utils::id_type::MerchantId, + profile_id: &str, + ) -> CustomResult; + async fn find_business_profile_by_profile_name_merchant_id( &self, key_manager_state: &KeyManagerState, @@ -112,6 +120,26 @@ impl BusinessProfileInterface for Store { .change_context(errors::StorageError::DecryptionError) } + async fn find_business_profile_by_merchant_id_profile_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + merchant_id: &common_utils::id_type::MerchantId, + profile_id: &str, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + storage::BusinessProfile::find_by_merchant_id_profile_id(&conn, merchant_id, profile_id) + .await + .map_err(|error| report!(errors::StorageError::from(error)))? + .convert( + key_manager_state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + #[instrument(skip_all)] async fn find_business_profile_by_profile_name_merchant_id( &self, @@ -262,6 +290,42 @@ impl BusinessProfileInterface for MockDb { ) } + async fn find_business_profile_by_merchant_id_profile_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + merchant_id: &common_utils::id_type::MerchantId, + profile_id: &str, + ) -> CustomResult { + self.business_profiles + .lock() + .await + .iter() + .find(|business_profile| { + business_profile.merchant_id == *merchant_id + && business_profile.profile_id == profile_id + }) + .cloned() + .async_map(|business_profile| async { + business_profile + .convert( + key_manager_state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()? + .ok_or( + errors::StorageError::ValueNotFound(format!( + "No business profile found for merchant_id = {merchant_id:?} and profile_id = {profile_id}" + )) + .into(), + ) + } + async fn update_business_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, diff --git a/crates/router/src/db/customers.rs b/crates/router/src/db/customers.rs index 9de76567de5a..885e7ffc2dc7 100644 --- a/crates/router/src/db/customers.rs +++ b/crates/router/src/db/customers.rs @@ -1,7 +1,9 @@ use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::ResultExt; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use futures::future::try_join_all; use hyperswitch_domain_models::customer; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use router_env::{instrument, tracing}; use super::MockDb; @@ -22,12 +24,14 @@ where customer::Customer: Conversion, { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn delete_customer_by_customer_id_merchant_id( &self, customer_id: &id_type::CustomerId, merchant_id: &id_type::MerchantId, ) -> CustomResult; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_optional_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -37,6 +41,17 @@ where storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::StorageError>; + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_optional_by_merchant_id_merchant_reference_id( + &self, + state: &KeyManagerState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError>; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[allow(clippy::too_many_arguments)] async fn update_customer_by_customer_id_merchant_id( &self, @@ -49,6 +64,7 @@ where storage_scheme: MerchantStorageScheme, ) -> CustomResult; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -58,6 +74,17 @@ where storage_scheme: MerchantStorageScheme, ) -> CustomResult; + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_customer_by_merchant_reference_id_merchant_id( + &self, + state: &KeyManagerState, + merchant_reference_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult; + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn list_customers_by_merchant_id( &self, state: &KeyManagerState, @@ -72,6 +99,29 @@ where key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult; + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[allow(clippy::too_many_arguments)] + async fn update_customer_by_global_id( + &self, + state: &KeyManagerState, + id: String, + customer: customer::Customer, + merchant_id: &id_type::MerchantId, + customer_update: storage_types::CustomerUpdate, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult; + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_customer_by_global_id( + &self, + state: &KeyManagerState, + id: &String, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult; } #[cfg(feature = "kv_store")] @@ -79,6 +129,7 @@ mod storage { use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use diesel_models::kv; use error_stack::{report, ResultExt}; + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use futures::future::try_join_all; use hyperswitch_domain_models::customer; use masking::PeekInterface; @@ -105,11 +156,11 @@ mod storage { utils::db_utils, }; - #[cfg(all(feature = "v2", feature = "customer_v2"))] #[async_trait::async_trait] impl CustomerInterface for Store { #[instrument(skip_all)] // check customer not found in kv and fallback to db + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_optional_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -178,6 +229,76 @@ mod storage { }) } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_optional_by_merchant_id_merchant_reference_id( + &self, + state: &KeyManagerState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + let database_call = || async { + storage_types::Customer::find_optional_by_merchant_id_merchant_reference_id( + &conn, + customer_id, + merchant_id, + ) + .await + .map_err(|err| report!(errors::StorageError::from(err))) + }; + let storage_scheme = + decide_storage_scheme::<_, diesel_models::Customer>(self, storage_scheme, Op::Find) + .await; + let maybe_customer = match storage_scheme { + MerchantStorageScheme::PostgresOnly => database_call().await, + MerchantStorageScheme::RedisKv => { + let key = PartitionKey::MerchantIdCustomerId { + merchant_id, + customer_id: customer_id.get_string_repr(), + }; + let field = format!("cust_{}", customer_id.get_string_repr()); + Box::pin(db_utils::try_redis_get_else_try_database_get( + // check for ValueNotFound + async { + kv_wrapper( + self, + KvOperation::::HGet(&field), + key, + ) + .await? + .try_into_hget() + .map(Some) + }, + database_call, + )) + .await + } + }?; + + let maybe_result = maybe_customer + .async_map(|c| async { + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()?; + + maybe_result.map_or(Ok(None), |customer: domain::Customer| match customer.name { + Some(ref name) if name.peek() == REDACTED => { + Err(errors::StorageError::CustomerRedacted)? + } + _ => Ok(Some(customer)), + }) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, @@ -260,6 +381,72 @@ mod storage { .change_context(errors::StorageError::DecryptionError) } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[instrument(skip_all)] + async fn find_customer_by_merchant_reference_id_merchant_id( + &self, + state: &KeyManagerState, + merchant_reference_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + let database_call = || async { + storage_types::Customer::find_by_merchant_reference_id_merchant_id( + &conn, + merchant_reference_id, + merchant_id, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + }; + let storage_scheme = + decide_storage_scheme::<_, diesel_models::Customer>(self, storage_scheme, Op::Find) + .await; + let customer = match storage_scheme { + MerchantStorageScheme::PostgresOnly => database_call().await, + MerchantStorageScheme::RedisKv => { + let key = PartitionKey::MerchantIdMerchantReferenceId { + merchant_id, + merchant_reference_id: merchant_reference_id.get_string_repr(), + }; + let field = format!("cust_{}", merchant_reference_id.get_string_repr()); + Box::pin(db_utils::try_redis_get_else_try_database_get( + async { + kv_wrapper( + self, + KvOperation::::HGet(&field), + key, + ) + .await? + .try_into_hget() + }, + database_call, + )) + .await + } + }?; + + let result: customer::Customer = customer + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?; + //.await + + match result.name { + Some(ref name) if name.peek() == REDACTED => { + Err(errors::StorageError::CustomerRedacted)? + } + _ => Ok(result), + } + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, @@ -324,6 +511,7 @@ mod storage { } } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn list_customers_by_merchant_id( &self, @@ -355,6 +543,82 @@ mod storage { Ok(customers) } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[instrument(skip_all)] + async fn insert_customer( + &self, + customer_data: customer::Customer, + state: &KeyManagerState, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let id = customer_data.id.clone(); + let mut new_customer = customer_data + .construct_new() + .await + .change_context(errors::StorageError::EncryptionError)?; + let storage_scheme = decide_storage_scheme::<_, diesel_models::Customer>( + self, + storage_scheme, + Op::Insert, + ) + .await; + new_customer.update_storage_scheme(storage_scheme); + let create_customer = match storage_scheme { + MerchantStorageScheme::PostgresOnly => { + let conn = connection::pg_connection_write(self).await?; + new_customer + .insert(&conn) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + MerchantStorageScheme::RedisKv => { + let key = PartitionKey::GlobalId { id: &id }; + let field = format!("cust_{}", id); + + let redis_entry = kv::TypedSql { + op: kv::DBOperation::Insert { + insertable: kv::Insertable::Customer(new_customer.clone()), + }, + }; + let storage_customer = new_customer.into(); + + match kv_wrapper::( + self, + KvOperation::HSetNx::( + &field, + &storage_customer, + redis_entry, + ), + key, + ) + .await + .change_context(errors::StorageError::KVError)? + .try_into_hsetnx() + { + Ok(redis_interface::HsetnxReply::KeyNotSet) => { + Err(report!(errors::StorageError::DuplicateValue { + entity: "customer", + key: Some(id.to_string()), + })) + } + Ok(redis_interface::HsetnxReply::KeySet) => Ok(storage_customer), + Err(er) => Err(er).change_context(errors::StorageError::KVError), + } + } + }?; + + create_customer + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn insert_customer( &self, @@ -363,7 +627,7 @@ mod storage { key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { - let customer_id = customer_data.get_customer_id().clone(); + let customer_id = customer_data.customer_id.clone(); let merchant_id = customer_data.merchant_id.clone(); let mut new_customer = customer_data .construct_new() @@ -433,52 +697,48 @@ mod storage { .change_context(errors::StorageError::DecryptionError) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn delete_customer_by_customer_id_merchant_id( &self, - _customer_id: &id_type::CustomerId, - _merchant_id: &id_type::MerchantId, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, ) -> CustomResult { - todo!() + let conn = connection::pg_connection_write(self).await?; + storage_types::Customer::delete_by_customer_id_merchant_id( + &conn, + customer_id, + merchant_id, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) } - } - #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] - #[async_trait::async_trait] - impl CustomerInterface for Store { + #[cfg(all(feature = "v2", feature = "customer_v2"))] #[instrument(skip_all)] - // check customer not found in kv and fallback to db - async fn find_customer_optional_by_customer_id_merchant_id( + async fn find_customer_by_global_id( &self, state: &KeyManagerState, - customer_id: &id_type::CustomerId, - merchant_id: &id_type::MerchantId, + id: &String, + _merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; let database_call = || async { - storage_types::Customer::find_optional_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id, - ) - .await - .map_err(|err| report!(errors::StorageError::from(err))) + storage_types::Customer::find_by_global_id(&conn, id) + .await + .map_err(|error| report!(errors::StorageError::from(error))) }; let storage_scheme = decide_storage_scheme::<_, diesel_models::Customer>(self, storage_scheme, Op::Find) .await; - let maybe_customer = match storage_scheme { + let customer = match storage_scheme { MerchantStorageScheme::PostgresOnly => database_call().await, MerchantStorageScheme::RedisKv => { - let key = PartitionKey::MerchantIdCustomerId { - merchant_id, - customer_id: customer_id.get_string_repr(), - }; - let field = format!("cust_{}", customer_id.get_string_repr()); + let key = PartitionKey::GlobalId { id }; + let field = format!("cust_{}", id); Box::pin(db_utils::try_redis_get_else_try_database_get( - // check for ValueNotFound async { kv_wrapper( self, @@ -487,7 +747,6 @@ mod storage { ) .await? .try_into_hget() - .map(Some) }, database_call, )) @@ -495,34 +754,32 @@ mod storage { } }?; - let maybe_result = maybe_customer - .async_map(|c| async { - c.convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError) - }) + let result: customer::Customer = customer + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) .await - .transpose()?; + .change_context(errors::StorageError::DecryptionError)?; + //.await - maybe_result.map_or(Ok(None), |customer: domain::Customer| match customer.name { + match result.name { Some(ref name) if name.peek() == REDACTED => { Err(errors::StorageError::CustomerRedacted)? } - _ => Ok(Some(customer)), - }) + _ => Ok(result), + } } + #[cfg(all(feature = "v2", feature = "customer_v2"))] #[instrument(skip_all)] - async fn update_customer_by_customer_id_merchant_id( + async fn update_customer_by_global_id( &self, state: &KeyManagerState, - customer_id: id_type::CustomerId, - merchant_id: id_type::MerchantId, + id: String, customer: customer::Customer, + _merchant_id: &id_type::MerchantId, customer_update: storage_types::CustomerUpdate, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -532,20 +789,16 @@ mod storage { .await .change_context(errors::StorageError::EncryptionError)?; let database_call = || async { - storage_types::Customer::update_by_customer_id_merchant_id( + storage_types::Customer::update_by_id( &conn, - customer_id.clone(), - merchant_id.clone(), + id.clone(), customer_update.clone().into(), ) .await .map_err(|error| report!(errors::StorageError::from(error))) }; - let key = PartitionKey::MerchantIdCustomerId { - merchant_id: &merchant_id, - customer_id: customer_id.get_string_repr(), - }; - let field = format!("cust_{}", customer_id.get_string_repr()); + let key = PartitionKey::GlobalId { id: &id }; + let field = format!("cust_{}", id); let storage_scheme = decide_storage_scheme::<_, diesel_models::Customer>( self, storage_scheme, @@ -597,195 +850,6 @@ mod storage { .await .change_context(errors::StorageError::DecryptionError) } - - #[instrument(skip_all)] - async fn find_customer_by_customer_id_merchant_id( - &self, - state: &KeyManagerState, - customer_id: &id_type::CustomerId, - merchant_id: &id_type::MerchantId, - key_store: &domain::MerchantKeyStore, - storage_scheme: MerchantStorageScheme, - ) -> CustomResult { - let conn = connection::pg_connection_read(self).await?; - let database_call = || async { - storage_types::Customer::find_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id, - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - }; - let storage_scheme = - decide_storage_scheme::<_, diesel_models::Customer>(self, storage_scheme, Op::Find) - .await; - let customer = match storage_scheme { - MerchantStorageScheme::PostgresOnly => database_call().await, - MerchantStorageScheme::RedisKv => { - let key = PartitionKey::MerchantIdCustomerId { - merchant_id, - customer_id: customer_id.get_string_repr(), - }; - let field = format!("cust_{}", customer_id.get_string_repr()); - Box::pin(db_utils::try_redis_get_else_try_database_get( - async { - kv_wrapper( - self, - KvOperation::::HGet(&field), - key, - ) - .await? - .try_into_hget() - }, - database_call, - )) - .await - } - }?; - - let result: customer::Customer = customer - .convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError)?; - //.await - - match result.name { - Some(ref name) if name.peek() == REDACTED => { - Err(errors::StorageError::CustomerRedacted)? - } - _ => Ok(result), - } - } - - #[instrument(skip_all)] - async fn list_customers_by_merchant_id( - &self, - state: &KeyManagerState, - merchant_id: &id_type::MerchantId, - key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { - let conn = connection::pg_connection_read(self).await?; - - let encrypted_customers = - storage_types::Customer::list_by_merchant_id(&conn, merchant_id) - .await - .map_err(|error| report!(errors::StorageError::from(error)))?; - - let customers = try_join_all(encrypted_customers.into_iter().map( - |encrypted_customer| async { - encrypted_customer - .convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError) - }, - )) - .await?; - - Ok(customers) - } - - #[instrument(skip_all)] - async fn insert_customer( - &self, - customer_data: customer::Customer, - state: &KeyManagerState, - key_store: &domain::MerchantKeyStore, - storage_scheme: MerchantStorageScheme, - ) -> CustomResult { - let customer_id = customer_data.get_customer_id().clone(); - let merchant_id = customer_data.merchant_id.clone(); - let mut new_customer = customer_data - .construct_new() - .await - .change_context(errors::StorageError::EncryptionError)?; - let storage_scheme = decide_storage_scheme::<_, diesel_models::Customer>( - self, - storage_scheme, - Op::Insert, - ) - .await; - new_customer.update_storage_scheme(storage_scheme); - let create_customer = match storage_scheme { - MerchantStorageScheme::PostgresOnly => { - let conn = connection::pg_connection_write(self).await?; - new_customer - .insert(&conn) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - } - MerchantStorageScheme::RedisKv => { - let key = PartitionKey::MerchantIdCustomerId { - merchant_id: &merchant_id, - customer_id: customer_id.get_string_repr(), - }; - let field = format!("cust_{}", customer_id.get_string_repr()); - - let redis_entry = kv::TypedSql { - op: kv::DBOperation::Insert { - insertable: kv::Insertable::Customer(new_customer.clone()), - }, - }; - let storage_customer = new_customer.into(); - - match kv_wrapper::( - self, - KvOperation::HSetNx::( - &field, - &storage_customer, - redis_entry, - ), - key, - ) - .await - .change_context(errors::StorageError::KVError)? - .try_into_hsetnx() - { - Ok(redis_interface::HsetnxReply::KeyNotSet) => { - Err(report!(errors::StorageError::DuplicateValue { - entity: "customer", - key: Some(customer_id.get_string_repr().to_string()), - })) - } - Ok(redis_interface::HsetnxReply::KeySet) => Ok(storage_customer), - Err(er) => Err(er).change_context(errors::StorageError::KVError), - } - } - }?; - - create_customer - .convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError) - } - - #[instrument(skip_all)] - async fn delete_customer_by_customer_id_merchant_id( - &self, - customer_id: &id_type::CustomerId, - merchant_id: &id_type::MerchantId, - ) -> CustomResult { - let conn = connection::pg_connection_write(self).await?; - storage_types::Customer::delete_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id, - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - } } } @@ -815,11 +879,49 @@ mod storage { }, }; - #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] - #[async_trait::async_trait] - impl CustomerInterface for Store { + #[async_trait::async_trait] + impl CustomerInterface for Store { + #[instrument(skip_all)] + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] + async fn find_customer_optional_by_customer_id_merchant_id( + &self, + state: &KeyManagerState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + let conn = connection::pg_connection_read(self).await?; + let maybe_customer: Option = + storage_types::Customer::find_optional_by_customer_id_merchant_id( + &conn, + customer_id, + merchant_id, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error)))? + .async_map(|c| async { + c.convert(state, key_store.key.get_inner(), merchant_id.clone().into()) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose()?; + maybe_customer.map_or(Ok(None), |customer| { + // in the future, once #![feature(is_some_and)] is stable, we can make this more concise: + // `if customer.name.is_some_and(|ref name| name == REDACTED) ...` + match customer.name { + Some(ref name) if name.peek() == REDACTED => { + Err(errors::StorageError::CustomerRedacted)? + } + _ => Ok(Some(customer)), + } + }) + } + #[instrument(skip_all)] - async fn find_customer_optional_by_customer_id_merchant_id( + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_optional_by_merchant_id_merchant_reference_id( &self, state: &KeyManagerState, customer_id: &id_type::CustomerId, @@ -855,6 +957,7 @@ mod storage { }) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, @@ -883,6 +986,7 @@ mod storage { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, @@ -915,6 +1019,40 @@ mod storage { } } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[instrument(skip_all)] + async fn find_customer_by_merchant_reference_id_merchant_id( + &self, + state: &KeyManagerState, + merchant_reference_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let conn = connection::pg_connection_read(self).await?; + let customer: customer::Customer = + storage_types::Customer::find_by_merchant_reference_id_merchant_id( + &conn, + merchant_reference_id, + merchant_id, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + .async_and_then(|c| async { + c.convert(state, key_store.key.get_inner(), merchant_id.clone().into()) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await?; + match customer.name { + Some(ref name) if name.peek() == REDACTED => { + Err(errors::StorageError::CustomerRedacted)? + } + _ => Ok(customer), + } + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn list_customers_by_merchant_id( &self, @@ -970,6 +1108,7 @@ mod storage { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn delete_customer_by_customer_id_merchant_id( &self, @@ -985,100 +1124,52 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error))) } - } - #[cfg(all(feature = "v2", feature = "customer_v2"))] - #[async_trait::async_trait] - impl CustomerInterface for Store { - #[instrument(skip_all)] - async fn find_customer_optional_by_customer_id_merchant_id( + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[allow(clippy::too_many_arguments)] + async fn update_customer_by_global_id( &self, state: &KeyManagerState, - customer_id: &id_type::CustomerId, + id: String, + customer: customer::Customer, merchant_id: &id_type::MerchantId, + customer_update: storage_types::CustomerUpdate, key_store: &domain::MerchantKeyStore, - _storage_scheme: MerchantStorageScheme, - ) -> CustomResult, errors::StorageError> { - let conn = connection::pg_connection_read(self).await?; - let maybe_customer: Option = - storage_types::Customer::find_optional_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id, - ) + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage_types::Customer::update_by_global_id(&conn, id, customer_update.into()) .await - .map_err(|error| report!(errors::StorageError::from(error)))? - .async_map(|c| async { - c.convert(state, key_store.key.get_inner(), merchant_id.to_string()) + .map_err(|error| report!(errors::StorageError::from(error))) + .async_and_then(|c| async { + c.convert(state, key_store.key.get_inner(), merchant_id) .await .change_context(errors::StorageError::DecryptionError) }) .await - .transpose()?; - maybe_customer.map_or(Ok(None), |customer| { - // in the future, once #![feature(is_some_and)] is stable, we can make this more concise: - // `if customer.name.is_some_and(|ref name| name == REDACTED) ...` - match customer.name { - Some(ref name) if name.peek() == REDACTED => { - Err(errors::StorageError::CustomerRedacted)? - } - _ => Ok(Some(customer)), - } - }) - } - - #[instrument(skip_all)] - async fn update_customer_by_customer_id_merchant_id( - &self, - state: &KeyManagerState, - customer_id: id_type::CustomerId, - merchant_id: id_type::MerchantId, - _customer: customer::Customer, - customer_update: storage_types::CustomerUpdate, - key_store: &domain::MerchantKeyStore, - _storage_scheme: MerchantStorageScheme, - ) -> CustomResult { - let conn = connection::pg_connection_write(self).await?; - storage_types::Customer::update_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id.clone(), - customer_update.into(), - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - .async_and_then(|c| async { - c.convert(state, key_store.key.get_inner(), merchant_id) - .await - .change_context(errors::StorageError::DecryptionError) - }) - .await } + #[cfg(all(feature = "v2", feature = "customer_v2"))] #[instrument(skip_all)] - async fn find_customer_by_customer_id_merchant_id( + async fn find_customer_by_global_id( &self, state: &KeyManagerState, - customer_id: &id_type::CustomerId, + id: &String, merchant_id: &id_type::MerchantId, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, ) -> CustomResult { let conn = connection::pg_connection_read(self).await?; let customer: customer::Customer = - storage_types::Customer::find_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id, - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - .async_and_then(|c| async { - c.convert(state, key_store.key.get_inner(), merchant_id.to_string()) - .await - .change_context(errors::StorageError::DecryptionError) - }) - .await?; + storage_types::Customer::find_by_global_id(&conn, customer_id, merchant_id) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + .async_and_then(|c| async { + c.convert(state, key_store.key.get_inner(), merchant_id.clone().into()) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await?; match customer.name { Some(ref name) if name.peek() == REDACTED => { Err(errors::StorageError::CustomerRedacted)? @@ -1086,83 +1177,13 @@ mod storage { _ => Ok(customer), } } - - #[instrument(skip_all)] - async fn list_customers_by_merchant_id( - &self, - state: &KeyManagerState, - merchant_id: &id_type::MerchantId, - key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { - let conn = connection::pg_connection_read(self).await?; - - let encrypted_customers = - storage_types::Customer::list_by_merchant_id(&conn, merchant_id) - .await - .map_err(|error| report!(errors::StorageError::from(error)))?; - - let customers = try_join_all(encrypted_customers.into_iter().map( - |encrypted_customer| async { - encrypted_customer - .convert(state, key_store.key.get_inner(), merchant_id.to_string()) - .await - .change_context(errors::StorageError::DecryptionError) - }, - )) - .await?; - - Ok(customers) - } - - #[instrument(skip_all)] - async fn insert_customer( - &self, - customer_data: customer::Customer, - state: &KeyManagerState, - key_store: &domain::MerchantKeyStore, - _storage_scheme: MerchantStorageScheme, - ) -> CustomResult { - let conn = connection::pg_connection_write(self).await?; - customer_data - .construct_new() - .await - .change_context(errors::StorageError::EncryptionError)? - .insert(&conn) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - .async_and_then(|c| async { - c.convert( - state, - key_store.key.get_inner(), - key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError) - }) - .await - } - - #[instrument(skip_all)] - async fn delete_customer_by_customer_id_merchant_id( - &self, - customer_id: &id_type::CustomerId, - merchant_id: &id_type::MerchantId, - ) -> CustomResult { - let conn = connection::pg_connection_write(self).await?; - storage_types::Customer::delete_by_customer_id_merchant_id( - &conn, - customer_id, - merchant_id, - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - } } } #[async_trait::async_trait] impl CustomerInterface for MockDb { #[allow(clippy::panic)] + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_optional_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -1175,7 +1196,38 @@ impl CustomerInterface for MockDb { let customer = customers .iter() .find(|customer| { - customer.get_customer_id() == *customer_id && customer.merchant_id == *merchant_id + customer.get_customer_id() == *customer_id && &customer.merchant_id == merchant_id + }) + .cloned(); + customer + .async_map(|c| async { + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone().into(), + ) + .await + .change_context(errors::StorageError::DecryptionError) + }) + .await + .transpose() + } + + #[allow(clippy::panic)] + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_optional_by_merchant_id_merchant_reference_id( + &self, + state: &KeyManagerState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + let customers = self.customers.lock().await; + let customer = customers + .iter() + .find(|customer| { + customer.get_customer_id() == *customer_id && &customer.merchant_id == merchant_id }) .cloned(); customer @@ -1192,6 +1244,7 @@ impl CustomerInterface for MockDb { .transpose() } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn list_customers_by_merchant_id( &self, state: &KeyManagerState, @@ -1221,6 +1274,7 @@ impl CustomerInterface for MockDb { Ok(customers) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, @@ -1236,6 +1290,7 @@ impl CustomerInterface for MockDb { Err(errors::StorageError::MockDbError)? } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_by_customer_id_merchant_id( &self, _state: &KeyManagerState, @@ -1248,6 +1303,19 @@ impl CustomerInterface for MockDb { Err(errors::StorageError::MockDbError)? } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_customer_by_merchant_reference_id_merchant_id( + &self, + _state: &KeyManagerState, + _merchant_reference_id: &id_type::CustomerId, + _merchant_id: &id_type::MerchantId, + _key_store: &domain::MerchantKeyStore, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + // [#172]: Implement function for `MockDb` + Err(errors::StorageError::MockDbError)? + } + #[allow(clippy::panic)] async fn insert_customer( &self, @@ -1274,6 +1342,7 @@ impl CustomerInterface for MockDb { .change_context(errors::StorageError::DecryptionError) } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn delete_customer_by_customer_id_merchant_id( &self, _customer_id: &id_type::CustomerId, @@ -1282,4 +1351,33 @@ impl CustomerInterface for MockDb { // [#172]: Implement function for `MockDb` Err(errors::StorageError::MockDbError)? } + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + #[allow(clippy::too_many_arguments)] + async fn update_customer_by_global_id( + &self, + _state: &KeyManagerState, + _id: String, + _customer: customer::Customer, + _merchant_id: &id_type::MerchantId, + _customer_update: storage_types::CustomerUpdate, + _key_store: &domain::MerchantKeyStore, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + // [#172]: Implement function for `MockDb` + Err(errors::StorageError::MockDbError)? + } + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_customer_by_global_id( + &self, + _state: &KeyManagerState, + _id: &String, + _merchant_id: &id_type::MerchantId, + _key_store: &domain::MerchantKeyStore, + _storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + // [#172]: Implement function for `MockDb` + Err(errors::StorageError::MockDbError)? + } } diff --git a/crates/router/src/db/events.rs b/crates/router/src/db/events.rs index 3b0f789cf044..edd1eecff958 100644 --- a/crates/router/src/db/events.rs +++ b/crates/router/src/db/events.rs @@ -84,14 +84,6 @@ where merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; - async fn list_events_by_profile_id_initial_attempt_id( - &self, - state: &KeyManagerState, - profile_id: &str, - initial_attempt_id: &str, - merchant_key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError>; - async fn update_event_by_merchant_id_event_id( &self, state: &KeyManagerState, @@ -338,37 +330,6 @@ impl EventInterface for Store { .await } - #[instrument(skip_all)] - async fn list_events_by_profile_id_initial_attempt_id( - &self, - state: &KeyManagerState, - profile_id: &str, - initial_attempt_id: &str, - merchant_key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { - let conn = connection::pg_connection_read(self).await?; - storage::Event::list_by_profile_id_initial_attempt_id(&conn, profile_id, initial_attempt_id) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - .async_and_then(|events| async { - let mut domain_events = Vec::with_capacity(events.len()); - for event in events.into_iter() { - domain_events.push( - event - .convert( - state, - merchant_key_store.key.get_inner(), - merchant_key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError)?, - ); - } - Ok(domain_events) - }) - .await - } - #[instrument(skip_all)] async fn update_event_by_merchant_id_event_id( &self, @@ -695,39 +656,6 @@ impl EventInterface for MockDb { Ok(domain_events) } - async fn list_events_by_profile_id_initial_attempt_id( - &self, - state: &KeyManagerState, - profile_id: &str, - initial_attempt_id: &str, - merchant_key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { - let locked_events = self.events.lock().await; - let events = locked_events - .iter() - .filter(|event| { - event.business_profile_id == Some(profile_id.to_owned()) - && event.initial_attempt_id == Some(initial_attempt_id.to_owned()) - }) - .cloned() - .collect::>(); - let mut domain_events = Vec::with_capacity(events.len()); - - for event in events { - let domain_event = event - .convert( - state, - merchant_key_store.key.get_inner(), - merchant_key_store.merchant_id.clone().into(), - ) - .await - .change_context(errors::StorageError::DecryptionError)?; - domain_events.push(domain_event); - } - - Ok(domain_events) - } - async fn update_event_by_merchant_id_event_id( &self, state: &KeyManagerState, diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index 31b9ffcd7f85..2f5bf8b67918 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -343,6 +343,7 @@ impl ConfigInterface for KafkaStore { #[async_trait::async_trait] impl CustomerInterface for KafkaStore { + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn delete_customer_by_customer_id_merchant_id( &self, customer_id: &id_type::CustomerId, @@ -353,6 +354,7 @@ impl CustomerInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_optional_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -372,6 +374,27 @@ impl CustomerInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_optional_by_merchant_id_merchant_reference_id( + &self, + state: &KeyManagerState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult, errors::StorageError> { + self.diesel_store + .find_optional_by_merchant_id_merchant_reference_id( + state, + customer_id, + merchant_id, + key_store, + storage_scheme, + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn update_customer_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -395,6 +418,31 @@ impl CustomerInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn update_customer_by_global_id( + &self, + state: &KeyManagerState, + id: String, + customer: domain::Customer, + merchant_id: &id_type::MerchantId, + customer_update: storage::CustomerUpdate, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + self.diesel_store + .update_customer_by_global_id( + state, + id, + customer, + merchant_id, + customer_update, + key_store, + storage_scheme, + ) + .await + } + + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn list_customers_by_merchant_id( &self, state: &KeyManagerState, @@ -406,6 +454,7 @@ impl CustomerInterface for KafkaStore { .await } + #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] async fn find_customer_by_customer_id_merchant_id( &self, state: &KeyManagerState, @@ -425,6 +474,40 @@ impl CustomerInterface for KafkaStore { .await } + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_customer_by_merchant_reference_id_merchant_id( + &self, + state: &KeyManagerState, + merchant_reference_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + self.diesel_store + .find_customer_by_merchant_reference_id_merchant_id( + state, + merchant_reference_id, + merchant_id, + key_store, + storage_scheme, + ) + .await + } + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + async fn find_customer_by_global_id( + &self, + state: &KeyManagerState, + id: &String, + merchant_id: &id_type::MerchantId, + key_store: &domain::MerchantKeyStore, + storage_scheme: MerchantStorageScheme, + ) -> CustomResult { + self.diesel_store + .find_customer_by_global_id(state, id, merchant_id, key_store, storage_scheme) + .await + } + async fn insert_customer( &self, customer_data: domain::Customer, @@ -668,23 +751,6 @@ impl EventInterface for KafkaStore { .await } - async fn list_events_by_profile_id_initial_attempt_id( - &self, - state: &KeyManagerState, - profile_id: &str, - initial_attempt_id: &str, - merchant_key_store: &domain::MerchantKeyStore, - ) -> CustomResult, errors::StorageError> { - self.diesel_store - .list_events_by_profile_id_initial_attempt_id( - state, - profile_id, - initial_attempt_id, - merchant_key_store, - ) - .await - } - async fn update_event_by_merchant_id_event_id( &self, state: &KeyManagerState, @@ -1909,6 +1975,39 @@ impl PayoutsInterface for KafkaStore { .filter_payouts_by_time_range_constraints(merchant_id, time_range, storage_scheme) .await } + + #[cfg(feature = "olap")] + async fn get_total_count_of_filtered_payouts( + &self, + merchant_id: &id_type::MerchantId, + active_payout_ids: &[String], + connector: Option>, + currency: Option>, + status: Option>, + payout_method: Option>, + ) -> CustomResult { + self.diesel_store + .get_total_count_of_filtered_payouts( + merchant_id, + active_payout_ids, + connector, + currency, + status, + payout_method, + ) + .await + } + + #[cfg(feature = "olap")] + async fn filter_active_payout_ids_by_constraints( + &self, + merchant_id: &id_type::MerchantId, + constraints: &hyperswitch_domain_models::payouts::PayoutFetchConstraints, + ) -> CustomResult, errors::DataStorageError> { + self.diesel_store + .filter_active_payout_ids_by_constraints(merchant_id, constraints) + .await + } } #[async_trait::async_trait] @@ -2270,6 +2369,23 @@ impl BusinessProfileInterface for KafkaStore { .await } + async fn find_business_profile_by_merchant_id_profile_id( + &self, + key_manager_state: &KeyManagerState, + merchant_key_store: &domain::MerchantKeyStore, + merchant_id: &id_type::MerchantId, + profile_id: &str, + ) -> CustomResult { + self.diesel_store + .find_business_profile_by_merchant_id_profile_id( + key_manager_state, + merchant_key_store, + merchant_id, + profile_id, + ) + .await + } + async fn update_business_profile_by_profile_id( &self, key_manager_state: &KeyManagerState, @@ -2645,24 +2761,51 @@ impl UserRoleInterface for KafkaStore { .await } - async fn delete_user_role_by_user_id_merchant_id( + async fn list_user_roles_by_user_id( + &self, + user_id: &str, + version: enums::UserRoleVersion, + ) -> CustomResult, errors::StorageError> { + self.diesel_store + .list_user_roles_by_user_id(user_id, version) + .await + } + + async fn find_user_role_by_user_id_and_lineage( &self, user_id: &str, + org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, version: enums::UserRoleVersion, - ) -> CustomResult { + ) -> CustomResult { self.diesel_store - .delete_user_role_by_user_id_merchant_id(user_id, merchant_id, version) + .find_user_role_by_user_id_and_lineage( + user_id, + org_id, + merchant_id, + profile_id, + version, + ) .await } - async fn list_user_roles_by_user_id( + async fn delete_user_role_by_user_id_and_lineage( &self, user_id: &str, + org_id: &id_type::OrganizationId, + merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, version: enums::UserRoleVersion, - ) -> CustomResult, errors::StorageError> { + ) -> CustomResult { self.diesel_store - .list_user_roles_by_user_id(user_id, version) + .delete_user_role_by_user_id_and_lineage( + user_id, + org_id, + merchant_id, + profile_id, + version, + ) .await } diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index d628ad0f2513..f039728de46f 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -568,9 +568,7 @@ impl MerchantConnectorAccountInterface for Store { merchant_connector_accounts { let _connector_name = merchant_connector_account.connector_name.clone(); - let _profile_id = merchant_connector_account.profile_id.clone().ok_or( - errors::StorageError::ValueNotFound("profile_id".to_string()), - )?; + let _profile_id = merchant_connector_account.profile_id.clone(); let _merchant_id = merchant_connector_account.merchant_id.clone(); let _merchant_connector_id = merchant_connector_account.get_id().clone(); @@ -653,12 +651,7 @@ impl MerchantConnectorAccountInterface for Store { key_store: &domain::MerchantKeyStore, ) -> CustomResult { let _connector_name = this.connector_name.clone(); - let _profile_id = this - .profile_id - .clone() - .ok_or(errors::StorageError::ValueNotFound( - "profile_id".to_string(), - ))?; + let _profile_id = this.profile_id.clone(); let _merchant_id = this.merchant_id.clone(); let _merchant_connector_id = this.merchant_connector_id.clone(); @@ -732,12 +725,7 @@ impl MerchantConnectorAccountInterface for Store { key_store: &domain::MerchantKeyStore, ) -> CustomResult { let _connector_name = this.connector_name.clone(); - let _profile_id = this - .profile_id - .clone() - .ok_or(errors::StorageError::ValueNotFound( - "profile_id".to_string(), - ))?; + let _profile_id = this.profile_id.clone(); let _merchant_id = this.merchant_id.clone(); let _merchant_connector_id = this.get_id().clone(); @@ -896,9 +884,7 @@ impl MerchantConnectorAccountInterface for Store { .await .map_err(|error| report!(errors::StorageError::from(error)))?; - let _profile_id = mca.profile_id.ok_or(errors::StorageError::ValueNotFound( - "profile_id".to_string(), - ))?; + let _profile_id = mca.profile_id; cache::publish_and_redact_multiple( self, @@ -1173,12 +1159,13 @@ impl MerchantConnectorAccountInterface for MockDb { created_at: common_utils::date_time::now(), modified_at: common_utils::date_time::now(), connector_webhook_details: t.connector_webhook_details, - profile_id: t.profile_id, + profile_id: Some(t.profile_id), applepay_verified_domains: t.applepay_verified_domains, pm_auth_config: t.pm_auth_config, status: t.status, connector_wallets_details: t.connector_wallets_details.map(Encryption::from), additional_merchant_data: t.additional_merchant_data.map(|data| data.into()), + version: t.version, }; accounts.push(account.clone()); account @@ -1219,6 +1206,7 @@ impl MerchantConnectorAccountInterface for MockDb { status: t.status, connector_wallets_details: t.connector_wallets_details.map(Encryption::from), additional_merchant_data: t.additional_merchant_data.map(|data| data.into()), + version: t.version, }; accounts.push(account.clone()); account @@ -1548,7 +1536,7 @@ mod merchant_connector_account_cache_tests { created_at: date_time::now(), modified_at: date_time::now(), connector_webhook_details: None, - profile_id: Some(profile_id.to_string()), + profile_id: profile_id.to_string(), applepay_verified_domains: None, pm_auth_config: None, status: common_enums::ConnectorStatus::Inactive, @@ -1565,6 +1553,7 @@ mod merchant_connector_account_cache_tests { .unwrap(), ), additional_merchant_data: None, + version: hyperswitch_domain_models::consts::API_VERSION, }; db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key) @@ -1712,7 +1701,7 @@ mod merchant_connector_account_cache_tests { created_at: date_time::now(), modified_at: date_time::now(), connector_webhook_details: None, - profile_id: Some(profile_id.to_string()), + profile_id: profile_id.to_string(), applepay_verified_domains: None, pm_auth_config: None, status: common_enums::ConnectorStatus::Inactive, @@ -1729,6 +1718,7 @@ mod merchant_connector_account_cache_tests { .unwrap(), ), additional_merchant_data: None, + version: hyperswitch_domain_models::consts::API_VERSION, }; db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key) diff --git a/crates/router/src/db/user_role.rs b/crates/router/src/db/user_role.rs index 203860728c06..05f37d0ee729 100644 --- a/crates/router/src/db/user_role.rs +++ b/crates/router/src/db/user_role.rs @@ -49,24 +49,35 @@ pub trait UserRoleInterface { version: enums::UserRoleVersion, ) -> CustomResult, errors::StorageError>; - async fn delete_user_role_by_user_id_merchant_id( + async fn list_user_roles_by_user_id( &self, user_id: &str, + version: enums::UserRoleVersion, + ) -> CustomResult, errors::StorageError>; + + async fn list_user_roles_by_merchant_id( + &self, merchant_id: &id_type::MerchantId, version: enums::UserRoleVersion, - ) -> CustomResult; + ) -> CustomResult, errors::StorageError>; - async fn list_user_roles_by_user_id( + async fn find_user_role_by_user_id_and_lineage( &self, user_id: &str, + org_id: &id_type::OrganizationId, + merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, version: enums::UserRoleVersion, - ) -> CustomResult, errors::StorageError>; + ) -> CustomResult; - async fn list_user_roles_by_merchant_id( + async fn delete_user_role_by_user_id_and_lineage( &self, + user_id: &str, + org_id: &id_type::OrganizationId, merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, version: enums::UserRoleVersion, - ) -> CustomResult, errors::StorageError>; + ) -> CustomResult; async fn transfer_org_ownership_between_users( &self, @@ -161,25 +172,6 @@ impl UserRoleInterface for Store { .map_err(|error| report!(errors::StorageError::from(error))) } - #[instrument(skip_all)] - async fn delete_user_role_by_user_id_merchant_id( - &self, - user_id: &str, - merchant_id: &id_type::MerchantId, - version: enums::UserRoleVersion, - ) -> CustomResult { - let conn = connection::pg_connection_write(self).await?; - - storage::UserRole::delete_by_user_id_merchant_id( - &conn, - user_id.to_owned(), - merchant_id.to_owned(), - version, - ) - .await - .map_err(|error| report!(errors::StorageError::from(error))) - } - #[instrument(skip_all)] async fn list_user_roles_by_user_id( &self, @@ -204,6 +196,50 @@ impl UserRoleInterface for Store { .map_err(|error| report!(errors::StorageError::from(error))) } + #[instrument(skip_all)] + async fn find_user_role_by_user_id_and_lineage( + &self, + user_id: &str, + org_id: &id_type::OrganizationId, + merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, + version: enums::UserRoleVersion, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::UserRole::find_by_user_id_org_id_merchant_id_profile_id( + &conn, + user_id.to_owned(), + org_id.to_owned(), + merchant_id.to_owned(), + profile_id.cloned(), + version, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + + #[instrument(skip_all)] + async fn delete_user_role_by_user_id_and_lineage( + &self, + user_id: &str, + org_id: &id_type::OrganizationId, + merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, + version: enums::UserRoleVersion, + ) -> CustomResult { + let conn = connection::pg_connection_write(self).await?; + storage::UserRole::delete_by_user_id_org_id_merchant_id_profile_id( + &conn, + user_id.to_owned(), + org_id.to_owned(), + merchant_id.to_owned(), + profile_id.cloned(), + version, + ) + .await + .map_err(|error| report!(errors::StorageError::from(error))) + } + #[instrument(skip_all)] async fn transfer_org_ownership_between_users( &self, @@ -565,32 +601,6 @@ impl UserRoleInterface for MockDb { Ok(()) } - async fn delete_user_role_by_user_id_merchant_id( - &self, - user_id: &str, - merchant_id: &id_type::MerchantId, - version: enums::UserRoleVersion, - ) -> CustomResult { - let mut user_roles = self.user_roles.lock().await; - - let index = user_roles.iter().position(|role| { - role.user_id == user_id - && role.version == version - && match role.merchant_id { - Some(ref mid) => mid == merchant_id, - None => false, - } - }); - - match index { - Some(idx) => Ok(user_roles.remove(idx)), - None => Err(errors::StorageError::ValueNotFound( - "Cannot find user role to delete".to_string(), - ) - .into()), - } - } - async fn list_user_roles_by_user_id( &self, user_id: &str, @@ -634,4 +644,83 @@ impl UserRoleInterface for MockDb { Ok(filtered_roles) } + + async fn find_user_role_by_user_id_and_lineage( + &self, + user_id: &str, + org_id: &id_type::OrganizationId, + merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, + version: enums::UserRoleVersion, + ) -> CustomResult { + let user_roles = self.user_roles.lock().await; + + for user_role in user_roles.iter() { + let org_level_check = user_role.org_id.as_ref() == Some(org_id) + && user_role.merchant_id.is_none() + && user_role.profile_id.is_none(); + + let merchant_level_check = user_role.org_id.as_ref() == Some(org_id) + && user_role.merchant_id.as_ref() == Some(merchant_id) + && user_role.profile_id.is_none(); + + let profile_level_check = user_role.org_id.as_ref() == Some(org_id) + && user_role.merchant_id.as_ref() == Some(merchant_id) + && user_role.profile_id.as_ref() == profile_id; + + // Check if any condition matches and the version matches + if user_role.user_id == user_id + && (org_level_check || merchant_level_check || profile_level_check) + && user_role.version == version + { + return Ok(user_role.clone()); + } + } + + Err(errors::StorageError::ValueNotFound(format!( + "No user role available for user_id = {} in the current token hierarchy", + user_id + )) + .into()) + } + + async fn delete_user_role_by_user_id_and_lineage( + &self, + user_id: &str, + org_id: &id_type::OrganizationId, + merchant_id: &id_type::MerchantId, + profile_id: Option<&String>, + version: enums::UserRoleVersion, + ) -> CustomResult { + let mut user_roles = self.user_roles.lock().await; + + // Find the position of the user role to delete + let index = user_roles.iter().position(|role| { + let org_level_check = role.org_id.as_ref() == Some(org_id) + && role.merchant_id.is_none() + && role.profile_id.is_none(); + + let merchant_level_check = role.org_id.as_ref() == Some(org_id) + && role.merchant_id.as_ref() == Some(merchant_id) + && role.profile_id.is_none(); + + let profile_level_check = role.org_id.as_ref() == Some(org_id) + && role.merchant_id.as_ref() == Some(merchant_id) + && role.profile_id.as_ref() == profile_id; + + // Check if the user role matches the conditions and the version matches + role.user_id == user_id + && (org_level_check || merchant_level_check || profile_level_check) + && role.version == version + }); + + // Remove and return the user role if found + match index { + Some(idx) => Ok(user_roles.remove(idx)), + None => Err(errors::StorageError::ValueNotFound( + "Cannot find user role to delete".to_string(), + ) + .into()), + } + } } diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index a6ce8c568b17..2bb1a5eee8e2 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -867,7 +867,6 @@ pub struct Customers; #[cfg(all( feature = "v2", feature = "customer_v2", - feature = "payment_methods_v2", any(feature = "olap", feature = "oltp") ))] impl Customers { @@ -875,7 +874,9 @@ impl Customers { let mut route = web::scope("/v2/customers").app_data(web::Data::new(state)); #[cfg(all(feature = "oltp", feature = "v2", feature = "customer_v2"))] { - route = route.service(web::resource("").route(web::post().to(customers_create))) + route = route + .service(web::resource("").route(web::post().to(customers_create))) + .service(web::resource("/{id}").route(web::put().to(customers_update))) } #[cfg(all(feature = "oltp", feature = "v2", feature = "payment_methods_v2"))] { @@ -1174,7 +1175,7 @@ pub struct MerchantConnectorAccount; ))] impl MerchantConnectorAccount { pub fn server(state: AppState) -> Scope { - let mut route = web::scope("/connector_accounts").app_data(web::Data::new(state)); + let mut route = web::scope("/v2/connector_accounts").app_data(web::Data::new(state)); #[cfg(feature = "olap")] { @@ -1469,21 +1470,8 @@ impl BusinessProfile { web::scope("/{profile_id}") .service( web::resource("/fallback_routing") - .route(web::get().to(|state, req| { - routing::routing_retrieve_default_config( - state, - req, - &TransactionType::Payment, - ) - })) - .route(web::post().to(|state, req, payload| { - routing::routing_update_default_config( - state, - req, - payload, - &TransactionType::Payment, - ) - })), + .route(web::get().to(routing::routing_retrieve_default_config)) + .route(web::post().to(routing::routing_update_default_config)), ) .service( web::resource("/activate_routing_algorithm").route(web::patch().to( @@ -1775,7 +1763,7 @@ pub struct WebhookEvents; #[cfg(feature = "olap")] impl WebhookEvents { pub fn server(config: AppState) -> Scope { - web::scope("/events/{merchant_id_or_profile_id}") + web::scope("/events/{merchant_id}") .app_data(web::Data::new(config)) .service(web::resource("").route(web::get().to(list_initial_webhook_delivery_attempts))) .service( diff --git a/crates/router/src/routes/customers.rs b/crates/router/src/routes/customers.rs index bb72112c8899..a84214374cf5 100644 --- a/crates/router/src/routes/customers.rs +++ b/crates/router/src/routes/customers.rs @@ -112,17 +112,61 @@ pub async fn customers_update( state: web::Data, req: HttpRequest, path: web::Path, - mut json_payload: web::Json, + mut json_payload: web::Json, ) -> HttpResponse { let flow = Flow::CustomersUpdate; let customer_id = path.into_inner(); json_payload.customer_id = Some(customer_id); + let customer_update_id = customers::UpdateCustomerId::new("temp_global_id".to_string()); Box::pin(api::server_wrap( flow, state, &req, json_payload.into_inner(), - |state, auth, req, _| update_customer(state, auth.merchant_account, req, auth.key_store), + |state, auth, req, _| { + update_customer( + state, + auth.merchant_account, + req, + auth.key_store, + customer_update_id.clone(), + ) + }, + auth::auth_type( + &auth::ApiKeyAuth, + &auth::JWTAuth(Permission::CustomerWrite), + req.headers(), + ), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all(feature = "v2", feature = "customer_v2"))] +#[instrument(skip_all, fields(flow = ?Flow::CustomersUpdate))] +pub async fn customers_update( + state: web::Data, + req: HttpRequest, + path: web::Path, + json_payload: web::Json, +) -> HttpResponse { + let flow = Flow::CustomersUpdate; + let id = path.into_inner().clone(); + let customer_update_id = customers::UpdateCustomerId::new(id); + Box::pin(api::server_wrap( + flow, + state, + &req, + json_payload.into_inner(), + |state, auth, req, _| { + update_customer( + state, + auth.merchant_account, + req, + auth.key_store, + customer_update_id.clone(), + ) + }, auth::auth_type( &auth::HeaderAuth(auth::ApiKeyAuth), &auth::JWTAuth(Permission::CustomerWrite), diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 7b2c406c7749..36f709572070 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -232,7 +232,8 @@ pub async fn list_payment_method_api( #[cfg(all( any(feature = "v2", feature = "v1"), - not(feature = "payment_methods_v2") + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") ))] /// List payment methods for a Customer /// @@ -355,7 +356,11 @@ pub async fn list_customer_payment_method_for_payment( .await } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[cfg(all( + feature = "v2", + feature = "payment_methods_v2", + feature = "customer_v2" +))] /// List payment methods for a Customer v2 /// /// To filter and list the applicable payment methods for a particular Customer ID, to be used in a non-payments context @@ -418,7 +423,8 @@ pub async fn list_customer_payment_method_api( #[cfg(all( any(feature = "v2", feature = "v1"), - not(feature = "payment_methods_v2") + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") ))] /// List payment methods for a Customer /// diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 485a55baa506..e6266f148f17 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -280,7 +280,55 @@ pub async fn routing_unlink_config( .await } -#[cfg(feature = "olap")] +#[cfg(all( + feature = "olap", + feature = "v2", + feature = "routing_v2", + feature = "business_profile_v2" +))] +#[instrument(skip_all)] +pub async fn routing_update_default_config( + state: web::Data, + req: HttpRequest, + path: web::Path, + json_payload: web::Json>, +) -> impl Responder { + let wrapper = routing_types::ProfileDefaultRoutingConfig { + profile_id: path.into_inner(), + connectors: json_payload.into_inner(), + }; + Box::pin(oss_api::server_wrap( + Flow::RoutingUpdateDefaultConfig, + state, + &req, + wrapper, + |state, auth: auth::AuthenticationData, wrapper, _| { + routing::update_default_fallback_routing( + state, + auth.merchant_account, + auth.key_store, + wrapper.profile_id, + wrapper.updated_config, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth(Permission::RoutingWrite), + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth(Permission::RoutingWrite), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all( + feature = "olap", + any(feature = "v1", feature = "v2"), + not(any(feature = "routing_v2", feature = "business_profile_v2")) +))] #[instrument(skip_all)] pub async fn routing_update_default_config( state: web::Data, @@ -314,7 +362,49 @@ pub async fn routing_update_default_config( .await } -#[cfg(feature = "olap")] +#[cfg(all( + feature = "olap", + feature = "v2", + feature = "routing_v2", + feature = "business_profile_v2" +))] +#[instrument(skip_all)] +pub async fn routing_retrieve_default_config( + state: web::Data, + req: HttpRequest, + path: web::Path, +) -> impl Responder { + Box::pin(oss_api::server_wrap( + Flow::RoutingRetrieveDefaultConfig, + state, + &req, + path.into_inner(), + |state, auth: auth::AuthenticationData, profile_id, _| { + routing::retrieve_default_fallback_algorithm_for_profile( + state, + auth.merchant_account, + auth.key_store, + profile_id, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::HeaderAuth(auth::ApiKeyAuth), + &auth::JWTAuth(Permission::RoutingRead), + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth(Permission::RoutingRead), + api_locking::LockAction::NotApplicable, + )) + .await +} + +#[cfg(all( + feature = "olap", + any(feature = "v1", feature = "v2"), + not(any(feature = "routing_v2", feature = "business_profile_v2")) +))] #[instrument(skip_all)] pub async fn routing_retrieve_default_config( state: web::Data, diff --git a/crates/router/src/routes/webhook_events.rs b/crates/router/src/routes/webhook_events.rs index 548ee6525994..d5150ad1fefa 100644 --- a/crates/router/src/routes/webhook_events.rs +++ b/crates/router/src/routes/webhook_events.rs @@ -19,11 +19,11 @@ pub async fn list_initial_webhook_delivery_attempts( query: web::Query, ) -> impl Responder { let flow = Flow::WebhookEventInitialDeliveryAttemptList; - let merchant_id_or_profile_id = path.into_inner(); + let merchant_id = path.into_inner(); let constraints = query.into_inner(); let request_internal = EventListRequestInternal { - merchant_id_or_profile_id: merchant_id_or_profile_id.get_string_repr().to_string(), + merchant_id: merchant_id.clone(), constraints, }; @@ -35,14 +35,14 @@ pub async fn list_initial_webhook_delivery_attempts( |state, _, request_internal, _| { webhook_events::list_initial_delivery_attempts( state, - request_internal.merchant_id_or_profile_id, + request_internal.merchant_id, request_internal.constraints, ) }, auth::auth_type( &auth::AdminApiAuth, &auth::JWTAuthMerchantFromRoute { - merchant_id: merchant_id_or_profile_id, + merchant_id, required_permission: Permission::WebhookEventRead, }, req.headers(), @@ -59,10 +59,10 @@ pub async fn list_webhook_delivery_attempts( path: web::Path<(common_utils::id_type::MerchantId, String)>, ) -> impl Responder { let flow = Flow::WebhookEventDeliveryAttemptList; - let (merchant_id_or_profile_id, initial_attempt_id) = path.into_inner(); + let (merchant_id, initial_attempt_id) = path.into_inner(); let request_internal = WebhookDeliveryAttemptListRequestInternal { - merchant_id_or_profile_id: merchant_id_or_profile_id.get_string_repr().to_string(), + merchant_id: merchant_id.clone(), initial_attempt_id, }; @@ -74,14 +74,14 @@ pub async fn list_webhook_delivery_attempts( |state, _, request_internal, _| { webhook_events::list_delivery_attempts( state, - request_internal.merchant_id_or_profile_id, + request_internal.merchant_id, request_internal.initial_attempt_id, ) }, auth::auth_type( &auth::AdminApiAuth, &auth::JWTAuthMerchantFromRoute { - merchant_id: merchant_id_or_profile_id, + merchant_id, required_permission: Permission::WebhookEventRead, }, req.headers(), @@ -98,10 +98,10 @@ pub async fn retry_webhook_delivery_attempt( path: web::Path<(common_utils::id_type::MerchantId, String)>, ) -> impl Responder { let flow = Flow::WebhookEventDeliveryRetry; - let (merchant_id_or_profile_id, event_id) = path.into_inner(); + let (merchant_id, event_id) = path.into_inner(); let request_internal = WebhookDeliveryRetryRequestInternal { - merchant_id_or_profile_id: merchant_id_or_profile_id.get_string_repr().to_string(), + merchant_id: merchant_id.clone(), event_id, }; @@ -113,14 +113,14 @@ pub async fn retry_webhook_delivery_attempt( |state, _, request_internal, _| { webhook_events::retry_delivery_attempt( state, - request_internal.merchant_id_or_profile_id, + request_internal.merchant_id, request_internal.event_id, ) }, auth::auth_type( &auth::AdminApiAuth, &auth::JWTAuthMerchantFromRoute { - merchant_id: merchant_id_or_profile_id, + merchant_id, required_permission: Permission::WebhookEventWrite, }, req.headers(), diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs index 7e7e7b8b40f4..30857f797fbb 100644 --- a/crates/router/src/services/authentication.rs +++ b/crates/router/src/services/authentication.rs @@ -1033,70 +1033,6 @@ where } } -/* -pub struct JWTAuthMerchantOrProfileFromRoute { - pub merchant_id_or_profile_id: String, - pub required_permission: Permission, -} - -#[async_trait] -impl AuthenticateAndFetch<(), A> for JWTAuthMerchantOrProfileFromRoute -where - A: SessionStateInfo + Sync, -{ - async fn authenticate_and_fetch( - &self, - request_headers: &HeaderMap, - state: &A, - ) -> RouterResult<((), AuthenticationType)> { - let payload = parse_jwt_payload::(request_headers, state).await?; - if payload.check_in_blacklist(state).await? { - return Err(errors::ApiErrorResponse::InvalidJwtToken.into()); - } - - let permissions = authorization::get_permissions(state, &payload).await?; - authorization::check_authorization(&self.required_permission, &permissions)?; - - // Check if token has access to MerchantId that has been requested through path or query param - if payload.merchant_id.get_string_repr() == self.merchant_id_or_profile_id { - return Ok(( - (), - AuthenticationType::MerchantJwt { - merchant_id: payload.merchant_id, - user_id: Some(payload.user_id), - }, - )); - } - - // Route did not contain the merchant ID in present JWT, check if it corresponds to a - // business profile - let business_profile = state - .store() - .find_business_profile_by_profile_id(&self.merchant_id_or_profile_id) - .await - // Return access forbidden if business profile not found - .to_not_found_response(errors::ApiErrorResponse::AccessForbidden { - resource: self.merchant_id_or_profile_id.clone(), - }) - .attach_printable("Could not find business profile specified in route")?; - - // Check if merchant (from JWT) has access to business profile that has been requested - // through path or query param - if payload.merchant_id == business_profile.merchant_id { - Ok(( - (), - AuthenticationType::MerchantJwt { - merchant_id: payload.merchant_id, - user_id: Some(payload.user_id), - }, - )) - } else { - Err(report!(errors::ApiErrorResponse::InvalidJwtToken)) - } - } -} -*/ - pub async fn parse_jwt_payload(headers: &HeaderMap, state: &A) -> RouterResult where T: serde::de::DeserializeOwned, diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index b9a05674fd33..513e4fa1ee29 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -306,7 +306,7 @@ impl ConnectorData { ) -> CustomResult { match enums::Connector::from_str(connector_name) { Ok(name) => match name { - enums::Connector::Aci => Ok(ConnectorEnum::Old(Box::new(&connector::Aci))), + enums::Connector::Aci => Ok(ConnectorEnum::Old(Box::new(connector::Aci::new()))), enums::Connector::Adyen => { Ok(ConnectorEnum::Old(Box::new(connector::Adyen::new()))) } @@ -386,17 +386,21 @@ impl ConnectorData { enums::Connector::DummyConnector7 => Ok(ConnectorEnum::Old(Box::new( &connector::DummyConnector::<7>, ))), - enums::Connector::Ebanx => Ok(ConnectorEnum::Old(Box::new(&connector::Ebanx))), + enums::Connector::Ebanx => { + Ok(ConnectorEnum::Old(Box::new(connector::Ebanx::new()))) + } enums::Connector::Fiserv => Ok(ConnectorEnum::Old(Box::new(&connector::Fiserv))), // enums::Connector::Fiservemea => { // Ok(ConnectorEnum::Old(Box::new(connector::Fiservemea))) // } - enums::Connector::Forte => Ok(ConnectorEnum::Old(Box::new(&connector::Forte))), + enums::Connector::Forte => { + Ok(ConnectorEnum::Old(Box::new(connector::Forte::new()))) + } enums::Connector::Globalpay => { Ok(ConnectorEnum::Old(Box::new(connector::Globalpay::new()))) } enums::Connector::Globepay => { - Ok(ConnectorEnum::Old(Box::new(&connector::Globepay))) + Ok(ConnectorEnum::Old(Box::new(connector::Globepay::new()))) } enums::Connector::Gocardless => { Ok(ConnectorEnum::Old(Box::new(&connector::Gocardless))) @@ -416,7 +420,9 @@ impl ConnectorData { enums::Connector::Opennode => { Ok(ConnectorEnum::Old(Box::new(&connector::Opennode))) } - // enums::Connector::Paybox => Ok(ConnectorEnum::Old(Box::new(connector::Paybox::new()))), // added for future use + enums::Connector::Paybox => { + Ok(ConnectorEnum::Old(Box::new(connector::Paybox::new()))) + } // "payeezy" => Ok(ConnectorIntegrationEnum::Old(Box::new(&connector::Payeezy)), As psync and rsync are not supported by this connector, it is added as template code for future usage enums::Connector::Payme => { Ok(ConnectorEnum::Old(Box::new(connector::Payme::new()))) @@ -444,6 +450,7 @@ impl ConnectorData { enums::Connector::Stripe => { Ok(ConnectorEnum::Old(Box::new(connector::Stripe::new()))) } + // enums::Connector::Taxjar => Ok(ConnectorEnum::Old(Box::new(connector::Taxjar))), enums::Connector::Wise => Ok(ConnectorEnum::Old(Box::new(&connector::Wise))), enums::Connector::Worldline => { Ok(ConnectorEnum::Old(Box::new(&connector::Worldline))) @@ -455,7 +462,7 @@ impl ConnectorData { Ok(ConnectorEnum::Old(Box::new(connector::Mifinity::new()))) } enums::Connector::Multisafepay => { - Ok(ConnectorEnum::Old(Box::new(&connector::Multisafepay))) + Ok(ConnectorEnum::Old(Box::new(connector::Multisafepay::new()))) } enums::Connector::Netcetera => { Ok(ConnectorEnum::Old(Box::new(&connector::Netcetera))) diff --git a/crates/router/src/types/api/customers.rs b/crates/router/src/types/api/customers.rs index e3f86f670f7a..2076ad60c00f 100644 --- a/crates/router/src/types/api/customers.rs +++ b/crates/router/src/types/api/customers.rs @@ -1,5 +1,7 @@ use api_models::customers; -pub use api_models::customers::{CustomerDeleteResponse, CustomerId, CustomerRequest}; +pub use api_models::customers::{ + CustomerDeleteResponse, CustomerId, CustomerRequest, CustomerUpdateRequest, UpdateCustomerId, +}; #[cfg(all(feature = "v2", feature = "customer_v2"))] use hyperswitch_domain_models::customer; use serde::Serialize; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index ebfd4b099bad..67af63b154e0 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -271,7 +271,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Noon => Self::Noon, api_enums::Connector::Nuvei => Self::Nuvei, api_enums::Connector::Opennode => Self::Opennode, - // api_enums::Connector::Paybox => Self::Paybox, added for future usage + api_enums::Connector::Paybox => Self::Paybox, api_enums::Connector::Payme => Self::Payme, api_enums::Connector::Payone => Self::Payone, api_enums::Connector::Paypal => Self::Paypal, @@ -296,6 +296,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Square => Self::Square, api_enums::Connector::Stax => Self::Stax, api_enums::Connector::Stripe => Self::Stripe, + // api_enums::Connector::Taxjar => Self::Taxjar, api_enums::Connector::Trustpay => Self::Trustpay, api_enums::Connector::Tsys => Self::Tsys, api_enums::Connector::Volt => Self::Volt, diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index e71bc0493066..ef07ab6a8462 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -884,6 +884,104 @@ impl CustomerAddress for api_models::customers::CustomerRequest { } } +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +#[async_trait::async_trait] +impl CustomerAddress for api_models::customers::CustomerUpdateRequest { + async fn get_address_update( + &self, + state: &SessionState, + address_details: payments::AddressDetails, + key: &[u8], + storage_scheme: storage::enums::MerchantStorageScheme, + merchant_id: id_type::MerchantId, + ) -> CustomResult { + let encrypted_data = crypto_operation( + &state.into(), + type_name!(storage::Address), + CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable( + AddressDetailsWithPhone { + address: Some(address_details.clone()), + phone_number: self.phone.clone(), + email: self.email.clone(), + }, + )), + Identifier::Merchant(merchant_id), + key, + ) + .await + .and_then(|val| val.try_into_batchoperation())?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; + Ok(storage::AddressUpdate::Update { + city: address_details.city, + country: address_details.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + zip: encryptable_address.zip, + state: encryptable_address.state, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: self.phone_country_code.clone(), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }) + } + + async fn get_domain_address( + &self, + state: &SessionState, + address_details: payments::AddressDetails, + merchant_id: &id_type::MerchantId, + customer_id: &id_type::CustomerId, + key: &[u8], + storage_scheme: storage::enums::MerchantStorageScheme, + ) -> CustomResult { + let encrypted_data = crypto_operation( + &state.into(), + type_name!(storage::Address), + CryptoOperation::BatchEncrypt(AddressDetailsWithPhone::to_encryptable( + AddressDetailsWithPhone { + address: Some(address_details.clone()), + phone_number: self.phone.clone(), + email: self.email.clone(), + }, + )), + Identifier::Merchant(merchant_id.to_owned()), + key, + ) + .await + .and_then(|val| val.try_into_batchoperation())?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; + let address = domain::Address { + city: address_details.city, + country: address_details.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + zip: encryptable_address.zip, + state: encryptable_address.state, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: self.phone_country_code.clone(), + merchant_id: merchant_id.to_owned(), + address_id: generate_id(consts::ID_LENGTH, "add"), + created_at: common_utils::date_time::now(), + modified_at: common_utils::date_time::now(), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }; + + Ok(domain::CustomerAddress { + address, + customer_id: customer_id.to_owned(), + }) + } +} + pub fn add_apple_pay_flow_metrics( apple_pay_flow: &Option, connector: Option, diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 1e829c8bdf89..f3ff1a603612 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -7,7 +7,6 @@ use common_utils::id_type; use masking::Secret; use router::{ configs::settings::Settings, - connector::aci, core::payments, db::StorageImpl, routes, services, @@ -214,9 +213,9 @@ async fn payments_create_success() { .get_session_state("public", || {}) .unwrap(); - static CV: aci::Aci = aci::Aci; + use router::connector::Aci; let connector = utils::construct_connector_data_old( - Box::new(&CV), + Box::new(Aci::new()), types::Connector::Aci, types::api::GetToken::Connector, None, @@ -247,7 +246,7 @@ async fn payments_create_success() { async fn payments_create_failure() { { let conf = Settings::new().unwrap(); - static CV: aci::Aci = aci::Aci; + use router::connector::Aci; let tx: oneshot::Sender<()> = oneshot::channel().0; let app_state = Box::pin(routes::AppState::with_storage( @@ -261,7 +260,7 @@ async fn payments_create_failure() { .get_session_state("public", || {}) .unwrap(); let connector = utils::construct_connector_data_old( - Box::new(&CV), + Box::new(Aci::new()), types::Connector::Aci, types::api::GetToken::Connector, None, @@ -304,9 +303,9 @@ async fn payments_create_failure() { async fn refund_for_successful_payments() { let conf = Settings::new().unwrap(); - static CV: aci::Aci = aci::Aci; + use router::connector::Aci; let connector = utils::construct_connector_data_old( - Box::new(&CV), + Box::new(Aci::new()), types::Connector::Aci, types::api::GetToken::Connector, None, @@ -374,9 +373,9 @@ async fn refund_for_successful_payments() { #[ignore] async fn refunds_create_failure() { let conf = Settings::new().unwrap(); - static CV: aci::Aci = aci::Aci; + use router::connector::Aci; let connector = utils::construct_connector_data_old( - Box::new(&CV), + Box::new(Aci::new()), types::Connector::Aci, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/ebanx.rs b/crates/router/tests/connectors/ebanx.rs index 689c28b201d5..28b571dac1f8 100644 --- a/crates/router/tests/connectors/ebanx.rs +++ b/crates/router/tests/connectors/ebanx.rs @@ -11,7 +11,7 @@ impl utils::Connector for EbanxTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Ebanx; utils::construct_connector_data_old( - Box::new(&Ebanx), + Box::new(Ebanx::new()), types::Connector::Ebanx, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/forte.rs b/crates/router/tests/connectors/forte.rs index 45a30614bfa8..8447ab358e41 100644 --- a/crates/router/tests/connectors/forte.rs +++ b/crates/router/tests/connectors/forte.rs @@ -17,7 +17,7 @@ impl utils::Connector for ForteTest { fn get_data(&self) -> api::ConnectorData { use router::connector::Forte; utils::construct_connector_data_old( - Box::new(&Forte), + Box::new(Forte::new()), types::Connector::Forte, api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/globepay.rs b/crates/router/tests/connectors/globepay.rs index 5172715b4f60..342feac6ec59 100644 --- a/crates/router/tests/connectors/globepay.rs +++ b/crates/router/tests/connectors/globepay.rs @@ -13,7 +13,7 @@ impl utils::Connector for GlobepayTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Globepay; utils::construct_connector_data_old( - Box::new(&Globepay), + Box::new(Globepay::new()), types::Connector::Globepay, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index 7fdccfaadeb4..e0fa3964b27d 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -69,6 +69,7 @@ mod shift4; mod square; mod stax; mod stripe; +mod taxjar; mod trustpay; mod tsys; mod utils; diff --git a/crates/router/tests/connectors/multisafepay.rs b/crates/router/tests/connectors/multisafepay.rs index 390af0b5d8f3..0d5af818c358 100644 --- a/crates/router/tests/connectors/multisafepay.rs +++ b/crates/router/tests/connectors/multisafepay.rs @@ -14,7 +14,7 @@ impl utils::Connector for MultisafepayTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Multisafepay; utils::construct_connector_data_old( - Box::new(&Multisafepay), + Box::new(Multisafepay::new()), types::Connector::Multisafepay, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/sample_auth.toml b/crates/router/tests/connectors/sample_auth.toml index 891f5991c668..0bf4d96d19f2 100644 --- a/crates/router/tests/connectors/sample_auth.toml +++ b/crates/router/tests/connectors/sample_auth.toml @@ -264,4 +264,7 @@ api_key="API Key" [wellsfargopayout] api_key = "Consumer Key" key1 = "Gateway Entity Id" -api_secret = "Consumer Secret" \ No newline at end of file +api_secret = "Consumer Secret" + +[taxjar] +api_key = "API Key" \ No newline at end of file diff --git a/crates/router/tests/connectors/taxjar.rs b/crates/router/tests/connectors/taxjar.rs new file mode 100644 index 000000000000..6bb044777f63 --- /dev/null +++ b/crates/router/tests/connectors/taxjar.rs @@ -0,0 +1,420 @@ +use masking::Secret; +use router::types::{self, api, domain, storage::enums}; +use test_utils::connector_auth; + +use crate::utils::{self, ConnectorActions}; + +#[derive(Clone, Copy)] +struct TaxjarTest; +impl ConnectorActions for TaxjarTest {} +impl utils::Connector for TaxjarTest { + fn get_data(&self) -> api::ConnectorData { + use router::connector::Taxjar; + utils::construct_connector_data_old( + Box::new(Taxjar::new()), + types::Connector::Adyen, + api::GetToken::Connector, + None, + ) + } + + fn get_auth_token(&self) -> types::ConnectorAuthType { + utils::to_connector_auth_type( + connector_auth::ConnectorAuthentication::new() + .taxjar + .expect("Missing connector authentication configuration") + .into(), + ) + } + + fn get_name(&self) -> String { + "taxjar".to_string() + } +} + +static CONNECTOR: TaxjarTest = TaxjarTest {}; + +fn get_default_payment_info() -> Option { + None +} + +fn payment_method_details() -> Option { + None +} + +// Cards Positive Tests +// Creates a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_only_authorize_payment() { + let response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized); +} + +// Captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info()) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Partially captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment( + payment_method_details(), + Some(types::PaymentsCaptureData { + amount_to_capture: 50, + ..utils::PaymentCaptureType::default().0 + }), + get_default_payment_info(), + ) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_authorized_payment() { + let authorize_response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Authorized, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("PSync response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized,); +} + +// Voids a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_void_authorized_payment() { + let response = CONNECTOR + .authorize_and_void_payment( + payment_method_details(), + Some(types::PaymentsCancelData { + connector_transaction_id: String::from(""), + cancellation_reason: Some("requested_by_customer".to_string()), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("Void payment response"); + assert_eq!(response.status, enums::AttemptStatus::Voided); +} + +// Refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Synchronizes a refund using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_manually_captured_refund() { + let refund_response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_make_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_auto_captured_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Charged, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + capture_method: Some(enums::CaptureMethod::Automatic), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!(response.status, enums::AttemptStatus::Charged,); +} + +// Refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_auto_captured_payment() { + let response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_succeeded_payment() { + let refund_response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + refund_response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_succeeded_payment_multiple_times() { + CONNECTOR + .make_payment_and_multiple_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await; +} + +// Synchronizes a refund using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_refund() { + let refund_response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Cards Negative scenarios +// Creates a payment with incorrect CVC. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_cvc() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_cvc: Secret::new("12345".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's security code is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry month. +#[actix_web::test] +async fn should_fail_payment_for_invalid_exp_month() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_month: Secret::new("20".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration month is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry year. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_expiry_year() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_year: Secret::new("2000".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration year is invalid.".to_string(), + ); +} + +// Voids a payment using automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_fail_void_payment_for_auto_capture() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let void_response = CONNECTOR + .void_payment(txn_id.unwrap(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + void_response.response.unwrap_err().message, + "You cannot cancel this PaymentIntent because it has a status of succeeded." + ); +} + +// Captures a payment using invalid connector payment id. +#[actix_web::test] +async fn should_fail_capture_for_invalid_payment() { + let capture_response = CONNECTOR + .capture_payment("123456789".to_string(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + capture_response.response.unwrap_err().message, + String::from("No such payment_intent: '123456789'") + ); +} + +// Refunds a payment with refund amount higher than payment amount. +#[actix_web::test] +async fn should_fail_for_refund_amount_higher_than_payment_amount() { + let response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 150, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Refund amount (₹1.50) is greater than charge amount (₹1.00)", + ); +} + +// Connector dependent test cases goes here + +// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests diff --git a/crates/storage_impl/src/mock_db/payouts.rs b/crates/storage_impl/src/mock_db/payouts.rs index 835f73c62dee..c151e8acb880 100644 --- a/crates/storage_impl/src/mock_db/payouts.rs +++ b/crates/storage_impl/src/mock_db/payouts.rs @@ -85,4 +85,28 @@ impl PayoutsInterface for MockDb { // TODO: Implement function for `MockDb` Err(StorageError::MockDbError)? } + + #[cfg(feature = "olap")] + async fn get_total_count_of_filtered_payouts( + &self, + _merchant_id: &common_utils::id_type::MerchantId, + _active_payout_ids: &[String], + _connector: Option>, + _currency: Option>, + _status: Option>, + _payout_method: Option>, + ) -> CustomResult { + // TODO: Implement function for `MockDb` + Err(StorageError::MockDbError)? + } + + #[cfg(feature = "olap")] + async fn filter_active_payout_ids_by_constraints( + &self, + _merchant_id: &common_utils::id_type::MerchantId, + _constraints: &hyperswitch_domain_models::payouts::PayoutFetchConstraints, + ) -> CustomResult, StorageError> { + // TODO: Implement function for `MockDb` + Err(StorageError::MockDbError)? + } } diff --git a/crates/storage_impl/src/payouts/payouts.rs b/crates/storage_impl/src/payouts/payouts.rs index f449666f6e00..f4e4257c02b6 100644 --- a/crates/storage_impl/src/payouts/payouts.rs +++ b/crates/storage_impl/src/payouts/payouts.rs @@ -1,17 +1,21 @@ #[cfg(feature = "olap")] +use api_models::enums::PayoutConnectors; +#[cfg(feature = "olap")] use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl}; +#[cfg(feature = "olap")] +use common_utils::errors::ReportSwitchExt; use common_utils::ext_traits::Encode; +#[cfg(feature = "olap")] +use diesel::{associations::HasTable, ExpressionMethods, QueryDsl}; #[cfg(all( feature = "olap", any(feature = "v1", feature = "v2"), not(feature = "customer_v2") ))] -use diesel::JoinOnDsl; -#[cfg(feature = "olap")] -use diesel::{associations::HasTable, ExpressionMethods, NullableExpressionMethods, QueryDsl}; +use diesel::{JoinOnDsl, NullableExpressionMethods}; #[cfg(feature = "olap")] use diesel_models::{ - customers::Customer as DieselCustomer, query::generics::db_metrics, + customers::Customer as DieselCustomer, enums as storage_enums, query::generics::db_metrics, schema::payouts::dsl as po_dsl, }; use diesel_models::{ @@ -47,12 +51,15 @@ use router_env::logger; use router_env::{instrument, tracing}; #[cfg(feature = "olap")] -use crate::{ - connection, - store::schema::{ - customers::all_columns as cust_all_columns, payout_attempt::all_columns as poa_all_columns, - payouts::all_columns as po_all_columns, - }, +use crate::connection; +#[cfg(all( + feature = "olap", + any(feature = "v1", feature = "v2"), + not(feature = "customer_v2") +))] +use crate::store::schema::{ + customers::all_columns as cust_all_columns, payout_attempt::all_columns as poa_all_columns, + payouts::all_columns as po_all_columns, }; use crate::{ diesel_error_to_data_error, @@ -345,6 +352,39 @@ impl PayoutsInterface for KVRouterStore { .filter_payouts_by_time_range_constraints(merchant_id, time_range, storage_scheme) .await } + + #[cfg(feature = "olap")] + async fn get_total_count_of_filtered_payouts( + &self, + merchant_id: &common_utils::id_type::MerchantId, + active_payout_ids: &[String], + connector: Option>, + currency: Option>, + status: Option>, + payout_method: Option>, + ) -> error_stack::Result { + self.router_store + .get_total_count_of_filtered_payouts( + merchant_id, + active_payout_ids, + connector, + currency, + status, + payout_method, + ) + .await + } + + #[cfg(feature = "olap")] + async fn filter_active_payout_ids_by_constraints( + &self, + merchant_id: &common_utils::id_type::MerchantId, + constraints: &PayoutFetchConstraints, + ) -> error_stack::Result, StorageError> { + self.router_store + .filter_active_payout_ids_by_constraints(merchant_id, constraints) + .await + } } #[async_trait::async_trait] @@ -428,8 +468,6 @@ impl PayoutsInterface for crate::RouterStore { filters: &PayoutFetchConstraints, storage_scheme: MerchantStorageScheme, ) -> error_stack::Result, StorageError> { - use common_utils::errors::ReportSwitchExt; - let conn = connection::pg_connection_read(self).await.switch()?; let conn = async_bb8_diesel::Connection::as_async_conn(&conn); @@ -492,18 +530,6 @@ impl PayoutsInterface for crate::RouterStore { query = query.offset(params.offset.into()); - query = match ¶ms.currency { - Some(currency) => { - query.filter(po_dsl::destination_currency.eq_any(currency.clone())) - } - None => query, - }; - - query = match ¶ms.status { - Some(status) => query.filter(po_dsl::status.eq_any(status.clone())), - None => query, - }; - if let Some(currency) = ¶ms.currency { query = query.filter(po_dsl::destination_currency.eq_any(currency.clone())); } @@ -549,8 +575,6 @@ impl PayoutsInterface for crate::RouterStore { storage_scheme: MerchantStorageScheme, ) -> error_stack::Result)>, StorageError> { - use common_utils::errors::ReportSwitchExt; - let conn = connection::pg_connection_read(self).await.switch()?; let conn = async_bb8_diesel::Connection::as_async_conn(&conn); let mut query = DieselPayouts::table() @@ -558,7 +582,7 @@ impl PayoutsInterface for crate::RouterStore { diesel_models::schema::payout_attempt::table .on(poa_dsl::payout_id.eq(po_dsl::payout_id)), ) - .inner_join( + .left_join( diesel_models::schema::customers::table .on(cust_dsl::customer_id.nullable().eq(po_dsl::customer_id)), ) @@ -701,6 +725,122 @@ impl PayoutsInterface for crate::RouterStore { self.filter_payouts_by_constraints(merchant_id, &payout_filters, storage_scheme) .await } + + #[cfg(feature = "olap")] + #[instrument(skip_all)] + async fn get_total_count_of_filtered_payouts( + &self, + merchant_id: &common_utils::id_type::MerchantId, + active_payout_ids: &[String], + connector: Option>, + currency: Option>, + status: Option>, + payout_type: Option>, + ) -> error_stack::Result { + let conn = self + .db_store + .get_replica_pool() + .get() + .await + .change_context(StorageError::DatabaseConnectionError)?; + let connector_strings = connector.as_ref().map(|connectors| { + connectors + .iter() + .map(|c| c.to_string()) + .collect::>() + }); + DieselPayouts::get_total_count_of_payouts( + &conn, + merchant_id, + active_payout_ids, + connector_strings, + currency, + status, + payout_type, + ) + .await + .map_err(|er| { + let new_err = diesel_error_to_data_error(er.current_context()); + er.change_context(new_err) + }) + } + + #[cfg(feature = "olap")] + #[instrument(skip_all)] + async fn filter_active_payout_ids_by_constraints( + &self, + merchant_id: &common_utils::id_type::MerchantId, + constraints: &PayoutFetchConstraints, + ) -> error_stack::Result, StorageError> { + let conn = connection::pg_connection_read(self).await.switch()?; + let conn = async_bb8_diesel::Connection::as_async_conn(&conn); + let mut query = DieselPayouts::table() + .inner_join( + diesel_models::schema::payout_attempt::table + .on(poa_dsl::payout_id.eq(po_dsl::payout_id)), + ) + .left_join( + diesel_models::schema::customers::table + .on(cust_dsl::customer_id.nullable().eq(po_dsl::customer_id)), + ) + .select(po_dsl::payout_id) + .filter(po_dsl::merchant_id.eq(merchant_id.to_owned())) + .order(po_dsl::created_at.desc()) + .into_boxed(); + + query = match constraints { + PayoutFetchConstraints::Single { payout_id } => { + query.filter(po_dsl::payout_id.eq(payout_id.to_owned())) + } + PayoutFetchConstraints::List(params) => { + if let Some(customer_id) = ¶ms.customer_id { + query = query.filter(po_dsl::customer_id.eq(customer_id.clone())); + } + if let Some(profile_id) = ¶ms.profile_id { + query = query.filter(po_dsl::profile_id.eq(profile_id.clone())); + } + + query = match params.starting_at { + Some(starting_at) => query.filter(po_dsl::created_at.ge(starting_at)), + None => query, + }; + + query = match params.ending_at { + Some(ending_at) => query.filter(po_dsl::created_at.le(ending_at)), + None => query, + }; + + query = match ¶ms.currency { + Some(currency) => { + query.filter(po_dsl::destination_currency.eq_any(currency.clone())) + } + None => query, + }; + + query = match ¶ms.status { + Some(status) => query.filter(po_dsl::status.eq_any(status.clone())), + None => query, + }; + + query + } + }; + + logger::debug!(filter = %diesel::debug_query::(&query).to_string()); + + db_metrics::track_database_call::<::Table, _, _>( + query.get_results_async::(conn), + db_metrics::DatabaseOperation::Filter, + ) + .await + .map_err(|er| { + StorageError::DatabaseError( + error_stack::report!(diesel_models::errors::DatabaseError::from(er)) + .attach_printable("Error filtering payout records"), + ) + .into() + }) + } } impl DataModelExt for Payouts { diff --git a/crates/storage_impl/src/redis/kv_store.rs b/crates/storage_impl/src/redis/kv_store.rs index 7e1058458278..5b2194c58aec 100644 --- a/crates/storage_impl/src/redis/kv_store.rs +++ b/crates/storage_impl/src/redis/kv_store.rs @@ -34,6 +34,11 @@ pub enum PartitionKey<'a> { merchant_id: &'a common_utils::id_type::MerchantId, customer_id: &'a str, }, + #[cfg(all(feature = "v2", feature = "customer_v2"))] + MerchantIdMerchantReferenceId { + merchant_id: &'a common_utils::id_type::MerchantId, + merchant_reference_id: &'a str, + }, MerchantIdPayoutId { merchant_id: &'a common_utils::id_type::MerchantId, payout_id: &'a str, @@ -46,6 +51,10 @@ pub enum PartitionKey<'a> { merchant_id: &'a common_utils::id_type::MerchantId, mandate_id: &'a str, }, + #[cfg(all(feature = "v2", feature = "customer_v2"))] + GlobalId { + id: &'a str, + }, } // PartitionKey::MerchantIdPaymentId {merchant_id, payment_id} impl<'a> std::fmt::Display for PartitionKey<'a> { @@ -66,6 +75,14 @@ impl<'a> std::fmt::Display for PartitionKey<'a> { "mid_{}_cust_{customer_id}", merchant_id.get_string_repr() )), + #[cfg(all(feature = "v2", feature = "customer_v2"))] + PartitionKey::MerchantIdMerchantReferenceId { + merchant_id, + merchant_reference_id, + } => f.write_str(&format!( + "mid_{}_cust_{merchant_reference_id}", + merchant_id.get_string_repr() + )), PartitionKey::MerchantIdPayoutId { merchant_id, payout_id, @@ -87,6 +104,9 @@ impl<'a> std::fmt::Display for PartitionKey<'a> { "mid_{}_mandate_{mandate_id}", merchant_id.get_string_repr() )), + + #[cfg(all(feature = "v2", feature = "customer_v2"))] + PartitionKey::GlobalId { id } => f.write_str(&format!("cust_{id}",)), } } } diff --git a/crates/test_utils/src/connector_auth.rs b/crates/test_utils/src/connector_auth.rs index 66384a2d92f8..b13be3075aa9 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -71,6 +71,7 @@ pub struct ConnectorAuthentication { pub square: Option, pub stax: Option, pub stripe: Option, + pub taxjar: Option, pub threedsecureio: Option, pub stripe_au: Option, pub stripe_uk: Option, diff --git a/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js b/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js index b96121a0520e..a00a2e39f959 100644 --- a/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js +++ b/cypress-tests/cypress/e2e/PaymentTest/00020-Variations.cy.js @@ -482,7 +482,7 @@ describe("Corner cases", () => { }); }); - after("flush global state", () => { + afterEach("flush global state", () => { cy.task("setGlobalState", globalState.data); }); diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Commons.js b/cypress-tests/cypress/e2e/PaymentUtils/Commons.js index b8f8750a11b0..9318480b6466 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Commons.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Commons.js @@ -14,6 +14,7 @@ function normalise(input) { const exceptions = { bankofamerica: "Bank of America", cybersource: "Cybersource", + paybox: "Paybox", paypal: "Paypal", wellsfargo: "Wellsfargo", // Add more known exceptions here diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js b/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js index fe9e0c68e0c9..3eddf0530155 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Iatapay.js @@ -1,6 +1,6 @@ export const connectorDetails = { bank_redirect_pm: { - ideal: { + Ideal: { Request: { payment_method: "bank_redirect", payment_method_type: "ideal", diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Paybox.js b/cypress-tests/cypress/e2e/PaymentUtils/Paybox.js new file mode 100644 index 000000000000..4bfeb077e4a4 --- /dev/null +++ b/cypress-tests/cypress/e2e/PaymentUtils/Paybox.js @@ -0,0 +1,136 @@ +const successfulNo3DSCardDetails = { + card_number: "1111222233334444", + card_exp_month: "05", + card_exp_year: "27", + card_holder_name: "joseph Doe", + card_cvc: "222", +}; + +export const connectorDetails = { + card_pm: { + PaymentIntent: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + currency: "EUR", + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "requires_payment_method", + }, + }, + }, + No3DSManualCapture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + No3DSAutoCapture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + setup_future_usage: "on_session", + }, + Response: { + status: 200, + body: { + status: "processing", + }, + }, + }, + Capture: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "processing", + amount: 6500, + amount_capturable: 6500, + amount_received: null, + }, + }, + }, + PartialCapture: { + Request: {}, + Response: { + status: 200, + body: { + status: "processing", + amount: 6500, + amount_capturable: 6500, + amount_received: null, + }, + }, + }, + Refund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "pending", + }, + }, + }, + PartialRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "pending", + }, + }, + }, + SyncRefund: { + Request: { + payment_method: "card", + payment_method_data: { + card: successfulNo3DSCardDetails, + }, + customer_acceptance: null, + }, + Response: { + status: 200, + body: { + status: "succeeded", + }, + }, + }, + }, +}; \ No newline at end of file diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js index 07d323d2a5ee..01680c47d5cf 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Utils.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Utils.js @@ -10,6 +10,7 @@ import { connectorDetails as datatransConnectorDetails } from "./Datatrans.js"; import { connectorDetails as iatapayConnectorDetails } from "./Iatapay.js"; import { connectorDetails as itaubankConnectorDetails } from "./ItauBank.js"; import { connectorDetails as nmiConnectorDetails } from "./Nmi.js"; +import { connectorDetails as payboxConnectorDetails } from "./Paybox.js"; import { connectorDetails as paypalConnectorDetails } from "./Paypal.js"; import { connectorDetails as stripeConnectorDetails } from "./Stripe.js"; import { connectorDetails as trustpayConnectorDetails } from "./Trustpay.js"; @@ -24,6 +25,7 @@ const connectorDetails = { iatapay: iatapayConnectorDetails, itaubank: itaubankConnectorDetails, nmi: nmiConnectorDetails, + paybox: payboxConnectorDetails, paypal: paypalConnectorDetails, stripe: stripeConnectorDetails, trustpay: trustpayConnectorDetails, diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index 62c70b98b98e..bc23934efc18 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -1250,7 +1250,7 @@ Cypress.Commands.add( "saveCardConfirmCallTest", (saveCardConfirmBody, req_data, res_data, globalState) => { const paymentIntentID = globalState.get("paymentID"); - if (req_data.setup_future_usage === "on_session"){ + if (req_data.setup_future_usage === "on_session") { saveCardConfirmBody.card_cvc = req_data.payment_method_data.card.card_cvc; } saveCardConfirmBody.payment_token = globalState.get("paymentToken"); diff --git a/cypress-tests/package.json b/cypress-tests/package.json index 0b67f122d8da..b0eb327f715b 100644 --- a/cypress-tests/package.json +++ b/cypress-tests/package.json @@ -9,6 +9,7 @@ "cypress:ci": "npx cypress run --headless", "cypress:payments": "cypress run --headless --spec 'cypress/e2e/PaymentTest/**/*'", "cypress:payouts": "cypress run --headless --spec 'cypress/e2e/PayoutTest/**/*'", + "cypress:payment-method-list": "cypress run --headless --spec 'cypress/e2e/PaymentMethodListTest/**/*'", "cypress:routing": "cypress run --headless --spec 'cypress/e2e/RoutingTest/**/*'" }, "author": "", diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index c74a11514168..9e5c8a3f0ceb 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -140,6 +140,7 @@ square.base_url = "https://connect.squareupsandbox.com/" square.secondary_base_url = "https://pci-connect.squareupsandbox.com/" stax.base_url = "https://apiprod.fattlabs.com/" stripe.base_url = "https://api.stripe.com/" +taxjar.base_url = "https://api.sandbox.taxjar.com/v2/" threedsecureio.base_url = "https://service.sandbox.3dsecure.io" stripe.base_url_file_upload = "https://files.stripe.com/" trustpay.base_url = "https://test-tpgw.trustpay.eu/" @@ -217,6 +218,7 @@ cards = [ "square", "stax", "stripe", + "taxjar", "threedsecureio", "trustpay", "tsys", diff --git a/migrations/2022-09-29-093314_create_seed_data/down.sql b/migrations/2022-09-29-093314_create_seed_data/down.sql deleted file mode 100644 index ac76570d13b3..000000000000 --- a/migrations/2022-09-29-093314_create_seed_data/down.sql +++ /dev/null @@ -1,5 +0,0 @@ -DELETE FROM merchant_connector_account -WHERE merchant_id = 'juspay_merchant'; - -DELETE FROM merchant_account -WHERE merchant_id = 'juspay_merchant'; diff --git a/migrations/2022-09-29-093314_create_seed_data/up.sql b/migrations/2022-09-29-093314_create_seed_data/up.sql deleted file mode 100644 index b2a864ad297d..000000000000 --- a/migrations/2022-09-29-093314_create_seed_data/up.sql +++ /dev/null @@ -1,27 +0,0 @@ -INSERT INTO merchant_account ( - merchant_id, - api_key, - merchant_name, - merchant_details, - custom_routing_rules, - publishable_key - ) -VALUES ( - 'juspay_merchant', - 'MySecretApiKey', - 'Juspay Merchant', - '{ "primary_email": "merchant@juspay.in" }', - '[ { "connectors_pecking_order": [ "stripe" ] } ]', - 'pk_MyPublicApiKey' - ); - -INSERT INTO merchant_connector_account ( - merchant_id, - connector_name, - connector_account_details - ) -VALUES ( - 'juspay_merchant', - 'stripe', - '{ "auth_type": "HeaderKey", "api_key": "Basic MyStripeApiKey" }' - ); diff --git a/migrations/2024-08-12-104928_add_api_version_in_mca/down.sql b/migrations/2024-08-12-104928_add_api_version_in_mca/down.sql new file mode 100644 index 000000000000..a2d88488b11a --- /dev/null +++ b/migrations/2024-08-12-104928_add_api_version_in_mca/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_connector_account DROP COLUMN IF EXISTS version; \ No newline at end of file diff --git a/migrations/2024-08-12-104928_add_api_version_in_mca/up.sql b/migrations/2024-08-12-104928_add_api_version_in_mca/up.sql new file mode 100644 index 000000000000..2ea9d27112af --- /dev/null +++ b/migrations/2024-08-12-104928_add_api_version_in_mca/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +ALTER TABLE merchant_connector_account +ADD COLUMN IF NOT EXISTS version "ApiVersion" NOT NULL DEFAULT 'v1'; diff --git a/scripts/add_connector.sh b/scripts/add_connector.sh index 49160492a681..3e5fc1c3d9dc 100755 --- a/scripts/add_connector.sh +++ b/scripts/add_connector.sh @@ -6,7 +6,7 @@ function find_prev_connector() { git checkout $self cp $self $self.tmp # Add new connector to existing list and sort it - connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv fiservemea forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode paybox payeezy payme payone paypal payu placetopay plaid powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wellsfargo wellsfargopayout wise worldline worldpay zsl "$1") + connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv fiservemea forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode paybox payeezy payme payone paypal payu placetopay plaid powertranz prophetpay rapyd razorpay shift4 square stax stripe taxjar threedsecureio trustpay tsys volt wellsfargo wellsfargopayout wise worldline worldpay zsl "$1") IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS res=`echo ${sorted[@]}` sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp diff --git a/v2_migrations/2024-07-30-100323_customer_v2/up.sql b/v2_migrations/2024-07-30-100323_customer_v2/up.sql index e84aa67fb709..7c2c5aee004c 100644 --- a/v2_migrations/2024-07-30-100323_customer_v2/up.sql +++ b/v2_migrations/2024-07-30-100323_customer_v2/up.sql @@ -7,6 +7,7 @@ ALTER TABLE customers ADD COLUMN IF NOT EXISTS default_shipping_address BYTEA DE ALTER TABLE customers DROP CONSTRAINT IF EXISTS customers_pkey; ALTER TABLE customers DROP COLUMN IF EXISTS id; + ALTER TABLE customers ADD COLUMN IF NOT EXISTS id VARCHAR(64); -- Back filling before making it primary key diff --git a/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql index 3d35becdfaba..94c02697c500 100644 --- a/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql +++ b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/down.sql @@ -1,18 +1,22 @@ -- This adds back dropped columns in `up.sql`. -- However, if the old columns were dropped, then we won't have data previously -- stored in these columns. -ALTER TABLE business_profile - ADD COLUMN routing_algorithm JSON DEFAULT NULL, +ALTER TABLE + business_profile +ADD + COLUMN routing_algorithm JSON DEFAULT NULL, -- ADD COLUMN intent_fulfillment_time BIGINT DEFAULT NULL, - ADD COLUMN frm_routing_algorithm JSONB DEFAULT NULL, - ADD COLUMN payout_routing_algorithm JSONB DEFAULT NULL; +ADD + COLUMN frm_routing_algorithm JSONB DEFAULT NULL, +ADD + COLUMN payout_routing_algorithm JSONB DEFAULT NULL; -ALTER TABLE business_profile - DROP COLUMN routing_algorithm_id, +ALTER TABLE + business_profile DROP COLUMN routing_algorithm_id, -- DROP COLUMN order_fulfillment_time, -- DROP COLUMN order_fulfillment_time_origin, DROP COLUMN frm_routing_algorithm_id, - DROP COLUMN payout_routing_algorithm_id; - -- DROP COLUMN default_fallback_routing; + DROP COLUMN payout_routing_algorithm_id, + DROP COLUMN default_fallback_routing; --- DROP TYPE "OrderFulfillmentTimeOrigin"; +-- DROP TYPE "OrderFulfillmentTimeOrigin"; \ No newline at end of file diff --git a/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql index 2b98bef00d46..d2275ad23626 100644 --- a/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql +++ b/v2_migrations/2024-07-31-100300_business_profile_add_v2_columns_drop_v1_columns/up.sql @@ -1,17 +1,21 @@ -- CREATE TYPE "OrderFulfillmentTimeOrigin" AS ENUM ('create', 'confirm'); - -ALTER TABLE business_profile - ADD COLUMN routing_algorithm_id VARCHAR(64) DEFAULT NULL, - -- ADD COLUMN order_fulfillment_time BIGINT DEFAULT NULL, - -- ADD COLUMN order_fulfillment_time_origin "OrderFulfillmentTimeOrigin" DEFAULT NULL, - ADD COLUMN frm_routing_algorithm_id VARCHAR(64) DEFAULT NULL, - ADD COLUMN payout_routing_algorithm_id VARCHAR(64) DEFAULT NULL; - -- ADD COLUMN default_fallback_routing JSONB DEFAULT NULL; +ALTER TABLE + business_profile +ADD + COLUMN routing_algorithm_id VARCHAR(64) DEFAULT NULL, + -- ADD COLUMN order_fulfillment_time BIGINT DEFAULT NULL, + -- ADD COLUMN order_fulfillment_time_origin "OrderFulfillmentTimeOrigin" DEFAULT NULL, +ADD + COLUMN frm_routing_algorithm_id VARCHAR(64) DEFAULT NULL, +ADD + COLUMN payout_routing_algorithm_id VARCHAR(64) DEFAULT NULL, +ADD + COLUMN default_fallback_routing JSONB DEFAULT NULL; -- Note: This query should not be run on higher environments as this leads to data loss. -- The application will work fine even without these queries being run. -ALTER TABLE business_profile - DROP COLUMN routing_algorithm, - -- DROP COLUMN intent_fulfillment_time, - DROP COLUMN frm_routing_algorithm, - DROP COLUMN payout_routing_algorithm; +ALTER TABLE + business_profile DROP COLUMN routing_algorithm, + -- DROP COLUMN intent_fulfillment_time, + DROP COLUMN frm_routing_algorithm, + DROP COLUMN payout_routing_algorithm; \ No newline at end of file diff --git a/v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/down.sql b/v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/down.sql new file mode 100644 index 000000000000..eca25b815ca7 --- /dev/null +++ b/v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_connector_account ALTER COLUMN profile_id DROP NOT NULL; \ No newline at end of file diff --git a/v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/up.sql b/v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/up.sql new file mode 100644 index 000000000000..216b8760d790 --- /dev/null +++ b/v2_migrations/2024-08-12-075913_make_profile_id_mandatory_in_mca/up.sql @@ -0,0 +1,3 @@ +-- Your SQL goes here +-- This migration is to make profile_id mandatory in mca table +ALTER TABLE merchant_connector_account ALTER COLUMN profile_id SET NOT NULL; From 0a3d45ec59e1bc47a9d6c96e5cd04fc01ce2f806 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 08:39:45 +0000 Subject: [PATCH 18/59] chore: run formatter --- crates/router/src/core/customers.rs | 5 +++- .../payment_methods/network_tokenization.rs | 24 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 594842039c52..107fd8007ae1 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -29,7 +29,10 @@ use crate::{ }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::{ - core::payment_methods::{cards, network_tokenization}, routes::metrics, types::storage::enums, utils::CustomerAddress, + core::payment_methods::{cards, network_tokenization}, + routes::metrics, + types::storage::enums, + utils::CustomerAddress, }; pub const REDACTED: &str = "Redacted"; diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index b58c0457413c..05bc863ecda9 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -175,15 +175,10 @@ pub async fn mk_tokenization_req( let key_id = tokenization_service.key_id.clone(); println!("payloadd bytess {:?}", payload_bytes); - let jwt = encryption::encrypt_jwe( - payload_bytes, - enc_key, - "A128GCM", - Some(key_id.as_str()), - ) - .await - .change_context(errors::NetworkTokenizationError::SaveTokenFailed) - .attach_printable("Error on jwe encrypt")?; + let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) + .await + .change_context(errors::NetworkTokenizationError::SaveTokenFailed) + .attach_printable("Error on jwe encrypt")?; let order_data = OrderData { consent_id: "test12324".to_string(), // ?? @@ -302,12 +297,15 @@ pub async fn make_card_network_tokenization_request( let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); - mk_tokenization_req(state, payload_bytes, amount_str, currency_str, customer_id.clone()) + mk_tokenization_req( + state, + payload_bytes, + amount_str, + currency_str, + customer_id.clone(), + ) .await .inspect_err(|e| logger::error!(error = %e, "Error while making tokenization request")) - - - } pub async fn get_token_from_tokenization_service( From ab5db97dda49a48f6d8b0171eabb0eacc4e4c475 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 21 Aug 2024 00:48:11 +0530 Subject: [PATCH 19/59] add support for token status check --- crates/router/src/configs/settings.rs | 1 + crates/router/src/core/customers.rs | 5 +- crates/router/src/core/errors.rs | 14 +- .../router/src/core/payment_methods/cards.rs | 25 --- .../payment_methods/network_tokenization.rs | 199 ++++++++++++++---- .../router/src/core/payments/tokenization.rs | 82 ++++---- 6 files changed, 213 insertions(+), 113 deletions(-) diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 7384d9e52ef0..8bc535f70995 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -410,6 +410,7 @@ pub struct NetworkTokenizationService { pub private_key: Secret, pub key_id: String, pub delete_token_url: String, + pub check_token_status_url: String, } #[derive(Debug, Deserialize, Clone)] diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 594842039c52..107fd8007ae1 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -29,7 +29,10 @@ use crate::{ }; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use crate::{ - core::payment_methods::{cards, network_tokenization}, routes::metrics, types::storage::enums, utils::CustomerAddress, + core::payment_methods::{cards, network_tokenization}, + routes::metrics, + types::storage::enums, + utils::CustomerAddress, }; pub const REDACTED: &str = "Redacted"; diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index f9d77148b06b..c22b9345736f 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -332,13 +332,13 @@ pub enum ConditionalConfigError { #[derive(Debug, thiserror::Error)] pub enum NetworkTokenizationError { - #[error("Failed to save card in card vault")] - SaveTokenFailed, - #[error("Failed to fetch card details from card vault")] - FetchTokenFailed, - #[error("Failed to encode card vault request")] + #[error("Failed to save network token in vault")] + SaveNetworkTokenFailed, + #[error("Failed to fetch network token details from vault")] + FetchNetworkTokenFailed, + #[error("Failed to encode network token vault request")] RequestEncodingFailed, - #[error("Failed to deserialize token service response")] + #[error("Failed to deserialize network token service response")] ResponseDeserializationFailed, #[error("Failed to create payment method")] PaymentMethodCreationFailed, @@ -356,4 +356,6 @@ pub enum NetworkTokenizationError { FetchPaymentMethodFailed, #[error("Failed to save payment method in vault")] SavePaymentMethodFailed, + #[error("Failed to delete network token")] + DeleteNetworkTokenFailed, } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index cb00fa3c4301..0579431bba8c 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -112,30 +112,6 @@ pub async fn create_payment_method( todo!() } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] -#[instrument(skip_all)] -#[allow(clippy::too_many_arguments)] -pub async fn create_payment_method( - state: &routes::SessionState, - req: &api::PaymentMethodCreate, - customer_id: &id_type::CustomerId, - payment_method_id: &str, - locker_id: Option, - merchant_id: &id_type::MerchantId, - pm_metadata: Option, - customer_acceptance: Option, - payment_method_data: Option, - key_store: &domain::MerchantKeyStore, - connector_mandate_details: Option, - status: Option, - network_transaction_id: Option, - storage_scheme: MerchantStorageScheme, - payment_method_billing_address: Option, - card_scheme: Option, -) -> errors::CustomResult { - todo!() -} - #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] @@ -1653,7 +1629,6 @@ pub async fn add_card_to_locker( errors::VaultError, > { metrics::STORED_TO_LOCKER.add(&metrics::CONTEXT, 1, &[]); - println!("add_card_to_lockerrr"); let add_card_to_hs_resp = Box::pin(common_utils::metrics::utils::record_operation_time( async { add_card_hs( diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index b58c0457413c..7554920ad4aa 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -61,6 +61,7 @@ pub struct ApiPayload { order_data: OrderData, key_id: String, should_send_token: bool, + sub_merchant_id: String, } #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] @@ -160,6 +161,31 @@ pub struct DeleteNetworkTokenResponse { status: DeleteNetworkTokenStatus, } +#[derive(Debug, Serialize, Deserialize)] +pub struct CheckTokenStatus { + card_reference: String, + customer_id: id_type::CustomerId, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum TokenStatus { + Active, + Inactive, +} + +#[derive(Debug, Deserialize)] +pub struct CheckTokenStatusResponsePayload { + token_expiry_month: Secret, + token_expiry_year: Secret, + token_status: TokenStatus, +} + +#[derive(Debug, Deserialize)] +pub struct CheckTokenStatusResponse { + payload: CheckTokenStatusResponsePayload, +} + pub async fn mk_tokenization_req( state: &routes::SessionState, payload_bytes: &[u8], @@ -169,11 +195,9 @@ pub async fn mk_tokenization_req( ) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError> { let tokenization_service = &state.conf.network_tokenization_service.get_inner(); - let enc_key = tokenization_service.public_key.peek().clone(); let key_id = tokenization_service.key_id.clone(); - println!("payloadd bytess {:?}", payload_bytes); let jwt = encryption::encrypt_jwe( payload_bytes, @@ -182,7 +206,7 @@ pub async fn mk_tokenization_req( Some(key_id.as_str()), ) .await - .change_context(errors::NetworkTokenizationError::SaveTokenFailed) + .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) .attach_printable("Error on jwe encrypt")?; let order_data = OrderData { @@ -198,6 +222,7 @@ pub async fn mk_tokenization_req( order_data, key_id, should_send_token: true, + sub_merchant_id: "visa_sbx_working".to_string(), }; let mut request = services::Request::new( @@ -215,8 +240,6 @@ pub async fn mk_tokenization_req( ); request.set_body(RequestContent::Json(Box::new(api_payload))); - println!("reqq to eulerr: {:?}", request); - let response = services::call_connector_api(state, request, "generate_token") .await .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); @@ -263,7 +286,7 @@ pub async fn mk_tokenization_req( jwe::RSA_OAEP_256, ) .await - .change_context(errors::NetworkTokenizationError::SaveTokenFailed) + .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) .attach_printable( "Failed to decrypt the tokenization response from the tokenization service", )?; @@ -297,17 +320,20 @@ pub async fn make_card_network_tokenization_request( logger::error!(fetch_err=?e); errors::NetworkTokenizationError::RequestEncodingFailed })?; - println!("payloaddd: {}", payload); + let payload_bytes = payload.as_bytes(); let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); - mk_tokenization_req(state, payload_bytes, amount_str, currency_str, customer_id.clone()) + mk_tokenization_req( + state, + payload_bytes, + amount_str, + currency_str, + customer_id.clone(), + ) .await .inspect_err(|e| logger::error!(error = %e, "Error while making tokenization request")) - - - } pub async fn get_token_from_tokenization_service( @@ -350,11 +376,11 @@ pub async fn get_token_from_tokenization_service( // Send the request using `call_connector_api` let response = services::call_connector_api(state, request, "get network token") .await - .change_context(errors::NetworkTokenizationError::SaveTokenFailed); + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); let res: TokenResponse = response .get_response_inner("cardNetworkTokenResponse") - .change_context(errors::VaultError::FetchCardFailed) + .change_context(errors::NetworkTokenizationError::FetchNetworkTokenFailed) .map_err(|e| { logger::error!(fetch_err=?e); errors::ApiErrorResponse::InternalServerError @@ -406,7 +432,6 @@ pub async fn do_status_check_for_network_token( state: &routes::SessionState, key_store: &domain::MerchantKeyStore, payment_method_info: &storage::PaymentMethod, - //input either network token ref or network token ) -> CustomResult { let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); @@ -432,12 +457,23 @@ pub async fn do_status_check_for_network_token( PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), _ => None, }); + let network_token_requestor_reference_id = + payment_method_info.network_token_reference_id.clone(); - is_token_active(token_data_decrypted) + is_token_active( + state, + token_data_decrypted, + payment_method_info.customer_id.clone(), + network_token_requestor_reference_id, + ) + .await } -fn is_token_active( +pub async fn is_token_active( //should be re-reviewed once more + state: &routes::SessionState, token_data_decrypted: Option, + customer_id: id_type::CustomerId, + network_token_requestor_reference_id: Option, ) -> CustomResult { if let Some(token_data) = token_data_decrypted { if let (Some(exp_month), Some(exp_year)) = (token_data.expiry_month, token_data.expiry_year) @@ -446,12 +482,80 @@ fn is_token_active( } Ok(true) } else { - check_token_status_with_tokenization_service() + if let Some(ref_id) = network_token_requestor_reference_id { + let status = check_token_status_with_tokenization_service(state, &customer_id, ref_id).await.change_context(errors::ApiErrorResponse::InternalServerError)?; + return Ok(status) + } + Ok(false) } } -fn check_token_status_with_tokenization_service() -> CustomResult { - Ok(true) +pub async fn check_token_status_with_tokenization_service( + state: &routes::SessionState, + customer_id: &id_type::CustomerId, + network_token_requestor_reference_id: String, +) -> CustomResult { + let tokenization_service = &state.conf.network_tokenization_service.get_inner(); + + let mut request = services::Request::new( + services::Method::Post, + tokenization_service.check_token_status_url.as_str(), + ); + let payload = CheckTokenStatus { + card_reference: network_token_requestor_reference_id, + customer_id: customer_id.clone(), + }; + + request.add_header(headers::CONTENT_TYPE, "application/json".into()); + request.add_header( + headers::AUTHORIZATION, + tokenization_service + .token_service_api_key + .clone() + .peek() + .clone() + .into_masked(), + ); + request.set_body(RequestContent::Json(Box::new(payload))); + + // Send the request using `call_connector_api` + let response = services::call_connector_api(state, request, "Check Network token Status") + .await + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); + let res = response + .map_err(|error| { + error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) + }) + .attach_printable("Error while receiving response") + .and_then(|inner| match inner { + Err(err_res) => { + let parsed_error: NetworkTokenErrorResponse = err_res + .response + .parse_struct("Delete Network Tokenization Response") + .change_context( + errors::NetworkTokenizationError::ResponseDeserializationFailed, + )?; + logger::error!( + error_code = %parsed_error.error_info.code, + developer_message = %parsed_error.error_info.developer_message, + "Network tokenization error: {}", + parsed_error.error_message + ); + Err(errors::NetworkTokenizationError::ResponseDeserializationFailed) + .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) + } + Ok(res) => Ok(res), + })?; + + let check_token_status_response: CheckTokenStatusResponse = res + .response + .parse_struct("Delete Network Tokenization Response") + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + + match check_token_status_response.payload.token_status { + TokenStatus::Active => Ok(true), + TokenStatus::Inactive => Ok(false), + } } pub async fn delete_network_token_from_locker_and_token_service( @@ -469,18 +573,17 @@ pub async fn delete_network_token_from_locker_and_token_service( token_locker_id.as_ref().unwrap_or(&payment_method_id), ) .await?; - let _delete_token_resp = delete_network_token_from_tokenization_service( + if delete_network_token_from_tokenization_service( state, network_token_requestor_reference_id, customer_id, ) - .await?; - - if resp.status == "Ok" { - logger::info!("Card From locker deleted Successfully!"); + .await + .is_ok() + { + logger::info!("Token From Tokenization Service deleted Successfully!"); } else { - logger::error!("Error: Deleting Card From Locker!\n{:#?}", resp); - Err(errors::ApiErrorResponse::InternalServerError)? + logger::error!("Error while deleting Token From Tokenization Service!"); } Ok(resp) @@ -490,7 +593,7 @@ pub async fn delete_network_token_from_tokenization_service( state: &routes::SessionState, network_token_requestor_reference_id: String, customer_id: &id_type::CustomerId, -) -> CustomResult { +) -> CustomResult { let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( services::Method::Post, @@ -516,23 +619,45 @@ pub async fn delete_network_token_from_tokenization_service( // Send the request using `call_connector_api` let response = services::call_connector_api(state, request, "delete network token") .await - .change_context(errors::VaultError::SaveCardFailed); - - let res: DeleteNTResponse = response - .get_response_inner("cardNetworkTokenResponse") - .change_context(errors::VaultError::FetchCardFailed) - .map_err(|e| { - logger::error!(fetch_err=?e); - errors::ApiErrorResponse::InternalServerError + .change_context(errors::NetworkTokenizationError::DeleteNetworkTokenFailed); + let res = response + .map_err(|error| { + error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) + }) + .attach_printable("Error while receiving response") + .and_then(|inner| match inner { + Err(err_res) => { + let parsed_error: NetworkTokenErrorResponse = err_res + .response + .parse_struct("Delete Network Tokenization Response") + .change_context( + errors::NetworkTokenizationError::ResponseDeserializationFailed, + )?; + logger::error!( + error_code = %parsed_error.error_info.code, + developer_message = %parsed_error.error_info.developer_message, + "Network tokenization error: {}", + parsed_error.error_message + ); + Err(errors::NetworkTokenizationError::ResponseDeserializationFailed) + .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) + } + Ok(res) => Ok(res), })?; - if res + let delete_token_response: DeleteNTResponse = res + .response + .parse_struct("Delete Network Tokenization Response") + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + + if delete_token_response == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { status: DeleteNetworkTokenStatus::Success, }) { Ok(true) } else { - Err(errors::ApiErrorResponse::InternalServerError)? + Err(errors::NetworkTokenizationError::DeleteNetworkTokenFailed) + .attach_printable(format!("Delete Token at Token service failed")) } } diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 3956b3df96ce..ce28b0ea3d86 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -216,7 +216,6 @@ where .await?; if is_network_tokenization_enabled { - println!("tokennn"); let (res2, dc2, network_token_requestor_ref_id) = Box::pin(save_in_locker( state, @@ -903,52 +902,47 @@ pub async fn save_token_in_locker( .card_networks; if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data { - println!("tokenn entered "); - println!("cardd {:?}", card); - if let Some(card_network) = &card.card_network { - println!("tokenn cn verified"); - if network_tokenization_supported_card_networks.contains(card_network) { - println!("calling tn serivce"); - if let Ok((token_response, network_token_requestor_ref_id)) = - network_tokenization::make_card_network_tokenization_request( - state, - &card, - &customer_id, - amount, - currency, - ) - .await - { - // Only proceed if the tokenization was successful - println!("tokenn success"); - let card_data = api::CardDetail { - card_number: token_response.token.clone(), - card_exp_month: token_response.token_expiry_month.clone(), - card_exp_year: token_response.token_expiry_year.clone(), - card_holder_name: None, - nick_name: None, - card_issuing_country: None, - card_network: Some(token_response.card_brand.clone()), - card_issuer: None, - card_type: None, - }; + // if let Some(card_network) = &card.card_network { + // if network_tokenization_supported_card_networks.contains(card_network) { + if let Ok((token_response, network_token_requestor_ref_id)) = + network_tokenization::make_card_network_tokenization_request( + state, + &card, + &customer_id, + amount, + currency, + ) + .await + { + // Only proceed if the tokenization was successful + let card_data = api::CardDetail { + card_number: token_response.token.clone(), + card_exp_month: token_response.token_expiry_month.clone(), + card_exp_year: token_response.token_expiry_year.clone(), + card_holder_name: None, + nick_name: None, + card_issuing_country: None, + card_network: Some(token_response.card_brand.clone()), + card_issuer: None, + card_type: None, + }; - let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( - state, - payment_method_request, - &card_data, - &customer_id, - merchant_account, - None, - )) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Card Failed")?; + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( + state, + payment_method_request, + &card_data, + &customer_id, + merchant_account, + None, + )) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Add Card Failed")?; - return Ok((res, dc, network_token_requestor_ref_id)); - } - } + return Ok((res, dc, network_token_requestor_ref_id)); } + // } + // } } let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); From 2df9150d6973241374e8b54c6f1dc8e58830a187 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:20:08 +0000 Subject: [PATCH 20/59] chore: run formatter --- .../src/core/payment_methods/network_tokenization.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 589d6774a060..38750c345f65 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -464,7 +464,8 @@ pub async fn do_status_check_for_network_token( .await } -pub async fn is_token_active( //should be re-reviewed once more +pub async fn is_token_active( + //should be re-reviewed once more state: &routes::SessionState, token_data_decrypted: Option, customer_id: id_type::CustomerId, @@ -478,8 +479,10 @@ pub async fn is_token_active( //should be re-reviewed once more Ok(true) } else { if let Some(ref_id) = network_token_requestor_reference_id { - let status = check_token_status_with_tokenization_service(state, &customer_id, ref_id).await.change_context(errors::ApiErrorResponse::InternalServerError)?; - return Ok(status) + let status = check_token_status_with_tokenization_service(state, &customer_id, ref_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + return Ok(status); } Ok(false) } From 3b7e2cc6c533b4af6d4dff024ecfa9e395f17eef Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 21 Aug 2024 00:50:27 +0530 Subject: [PATCH 21/59] add check_token_status_url in dev toml --- config/development.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/development.toml b/config/development.toml index c7c51bee035f..5b062606e415 100644 --- a/config/development.toml +++ b/config/development.toml @@ -729,3 +729,4 @@ public_key= "" private_key= "" key_id= "" delete_token_url= "" +check_token_status_url= "" \ No newline at end of file From 1f46ecbc0985f8f5e4c04b7a1e5be7b1d648a32e Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 21 Aug 2024 01:11:17 +0530 Subject: [PATCH 22/59] resolve pr comments - rename the variable name --- crates/diesel_models/src/payment_method.rs | 84 +++++++++---------- .../diesel_models/src/query/payment_method.rs | 8 -- crates/diesel_models/src/schema.rs | 6 +- .../src/connector/razorpay/transformers.rs | 2 +- crates/router/src/core/customers.rs | 4 +- crates/router/src/core/payment_methods.rs | 2 +- .../router/src/core/payment_methods/cards.rs | 42 +++++----- .../payment_methods/network_tokenization.rs | 10 +-- crates/router/src/core/payments.rs | 2 +- crates/router/src/core/payments/helpers.rs | 4 +- .../router/src/core/payments/tokenization.rs | 8 +- crates/router/src/core/pm_auth.rs | 6 +- crates/router/src/db/payment_method.rs | 6 +- crates/router/src/types/api/mandates.rs | 8 +- .../src/types/storage/payment_method.rs | 6 +- .../down.sql | 2 +- .../up.sql | 2 +- .../down.sql | 2 +- .../up.sql | 2 +- .../down.sql | 2 +- .../up.sql | 2 +- 21 files changed, 101 insertions(+), 109 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 87728c569e97..5b3acb4dcbe6 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -44,9 +44,9 @@ pub struct PaymentMethod { pub client_secret: Option, pub payment_method_billing_address: Option, pub updated_by: Option, - pub network_token_reference_id: Option, - pub token_locker_id: Option, - pub token_payment_method_data: Option, + pub network_token_requestor_reference_id: Option, + pub network_token_locker_id: Option, + pub network_token_payment_method_data: Option, } #[derive( @@ -84,9 +84,9 @@ pub struct PaymentMethodNew { pub client_secret: Option, pub payment_method_billing_address: Option, pub updated_by: Option, - pub network_token_reference_id: Option, - pub token_locker_id: Option, - pub token_payment_method_data: Option, + pub network_token_requestor_reference_id: Option, + pub network_token_locker_id: Option, + pub network_token_payment_method_data: Option, } impl PaymentMethodNew { @@ -128,12 +128,12 @@ pub enum PaymentMethodUpdate { payment_method_data: Option, status: Option, locker_id: Option, - network_token_reference_id: Option, + network_token_requestor_reference_id: Option, payment_method: Option, payment_method_type: Option, payment_method_issuer: Option, - token_locker_id: Option, - token_payment_method_data: Option, + network_token_locker_id: Option, + network_token_payment_method_data: Option, }, ConnectorMandateDetailsUpdate { connector_mandate_details: Option, @@ -162,14 +162,14 @@ pub struct PaymentMethodUpdateInternal { network_transaction_id: Option, status: Option, locker_id: Option, - network_token_reference_id: Option, + network_token_requestor_reference_id: Option, payment_method: Option, connector_mandate_details: Option, updated_by: Option, payment_method_type: Option, payment_method_issuer: Option, - token_locker_id: Option, - token_payment_method_data: Option, + network_token_locker_id: Option, + network_token_payment_method_data: Option, } impl PaymentMethodUpdateInternal { @@ -219,14 +219,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data, @@ -237,14 +237,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { metadata: None, @@ -253,14 +253,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { payment_method_data, @@ -272,14 +272,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status: None, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { network_transaction_id, @@ -291,14 +291,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id, status, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { metadata: None, @@ -307,25 +307,25 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, PaymentMethodUpdate::AdditionalDataUpdate { payment_method_data, status, locker_id, - network_token_reference_id, + network_token_requestor_reference_id, payment_method, payment_method_type, payment_method_issuer, - token_locker_id, - token_payment_method_data, + network_token_locker_id, + network_token_payment_method_data, } => Self { metadata: None, payment_method_data, @@ -333,14 +333,14 @@ impl From for PaymentMethodUpdateInternal { network_transaction_id: None, status, locker_id, - network_token_reference_id, + network_token_requestor_reference_id, payment_method, connector_mandate_details: None, updated_by: None, payment_method_issuer, payment_method_type, - token_locker_id, - token_payment_method_data, + network_token_locker_id, + network_token_payment_method_data, }, PaymentMethodUpdate::ConnectorMandateDetailsUpdate { connector_mandate_details, @@ -350,15 +350,15 @@ impl From for PaymentMethodUpdateInternal { last_used_at: None, status: None, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: None, connector_mandate_details, network_transaction_id: None, updated_by: None, payment_method_issuer: None, payment_method_type: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, } } @@ -371,7 +371,7 @@ impl From<&PaymentMethodNew> for PaymentMethod { merchant_id: payment_method_new.merchant_id.clone(), payment_method_id: payment_method_new.payment_method_id.clone(), locker_id: payment_method_new.locker_id.clone(), - network_token_reference_id: payment_method_new.network_token_reference_id.clone(), + network_token_requestor_reference_id: payment_method_new.network_token_requestor_reference_id.clone(), accepted_currency: payment_method_new.accepted_currency.clone(), scheme: payment_method_new.scheme.clone(), token: payment_method_new.token.clone(), @@ -400,8 +400,8 @@ impl From<&PaymentMethodNew> for PaymentMethod { payment_method_billing_address: payment_method_new .payment_method_billing_address .clone(), - token_locker_id: payment_method_new.token_locker_id.clone(), - token_payment_method_data: payment_method_new.token_payment_method_data.clone(), + network_token_locker_id: payment_method_new.network_token_locker_id.clone(), + network_token_payment_method_data: payment_method_new.network_token_payment_method_data.clone(), } } } diff --git a/crates/diesel_models/src/query/payment_method.rs b/crates/diesel_models/src/query/payment_method.rs index 259be1e17300..90bf455294d1 100644 --- a/crates/diesel_models/src/query/payment_method.rs +++ b/crates/diesel_models/src/query/payment_method.rs @@ -53,14 +53,6 @@ impl PaymentMethod { .await } - // pub async fn find_by_network_token_reference_id(conn: &PgPooledConn, network_token_reference_id: &str) -> StorageResult { - // generics::generic_find_one::<::Table, _, _>( - // conn, - // dsl::locker_id.eq(network_token_reference_id.to_owned()), - // ) - // .await - // } - pub async fn find_by_payment_method_id( conn: &PgPooledConn, payment_method_id: &str, diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 19468e49566c..ddd258e9fc65 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -988,10 +988,10 @@ diesel::table! { #[max_length = 64] updated_by -> Nullable, #[max_length = 64] - network_token_reference_id -> Nullable, + network_token_requestor_reference_id -> Nullable, #[max_length = 64] - token_locker_id -> Nullable, - token_payment_method_data -> Nullable, + network_token_locker_id -> Nullable, + network_token_payment_method_data -> Nullable, } } diff --git a/crates/router/src/connector/razorpay/transformers.rs b/crates/router/src/connector/razorpay/transformers.rs index 69d6bdce3848..621c0969aff0 100644 --- a/crates/router/src/connector/razorpay/transformers.rs +++ b/crates/router/src/connector/razorpay/transformers.rs @@ -152,7 +152,7 @@ pub struct MerchantAccount { reverse_token_enabled: Option, webhook_configs: Option, last_modified: Option, - token_locker_id: Option, + network_token_locker_id: Option, enable_sending_last_four_digits: Option, website: Option, mobile: Option, diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 107fd8007ae1..0d104993119b 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -541,13 +541,13 @@ pub async fn delete_customer( .await .switch()?; - if let Some(network_token_ref_id) = pm.network_token_reference_id { + if let Some(network_token_ref_id) = pm.network_token_requestor_reference_id { let resp = network_tokenization::delete_network_token_from_locker_and_token_service( &state, &req.customer_id, merchant_account.get_id(), pm.payment_method_id.clone(), - pm.token_locker_id, + pm.network_token_locker_id, network_token_ref_id, ) .await diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index ce7495af2736..c99afcade1e2 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -526,7 +526,7 @@ pub async fn retrieve_payment_method_with_token( helpers::retrieve_card_with_permanent_token( state, card_token - .token_locker_id + .network_token_locker_id .as_ref() .unwrap_or(card_token.locker_id.as_ref().unwrap_or(&card_token.token)), card_token diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 0579431bba8c..6f23523407f3 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -132,9 +132,9 @@ pub async fn create_payment_method( storage_scheme: MerchantStorageScheme, payment_method_billing_address: Option, card_scheme: Option, - network_token_reference_id: Option, - token_locker_id: Option, - token_payment_method_data: Option, + network_token_requestor_reference_id: Option, + network_token_locker_id: Option, + network_token_payment_method_data: Option, ) -> errors::CustomResult { let db = &*state.store; let customer = db @@ -188,9 +188,9 @@ pub async fn create_payment_method( last_used_at: current_time, payment_method_billing_address, updated_by: None, - network_token_reference_id, - token_locker_id, - token_payment_method_data, + network_token_requestor_reference_id, + network_token_locker_id, + network_token_payment_method_data, }, storage_scheme, ) @@ -594,9 +594,9 @@ pub async fn skip_locker_call_and_migrate_payment_method( last_used_at: current_time, payment_method_billing_address: payment_method_billing_address.map(Into::into), updated_by: None, - network_token_reference_id: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_requestor_reference_id: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }, merchant_account.storage_scheme, ) @@ -897,12 +897,12 @@ pub async fn add_payment_method_data( payment_method_data: Some(pm_data_encrypted.into()), status: Some(enums::PaymentMethodStatus::Active), locker_id: Some(locker_id), - network_token_reference_id: None, + network_token_requestor_reference_id: None, payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, - token_locker_id: None, // todo! - token_payment_method_data: None, //todo! + network_token_locker_id: None, // todo! + network_token_payment_method_data: None, //todo! }; db.update_payment_method( @@ -1221,9 +1221,9 @@ pub async fn insert_payment_method( network_transaction_id: Option, storage_scheme: MerchantStorageScheme, payment_method_billing_address: Option, - network_token_reference_id: Option, - token_locker_id: Option, - token_payment_method_data: Option, + network_token_requestor_reference_id: Option, + network_token_locker_id: Option, + network_token_payment_method_data: Option, ) -> errors::RouterResult { let pm_card_details = resp .card @@ -1258,9 +1258,9 @@ pub async fn insert_payment_method( card.card_network .map(|card_network| card_network.to_string()) }), - network_token_reference_id, - token_locker_id, - token_payment_method_data, + network_token_requestor_reference_id, + network_token_locker_id, + network_token_payment_method_data, ) .await } @@ -4140,7 +4140,7 @@ async fn get_pm_list_context( Some(pm.payment_method_id.clone()), pm.locker_id.clone().or(Some(pm.payment_method_id.clone())), pm.locker_id.clone().unwrap_or(pm.payment_method_id.clone()), - pm.network_token_reference_id + pm.network_token_requestor_reference_id .clone() .or(Some(pm.payment_method_id.clone())), ), @@ -5265,13 +5265,13 @@ pub async fn delete_payment_method( ) .await?; - if let Some(network_token_ref_id) = key.network_token_reference_id { + if let Some(network_token_ref_id) = key.network_token_requestor_reference_id { let resp = network_tokenization::delete_network_token_from_locker_and_token_service( &state, &key.customer_id, &key.merchant_id, key.payment_method_id.clone(), - key.token_locker_id, + key.network_token_locker_id, network_token_ref_id, ) .await?; diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 38750c345f65..fa8cfeb2171f 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -344,7 +344,7 @@ pub async fn get_token_from_tokenization_service( .await .change_context(errors::ApiErrorResponse::InternalServerError)?; - let token_ref = pm_data.clone().network_token_reference_id; + let token_ref = pm_data.clone().network_token_requestor_reference_id; let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( @@ -435,7 +435,7 @@ pub async fn do_status_check_for_network_token( &state.into(), type_name!(payment_method::PaymentMethod), domain::types::CryptoOperation::DecryptOptional( - payment_method_info.token_payment_method_data.clone(), + payment_method_info.network_token_payment_method_data.clone(), ), identifier, key, @@ -453,7 +453,7 @@ pub async fn do_status_check_for_network_token( _ => None, }); let network_token_requestor_reference_id = - payment_method_info.network_token_reference_id.clone(); + payment_method_info.network_token_requestor_reference_id.clone(); is_token_active( state, @@ -561,14 +561,14 @@ pub async fn delete_network_token_from_locker_and_token_service( customer_id: &id_type::CustomerId, merchant_id: &id_type::MerchantId, payment_method_id: String, - token_locker_id: Option, + network_token_locker_id: Option, network_token_requestor_reference_id: String, ) -> errors::RouterResult { let resp = payment_methods::cards::delete_card_from_locker( &state, customer_id, merchant_id, - token_locker_id.as_ref().unwrap_or(&payment_method_id), + network_token_locker_id.as_ref().unwrap_or(&payment_method_id), ) .await?; if delete_network_token_from_tokenization_service( diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 3f627de7dbc4..b485257e19fb 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3860,7 +3860,7 @@ pub async fn is_network_token_with_transaction_id_flow( && is_network_tokenization_enabled == Some(true) && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) && payment_method_info.network_transaction_id.is_some() - && payment_method_info.token_locker_id.is_some() + && payment_method_info.network_token_locker_id.is_some() && do_status_check_for_network_token(state, key_store, payment_method_info) .await .is_ok() diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index ce8954c72d11..d4421a4e525f 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2080,8 +2080,8 @@ pub async fn make_pm_data<'a, F: Clone, R>( .locker_id .clone() .unwrap_or(payment_method_info.payment_method_id.clone()), - token_locker_id: payment_method_info - .network_token_reference_id + network_token_locker_id: payment_method_info + .network_token_requestor_reference_id .clone() .or(Some(payment_method_info.payment_method_id.clone())), })); diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index ce28b0ea3d86..bffdaf4da1a8 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -235,7 +235,7 @@ where ((res, dc, None), None) } }; - let token_locker_id = match token_resp { + let network_token_locker_id = match token_resp { Some(ref token_resp) => { if network_token_requestor_ref_id.is_some() { Some(token_resp.payment_method_id.clone()) @@ -390,7 +390,7 @@ where .map(|card_network| card_network.to_string()) }), network_token_requestor_ref_id, //todo! - token_locker_id, + network_token_locker_id, pm_token_data_encrypted.map(Into::into), ) .await @@ -491,7 +491,7 @@ where }) }), network_token_requestor_ref_id, //todo! - token_locker_id, + network_token_locker_id, pm_token_data_encrypted.map(Into::into), ) .await @@ -691,7 +691,7 @@ where .map(|card_network| card_network.to_string()) }), network_token_requestor_ref_id, //todo! - token_locker_id, //todo! + network_token_locker_id, //todo! pm_token_data_encrypted.map(Into::into), //todo! ) .await?; diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 564b5b9e1fc5..3d3ba7192556 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -505,7 +505,7 @@ async fn store_bank_details_in_payment_methods( created_at: now, last_modified: now, locker_id: None, - network_token_reference_id: None, + network_token_requestor_reference_id: None, last_used_at: now, connector_mandate_details: None, customer_acceptance: None, @@ -513,8 +513,8 @@ async fn store_bank_details_in_payment_methods( client_secret: None, payment_method_billing_address: None, updated_by: None, - token_locker_id: None, - token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }; new_entries.push(pm_new); diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index e5f16409c9c3..4111444527dc 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -668,7 +668,7 @@ impl PaymentMethodInterface for MockDb { merchant_id: payment_method_new.merchant_id, payment_method_id: payment_method_new.payment_method_id, locker_id: payment_method_new.locker_id, - network_token_reference_id: payment_method_new.network_token_reference_id, + network_token_requestor_reference_id: payment_method_new.network_token_requestor_reference_id, accepted_currency: payment_method_new.accepted_currency, scheme: payment_method_new.scheme, token: payment_method_new.token, @@ -695,8 +695,8 @@ impl PaymentMethodInterface for MockDb { network_transaction_id: payment_method_new.network_transaction_id, updated_by: payment_method_new.updated_by, payment_method_billing_address: payment_method_new.payment_method_billing_address, - token_locker_id: payment_method_new.token_locker_id, - token_payment_method_data: payment_method_new.token_payment_method_data, + network_token_locker_id: payment_method_new.network_token_locker_id, + network_token_payment_method_data: payment_method_new.network_token_payment_method_data, }; payment_methods.push(payment_method.clone()); Ok(payment_method) diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index 5804b7d6749e..e0bf087a341a 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -52,13 +52,13 @@ impl MandateResponseExt for MandateResponse { .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) .attach_printable("payment_method not found")?; - // let token_data = if payment_method.token_locker_id.is_some() && state.conf.locker.locker_enabled { + // let token_data = if payment_method.network_token_locker_id.is_some() && state.conf.locker.locker_enabled { // let card = payment_methods::cards::get_card_from_locker( // state, // &payment_method.customer_id, // &payment_method.merchant_id, // payment_method - // .token_locker_id + // .network_token_locker_id // .as_ref() // .unwrap_or(&payment_method.payment_method_id), // ) @@ -95,13 +95,13 @@ impl MandateResponseExt for MandateResponse { // .change_context(errors::ApiErrorResponse::InternalServerError) // .attach_printable("Failed while getting card details")? let token_data = - if payment_method.token_locker_id.is_some() && state.conf.locker.locker_enabled { + if payment_method.network_token_locker_id.is_some() && state.conf.locker.locker_enabled { let card = payment_methods::cards::get_card_from_locker( state, &payment_method.customer_id, &payment_method.merchant_id, payment_method - .token_locker_id + .network_token_locker_id .as_ref() .unwrap_or(&payment_method.payment_method_id), ) diff --git a/crates/router/src/types/storage/payment_method.rs b/crates/router/src/types/storage/payment_method.rs index 1865460a02ba..4bc718e098d8 100644 --- a/crates/router/src/types/storage/payment_method.rs +++ b/crates/router/src/types/storage/payment_method.rs @@ -24,7 +24,7 @@ pub struct CardTokenData { pub payment_method_id: Option, pub locker_id: Option, pub token: String, - pub token_locker_id: Option, + pub network_token_locker_id: Option, } #[derive(Debug, Clone, serde::Serialize, Default, serde::Deserialize)] @@ -62,13 +62,13 @@ impl PaymentTokenData { payment_method_id: Option, locker_id: Option, token: String, - token_locker_id: Option, + network_token_locker_id: Option, ) -> Self { Self::PermanentCard(CardTokenData { payment_method_id, locker_id, token, - token_locker_id, + network_token_locker_id, }) } diff --git a/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql index edd6883468c6..275c96eebc59 100644 --- a/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql +++ b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_reference_id; \ No newline at end of file +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_requestor_reference_id; \ No newline at end of file diff --git a/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql index 2eebb7af1d4a..bd1be49ab1ff 100644 --- a/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql +++ b/migrations/2024-07-22-091927_add_network_token_reference_in_payment_method/up.sql @@ -1,2 +1,2 @@ -- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_reference_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_requestor_reference_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file diff --git a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql index 27382dac75d1..2ade0e4a2804 100644 --- a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql +++ b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql @@ -1,3 +1,3 @@ -- This file should undo anything in `up.sql` -ALTER TABLE payment_methods DROP COLUMN IF EXISTS token_locker_id; \ No newline at end of file +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_network_token_locker_id; \ No newline at end of file diff --git a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql index 90789cc85508..67e82107bdb1 100644 --- a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql +++ b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql @@ -1,2 +1,2 @@ -- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS token_locker_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_network_token_locker_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file diff --git a/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql index 1faeab3888a4..40361115f1e5 100644 --- a/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql +++ b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -ALTER TABLE payment_methods DROP COLUMN IF EXISTS token_payment_method_data; \ No newline at end of file +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_payment_method_data; \ No newline at end of file diff --git a/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql index 51b44f9b7250..2df1f33ec3d0 100644 --- a/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql +++ b/migrations/2024-08-05-195707_add_token_payment_method_data_in_payment_methods/up.sql @@ -1,2 +1,2 @@ -- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS token_payment_method_data BYTEA DEFAULT NULL; \ No newline at end of file +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_payment_method_data BYTEA DEFAULT NULL; \ No newline at end of file From 2e7a9fdd9cf4ce2b8720c28958e0a392c1ce8a1a Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 19:42:30 +0000 Subject: [PATCH 23/59] chore: run formatter --- crates/diesel_models/src/payment_method.rs | 8 +++- .../router/src/core/payment_methods/cards.rs | 2 +- .../payment_methods/network_tokenization.rs | 13 +++-- .../router/src/core/payments/tokenization.rs | 2 +- crates/router/src/db/payment_method.rs | 3 +- crates/router/src/types/api/mandates.rs | 47 ++++++++++--------- 6 files changed, 43 insertions(+), 32 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 5b3acb4dcbe6..c7c93572b3b7 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -371,7 +371,9 @@ impl From<&PaymentMethodNew> for PaymentMethod { merchant_id: payment_method_new.merchant_id.clone(), payment_method_id: payment_method_new.payment_method_id.clone(), locker_id: payment_method_new.locker_id.clone(), - network_token_requestor_reference_id: payment_method_new.network_token_requestor_reference_id.clone(), + network_token_requestor_reference_id: payment_method_new + .network_token_requestor_reference_id + .clone(), accepted_currency: payment_method_new.accepted_currency.clone(), scheme: payment_method_new.scheme.clone(), token: payment_method_new.token.clone(), @@ -401,7 +403,9 @@ impl From<&PaymentMethodNew> for PaymentMethod { .payment_method_billing_address .clone(), network_token_locker_id: payment_method_new.network_token_locker_id.clone(), - network_token_payment_method_data: payment_method_new.network_token_payment_method_data.clone(), + network_token_payment_method_data: payment_method_new + .network_token_payment_method_data + .clone(), } } } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 6f23523407f3..a20c5f226efe 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -901,7 +901,7 @@ pub async fn add_payment_method_data( payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, - network_token_locker_id: None, // todo! + network_token_locker_id: None, // todo! network_token_payment_method_data: None, //todo! }; diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index fa8cfeb2171f..9e811822fa79 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -435,7 +435,9 @@ pub async fn do_status_check_for_network_token( &state.into(), type_name!(payment_method::PaymentMethod), domain::types::CryptoOperation::DecryptOptional( - payment_method_info.network_token_payment_method_data.clone(), + payment_method_info + .network_token_payment_method_data + .clone(), ), identifier, key, @@ -452,8 +454,9 @@ pub async fn do_status_check_for_network_token( PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), _ => None, }); - let network_token_requestor_reference_id = - payment_method_info.network_token_requestor_reference_id.clone(); + let network_token_requestor_reference_id = payment_method_info + .network_token_requestor_reference_id + .clone(); is_token_active( state, @@ -568,7 +571,9 @@ pub async fn delete_network_token_from_locker_and_token_service( &state, customer_id, merchant_id, - network_token_locker_id.as_ref().unwrap_or(&payment_method_id), + network_token_locker_id + .as_ref() + .unwrap_or(&payment_method_id), ) .await?; if delete_network_token_from_tokenization_service( diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index bffdaf4da1a8..1f38b35a3abc 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -691,7 +691,7 @@ where .map(|card_network| card_network.to_string()) }), network_token_requestor_ref_id, //todo! - network_token_locker_id, //todo! + network_token_locker_id, //todo! pm_token_data_encrypted.map(Into::into), //todo! ) .await?; diff --git a/crates/router/src/db/payment_method.rs b/crates/router/src/db/payment_method.rs index 4111444527dc..5f32d58fad66 100644 --- a/crates/router/src/db/payment_method.rs +++ b/crates/router/src/db/payment_method.rs @@ -668,7 +668,8 @@ impl PaymentMethodInterface for MockDb { merchant_id: payment_method_new.merchant_id, payment_method_id: payment_method_new.payment_method_id, locker_id: payment_method_new.locker_id, - network_token_requestor_reference_id: payment_method_new.network_token_requestor_reference_id, + network_token_requestor_reference_id: payment_method_new + .network_token_requestor_reference_id, accepted_currency: payment_method_new.accepted_currency, scheme: payment_method_new.scheme, token: payment_method_new.token, diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index e0bf087a341a..b43ea7650fe5 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -94,30 +94,31 @@ impl MandateResponseExt for MandateResponse { // payment_methods::transformers::get_card_detail(&payment_method, card) // .change_context(errors::ApiErrorResponse::InternalServerError) // .attach_printable("Failed while getting card details")? - let token_data = - if payment_method.network_token_locker_id.is_some() && state.conf.locker.locker_enabled { - let card = payment_methods::cards::get_card_from_locker( - state, - &payment_method.customer_id, - &payment_method.merchant_id, - payment_method - .network_token_locker_id - .as_ref() - .unwrap_or(&payment_method.payment_method_id), - ) - .await?; + let token_data = if payment_method.network_token_locker_id.is_some() + && state.conf.locker.locker_enabled + { + let card = payment_methods::cards::get_card_from_locker( + state, + &payment_method.customer_id, + &payment_method.merchant_id, + payment_method + .network_token_locker_id + .as_ref() + .unwrap_or(&payment_method.payment_method_id), + ) + .await?; - payment_methods::transformers::get_card_detail(&payment_method, card) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while getting card details")? - } else { - payment_methods::cards::get_card_details_without_locker_fallback( - &payment_method, - state, - &key_store, - ) - .await? - }; + payment_methods::transformers::get_card_detail(&payment_method, card) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while getting card details")? + } else { + payment_methods::cards::get_card_details_without_locker_fallback( + &payment_method, + state, + &key_store, + ) + .await? + }; Some(MandateCardDetails::from(token_data).into_inner()) } else { From 20331b093fb042f15756854bc12e6534df9a69a4 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 23 Aug 2024 02:39:11 +0530 Subject: [PATCH 24/59] fix NT + NTI flow for mandates --- crates/api_models/src/payments.rs | 10 +- crates/diesel_models/src/payment_method.rs | 8 +- .../src/payment_method_data.rs | 2 +- .../src/configs/secrets_transformers.rs | 1 + .../src/connector/adyen/transformers.rs | 8 +- .../src/connector/cybersource/transformers.rs | 69 +++++++++- crates/router/src/core/payment_methods.rs | 7 +- .../payment_methods/network_tokenization.rs | 116 ++++++++-------- crates/router/src/core/payments.rs | 73 ++++++---- crates/router/src/core/payments/helpers.rs | 126 +++++++++++------- crates/router/src/types/api/mandates.rs | 87 ++++-------- 11 files changed, 286 insertions(+), 221 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index c498807a65e9..a06a9bb4d61e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -984,17 +984,15 @@ pub struct MandateIds { #[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone)] pub enum MandateReferenceId { ConnectorMandateId(ConnectorMandateReferenceId), // mandate_id send by connector - NetworkMandateId(String), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns - NetworkTokenWithNTI(String), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns along with network token + NetworkMandateId(String), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns along with card data + NetworkTokenWithNTI(NetworkTokenWithNTIRef), // network_txns_id send by Issuer to connector, Used for PG agnostic mandate txns along with network token data } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] pub struct NetworkTokenWithNTIRef { - pub network_token: CardNumber, pub network_transaction_id: String, - pub payment_method_id: Option, - pub token_exp_month: Option, - pub token_exp_year: Option, + pub token_exp_month: Option>, + pub token_exp_year: Option>, } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 5b3acb4dcbe6..c7c93572b3b7 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -371,7 +371,9 @@ impl From<&PaymentMethodNew> for PaymentMethod { merchant_id: payment_method_new.merchant_id.clone(), payment_method_id: payment_method_new.payment_method_id.clone(), locker_id: payment_method_new.locker_id.clone(), - network_token_requestor_reference_id: payment_method_new.network_token_requestor_reference_id.clone(), + network_token_requestor_reference_id: payment_method_new + .network_token_requestor_reference_id + .clone(), accepted_currency: payment_method_new.accepted_currency.clone(), scheme: payment_method_new.scheme.clone(), token: payment_method_new.token.clone(), @@ -401,7 +403,9 @@ impl From<&PaymentMethodNew> for PaymentMethod { .payment_method_billing_address .clone(), network_token_locker_id: payment_method_new.network_token_locker_id.clone(), - network_token_payment_method_data: payment_method_new.network_token_payment_method_data.clone(), + network_token_payment_method_data: payment_method_new + .network_token_payment_method_data + .clone(), } } } diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index cea72fc45d8a..8473fe9dcf2b 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -479,7 +479,7 @@ pub struct NetworkTokenData { pub token_number: cards::CardNumber, pub token_exp_month: Secret, pub token_exp_year: Secret, - pub token_cryptogram: Secret, + pub token_cryptogram: Option>, pub card_issuer: Option, pub card_network: Option, pub card_type: Option, diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 9d3afc51667d..7cafdc5fb096 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -403,6 +403,7 @@ pub(crate) async fn fetch_raw_secrets( .await .expect("Failed to decrypt user_auth_methods configs"); + #[allow(clippy::expect_used)] let network_tokenization_service = settings::NetworkTokenizationService::convert_to_raw_secret( conf.network_tokenization_service, secret_management_client, diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 79e51879c474..930073dd2b48 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2594,7 +2594,7 @@ impl<'a> } } } - payments::MandateReferenceId::NetworkTokenWithNTI(network_mandate_id) => { + payments::MandateReferenceId::NetworkTokenWithNTI(mandate_data) => { match item.router_data.request.payment_method_data { domain::PaymentMethodData::NetworkToken(ref token_data) => { let card_issuer = token_data.get_card_issuer()?; @@ -2606,8 +2606,8 @@ impl<'a> expiry_month: token_data.token_exp_month.clone(), expiry_year: token_data.get_expiry_year_4_digit(), holder_name: card_holder_name, - brand: Some(brand), // FIXME: Remove hardcoding - network_payment_reference: Some(Secret::new(network_mandate_id)), + brand: Some(brand), + network_payment_reference: Some(Secret::new(mandate_data.network_transaction_id)), }; Ok(AdyenPaymentMethod::NetworkToken(Box::new( adyen_network_token, @@ -4991,7 +4991,7 @@ impl<'a> let mpi_data = MpiData { directory_response: "Y".to_string(), authentication_response: "Y".to_string(), - token_authentication_verification_value: token_data.token_cryptogram.clone(), + token_authentication_verification_value: token_data.token_cryptogram.clone().unwrap_or_default(), eci: "07".to_string(), }; diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index ab0468088c5c..46d551083735 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -711,7 +711,72 @@ impl }), ) } - Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) => todo!(), // + Some(payments::MandateReferenceId::NetworkTokenWithNTI(mandate_data)) => { + let (original_amount, original_currency) = match network + .clone() + .map(|network| network.to_lowercase()) + .as_deref() + { + //This is to make original_authorized_amount mandatory for discover card networks in NetworkMandateId flow + Some("004") => { + let original_amount = Some( + item.router_data + .get_recurring_mandate_payment_data()? + .get_original_payment_amount()?, + ); + let original_currency = Some( + item.router_data + .get_recurring_mandate_payment_data()? + .get_original_payment_currency()?, + ); + (original_amount, original_currency) + } + _ => { + let original_amount = item + .router_data + .recurring_mandate_payment_data + .as_ref() + .and_then(|recurring_mandate_payment_data| { + recurring_mandate_payment_data + .original_payment_authorized_amount + }); + + let original_currency = item + .router_data + .recurring_mandate_payment_data + .as_ref() + .and_then(|recurring_mandate_payment_data| { + recurring_mandate_payment_data + .original_payment_authorized_currency + }); + + (original_amount, original_currency) + } + }; + let original_authorized_amount = match original_amount.zip(original_currency) { + Some((original_amount, original_currency)) => Some( + utils::to_currency_base_unit(original_amount, original_currency)?, + ), + None => None, + }; + commerce_indicator = "recurring".to_string(); + ( + None, + None, + Some(CybersourceAuthorizationOptions { + initiator: Some(CybersourcePaymentInitiator { + initiator_type: Some(CybersourcePaymentInitiatorTypes::Merchant), + credential_stored_on_file: None, + stored_credential_used: Some(true), + }), + merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { + reason: Some("7".to_string()), + original_authorized_amount, + previous_transaction_id: Some(Secret::new(mandate_data.network_transaction_id)), + }), + }), + ) + } None => (None, None, None), } } else { @@ -1100,7 +1165,7 @@ impl number: token_data.token_number, expiration_month: token_data.token_exp_month, expiration_year: token_data.token_exp_year, - cryptogram: token_data.token_cryptogram, + cryptogram: token_data.token_cryptogram.clone().unwrap_or_default(), transaction_type: "1".to_string(), }, })); diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index c99afcade1e2..716b4ae9583a 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -451,6 +451,7 @@ pub async fn retrieve_payment_method_with_token( card_token_data: Option<&domain::CardToken>, customer: &Option, storage_scheme: common_enums::enums::MerchantStorageScheme, + mandate_id: Option, ) -> RouterResult { let token = match token_data { storage::PaymentTokenData::TemporaryGeneric(generic_token) => { @@ -503,6 +504,7 @@ pub async fn retrieve_payment_method_with_token( card_token_data, merchant_key_store, storage_scheme, + mandate_id, ) .await .map(|card| Some((card, enums::PaymentMethod::Card)))? @@ -526,9 +528,9 @@ pub async fn retrieve_payment_method_with_token( helpers::retrieve_card_with_permanent_token( state, card_token - .network_token_locker_id + .locker_id .as_ref() - .unwrap_or(card_token.locker_id.as_ref().unwrap_or(&card_token.token)), + .unwrap_or(&card_token.token), card_token .payment_method_id .as_ref() @@ -537,6 +539,7 @@ pub async fn retrieve_payment_method_with_token( card_token_data, merchant_key_store, storage_scheme, + mandate_id, ) .await .map(|card| Some((card, enums::PaymentMethod::Card)))? diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index fa8cfeb2171f..30beed8a5df4 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -175,6 +175,7 @@ pub enum TokenStatus { } #[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct CheckTokenStatusResponsePayload { token_expiry_month: Secret, token_expiry_year: Secret, @@ -199,10 +200,15 @@ pub async fn mk_tokenization_req( let key_id = tokenization_service.key_id.clone(); - let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) - .await - .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) - .attach_printable("Error on jwe encrypt")?; + let jwt = encryption::encrypt_jwe( + payload_bytes, + enc_key, + "A128GCM", + Some(key_id.as_str()), + ) + .await + .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) + .attach_printable("Error on jwe encrypt")?; let order_data = OrderData { consent_id: "test12324".to_string(), // ?? @@ -333,27 +339,18 @@ pub async fn make_card_network_tokenization_request( pub async fn get_token_from_tokenization_service( state: &routes::SessionState, - merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - pm_id: String, //to fetch from pm table + network_token_requestor_ref_id: String, + pm_data: &storage::PaymentMethod, ) -> errors::RouterResult { - let db = state.store.as_ref(); - let key = key_store.key.get_inner().peek(); - let pm_data = db - .find_payment_method(&pm_id, merchant_account.storage_scheme) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - - let token_ref = pm_data.clone().network_token_requestor_reference_id; - let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( services::Method::Post, tokenization_service.fetch_token_url.as_str(), ); let payload = GetCardToken { - card_reference: token_ref.unwrap(), - customer_id: pm_data.clone().customer_id, + card_reference: network_token_requestor_ref_id, + customer_id: pm_data.customer_id.clone(), }; request.add_header(headers::CONTENT_TYPE, "application/json".into()); @@ -380,11 +377,12 @@ pub async fn get_token_from_tokenization_service( logger::error!(fetch_err=?e); errors::ApiErrorResponse::InternalServerError })?; + let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); - let card_decrypted = domain::types::crypto_operation::( + let token_decrypted = domain::types::crypto_operation::( &state.into(), type_name!(payment_method::PaymentMethod), - domain::types::CryptoOperation::DecryptOptional(pm_data.payment_method_data.clone()), + domain::types::CryptoOperation::DecryptOptional(pm_data.network_token_payment_method_data.clone()), identifier, key, ) @@ -400,42 +398,44 @@ pub async fn get_token_from_tokenization_service( PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), _ => None, }); - let card_data = NetworkTokenData { + let network_token_data = NetworkTokenData { token_number: res.authentication_details.token, - token_cryptogram: res.authentication_details.cryptogram, - token_exp_month: card_decrypted + token_cryptogram: Some(res.authentication_details.cryptogram), + token_exp_month: token_decrypted .clone() .unwrap() .expiry_month .unwrap_or_default(), - token_exp_year: card_decrypted + token_exp_year: token_decrypted .clone() .unwrap() .expiry_year .unwrap_or_default(), - nick_name: card_decrypted.clone().unwrap().card_holder_name, + nick_name: token_decrypted.clone().unwrap().card_holder_name, card_issuer: None, card_network: Some(res.network), card_type: None, card_issuing_country: None, bank_code: None, }; - Ok(card_data) + Ok(network_token_data) } pub async fn do_status_check_for_network_token( state: &routes::SessionState, key_store: &domain::MerchantKeyStore, payment_method_info: &storage::PaymentMethod, -) -> CustomResult { +) -> CustomResult<(Option>, Option>), errors::ApiErrorResponse> { let key = key_store.key.get_inner().peek(); let identifier = Identifier::Merchant(key_store.merchant_id.clone()); - let token_data_decrypted = + let network_token_data_decrypted = domain::types::crypto_operation::( &state.into(), type_name!(payment_method::PaymentMethod), domain::types::CryptoOperation::DecryptOptional( - payment_method_info.network_token_payment_method_data.clone(), + payment_method_info + .network_token_payment_method_data + .clone(), ), identifier, key, @@ -452,39 +452,25 @@ pub async fn do_status_check_for_network_token( PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), _ => None, }); - let network_token_requestor_reference_id = - payment_method_info.network_token_requestor_reference_id.clone(); - - is_token_active( - state, - token_data_decrypted, - payment_method_info.customer_id.clone(), - network_token_requestor_reference_id, - ) - .await -} - -pub async fn is_token_active( - //should be re-reviewed once more - state: &routes::SessionState, - token_data_decrypted: Option, - customer_id: id_type::CustomerId, - network_token_requestor_reference_id: Option, -) -> CustomResult { - if let Some(token_data) = token_data_decrypted { - if let (Some(exp_month), Some(exp_year)) = (token_data.expiry_month, token_data.expiry_year) - { - helpers::validate_card_expiry(&exp_month, &exp_year)?; - } - Ok(true) - } else { + let network_token_requestor_reference_id = payment_method_info + .network_token_requestor_reference_id + .clone(); + if network_token_data_decrypted.and_then(|token_data|{ + token_data.expiry_month.zip(token_data.expiry_year) + }).and_then(|(exp_month, exp_year)|{ + helpers::validate_card_expiry(&exp_month, &exp_year).ok() + }).is_none(){ if let Some(ref_id) = network_token_requestor_reference_id { - let status = check_token_status_with_tokenization_service(state, &customer_id, ref_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - return Ok(status); + let (token_exp_month, token_exp_year) = + check_token_status_with_tokenization_service(state, &payment_method_info.customer_id.clone(), ref_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + Ok((token_exp_month,token_exp_year)) + }else{ + Err(errors::NetworkTokenizationError::FetchNetworkTokenFailed).change_context(errors::ApiErrorResponse::InternalServerError)? } - Ok(false) + }else{ + Ok((None, None)) } } @@ -492,7 +478,7 @@ pub async fn check_token_status_with_tokenization_service( state: &routes::SessionState, customer_id: &id_type::CustomerId, network_token_requestor_reference_id: String, -) -> CustomResult { +) -> CustomResult<( Option>, Option>), errors::NetworkTokenizationError> { let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( @@ -551,8 +537,8 @@ pub async fn check_token_status_with_tokenization_service( .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; match check_token_status_response.payload.token_status { - TokenStatus::Active => Ok(true), - TokenStatus::Inactive => Ok(false), + TokenStatus::Active => Ok((Some(check_token_status_response.payload.token_expiry_month), Some(check_token_status_response.payload.token_expiry_year))), + TokenStatus::Inactive => Ok(( None, None)), } } @@ -565,10 +551,12 @@ pub async fn delete_network_token_from_locker_and_token_service( network_token_requestor_reference_id: String, ) -> errors::RouterResult { let resp = payment_methods::cards::delete_card_from_locker( - &state, + state, customer_id, merchant_id, - network_token_locker_id.as_ref().unwrap_or(&payment_method_id), + network_token_locker_id + .as_ref() + .unwrap_or(&payment_method_id), ) .await?; if delete_network_token_from_tokenization_service( diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b485257e19fb..d97d740887b1 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3693,33 +3693,50 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, - is_network_tokenization_enabled: Option, + is_network_tokenization_enabled: bool, payment_method_info: &storage::PaymentMethod, ) -> bool { is_connector_agnostic_mit_enabled == Some(true) - && is_network_tokenization_enabled == Some(true) + && is_network_tokenization_enabled == true && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) && payment_method_info.network_transaction_id.is_some() && payment_method_info.network_token_locker_id.is_some() - && do_status_check_for_network_token(state, key_store, payment_method_info) - .await - .is_ok() + && payment_method_info + .network_token_requestor_reference_id + .is_some() } pub fn should_add_task_to_process_tracker(payment_data: &PaymentData) -> bool { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index d4421a4e525f..8bc0313d188c 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1877,6 +1877,7 @@ pub async fn retrieve_card_with_permanent_token( card_token_data: Option<&domain::CardToken>, merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, + mandate_id: Option, ) -> RouterResult { let customer_id = payment_intent .customer_id @@ -1885,23 +1886,6 @@ pub async fn retrieve_card_with_permanent_token( .change_context(errors::ApiErrorResponse::UnprocessableEntity { message: "no customer id provided for the payment".to_string(), })?; - // let card = - // cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) - // .await - // .change_context(errors::ApiErrorResponse::InternalServerError) - // .attach_printable("failed to fetch card information from the permanent locker")?; - - // let name_on_card = if let Some(name) = card.name_on_card.clone() { - // if name.clone().expose().is_empty() { - // card_token_data - // .and_then(|token_data| token_data.card_holder_name.clone()) - // .or(Some(name)) - // } else { - // card.clone().name_on_card - // } - // } else { - // card_token_data.and_then(|token_data| token_data.card_holder_name.clone()) - // }; let db = state.store.as_ref(); let key_manager_state = &state.into(); @@ -1913,42 +1897,85 @@ pub async fn retrieve_card_with_permanent_token( ) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; + if merchant_account.is_network_tokenization_enabled { - let network_token_data = network_tokenization::get_token_from_tokenization_service( - state, - &merchant_account, - merchant_key_store, - payment_method_id.to_string(), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) - } else { - let card = - cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) + let pm_data = db + .find_payment_method( + &payment_method_id.to_string(), + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + if let Some(mandate_ids) = mandate_id { + if let Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) = + mandate_ids.mandate_reference_id + { + let mut token_data = cards::get_card_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + pm_data.network_token_locker_id.as_ref().unwrap(), + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed to fetch card information from the permanent locker")?; - let api_card = api::Card { - card_number: card.card_number, - card_holder_name: None, - card_exp_month: card.card_exp_month, - card_exp_year: card.card_exp_year, - card_cvc: card_token_data - .cloned() - .unwrap_or_default() - .card_cvc - .unwrap_or_default(), - card_issuer: None, - nick_name: card.nick_name.map(masking::Secret::new), - card_network: None, - card_type: None, - card_issuing_country: None, - bank_code: None, - }; + let x = nt_data.token_exp_month.zip(nt_data.token_exp_year); + if let Some((exp_month, exp_year)) = x { + token_data.card_exp_month = exp_month; + token_data.card_exp_year = exp_year; + } + let network_token_data = domain::NetworkTokenData { + token_number: token_data.card_number, + token_cryptogram: None, + token_exp_month: token_data.card_exp_month, + token_exp_year: token_data.card_exp_year, + nick_name: token_data.nick_name.map(masking::Secret::new), + card_issuer: None, + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + } + } - Ok(domain::PaymentMethodData::Card(api_card.into())) + if let Some(token_ref) = pm_data.clone().network_token_requestor_reference_id { + let network_token_data = network_tokenization::get_token_from_tokenization_service( + state, + merchant_key_store, + token_ref, + &pm_data, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + } } + + let card = + cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("failed to fetch card information from the permanent locker")?; + let api_card = api::Card { + card_number: card.card_number, + card_holder_name: None, + card_exp_month: card.card_exp_month, + card_exp_year: card.card_exp_year, + card_cvc: card_token_data + .cloned() + .unwrap_or_default() + .card_cvc + .unwrap_or_default(), + card_issuer: None, + nick_name: card.nick_name.map(masking::Secret::new), + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + Ok(domain::PaymentMethodData::Card(api_card.into())) } pub async fn retrieve_payment_method_from_db_with_token_data( @@ -2089,6 +2116,8 @@ pub async fn make_pm_data<'a, F: Clone, R>( } } + let mandate_id = payment_data.mandate_id.clone(); + // TODO: Handle case where payment method and token both are present in request properly. let (payment_method, pm_id) = match (&request, payment_data.token_data.as_ref()) { (_, Some(hyperswitch_token)) => { @@ -2100,6 +2129,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( card_token_data.as_ref(), customer, storage_scheme, + mandate_id, ) .await; @@ -4964,6 +4994,7 @@ pub async fn get_payment_method_details_from_payment_token( None, key_store, storage_scheme, + None, ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), @@ -4979,6 +5010,7 @@ pub async fn get_payment_method_details_from_payment_token( None, key_store, storage_scheme, + None, ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index e0bf087a341a..686f7f3ce495 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -52,74 +52,33 @@ impl MandateResponseExt for MandateResponse { .change_context(errors::ApiErrorResponse::PaymentMethodNotFound) .attach_printable("payment_method not found")?; - // let token_data = if payment_method.network_token_locker_id.is_some() && state.conf.locker.locker_enabled { - // let card = payment_methods::cards::get_card_from_locker( - // state, - // &payment_method.customer_id, - // &payment_method.merchant_id, - // payment_method - // .network_token_locker_id - // .as_ref() - // .unwrap_or(&payment_method.payment_method_id), - // ) - // .await?; - - // payment_methods::transformers::get_card_detail(&payment_method, card) - // .change_context(errors::ApiErrorResponse::InternalServerError) - // .attach_printable("Failed while getting card details")? - // } else { - // payment_methods::cards::get_card_details_without_locker_fallback( - // &payment_method, - // state, - // &key_store, - // ) - // .await? - // }; - let card = if pm == storage_enums::PaymentMethod::Card { // if locker is disabled , decrypt the payment method data - // let card_details = if state.conf.locker.locker_enabled { - // let card = payment_methods::cards::get_card_from_locker( - // // get network token details here? instead of that status check? - // state, - // &payment_method.customer_id, - // &payment_method.merchant_id, - // payment_method - // .locker_id - // .as_ref() - // .unwrap_or(&payment_method.payment_method_id), - // ) - // .await?; + let card_details = if state.conf.locker.locker_enabled { + let card = payment_methods::cards::get_card_from_locker( + state, + &payment_method.customer_id, + &payment_method.merchant_id, + payment_method + .locker_id + .as_ref() + .unwrap_or(&payment_method.payment_method_id), + ) + .await?; - // payment_methods::transformers::get_card_detail(&payment_method, card) - // .change_context(errors::ApiErrorResponse::InternalServerError) - // .attach_printable("Failed while getting card details")? - let token_data = - if payment_method.network_token_locker_id.is_some() && state.conf.locker.locker_enabled { - let card = payment_methods::cards::get_card_from_locker( - state, - &payment_method.customer_id, - &payment_method.merchant_id, - payment_method - .network_token_locker_id - .as_ref() - .unwrap_or(&payment_method.payment_method_id), - ) - .await?; - - payment_methods::transformers::get_card_detail(&payment_method, card) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while getting card details")? - } else { - payment_methods::cards::get_card_details_without_locker_fallback( - &payment_method, - state, - &key_store, - ) - .await? - }; + payment_methods::transformers::get_card_detail(&payment_method, card) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while getting card details")? + } else { + payment_methods::cards::get_card_details_without_locker_fallback( + &payment_method, + state, + &key_store, + ) + .await? + }; - Some(MandateCardDetails::from(token_data).into_inner()) + Some(MandateCardDetails::from(card_details).into_inner()) } else { None }; From 34bac1bf862d5d7752b14665dc4bd0ec76b50fe6 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Thu, 22 Aug 2024 21:13:33 +0000 Subject: [PATCH 25/59] chore: run formatter --- .../src/connector/adyen/transformers.rs | 11 +++- .../src/connector/cybersource/transformers.rs | 4 +- crates/router/src/core/payment_methods.rs | 5 +- .../payment_methods/network_tokenization.rs | 59 ++++++++++--------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 930073dd2b48..8bfd7834e9d6 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2606,8 +2606,10 @@ impl<'a> expiry_month: token_data.token_exp_month.clone(), expiry_year: token_data.get_expiry_year_4_digit(), holder_name: card_holder_name, - brand: Some(brand), - network_payment_reference: Some(Secret::new(mandate_data.network_transaction_id)), + brand: Some(brand), + network_payment_reference: Some(Secret::new( + mandate_data.network_transaction_id, + )), }; Ok(AdyenPaymentMethod::NetworkToken(Box::new( adyen_network_token, @@ -4991,7 +4993,10 @@ impl<'a> let mpi_data = MpiData { directory_response: "Y".to_string(), authentication_response: "Y".to_string(), - token_authentication_verification_value: token_data.token_cryptogram.clone().unwrap_or_default(), + token_authentication_verification_value: token_data + .token_cryptogram + .clone() + .unwrap_or_default(), eci: "07".to_string(), }; diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 46d551083735..5f874b0cbc11 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -772,7 +772,9 @@ impl merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { reason: Some("7".to_string()), original_authorized_amount, - previous_transaction_id: Some(Secret::new(mandate_data.network_transaction_id)), + previous_transaction_id: Some(Secret::new( + mandate_data.network_transaction_id, + )), }), }), ) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 716b4ae9583a..35267c0da0db 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -527,10 +527,7 @@ pub async fn retrieve_payment_method_with_token( storage::PaymentTokenData::PermanentCard(card_token) => { helpers::retrieve_card_with_permanent_token( state, - card_token - .locker_id - .as_ref() - .unwrap_or(&card_token.token), + card_token.locker_id.as_ref().unwrap_or(&card_token.token), card_token .payment_method_id .as_ref() diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 30beed8a5df4..6c28131cfe49 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -200,15 +200,10 @@ pub async fn mk_tokenization_req( let key_id = tokenization_service.key_id.clone(); - let jwt = encryption::encrypt_jwe( - payload_bytes, - enc_key, - "A128GCM", - Some(key_id.as_str()), - ) - .await - .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) - .attach_printable("Error on jwe encrypt")?; + let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, "A128GCM", Some(key_id.as_str())) + .await + .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) + .attach_printable("Error on jwe encrypt")?; let order_data = OrderData { consent_id: "test12324".to_string(), // ?? @@ -340,7 +335,7 @@ pub async fn make_card_network_tokenization_request( pub async fn get_token_from_tokenization_service( state: &routes::SessionState, key_store: &domain::MerchantKeyStore, - network_token_requestor_ref_id: String, + network_token_requestor_ref_id: String, pm_data: &storage::PaymentMethod, ) -> errors::RouterResult { let tokenization_service = &state.conf.network_tokenization_service.get_inner(); @@ -382,7 +377,9 @@ pub async fn get_token_from_tokenization_service( let token_decrypted = domain::types::crypto_operation::( &state.into(), type_name!(payment_method::PaymentMethod), - domain::types::CryptoOperation::DecryptOptional(pm_data.network_token_payment_method_data.clone()), + domain::types::CryptoOperation::DecryptOptional( + pm_data.network_token_payment_method_data.clone(), + ), identifier, key, ) @@ -455,21 +452,25 @@ pub async fn do_status_check_for_network_token( let network_token_requestor_reference_id = payment_method_info .network_token_requestor_reference_id .clone(); - if network_token_data_decrypted.and_then(|token_data|{ - token_data.expiry_month.zip(token_data.expiry_year) - }).and_then(|(exp_month, exp_year)|{ - helpers::validate_card_expiry(&exp_month, &exp_year).ok() - }).is_none(){ + if network_token_data_decrypted + .and_then(|token_data| token_data.expiry_month.zip(token_data.expiry_year)) + .and_then(|(exp_month, exp_year)| helpers::validate_card_expiry(&exp_month, &exp_year).ok()) + .is_none() + { if let Some(ref_id) = network_token_requestor_reference_id { - let (token_exp_month, token_exp_year) = - check_token_status_with_tokenization_service(state, &payment_method_info.customer_id.clone(), ref_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - Ok((token_exp_month,token_exp_year)) - }else{ - Err(errors::NetworkTokenizationError::FetchNetworkTokenFailed).change_context(errors::ApiErrorResponse::InternalServerError)? + let (token_exp_month, token_exp_year) = check_token_status_with_tokenization_service( + state, + &payment_method_info.customer_id.clone(), + ref_id, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + Ok((token_exp_month, token_exp_year)) + } else { + Err(errors::NetworkTokenizationError::FetchNetworkTokenFailed) + .change_context(errors::ApiErrorResponse::InternalServerError)? } - }else{ + } else { Ok((None, None)) } } @@ -478,7 +479,8 @@ pub async fn check_token_status_with_tokenization_service( state: &routes::SessionState, customer_id: &id_type::CustomerId, network_token_requestor_reference_id: String, -) -> CustomResult<( Option>, Option>), errors::NetworkTokenizationError> { +) -> CustomResult<(Option>, Option>), errors::NetworkTokenizationError> +{ let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( @@ -537,8 +539,11 @@ pub async fn check_token_status_with_tokenization_service( .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; match check_token_status_response.payload.token_status { - TokenStatus::Active => Ok((Some(check_token_status_response.payload.token_expiry_month), Some(check_token_status_response.payload.token_expiry_year))), - TokenStatus::Inactive => Ok(( None, None)), + TokenStatus::Active => Ok(( + Some(check_token_status_response.payload.token_expiry_month), + Some(check_token_status_response.payload.token_expiry_year), + )), + TokenStatus::Inactive => Ok((None, None)), } } From 39de00b27d69e6069463e5f9c715eb1e350a8dc8 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 23 Aug 2024 23:51:01 +0530 Subject: [PATCH 26/59] resolve pr comments --- config/deployments/env_specific.toml | 10 ++++++ config/docker_compose.toml | 12 ++++++- crates/common_enums/src/enums.rs | 9 ++++++ .../src/payment_method_data.rs | 31 ------------------- .../src/connector/adyen/transformers.rs | 2 +- .../connector/authorizedotnet/transformers.rs | 8 ++++- .../router/src/core/blocklist/transformers.rs | 4 +-- .../payment_methods/network_tokenization.rs | 15 +++------ .../src/core/payment_methods/transformers.rs | 4 +-- crates/router/src/core/payments.rs | 4 +-- .../payments/operations/payment_confirm.rs | 2 +- .../router/src/core/payments/tokenization.rs | 8 ++--- crates/router/src/services/encryption.rs | 13 ++++++-- 13 files changed, 63 insertions(+), 59 deletions(-) diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index a7bd116a3228..eba24cf7ca9c 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -300,3 +300,13 @@ public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "p [user_auth_methods] encryption_key = "user_auth_table_encryption_key" # Encryption key used for encrypting data in user_authentication_methods table + +[network_tokenization_service] +generate_token_url= "" +fetch_token_url= "" +token_service_api_key= "" +public_key= "" +private_key= "" +key_id= "" +delete_token_url= "" +check_token_status_url= "" \ No newline at end of file diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 00942e7bb520..0bbf0ce227a6 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -572,4 +572,14 @@ ach = { country = "US", currency = "USD" } connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "visa" \ No newline at end of file +card_networks = "Visa" + +[network_tokenization_service] +generate_token_url= "" +fetch_token_url= "" +token_service_api_key= "" +public_key= "" +private_key= "" +key_id= "" +delete_token_url= "" +check_token_status_url= "" \ No newline at end of file diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index af17f574948c..3ba9d21a4ae7 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1756,15 +1756,24 @@ pub enum MandateStatus { pub enum CardNetwork { #[serde(alias = "VISA")] Visa, + #[serde(alias = "MASTERCARD")] Mastercard, + #[serde(alias = "AMERICANEXPRESS")] AmericanExpress, JCB, + #[serde(alias = "DINERSCLUB")] DinersClub, + #[serde(alias = "DISCOVER")] Discover, + #[serde(alias = "CARTRSBANCAIRES")] CartesBancaires, + #[serde(alias = "UNIONPAY")] UnionPay, + #[serde(alias = "INTERAC")] Interac, + #[serde(alias = "RUPAY")] RuPay, + #[serde(alias = "MAESTRO")] Maestro, } diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 8473fe9dcf2b..f52859efa549 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -570,37 +570,6 @@ impl From for Card { } } -// impl From for NetworkTokenData { -// fn from(value: api_models::payments::Card) -> Self { -// let api_models::payments::Card { -// card_number, -// card_exp_month, -// card_exp_year, -// card_holder_name: _, -// card_cvc, -// card_issuer, -// card_network, -// card_type, -// card_issuing_country, -// bank_code, -// nick_name, -// } = value; - -// Self { -// token_number, -// token_exp_month, -// token_exp_year, -// token_cryptogram, -// card_issuer, -// card_network, -// card_type, -// card_issuing_country, -// bank_code, -// nick_name, -// } -// } -// } - impl From for CardRedirectData { fn from(value: api_models::payments::CardRedirectData) -> Self { match value { diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 930073dd2b48..04a0a283d441 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -4946,7 +4946,7 @@ impl<'a> TryFrom<(&domain::NetworkTokenData, Option>)> for AdyenP expiry_month: token_data.token_exp_month.clone(), expiry_year: token_data.get_expiry_year_4_digit(), holder_name: card_holder_name, - brand: Some(CardBrand::Visa), // FIXME: Remove hardcoding + brand: None, network_payment_reference: None, }; Ok(AdyenPaymentMethod::NetworkToken(Box::new( diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 8e363dae8ca8..3960b20b30a4 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -502,7 +502,13 @@ impl TryFrom<&AuthorizedotnetRouterData<&types::PaymentsAuthorizeRouterData>> Some(api_models::payments::MandateReferenceId::ConnectorMandateId( connector_mandate_id, )) => TransactionRequest::try_from((item, connector_mandate_id))?, - Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) => todo!(), + Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) => { + Err(errors::ConnectorError::NotImplemented( + utils::get_unimplemented_payment_method_error_message( + "authorizedotnet", + ), + ))? + }, None => { match &item.router_data.request.payment_method_data { domain::PaymentMethodData::Card(ccard) => { diff --git a/crates/router/src/core/blocklist/transformers.rs b/crates/router/src/core/blocklist/transformers.rs index e716831052a9..f8ac5b477f15 100644 --- a/crates/router/src/core/blocklist/transformers.rs +++ b/crates/router/src/core/blocklist/transformers.rs @@ -15,7 +15,7 @@ use crate::{ payment_methods::transformers as payment_methods, }, headers, routes, - services::{api as services, encryption}, + services::{api as services, encryption, EncryptionAlgorithm}, types::{storage, transformers::ForeignFrom}, utils::ConnectorResponseExt, }; @@ -87,7 +87,7 @@ async fn generate_jwe_payload_for_request( } }; - let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, "A256GCM", None) + let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None) .await .change_context(errors::VaultError::SaveCardFailed) .attach_printable("Error on jwe encrypt")?; diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 30beed8a5df4..401ecc2667bf 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -26,7 +26,7 @@ use crate::{ }, headers, logger, routes::{self}, - services::{self, encryption}, + services::{self, encryption, EncryptionAlgorithm}, types::{ api::{self}, domain, @@ -69,13 +69,6 @@ pub struct CardNetworkTokenResponse { payload: String, } -#[derive(Debug, Deserialize, Eq, PartialEq)] -#[serde(untagged)] -pub enum CardNTResponse { - CardNetworkTokenResponse(CardNetworkTokenResponse), - CardNetworkTokenErrorResponse(NetworkTokenErrorResponse), -} - #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardNetworkTokenResponsePayloadTemporary { @@ -203,7 +196,7 @@ pub async fn mk_tokenization_req( let jwt = encryption::encrypt_jwe( payload_bytes, enc_key, - "A128GCM", + EncryptionAlgorithm::A128GCM, Some(key_id.as_str()), ) .await @@ -389,13 +382,13 @@ pub async fn get_token_from_tokenization_service( .await .and_then(|val| val.try_into_optionaloperation()) .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") + .attach_printable("unable to decrypt network token details") .ok() .flatten() .map(|x| x.into_inner().expose()) .and_then(|v| serde_json::from_value::(v).ok()) .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + PaymentMethodsData::Card(token) => Some(api::CardDetailFromLocker::from(token)), _ => None, }); let network_token_data = NetworkTokenData { diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 93a5e752b2c2..413484c83d7f 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -14,7 +14,7 @@ use crate::{ core::errors::{self, CustomResult}, headers, pii::{prelude::*, Secret}, - services::{api as services, encryption}, + services::{api as services, encryption, EncryptionAlgorithm}, types::{api, storage}, utils::OptionExt, }; @@ -264,7 +264,7 @@ pub async fn mk_basilisk_req( } }; - let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, "A256GCM", None) + let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None) .await .change_context(errors::VaultError::SaveCardFailed) .attach_printable("Error on jwe encrypt")?; diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index d97d740887b1..57178e208858 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3702,7 +3702,7 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment Domain for PaymentConfirm { }; let encrypted_payload = - services::encrypt_jwe(&card_data, merchant_config.public_key.peek(), "A256GCM", None) + services::encrypt_jwe(&card_data, merchant_config.public_key.peek(), services::EncryptionAlgorithm::A256GCM, None) .await .map_err(|err| { logger::error!(jwe_encryption_err=?err,"Error while JWE encrypting extended card info") diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 1f38b35a3abc..862a78a1a75b 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -204,7 +204,7 @@ where pm_status = Some(common_enums::PaymentMethodStatus::from( save_payment_method_data.attempt_status, )); - let (res, dc, ref_id) = Box::pin(save_in_locker( + let (res, dc, _ref_id) = Box::pin(save_in_locker( state, merchant_account, Some(&save_payment_method_data.request.get_payment_method_data()), @@ -216,7 +216,7 @@ where .await?; if is_network_tokenization_enabled { - let (res2, dc2, network_token_requestor_ref_id) = + let (res2, _dc2, network_token_requestor_ref_id) = Box::pin(save_in_locker( state, merchant_account, @@ -802,7 +802,7 @@ pub async fn save_in_locker( merchant_account: &domain::MerchantAccount, payment_method_data: Option<&domain::PaymentMethodData>, payment_method_request: api::PaymentMethodCreate, - save_token: bool, + save_network_token: bool, amount: Option, currency: Option, ) -> RouterResult<( @@ -811,7 +811,7 @@ pub async fn save_in_locker( Option, )> { payment_method_request.validate()?; - if save_token { + if save_network_token { save_token_in_locker( state, merchant_account, diff --git a/crates/router/src/services/encryption.rs b/crates/router/src/services/encryption.rs index 13cf8f35e582..d1c16c0e0770 100644 --- a/crates/router/src/services/encryption.rs +++ b/crates/router/src/services/encryption.rs @@ -26,15 +26,22 @@ pub struct JweBody { pub encrypted_key: String, } +#[derive(Debug, Eq, PartialEq, Copy, Clone, strum::AsRefStr, strum::Display)] +pub enum EncryptionAlgorithm{ + A128GCM, + A256GCM, +} + pub async fn encrypt_jwe( payload: &[u8], public_key: impl AsRef<[u8]>, - enc: &str, + enc: EncryptionAlgorithm, key_id: Option<&str>, ) -> CustomResult { let alg = jwe::RSA_OAEP_256; let mut src_header = jwe::JweHeader::new(); - src_header.set_content_encryption(enc); + let enc_str = enc.as_ref(); + src_header.set_content_encryption(enc_str); src_header.set_token_type("JWT"); if let Some(kid) = key_id { src_header.set_key_id(kid); @@ -215,7 +222,7 @@ VuY3OeNxi+dC2r7HppP3O/MJ4gX/RJJfSrcaGP8/Ke1W5+jE97Qy let jwt = encrypt_jwe( "request_payload".as_bytes(), ENCRYPTION_KEY, - "A256GCM", + EncryptionAlgorithm::A256GCM, None, ) .await From 5bafdd7597d0be771ab809686627d1e981b446d8 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 23 Aug 2024 23:54:58 +0530 Subject: [PATCH 27/59] fix formatting --- .../src/connector/authorizedotnet/transformers.rs | 6 ++---- crates/router/src/core/blocklist/transformers.rs | 9 +++++---- .../core/payment_methods/network_tokenization.rs | 13 +++++++++---- .../router/src/core/payment_methods/transformers.rs | 9 +++++---- crates/router/src/services/encryption.rs | 2 +- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 3960b20b30a4..c205b09b23aa 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -504,11 +504,9 @@ impl TryFrom<&AuthorizedotnetRouterData<&types::PaymentsAuthorizeRouterData>> )) => TransactionRequest::try_from((item, connector_mandate_id))?, Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_)) => { Err(errors::ConnectorError::NotImplemented( - utils::get_unimplemented_payment_method_error_message( - "authorizedotnet", - ), + utils::get_unimplemented_payment_method_error_message("authorizedotnet"), ))? - }, + } None => { match &item.router_data.request.payment_method_data { domain::PaymentMethodData::Card(ccard) => { diff --git a/crates/router/src/core/blocklist/transformers.rs b/crates/router/src/core/blocklist/transformers.rs index f8ac5b477f15..b5b3d6625e14 100644 --- a/crates/router/src/core/blocklist/transformers.rs +++ b/crates/router/src/core/blocklist/transformers.rs @@ -87,10 +87,11 @@ async fn generate_jwe_payload_for_request( } }; - let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None) - .await - .change_context(errors::VaultError::SaveCardFailed) - .attach_printable("Error on jwe encrypt")?; + let jwe_encrypted = + encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None) + .await + .change_context(errors::VaultError::SaveCardFailed) + .attach_printable("Error on jwe encrypt")?; let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect(); let generate_jwe_body = |payload: Vec<&str>| -> Option { diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index b04e1f60e2f6..630a73999bce 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -193,10 +193,15 @@ pub async fn mk_tokenization_req( let key_id = tokenization_service.key_id.clone(); - let jwt = encryption::encrypt_jwe(payload_bytes, enc_key, EncryptionAlgorithm::A128GCM, Some(key_id.as_str())) - .await - .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) - .attach_printable("Error on jwe encrypt")?; + let jwt = encryption::encrypt_jwe( + payload_bytes, + enc_key, + EncryptionAlgorithm::A128GCM, + Some(key_id.as_str()), + ) + .await + .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) + .attach_printable("Error on jwe encrypt")?; let order_data = OrderData { consent_id: "test12324".to_string(), // ?? diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 413484c83d7f..5f7f97c5b8a1 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -264,10 +264,11 @@ pub async fn mk_basilisk_req( } }; - let jwe_encrypted = encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None) - .await - .change_context(errors::VaultError::SaveCardFailed) - .attach_printable("Error on jwe encrypt")?; + let jwe_encrypted = + encryption::encrypt_jwe(&payload, public_key, EncryptionAlgorithm::A256GCM, None) + .await + .change_context(errors::VaultError::SaveCardFailed) + .attach_printable("Error on jwe encrypt")?; let jwe_payload: Vec<&str> = jwe_encrypted.split('.').collect(); let generate_jwe_body = |payload: Vec<&str>| -> Option { diff --git a/crates/router/src/services/encryption.rs b/crates/router/src/services/encryption.rs index d1c16c0e0770..eb7ae9e4bc31 100644 --- a/crates/router/src/services/encryption.rs +++ b/crates/router/src/services/encryption.rs @@ -27,7 +27,7 @@ pub struct JweBody { } #[derive(Debug, Eq, PartialEq, Copy, Clone, strum::AsRefStr, strum::Display)] -pub enum EncryptionAlgorithm{ +pub enum EncryptionAlgorithm { A128GCM, A256GCM, } From 4cb720b007a5e4a24ba723ae9eda19ff95916126 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 23 Aug 2024 23:59:25 +0530 Subject: [PATCH 28/59] remove unnecessary comments --- crates/router/src/core/payments.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 57178e208858..3ac57fa377ea 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3742,7 +3742,7 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment Date: Tue, 27 Aug 2024 12:16:54 +0530 Subject: [PATCH 29/59] code refactoring --- crates/router/src/core/customers.rs | 2 +- crates/router/src/core/payment_methods/cards.rs | 4 ++-- .../core/payment_methods/network_tokenization.rs | 2 +- crates/router/src/core/payments.rs | 4 ++-- .../core/payments/operations/payment_confirm.rs | 8 ++------ crates/router/src/core/payments/tokenization.rs | 14 +++++++------- 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 0d104993119b..3d025f33cc28 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -542,7 +542,7 @@ pub async fn delete_customer( .switch()?; if let Some(network_token_ref_id) = pm.network_token_requestor_reference_id { - let resp = network_tokenization::delete_network_token_from_locker_and_token_service( + let _resp = network_tokenization::delete_network_token_from_locker_and_token_service( &state, &req.customer_id, merchant_account.get_id(), diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index a20c5f226efe..8e3016b4e54b 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -302,8 +302,8 @@ pub async fn get_or_insert_payment_method( merchant_account.storage_scheme, None, None, // todo! - None, //todo! - None, //todo! + None, + None, ) .await } else { diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 630a73999bce..595858777fef 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -204,7 +204,7 @@ pub async fn mk_tokenization_req( .attach_printable("Error on jwe encrypt")?; let order_data = OrderData { - consent_id: "test12324".to_string(), // ?? + consent_id: uuid::Uuid::new_v4().to_string(), customer_id, amount, currency, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 3ac57fa377ea..121ff4de5dcd 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -2556,8 +2556,8 @@ where .and_then(|inner| inner.mandate_reference_id.as_ref()) .map(|mandate_reference| match mandate_reference { api_models::payments::MandateReferenceId::ConnectorMandateId(_) => true, - api_models::payments::MandateReferenceId::NetworkMandateId(_) => false, - api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_) => false, + api_models::payments::MandateReferenceId::NetworkMandateId(_) + | api_models::payments::MandateReferenceId::NetworkTokenWithNTI(_) => false, }) .unwrap_or(false); diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 179f8eca8fde..cca5516fb742 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1197,12 +1197,8 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen }; let customer_details = payment_data.payment_intent.customer_details.clone(); - let business_sub_label = payment_data - .payment_attempt - .clone() - .business_sub_label - .clone(); - let authentication_type = payment_data.payment_attempt.clone().authentication_type; + let business_sub_label = payment_data.payment_attempt.business_sub_label.clone(); + let authentication_type = payment_data.payment_attempt.authentication_type; let (shipping_address_id, billing_address_id, payment_method_billing_address_id) = ( payment_data.payment_intent.shipping_address_id.clone(), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 862a78a1a75b..af0e0c35f288 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -216,7 +216,7 @@ where .await?; if is_network_tokenization_enabled { - let (res2, _dc2, network_token_requestor_ref_id) = + let (network_token_resp, _network_token_duplication_check, network_token_requestor_ref_id) = Box::pin(save_in_locker( state, merchant_account, @@ -230,7 +230,7 @@ where )) .await?; - ((res, dc, network_token_requestor_ref_id), Some(res2)) + ((res, dc, network_token_requestor_ref_id), Some(network_token_resp)) } else { ((res, dc, None), None) } @@ -389,7 +389,7 @@ where card.card_network .map(|card_network| card_network.to_string()) }), - network_token_requestor_ref_id, //todo! + network_token_requestor_ref_id, network_token_locker_id, pm_token_data_encrypted.map(Into::into), ) @@ -490,7 +490,7 @@ where card_network.to_string() }) }), - network_token_requestor_ref_id, //todo! + network_token_requestor_ref_id, network_token_locker_id, pm_token_data_encrypted.map(Into::into), ) @@ -690,9 +690,9 @@ where card.card_network .map(|card_network| card_network.to_string()) }), - network_token_requestor_ref_id, //todo! - network_token_locker_id, //todo! - pm_token_data_encrypted.map(Into::into), //todo! + network_token_requestor_ref_id, + network_token_locker_id, + pm_token_data_encrypted.map(Into::into), ) .await?; }; From 3a1564323d30924335b25e7975b5a79f89323775 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 27 Aug 2024 12:31:30 +0530 Subject: [PATCH 30/59] remove logging --- crates/common_utils/src/request.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/common_utils/src/request.rs b/crates/common_utils/src/request.rs index 3fa4d1e94962..264179fc6037 100644 --- a/crates/common_utils/src/request.rs +++ b/crates/common_utils/src/request.rs @@ -42,13 +42,13 @@ pub struct Request { impl std::fmt::Debug for RequestContent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Json(a) => a.masked_serialize().fmt(f), - Self::FormUrlEncoded(_) => f.write_str("FormUrlEncodedRequestBody"), - Self::FormData(_) => f.write_str("FormDataRequestBody"), - Self::Xml(_) => f.write_str("XmlRequestBody"), - Self::RawBytes(_) => f.write_str("RawBytesRequestBody"), - } + f.write_str(match self { + Self::Json(_) => "JsonRequestBody", + Self::FormUrlEncoded(_) => "FormUrlEncodedRequestBody", + Self::FormData(_) => "FormDataRequestBody", + Self::Xml(_) => "XmlRequestBody", + Self::RawBytes(_) => "RawBytesRequestBody", + }) } } From 238cf80cddda8e3dc7ebd068a616feef21a3c4ae Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 28 Aug 2024 03:02:47 +0530 Subject: [PATCH 31/59] code refactoring --- .../src/connector/cybersource/transformers.rs | 4 +- .../router/src/core/payment_methods/cards.rs | 10 +- .../payment_methods/network_tokenization.rs | 118 ++++++++++++------ crates/router/src/core/payments.rs | 10 ++ crates/router/src/core/payments/helpers.rs | 10 +- 5 files changed, 104 insertions(+), 48 deletions(-) diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 5f874b0cbc11..0dc8fd1d4531 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -351,7 +351,7 @@ pub struct NetworkTokenizedCard { number: cards::CardNumber, expiration_month: Secret, expiration_year: Secret, - cryptogram: Secret, + cryptogram: Option>, transaction_type: String, } @@ -1167,7 +1167,7 @@ impl number: token_data.token_number, expiration_month: token_data.token_exp_month, expiration_year: token_data.token_exp_year, - cryptogram: token_data.token_cryptogram.clone().unwrap_or_default(), + cryptogram: token_data.token_cryptogram.clone(), transaction_type: "1".to_string(), }, })); diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 8e3016b4e54b..23ad1787a0d0 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -301,7 +301,7 @@ pub async fn get_or_insert_payment_method( req.network_transaction_id.clone(), merchant_account.storage_scheme, None, - None, // todo! + None, None, None, ) @@ -901,8 +901,8 @@ pub async fn add_payment_method_data( payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, - network_token_locker_id: None, // todo! - network_token_payment_method_data: None, //todo! + network_token_locker_id: None, + network_token_payment_method_data: None, }; db.update_payment_method( @@ -1193,8 +1193,8 @@ pub async fn add_payment_method( req.network_transaction_id.clone(), merchant_account.storage_scheme, payment_method_billing_address.map(Into::into), - None, //todo! - None, //todo! + None, + None, None, ) .await?; diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 595858777fef..ddd972dfd5ae 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -4,7 +4,7 @@ use api_models::{enums as api_enums, payment_methods::PaymentMethodsData}; use cards::CardNumber; use common_utils::{ errors::CustomResult, - ext_traits::{BytesExt, Encode}, + ext_traits::{BytesExt, Encode, ValueExt}, id_type, request::RequestContent, type_name, @@ -31,8 +31,7 @@ use crate::{ api::{self}, domain, storage::{self, enums as storage_enums}, - }, - utils::ConnectorResponseExt, + } }; #[derive(Debug, Serialize, Deserialize)] @@ -116,6 +115,7 @@ pub struct AuthenticationDetails { pub struct TokenResponse { authentication_details: AuthenticationDetails, network: api_enums::CardNetwork, + } #[derive(Debug, Serialize, Deserialize)] @@ -330,12 +330,11 @@ pub async fn make_card_network_tokenization_request( .inspect_err(|e| logger::error!(error = %e, "Error while making tokenization request")) } -pub async fn get_token_from_tokenization_service( +pub async fn get_network_token( state: &routes::SessionState, - key_store: &domain::MerchantKeyStore, + customer_id: id_type::CustomerId, network_token_requestor_ref_id: String, - pm_data: &storage::PaymentMethod, -) -> errors::RouterResult { +) -> CustomResult { let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( services::Method::Post, @@ -343,7 +342,7 @@ pub async fn get_token_from_tokenization_service( ); let payload = GetCardToken { card_reference: network_token_requestor_ref_id, - customer_id: pm_data.customer_id.clone(), + customer_id, }; request.add_header(headers::CONTENT_TYPE, "application/json".into()); @@ -363,52 +362,91 @@ pub async fn get_token_from_tokenization_service( .await .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); - let res: TokenResponse = response - .get_response_inner("cardNetworkTokenResponse") - .change_context(errors::NetworkTokenizationError::FetchNetworkTokenFailed) - .map_err(|e| { - logger::error!(fetch_err=?e); - errors::ApiErrorResponse::InternalServerError + let res = response + .map_err(|error| { + error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) + }) + .attach_printable("Error while receiving response") + .and_then(|inner| match inner { + Err(err_res) => { + let parsed_error: NetworkTokenErrorResponse = err_res + .response + .parse_struct("Card Network Tokenization Response") + .change_context( + errors::NetworkTokenizationError::ResponseDeserializationFailed, + )?; + logger::error!( + error_code = %parsed_error.error_info.code, + developer_message = %parsed_error.error_info.developer_message, + "Network tokenization error: {}", + parsed_error.error_message + ); + Err(errors::NetworkTokenizationError::ResponseDeserializationFailed) + .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) + } + Ok(res) => Ok(res), + }) + .inspect_err(|err| { + logger::error!("Error while deserializing response: {:?}", err); })?; - let key = key_store.key.get_inner().peek(); - let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + + let token_response: TokenResponse = res + .response + .parse_struct("Get Network Token Response") + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + + Ok(token_response) +} + +pub async fn get_token_from_tokenization_service( + state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, + network_token_requestor_ref_id: String, + pm_data: &storage::PaymentMethod, +) -> errors::RouterResult { + let token_response = get_network_token(state, pm_data.customer_id.clone(), network_token_requestor_ref_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + let token_decrypted = domain::types::crypto_operation::( &state.into(), type_name!(payment_method::PaymentMethod), domain::types::CryptoOperation::DecryptOptional( pm_data.network_token_payment_method_data.clone(), ), - identifier, - key, + Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.get_inner().peek(), ) .await .and_then(|val| val.try_into_optionaloperation()) .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt network token details") - .ok() - .flatten() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to decrypt Network token details")? .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) + .map( + |value| -> Result> { + value + .parse_value::("PaymentMethodsData") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize payment methods data") + }, + ) + .transpose()? .and_then(|pmd| match pmd { PaymentMethodsData::Card(token) => Some(api::CardDetailFromLocker::from(token)), _ => None, - }); + }) + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to obtain decrypted card object from db")?; + let network_token_data = NetworkTokenData { - token_number: res.authentication_details.token, - token_cryptogram: Some(res.authentication_details.cryptogram), - token_exp_month: token_decrypted - .clone() - .unwrap() - .expiry_month - .unwrap_or_default(), - token_exp_year: token_decrypted - .clone() - .unwrap() - .expiry_year - .unwrap_or_default(), - nick_name: token_decrypted.clone().unwrap().card_holder_name, + token_number: token_response.authentication_details.token, + token_cryptogram: Some(token_response.authentication_details.cryptogram), + token_exp_month: token_decrypted.expiry_month.unwrap_or_default(), + token_exp_year: token_decrypted.expiry_year.unwrap_or_default(), + nick_name: token_decrypted.card_holder_name, card_issuer: None, - card_network: Some(res.network), + card_network: Some(token_response.network), card_type: None, card_issuing_country: None, bank_code: None, @@ -529,6 +567,9 @@ pub async fn check_token_status_with_tokenization_service( .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) } Ok(res) => Ok(res), + }) + .inspect_err(|err| { + logger::error!("Error while deserializing response: {:?}", err); })?; let check_token_status_response: CheckTokenStatusResponse = res @@ -632,6 +673,9 @@ pub async fn delete_network_token_from_tokenization_service( .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) } Ok(res) => Ok(res), + }) + .inspect_err(|err| { + logger::error!("Error while deserializing response: {:?}", err); })?; let delete_token_response: DeleteNTResponse = res diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 121ff4de5dcd..1a610dc734fc 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3693,6 +3693,8 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, is_network_tokenization_enabled: bool, payment_method_info: &storage::PaymentMethod, ) -> bool { + let ntid_supported_connectors = &state + .conf + .network_transaction_id_supported_connectors + .connector_list; + is_connector_agnostic_mit_enabled == Some(true) && is_network_tokenization_enabled == true && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) @@ -3879,6 +3888,7 @@ pub fn is_network_token_with_transaction_id_flow( && payment_method_info .network_token_requestor_reference_id .is_some() + && ntid_supported_connectors.contains(&connector) } pub fn should_add_task_to_process_tracker(payment_data: &PaymentData) -> bool { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 8bc0313d188c..c40aa68dfd0e 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1914,12 +1914,12 @@ pub async fn retrieve_card_with_permanent_token( state, customer_id, &payment_intent.merchant_id, - pm_data.network_token_locker_id.as_ref().unwrap(), + pm_data.network_token_locker_id.as_ref().unwrap(), // ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed to fetch card information from the permanent locker")?; - let x = nt_data.token_exp_month.zip(nt_data.token_exp_year); + let x = nt_data.token_exp_month.zip(nt_data.token_exp_year); // if let Some((exp_month, exp_year)) = x { token_data.card_exp_month = exp_month; token_data.card_exp_year = exp_year; @@ -1948,8 +1948,10 @@ pub async fn retrieve_card_with_permanent_token( &pm_data, ) .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + .change_context(errors::ApiErrorResponse::InternalServerError); + if let Ok(network_token_data) = network_token_data { + return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + } } } From d690b9d40b1c96c0497f33238dcb5676523a8f2b Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 28 Aug 2024 03:10:27 +0530 Subject: [PATCH 32/59] fix formatting --- .../router/src/core/payment_methods/cards.rs | 12 +++--- .../payment_methods/network_tokenization.rs | 17 +++++---- .../router/src/core/payments/tokenization.rs | 38 ++++++++++--------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 23ad1787a0d0..8d0d6fcfdcc7 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -302,8 +302,8 @@ pub async fn get_or_insert_payment_method( merchant_account.storage_scheme, None, None, - None, - None, + None, + None, ) .await } else { @@ -901,8 +901,8 @@ pub async fn add_payment_method_data( payment_method: req.payment_method, payment_method_issuer: req.payment_method_issuer, payment_method_type: req.payment_method_type, - network_token_locker_id: None, - network_token_payment_method_data: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }; db.update_payment_method( @@ -1193,8 +1193,8 @@ pub async fn add_payment_method( req.network_transaction_id.clone(), merchant_account.storage_scheme, payment_method_billing_address.map(Into::into), - None, - None, + None, + None, None, ) .await?; diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index ddd972dfd5ae..2d39ccc83566 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -31,7 +31,7 @@ use crate::{ api::{self}, domain, storage::{self, enums as storage_enums}, - } + }, }; #[derive(Debug, Serialize, Deserialize)] @@ -115,7 +115,6 @@ pub struct AuthenticationDetails { pub struct TokenResponse { authentication_details: AuthenticationDetails, network: api_enums::CardNetwork, - } #[derive(Debug, Serialize, Deserialize)] @@ -342,7 +341,7 @@ pub async fn get_network_token( ); let payload = GetCardToken { card_reference: network_token_requestor_ref_id, - customer_id, + customer_id, }; request.add_header(headers::CONTENT_TYPE, "application/json".into()); @@ -394,7 +393,7 @@ pub async fn get_network_token( .response .parse_struct("Get Network Token Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; - + Ok(token_response) } @@ -404,9 +403,13 @@ pub async fn get_token_from_tokenization_service( network_token_requestor_ref_id: String, pm_data: &storage::PaymentMethod, ) -> errors::RouterResult { - let token_response = get_network_token(state, pm_data.customer_id.clone(), network_token_requestor_ref_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; + let token_response = get_network_token( + state, + pm_data.customer_id.clone(), + network_token_requestor_ref_id, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; let token_decrypted = domain::types::crypto_operation::( &state.into(), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index af0e0c35f288..e55b2140afca 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -216,21 +216,25 @@ where .await?; if is_network_tokenization_enabled { - let (network_token_resp, _network_token_duplication_check, network_token_requestor_ref_id) = - Box::pin(save_in_locker( - state, - merchant_account, - Some( - &save_payment_method_data.request.get_payment_method_data(), - ), - payment_method_create_request.to_owned(), - true, - amount, - currency, - )) - .await?; + let ( + network_token_resp, + _network_token_duplication_check, + network_token_requestor_ref_id, + ) = Box::pin(save_in_locker( + state, + merchant_account, + Some(&save_payment_method_data.request.get_payment_method_data()), + payment_method_create_request.to_owned(), + true, + amount, + currency, + )) + .await?; - ((res, dc, network_token_requestor_ref_id), Some(network_token_resp)) + ( + (res, dc, network_token_requestor_ref_id), + Some(network_token_resp), + ) } else { ((res, dc, None), None) } @@ -690,9 +694,9 @@ where card.card_network .map(|card_network| card_network.to_string()) }), - network_token_requestor_ref_id, - network_token_locker_id, - pm_token_data_encrypted.map(Into::into), + network_token_requestor_ref_id, + network_token_locker_id, + pm_token_data_encrypted.map(Into::into), ) .await?; }; From 51f17cf854d087f46559d0e644cfc519b2681b28 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 28 Aug 2024 03:52:06 +0530 Subject: [PATCH 33/59] fix clippy warnings --- crates/router/src/core/payment_methods.rs | 1 + .../payment_methods/network_tokenization.rs | 6 +- crates/router/src/core/payments.rs | 3 +- crates/router/src/core/payments/helpers.rs | 62 ++++++++------- .../router/src/core/payments/tokenization.rs | 78 +++++++++---------- 5 files changed, 77 insertions(+), 73 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 35267c0da0db..ae1bd234b94e 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -443,6 +443,7 @@ pub async fn add_payment_method_status_update_task( } #[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] pub async fn retrieve_payment_method_with_token( state: &SessionState, merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 2d39ccc83566..0ba1b2321bab 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -440,7 +440,7 @@ pub async fn get_token_from_tokenization_service( _ => None, }) .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to obtain decrypted card object from db")?; + .attach_printable("Failed to obtain decrypted token object from db")?; let network_token_data = NetworkTokenData { token_number: token_response.authentication_details.token, @@ -485,7 +485,7 @@ pub async fn do_status_check_for_network_token( .map(|x| x.into_inner().expose()) .and_then(|v| serde_json::from_value::(v).ok()) .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + PaymentMethodsData::Card(token) => Some(api::CardDetailFromLocker::from(token)), _ => None, }); let network_token_requestor_reference_id = payment_method_info @@ -694,6 +694,6 @@ pub async fn delete_network_token_from_tokenization_service( Ok(true) } else { Err(errors::NetworkTokenizationError::DeleteNetworkTokenFailed) - .attach_printable(format!("Delete Token at Token service failed")) + .attach_printable("Delete Token at Token service failed") } } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 1a610dc734fc..433444eac578 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3633,6 +3633,7 @@ where .await } +#[allow(clippy::too_many_arguments)] pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( state: &SessionState, payment_data: &mut PaymentData, @@ -3881,7 +3882,7 @@ pub fn is_network_token_with_transaction_id_flow( .connector_list; is_connector_agnostic_mit_enabled == Some(true) - && is_network_tokenization_enabled == true + && is_network_tokenization_enabled && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) && payment_method_info.network_transaction_id.is_some() && payment_method_info.network_token_locker_id.is_some() diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index c40aa68dfd0e..054242a0c589 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1869,6 +1869,7 @@ pub async fn retrieve_payment_method_with_temporary_token( }) } +#[allow(clippy::too_many_arguments)] pub async fn retrieve_card_with_permanent_token( state: &SessionState, locker_id: &str, @@ -1900,43 +1901,44 @@ pub async fn retrieve_card_with_permanent_token( if merchant_account.is_network_tokenization_enabled { let pm_data = db - .find_payment_method( - &payment_method_id.to_string(), - merchant_account.storage_scheme, - ) + .find_payment_method(payment_method_id, merchant_account.storage_scheme) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; if let Some(mandate_ids) = mandate_id { if let Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) = mandate_ids.mandate_reference_id { - let mut token_data = cards::get_card_from_locker( - state, - customer_id, - &payment_intent.merchant_id, - pm_data.network_token_locker_id.as_ref().unwrap(), // - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch card information from the permanent locker")?; - let x = nt_data.token_exp_month.zip(nt_data.token_exp_year); // - if let Some((exp_month, exp_year)) = x { - token_data.card_exp_month = exp_month; - token_data.card_exp_year = exp_year; + if let Some(network_token_locker_id) = pm_data.network_token_locker_id { + let mut token_data = cards::get_card_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + network_token_locker_id.as_ref(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to fetch card information from the permanent locker", + )?; + let expiry = nt_data.token_exp_month.zip(nt_data.token_exp_year); + if let Some((exp_month, exp_year)) = expiry { + token_data.card_exp_month = exp_month; + token_data.card_exp_year = exp_year; + } + let network_token_data = domain::NetworkTokenData { + token_number: token_data.card_number, + token_cryptogram: None, + token_exp_month: token_data.card_exp_month, + token_exp_year: token_data.card_exp_year, + nick_name: token_data.nick_name.map(masking::Secret::new), + card_issuer: None, + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); } - let network_token_data = domain::NetworkTokenData { - token_number: token_data.card_number, - token_cryptogram: None, - token_exp_month: token_data.card_exp_month, - token_exp_year: token_data.card_exp_year, - nick_name: token_data.nick_name.map(masking::Secret::new), - card_issuer: None, - card_network: None, - card_type: None, - card_issuing_country: None, - bank_code: None, - }; - return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); } } diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index e55b2140afca..85d840c0fe66 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -210,7 +210,7 @@ where Some(&save_payment_method_data.request.get_payment_method_data()), payment_method_create_request.to_owned(), false, - amount.clone(), + amount, currency, )) .await?; @@ -906,47 +906,47 @@ pub async fn save_token_in_locker( .card_networks; if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data { - // if let Some(card_network) = &card.card_network { - // if network_tokenization_supported_card_networks.contains(card_network) { - if let Ok((token_response, network_token_requestor_ref_id)) = - network_tokenization::make_card_network_tokenization_request( - state, - &card, - &customer_id, - amount, - currency, - ) - .await - { - // Only proceed if the tokenization was successful - let card_data = api::CardDetail { - card_number: token_response.token.clone(), - card_exp_month: token_response.token_expiry_month.clone(), - card_exp_year: token_response.token_expiry_year.clone(), - card_holder_name: None, - nick_name: None, - card_issuing_country: None, - card_network: Some(token_response.card_brand.clone()), - card_issuer: None, - card_type: None, - }; + if let Some(card_network) = &card.card_network { + if network_tokenization_supported_card_networks.contains(card_network) { + if let Ok((token_response, network_token_requestor_ref_id)) = + network_tokenization::make_card_network_tokenization_request( + state, + card, + &customer_id, + amount, + currency, + ) + .await + { + // Only proceed if the tokenization was successful + let card_data = api::CardDetail { + card_number: token_response.token.clone(), + card_exp_month: token_response.token_expiry_month.clone(), + card_exp_year: token_response.token_expiry_year.clone(), + card_holder_name: None, + nick_name: None, + card_issuing_country: None, + card_network: Some(token_response.card_brand.clone()), + card_issuer: None, + card_type: None, + }; - let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( - state, - payment_method_request, - &card_data, - &customer_id, - merchant_account, - None, - )) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Card Failed")?; + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( + state, + payment_method_request, + &card_data, + &customer_id, + merchant_account, + None, + )) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Add Network Token Failed")?; - return Ok((res, dc, network_token_requestor_ref_id)); + return Ok((res, dc, network_token_requestor_ref_id)); + } + } } - // } - // } } let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); From f287f738b50629ccda529306c88f5f3a30ece030 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 28 Aug 2024 16:48:13 +0530 Subject: [PATCH 34/59] resolve pr comments --- crates/router/src/core/payment_methods.rs | 3 + .../payment_methods/network_tokenization.rs | 1 + crates/router/src/core/payments/helpers.rs | 95 ++++++++++--------- .../payments/operations/payment_confirm.rs | 1 - 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index ae1bd234b94e..a1c53044ef4d 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -453,6 +453,7 @@ pub async fn retrieve_payment_method_with_token( customer: &Option, storage_scheme: common_enums::enums::MerchantStorageScheme, mandate_id: Option, + payment_method_info: Option, ) -> RouterResult { let token = match token_data { storage::PaymentTokenData::TemporaryGeneric(generic_token) => { @@ -506,6 +507,7 @@ pub async fn retrieve_payment_method_with_token( merchant_key_store, storage_scheme, mandate_id, + payment_method_info, ) .await .map(|card| Some((card, enums::PaymentMethod::Card)))? @@ -538,6 +540,7 @@ pub async fn retrieve_payment_method_with_token( merchant_key_store, storage_scheme, mandate_id, + payment_method_info, ) .await .map(|card| Some((card, enums::PaymentMethod::Card)))? diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 0ba1b2321bab..98079ff21da5 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -597,6 +597,7 @@ pub async fn delete_network_token_from_locker_and_token_service( network_token_locker_id: Option, network_token_requestor_reference_id: String, ) -> errors::RouterResult { + //deleting network token from locker let resp = payment_methods::cards::delete_card_from_locker( state, customer_id, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 054242a0c589..d85ae3e5c44a 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1363,6 +1363,7 @@ pub(crate) async fn get_payment_method_create_request( Some(pm_data) => match payment_method { Some(payment_method) => match pm_data { domain::PaymentMethodData::Card(card) => { + println!("reqq network {:?}", card.card_network); let card_detail = api::CardDetail { card_number: card.card_number.clone(), card_exp_month: card.card_exp_month.clone(), @@ -1873,12 +1874,13 @@ pub async fn retrieve_payment_method_with_temporary_token( pub async fn retrieve_card_with_permanent_token( state: &SessionState, locker_id: &str, - payment_method_id: &str, + _payment_method_id: &str, payment_intent: &PaymentIntent, card_token_data: Option<&domain::CardToken>, merchant_key_store: &domain::MerchantKeyStore, _storage_scheme: enums::MerchantStorageScheme, mandate_id: Option, + payment_method_info: Option, ) -> RouterResult { let customer_id = payment_intent .customer_id @@ -1900,59 +1902,59 @@ pub async fn retrieve_card_with_permanent_token( .change_context(errors::ApiErrorResponse::InternalServerError)?; if merchant_account.is_network_tokenization_enabled { - let pm_data = db - .find_payment_method(payment_method_id, merchant_account.storage_scheme) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; if let Some(mandate_ids) = mandate_id { if let Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) = mandate_ids.mandate_reference_id { - if let Some(network_token_locker_id) = pm_data.network_token_locker_id { - let mut token_data = cards::get_card_from_locker( - state, - customer_id, - &payment_intent.merchant_id, - network_token_locker_id.as_ref(), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "failed to fetch card information from the permanent locker", - )?; - let expiry = nt_data.token_exp_month.zip(nt_data.token_exp_year); - if let Some((exp_month, exp_year)) = expiry { - token_data.card_exp_month = exp_month; - token_data.card_exp_year = exp_year; + if let Some(ref pm_data) = payment_method_info { + if let Some(network_token_locker_id) = &pm_data.network_token_locker_id { + let mut token_data = cards::get_card_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + network_token_locker_id.as_ref(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to fetch card information from the permanent locker", + )?; + let expiry = nt_data.token_exp_month.zip(nt_data.token_exp_year); + if let Some((exp_month, exp_year)) = expiry { + token_data.card_exp_month = exp_month; + token_data.card_exp_year = exp_year; + } + let network_token_data = domain::NetworkTokenData { + token_number: token_data.card_number, + token_cryptogram: None, + token_exp_month: token_data.card_exp_month, + token_exp_year: token_data.card_exp_year, + nick_name: token_data.nick_name.map(masking::Secret::new), + card_issuer: None, + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); } - let network_token_data = domain::NetworkTokenData { - token_number: token_data.card_number, - token_cryptogram: None, - token_exp_month: token_data.card_exp_month, - token_exp_year: token_data.card_exp_year, - nick_name: token_data.nick_name.map(masking::Secret::new), - card_issuer: None, - card_network: None, - card_type: None, - card_issuing_country: None, - bank_code: None, - }; - return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); } } } - if let Some(token_ref) = pm_data.clone().network_token_requestor_reference_id { - let network_token_data = network_tokenization::get_token_from_tokenization_service( - state, - merchant_key_store, - token_ref, - &pm_data, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError); - if let Ok(network_token_data) = network_token_data { - return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + if let Some(ref pm_data) = payment_method_info { + if let Some(token_ref) = pm_data.network_token_requestor_reference_id.clone() { + let network_token_data = network_tokenization::get_token_from_tokenization_service( + state, + merchant_key_store, + token_ref, + &pm_data, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError); + if let Ok(network_token_data) = network_token_data { + return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + } } } } @@ -2134,6 +2136,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( customer, storage_scheme, mandate_id, + payment_data.payment_method_info.clone(), ) .await; @@ -4999,6 +5002,7 @@ pub async fn get_payment_method_details_from_payment_token( key_store, storage_scheme, None, + None, ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), @@ -5015,6 +5019,7 @@ pub async fn get_payment_method_details_from_payment_token( key_store, storage_scheme, None, + None, ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index cca5516fb742..e05a2744ffb2 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -1205,7 +1205,6 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen payment_data.payment_intent.billing_address_id.clone(), payment_data .payment_attempt - .clone() .payment_method_billing_address_id .clone(), ); From 10c26a2ad281a04c0a2b84958030ee583703e221 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 28 Aug 2024 23:49:49 +0530 Subject: [PATCH 35/59] code refactoring --- crates/router/src/core/payments/tokenization.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 85d840c0fe66..97a1f55c0b88 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -209,7 +209,7 @@ where merchant_account, Some(&save_payment_method_data.request.get_payment_method_data()), payment_method_create_request.to_owned(), - false, + false, //saving the card in locker amount, currency, )) @@ -225,7 +225,7 @@ where merchant_account, Some(&save_payment_method_data.request.get_payment_method_data()), payment_method_create_request.to_owned(), - true, + true, //saving the token in locker amount, currency, )) @@ -262,7 +262,7 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt payment method data")?; - let pm_token_data_encrypted: Option>> = + let pm_network_token_data_encrypted: Option>> = match token_resp { Some(token_resp) => { let pm_token_details = token_resp.card.as_ref().map(|card| { @@ -395,7 +395,7 @@ where }), network_token_requestor_ref_id, network_token_locker_id, - pm_token_data_encrypted.map(Into::into), + pm_network_token_data_encrypted.map(Into::into), ) .await } else { @@ -496,7 +496,7 @@ where }), network_token_requestor_ref_id, network_token_locker_id, - pm_token_data_encrypted.map(Into::into), + pm_network_token_data_encrypted.map(Into::into), ) .await } else { @@ -696,7 +696,7 @@ where }), network_token_requestor_ref_id, network_token_locker_id, - pm_token_data_encrypted.map(Into::into), + pm_network_token_data_encrypted.map(Into::into), ) .await?; }; @@ -857,7 +857,7 @@ pub async fn save_card_in_locker( .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Add Card Failed")?; - Ok((res, dc, None)) + Ok((res, dc, None)) //network_token_requestor_ref_id is None in case of card } None => { let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); From 4d6e5cf2e1cde6f937e07f3eed7c226729e1192e Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 19:32:42 +0000 Subject: [PATCH 36/59] chore: run formatter --- .../src/connector/cybersource/transformers.rs | 3 +- .../router/src/core/payment_methods/cards.rs | 3 +- .../router/src/core/payments/tokenization.rs | 35 +++++++++---------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 44ece549c457..c3ad90e6cc99 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -18,8 +18,7 @@ use crate::{ connector::utils::{ self, AddressDetailsData, ApplePayDecrypt, CardData, NetworkTokenData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, - PaymentsPreProcessingData, PaymentsSyncRequestData, - RecurringMandateData, RouterData, + PaymentsPreProcessingData, PaymentsSyncRequestData, RecurringMandateData, RouterData, }, consts, core::errors, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 61ecd12cca5d..79b54263f11a 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -76,8 +76,7 @@ use crate::{ }, core::{ errors::{self, StorageErrorExt}, - payment_methods::{network_tokenization, - transformers as payment_methods, vault}, + payment_methods::{network_tokenization, transformers as payment_methods, vault}, payments::{ helpers, routing::{self, SessionFlowRoutingInput}, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index bb1a746630f3..8078b4c7f980 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -271,26 +271,23 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt payment method data")?; - let pm_network_token_data_encrypted: Option>> = - match token_resp { - Some(token_resp) => { - let pm_token_details = token_resp.card.as_ref().map(|card| { - PaymentMethodsData::Card(CardDetailsPaymentMethod::from( - card.clone(), - )) - }); + let pm_network_token_data_encrypted: Option< + Encryptable>, + > = match token_resp { + Some(token_resp) => { + let pm_token_details = token_resp.card.as_ref().map(|card| { + PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) + }); - pm_token_details - .async_map(|pm_card| { - create_encrypted_data(state, key_store, pm_card) - }) - .await - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt payment method data")? - } - None => None, - }; + pm_token_details + .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt payment method data")? + } + None => None, + }; let encrypted_payment_method_billing_address: Option< Encryptable>, From c39e34ff4a2c588d1220131c2c0b6f0ee7f7d421 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Thu, 29 Aug 2024 04:00:17 +0530 Subject: [PATCH 37/59] resolve pr comments --- config/config.example.toml | 2 +- config/deployments/env_specific.toml | 18 +- config/deployments/integration_test.toml | 2 +- config/deployments/sandbox.toml | 2 +- crates/common_enums/src/enums.rs | 2 +- .../src/connector/adyen/transformers.rs | 187 +----------------- .../src/connector/cybersource/transformers.rs | 3 +- .../router/src/core/payment_methods/cards.rs | 9 +- .../payment_methods/network_tokenization.rs | 42 ++-- crates/router/src/core/payments/helpers.rs | 3 +- .../router/src/core/payments/tokenization.rs | 35 ++-- crates/router/src/services/encryption.rs | 4 +- .../down.sql | 2 +- .../up.sql | 2 +- 14 files changed, 74 insertions(+), 239 deletions(-) diff --git a/config/config.example.toml b/config/config.example.toml index 226aafbe95cd..0269929a9a00 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -721,4 +721,4 @@ encryption_key = "" # Encryption key used for encrypting data in user_authentica connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "visa" # Supported card networks for network tokenization \ No newline at end of file +card_networks = "Visa" # Supported card networks for network tokenization \ No newline at end of file diff --git a/config/deployments/env_specific.toml b/config/deployments/env_specific.toml index eba24cf7ca9c..084cde38eb15 100644 --- a/config/deployments/env_specific.toml +++ b/config/deployments/env_specific.toml @@ -301,12 +301,12 @@ public = { name = "hyperswitch", base_url = "http://localhost:8080", schema = "p [user_auth_methods] encryption_key = "user_auth_table_encryption_key" # Encryption key used for encrypting data in user_authentication_methods table -[network_tokenization_service] -generate_token_url= "" -fetch_token_url= "" -token_service_api_key= "" -public_key= "" -private_key= "" -key_id= "" -delete_token_url= "" -check_token_status_url= "" \ No newline at end of file +[network_tokenization_service] # Network Tokenization Service Configuration +generate_token_url= "" # base url to generate token +fetch_token_url= "" # base url to fetch token +token_service_api_key= "" # api key for token service +public_key= "" # public key to encrypt data for token service +private_key= "" # private key to decrypt response payload from token service +key_id= "" # key id to encrypt data for token service +delete_token_url= "" # base url to delete token from token service +check_token_status_url= "" # base url to check token status from token service \ No newline at end of file diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index d45369eeafbb..54203d1467bb 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -369,4 +369,4 @@ sdk_eligible_payment_methods = "card" connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "visa" \ No newline at end of file +card_networks = "Visa" \ No newline at end of file diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index dfc264a3f10e..8787b190a555 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -386,4 +386,4 @@ sdk_eligible_payment_methods = "card" connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "visa" \ No newline at end of file +card_networks = "Visa" \ No newline at end of file diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index db12413f0361..aca992f8dfac 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1767,7 +1767,7 @@ pub enum CardNetwork { DinersClub, #[serde(alias = "DISCOVER")] Discover, - #[serde(alias = "CARTRSBANCAIRES")] + #[serde(alias = "CARTESBANCAIRES")] CartesBancaires, #[serde(alias = "UNIONPAY")] UnionPay, diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 624ddf71622d..48a82df750fb 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -15,7 +15,7 @@ use crate::{connector::utils::PayoutsData, types::api::payouts, utils::OptionExt use crate::{ connector::utils::{ self, AddressDetailsData, BrowserInformationData, CardData, MandateReferenceData, - NetworkTokenData, PaymentsAuthorizeRequestData, PhoneDetailsData, RouterData, + PaymentsAuthorizeRequestData, PhoneDetailsData, RouterData, }, consts, core::errors, @@ -136,15 +136,6 @@ pub struct LineItem { quantity: Option, } -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct MpiData { - directory_response: String, - authentication_response: String, - token_authentication_verification_value: Secret, //cryptogram - eci: String, -} - #[serde_with::skip_serializing_none] #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -175,7 +166,6 @@ pub struct AdyenPaymentRequest<'a> { channel: Option, metadata: Option, merchant_order_reference: Option, - mpi_data: Option, } #[derive(Debug, Serialize)] @@ -613,7 +603,6 @@ pub enum AdyenPaymentMethod<'a> { #[serde(rename = "econtext_stores")] PayEasy(Box), Pix(Box), - NetworkToken(Box), } #[derive(Debug, Clone, Serialize)] @@ -1211,19 +1200,6 @@ pub struct AdyenAuthType { pub(super) review_key: Option>, } -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct AdyenNetworkTokenData { - #[serde(rename = "type")] - payment_type: PaymentType, - number: CardNumber, - expiry_month: Secret, - expiry_year: Secret, - holder_name: Option>, - brand: Option, //Mandatory for mandate using network_txns_id - network_payment_reference: Option>, -} - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum PaymentType { @@ -1572,16 +1548,14 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>> domain::PaymentMethodData::GiftCard(ref gift_card_data) => { AdyenPaymentRequest::try_from((item, gift_card_data.as_ref())) } - domain::PaymentMethodData::NetworkToken(ref token_data) => { - AdyenPaymentRequest::try_from((item, token_data)) - } domain::PaymentMethodData::Crypto(_) | domain::PaymentMethodData::MandatePayment | domain::PaymentMethodData::Reward | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::NetworkToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyen"), ))? @@ -2592,51 +2566,12 @@ impl<'a> } } } - payments::MandateReferenceId::NetworkTokenWithNTI(mandate_data) => { - match item.router_data.request.payment_method_data { - domain::PaymentMethodData::NetworkToken(ref token_data) => { - let card_issuer = token_data.get_card_issuer()?; - let brand = CardBrand::try_from(&card_issuer)?; - let card_holder_name = item.router_data.get_optional_billing_full_name(); - let adyen_network_token = AdyenNetworkTokenData { - payment_type: PaymentType::NetworkToken, - number: token_data.token_number.clone(), - expiry_month: token_data.token_exp_month.clone(), - expiry_year: token_data.get_expiry_year_4_digit(), - holder_name: card_holder_name, - brand: Some(brand), - network_payment_reference: Some(Secret::new( - mandate_data.network_transaction_id, - )), - }; - Ok(AdyenPaymentMethod::NetworkToken(Box::new( - adyen_network_token, - ))) - } - - domain::PaymentMethodData::Card(_) - | domain::PaymentMethodData::CardRedirect(_) - | domain::PaymentMethodData::Wallet(_) - | domain::PaymentMethodData::PayLater(_) - | domain::PaymentMethodData::BankRedirect(_) - | domain::PaymentMethodData::BankDebit(_) - | domain::PaymentMethodData::BankTransfer(_) - | domain::PaymentMethodData::Crypto(_) - | domain::PaymentMethodData::MandatePayment - | domain::PaymentMethodData::Reward - | domain::PaymentMethodData::RealTimePayment(_) - | domain::PaymentMethodData::Upi(_) - | domain::PaymentMethodData::Voucher(_) - | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::OpenBanking(_) - | domain::PaymentMethodData::CardToken(_) => { - Err(errors::ConnectorError::NotSupported { - message: "Network tokenization for payment method".to_string(), - connector: "Adyen", - })? - } - } - } // + payments::MandateReferenceId::NetworkTokenWithNTI(_) => { + Err(errors::ConnectorError::NotSupported { + message: "Network tokenization for payment method".to_string(), + connector: "Adyen", + })? + } }?; Ok(AdyenPaymentRequest { amount, @@ -2664,7 +2599,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }) } } @@ -2728,7 +2662,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }) } } @@ -2784,7 +2717,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }; Ok(request) } @@ -2844,7 +2776,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }; Ok(request) } @@ -2896,7 +2827,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }; Ok(request) } @@ -2948,7 +2878,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }; Ok(request) } @@ -3012,7 +2941,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }) } } @@ -3112,7 +3040,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }) } } @@ -3188,7 +3115,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }) } } @@ -3248,7 +3174,6 @@ impl<'a> shopper_ip: item.router_data.request.get_ip_address_as_optional(), metadata: item.router_data.request.metadata.clone().map(Into::into), merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: None, }) } } @@ -5233,97 +5158,3 @@ impl ForeignTryFrom<(&Self, AdyenDisputeResponse)> for types::DefendDisputeRoute } } } - -impl<'a> TryFrom<(&domain::NetworkTokenData, Option>)> for AdyenPaymentMethod<'a> { - type Error = Error; - fn try_from( - (token_data, card_holder_name): (&domain::NetworkTokenData, Option>), - ) -> Result { - let adyen_network_token = AdyenNetworkTokenData { - payment_type: PaymentType::NetworkToken, - number: token_data.token_number.clone(), - expiry_month: token_data.token_exp_month.clone(), - expiry_year: token_data.get_expiry_year_4_digit(), - holder_name: card_holder_name, - brand: None, - network_payment_reference: None, - }; - Ok(AdyenPaymentMethod::NetworkToken(Box::new( - adyen_network_token, - ))) - } -} - -impl<'a> - TryFrom<( - &AdyenRouterData<&types::PaymentsAuthorizeRouterData>, - &domain::NetworkTokenData, - )> for AdyenPaymentRequest<'a> -{ - type Error = Error; - fn try_from( - value: ( - &AdyenRouterData<&types::PaymentsAuthorizeRouterData>, - &domain::NetworkTokenData, - ), - ) -> Result { - let (item, token_data) = value; - let amount = get_amount_data(item); - let auth_type = AdyenAuthType::try_from(&item.router_data.connector_auth_type)?; - let shopper_interaction = AdyenShopperInteraction::from(item.router_data); - let shopper_reference = build_shopper_reference( - &item.router_data.customer_id, - item.router_data.merchant_id.clone(), - ); - let (recurring_processing_model, store_payment_method, _) = - get_recurring_processing_model(item.router_data)?; - let browser_info = get_browser_info(item.router_data)?; - let billing_address = - get_address_info(item.router_data.get_optional_billing()).transpose()?; - let country_code = get_country_code(item.router_data.get_optional_billing()); - let additional_data = get_additional_data(item.router_data); - let return_url = item.router_data.request.get_return_url()?; - let card_holder_name = item.router_data.get_optional_billing_full_name(); - let payment_method = AdyenPaymentMethod::try_from((token_data, card_holder_name))?; - let shopper_email = item.router_data.request.email.clone(); - let shopper_name = get_shopper_name(item.router_data.get_optional_billing()); - let mpi_data = MpiData { - directory_response: "Y".to_string(), - authentication_response: "Y".to_string(), - token_authentication_verification_value: token_data - .token_cryptogram - .clone() - .unwrap_or_default(), - eci: "07".to_string(), - }; - - Ok(AdyenPaymentRequest { - amount, - merchant_account: auth_type.merchant_account, - payment_method, - reference: item.router_data.connector_request_reference_id.clone(), - return_url, - shopper_interaction, - recurring_processing_model, - browser_info, - additional_data, - telephone_number: None, - shopper_name, - shopper_email, - shopper_locale: None, - social_security_number: None, - billing_address, - delivery_address: None, - country_code, - line_items: None, - shopper_reference, - store_payment_method, - channel: None, - shopper_statement: item.router_data.request.statement_descriptor.clone(), - shopper_ip: item.router_data.request.get_ip_address_as_optional(), - metadata: item.router_data.request.metadata.clone().map(Into::into), - merchant_order_reference: item.router_data.request.merchant_order_reference_id.clone(), - mpi_data: Some(mpi_data), - }) - } -} diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 44ece549c457..c3ad90e6cc99 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -18,8 +18,7 @@ use crate::{ connector::utils::{ self, AddressDetailsData, ApplePayDecrypt, CardData, NetworkTokenData, PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, - PaymentsPreProcessingData, PaymentsSyncRequestData, - RecurringMandateData, RouterData, + PaymentsPreProcessingData, PaymentsSyncRequestData, RecurringMandateData, RouterData, }, consts, core::errors, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 61ecd12cca5d..8649b7bbdb46 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -76,8 +76,7 @@ use crate::{ }, core::{ errors::{self, StorageErrorExt}, - payment_methods::{network_tokenization, - transformers as payment_methods, vault}, + payment_methods::{network_tokenization, transformers as payment_methods, vault}, payments::{ helpers, routing::{self, SessionFlowRoutingInput}, @@ -136,7 +135,11 @@ pub async fn create_payment_method( todo!() } -#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2"), + not(feature = "customer_v2") +))] #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_payment_method( diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 98079ff21da5..fad3531d05c8 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -111,10 +111,18 @@ pub struct AuthenticationDetails { cryptogram: Secret, token: CardNumber, } + +#[derive(Debug, Serialize, Deserialize)] +pub struct TokenDetails { + exp_month: Secret, + exp_year: Secret, +} + #[derive(Debug, Serialize, Deserialize)] pub struct TokenResponse { authentication_details: AuthenticationDetails, network: api_enums::CardNetwork, + token_details: TokenDetails, } #[derive(Debug, Serialize, Deserialize)] @@ -231,6 +239,7 @@ pub async fn mk_tokenization_req( .clone() .into_masked(), ); + request.add_default_headers(); request.set_body(RequestContent::Json(Box::new(api_payload))); let response = services::call_connector_api(state, request, "generate_token") @@ -238,9 +247,7 @@ pub async fn mk_tokenization_req( .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); let res = response - .map_err(|error| { - error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) - }) + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { @@ -308,11 +315,7 @@ pub async fn make_card_network_tokenization_request( let payload = card_data .encode_to_string_of_json() .and_then(|x| x.encode_to_string_of_json()) - .change_context(errors::NetworkTokenizationError::RequestEncodingFailed) - .map_err(|e| { - logger::error!(fetch_err=?e); - errors::NetworkTokenizationError::RequestEncodingFailed - })?; + .change_context(errors::NetworkTokenizationError::RequestEncodingFailed)?; let payload_bytes = payload.as_bytes(); let amount_str = amount.map_or_else(String::new, |a| a.to_string()); @@ -354,6 +357,7 @@ pub async fn get_network_token( .clone() .into_masked(), ); + request.add_default_headers(); request.set_body(RequestContent::Json(Box::new(payload))); // Send the request using `call_connector_api` @@ -362,9 +366,7 @@ pub async fn get_network_token( .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); let res = response - .map_err(|error| { - error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) - }) + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { @@ -445,8 +447,12 @@ pub async fn get_token_from_tokenization_service( let network_token_data = NetworkTokenData { token_number: token_response.authentication_details.token, token_cryptogram: Some(token_response.authentication_details.cryptogram), - token_exp_month: token_decrypted.expiry_month.unwrap_or_default(), - token_exp_year: token_decrypted.expiry_year.unwrap_or_default(), + token_exp_month: token_decrypted + .expiry_month + .unwrap_or(token_response.token_details.exp_month), + token_exp_year: token_decrypted + .expiry_year + .unwrap_or(token_response.token_details.exp_year), nick_name: token_decrypted.card_holder_name, card_issuer: None, card_network: Some(token_response.network), @@ -541,6 +547,7 @@ pub async fn check_token_status_with_tokenization_service( .clone() .into_masked(), ); + request.add_default_headers(); request.set_body(RequestContent::Json(Box::new(payload))); // Send the request using `call_connector_api` @@ -548,9 +555,7 @@ pub async fn check_token_status_with_tokenization_service( .await .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); let res = response - .map_err(|error| { - error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) - }) + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { @@ -648,6 +653,7 @@ pub async fn delete_network_token_from_tokenization_service( .clone() .into_masked(), ); + request.add_default_headers(); request.set_body(RequestContent::Json(Box::new(payload))); // Send the request using `call_connector_api` @@ -655,9 +661,7 @@ pub async fn delete_network_token_from_tokenization_service( .await .change_context(errors::NetworkTokenizationError::DeleteNetworkTokenFailed); let res = response - .map_err(|error| { - error.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) - }) + .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed) .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 6f5e2999ebf3..a6d982a81f9e 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1842,7 +1842,8 @@ pub async fn retrieve_card_with_permanent_token( pm_data, ) .await - .change_context(errors::ApiErrorResponse::InternalServerError); + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("failed to fetch network token data from tokenization service"); if let Ok(network_token_data) = network_token_data { return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); } diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index bb1a746630f3..8078b4c7f980 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -271,26 +271,23 @@ where .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt payment method data")?; - let pm_network_token_data_encrypted: Option>> = - match token_resp { - Some(token_resp) => { - let pm_token_details = token_resp.card.as_ref().map(|card| { - PaymentMethodsData::Card(CardDetailsPaymentMethod::from( - card.clone(), - )) - }); + let pm_network_token_data_encrypted: Option< + Encryptable>, + > = match token_resp { + Some(token_resp) => { + let pm_token_details = token_resp.card.as_ref().map(|card| { + PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) + }); - pm_token_details - .async_map(|pm_card| { - create_encrypted_data(state, key_store, pm_card) - }) - .await - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt payment method data")? - } - None => None, - }; + pm_token_details + .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) + .await + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt payment method data")? + } + None => None, + }; let encrypted_payment_method_billing_address: Option< Encryptable>, diff --git a/crates/router/src/services/encryption.rs b/crates/router/src/services/encryption.rs index eb7ae9e4bc31..693504a6faf0 100644 --- a/crates/router/src/services/encryption.rs +++ b/crates/router/src/services/encryption.rs @@ -35,12 +35,12 @@ pub enum EncryptionAlgorithm { pub async fn encrypt_jwe( payload: &[u8], public_key: impl AsRef<[u8]>, - enc: EncryptionAlgorithm, + algorithm: EncryptionAlgorithm, key_id: Option<&str>, ) -> CustomResult { let alg = jwe::RSA_OAEP_256; let mut src_header = jwe::JweHeader::new(); - let enc_str = enc.as_ref(); + let enc_str = algorithm.as_ref(); src_header.set_content_encryption(enc_str); src_header.set_token_type("JWT"); if let Some(kid) = key_id { diff --git a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql index 2ade0e4a2804..aeff6e948852 100644 --- a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql +++ b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/down.sql @@ -1,3 +1,3 @@ -- This file should undo anything in `up.sql` -ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_network_token_locker_id; \ No newline at end of file +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_locker_id; \ No newline at end of file diff --git a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql index 67e82107bdb1..0e49907899e4 100644 --- a/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql +++ b/migrations/2024-07-27-212955_add_token_locker_id_in_payment_method/up.sql @@ -1,2 +1,2 @@ -- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_network_token_locker_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_locker_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file From 5b77ecc7c6940630ded2a54eb91cab1b7ef70968 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Thu, 29 Aug 2024 15:25:25 +0530 Subject: [PATCH 38/59] fix ci check for schema.rs and resolve pr comments --- crates/diesel_models/src/schema.rs | 2 +- crates/router/src/connector/cybersource/transformers.rs | 2 +- crates/router/src/core/customers.rs | 2 +- .../src/core/payment_methods/network_tokenization.rs | 3 ++- crates/router/src/core/payments.rs | 9 ++++++--- crates/router/src/core/payments/helpers.rs | 2 +- crates/router/src/services/encryption.rs | 4 ++-- .../down.sql | 0 .../up.sql | 2 +- .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 15 files changed, 15 insertions(+), 11 deletions(-) rename migrations/{2024-07-22-091927_add_network_token_reference_in_payment_method => 2024-08-22-091927_add_network_token_reference_in_payment_method}/down.sql (100%) rename migrations/{2024-07-22-091927_add_network_token_reference_in_payment_method => 2024-08-22-091927_add_network_token_reference_in_payment_method}/up.sql (62%) rename migrations/{2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account => 2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account}/down.sql (100%) rename migrations/{2024-07-24-104142_add_is_network_tokenization_enabled_in_merchant_account => 2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account}/up.sql (100%) rename migrations/{2024-07-27-212955_add_token_locker_id_in_payment_method => 2024-08-27-212955_add_token_locker_id_in_payment_method}/down.sql (100%) rename migrations/{2024-07-27-212955_add_token_locker_id_in_payment_method => 2024-08-27-212955_add_token_locker_id_in_payment_method}/up.sql (100%) rename migrations/{2024-08-05-195707_add_token_payment_method_data_in_payment_methods => 2024-08-28-195707_add_token_payment_method_data_in_payment_methods}/down.sql (100%) rename migrations/{2024-08-05-195707_add_token_payment_method_data_in_payment_methods => 2024-08-28-195707_add_token_payment_method_data_in_payment_methods}/up.sql (100%) diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 5156be42672b..0d4c703dda87 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -992,7 +992,7 @@ diesel::table! { payment_method_billing_address -> Nullable, #[max_length = 64] updated_by -> Nullable, - #[max_length = 64] + #[max_length = 128] network_token_requestor_reference_id -> Nullable, #[max_length = 64] network_token_locker_id -> Nullable, diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index c3ad90e6cc99..e0cd30007ea4 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -1200,7 +1200,7 @@ impl domain::NetworkTokenData, ), ) -> Result { - let email = item.router_data.request.get_email()?; + let email = item.router_data.get_billing_email()?; let bill_to = build_bill_to(item.router_data.get_optional_billing(), email)?; let order_information = OrderInformationWithBill::from((item, Some(bill_to))); diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 17e1b7f39218..756ac72a3fdb 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -575,7 +575,7 @@ pub async fn delete_customer( .switch()?; if let Some(network_token_ref_id) = pm.network_token_requestor_reference_id { - let _resp = network_tokenization::delete_network_token_from_locker_and_token_service( + network_tokenization::delete_network_token_from_locker_and_token_service( &state, &req.customer_id, merchant_account.get_id(), diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index fad3531d05c8..ab66ee751bf9 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -513,7 +513,8 @@ pub async fn do_status_check_for_network_token( Ok((token_exp_month, token_exp_year)) } else { Err(errors::NetworkTokenizationError::FetchNetworkTokenFailed) - .change_context(errors::ApiErrorResponse::InternalServerError)? + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Check network token status failed")? } } else { Ok((None, None)) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 2c6f71522a4f..9c2c5259bd2b 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3739,7 +3739,8 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment Date: Thu, 29 Aug 2024 09:56:15 +0000 Subject: [PATCH 39/59] chore: run formatter --- crates/router/src/core/customers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 756ac72a3fdb..5cc9bce9ee6c 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -578,7 +578,7 @@ pub async fn delete_customer( network_tokenization::delete_network_token_from_locker_and_token_service( &state, &req.customer_id, - merchant_account.get_id(), + merchant_account.get_id(), pm.payment_method_id.clone(), pm.network_token_locker_id, network_token_ref_id, From 4acd4b54eedf4a71ea22b1fd1b753c95c55a9236 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 30 Aug 2024 02:37:45 +0530 Subject: [PATCH 40/59] add loggers for req and response --- .../payment_methods/network_tokenization.rs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index ab66ee751bf9..d4fd6d2e86b2 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -210,6 +210,8 @@ pub async fn mk_tokenization_req( .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) .attach_printable("Error on jwe encrypt")?; + logger::info!("JWE Encrypted Payload: {}", jwt); + let order_data = OrderData { consent_id: uuid::Uuid::new_v4().to_string(), customer_id, @@ -240,8 +242,11 @@ pub async fn mk_tokenization_req( .into_masked(), ); request.add_default_headers(); + logger::info!("api Payload to generate token: {:?}", api_payload); request.set_body(RequestContent::Json(Box::new(api_payload))); + logger::info!("Request to generate token: {:?}", request); + let response = services::call_connector_api(state, request, "generate_token") .await .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed); @@ -276,6 +281,7 @@ pub async fn mk_tokenization_req( .response .parse_struct("Card Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + logger::info!("Network Token Response: {:?}", network_response); let dec_key = tokenization_service.private_key.peek().clone(); @@ -291,6 +297,8 @@ pub async fn mk_tokenization_req( "Failed to decrypt the tokenization response from the tokenization service", )?; + logger::info!("Decrypted Response: {:?}", card_network_token_response); + let cn_response: CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; @@ -329,7 +337,7 @@ pub async fn make_card_network_tokenization_request( customer_id.clone(), ) .await - .inspect_err(|e| logger::error!(error = %e, "Error while making tokenization request")) + .inspect_err(|e| logger::error!(error=?e, "Error while making tokenization request")) } pub async fn get_network_token( @@ -358,8 +366,11 @@ pub async fn get_network_token( .into_masked(), ); request.add_default_headers(); + logger::info!("Payload to fetch network token: {:?}", payload); request.set_body(RequestContent::Json(Box::new(payload))); + logger::info!("Request to fetch network token: {:?}", request); + // Send the request using `call_connector_api` let response = services::call_connector_api(state, request, "get network token") .await @@ -395,6 +406,7 @@ pub async fn get_network_token( .response .parse_struct("Get Network Token Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + logger::info!("Fetch Network Token Response: {:?}", token_response); Ok(token_response) } @@ -411,7 +423,8 @@ pub async fn get_token_from_tokenization_service( network_token_requestor_ref_id, ) .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; + .change_context(errors::ApiErrorResponse::InternalServerError) + .inspect_err(|e| logger::error!(error=?e, "Error while fetching token from tokenization service"))?; let token_decrypted = domain::types::crypto_operation::( &state.into(), @@ -655,8 +668,11 @@ pub async fn delete_network_token_from_tokenization_service( .into_masked(), ); request.add_default_headers(); + logger::info!("Payload to delete network token: {:?}", payload); request.set_body(RequestContent::Json(Box::new(payload))); + logger::info!("Request to delete network token: {:?}", request); + // Send the request using `call_connector_api` let response = services::call_connector_api(state, request, "delete network token") .await @@ -692,6 +708,8 @@ pub async fn delete_network_token_from_tokenization_service( .parse_struct("Delete Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; + logger::info!("Delete Network Token Response: {:?}", delete_token_response); + if delete_token_response == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { status: DeleteNetworkTokenStatus::Success, From 93bbd5351a423e464c246ba9d7e7fa9b0878c6d2 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:08:14 +0000 Subject: [PATCH 41/59] chore: run formatter --- crates/router/src/connector/cybersource/transformers.rs | 7 ++++--- .../src/core/payment_methods/network_tokenization.rs | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 08fa5b7e63e9..d5b6980fbc80 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -16,9 +16,10 @@ use serde_json::Value; use crate::connector::utils::PayoutsData; use crate::{ connector::utils::{ - self, AddressDetailsData, ApplePayDecrypt, CardData, PaymentsAuthorizeRequestData, - PaymentsCompleteAuthorizeRequestData, PaymentsPreProcessingData, - PaymentsSetupMandateRequestData, PaymentsSyncRequestData, RecurringMandateData, RouterData, NetworkTokenData + self, AddressDetailsData, ApplePayDecrypt, CardData, NetworkTokenData, + PaymentsAuthorizeRequestData, PaymentsCompleteAuthorizeRequestData, + PaymentsPreProcessingData, PaymentsSetupMandateRequestData, PaymentsSyncRequestData, + RecurringMandateData, RouterData, }, consts, core::errors, diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index d4fd6d2e86b2..40d935e64592 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -424,7 +424,9 @@ pub async fn get_token_from_tokenization_service( ) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .inspect_err(|e| logger::error!(error=?e, "Error while fetching token from tokenization service"))?; + .inspect_err( + |e| logger::error!(error=?e, "Error while fetching token from tokenization service"), + )?; let token_decrypted = domain::types::crypto_operation::( &state.into(), From 4c5368fc0f3bbbb460919491f5348c3750846f0b Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 2 Sep 2024 19:45:38 +0530 Subject: [PATCH 42/59] move the is_network_tokenization_enabled field from merchant_account to business_profile --- crates/api_models/src/admin.rs | 16 +++--- crates/diesel_models/src/business_profile.rs | 5 ++ crates/diesel_models/src/merchant_account.rs | 5 -- crates/diesel_models/src/schema.rs | 2 +- .../src/business_profile.rs | 50 +++++++++++++++++++ .../src/merchant_account.rs | 13 ----- crates/router/src/core/admin.rs | 4 +- crates/router/src/core/payment_methods.rs | 3 ++ crates/router/src/core/payments.rs | 24 +++++---- crates/router/src/core/payments/helpers.rs | 20 +++----- .../router/src/core/payments/tokenization.rs | 2 +- crates/router/src/core/routing/helpers.rs | 1 - crates/router/src/types/api/admin.rs | 3 +- crates/router/src/types/domain/user.rs | 1 - .../down.sql | 3 +- .../up.sql | 3 +- 16 files changed, 96 insertions(+), 59 deletions(-) rename migrations/{2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account => 2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile}/down.sql (50%) rename migrations/{2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account => 2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile}/up.sql (53%) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 8e253a48a4a3..7e99a9eea34f 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -119,8 +119,6 @@ pub struct MerchantAccountCreate { #[schema(value_type = Option)] pub pm_collect_link_config: Option, - #[serde(default)] - pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -339,8 +337,6 @@ pub struct MerchantAccountUpdate { /// Default payment method collect link config #[schema(value_type = Option)] pub pm_collect_link_config: Option, - - pub is_network_tokenization_enabled: Option, } #[cfg(all( @@ -540,8 +536,6 @@ pub struct MerchantAccountResponse { /// Default payment method collect link config #[schema(value_type = Option)] pub pm_collect_link_config: Option, - - pub is_network_tokenization_enabled: bool, } #[cfg(all(feature = "v2", feature = "merchant_account_v2"))] @@ -1975,6 +1969,11 @@ pub struct BusinessProfileCreate { /// If set to `true` tax_connector_id will be checked. #[serde(default)] pub is_tax_connector_enabled: bool, + + /// Indicates if is_network_tokenization_enabled is enabled or not. + /// If set to `true` is_network_tokenization_enabled will be checked. + #[serde(default)] + pub is_network_tokenization_enabled: bool, } #[nutype::nutype( @@ -2197,6 +2196,8 @@ pub struct BusinessProfileResponse { /// Indicates if tax_calculator connector is enabled or not. /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: bool, + + pub is_network_tokenization_enabled: bool, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] @@ -2415,6 +2416,9 @@ pub struct BusinessProfileUpdate { /// Indicates if tax_calculator connector is enabled or not. /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: Option, + + /// Indicates if is_network_tokenization_enabled is enabled or not. + pub is_network_tokenization_enabled: Option, } #[cfg(all(feature = "v2", feature = "business_profile_v2"))] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 4ac16850ce6a..ce4524f440a9 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -59,6 +59,7 @@ pub struct BusinessProfile { pub tax_connector_id: Option, pub is_tax_connector_enabled: Option, pub api_version: common_enums::ApiVersion, + pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -141,6 +142,7 @@ pub struct BusinessProfileUpdateInternal { pub always_collect_shipping_details_from_wallet_connector: Option, pub tax_connector_id: Option, pub is_tax_connector_enabled: Option, + pub is_network_tokenization_enabled: Option, } #[cfg(all( @@ -179,6 +181,7 @@ impl BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector, tax_connector_id, is_tax_connector_enabled, + is_network_tokenization_enabled, } = self; BusinessProfile { profile_id: source.profile_id, @@ -232,6 +235,8 @@ impl BusinessProfileUpdateInternal { tax_connector_id: tax_connector_id.or(source.tax_connector_id), is_tax_connector_enabled: is_tax_connector_enabled.or(source.is_tax_connector_enabled), api_version: source.api_version, + is_network_tokenization_enabled: is_network_tokenization_enabled + .unwrap_or(source.is_network_tokenization_enabled), } } } diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index 53a75a9a126e..ac7635e88c53 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -57,7 +57,6 @@ pub struct MerchantAccount { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -93,7 +92,6 @@ pub struct MerchantAccountSetter { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -131,7 +129,6 @@ impl From for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, - is_network_tokenization_enabled: item.is_network_tokenization_enabled, } } } @@ -249,7 +246,6 @@ pub struct MerchantAccountNew { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_network_tokenization_enabled: bool, } #[cfg(all(feature = "v2", feature = "merchant_account_v2"))] @@ -314,5 +310,4 @@ pub struct MerchantAccountUpdateInternal { pub recon_status: Option, pub payment_link_config: Option, pub pm_collect_link_config: Option, - pub is_network_tokenization_enabled: Option, } diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 2e2f694b57c2..835761515ef9 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -209,6 +209,7 @@ diesel::table! { tax_connector_id -> Nullable, is_tax_connector_enabled -> Nullable, api_version -> ApiVersion, + is_network_tokenization_enabled -> Bool, } } @@ -674,7 +675,6 @@ diesel::table! { payment_link_config -> Nullable, pm_collect_link_config -> Nullable, version -> ApiVersion, - is_network_tokenization_enabled -> Bool, } } diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index ce01681cc15b..e95d09635aeb 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -57,6 +57,7 @@ pub struct BusinessProfile { pub tax_connector_id: Option, pub is_tax_connector_enabled: bool, pub api_version: common_enums::ApiVersion, + pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -96,6 +97,7 @@ pub struct BusinessProfileSetter { pub always_collect_shipping_details_from_wallet_connector: Option, pub tax_connector_id: Option, pub is_tax_connector_enabled: bool, + pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -142,6 +144,7 @@ impl From for BusinessProfile { tax_connector_id: value.tax_connector_id, is_tax_connector_enabled: value.is_tax_connector_enabled, api_version: consts::API_VERSION, + is_network_tokenization_enabled: value.is_network_tokenization_enabled, } } } @@ -193,6 +196,7 @@ pub struct BusinessProfileGeneralUpdate { pub always_collect_shipping_details_from_wallet_connector: Option, pub tax_connector_id: Option, pub is_tax_connector_enabled: Option, + pub is_network_tokenization_enabled: Option, } #[cfg(all( @@ -212,6 +216,11 @@ pub enum BusinessProfileUpdate { ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled: Option, }, + NetworkTokenizationUpdate{ + is_network_tokenization_enabled: Option, + } + + } #[cfg(all( @@ -251,6 +260,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector, tax_connector_id, is_tax_connector_enabled, + is_network_tokenization_enabled } = *update; Self { @@ -284,6 +294,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector, tax_connector_id, is_tax_connector_enabled, + is_network_tokenization_enabled } } BusinessProfileUpdate::RoutingAlgorithmUpdate { @@ -319,6 +330,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None }, BusinessProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, @@ -352,6 +364,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None }, BusinessProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, @@ -385,6 +398,41 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None + }, + BusinessProfileUpdate::NetworkTokenizationUpdate { + is_network_tokenization_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + routing_algorithm: None, + intent_fulfillment_time: None, + frm_routing_algorithm: None, + payout_routing_algorithm: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + always_collect_billing_details_from_wallet_connector: None, + always_collect_shipping_details_from_wallet_connector: None, + tax_connector_id: None, + is_tax_connector_enabled: None, + is_network_tokenization_enabled, }, } } @@ -440,6 +488,7 @@ impl super::behaviour::Conversion for BusinessProfile { tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: Some(self.is_tax_connector_enabled), api_version: self.api_version, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } @@ -504,6 +553,7 @@ impl super::behaviour::Conversion for BusinessProfile { tax_connector_id: item.tax_connector_id, is_tax_connector_enabled: item.is_tax_connector_enabled.unwrap_or(false), api_version: item.api_version, + is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } .await diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index 496e25ff743e..50b1effd27f2 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -50,7 +50,6 @@ pub struct MerchantAccount { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -88,7 +87,6 @@ pub struct MerchantAccountSetter { pub payment_link_config: Option, pub pm_collect_link_config: Option, pub version: common_enums::ApiVersion, - pub is_network_tokenization_enabled: bool, } #[cfg(all( @@ -126,7 +124,6 @@ impl From for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, - is_network_tokenization_enabled: item.is_network_tokenization_enabled, } } } @@ -237,7 +234,6 @@ pub enum MerchantAccountUpdate { default_profile: Option>, payment_link_config: Option, pm_collect_link_config: Option, - is_network_tokenization_enabled: Option, }, StorageSchemeUpdate { storage_scheme: MerchantStorageScheme, @@ -299,7 +295,6 @@ impl From for MerchantAccountUpdateInternal { default_profile, payment_link_config, pm_collect_link_config, - is_network_tokenization_enabled, } => Self { merchant_name: merchant_name.map(Encryption::from), merchant_details: merchant_details.map(Encryption::from), @@ -326,7 +321,6 @@ impl From for MerchantAccountUpdateInternal { organization_id: None, is_recon_enabled: None, recon_status: None, - is_network_tokenization_enabled, }, MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme } => Self { storage_scheme: Some(storage_scheme), @@ -354,7 +348,6 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, - is_network_tokenization_enabled: None, }, MerchantAccountUpdate::ReconUpdate { recon_status } => Self { recon_status: Some(recon_status), @@ -382,7 +375,6 @@ impl From for MerchantAccountUpdateInternal { default_profile: None, payment_link_config: None, pm_collect_link_config: None, - is_network_tokenization_enabled: None, }, MerchantAccountUpdate::UnsetDefaultProfile => Self { default_profile: Some(None), @@ -410,7 +402,6 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, - is_network_tokenization_enabled: None, }, MerchantAccountUpdate::ModifiedAtUpdate => Self { modified_at: now, @@ -438,7 +429,6 @@ impl From for MerchantAccountUpdateInternal { recon_status: None, payment_link_config: None, pm_collect_link_config: None, - is_network_tokenization_enabled: None, }, } } @@ -641,7 +631,6 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, version: self.version, - is_network_tokenization_enabled: self.is_network_tokenization_enabled, }; Ok(diesel_models::MerchantAccount::from(setter)) @@ -719,7 +708,6 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: item.payment_link_config, pm_collect_link_config: item.pm_collect_link_config, version: item.version, - is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } .await @@ -758,7 +746,6 @@ impl super::behaviour::Conversion for MerchantAccount { payment_link_config: self.payment_link_config, pm_collect_link_config: self.pm_collect_link_config, version: crate::consts::API_VERSION, - is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 830087af343c..6d9d6f9ef902 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -404,7 +404,6 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { payment_link_config: None, pm_collect_link_config, version: hyperswitch_domain_models::consts::API_VERSION, - is_network_tokenization_enabled: self.is_network_tokenization_enabled, }, ) } @@ -977,7 +976,6 @@ impl MerchantAccountUpdateBridge for api::MerchantAccountUpdate { payment_link_config: None, pm_collect_link_config, routing_algorithm: self.routing_algorithm, - is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } } @@ -3548,6 +3546,7 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate { .always_collect_billing_details_from_wallet_connector, always_collect_shipping_details_from_wallet_connector: self .always_collect_shipping_details_from_wallet_connector, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }, )) } @@ -3903,6 +3902,7 @@ impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { .always_collect_shipping_details_from_wallet_connector, tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: self.is_tax_connector_enabled, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }, ))) } diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 615b5657715e..2d30bc5a50d5 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -463,6 +463,7 @@ pub async fn retrieve_payment_method_with_token( storage_scheme: common_enums::enums::MerchantStorageScheme, mandate_id: Option, payment_method_info: Option, + business_profile: Option<&domain::BusinessProfile>, ) -> RouterResult { let token = match token_data { storage::PaymentTokenData::TemporaryGeneric(generic_token) => { @@ -517,6 +518,7 @@ pub async fn retrieve_payment_method_with_token( storage_scheme, mandate_id, payment_method_info, + business_profile, ) .await .map(|card| Some((card, enums::PaymentMethod::Card)))? @@ -550,6 +552,7 @@ pub async fn retrieve_payment_method_with_token( storage_scheme, mandate_id, payment_method_info, + business_profile, ) .await .map(|card| Some((card, enums::PaymentMethod::Card)))? diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 9621f9aac9ec..69b5274160c7 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3593,7 +3593,7 @@ where connector_data, mandate_type, business_profile.is_connector_agnostic_mit_enabled, - merchant_account.is_network_tokenization_enabled, + business_profile.is_network_tokenization_enabled, key_store, ) .await; @@ -3642,7 +3642,7 @@ where connector_data, mandate_type, business_profile.is_connector_agnostic_mit_enabled, - merchant_account.is_network_tokenization_enabled, + business_profile.is_network_tokenization_enabled, key_store, ) .await; @@ -4250,7 +4250,7 @@ where connector_data, mandate_type, business_profile.is_connector_agnostic_mit_enabled, - merchant_account.is_network_tokenization_enabled, + business_profile.is_network_tokenization_enabled, key_store, ) .await @@ -4400,12 +4400,22 @@ pub async fn payment_external_authentication( .await .to_not_found_response(errors::ApiErrorResponse::InternalServerError) .attach_printable("Error while fetching authentication record")?; + + let business_profile = state + .store + .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) + .await + .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + id: profile_id.get_string_repr().to_owned(), + })?; + let payment_method_details = helpers::get_payment_method_details_from_payment_token( &state, &payment_attempt, &payment_intent, &key_store, storage_scheme, + &business_profile ) .await? .ok_or(errors::ApiErrorResponse::InternalServerError) @@ -4431,14 +4441,6 @@ pub async fn payment_external_authentication( let webhook_url = helpers::create_webhook_url(&state.base_url, merchant_id, &authentication_connector); - let business_profile = state - .store - .find_business_profile_by_profile_id(key_manager_state, &key_store, profile_id) - .await - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { - id: profile_id.get_string_repr().to_owned(), - })?; - let authentication_details = business_profile .authentication_connector_details .clone() diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index c9dba641d818..443b502f6678 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1807,6 +1807,7 @@ pub async fn retrieve_card_with_permanent_token( _storage_scheme: enums::MerchantStorageScheme, mandate_id: Option, payment_method_info: Option, + business_profile: Option<&domain::BusinessProfile>, ) -> RouterResult { let customer_id = payment_intent .customer_id @@ -1816,18 +1817,7 @@ pub async fn retrieve_card_with_permanent_token( message: "no customer id provided for the payment".to_string(), })?; - let db = state.store.as_ref(); - let key_manager_state = &state.into(); - let merchant_account = db - .find_merchant_account_by_merchant_id( - key_manager_state, - &payment_intent.merchant_id, - merchant_key_store, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - - if merchant_account.is_network_tokenization_enabled { + if business_profile.unwrap().is_network_tokenization_enabled { if let Some(mandate_ids) = mandate_id { if let Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) = mandate_ids.mandate_reference_id @@ -2064,6 +2054,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( storage_scheme, mandate_id, payment_data.payment_method_info.clone(), + business_profile ) .await; @@ -4965,10 +4956,11 @@ pub fn update_additional_payment_data_with_connector_response_pm_data( pub async fn get_payment_method_details_from_payment_token( state: &SessionState, - payment_attempt: &PaymentAttempt, + payment_attempt: &PaymentAttempt, payment_intent: &PaymentIntent, key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, + business_profile: &domain::BusinessProfile, ) -> RouterResult> { let hyperswitch_token = if let Some(token) = payment_attempt.payment_token.clone() { let redis_conn = state @@ -5054,6 +5046,7 @@ pub async fn get_payment_method_details_from_payment_token( storage_scheme, None, None, + Some(business_profile), ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), @@ -5071,6 +5064,7 @@ pub async fn get_payment_method_details_from_payment_token( storage_scheme, None, None, + Some(business_profile), ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 8078b4c7f980..80b130be748e 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -200,7 +200,7 @@ where let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = merchant_account.get_id(); let is_network_tokenization_enabled = - merchant_account.is_network_tokenization_enabled; + business_profile.is_network_tokenization_enabled; let ((mut resp, duplication_check, network_token_requestor_ref_id), token_resp) = if !state.conf.locker.locker_enabled { let (res, dc) = skip_saving_card_in_locker( diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 09ada6687ed3..f0dbf8dfa8fe 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -152,7 +152,6 @@ pub async fn update_merchant_active_algorithm_ref( default_profile: None, payment_link_config: None, pm_collect_link_config: None, - is_network_tokenization_enabled: None, }; let db = &*state.store; diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 139384d44c19..cb8fe3dc699e 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -81,7 +81,6 @@ impl ForeignTryFrom for MerchantAccountResponse { default_profile: item.default_profile, recon_status: item.recon_status, pm_collect_link_config, - is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } } @@ -170,6 +169,7 @@ impl ForeignTryFrom for BusinessProfileResponse { outgoing_webhook_custom_http_headers, tax_connector_id: item.tax_connector_id, is_tax_connector_enabled: item.is_tax_connector_enabled, + is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } } @@ -357,6 +357,7 @@ pub async fn create_business_profile_from_merchant_account( .map(Into::into), tax_connector_id: request.tax_connector_id, is_tax_connector_enabled: request.is_tax_connector_enabled, + is_network_tokenization_enabled: request.is_network_tokenization_enabled, }, )) } diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index ae490143a8e8..58a0a68df51f 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -441,7 +441,6 @@ impl NewUserMerchant { enable_payment_response_hash: None, redirect_to_merchant_with_http_post: None, pm_collect_link_config: None, - is_network_tokenization_enabled: false, }) } diff --git a/migrations/2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql b/migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql similarity index 50% rename from migrations/2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql rename to migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql index 3816696829f6..2d15e286236f 100644 --- a/migrations/2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account/down.sql +++ b/migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql @@ -1,3 +1,2 @@ -- This file should undo anything in `up.sql` - -ALTER TABLE merchant_account DROP COLUMN IF EXISTS is_network_tokenization_enabled; \ No newline at end of file +ALTER TABLE business_profile DROP COLUMN IF EXISTS is_network_tokenization_enabled; \ No newline at end of file diff --git a/migrations/2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql b/migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql similarity index 53% rename from migrations/2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql rename to migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql index 1d5e7e1a66f8..c45ae434ac3d 100644 --- a/migrations/2024-08-24-104142_add_is_network_tokenization_enabled_in_merchant_account/up.sql +++ b/migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql @@ -1,3 +1,2 @@ -- Your SQL goes here - -ALTER TABLE merchant_account ADD COLUMN IF NOT EXISTS is_network_tokenization_enabled BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file +ALTER TABLE business_profile ADD COLUMN IF NOT EXISTS is_network_tokenization_enabled BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file From 0fde02d78b8ca005380aa96497bb2a4db1bbfeda Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 2 Sep 2024 19:47:16 +0530 Subject: [PATCH 43/59] fix formatting --- crates/api_models/src/admin.rs | 1 - .../src/business_profile.rs | 16 +++++++--------- crates/router/src/core/payments.rs | 2 +- crates/router/src/core/payments/helpers.rs | 4 ++-- crates/router/src/core/payments/tokenization.rs | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 7e99a9eea34f..093ce5afc402 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -118,7 +118,6 @@ pub struct MerchantAccountCreate { /// Default payment method collect link config #[schema(value_type = Option)] pub pm_collect_link_config: Option, - } #[cfg(all( diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index e95d09635aeb..11ec50293f44 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -216,11 +216,9 @@ pub enum BusinessProfileUpdate { ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled: Option, }, - NetworkTokenizationUpdate{ + NetworkTokenizationUpdate { is_network_tokenization_enabled: Option, - } - - + }, } #[cfg(all( @@ -260,7 +258,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector, tax_connector_id, is_tax_connector_enabled, - is_network_tokenization_enabled + is_network_tokenization_enabled, } = *update; Self { @@ -294,7 +292,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector, tax_connector_id, is_tax_connector_enabled, - is_network_tokenization_enabled + is_network_tokenization_enabled, } } BusinessProfileUpdate::RoutingAlgorithmUpdate { @@ -330,7 +328,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector: None, tax_connector_id: None, is_tax_connector_enabled: None, - is_network_tokenization_enabled: None + is_network_tokenization_enabled: None, }, BusinessProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, @@ -364,7 +362,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector: None, tax_connector_id: None, is_tax_connector_enabled: None, - is_network_tokenization_enabled: None + is_network_tokenization_enabled: None, }, BusinessProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, @@ -398,7 +396,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector: None, tax_connector_id: None, is_tax_connector_enabled: None, - is_network_tokenization_enabled: None + is_network_tokenization_enabled: None, }, BusinessProfileUpdate::NetworkTokenizationUpdate { is_network_tokenization_enabled, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 69b5274160c7..0501ab38cef8 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -4415,7 +4415,7 @@ pub async fn payment_external_authentication( &payment_intent, &key_store, storage_scheme, - &business_profile + &business_profile, ) .await? .ok_or(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 443b502f6678..34a096a6bd52 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2054,7 +2054,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( storage_scheme, mandate_id, payment_data.payment_method_info.clone(), - business_profile + business_profile, ) .await; @@ -4956,7 +4956,7 @@ pub fn update_additional_payment_data_with_connector_response_pm_data( pub async fn get_payment_method_details_from_payment_token( state: &SessionState, - payment_attempt: &PaymentAttempt, + payment_attempt: &PaymentAttempt, payment_intent: &PaymentIntent, key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 80b130be748e..e6c94faf6b1c 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -200,7 +200,7 @@ where let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = merchant_account.get_id(); let is_network_tokenization_enabled = - business_profile.is_network_tokenization_enabled; + business_profile.is_network_tokenization_enabled; let ((mut resp, duplication_check, network_token_requestor_ref_id), token_resp) = if !state.conf.locker.locker_enabled { let (res, dc) = skip_saving_card_in_locker( From 2217b27208a0c803d8a451da46416e6d0dad2160 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 3 Sep 2024 01:55:53 +0530 Subject: [PATCH 44/59] fix clippy --- crates/router/src/core/payment_methods.rs | 2 +- crates/router/src/core/payments.rs | 8 ++++---- crates/router/src/core/payments/helpers.rs | 12 ++++++------ crates/router/src/core/payments/operations.rs | 10 +++++----- .../operations/payment_complete_authorize.rs | 2 +- .../src/core/payments/operations/payment_confirm.rs | 2 +- .../src/core/payments/operations/payment_create.rs | 2 +- .../src/core/payments/operations/payment_session.rs | 2 +- .../src/core/payments/operations/payment_start.rs | 2 +- .../src/core/payments/operations/payment_status.rs | 2 +- .../src/core/payments/operations/payment_update.rs | 2 +- .../operations/payments_incremental_authorization.rs | 2 +- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 2d30bc5a50d5..2f1079a63a18 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -463,7 +463,7 @@ pub async fn retrieve_payment_method_with_token( storage_scheme: common_enums::enums::MerchantStorageScheme, mandate_id: Option, payment_method_info: Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult { let token = match token_data { storage::PaymentTokenData::TemporaryGeneric(generic_token) => { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 0501ab38cef8..3eba26468d46 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -216,7 +216,7 @@ where &validate_result, &key_store, &customer, - Some(&business_profile), + &business_profile, ) .await?; @@ -1540,7 +1540,7 @@ where &merchant_connector_account, key_store, customer, - Some(business_profile), + business_profile, ) .await?; *payment_data = pd; @@ -2549,7 +2549,7 @@ pub async fn get_connector_tokenization_action_when_confirm_true( merchant_connector_account: &helpers::MerchantConnectorAccountType, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<(PaymentData, TokenizationAction)> where F: Send + Clone, @@ -2678,7 +2678,7 @@ pub async fn tokenize_in_router_when_confirm_false_or_external_authentication, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult> where F: Send + Clone, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 34a096a6bd52..6aa321eea012 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1807,7 +1807,7 @@ pub async fn retrieve_card_with_permanent_token( _storage_scheme: enums::MerchantStorageScheme, mandate_id: Option, payment_method_info: Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult { let customer_id = payment_intent .customer_id @@ -1817,7 +1817,7 @@ pub async fn retrieve_card_with_permanent_token( message: "no customer id provided for the payment".to_string(), })?; - if business_profile.unwrap().is_network_tokenization_enabled { + if business_profile.is_network_tokenization_enabled { if let Some(mandate_ids) = mandate_id { if let Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) = mandate_ids.mandate_reference_id @@ -1993,7 +1993,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( merchant_key_store: &domain::MerchantKeyStore, customer: &Option, storage_scheme: common_enums::enums::MerchantStorageScheme, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, R>, Option, @@ -2081,7 +2081,7 @@ pub async fn make_pm_data<'a, F: Clone, R>( &payment_data.payment_intent, &payment_data.payment_attempt, merchant_key_store, - business_profile, + Some(business_profile), ) .await?; @@ -5046,7 +5046,7 @@ pub async fn get_payment_method_details_from_payment_token( storage_scheme, None, None, - Some(business_profile), + business_profile, ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), @@ -5064,7 +5064,7 @@ pub async fn get_payment_method_details_from_payment_token( storage_scheme, None, None, - Some(business_profile), + business_profile, ) .await .map(|card| Some((card, enums::PaymentMethod::Card))), diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index ebc00e42e5ae..6fe65ee22237 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -131,7 +131,7 @@ pub trait Domain: Send + Sync { storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, R>, Option, @@ -307,7 +307,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest>, Option, @@ -370,7 +370,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCaptureRequest>, Option, @@ -445,7 +445,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsCancelRequest>, Option, @@ -509,7 +509,7 @@ where _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRejectRequest>, 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 f45872c2a883..6019987729cf 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -397,7 +397,7 @@ impl Domain for CompleteAuthorize { storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, Option, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 42eca552f38b..56e97afa1640 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -775,7 +775,7 @@ impl Domain for PaymentConfirm { storage_scheme: storage_enums::MerchantStorageScheme, key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, Option, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 200d54909780..172acf460945 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -591,7 +591,7 @@ impl Domain for PaymentCreate { storage_scheme: enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, Option, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 4bf740f14648..4770433d0427 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -329,7 +329,7 @@ where _storage_scheme: storage_enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'b, F, api::PaymentsSessionRequest>, Option, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index 2304ab12565f..b47bb6d8264d 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -301,7 +301,7 @@ where storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsStartRequest>, Option, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index f9aa1c2be56e..120e4eec3036 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -89,7 +89,7 @@ impl Domain for PaymentStatus { _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, Option, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index e42f0ccdfdd5..48dafbcc23cb 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -522,7 +522,7 @@ impl Domain for PaymentUpdate { storage_scheme: storage_enums::MerchantStorageScheme, merchant_key_store: &domain::MerchantKeyStore, customer: &Option, - business_profile: Option<&domain::BusinessProfile>, + business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, 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 72ae1d6190dd..75aba903685e 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -319,7 +319,7 @@ impl Domain _storage_scheme: enums::MerchantStorageScheme, _merchant_key_store: &domain::MerchantKeyStore, _customer: &Option, - _business_profile: Option<&domain::BusinessProfile>, + _business_profile: &domain::BusinessProfile, ) -> RouterResult<( BoxedOperation<'a, F, PaymentsIncrementalAuthorizationRequest>, Option, From d3ffc79a33914c624313123edeb34bb54c4a50c2 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 3 Sep 2024 20:34:25 +0530 Subject: [PATCH 45/59] code refactoring --- config/deployments/integration_test.toml | 5 +- config/deployments/sandbox.toml | 5 +- config/development.toml | 5 +- config/docker_compose.toml | 5 +- .../src/configs/secrets_transformers.rs | 19 ++- crates/router/src/configs/settings.rs | 14 +- crates/router/src/configs/validations.rs | 55 +++++++ crates/router/src/core/errors.rs | 19 +-- .../payment_methods/network_tokenization.rs | 150 ++++++++++-------- crates/router/src/core/payments.rs | 11 +- crates/router/src/core/payments/helpers.rs | 30 +++- 11 files changed, 220 insertions(+), 98 deletions(-) diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index c2c6e7b62a63..c06d29aa91ce 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -371,4 +371,7 @@ sdk_eligible_payment_methods = "card" connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "Visa" \ No newline at end of file +card_networks = "Visa" + +[network_tokenization_supported_connectors] +connector_list = "cybersource" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index e013051259c2..08ca0c8f73dc 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -388,4 +388,7 @@ sdk_eligible_payment_methods = "card" connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "Visa" \ No newline at end of file +card_networks = "Visa" + +[network_tokenization_supported_connectors] +connector_list = "cybersource" diff --git a/config/development.toml b/config/development.toml index 5b68eac58377..d8c81c66f3f0 100644 --- a/config/development.toml +++ b/config/development.toml @@ -737,4 +737,7 @@ public_key= "" private_key= "" key_id= "" delete_token_url= "" -check_token_status_url= "" \ No newline at end of file +check_token_status_url= "" + +[network_tokenization_supported_connectors] +connector_list = "cybersource" \ No newline at end of file diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 133c9c6f3afb..a500191794a3 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -596,4 +596,7 @@ public_key= "" private_key= "" key_id= "" delete_token_url= "" -check_token_status_url= "" \ No newline at end of file +check_token_status_url= "" + +[network_tokenization_supported_connectors] +connector_list = "cybersource" diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 078bd262f100..bb41b60fa28a 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -1,4 +1,4 @@ -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, ext_traits::AsyncExt}; use hyperswitch_interfaces::secrets_interface::{ secret_handler::SecretsHandler, secret_state::{RawSecret, SecretStateContainer, SecuredSecret}, @@ -409,12 +409,16 @@ pub(crate) async fn fetch_raw_secrets( .expect("Failed to decrypt user_auth_methods configs"); #[allow(clippy::expect_used)] - let network_tokenization_service = settings::NetworkTokenizationService::convert_to_raw_secret( - conf.network_tokenization_service, - secret_management_client, - ) - .await - .expect("Failed to decrypt network tokenization service configs"); + let network_tokenization_service = + conf.network_tokenization_service + .async_map(|network_tokenization_service| async { + settings::NetworkTokenizationService::convert_to_raw_secret( + network_tokenization_service, + secret_management_client, + ) + .await + .expect("Failed to decrypt network tokenization service configs") + }).await; Settings { server: conf.server, @@ -491,5 +495,6 @@ pub(crate) async fn fetch_raw_secrets( network_tokenization_supported_card_networks: conf .network_tokenization_supported_card_networks, network_tokenization_service, + network_tokenization_supported_connectors: conf.network_tokenization_supported_connectors, } } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 33a08d5e575a..e51c2c886ad9 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -121,7 +121,8 @@ pub struct Settings { pub decision: Option, pub locker_based_open_banking_connectors: LockerBasedRecipientConnectorList, pub network_tokenization_supported_card_networks: NetworkTokenizationSupportedCardNetworks, - pub network_tokenization_service: SecretStateContainer, + pub network_tokenization_service: Option>, + pub network_tokenization_supported_connectors: NetworkTokenizationSupportedConnectors, } #[derive(Debug, Deserialize, Clone, Default)] @@ -732,6 +733,12 @@ pub struct UserAuthMethodSettings { pub encryption_key: Secret, } +#[derive(Debug, Deserialize, Clone, Default)] +pub struct NetworkTokenizationSupportedConnectors { + #[serde(deserialize_with = "deserialize_hashset")] + pub connector_list: HashSet, +} + impl Settings { pub fn new() -> ApplicationResult { Self::with_config_path(None) @@ -841,6 +848,11 @@ impl Settings { .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.into()))?; self.generic_link.payment_method_collect.validate()?; self.generic_link.payout_link.validate()?; + + self.network_tokenization_service + .as_ref() + .map(|x| x.get_inner().validate()).transpose()?; + Ok(()) } } diff --git a/crates/router/src/configs/validations.rs b/crates/router/src/configs/validations.rs index 441172b05cca..c6b853c449de 100644 --- a/crates/router/src/configs/validations.rs +++ b/crates/router/src/configs/validations.rs @@ -192,3 +192,58 @@ impl super::settings::GenericLinkEnvConfig { }) } } + + +impl super::settings::NetworkTokenizationService { + pub fn validate(&self) -> Result<(), ApplicationError> { + use common_utils::fp_utils::when; + + when(self.generate_token_url.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "generate_token_url must not be empty".into(), + )) + })?; + + when(self.fetch_token_url.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "fetch_token_url must not be empty".into(), + )) + })?; + + when(self.delete_token_url.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "delete_token_url must not be empty".into(), + )) + })?; + + when(self.check_token_status_url.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "check_token_status_url must not be empty".into(), + )) + })?; + + when(self.token_service_api_key.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "token_service_api_key must not be empty".into(), + )) + })?; + + when(self.public_key.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "public_key must not be empty".into(), + )) + })?; + + when(self.key_id.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "key_id must not be empty".into(), + )) + })?; + + when(self.private_key.is_default_or_empty(), || { + Err(ApplicationError::InvalidConfigurationValueError( + "private_key must not be empty".into(), + )) + }) + } +} \ No newline at end of file diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index c22b9345736f..808f093feea6 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -340,22 +340,9 @@ pub enum NetworkTokenizationError { RequestEncodingFailed, #[error("Failed to deserialize network token service response")] ResponseDeserializationFailed, - #[error("Failed to create payment method")] - PaymentMethodCreationFailed, - #[error("The given payment method is currently not supported in vault")] - PaymentMethodNotSupported, - #[error("The given payout method is currently not supported in vault")] - PayoutMethodNotSupported, - #[error("Missing required field: {field_name}")] - MissingRequiredField { field_name: &'static str }, - #[error("The card vault returned an unexpected response: {0:?}")] - UnexpectedResponseError(bytes::Bytes), - #[error("Failed to update in PMD table")] - UpdateInPaymentMethodDataTableFailed, - #[error("Failed to fetch payment method in vault")] - FetchPaymentMethodFailed, - #[error("Failed to save payment method in vault")] - SavePaymentMethodFailed, #[error("Failed to delete network token")] DeleteNetworkTokenFailed, + #[error("Network token service not configured")] + NetworkTokenizationServiceNotConfigured + } diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 40d935e64592..16508452010d 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -27,6 +27,7 @@ use crate::{ headers, logger, routes::{self}, services::{self, encryption, EncryptionAlgorithm}, + settings::NetworkTokenizationService, types::{ api::{self}, domain, @@ -68,22 +69,6 @@ pub struct CardNetworkTokenResponse { payload: String, } -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CardNetworkTokenResponsePayloadTemporary { - pub card_brand: api_enums::CardNetwork, - pub card_fingerprint: String, - pub card_reference: String, - pub correlation_id: String, - pub customer_id: String, - pub par: String, - pub token_expiry_month: Secret, - pub token_expiry_year: Secret, - pub token_isin: String, - pub token_last_four: String, - pub token_status: String, -} - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardNetworkTokenResponsePayload { @@ -193,9 +178,9 @@ pub async fn mk_tokenization_req( amount: String, currency: String, customer_id: id_type::CustomerId, + tokenization_service: &NetworkTokenizationService, ) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError> { - let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let enc_key = tokenization_service.public_key.peek().clone(); let key_id = tokenization_service.key_id.clone(); @@ -242,7 +227,8 @@ pub async fn mk_tokenization_req( .into_masked(), ); request.add_default_headers(); - logger::info!("api Payload to generate token: {:?}", api_payload); + + logger::info!("api Payload to generate token: {:?}", api_payload); //added for debugging request.set_body(RequestContent::Json(Box::new(api_payload))); logger::info!("Request to generate token: {:?}", request); @@ -281,7 +267,7 @@ pub async fn mk_tokenization_req( .response .parse_struct("Card Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; - logger::info!("Network Token Response: {:?}", network_response); + logger::info!("Network Token Response: {:?}", network_response); //added for debugging let dec_key = tokenization_service.private_key.peek().clone(); @@ -297,7 +283,7 @@ pub async fn mk_tokenization_req( "Failed to decrypt the tokenization response from the tokenization service", )?; - logger::info!("Decrypted Response: {:?}", card_network_token_response); + logger::info!("Decrypted Response: {:?}", card_network_token_response); //added for debugging let cn_response: CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) @@ -328,24 +314,32 @@ pub async fn make_card_network_tokenization_request( let payload_bytes = payload.as_bytes(); let amount_str = amount.map_or_else(String::new, |a| a.to_string()); let currency_str = currency.map_or_else(String::new, |c| c.to_string()); - - mk_tokenization_req( - state, - payload_bytes, - amount_str, - currency_str, - customer_id.clone(), - ) - .await - .inspect_err(|e| logger::error!(error=?e, "Error while making tokenization request")) + if let Some(network_tokenization_service) = &state.conf.network_tokenization_service { + mk_tokenization_req( + state, + payload_bytes, + amount_str, + currency_str, + customer_id.clone(), + network_tokenization_service.get_inner(), + ) + .await + .inspect_err(|e| logger::error!(error=?e, "Error while making tokenization request")) + } else { + Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured) + .inspect_err(|_| { + logger::error!("Network Tokenization Service not configured" ); + }) + .attach_printable("Network Tokenization Service not configured") + } } pub async fn get_network_token( state: &routes::SessionState, customer_id: id_type::CustomerId, network_token_requestor_ref_id: String, + tokenization_service: &NetworkTokenizationService, ) -> CustomResult { - let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( services::Method::Post, tokenization_service.fetch_token_url.as_str(), @@ -366,7 +360,7 @@ pub async fn get_network_token( .into_masked(), ); request.add_default_headers(); - logger::info!("Payload to fetch network token: {:?}", payload); + logger::info!("Payload to fetch network token: {:?}", payload); //added for debugging`` request.set_body(RequestContent::Json(Box::new(payload))); logger::info!("Request to fetch network token: {:?}", request); @@ -417,16 +411,27 @@ pub async fn get_token_from_tokenization_service( network_token_requestor_ref_id: String, pm_data: &storage::PaymentMethod, ) -> errors::RouterResult { - let token_response = get_network_token( - state, - pm_data.customer_id.clone(), - network_token_requestor_ref_id, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .inspect_err( - |e| logger::error!(error=?e, "Error while fetching token from tokenization service"), - )?; + let token_response = + if let Some(network_tokenization_service) = &state.conf.network_tokenization_service { + get_network_token( + state, + pm_data.customer_id.clone(), + network_token_requestor_ref_id, + network_tokenization_service.get_inner(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Fetch network token failed") + .inspect_err( + |e| logger::error!(error=?e, "Error while fetching token from tokenization service"), + ) + } else { + Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured) + .change_context(errors::ApiErrorResponse::InternalServerError) + .inspect_err(|_| { + logger::error!("Network Tokenization Service not configured" ); + }) + }?; let token_decrypted = domain::types::crypto_operation::( &state.into(), @@ -518,14 +523,27 @@ pub async fn do_status_check_for_network_token( .is_none() { if let Some(ref_id) = network_token_requestor_reference_id { - let (token_exp_month, token_exp_year) = check_token_status_with_tokenization_service( - state, - &payment_method_info.customer_id.clone(), - ref_id, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?; - Ok((token_exp_month, token_exp_year)) + if let Some(network_tokenization_service) = &state.conf.network_tokenization_service { + let (token_exp_month, token_exp_year) = + check_token_status_with_tokenization_service( + state, + &payment_method_info.customer_id.clone(), + ref_id, + network_tokenization_service.get_inner(), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Check network token status with tokenization service failed", + )?; + Ok((token_exp_month, token_exp_year)) + } else { + Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured) + .change_context(errors::ApiErrorResponse::InternalServerError) + .inspect_err(|_| { + logger::error!("Network Tokenization Service not configured" ); + }) + } } else { Err(errors::NetworkTokenizationError::FetchNetworkTokenFailed) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -540,10 +558,9 @@ pub async fn check_token_status_with_tokenization_service( state: &routes::SessionState, customer_id: &id_type::CustomerId, network_token_requestor_reference_id: String, + tokenization_service: &NetworkTokenizationService, ) -> CustomResult<(Option>, Option>), errors::NetworkTokenizationError> { - let tokenization_service = &state.conf.network_tokenization_service.get_inner(); - let mut request = services::Request::new( services::Method::Post, tokenization_service.check_token_status_url.as_str(), @@ -628,18 +645,21 @@ pub async fn delete_network_token_from_locker_and_token_service( .unwrap_or(&payment_method_id), ) .await?; - if delete_network_token_from_tokenization_service( - state, - network_token_requestor_reference_id, - customer_id, - ) - .await - .is_ok() - { - logger::info!("Token From Tokenization Service deleted Successfully!"); - } else { - logger::error!("Error while deleting Token From Tokenization Service!"); - } + if let Some(tokenization_service) = &state.conf.network_tokenization_service { + if delete_network_token_from_tokenization_service( + state, + network_token_requestor_reference_id, + customer_id, + tokenization_service.get_inner(), + ) + .await + .is_ok() + { + logger::info!("Token From Tokenization Service deleted Successfully!"); + } else { + logger::error!("Error while deleting Token From Tokenization Service!"); + } + }; Ok(resp) } @@ -648,8 +668,8 @@ pub async fn delete_network_token_from_tokenization_service( state: &routes::SessionState, network_token_requestor_reference_id: String, customer_id: &id_type::CustomerId, + tokenization_service: &NetworkTokenizationService, ) -> CustomResult { - let tokenization_service = &state.conf.network_tokenization_service.get_inner(); let mut request = services::Request::new( services::Method::Post, tokenization_service.delete_token_url.as_str(), diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 3eba26468d46..b16efc8c1642 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3721,7 +3721,7 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment, @@ -3936,6 +3937,11 @@ pub fn is_network_token_with_transaction_id_flow( .network_transaction_id_supported_connectors .connector_list; + let network_tokenization_supported_connectors = &state + .conf + .network_tokenization_supported_connectors + .connector_list; + is_connector_agnostic_mit_enabled == Some(true) && is_network_tokenization_enabled && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) @@ -3945,6 +3951,7 @@ pub fn is_network_token_with_transaction_id_flow( .network_token_requestor_reference_id .is_some() && ntid_supported_connectors.contains(&connector) + && network_tokenization_supported_connectors.contains(&connector) } pub fn should_add_task_to_process_tracker(payment_data: &PaymentData) -> bool { diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 6aa321eea012..56cc01dbee9a 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, str::FromStr}; #[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use api_models::customers::CustomerRequestWithEmail; @@ -1876,14 +1876,30 @@ pub async fn retrieve_card_with_permanent_token( } } + let card = cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed to fetch card information from the permanent locker")?; + + // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked + // from payment_method_data.card_token object + let name_on_card = if let Some(name) = card.name_on_card.clone() { + if name.clone().expose().is_empty() { + card_token_data + .and_then(|token_data| token_data.card_holder_name.clone()) + .or(Some(name)) + } else { + card.name_on_card + } + } else { + card_token_data.and_then(|token_data| token_data.card_holder_name.clone()) + }; + let api_card = api::Card { card_number: card.card_number, - card_holder_name: None, + card_holder_name: name_on_card, card_exp_month: card.card_exp_month, card_exp_year: card.card_exp_year, card_cvc: card_token_data @@ -1893,7 +1909,15 @@ pub async fn retrieve_card_with_permanent_token( .unwrap_or_default(), card_issuer: None, nick_name: card.nick_name.map(masking::Secret::new), - card_network: None, + card_network: card + .card_brand + .map(|card_brand| enums::CardNetwork::from_str(&card_brand)) + .transpose() + .map_err(|e| { + logger::error!("Failed to parse card network {e:?}"); + }) + .ok() + .flatten(), card_type: None, card_issuing_country: None, bank_code: None, From 3520578b5dfedece59b848d988f3399820854e13 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Tue, 3 Sep 2024 23:49:56 +0530 Subject: [PATCH 46/59] remove unnecessary logs --- .../router/src/core/payment_methods/cards.rs | 28 ------------------- .../payment_methods/network_tokenization.rs | 6 ---- 2 files changed, 34 deletions(-) diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 752703398226..bdfd1f1308df 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -107,34 +107,6 @@ use crate::{ types::transformers::ForeignFrom, }; -#[cfg(all( - feature = "v2", - feature = "payment_methods_v2", - feature = "customer_v2" -))] -#[instrument(skip_all)] -#[allow(clippy::too_many_arguments)] -pub async fn create_payment_method( - state: &routes::SessionState, - req: &api::PaymentMethodCreate, - customer_id: &id_type::CustomerId, - payment_method_id: &str, - locker_id: Option, - merchant_id: &id_type::MerchantId, - pm_metadata: Option, - customer_acceptance: Option, - payment_method_data: Option, - key_store: &domain::MerchantKeyStore, - connector_mandate_details: Option, - status: Option, - network_transaction_id: Option, - storage_scheme: MerchantStorageScheme, - payment_method_billing_address: Option, - card_scheme: Option, -) -> errors::CustomResult { - todo!() -} - #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2"), diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 16508452010d..32c39342561c 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -195,8 +195,6 @@ pub async fn mk_tokenization_req( .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) .attach_printable("Error on jwe encrypt")?; - logger::info!("JWE Encrypted Payload: {}", jwt); - let order_data = OrderData { consent_id: uuid::Uuid::new_v4().to_string(), customer_id, @@ -228,7 +226,6 @@ pub async fn mk_tokenization_req( ); request.add_default_headers(); - logger::info!("api Payload to generate token: {:?}", api_payload); //added for debugging request.set_body(RequestContent::Json(Box::new(api_payload))); logger::info!("Request to generate token: {:?}", request); @@ -283,7 +280,6 @@ pub async fn mk_tokenization_req( "Failed to decrypt the tokenization response from the tokenization service", )?; - logger::info!("Decrypted Response: {:?}", card_network_token_response); //added for debugging let cn_response: CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) @@ -360,7 +356,6 @@ pub async fn get_network_token( .into_masked(), ); request.add_default_headers(); - logger::info!("Payload to fetch network token: {:?}", payload); //added for debugging`` request.set_body(RequestContent::Json(Box::new(payload))); logger::info!("Request to fetch network token: {:?}", request); @@ -690,7 +685,6 @@ pub async fn delete_network_token_from_tokenization_service( .into_masked(), ); request.add_default_headers(); - logger::info!("Payload to delete network token: {:?}", payload); request.set_body(RequestContent::Json(Box::new(payload))); logger::info!("Request to delete network token: {:?}", request); From f8b4287c99a592482937507924dd4fc47e85db45 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Thu, 5 Sep 2024 14:39:39 +0530 Subject: [PATCH 47/59] resolve pr comments --- crates/api_models/src/admin.rs | 3 + crates/router/src/configs/settings.rs | 10 +- crates/router/src/configs/validations.rs | 24 -- .../src/connector/cybersource/transformers.rs | 4 +- .../payment_methods/network_tokenization.rs | 205 +++++------ crates/router/src/core/payments/helpers.rs | 205 +++++++---- .../router/src/core/payments/tokenization.rs | 317 +++++++++--------- crates/router/src/routes/metrics.rs | 6 + 8 files changed, 429 insertions(+), 345 deletions(-) diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 093ce5afc402..4598c3197de6 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2196,6 +2196,9 @@ pub struct BusinessProfileResponse { /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: bool, + /// Indicates if is_network_tokenization_enabled is enabled or not. + /// If set to `true` is_network_tokenization_enabled will be checked. + #[schema(default = false, example = false)] pub is_network_tokenization_enabled: bool, } diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index e51c2c886ad9..93d40d9178ec 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -402,16 +402,16 @@ pub struct NetworkTokenizationSupportedCardNetworks { pub card_networks: HashSet, } -#[derive(Debug, Deserialize, Clone, Default)] +#[derive(Debug, Deserialize, Clone)] pub struct NetworkTokenizationService { - pub generate_token_url: String, - pub fetch_token_url: String, + pub generate_token_url: url::Url, + pub fetch_token_url: url::Url, pub token_service_api_key: Secret, pub public_key: Secret, pub private_key: Secret, pub key_id: String, - pub delete_token_url: String, - pub check_token_status_url: String, + pub delete_token_url: url::Url, + pub check_token_status_url: url::Url, } #[derive(Debug, Deserialize, Clone)] diff --git a/crates/router/src/configs/validations.rs b/crates/router/src/configs/validations.rs index c6b853c449de..b80f5a38089d 100644 --- a/crates/router/src/configs/validations.rs +++ b/crates/router/src/configs/validations.rs @@ -198,30 +198,6 @@ impl super::settings::NetworkTokenizationService { pub fn validate(&self) -> Result<(), ApplicationError> { use common_utils::fp_utils::when; - when(self.generate_token_url.is_default_or_empty(), || { - Err(ApplicationError::InvalidConfigurationValueError( - "generate_token_url must not be empty".into(), - )) - })?; - - when(self.fetch_token_url.is_default_or_empty(), || { - Err(ApplicationError::InvalidConfigurationValueError( - "fetch_token_url must not be empty".into(), - )) - })?; - - when(self.delete_token_url.is_default_or_empty(), || { - Err(ApplicationError::InvalidConfigurationValueError( - "delete_token_url must not be empty".into(), - )) - })?; - - when(self.check_token_status_url.is_default_or_empty(), || { - Err(ApplicationError::InvalidConfigurationValueError( - "check_token_status_url must not be empty".into(), - )) - })?; - when(self.token_service_api_key.is_default_or_empty(), || { Err(ApplicationError::InvalidConfigurationValueError( "token_service_api_key must not be empty".into(), diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index d5b6980fbc80..6229fc1b3cd1 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -792,7 +792,7 @@ impl ), None => None, }; - commerce_indicator = "recurring".to_string(); + commerce_indicator = "recurring".to_string(); // ( None, None, @@ -803,7 +803,7 @@ impl stored_credential_used: Some(true), }), merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { - reason: Some("7".to_string()), + reason: Some("7".to_string()), // 7 is for MIT using NTI original_authorized_amount, previous_transaction_id: Some(Secret::new( mandate_data.network_transaction_id, diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 32c39342561c..5a24fe9b2b20 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -6,32 +6,27 @@ use common_utils::{ errors::CustomResult, ext_traits::{BytesExt, Encode, ValueExt}, id_type, + metrics::utils::record_operation_time, request::RequestContent, type_name, types::keymanager::Identifier, }; use diesel_models::payment_method; use error_stack::ResultExt; -use hyperswitch_domain_models::payment_method_data::NetworkTokenData; use josekit::jwe; use masking::{ExposeInterface, Mask, PeekInterface, Secret}; use serde::{Deserialize, Serialize}; use super::transformers::DeleteCardResp; use crate::{ - core::{ - errors, - payment_methods::{self}, - payments::helpers, - }, + core::{errors, payment_methods, payments::helpers}, headers, logger, - routes::{self}, - services::{self, encryption, EncryptionAlgorithm}, - settings::NetworkTokenizationService, + routes::{self, metrics}, + services::{self, encryption}, + settings, types::{ - api::{self}, - domain, - storage::{self, enums as storage_enums}, + api, domain, + storage, }, }; @@ -44,16 +39,14 @@ pub struct CardData { card_security_code: Secret, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct OrderData { consent_id: String, - customer_id: id_type::CustomerId, - amount: String, - currency: String, + customer_id: id_type::CustomerId } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct ApiPayload { service: String, @@ -64,12 +57,12 @@ pub struct ApiPayload { sub_merchant_id: String, } -#[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] +#[derive(Debug, Deserialize, Eq, PartialEq)] pub struct CardNetworkTokenResponse { - payload: String, + payload: Secret, //encrypted payload } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct CardNetworkTokenResponsePayload { pub card_brand: api_enums::CardNetwork, @@ -86,12 +79,12 @@ pub struct CardNetworkTokenResponsePayload { pub token_status: String, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize)] pub struct GetCardToken { card_reference: String, customer_id: id_type::CustomerId, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] pub struct AuthenticationDetails { cryptogram: Secret, token: CardNumber, @@ -103,7 +96,7 @@ pub struct TokenDetails { exp_year: Secret, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Deserialize)] pub struct TokenResponse { authentication_details: AuthenticationDetails, network: api_enums::CardNetwork, @@ -172,13 +165,13 @@ pub struct CheckTokenStatusResponse { payload: CheckTokenStatusResponsePayload, } +pub const NETWORK_TOKEN_SERVICE: &str = "NETWORK_TOKEN"; + pub async fn mk_tokenization_req( state: &routes::SessionState, payload_bytes: &[u8], - amount: String, - currency: String, customer_id: id_type::CustomerId, - tokenization_service: &NetworkTokenizationService, + tokenization_service: &settings::NetworkTokenizationService, ) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError> { let enc_key = tokenization_service.public_key.peek().clone(); @@ -188,7 +181,7 @@ pub async fn mk_tokenization_req( let jwt = encryption::encrypt_jwe( payload_bytes, enc_key, - EncryptionAlgorithm::A128GCM, + services::EncryptionAlgorithm::A128GCM, Some(key_id.as_str()), ) .await @@ -198,17 +191,15 @@ pub async fn mk_tokenization_req( let order_data = OrderData { consent_id: uuid::Uuid::new_v4().to_string(), customer_id, - amount, - currency, }; let api_payload = ApiPayload { - service: "NETWORK_TOKEN".to_string(), + service: NETWORK_TOKEN_SERVICE.to_string(), card_data: jwt, order_data, key_id, should_send_token: true, - sub_merchant_id: "visa_sbx_working".to_string(), + sub_merchant_id: "visa_sbx_working".to_string(), //todo!() this will be removed. }; let mut request = services::Request::new( @@ -264,12 +255,12 @@ pub async fn mk_tokenization_req( .response .parse_struct("Card Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; - logger::info!("Network Token Response: {:?}", network_response); //added for debugging + logger::debug!("Network Token Response: {:?}", network_response); //added for debugging, will be removed let dec_key = tokenization_service.private_key.peek().clone(); let card_network_token_response = services::decrypt_jwe( - &network_response.payload, + network_response.payload.peek(), services::KeyIdCheck::SkipKeyIdCheck, dec_key, jwe::RSA_OAEP_256, @@ -280,7 +271,6 @@ pub async fn mk_tokenization_req( "Failed to decrypt the tokenization response from the tokenization service", )?; - let cn_response: CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; @@ -291,8 +281,6 @@ pub async fn make_card_network_tokenization_request( state: &routes::SessionState, card: &domain::Card, customer_id: &id_type::CustomerId, - amount: Option, - currency: Option, ) -> CustomResult<(CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError> { let card_data = CardData { @@ -308,23 +296,29 @@ pub async fn make_card_network_tokenization_request( .change_context(errors::NetworkTokenizationError::RequestEncodingFailed)?; let payload_bytes = payload.as_bytes(); - let amount_str = amount.map_or_else(String::new, |a| a.to_string()); - let currency_str = currency.map_or_else(String::new, |c| c.to_string()); if let Some(network_tokenization_service) = &state.conf.network_tokenization_service { - mk_tokenization_req( - state, - payload_bytes, - amount_str, - currency_str, - customer_id.clone(), - network_tokenization_service.get_inner(), + record_operation_time( + async { + mk_tokenization_req( + state, + payload_bytes, + customer_id.clone(), + network_tokenization_service.get_inner(), + ) + .await + .inspect_err( + |e| logger::error!(error=?e, "Error while making tokenization request"), + ) + }, + &metrics::GENERATE_NETWORK_TOKEN_TIME, + &metrics::CONTEXT, + &[router_env::opentelemetry::KeyValue::new("locker", "rust")], ) .await - .inspect_err(|e| logger::error!(error=?e, "Error while making tokenization request")) } else { Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured) .inspect_err(|_| { - logger::error!("Network Tokenization Service not configured" ); + logger::error!("Network Tokenization Service not configured"); }) .attach_printable("Network Tokenization Service not configured") } @@ -334,7 +328,7 @@ pub async fn get_network_token( state: &routes::SessionState, customer_id: id_type::CustomerId, network_token_requestor_ref_id: String, - tokenization_service: &NetworkTokenizationService, + tokenization_service: &settings::NetworkTokenizationService, ) -> CustomResult { let mut request = services::Request::new( services::Method::Post, @@ -386,9 +380,6 @@ pub async fn get_network_token( .attach_printable(format!("Response Deserialization Failed: {err_res:?}")) } Ok(res) => Ok(res), - }) - .inspect_err(|err| { - logger::error!("Error while deserializing response: {:?}", err); })?; let token_response: TokenResponse = res @@ -405,27 +396,35 @@ pub async fn get_token_from_tokenization_service( key_store: &domain::MerchantKeyStore, network_token_requestor_ref_id: String, pm_data: &storage::PaymentMethod, -) -> errors::RouterResult { +) -> errors::RouterResult { let token_response = if let Some(network_tokenization_service) = &state.conf.network_tokenization_service { - get_network_token( - state, - pm_data.customer_id.clone(), - network_token_requestor_ref_id, - network_tokenization_service.get_inner(), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Fetch network token failed") - .inspect_err( - |e| logger::error!(error=?e, "Error while fetching token from tokenization service"), - ) + record_operation_time( + async { + get_network_token( + state, + pm_data.customer_id.clone(), + network_token_requestor_ref_id, + network_tokenization_service.get_inner(), + ) + .await + .inspect_err( + |e| logger::error!(error=?e, "Error while fetching token from tokenization service") + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Fetch network token failed") + }, + &metrics::FETCH_NETWORK_TOKEN_TIME, + &metrics::CONTEXT, + &[], + ) + .await } else { Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured) - .change_context(errors::ApiErrorResponse::InternalServerError) - .inspect_err(|_| { - logger::error!("Network Tokenization Service not configured" ); + .inspect_err(|err| { + logger::error!(error=? err); }) + .change_context(errors::ApiErrorResponse::InternalServerError) }?; let token_decrypted = domain::types::crypto_operation::( @@ -459,7 +458,7 @@ pub async fn get_token_from_tokenization_service( .ok_or(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to obtain decrypted token object from db")?; - let network_token_data = NetworkTokenData { + let network_token_data = domain::NetworkTokenData { token_number: token_response.authentication_details.token, token_cryptogram: Some(token_response.authentication_details.cryptogram), token_exp_month: token_decrypted @@ -519,24 +518,34 @@ pub async fn do_status_check_for_network_token( { if let Some(ref_id) = network_token_requestor_reference_id { if let Some(network_tokenization_service) = &state.conf.network_tokenization_service { - let (token_exp_month, token_exp_year) = - check_token_status_with_tokenization_service( - state, - &payment_method_info.customer_id.clone(), - ref_id, - network_tokenization_service.get_inner(), - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "Check network token status with tokenization service failed", - )?; + let (token_exp_month, token_exp_year) = record_operation_time( + async { + check_token_status_with_tokenization_service( + state, + &payment_method_info.customer_id.clone(), + ref_id, + network_tokenization_service.get_inner(), + ) + .await + .inspect_err( + |e| logger::error!(error=?e, "Error while fetching token from tokenization service") + ) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Check network token status with tokenization service failed", + ) + }, + &metrics::CHECK_NETWORK_TOKEN_STATUS_TIME, + &metrics::CONTEXT, + &[], + ) + .await?; Ok((token_exp_month, token_exp_year)) } else { Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured) .change_context(errors::ApiErrorResponse::InternalServerError) .inspect_err(|_| { - logger::error!("Network Tokenization Service not configured" ); + logger::error!("Network Tokenization Service not configured"); }) } } else { @@ -553,7 +562,7 @@ pub async fn check_token_status_with_tokenization_service( state: &routes::SessionState, customer_id: &id_type::CustomerId, network_token_requestor_reference_id: String, - tokenization_service: &NetworkTokenizationService, + tokenization_service: &settings::NetworkTokenizationService, ) -> CustomResult<(Option>, Option>), errors::NetworkTokenizationError> { let mut request = services::Request::new( @@ -641,19 +650,25 @@ pub async fn delete_network_token_from_locker_and_token_service( ) .await?; if let Some(tokenization_service) = &state.conf.network_tokenization_service { - if delete_network_token_from_tokenization_service( - state, - network_token_requestor_reference_id, - customer_id, - tokenization_service.get_inner(), + let delete_token_resp = record_operation_time( + async { + delete_network_token_from_tokenization_service( + state, + network_token_requestor_reference_id, + customer_id, + tokenization_service.get_inner(), + ) + .await + }, + &metrics::DELETE_NETWORK_TOKEN_TIME, + &metrics::CONTEXT, + &[], ) - .await - .is_ok() - { - logger::info!("Token From Tokenization Service deleted Successfully!"); - } else { - logger::error!("Error while deleting Token From Tokenization Service!"); - } + .await; + match delete_token_resp{ + Ok(_) => logger::info!("Token From Tokenization Service deleted Successfully!"), + Err(e) => logger::error!(error=?e, "Error while deleting Token From Tokenization Service!") + }; }; Ok(resp) @@ -663,7 +678,7 @@ pub async fn delete_network_token_from_tokenization_service( state: &routes::SessionState, network_token_requestor_reference_id: String, customer_id: &id_type::CustomerId, - tokenization_service: &NetworkTokenizationService, + tokenization_service: &settings::NetworkTokenizationService, ) -> CustomResult { let mut request = services::Request::new( services::Method::Post, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 56cc01dbee9a..1f1f7ede0bf0 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1817,75 +1817,154 @@ pub async fn retrieve_card_with_permanent_token( message: "no customer id provided for the payment".to_string(), })?; - if business_profile.is_network_tokenization_enabled { - if let Some(mandate_ids) = mandate_id { - if let Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) = - mandate_ids.mandate_reference_id - { - if let Some(ref pm_data) = payment_method_info { - if let Some(network_token_locker_id) = &pm_data.network_token_locker_id { - let mut token_data = cards::get_card_from_locker( + if !business_profile.is_network_tokenization_enabled { + fetch_card_details_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + locker_id, + card_token_data, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("failed to fetch card information from the permanent locker") + } else { + match (payment_method_info, mandate_id) { + (None, _) => Err(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Payment method data is not present"), + (Some(ref pm_data), None) => { + // Regular (non-mandate) Payment flow + if let Some(token_ref) = pm_data.network_token_requestor_reference_id.clone() { + match network_tokenization::get_token_from_tokenization_service( + state, + merchant_key_store, + token_ref, + pm_data, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to fetch network token data from tokenization service", + ) { + Ok(network_token_data) => { + Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) + } + Err(err) => { + logger::info!("Failed to fetch network token data from tokenization service {err:?}"); + logger::info!("Falling back to fetch card details from locker"); + fetch_card_details_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + locker_id, + card_token_data, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to fetch card information from the permanent locker", + ) + } + } + } else { + fetch_card_details_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + locker_id, + card_token_data, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to fetch card information from the permanent locker", + ) + } + }, + (Some(ref pm_data), Some(mandate_ids)) => { + // Mandate Payment flow + match mandate_ids.mandate_reference_id { + Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) => { + { + if let Some(network_token_locker_id) = + pm_data.network_token_locker_id.as_ref() + { + let mut token_data = cards::get_card_from_locker( + state, + customer_id, + &payment_intent.merchant_id, + network_token_locker_id, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "failed to fetch network token information from the permanent locker", + )?; + let expiry = + nt_data.token_exp_month.zip(nt_data.token_exp_year); + if let Some((exp_month, exp_year)) = expiry { + token_data.card_exp_month = exp_month; + token_data.card_exp_year = exp_year; + } + let network_token_data = domain::NetworkTokenData { + token_number: token_data.card_number, + token_cryptogram: None, + token_exp_month: token_data.card_exp_month, + token_exp_year: token_data.card_exp_year, + nick_name: token_data.nick_name.map(masking::Secret::new), + card_issuer: None, + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) + } else { + // Mandate but network token locker id is not present + Err(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Network token locker id is not present") + } + } + }, + + Some(api_models::payments::MandateReferenceId::NetworkMandateId(_)) => { + fetch_card_details_from_locker( state, customer_id, &payment_intent.merchant_id, - network_token_locker_id.as_ref(), + locker_id, + card_token_data, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "failed to fetch network token information from the permanent locker", - )?; - let expiry = nt_data.token_exp_month.zip(nt_data.token_exp_year); - if let Some((exp_month, exp_year)) = expiry { - token_data.card_exp_month = exp_month; - token_data.card_exp_year = exp_year; - } - let network_token_data = domain::NetworkTokenData { - token_number: token_data.card_number, - token_cryptogram: None, - token_exp_month: token_data.card_exp_month, - token_exp_year: token_data.card_exp_year, - nick_name: token_data.nick_name.map(masking::Secret::new), - card_issuer: None, - card_network: None, - card_type: None, - card_issuing_country: None, - bank_code: None, - }; - return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); - } - } - } - } + .attach_printable("failed to fetch card information from the permanent locker") + }, - if let Some(ref pm_data) = payment_method_info { - if let Some(token_ref) = pm_data.network_token_requestor_reference_id.clone() { - let network_token_data = network_tokenization::get_token_from_tokenization_service( - state, - merchant_key_store, - token_ref, - pm_data, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch network token data from tokenization service"); - if let Ok(network_token_data) = network_token_data { - return Ok(domain::PaymentMethodData::NetworkToken(network_token_data)); + Some(api_models::payments::MandateReferenceId::ConnectorMandateId(_)) | None => { + Err(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Payment method data is not present") + } } } } } +} - - let card = - cards::get_card_from_locker(state, customer_id, &payment_intent.merchant_id, locker_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch card information from the permanent locker")?; +pub async fn fetch_card_details_from_locker( + state: &SessionState, + customer_id: &id_type::CustomerId, + merchant_id: &id_type::MerchantId, + locker_id: &str, + card_token_data: Option<&domain::CardToken>, +) -> RouterResult { + let card = cards::get_card_from_locker(state, customer_id, merchant_id, locker_id) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("failed to fetch card information from the permanent locker")?; // The card_holder_name from locker retrieved card is considered if it is a non-empty string or else card_holder_name is picked - // from payment_method_data.card_token object - let name_on_card = if let Some(name) = card.name_on_card.clone() { + // from payment_method_data.card_token object + let name_on_card = if let Some(name) = card.name_on_card.clone() { if name.clone().expose().is_empty() { card_token_data .and_then(|token_data| token_data.card_holder_name.clone()) @@ -1910,14 +1989,14 @@ pub async fn retrieve_card_with_permanent_token( card_issuer: None, nick_name: card.nick_name.map(masking::Secret::new), card_network: card - .card_brand - .map(|card_brand| enums::CardNetwork::from_str(&card_brand)) - .transpose() - .map_err(|e| { - logger::error!("Failed to parse card network {e:?}"); - }) - .ok() - .flatten(), + .card_brand + .map(|card_brand| enums::CardNetwork::from_str(&card_brand)) + .transpose() + .map_err(|e| { + logger::error!("Failed to parse card network {e:?}"); + }) + .ok() + .flatten(), card_type: None, card_issuing_country: None, bank_code: None, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index e6c94faf6b1c..e1f09fb26f49 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -201,54 +201,55 @@ where let merchant_id = merchant_account.get_id(); let is_network_tokenization_enabled = business_profile.is_network_tokenization_enabled; - let ((mut resp, duplication_check, network_token_requestor_ref_id), token_resp) = - if !state.conf.locker.locker_enabled { - let (res, dc) = skip_saving_card_in_locker( - merchant_account, - payment_method_create_request.to_owned(), - ) - .await?; - ((res, dc, None), None) - } else { - pm_status = Some(common_enums::PaymentMethodStatus::from( - save_payment_method_data.attempt_status, - )); - let (res, dc, _ref_id) = Box::pin(save_in_locker( - state, - merchant_account, - Some(&save_payment_method_data.request.get_payment_method_data()), - payment_method_create_request.to_owned(), - false, //saving the card in locker - amount, - currency, - )) - .await?; - - if is_network_tokenization_enabled { - let ( - network_token_resp, - _network_token_duplication_check, - network_token_requestor_ref_id, - ) = Box::pin(save_in_locker( - state, - merchant_account, - Some(&save_payment_method_data.request.get_payment_method_data()), - payment_method_create_request.to_owned(), - true, //saving the token in locker - amount, - currency, - )) - .await?; + let ( + (mut resp, duplication_check, network_token_requestor_ref_id), + network_token_resp, + ) = if !state.conf.locker.locker_enabled { + let (res, dc) = skip_saving_card_in_locker( + merchant_account, + payment_method_create_request.to_owned(), + ) + .await?; + ((res, dc, None), None) + } else { + pm_status = Some(common_enums::PaymentMethodStatus::from( + save_payment_method_data.attempt_status, + )); + let (res, dc) = Box::pin(save_in_locker( + state, + merchant_account, + payment_method_create_request.to_owned(), + )) + .await?; - ( - (res, dc, network_token_requestor_ref_id), - Some(network_token_resp), - ) - } else { - ((res, dc, None), None) + if is_network_tokenization_enabled { + let pm_data = &save_payment_method_data.request.get_payment_method_data(); + match pm_data { + domain::PaymentMethodData::Card(card) => { + let ( + network_token_resp, + _network_token_duplication_check, + network_token_requestor_ref_id, + ) = Box::pin(save_network_token_in_locker( + state, + merchant_account, + card, + payment_method_create_request.clone(), + )) + .await?; + + ( + (res, dc, network_token_requestor_ref_id), + network_token_resp, + ) + } + _ => ((res, dc, None), None), //network_token_resp is None in case of other payment methods } - }; - let network_token_locker_id = match token_resp { + } else { + ((res, dc, None), None) + } + }; + let network_token_locker_id = match network_token_resp { Some(ref token_resp) => { if network_token_requestor_ref_id.is_some() { Some(token_resp.payment_method_id.clone()) @@ -273,7 +274,7 @@ where let pm_network_token_data_encrypted: Option< Encryptable>, - > = match token_resp { + > = match network_token_resp { Some(token_resp) => { let pm_token_details = token_resp.card.as_ref().map(|card| { PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) @@ -854,61 +855,29 @@ async fn skip_saving_card_in_locker( pub async fn save_in_locker( state: &SessionState, merchant_account: &domain::MerchantAccount, - payment_method_data: Option<&domain::PaymentMethodData>, payment_method_request: api::PaymentMethodCreate, - save_network_token: bool, - amount: Option, - currency: Option, ) -> RouterResult<( api_models::payment_methods::PaymentMethodResponse, Option, - Option, )> { payment_method_request.validate()?; - if save_network_token { - save_token_in_locker( - state, - merchant_account, - payment_method_data, - payment_method_request.clone(), - amount, - currency, - ) - .await - } else { - save_card_in_locker(state, merchant_account, payment_method_request.clone()).await - } -} - -pub async fn save_card_in_locker( - state: &SessionState, - merchant_account: &domain::MerchantAccount, - payment_method_request: api::PaymentMethodCreate, -) -> RouterResult<( - api_models::payment_methods::PaymentMethodResponse, - Option, - Option, -)> { let merchant_id = merchant_account.get_id(); let customer_id = payment_method_request .customer_id .clone() .get_required_value("customer_id")?; match payment_method_request.card.clone() { - Some(card) => { - let (res, dc) = 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")?; - Ok((res, dc, None)) //network_token_requestor_ref_id is None in case of card - } + 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"), None => { let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); let payment_method_response = api::PaymentMethodResponse { @@ -928,11 +897,64 @@ pub async fn save_card_in_locker( last_used_at: Some(common_utils::date_time::now()), client_secret: None, }; - Ok((payment_method_response, None, None)) + Ok((payment_method_response, None)) } } } +// pub async fn save_card_in_locker( +// state: &SessionState, +// merchant_account: &domain::MerchantAccount, +// payment_method_request: api::PaymentMethodCreate, +// ) -> RouterResult<( +// api_models::payment_methods::PaymentMethodResponse, +// Option, +// Option, +// )> { +// let merchant_id = merchant_account.get_id(); +// let customer_id = payment_method_request +// .customer_id +// .clone() +// .get_required_value("customer_id")?; +// match payment_method_request.card.clone() { +// Some(card) => { +// let (res, dc) = 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")?; +// Ok((res, dc, None)) //network_token_requestor_ref_id is None in case of card +// } +// None => { +// let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); +// let payment_method_response = api::PaymentMethodResponse { +// merchant_id: merchant_id.clone(), +// customer_id: Some(customer_id), +// payment_method_id: pm_id, +// payment_method: payment_method_request.payment_method, +// payment_method_type: payment_method_request.payment_method_type, +// #[cfg(feature = "payouts")] +// bank_transfer: None, +// card: None, +// metadata: None, +// created: Some(common_utils::date_time::now()), +// recurring_enabled: false, //[#219] +// installment_payment_enabled: false, //[#219] +// payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), //[#219] +// last_used_at: Some(common_utils::date_time::now()), +// client_secret: None, +// }; +// Ok((payment_method_response, None, None)) +// } +// } +// } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub async fn save_in_locker( _state: &SessionState, @@ -945,19 +967,16 @@ pub async fn save_in_locker( todo!() } -pub async fn save_token_in_locker( +pub async fn save_network_token_in_locker( state: &SessionState, merchant_account: &domain::MerchantAccount, - payment_method_data: Option<&domain::PaymentMethodData>, + card_data: &domain::Card, payment_method_request: api::PaymentMethodCreate, - amount: Option, - currency: Option, ) -> RouterResult<( - api_models::payment_methods::PaymentMethodResponse, + Option, Option, Option, )> { - let merchant_id = merchant_account.get_id(); let customer_id = payment_method_request .customer_id .clone() @@ -967,69 +986,55 @@ pub async fn save_token_in_locker( .network_tokenization_supported_card_networks .card_networks; - if let Some(domain::PaymentMethodData::Card(card)) = payment_method_data { - if let Some(card_network) = &card.card_network { - if network_tokenization_supported_card_networks.contains(card_network) { - if let Ok((token_response, network_token_requestor_ref_id)) = - network_tokenization::make_card_network_tokenization_request( - state, - card, - &customer_id, - amount, - currency, - ) - .await - { - // Only proceed if the tokenization was successful - let card_data = api::CardDetail { - card_number: token_response.token.clone(), - card_exp_month: token_response.token_expiry_month.clone(), - card_exp_year: token_response.token_expiry_year.clone(), - card_holder_name: None, - nick_name: None, - card_issuing_country: None, - card_network: Some(token_response.card_brand.clone()), - card_issuer: None, - card_type: None, - }; - - let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( - state, - payment_method_request, - &card_data, - &customer_id, - merchant_account, - None, - )) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Add Network Token Failed")?; + if card_data + .card_network + .as_ref() + .filter(|cn| network_tokenization_supported_card_networks.contains(cn)) + .is_some() + { + match network_tokenization::make_card_network_tokenization_request( + state, + card_data, + &customer_id, + ) + .await + { + Ok((token_response, network_token_requestor_ref_id)) => { + // Only proceed if the tokenization was successful + let card_data = api::CardDetail { + card_number: token_response.token.clone(), + card_exp_month: token_response.token_expiry_month.clone(), + card_exp_year: token_response.token_expiry_year.clone(), + card_holder_name: None, + nick_name: None, + card_issuing_country: None, + card_network: Some(token_response.card_brand.clone()), + card_issuer: None, + card_type: None, + }; - return Ok((res, dc, network_token_requestor_ref_id)); - } + let (res, dc) = Box::pin(payment_methods::cards::add_card_to_locker( + state, + payment_method_request, + &card_data, + &customer_id, + merchant_account, + None, + )) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Add Network Token Failed")?; + + Ok((Some(res), dc, network_token_requestor_ref_id)) + } + Err(err) => { + logger::error!("Failed to tokenize card: {:?}", err); + Ok((None, None, None)) //None will be returned in case of error when calling network tokenization service } } + } else { + Ok((None, None, None)) //None will be returned in case of unsupported card network. } - - let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); - let payment_method_response = api::PaymentMethodResponse { - merchant_id: merchant_id.clone(), - customer_id: Some(customer_id), - payment_method_id: pm_id, - payment_method: payment_method_request.payment_method, - payment_method_type: payment_method_request.payment_method_type, - #[cfg(feature = "payouts")] - bank_transfer: None, - card: None, - metadata: None, - created: Some(common_utils::date_time::now()), - recurring_enabled: false, //[#219] - installment_payment_enabled: false, //[#219] - payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), //[#219] - last_used_at: Some(common_utils::date_time::now()), - client_secret: None, - }; - Ok((payment_method_response, None, None)) } pub fn create_payment_method_metadata( diff --git a/crates/router/src/routes/metrics.rs b/crates/router/src/routes/metrics.rs index 970ba5bd2de5..ec3fac330772 100644 --- a/crates/router/src/routes/metrics.rs +++ b/crates/router/src/routes/metrics.rs @@ -134,3 +134,9 @@ counter_metric!(ACCESS_TOKEN_CACHE_MISS, GLOBAL_METER); // A counter to indicate the integrity check failures counter_metric!(INTEGRITY_CHECK_FAILED, GLOBAL_METER); + +// Network Tokenization metrics +histogram_metric!(GENERATE_NETWORK_TOKEN_TIME, GLOBAL_METER); +histogram_metric!(FETCH_NETWORK_TOKEN_TIME, GLOBAL_METER); +histogram_metric!(DELETE_NETWORK_TOKEN_TIME, GLOBAL_METER); +histogram_metric!(CHECK_NETWORK_TOKEN_STATUS_TIME, GLOBAL_METER); \ No newline at end of file From d64f86986af62d290f18e039d031a8a3fab62c07 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 9 Sep 2024 18:16:22 +0530 Subject: [PATCH 48/59] resolve pr comments --- crates/router/src/core/payments.rs | 408 ++++++++++++++++++----------- 1 file changed, 254 insertions(+), 154 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index b16efc8c1642..f7ed978812a1 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3698,84 +3698,45 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment( - "connector_mandate_details", - ) - }) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to deserialize connector mandate details")?; - - let mut connector_choice = None; - - for connector_data in connectors { - let merchant_connector_id = connector_data - .merchant_connector_id - .as_ref() - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to find the merchant connector id")?; - if is_network_token_with_network_transaction_id_flow( - state, - connector_data.connector_name, - is_connector_agnostic_mit_enabled, - is_network_tokenization_enabled, - payment_method_info, - ) { - if let Ok((token_exp_month, token_exp_year)) = - do_status_check_for_network_token(state, key_store, payment_method_info) - .await - { - logger::info!( - "using network_tokenization with network_transaction_id for MIT flow" - ); + .get_required_value("payment_method_info")?; - let network_transaction_id = payment_method_info - .network_transaction_id - .as_ref() - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to featch the network transaction id")?; - - let mandate_reference_id = - Some(payments_api::MandateReferenceId::NetworkTokenWithNTI( - payments_api::NetworkTokenWithNTIRef { - network_transaction_id: network_transaction_id.to_string(), - token_exp_month, - token_exp_year, - }, - )); + //fetch connectors that support ntid flow + let ntid_supported_connectors = &state + .conf + .network_transaction_id_supported_connectors + .connector_list; + //filered connectors list with ntid_supported_connectors + let filtered_ntid_supported_connectors = + filter_ntid_supported_connectors(connectors.clone(), ntid_supported_connectors); + + //fetch connectors that support network tokenization flow + let network_tokenization_supported_connectors = &state + .conf + .network_tokenization_supported_connectors + .connector_list; + //filered connectors list with ntid_supported_connectors and network_tokenization_supported_connectors + let filtered_nt_supported_connectors = filter_network_tokenization_supported_connectors( + filtered_ntid_supported_connectors, + network_tokenization_supported_connectors, + ); - connector_choice = Some((connector_data, mandate_reference_id.clone())); - break; - } else { - logger::info!("Proceeding with MIT flow using the provided network_transaction_id, as network tokenization details are unavailable or failed to validate."); - let network_transaction_id = payment_method_info - .network_transaction_id - .as_ref() - .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to featch the network transaction id")?; + let action_type = decide_action_type( + state, + key_store, + is_connector_agnostic_mit_enabled, + is_network_tokenization_enabled, + &payment_method_info, + &filtered_nt_supported_connectors, + ) + .await; - let mandate_reference_id = - Some(payments_api::MandateReferenceId::NetworkMandateId( - network_transaction_id.to_string(), - )); + match action_type { + Some(ActionType::NetworkTokenWithNetworkTransactionId(token_exp_data)) => { + logger::info!( + "using network_tokenization with network_transaction_id for MIT flow" + ); - connector_choice = Some((connector_data, mandate_reference_id.clone())); - break; - } - } else if is_network_transaction_id_flow( - state, - is_connector_agnostic_mit_enabled, - connector_data.connector_name, - payment_method_info, - ) { - logger::info!("using network_transaction_id for MIT flow"); let network_transaction_id = payment_method_info .network_transaction_id .as_ref() @@ -3783,24 +3744,156 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment { + decide_connector_for_normal_or_recurring_payment( + state, + payment_data, + routing_data, + connectors, + is_connector_agnostic_mit_enabled, + &payment_method_info, + ) + .await + } + } + } + ( + None, + None, + Some(RecurringDetails::ProcessorPaymentToken(_token)), + Some(true), + Some(api::MandateTransactionType::RecurringMandateTransaction), + ) => { + if let Some(connector) = connectors.first() { + routing_data.routed_through = Some(connector.connector_name.clone().to_string()); + routing_data + .merchant_connector_id + .clone_from(&connector.merchant_connector_id); + Ok(ConnectorCallType::PreDetermined(api::ConnectorData { + connector: connector.connector.clone(), + connector_name: connector.connector_name, + get_token: connector.get_token.clone(), + merchant_connector_id: connector.merchant_connector_id.clone(), + })) + } else { + logger::error!("no eligible connector found for the ppt_mandate payment"); + Err(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration.into()) + } + } + + _ => { + helpers::override_setup_future_usage_to_on_session(&*state.store, payment_data).await?; + + let first_choice = connectors + .first() + .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration) + .attach_printable("no eligible connector found for payment")? + .clone(); + + routing_data.routed_through = Some(first_choice.connector_name.to_string()); + + routing_data.merchant_connector_id = first_choice.merchant_connector_id; + + Ok(ConnectorCallType::Retryable(connectors)) + } + } +} + +#[allow(clippy::too_many_arguments)] +pub async fn decide_connector_for_normal_or_recurring_payment( + state: &SessionState, + payment_data: &mut PaymentData, + routing_data: &mut storage::RoutingData, + connectors: Vec, + is_connector_agnostic_mit_enabled: Option, + payment_method_info: &storage::PaymentMethod, +) -> RouterResult { + let connector_mandate_details = &payment_method_info + .connector_mandate_details + .clone() + .map(|details| { + details.parse_value::("connector_mandate_details") + }) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to deserialize connector mandate details")?; + + let mut connector_choice = None; + + for connector_data in connectors { + let merchant_connector_id = connector_data + .merchant_connector_id + .as_ref() + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to find the merchant connector id")?; + if is_network_transaction_id_flow( + state, + is_connector_agnostic_mit_enabled, + connector_data.connector_name, + payment_method_info, + ) { + logger::info!("using network_transaction_id for MIT flow"); + let network_transaction_id = payment_method_info + .network_transaction_id + .as_ref() + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to featch the network transaction id")?; + + let mandate_reference_id = Some(payments_api::MandateReferenceId::NetworkMandateId( + network_transaction_id.to_string(), + )); + + connector_choice = Some((connector_data, mandate_reference_id.clone())); + break; + } else if connector_mandate_details + .clone() + .map(|connector_mandate_details| { + connector_mandate_details.contains_key(merchant_connector_id) + }) + .unwrap_or(false) + { + logger::info!("using connector_mandate_id for MIT flow"); + if let Some(merchant_connector_id) = connector_data.merchant_connector_id.as_ref() { + if let Some(mandate_reference_record) = connector_mandate_details.clone() .get_required_value("connector_mandate_details") .change_context(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration) .attach_printable("no eligible connector found for token-based MIT flow since there were no connector mandate details")? @@ -3842,69 +3935,90 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment { - if let Some(connector) = connectors.first() { - routing_data.routed_through = Some(connector.connector_name.clone().to_string()); - routing_data - .merchant_connector_id - .clone_from(&connector.merchant_connector_id); - Ok(ConnectorCallType::PreDetermined(api::ConnectorData { - connector: connector.connector.clone(), - connector_name: connector.connector_name, - get_token: connector.get_token.clone(), - merchant_connector_id: connector.merchant_connector_id.clone(), - })) - } else { - logger::error!("no eligible connector found for the ppt_mandate payment"); - Err(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration.into()) - } - } + Ok(ConnectorCallType::PreDetermined(chosen_connector_data)) +} - _ => { - helpers::override_setup_future_usage_to_on_session(&*state.store, payment_data).await?; +pub fn filter_ntid_supported_connectors( + connectors: Vec, + ntid_supported_connectors: &HashSet, +) -> Vec { + connectors + .into_iter() + .filter(|data| ntid_supported_connectors.contains(&data.connector_name)) + .collect() +} - let first_choice = connectors - .first() - .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration) - .attach_printable("no eligible connector found for payment")? - .clone(); +pub fn filter_network_tokenization_supported_connectors( + connectors: Vec, + network_tokenization_supported_connectors: &HashSet, +) -> Vec { + connectors + .into_iter() + .filter(|data| network_tokenization_supported_connectors.contains(&data.connector_name)) + .collect() +} - routing_data.routed_through = Some(first_choice.connector_name.to_string()); +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] +pub struct TokenExpiry { + pub token_exp_month: Option>, + pub token_exp_year: Option>, +} - routing_data.merchant_connector_id = first_choice.merchant_connector_id; +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] +pub enum ActionType { + NetworkTokenWithNetworkTransactionId(TokenExpiry), +} - Ok(ConnectorCallType::Retryable(connectors)) +pub async fn decide_action_type( + state: &SessionState, + key_store: &domain::MerchantKeyStore, + is_connector_agnostic_mit_enabled: Option, + is_network_tokenization_enabled: bool, + payment_method_info: &storage::PaymentMethod, + filtered_nt_supported_connectors: &Vec, //network tokenization supported connectors +) -> Option { + if is_network_token_with_network_transaction_id_flow( + is_connector_agnostic_mit_enabled, + is_network_tokenization_enabled, + payment_method_info, + ) && !filtered_nt_supported_connectors.is_empty() + { + if let Ok((token_exp_month, token_exp_year)) = + do_status_check_for_network_token(state, key_store, payment_method_info).await + { + Some(ActionType::NetworkTokenWithNetworkTransactionId ( + TokenExpiry { + token_exp_month, + token_exp_year, + } + )) + } else { + None } + } else { + None } } @@ -3926,22 +4040,10 @@ pub fn is_network_transaction_id_flow( } pub fn is_network_token_with_network_transaction_id_flow( - state: &SessionState, - connector: enums::Connector, is_connector_agnostic_mit_enabled: Option, is_network_tokenization_enabled: bool, payment_method_info: &storage::PaymentMethod, ) -> bool { - let ntid_supported_connectors = &state - .conf - .network_transaction_id_supported_connectors - .connector_list; - - let network_tokenization_supported_connectors = &state - .conf - .network_tokenization_supported_connectors - .connector_list; - is_connector_agnostic_mit_enabled == Some(true) && is_network_tokenization_enabled && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) @@ -3950,8 +4052,6 @@ pub fn is_network_token_with_network_transaction_id_flow( && payment_method_info .network_token_requestor_reference_id .is_some() - && ntid_supported_connectors.contains(&connector) - && network_tokenization_supported_connectors.contains(&connector) } pub fn should_add_task_to_process_tracker(payment_data: &PaymentData) -> bool { From f4e58b1534c120278fcbed89c8fb2a2e12c6eabb Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:55:01 +0000 Subject: [PATCH 49/59] chore: run formatter --- crates/diesel_models/src/payment_method.rs | 2 +- .../src/payment_methods.rs | 10 ++- .../src/configs/secrets_transformers.rs | 21 ++--- crates/router/src/configs/settings.rs | 7 +- crates/router/src/configs/validations.rs | 3 +- crates/router/src/connector/utils.rs | 4 +- crates/router/src/core/customers.rs | 11 +-- crates/router/src/core/errors.rs | 3 +- crates/router/src/core/payments.rs | 4 +- crates/router/src/core/payments/helpers.rs | 80 +++++++++---------- crates/router/src/routes/metrics.rs | 2 +- 11 files changed, 75 insertions(+), 72 deletions(-) diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 2dd350c32b53..0456c913e95b 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -574,7 +574,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_issuer: None, payment_method_type: None, last_modified: common_utils::date_time::now(), - network_token_locker_id: None, + network_token_locker_id: None, network_token_payment_method_data: None, }, } diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index c849561519d6..d0550c5a161b 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -54,7 +54,6 @@ pub struct PaymentMethod { pub network_token_requestor_reference_id: Option, pub network_token_locker_id: Option, pub network_token_payment_method_data: OptionalEncryptableValue, - } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -142,8 +141,9 @@ impl super::behaviour::Conversion for PaymentMethod { version: self.version, network_token_requestor_reference_id: self.network_token_requestor_reference_id, network_token_locker_id: self.network_token_locker_id, - network_token_payment_method_data: self.network_token_payment_method_data.map(|val| val.into()), - + network_token_payment_method_data: self + .network_token_payment_method_data + .map(|val| val.into()), }) } @@ -276,7 +276,9 @@ impl super::behaviour::Conversion for PaymentMethod { version: self.version, network_token_requestor_reference_id: self.network_token_requestor_reference_id, network_token_locker_id: self.network_token_locker_id, - network_token_payment_method_data: self.network_token_payment_method_data.map(|val| val.into()), + network_token_payment_method_data: self + .network_token_payment_method_data + .map(|val| val.into()), }) } } diff --git a/crates/router/src/configs/secrets_transformers.rs b/crates/router/src/configs/secrets_transformers.rs index 11eb079ec31d..e1b13941a66d 100644 --- a/crates/router/src/configs/secrets_transformers.rs +++ b/crates/router/src/configs/secrets_transformers.rs @@ -407,16 +407,17 @@ pub(crate) async fn fetch_raw_secrets( .expect("Failed to decrypt user_auth_methods configs"); #[allow(clippy::expect_used)] - let network_tokenization_service = - conf.network_tokenization_service - .async_map(|network_tokenization_service| async { - settings::NetworkTokenizationService::convert_to_raw_secret( - network_tokenization_service, - secret_management_client, - ) - .await - .expect("Failed to decrypt network tokenization service configs") - }).await; + let network_tokenization_service = conf + .network_tokenization_service + .async_map(|network_tokenization_service| async { + settings::NetworkTokenizationService::convert_to_raw_secret( + network_tokenization_service, + secret_management_client, + ) + .await + .expect("Failed to decrypt network tokenization service configs") + }) + .await; Settings { server: conf.server, diff --git a/crates/router/src/configs/settings.rs b/crates/router/src/configs/settings.rs index 7dbe1faf7501..3d5aa3d921b9 100644 --- a/crates/router/src/configs/settings.rs +++ b/crates/router/src/configs/settings.rs @@ -848,10 +848,11 @@ impl Settings { .map_err(|err| ApplicationError::InvalidConfigurationValueError(err.into()))?; self.generic_link.payment_method_collect.validate()?; self.generic_link.payout_link.validate()?; - + self.network_tokenization_service - .as_ref() - .map(|x| x.get_inner().validate()).transpose()?; + .as_ref() + .map(|x| x.get_inner().validate()) + .transpose()?; Ok(()) } diff --git a/crates/router/src/configs/validations.rs b/crates/router/src/configs/validations.rs index b80f5a38089d..67db7b1266cd 100644 --- a/crates/router/src/configs/validations.rs +++ b/crates/router/src/configs/validations.rs @@ -193,7 +193,6 @@ impl super::settings::GenericLinkEnvConfig { } } - impl super::settings::NetworkTokenizationService { pub fn validate(&self) -> Result<(), ApplicationError> { use common_utils::fp_utils::when; @@ -222,4 +221,4 @@ impl super::settings::NetworkTokenizationService { )) }) } -} \ No newline at end of file +} diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index bb4326691c03..530a3e294152 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -856,7 +856,9 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData { Some(payments::MandateReferenceId::NetworkMandateId(network_transaction_id)) => { Some(network_transaction_id.clone()) } - Some(payments::MandateReferenceId::ConnectorMandateId(_)) | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) | None => None, + Some(payments::MandateReferenceId::ConnectorMandateId(_)) + | Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) + | None => None, }) } diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index 0bca7d7972b8..e4eb60982d4b 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -789,9 +789,10 @@ impl CustomerDeleteBridge for customers::CustomerId { ) .await .switch()?; - - if let Some(network_token_ref_id) = pm.network_token_requestor_reference_id { - network_tokenization::delete_network_token_from_locker_and_token_service( + + if let Some(network_token_ref_id) = pm.network_token_requestor_reference_id + { + network_tokenization::delete_network_token_from_locker_and_token_service( state, &self.customer_id, merchant_account.get_id(), @@ -801,10 +802,10 @@ impl CustomerDeleteBridge for customers::CustomerId { ) .await .switch()?; - } + } } - db.delete_payment_method_by_merchant_id_payment_method_id( + db.delete_payment_method_by_merchant_id_payment_method_id( key_manager_state, key_store, merchant_account.get_id(), diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index 808f093feea6..9bf6eb20621b 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -343,6 +343,5 @@ pub enum NetworkTokenizationError { #[error("Failed to delete network token")] DeleteNetworkTokenFailed, #[error("Network token service not configured")] - NetworkTokenizationServiceNotConfigured - + NetworkTokenizationServiceNotConfigured, } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 7bde4a7a21c4..4e42e556ce7c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -4018,11 +4018,11 @@ pub async fn decide_action_type( if let Ok((token_exp_month, token_exp_year)) = do_status_check_for_network_token(state, payment_method_info).await { - Some(ActionType::NetworkTokenWithNetworkTransactionId ( + Some(ActionType::NetworkTokenWithNetworkTransactionId( TokenExpiry { token_exp_month, token_exp_year, - } + }, )) } else { None diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 1d4563bd3223..a5ff1f2dc374 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1909,9 +1909,7 @@ pub async fn retrieve_card_with_permanent_token( // Regular (non-mandate) Payment flow if let Some(token_ref) = pm_data.network_token_requestor_reference_id.clone() { match network_tokenization::get_token_from_tokenization_service( - state, - token_ref, - pm_data, + state, token_ref, pm_data, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1948,20 +1946,20 @@ pub async fn retrieve_card_with_permanent_token( ) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "failed to fetch card information from the permanent locker", - ) + .attach_printable("failed to fetch card information from the permanent locker") } - }, + } (Some(ref pm_data), Some(mandate_ids)) => { // Mandate Payment flow match mandate_ids.mandate_reference_id { - Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI(nt_data)) => { + Some(api_models::payments::MandateReferenceId::NetworkTokenWithNTI( + nt_data, + )) => { { if let Some(network_token_locker_id) = - pm_data.network_token_locker_id.as_ref() - { - let mut token_data = cards::get_card_from_locker( + pm_data.network_token_locker_id.as_ref() + { + let mut token_data = cards::get_card_from_locker( state, customer_id, &payment_intent.merchant_id, @@ -1972,32 +1970,31 @@ pub async fn retrieve_card_with_permanent_token( .attach_printable( "failed to fetch network token information from the permanent locker", )?; - let expiry = - nt_data.token_exp_month.zip(nt_data.token_exp_year); - if let Some((exp_month, exp_year)) = expiry { - token_data.card_exp_month = exp_month; - token_data.card_exp_year = exp_year; - } - let network_token_data = domain::NetworkTokenData { - token_number: token_data.card_number, - token_cryptogram: None, - token_exp_month: token_data.card_exp_month, - token_exp_year: token_data.card_exp_year, - nick_name: token_data.nick_name.map(masking::Secret::new), - card_issuer: None, - card_network: None, - card_type: None, - card_issuing_country: None, - bank_code: None, - }; - Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) - } else { - // Mandate but network token locker id is not present - Err(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Network token locker id is not present") + let expiry = nt_data.token_exp_month.zip(nt_data.token_exp_year); + if let Some((exp_month, exp_year)) = expiry { + token_data.card_exp_month = exp_month; + token_data.card_exp_year = exp_year; } + let network_token_data = domain::NetworkTokenData { + token_number: token_data.card_number, + token_cryptogram: None, + token_exp_month: token_data.card_exp_month, + token_exp_year: token_data.card_exp_year, + nick_name: token_data.nick_name.map(masking::Secret::new), + card_issuer: None, + card_network: None, + card_type: None, + card_issuing_country: None, + bank_code: None, + }; + Ok(domain::PaymentMethodData::NetworkToken(network_token_data)) + } else { + // Mandate but network token locker id is not present + Err(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Network token locker id is not present") + } } - }, + } Some(api_models::payments::MandateReferenceId::NetworkMandateId(_)) => { fetch_card_details_from_locker( @@ -2009,13 +2006,14 @@ pub async fn retrieve_card_with_permanent_token( ) .await .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("failed to fetch card information from the permanent locker") - }, - - Some(api_models::payments::MandateReferenceId::ConnectorMandateId(_)) | None => { - Err(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Payment method data is not present") + .attach_printable( + "failed to fetch card information from the permanent locker", + ) } + + Some(api_models::payments::MandateReferenceId::ConnectorMandateId(_)) + | None => Err(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Payment method data is not present"), } } } diff --git a/crates/router/src/routes/metrics.rs b/crates/router/src/routes/metrics.rs index ec3fac330772..f3f1cd7cda0f 100644 --- a/crates/router/src/routes/metrics.rs +++ b/crates/router/src/routes/metrics.rs @@ -139,4 +139,4 @@ counter_metric!(INTEGRITY_CHECK_FAILED, GLOBAL_METER); histogram_metric!(GENERATE_NETWORK_TOKEN_TIME, GLOBAL_METER); histogram_metric!(FETCH_NETWORK_TOKEN_TIME, GLOBAL_METER); histogram_metric!(DELETE_NETWORK_TOKEN_TIME, GLOBAL_METER); -histogram_metric!(CHECK_NETWORK_TOKEN_STATUS_TIME, GLOBAL_METER); \ No newline at end of file +histogram_metric!(CHECK_NETWORK_TOKEN_STATUS_TIME, GLOBAL_METER); From 461e878fc9a6dce746086b67dd0e27ccbc22006d Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 21:57:53 +0000 Subject: [PATCH 50/59] docs(openapi): re-generate OpenAPI specification --- api-reference/openapi_spec.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index f39fb0c68189..7ce3a0a8421b 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -7339,6 +7339,10 @@ "is_tax_connector_enabled": { "type": "boolean", "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." } }, "additionalProperties": false @@ -7351,7 +7355,8 @@ "profile_name", "enable_payment_response_hash", "redirect_to_merchant_with_http_post", - "is_tax_connector_enabled" + "is_tax_connector_enabled", + "is_network_tokenization_enabled" ], "properties": { "merchant_id": { @@ -7530,6 +7535,12 @@ "is_tax_connector_enabled": { "type": "boolean", "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", + "default": false, + "example": false } } }, @@ -11654,9 +11665,6 @@ } ], "nullable": true - }, - "is_network_tokenization_enabled": { - "type": "boolean" } }, "additionalProperties": false @@ -11752,8 +11760,7 @@ "primary_business_details", "organization_id", "is_recon_enabled", - "recon_status", - "is_network_tokenization_enabled" + "recon_status" ], "properties": { "merchant_id": { @@ -11891,9 +11898,6 @@ } ], "nullable": true - }, - "is_network_tokenization_enabled": { - "type": "boolean" } } }, @@ -12022,10 +12026,6 @@ } ], "nullable": true - }, - "is_network_tokenization_enabled": { - "type": "boolean", - "nullable": true } }, "additionalProperties": false From a4c8f1f316adb1359dbd3b4f8d91e04f8f8416d3 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 11 Sep 2024 19:10:48 +0530 Subject: [PATCH 51/59] resolve pr comments --- config/config.example.toml | 16 ++- .../payment_methods/network_tokenization.rs | 23 +-- crates/router/src/core/payments.rs | 133 +++++++++++------- .../router/src/core/payments/tokenization.rs | 55 +------- .../down.sql | 2 - .../up.sql | 2 - .../down.sql | 3 - .../up.sql | 2 - .../down.sql | 2 - .../up.sql | 2 - .../down.sql | 6 + .../up.sql | 6 + 12 files changed, 119 insertions(+), 133 deletions(-) delete mode 100644 migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/down.sql delete mode 100644 migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/up.sql delete mode 100644 migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/down.sql delete mode 100644 migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/up.sql delete mode 100644 migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/down.sql delete mode 100644 migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/up.sql create mode 100644 migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql create mode 100644 migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql diff --git a/config/config.example.toml b/config/config.example.toml index 4ad2733d4131..fe4c87abbd41 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -726,6 +726,18 @@ connector_list = "" [recipient_emails] recon = "test@example.com" - [network_tokenization_supported_card_networks] -card_networks = "Visa" # Supported card networks for network tokenization \ No newline at end of file +card_networks = "Visa" # Supported card networks for network tokenization + +[network_tokenization_service] # Network Tokenization Service Configuration +generate_token_url= "" # base url to generate token +fetch_token_url= "" # base url to fetch token +token_service_api_key= "" # api key for token service +public_key= "" # public key to encrypt data for token service +private_key= "" # private key to decrypt response payload from token service +key_id= "" # key id to encrypt data for token service +delete_token_url= "" # base url to delete token from token service +check_token_status_url= "" # base url to check token status from token service + +[network_tokenization_supported_connectors] +connector_list = "cybersource" # Supported connectors for network tokenization \ No newline at end of file diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index a4dc77870a29..096f5cd4fb8d 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -44,7 +44,7 @@ pub struct OrderData { #[serde(rename_all = "camelCase")] pub struct ApiPayload { service: String, - card_data: String, + card_data: Secret, //encrypted card data order_data: OrderData, key_id: String, should_send_token: bool, @@ -81,7 +81,7 @@ pub struct GetCardToken { #[derive(Debug, Deserialize)] pub struct AuthenticationDetails { cryptogram: Secret, - token: CardNumber, + token: CardNumber, //network token } #[derive(Debug, Serialize, Deserialize)] @@ -99,7 +99,7 @@ pub struct TokenResponse { #[derive(Debug, Serialize, Deserialize)] pub struct DeleteCardToken { - card_reference: String, + card_reference: String, //network token requestor ref id customer_id: id_type::CustomerId, } @@ -109,13 +109,6 @@ pub enum DeleteNetworkTokenStatus { Success, } -#[derive(Debug, Deserialize, Eq, PartialEq)] -#[serde(untagged)] -pub enum DeleteNTResponse { - DeleteNetworkTokenResponse(DeleteNetworkTokenResponse), - DeleteNetworkTokenErrorResponse(NetworkTokenErrorResponse), -} - #[derive(Debug, Deserialize, Eq, PartialEq)] pub struct NetworkTokenErrorInfo { code: String, @@ -189,7 +182,7 @@ pub async fn mk_tokenization_req( let api_payload = ApiPayload { service: NETWORK_TOKEN_SERVICE.to_string(), - card_data: jwt, + card_data: Secret::new(jwt), order_data, key_id, should_send_token: true, @@ -692,18 +685,14 @@ pub async fn delete_network_token_from_tokenization_service( logger::error!("Error while deserializing response: {:?}", err); })?; - let delete_token_response: DeleteNTResponse = res + let delete_token_response: DeleteNetworkTokenResponse = res .response .parse_struct("Delete Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; logger::info!("Delete Network Token Response: {:?}", delete_token_response); - if delete_token_response - == DeleteNTResponse::DeleteNetworkTokenResponse(DeleteNetworkTokenResponse { - status: DeleteNetworkTokenStatus::Success, - }) - { + if delete_token_response.status == DeleteNetworkTokenStatus::Success { Ok(true) } else { Err(errors::NetworkTokenizationError::DeleteNetworkTokenFailed) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 7bde4a7a21c4..0fac0d2a5cca 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -74,7 +74,7 @@ use crate::{ connector::utils::missing_field_err, core::{ errors::{self, CustomResult, RouterResponse, RouterResult}, - payment_methods::{cards, network_tokenization::do_status_check_for_network_token}, + payment_methods::{cards, network_tokenization}, utils, }, db::StorageInterface, @@ -3752,34 +3752,23 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment { decide_connector_for_normal_or_recurring_payment( @@ -3992,14 +3983,22 @@ pub fn filter_network_tokenization_supported_connectors( } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] -pub struct TokenExpiry { - pub token_exp_month: Option>, +pub struct NetworkTokenExpiry { + pub token_exp_month: Option>, pub token_exp_year: Option>, } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] +pub struct NTWithNTIRef { + pub network_token_exp: NetworkTokenExpiry, + pub network_transaction_id: String, + pub network_token_locker_id: String, + pub network_token_requestor_ref_id: String, +} + #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] pub enum ActionType { - NetworkTokenWithNetworkTransactionId(TokenExpiry), + NetworkTokenWithNetworkTransactionId(NTWithNTIRef), } pub async fn decide_action_type( @@ -4009,26 +4008,43 @@ pub async fn decide_action_type( payment_method_info: &domain::PaymentMethod, filtered_nt_supported_connectors: Vec, //network tokenization supported connectors ) -> Option { - if is_network_token_with_network_transaction_id_flow( - is_connector_agnostic_mit_enabled, - is_network_tokenization_enabled, - payment_method_info, - ) && !filtered_nt_supported_connectors.is_empty() - { - if let Ok((token_exp_month, token_exp_year)) = - do_status_check_for_network_token(state, payment_method_info).await - { - Some(ActionType::NetworkTokenWithNetworkTransactionId ( - TokenExpiry { - token_exp_month, - token_exp_year, - } - )) - } else { - None + match ( + is_network_token_with_network_transaction_id_flow( + is_connector_agnostic_mit_enabled, + is_network_tokenization_enabled, + payment_method_info, + ), + !filtered_nt_supported_connectors.is_empty(), + ) { + ( + IsNtWithNtiFlow::NtWithNtiSupported( + network_transaction_id, + network_token_locker_id, + network_token_requestor_ref_id, + ), + true, + ) => { + if let Ok((token_exp_month, token_exp_year)) = + network_tokenization::do_status_check_for_network_token(state, payment_method_info) + .await + { + Some(ActionType::NetworkTokenWithNetworkTransactionId( + NTWithNTIRef{ + network_token_exp: NetworkTokenExpiry { + token_exp_month, + token_exp_year, + }, + network_transaction_id, + network_token_locker_id, + network_token_requestor_ref_id, + + } + )) + } else { + None + } } - } else { - None + (IsNtWithNtiFlow::NtWithNtiSupported(_, _, _), false) | (IsNtWithNtiFlow::NTWithNTINotSupported, _) => None, } } @@ -4049,19 +4065,42 @@ pub fn is_network_transaction_id_flow( && payment_method_info.network_transaction_id.is_some() } +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] +pub enum IsNtWithNtiFlow { + NtWithNtiSupported(String, String, String), //Network token with Network transaction id supported flow + NTWithNTINotSupported, //Network token with Network transaction id not supported +} + pub fn is_network_token_with_network_transaction_id_flow( is_connector_agnostic_mit_enabled: Option, is_network_tokenization_enabled: bool, payment_method_info: &domain::PaymentMethod, -) -> bool { - is_connector_agnostic_mit_enabled == Some(true) - && is_network_tokenization_enabled - && payment_method_info.payment_method == Some(storage_enums::PaymentMethod::Card) - && payment_method_info.network_transaction_id.is_some() - && payment_method_info.network_token_locker_id.is_some() - && payment_method_info +) -> IsNtWithNtiFlow { + match ( + is_connector_agnostic_mit_enabled, + is_network_tokenization_enabled, + payment_method_info.payment_method, + payment_method_info.network_transaction_id.clone(), + payment_method_info.network_token_locker_id.clone(), + payment_method_info .network_token_requestor_reference_id - .is_some() + .clone(), + ) { + ( + Some(true), + true, + Some(storage_enums::PaymentMethod::Card), + Some(network_transaction_id), + Some(network_token_locker_id), + Some(network_token_requestor_ref_id), + ) => IsNtWithNtiFlow::NtWithNtiSupported( + network_transaction_id, + network_token_locker_id, + network_token_requestor_ref_id, + ), + _ => IsNtWithNtiFlow::NTWithNTINotSupported, + } + } pub fn should_add_task_to_process_tracker(payment_data: &PaymentData) -> bool { diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index ce6ee8d4a082..812736b7f004 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -228,7 +228,7 @@ where domain::PaymentMethodData::Card(card) => { let ( network_token_resp, - _network_token_duplication_check, + _network_token_duplication_check, //the duplication check is discarded, since each card has only one token, handling card duplication check will be suffice network_token_requestor_ref_id, ) = Box::pin(save_network_token_in_locker( state, @@ -919,59 +919,6 @@ pub async fn save_in_locker( } } -// pub async fn save_card_in_locker( -// state: &SessionState, -// merchant_account: &domain::MerchantAccount, -// payment_method_request: api::PaymentMethodCreate, -// ) -> RouterResult<( -// api_models::payment_methods::PaymentMethodResponse, -// Option, -// Option, -// )> { -// let merchant_id = merchant_account.get_id(); -// let customer_id = payment_method_request -// .customer_id -// .clone() -// .get_required_value("customer_id")?; -// match payment_method_request.card.clone() { -// Some(card) => { -// let (res, dc) = 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")?; -// Ok((res, dc, None)) //network_token_requestor_ref_id is None in case of card -// } -// None => { -// let pm_id = common_utils::generate_id(consts::ID_LENGTH, "pm"); -// let payment_method_response = api::PaymentMethodResponse { -// merchant_id: merchant_id.clone(), -// customer_id: Some(customer_id), -// payment_method_id: pm_id, -// payment_method: payment_method_request.payment_method, -// payment_method_type: payment_method_request.payment_method_type, -// #[cfg(feature = "payouts")] -// bank_transfer: None, -// card: None, -// metadata: None, -// created: Some(common_utils::date_time::now()), -// recurring_enabled: false, //[#219] -// installment_payment_enabled: false, //[#219] -// payment_experience: Some(vec![api_models::enums::PaymentExperience::RedirectToUrl]), //[#219] -// last_used_at: Some(common_utils::date_time::now()), -// client_secret: None, -// }; -// Ok((payment_method_response, None, None)) -// } -// } -// } - #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub async fn save_in_locker( _state: &SessionState, diff --git a/migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/down.sql b/migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/down.sql deleted file mode 100644 index 275c96eebc59..000000000000 --- a/migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_requestor_reference_id; \ No newline at end of file diff --git a/migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/up.sql b/migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/up.sql deleted file mode 100644 index be223602999d..000000000000 --- a/migrations/2024-08-22-091927_add_network_token_reference_in_payment_method/up.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_requestor_reference_id VARCHAR(128) DEFAULT NULL; \ No newline at end of file diff --git a/migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/down.sql b/migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/down.sql deleted file mode 100644 index aeff6e948852..000000000000 --- a/migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` - -ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_locker_id; \ No newline at end of file diff --git a/migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/up.sql b/migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/up.sql deleted file mode 100644 index 0e49907899e4..000000000000 --- a/migrations/2024-08-27-212955_add_token_locker_id_in_payment_method/up.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_locker_id VARCHAR(64) DEFAULT NULL; \ No newline at end of file diff --git a/migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/down.sql b/migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/down.sql deleted file mode 100644 index 40361115f1e5..000000000000 --- a/migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_payment_method_data; \ No newline at end of file diff --git a/migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/up.sql b/migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/up.sql deleted file mode 100644 index 2df1f33ec3d0..000000000000 --- a/migrations/2024-08-28-195707_add_token_payment_method_data_in_payment_methods/up.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Your SQL goes here -ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_payment_method_data BYTEA DEFAULT NULL; \ No newline at end of file diff --git a/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql b/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql new file mode 100644 index 000000000000..f550984cf047 --- /dev/null +++ b/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql @@ -0,0 +1,6 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_requestor_reference_id; + +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_locker_id; + +ALTER TABLE payment_methods DROP COLUMN IF EXISTS network_token_payment_method_data; \ No newline at end of file diff --git a/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql b/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql new file mode 100644 index 000000000000..598afc9247a6 --- /dev/null +++ b/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql @@ -0,0 +1,6 @@ +-- Your SQL goes here +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_requestor_reference_id VARCHAR(128) DEFAULT NULL; + +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_locker_id VARCHAR(64) DEFAULT NULL; + +ALTER TABLE payment_methods ADD COLUMN IF NOT EXISTS network_token_payment_method_data BYTEA DEFAULT NULL; \ No newline at end of file From 60fb2d63beace91f44158f6157f9c7e1a00b55c4 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 11 Sep 2024 23:56:20 +0530 Subject: [PATCH 52/59] code refactoring --- crates/router/src/core/payments.rs | 66 +++++++++++------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index e5f22b302aff..77d80d6506e0 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3873,8 +3873,8 @@ where Some(payments_api::MandateReferenceId::NetworkTokenWithNTI( payments_api::NetworkTokenWithNTIRef { network_transaction_id: nt_data.network_transaction_id.to_string(), - token_exp_month: nt_data.network_token_exp.token_exp_month, - token_exp_year: nt_data.network_token_exp.token_exp_year, + token_exp_month: nt_data.token_exp_month, + token_exp_year: nt_data.token_exp_year, }, )); let chosen_connector_data = filtered_nt_supported_connectors @@ -4090,16 +4090,6 @@ pub fn filter_ntid_supported_connectors( .collect() } -pub fn filter_network_tokenization_supported_connectors( - connectors: Vec, - network_tokenization_supported_connectors: &HashSet, -) -> Vec { - connectors - .into_iter() - .filter(|data| network_tokenization_supported_connectors.contains(&data.connector_name)) - .collect() -} - #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] pub struct NetworkTokenExpiry { pub token_exp_month: Option>, @@ -4108,10 +4098,9 @@ pub struct NetworkTokenExpiry { #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] pub struct NTWithNTIRef { - pub network_token_exp: NetworkTokenExpiry, pub network_transaction_id: String, - pub network_token_locker_id: String, - pub network_token_requestor_ref_id: String, + pub token_exp_month: Option>, + pub token_exp_year: Option>, } #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] @@ -4119,6 +4108,16 @@ pub enum ActionType { NetworkTokenWithNetworkTransactionId(NTWithNTIRef), } +pub fn filter_network_tokenization_supported_connectors( + connectors: Vec, + network_tokenization_supported_connectors: &HashSet, +) -> Vec { + connectors + .into_iter() + .filter(|data| network_tokenization_supported_connectors.contains(&data.connector_name)) + .collect() +} + pub async fn decide_action_type( state: &SessionState, is_connector_agnostic_mit_enabled: Option, @@ -4134,34 +4133,23 @@ pub async fn decide_action_type( ), !filtered_nt_supported_connectors.is_empty(), ) { - ( - IsNtWithNtiFlow::NtWithNtiSupported( - network_transaction_id, - network_token_locker_id, - network_token_requestor_ref_id, - ), - true, - ) => { + (IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id), true) => { if let Ok((token_exp_month, token_exp_year)) = network_tokenization::do_status_check_for_network_token(state, payment_method_info) .await { Some(ActionType::NetworkTokenWithNetworkTransactionId( NTWithNTIRef { - network_token_exp: NetworkTokenExpiry { - token_exp_month, - token_exp_year, - }, + token_exp_month, + token_exp_year, network_transaction_id, - network_token_locker_id, - network_token_requestor_ref_id, }, )) } else { None } } - (IsNtWithNtiFlow::NtWithNtiSupported(_, _, _), false) + (IsNtWithNtiFlow::NtWithNtiSupported(_), false) | (IsNtWithNtiFlow::NTWithNTINotSupported, _) => None, } } @@ -4185,8 +4173,8 @@ pub fn is_network_transaction_id_flow( #[derive(Debug, serde::Deserialize, serde::Serialize, Clone, Eq, PartialEq)] pub enum IsNtWithNtiFlow { - NtWithNtiSupported(String, String, String), //Network token with Network transaction id supported flow - NTWithNTINotSupported, //Network token with Network transaction id not supported + NtWithNtiSupported(String), //Network token with Network transaction id supported flow + NTWithNTINotSupported, //Network token with Network transaction id not supported } pub fn is_network_token_with_network_transaction_id_flow( @@ -4199,23 +4187,19 @@ pub fn is_network_token_with_network_transaction_id_flow( is_network_tokenization_enabled, payment_method_info.payment_method, payment_method_info.network_transaction_id.clone(), - payment_method_info.network_token_locker_id.clone(), + payment_method_info.network_token_locker_id.is_some(), payment_method_info .network_token_requestor_reference_id - .clone(), + .is_some(), ) { ( Some(true), true, Some(storage_enums::PaymentMethod::Card), Some(network_transaction_id), - Some(network_token_locker_id), - Some(network_token_requestor_ref_id), - ) => IsNtWithNtiFlow::NtWithNtiSupported( - network_transaction_id, - network_token_locker_id, - network_token_requestor_ref_id, - ), + true, + true, + ) => IsNtWithNtiFlow::NtWithNtiSupported(network_transaction_id), _ => IsNtWithNtiFlow::NTWithNTINotSupported, } } From 9a67d49edf248ee64b69f7205a269881526f8655 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Wed, 11 Sep 2024 23:58:33 +0530 Subject: [PATCH 53/59] fix spell check --- crates/router/src/core/payments.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 77d80d6506e0..360b193c712c 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3996,7 +3996,7 @@ where .network_transaction_id .as_ref() .ok_or(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to featch the network transaction id")?; + .attach_printable("Failed to fetch the network transaction id")?; let mandate_reference_id = Some(payments_api::MandateReferenceId::NetworkMandateId( network_transaction_id.to_string(), From 732fdd0f769b4d008f527e80a7e00b7eb2db5565 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Thu, 12 Sep 2024 13:34:13 +0530 Subject: [PATCH 54/59] resolve pr commits --- config/development.toml | 10 ---------- .../src/core/payment_methods/network_tokenization.rs | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/config/development.toml b/config/development.toml index 6add351b7dc8..f2e64adaabfe 100644 --- a/config/development.toml +++ b/config/development.toml @@ -737,15 +737,5 @@ recon = "recon@example.com" [network_tokenization_supported_card_networks] card_networks = "Visa" -[network_tokenization_service] -generate_token_url= "" -fetch_token_url= "" -token_service_api_key= "" -public_key= "" -private_key= "" -key_id= "" -delete_token_url= "" -check_token_status_url= "" - [network_tokenization_supported_connectors] connector_list = "cybersource" \ No newline at end of file diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 096f5cd4fb8d..c8b76f6f7b43 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -60,7 +60,7 @@ pub struct CardNetworkTokenResponse { #[serde(rename_all = "camelCase")] pub struct CardNetworkTokenResponsePayload { pub card_brand: api_enums::CardNetwork, - pub card_fingerprint: String, + pub card_fingerprint: Option>, pub card_reference: String, pub correlation_id: String, pub customer_id: String, From 2516514b78db1b272d0061d85f76afbc83590fab Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Fri, 13 Sep 2024 02:56:22 +0530 Subject: [PATCH 55/59] add alias amex to american express card network --- crates/common_enums/src/enums.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 00781ce75945..24eba581c076 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1786,6 +1786,7 @@ pub enum CardNetwork { #[serde(alias = "MASTERCARD")] Mastercard, #[serde(alias = "AMERICANEXPRESS")] + #[serde(alias = "AMEX")] AmericanExpress, JCB, #[serde(alias = "DINERSCLUB")] From 5d64fc49f7d85f024fc5ae3b5bdfcefcf5e0d38b Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 16 Sep 2024 01:55:43 +0530 Subject: [PATCH 56/59] fix clippy_v2 --- config/config.example.toml | 2 +- config/deployments/integration_test.toml | 2 +- config/deployments/production.toml | 8 ++- config/deployments/sandbox.toml | 3 +- config/development.toml | 2 +- config/docker_compose.toml | 2 +- crates/api_models/src/admin.rs | 13 +++++ crates/diesel_models/src/business_profile.rs | 7 +++ crates/diesel_models/src/payment_method.rs | 33 +++++++++++- crates/diesel_models/src/schema_v2.rs | 6 +++ .../src/business_profile.rs | 53 +++++++++++++++++++ .../src/payment_methods.rs | 29 ++++++++++ crates/router/src/core/admin.rs | 2 + .../router/src/core/payment_methods/cards.rs | 2 +- .../payment_methods/network_tokenization.rs | 4 +- crates/router/src/core/payments/helpers.rs | 2 +- .../router/src/core/payments/tokenization.rs | 18 +++++++ crates/router/src/core/pm_auth.rs | 5 +- crates/router/src/types/api/admin.rs | 1 + .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 23 files changed, 181 insertions(+), 13 deletions(-) rename migrations/{2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile => 2024-09-12-112019_add_is_network_tokenization_enabled_in_business_profile}/down.sql (100%) rename migrations/{2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile => 2024-09-12-112019_add_is_network_tokenization_enabled_in_business_profile}/up.sql (100%) rename migrations/{2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods => 2024-09-12-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods}/down.sql (100%) rename migrations/{2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods => 2024-09-12-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods}/up.sql (100%) diff --git a/config/config.example.toml b/config/config.example.toml index 847dcdb0940b..0a84914793ae 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -731,7 +731,7 @@ connector_list = "" recon = "test@example.com" [network_tokenization_supported_card_networks] -card_networks = "Visa" # Supported card networks for network tokenization +card_networks = "Visa, AmericanExpress, Mastercard" # Supported card networks for network tokenization [network_tokenization_service] # Network Tokenization Service Configuration generate_token_url= "" # base url to generate token diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index a0968e4afd66..556b5ac9456a 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -375,7 +375,7 @@ sdk_eligible_payment_methods = "card" connector_list = "" [network_tokenization_supported_card_networks] -card_networks = "Visa" +card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" \ No newline at end of file diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 9c387aa10afd..b27efabb4e10 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -385,4 +385,10 @@ keys = "accept-language,user-agent" sdk_eligible_payment_methods = "card" [locker_based_open_banking_connectors] -connector_list = "" \ No newline at end of file +connector_list = "" + +[network_tokenization_supported_card_networks] +card_networks = "Visa, AmericanExpress, Mastercard" + +[network_tokenization_supported_connectors] +connector_list = "cybersource" \ No newline at end of file diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 8c3fbd1a7cc0..44cda81a2168 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -390,8 +390,9 @@ sdk_eligible_payment_methods = "card" [locker_based_open_banking_connectors] connector_list = "" + [network_tokenization_supported_card_networks] -card_networks = "Visa" +card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" diff --git a/config/development.toml b/config/development.toml index f2e64adaabfe..c37553d2219d 100644 --- a/config/development.toml +++ b/config/development.toml @@ -735,7 +735,7 @@ connector_list = "" recon = "recon@example.com" [network_tokenization_supported_card_networks] -card_networks = "Visa" +card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_supported_connectors] connector_list = "cybersource" \ No newline at end of file diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 626f6f345518..e4ca3d3126bd 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -594,7 +594,7 @@ connector_list = "" recon = "recon@example.com" [network_tokenization_supported_card_networks] -card_networks = "Visa" +card_networks = "Visa, AmericanExpress, Mastercard" [network_tokenization_service] generate_token_url= "" diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 98d9adcf2682..da9438182ac3 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -2027,6 +2027,11 @@ pub struct BusinessProfileCreate { /// If set to `true` tax_connector_id will be checked. #[serde(default)] pub is_tax_connector_enabled: bool, + + /// Indicates if is_network_tokenization_enabled is enabled or not. + /// If set to `true` is_network_tokenization_enabled will be checked. + #[serde(default)] + pub is_network_tokenization_enabled: bool, } #[cfg(feature = "v1")] @@ -2257,6 +2262,11 @@ pub struct BusinessProfileResponse { /// Indicates if tax_calculator connector is enabled or not. /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: bool, + + /// Indicates if is_network_tokenization_enabled is enabled or not. + /// If set to `true` is_network_tokenization_enabled will be checked. + #[schema(default = false, example = false)] + pub is_network_tokenization_enabled: bool, } #[cfg(feature = "v1")] @@ -2469,6 +2479,9 @@ pub struct BusinessProfileUpdate { /// Indicates if tax_calculator connector is enabled or not. /// If set to `true` tax_connector_id will be checked. pub is_tax_connector_enabled: Option, + + /// Indicates if is_network_tokenization_enabled is enabled or not. + pub is_network_tokenization_enabled: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index a5a9bcd235c4..fc0afe21c385 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -94,6 +94,7 @@ pub struct BusinessProfileNew { pub tax_connector_id: Option, pub is_tax_connector_enabled: Option, pub version: common_enums::ApiVersion, + pub is_network_tokenization_enabled: bool, } #[cfg(feature = "v1")] @@ -271,6 +272,7 @@ pub struct BusinessProfile { pub default_fallback_routing: Option, pub id: common_utils::id_type::ProfileId, pub version: common_enums::ApiVersion, + pub is_network_tokenization_enabled: bool, } impl BusinessProfile { @@ -325,6 +327,7 @@ pub struct BusinessProfileNew { pub default_fallback_routing: Option, pub id: common_utils::id_type::ProfileId, pub version: common_enums::ApiVersion, + pub is_network_tokenization_enabled: bool, } #[cfg(feature = "v2")] @@ -363,6 +366,7 @@ pub struct BusinessProfileUpdateInternal { pub frm_routing_algorithm_id: Option, pub payout_routing_algorithm_id: Option, pub default_fallback_routing: Option, + pub is_network_tokenization_enabled: Option, } #[cfg(feature = "v2")] @@ -400,6 +404,7 @@ impl BusinessProfileUpdateInternal { frm_routing_algorithm_id, payout_routing_algorithm_id, default_fallback_routing, + is_network_tokenization_enabled, } = self; BusinessProfile { id: source.id, @@ -457,6 +462,7 @@ impl BusinessProfileUpdateInternal { .or(source.payout_routing_algorithm_id), default_fallback_routing: default_fallback_routing.or(source.default_fallback_routing), version: source.version, + is_network_tokenization_enabled: is_network_tokenization_enabled.unwrap_or(source.is_network_tokenization_enabled), } } } @@ -507,6 +513,7 @@ impl From for BusinessProfile { payout_routing_algorithm_id: new.payout_routing_algorithm_id, default_fallback_routing: new.default_fallback_routing, version: new.version, + is_network_tokenization_enabled: new.is_network_tokenization_enabled, } } } diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index 0456c913e95b..ef7167fb6b5c 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -90,6 +90,9 @@ pub struct PaymentMethod { pub locker_fingerprint_id: Option, pub id: String, pub version: common_enums::ApiVersion, + pub network_token_requestor_reference_id: Option, + pub network_token_locker_id: Option, + pub network_token_payment_method_data: Option, } impl PaymentMethod { @@ -178,6 +181,9 @@ pub struct PaymentMethodNew { pub locker_fingerprint_id: Option, pub id: String, pub version: common_enums::ApiVersion, + pub network_token_requestor_reference_id: Option, + pub network_token_locker_id: Option, + pub network_token_payment_method_data: Option, } impl PaymentMethodNew { @@ -236,10 +242,10 @@ pub enum PaymentMethodUpdate { payment_method_data: Option, status: Option, locker_id: Option, - network_token_requestor_reference_id: Option, payment_method: Option, payment_method_type: Option, payment_method_issuer: Option, + network_token_requestor_reference_id: Option, network_token_locker_id: Option, network_token_payment_method_data: Option, }, @@ -278,6 +284,9 @@ pub enum PaymentMethodUpdate { locker_id: Option, payment_method: Option, payment_method_type: Option, + network_token_requestor_reference_id: Option, + network_token_locker_id: Option, + network_token_payment_method_data: Option, }, ConnectorMandateDetailsUpdate { connector_mandate_details: Option, @@ -310,6 +319,9 @@ pub struct PaymentMethodUpdateInternal { updated_by: Option, payment_method_type: Option, last_modified: PrimitiveDateTime, + network_token_requestor_reference_id: Option, + network_token_locker_id: Option, + network_token_payment_method_data: Option, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -601,6 +613,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, PaymentMethodUpdate::PaymentMethodDataUpdate { @@ -618,6 +631,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, PaymentMethodUpdate::LastUsedUpdate { last_used_at } => Self { @@ -633,6 +647,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, PaymentMethodUpdate::UpdatePaymentMethodDataAndLastUsed { @@ -651,6 +666,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, PaymentMethodUpdate::NetworkTransactionIdAndStatusUpdate { @@ -669,6 +685,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, PaymentMethodUpdate::StatusUpdate { status } => Self { @@ -684,6 +701,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, PaymentMethodUpdate::AdditionalDataUpdate { @@ -692,6 +710,9 @@ impl From for PaymentMethodUpdateInternal { locker_id, payment_method, payment_method_type, + network_token_requestor_reference_id, + network_token_locker_id, + network_token_payment_method_data, } => Self { metadata: None, payment_method_data, @@ -704,6 +725,7 @@ impl From for PaymentMethodUpdateInternal { updated_by: None, payment_method_type, last_modified: common_utils::date_time::now(), + network_token_requestor_reference_id, network_token_locker_id, network_token_payment_method_data, }, @@ -722,6 +744,7 @@ impl From for PaymentMethodUpdateInternal { payment_method_type: None, last_modified: common_utils::date_time::now(), network_token_locker_id: None, + network_token_requestor_reference_id: None, network_token_payment_method_data: None, }, } @@ -805,6 +828,14 @@ impl From<&PaymentMethodNew> for PaymentMethod { id: payment_method_new.id.clone(), locker_fingerprint_id: payment_method_new.locker_fingerprint_id.clone(), version: payment_method_new.version, + network_token_requestor_reference_id: payment_method_new + .network_token_requestor_reference_id + .clone(), + network_token_locker_id: payment_method_new.network_token_locker_id.clone(), + network_token_payment_method_data: payment_method_new + .network_token_payment_method_data + .clone(), + } } } diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 2deaa3d4ee42..235a6247f10d 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -216,6 +216,7 @@ diesel::table! { #[max_length = 64] id -> Varchar, version -> ApiVersion, + is_network_tokenization_enabled -> Bool, } } @@ -968,6 +969,11 @@ diesel::table! { #[max_length = 64] id -> Varchar, version -> ApiVersion, + #[max_length = 128] + network_token_requestor_reference_id -> Nullable, + #[max_length = 64] + network_token_locker_id -> Nullable, + network_token_payment_method_data -> Nullable, } } diff --git a/crates/hyperswitch_domain_models/src/business_profile.rs b/crates/hyperswitch_domain_models/src/business_profile.rs index 0797a95d8d78..cf96fdd46da1 100644 --- a/crates/hyperswitch_domain_models/src/business_profile.rs +++ b/crates/hyperswitch_domain_models/src/business_profile.rs @@ -577,6 +577,7 @@ impl super::behaviour::Conversion for BusinessProfile { tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: Some(self.is_tax_connector_enabled), version: self.version, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } } @@ -619,6 +620,7 @@ pub struct BusinessProfile { pub tax_connector_id: Option, pub is_tax_connector_enabled: bool, pub version: common_enums::ApiVersion, + pub is_network_tokenization_enabled: bool, } #[cfg(feature = "v2")] @@ -657,6 +659,7 @@ pub struct BusinessProfileSetter { pub default_fallback_routing: Option, pub tax_connector_id: Option, pub is_tax_connector_enabled: bool, + pub is_network_tokenization_enabled: bool, } #[cfg(feature = "v2")] @@ -702,6 +705,7 @@ impl From for BusinessProfile { tax_connector_id: value.tax_connector_id, is_tax_connector_enabled: value.is_tax_connector_enabled, version: consts::API_VERSION, + is_network_tokenization_enabled: value.is_network_tokenization_enabled, } } } @@ -751,6 +755,7 @@ pub struct BusinessProfileGeneralUpdate { pub always_collect_shipping_details_from_wallet_connector: Option, pub order_fulfillment_time: Option, pub order_fulfillment_time_origin: Option, + pub is_network_tokenization_enabled: Option, } #[cfg(feature = "v2")] @@ -770,6 +775,9 @@ pub enum BusinessProfileUpdate { ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled: Option, }, + NetworkTokenizationUpdate { + is_network_tokenization_enabled: Option, + }, } #[cfg(feature = "v2")] @@ -802,6 +810,7 @@ impl From for BusinessProfileUpdateInternal { always_collect_shipping_details_from_wallet_connector, order_fulfillment_time, order_fulfillment_time_origin, + is_network_tokenization_enabled, } = *update; Self { profile_name, @@ -836,6 +845,7 @@ impl From for BusinessProfileUpdateInternal { default_fallback_routing: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled, } } BusinessProfileUpdate::RoutingAlgorithmUpdate { @@ -873,6 +883,7 @@ impl From for BusinessProfileUpdateInternal { default_fallback_routing: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None, }, BusinessProfileUpdate::ExtendedCardInfoUpdate { is_extended_card_info_enabled, @@ -908,6 +919,7 @@ impl From for BusinessProfileUpdateInternal { default_fallback_routing: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None, }, BusinessProfileUpdate::ConnectorAgnosticMitUpdate { is_connector_agnostic_mit_enabled, @@ -943,6 +955,7 @@ impl From for BusinessProfileUpdateInternal { default_fallback_routing: None, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None, }, BusinessProfileUpdate::DefaultRoutingFallbackUpdate { default_fallback_routing, @@ -978,6 +991,43 @@ impl From for BusinessProfileUpdateInternal { default_fallback_routing, tax_connector_id: None, is_tax_connector_enabled: None, + is_network_tokenization_enabled: None, + }, + BusinessProfileUpdate::NetworkTokenizationUpdate { + is_network_tokenization_enabled, + } => Self { + profile_name: None, + modified_at: now, + return_url: None, + enable_payment_response_hash: None, + payment_response_hash_key: None, + redirect_to_merchant_with_http_post: None, + webhook_details: None, + metadata: None, + is_recon_enabled: None, + applepay_verified_domains: None, + payment_link_config: None, + session_expiry: None, + authentication_connector_details: None, + payout_link_config: None, + is_extended_card_info_enabled: None, + extended_card_info_config: None, + is_connector_agnostic_mit_enabled: None, + use_billing_as_payment_method_billing: None, + collect_shipping_details_from_wallet_connector: None, + collect_billing_details_from_wallet_connector: None, + outgoing_webhook_custom_http_headers: None, + always_collect_billing_details_from_wallet_connector: None, + always_collect_shipping_details_from_wallet_connector: None, + routing_algorithm_id: None, + payout_routing_algorithm_id: None, + order_fulfillment_time: None, + order_fulfillment_time_origin: None, + frm_routing_algorithm_id: None, + default_fallback_routing: None, + tax_connector_id: None, + is_tax_connector_enabled: None, + is_network_tokenization_enabled, }, } } @@ -1032,6 +1082,7 @@ impl super::behaviour::Conversion for BusinessProfile { tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: Some(self.is_tax_connector_enabled), version: self.version, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } @@ -1098,6 +1149,7 @@ impl super::behaviour::Conversion for BusinessProfile { tax_connector_id: item.tax_connector_id, is_tax_connector_enabled: item.is_tax_connector_enabled.unwrap_or(false), version: item.version, + is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } .await @@ -1149,6 +1201,7 @@ impl super::behaviour::Conversion for BusinessProfile { tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: Some(self.is_tax_connector_enabled), version: self.version, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }) } } diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index d0550c5a161b..e7cbae9e88c8 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -79,6 +79,9 @@ pub struct PaymentMethod { pub locker_fingerprint_id: Option, pub id: String, pub version: common_enums::ApiVersion, + pub network_token_requestor_reference_id: Option, + pub network_token_locker_id: Option, + pub network_token_payment_method_data: OptionalEncryptableValue, } impl PaymentMethod { @@ -312,6 +315,11 @@ impl super::behaviour::Conversion for PaymentMethod { updated_by: self.updated_by, locker_fingerprint_id: self.locker_fingerprint_id, version: self.version, + network_token_requestor_reference_id: self.network_token_requestor_reference_id, + network_token_locker_id: self.network_token_locker_id, + network_token_payment_method_data: self + .network_token_payment_method_data + .map(|val| val.into()), }) } @@ -372,6 +380,22 @@ impl super::behaviour::Conversion for PaymentMethod { updated_by: item.updated_by, locker_fingerprint_id: item.locker_fingerprint_id, version: item.version, + network_token_requestor_reference_id: item.network_token_requestor_reference_id, + network_token_locker_id: item.network_token_locker_id, + network_token_payment_method_data: item + .network_token_payment_method_data + .async_lift(|inner| async { + crypto_operation( + state, + type_name!(Self::DstType), + CryptoOperation::DecryptOptional(inner), + key_manager_identifier.clone(), + key.peek(), + ) + .await + .and_then(|val| val.try_into_optionaloperation()) + }) + .await?, }) } .await @@ -404,6 +428,11 @@ impl super::behaviour::Conversion for PaymentMethod { updated_by: self.updated_by, locker_fingerprint_id: self.locker_fingerprint_id, version: self.version, + network_token_requestor_reference_id: self.network_token_requestor_reference_id, + network_token_locker_id: self.network_token_locker_id, + network_token_payment_method_data: self + .network_token_payment_method_data + .map(|val| val.into()), }) } } diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index c9d6e06f77a7..c4ef44cbe42b 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -3572,6 +3572,7 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate { default_fallback_routing: None, tax_connector_id: self.tax_connector_id, is_tax_connector_enabled: self.is_tax_connector_enabled, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }, )) } @@ -3908,6 +3909,7 @@ impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate { .always_collect_billing_details_from_wallet_connector, always_collect_shipping_details_from_wallet_connector: self .always_collect_shipping_details_from_wallet_connector, + is_network_tokenization_enabled: self.is_network_tokenization_enabled, }, ))) } diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index ed1b5dd863b7..3babe4b0a958 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -4602,7 +4602,7 @@ async fn get_pm_list_context( pm.locker_id.clone().unwrap_or(pm.get_id().clone()), pm.network_token_requestor_reference_id .clone() - .or(Some(pm.payment_method_id.clone())), + .or(Some(pm.get_id().clone())), ), ), }) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index c8b76f6f7b43..0281ac844254 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -48,7 +48,6 @@ pub struct ApiPayload { order_data: OrderData, key_id: String, should_send_token: bool, - sub_merchant_id: String, } #[derive(Debug, Deserialize, Eq, PartialEq)] @@ -185,8 +184,7 @@ pub async fn mk_tokenization_req( card_data: Secret::new(jwt), order_data, key_id, - should_send_token: true, - sub_merchant_id: "visa_sbx_working".to_string(), //todo!() this will be removed. + should_send_token: true }; let mut request = services::Request::new( diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 274a8f541728..08fcc65981df 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2124,7 +2124,7 @@ pub async fn make_pm_data<'a, F: Clone, R, D>( network_token_locker_id: payment_method_info .network_token_requestor_reference_id .clone() - .or(Some(payment_method_info.payment_method_id.clone())), + .or(Some(payment_method_info.get_id().clone())), })); } } diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 812736b7f004..46e02e6b1a88 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -931,6 +931,24 @@ pub async fn save_in_locker( todo!() } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub async fn save_network_token_in_locker( + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _card_data: &domain::Card, + _payment_method_request: api::PaymentMethodCreate, +) -> RouterResult<( + Option, + Option, + Option, +)> { + todo!() +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] pub async fn save_network_token_in_locker( state: &SessionState, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 9cb97d308c79..91b85d43f1c2 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -507,7 +507,6 @@ async fn store_bank_details_in_payment_methods( created_at: now, last_modified: now, locker_id: None, - network_token_requestor_reference_id: None, last_used_at: now, connector_mandate_details: None, customer_acceptance: None, @@ -516,6 +515,7 @@ async fn store_bank_details_in_payment_methods( payment_method_billing_address: None, updated_by: None, version: domain::consts::API_VERSION, + network_token_requestor_reference_id: None, network_token_locker_id: None, network_token_payment_method_data: None, }; @@ -542,6 +542,9 @@ async fn store_bank_details_in_payment_methods( updated_by: None, locker_fingerprint_id: None, version: domain::consts::API_VERSION, + network_token_requestor_reference_id: None, + network_token_locker_id: None, + network_token_payment_method_data: None, }; new_entries.push(pm_new); diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 7008f5c49f6b..6f81990206f0 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -229,6 +229,7 @@ impl ForeignTryFrom for BusinessProfileResponse { order_fulfillment_time_origin: item.order_fulfillment_time_origin, tax_connector_id: item.tax_connector_id, is_tax_connector_enabled: item.is_tax_connector_enabled, + is_network_tokenization_enabled: item.is_network_tokenization_enabled, }) } } diff --git a/migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql b/migrations/2024-09-12-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql similarity index 100% rename from migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql rename to migrations/2024-09-12-112019_add_is_network_tokenization_enabled_in_business_profile/down.sql diff --git a/migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql b/migrations/2024-09-12-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql similarity index 100% rename from migrations/2024-09-02-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql rename to migrations/2024-09-12-112019_add_is_network_tokenization_enabled_in_business_profile/up.sql diff --git a/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql b/migrations/2024-09-12-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql similarity index 100% rename from migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql rename to migrations/2024-09-12-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/down.sql diff --git a/migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql b/migrations/2024-09-12-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql similarity index 100% rename from migrations/2024-09-11-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql rename to migrations/2024-09-12-123315_add_network_token_locker_id_and_network_token_payment_method_data_and_network_token_ref_id_in_payment_methods/up.sql From a3fd0da6da212dd9eeb95515433a38fcf332e158 Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Sun, 15 Sep 2024 20:48:50 +0000 Subject: [PATCH 57/59] chore: run formatter --- crates/diesel_models/src/business_profile.rs | 3 ++- crates/diesel_models/src/payment_method.rs | 1 - .../core/payment_methods/network_tokenization.rs | 2 +- crates/router/src/types/domain/payments.rs | 14 +++++++------- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index fc0afe21c385..2b761838b418 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -462,7 +462,8 @@ impl BusinessProfileUpdateInternal { .or(source.payout_routing_algorithm_id), default_fallback_routing: default_fallback_routing.or(source.default_fallback_routing), version: source.version, - is_network_tokenization_enabled: is_network_tokenization_enabled.unwrap_or(source.is_network_tokenization_enabled), + is_network_tokenization_enabled: is_network_tokenization_enabled + .unwrap_or(source.is_network_tokenization_enabled), } } } diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index ef7167fb6b5c..ca919eb15daf 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -835,7 +835,6 @@ impl From<&PaymentMethodNew> for PaymentMethod { network_token_payment_method_data: payment_method_new .network_token_payment_method_data .clone(), - } } } diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 0281ac844254..f02bfaa42617 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -184,7 +184,7 @@ pub async fn mk_tokenization_req( card_data: Secret::new(jwt), order_data, key_id, - should_send_token: true + should_send_token: true, }; let mut request = services::Request::new( diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index f1e33ef40185..53bc138c6492 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -4,11 +4,11 @@ pub use hyperswitch_domain_models::payment_method_data::{ CardToken, CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails, GoPayRedirection, GooglePayPaymentMethodInfo, GooglePayRedirectData, GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, - KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData, - PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, - SwishQrData, TokenizedBankDebitValue1, TokenizedBankDebitValue2, TokenizedBankRedirectValue1, - TokenizedBankRedirectValue2, TokenizedBankTransferValue1, TokenizedBankTransferValue2, - TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, - TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, - WeChatPayQr, NetworkTokenData, + KakaoPayRedirection, MbWayRedirection, MifinityData, NetworkTokenData, OpenBankingData, + PayLaterData, PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, + SepaAndBacsBillingDetails, SwishQrData, TokenizedBankDebitValue1, TokenizedBankDebitValue2, + TokenizedBankRedirectValue1, TokenizedBankRedirectValue2, TokenizedBankTransferValue1, + TokenizedBankTransferValue2, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, + TokenizedWalletValue2, TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, + VoucherData, WalletData, WeChatPayQr, }; From 86dd665f07372cfe99e59062faab4e42c045b1bf Mon Sep 17 00:00:00 2001 From: "hyperswitch-bot[bot]" <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Date: Sun, 15 Sep 2024 20:51:49 +0000 Subject: [PATCH 58/59] docs(openapi): re-generate OpenAPI specification --- api-reference-v2/openapi_spec.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json index acff517e1072..be14ec1ed41c 100644 --- a/api-reference-v2/openapi_spec.json +++ b/api-reference-v2/openapi_spec.json @@ -4166,6 +4166,10 @@ "is_tax_connector_enabled": { "type": "boolean", "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked." } }, "additionalProperties": false @@ -4178,7 +4182,8 @@ "profile_name", "enable_payment_response_hash", "redirect_to_merchant_with_http_post", - "is_tax_connector_enabled" + "is_tax_connector_enabled", + "is_network_tokenization_enabled" ], "properties": { "merchant_id": { @@ -4348,6 +4353,12 @@ "is_tax_connector_enabled": { "type": "boolean", "description": "Indicates if tax_calculator connector is enabled or not.\nIf set to `true` tax_connector_id will be checked." + }, + "is_network_tokenization_enabled": { + "type": "boolean", + "description": "Indicates if is_network_tokenization_enabled is enabled or not.\nIf set to `true` is_network_tokenization_enabled will be checked.", + "default": false, + "example": false } } }, From 1ee4b3d17a9d3a7d2c5aad1982bb70face1f80e7 Mon Sep 17 00:00:00 2001 From: Prasunna Soppa Date: Mon, 16 Sep 2024 02:49:01 +0530 Subject: [PATCH 59/59] fix clippy_v2 --- crates/router/src/core/payment_methods.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index a044d8432289..72fbc3d3d4e9 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -476,6 +476,9 @@ pub async fn retrieve_payment_method_with_token( _card_token_data: Option<&domain::CardToken>, _customer: &Option, _storage_scheme: common_enums::enums::MerchantStorageScheme, + _mandate_id: Option, + _payment_method_info: Option, + _business_profile: &domain::BusinessProfile, ) -> RouterResult { todo!() }