diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 77963ee90d..6b9b35ac2c 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -11,7 +11,10 @@ use common_utils::{ id_type, link_utils, pii, types::{MinorUnit, Percentage, Surcharge}, }; +#[cfg(all(any(feature = "v1", feature = "v2"), not(feature = "customer_v2")))] use masking::PeekInterface; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use masking::{ExposeInterface, PeekInterface}; use serde::de; use utoipa::{schema, ToSchema}; @@ -901,9 +904,8 @@ pub struct ConnectorTokenDetails { pub metadata: Option, /// The value of the connector token. This token can be used to make merchant initiated payments ( MIT ), directly with the connector. - pub token: String, + pub token: masking::Secret, } - #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Serialize, serde::Deserialize, ToSchema, Clone)] pub struct PaymentMethodResponse { @@ -951,6 +953,8 @@ pub struct PaymentMethodResponse { /// The connector token details if available pub connector_tokens: Option>, + + pub network_token: Option, } #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] @@ -976,6 +980,22 @@ pub struct CardDetailsPaymentMethod { pub saved_to_locker: bool, } +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct NetworkTokenDetailsPaymentMethod { + pub last4_digits: Option, + pub issuer_country: Option, + pub network_token_expiry_month: Option>, + pub network_token_expiry_year: Option>, + pub nick_name: Option>, + pub card_holder_name: Option>, + pub card_isin: Option, + pub card_issuer: Option, + pub card_network: Option, + pub card_type: Option, + #[serde(default = "saved_in_locker_default")] + pub saved_to_locker: bool, +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct PaymentMethodDataBankCreds { pub mask: String, @@ -1122,6 +1142,12 @@ pub struct CardDetailFromLocker { pub saved_to_locker: bool, } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)] +pub struct NetworkTokenResponse { + pub payment_method_data: NetworkTokenDetailsPaymentMethod, +} + fn saved_in_locker_default() -> bool { true } @@ -1990,6 +2016,9 @@ pub struct CustomerPaymentMethod { /// The billing details of the payment method #[schema(value_type = Option
)] pub billing: Option, + + ///The network token details for the payment method + pub network_tokenization: Option, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] diff --git a/crates/hyperswitch_domain_models/src/lib.rs b/crates/hyperswitch_domain_models/src/lib.rs index f2dddf39b0..fac251d8b6 100644 --- a/crates/hyperswitch_domain_models/src/lib.rs +++ b/crates/hyperswitch_domain_models/src/lib.rs @@ -31,6 +31,8 @@ pub mod router_request_types; pub mod router_response_types; pub mod type_encryption; pub mod types; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub mod vault; #[cfg(not(feature = "payouts"))] pub trait PayoutAttemptInterface {} diff --git a/crates/hyperswitch_domain_models/src/network_tokenization.rs b/crates/hyperswitch_domain_models/src/network_tokenization.rs index a29792e969..4dd54fe332 100644 --- a/crates/hyperswitch_domain_models/src/network_tokenization.rs +++ b/crates/hyperswitch_domain_models/src/network_tokenization.rs @@ -1,6 +1,3 @@ -use std::fmt::Debug; - -use api_models::enums as api_enums; #[cfg(all( any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") @@ -8,218 +5,6 @@ use api_models::enums as api_enums; use cards::CardNumber; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] use cards::{CardNumber, NetworkToken}; -use common_utils::id_type; -use masking::Secret; -use serde::{Deserialize, Serialize}; - -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "payment_methods_v2") -))] -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CardData { - pub card_number: CardNumber, - pub exp_month: Secret, - pub exp_year: Secret, - pub card_security_code: Option>, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CardData { - pub card_number: CardNumber, - pub exp_month: Secret, - pub exp_year: Secret, - #[serde(skip_serializing_if = "Option::is_none")] - pub card_security_code: Option>, -} - -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "payment_methods_v2") -))] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct OrderData { - pub consent_id: String, - pub customer_id: id_type::CustomerId, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct OrderData { - pub consent_id: String, - pub customer_id: id_type::GlobalCustomerId, -} - -#[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ApiPayload { - pub service: String, - pub card_data: Secret, //encrypted card data - pub order_data: OrderData, - pub key_id: String, - pub should_send_token: bool, -} - -#[derive(Debug, Deserialize, Eq, PartialEq)] -pub struct CardNetworkTokenResponse { - pub payload: Secret, //encrypted payload -} - -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "payment_methods_v2") -))] -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CardNetworkTokenResponsePayload { - pub card_brand: api_enums::CardNetwork, - pub card_fingerprint: Option>, - 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, - pub token_last_four: String, - pub token_status: String, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct GenerateNetworkTokenResponsePayload { - pub card_brand: api_enums::CardNetwork, - pub card_fingerprint: Option>, - pub card_reference: String, - pub correlation_id: String, - pub customer_id: String, - pub par: String, - pub token: NetworkToken, - pub token_expiry_month: Secret, - pub token_expiry_year: Secret, - pub token_isin: String, - pub token_last_four: String, - pub token_status: String, -} - -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "payment_methods_v2") -))] -#[derive(Debug, Serialize)] -pub struct GetCardToken { - pub card_reference: String, - pub customer_id: id_type::CustomerId, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, Serialize)] -pub struct GetCardToken { - pub card_reference: String, - pub customer_id: id_type::GlobalCustomerId, -} -#[derive(Debug, Deserialize)] -pub struct AuthenticationDetails { - pub cryptogram: Secret, - pub token: CardNumber, //network token -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct TokenDetails { - pub exp_month: Secret, - pub exp_year: Secret, -} - -#[derive(Debug, Deserialize)] -pub struct TokenResponse { - pub authentication_details: AuthenticationDetails, - pub network: api_enums::CardNetwork, - pub token_details: TokenDetails, -} - -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "payment_methods_v2") -))] -#[derive(Debug, Serialize, Deserialize)] -pub struct DeleteCardToken { - pub card_reference: String, //network token requestor ref id - pub customer_id: id_type::CustomerId, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, Serialize, Deserialize)] -pub struct DeleteCardToken { - pub card_reference: String, //network token requestor ref id - pub customer_id: id_type::GlobalCustomerId, -} - -#[derive(Debug, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "UPPERCASE")] -pub enum DeleteNetworkTokenStatus { - Success, -} - -#[derive(Debug, Deserialize, Eq, PartialEq)] -pub struct NetworkTokenErrorInfo { - pub code: String, - pub developer_message: String, -} - -#[derive(Debug, Deserialize, Eq, PartialEq)] -pub struct NetworkTokenErrorResponse { - pub error_message: String, - pub error_info: NetworkTokenErrorInfo, -} - -#[derive(Debug, Deserialize, Eq, PartialEq)] -pub struct DeleteNetworkTokenResponse { - pub status: DeleteNetworkTokenStatus, -} - -#[cfg(all( - any(feature = "v1", feature = "v2"), - not(feature = "payment_methods_v2") -))] -#[derive(Debug, Serialize, Deserialize)] -pub struct CheckTokenStatus { - pub card_reference: String, - pub customer_id: id_type::CustomerId, -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, Serialize, Deserialize)] -pub struct CheckTokenStatus { - pub card_reference: String, - pub customer_id: id_type::GlobalCustomerId, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -pub enum TokenStatus { - Active, - Inactive, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CheckTokenStatusResponsePayload { - pub token_expiry_month: Secret, - pub token_expiry_year: Secret, - pub token_status: TokenStatus, -} - -#[derive(Debug, Deserialize)] -pub struct CheckTokenStatusResponse { - pub payload: CheckTokenStatusResponsePayload, -} #[cfg(all( any(feature = "v1", feature = "v2"), diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 64a95778af..3f253f7cc3 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -1884,3 +1884,21 @@ impl From for NetworkTokenDetailsPaymentMethod { } } } + +impl From for payment_methods::NetworkTokenDetailsPaymentMethod { + fn from(item: NetworkTokenDetailsPaymentMethod) -> Self { + Self { + last4_digits: item.last4_digits, + issuer_country: item.issuer_country, + network_token_expiry_month: item.network_token_expiry_month, + network_token_expiry_year: item.network_token_expiry_year, + nick_name: item.nick_name, + card_holder_name: item.card_holder_name, + card_isin: item.card_isin, + card_issuer: item.card_issuer, + card_network: item.card_network, + card_type: item.card_type, + saved_to_locker: item.saved_to_locker, + } + } +} diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index 963ec83e0b..66fc6214ea 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -20,7 +20,10 @@ use serde_json::Value; use time::PrimitiveDateTime; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -use crate::{address::Address, type_encryption::OptionalEncryptableJsonType}; +use crate::{ + address::Address, payment_method_data as domain_payment_method_data, + type_encryption::OptionalEncryptableJsonType, +}; use crate::{ errors, mandates::{self, CommonMandateReference}, @@ -116,7 +119,8 @@ pub struct PaymentMethod { pub network_token_requestor_reference_id: Option, pub network_token_locker_id: Option, #[encrypt(ty = Value)] - pub network_token_payment_method_data: Option>, + pub network_token_payment_method_data: + Option>, } impl PaymentMethod { @@ -512,11 +516,16 @@ impl super::behaviour::Conversion for PaymentMethod { .change_context(common_utils::errors::CryptoError::DecodingFailed) .attach_printable("Error while deserializing Payment Method Data")?; - let network_token_payment_method_data = - data.network_token_payment_method_data - .map(|network_token_payment_method_data| { - network_token_payment_method_data.map(|value| value.expose()) - }); + let network_token_payment_method_data = data + .network_token_payment_method_data + .map(|network_token_payment_method_data| { + network_token_payment_method_data.deserialize_inner_value(|value| { + value.parse_value("Network token Payment Method Data") + }) + }) + .transpose() + .change_context(common_utils::errors::CryptoError::DecodingFailed) + .attach_printable("Error while deserializing Network token Payment Method Data")?; Ok::>(Self { customer_id: storage_model.customer_id, diff --git a/crates/hyperswitch_domain_models/src/vault.rs b/crates/hyperswitch_domain_models/src/vault.rs new file mode 100644 index 0000000000..9aac1e1f18 --- /dev/null +++ b/crates/hyperswitch_domain_models/src/vault.rs @@ -0,0 +1,54 @@ +use api_models::payment_methods; +use serde::{Deserialize, Serialize}; + +use crate::payment_method_data; + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub enum PaymentMethodVaultingData { + Card(payment_methods::CardDetail), + NetworkToken(payment_method_data::NetworkTokenDetails), +} + +impl PaymentMethodVaultingData { + pub fn get_card(&self) -> Option<&payment_methods::CardDetail> { + match self { + Self::Card(card) => Some(card), + _ => None, + } + } + pub fn get_payment_methods_data(&self) -> payment_method_data::PaymentMethodsData { + match self { + Self::Card(card) => payment_method_data::PaymentMethodsData::Card( + payment_method_data::CardDetailsPaymentMethod::from(card.clone()), + ), + Self::NetworkToken(network_token) => { + payment_method_data::PaymentMethodsData::NetworkToken( + payment_method_data::NetworkTokenDetailsPaymentMethod::from( + network_token.clone(), + ), + ) + } + } + } +} + +pub trait VaultingDataInterface { + fn get_vaulting_data_key(&self) -> String; +} + +impl VaultingDataInterface for PaymentMethodVaultingData { + fn get_vaulting_data_key(&self) -> String { + match &self { + Self::Card(card) => card.card_number.to_string(), + Self::NetworkToken(network_token) => network_token.network_token.to_string(), + } + } +} + +impl From for PaymentMethodVaultingData { + fn from(item: payment_methods::PaymentMethodCreateData) -> Self { + match item { + payment_methods::PaymentMethodCreateData::Card(card) => Self::Card(card), + } + } +} diff --git a/crates/router/src/core/errors.rs b/crates/router/src/core/errors.rs index d82bef27d1..64b2b81725 100644 --- a/crates/router/src/core/errors.rs +++ b/crates/router/src/core/errors.rs @@ -446,6 +446,12 @@ pub enum NetworkTokenizationError { NetworkTokenizationServiceNotConfigured, #[error("Failed while calling Network Token Service API")] ApiError, + #[error("Network Tokenization is not enabled for profile")] + NetworkTokenizationNotEnabledForProfile, + #[error("Network Tokenization is not supported for {message}")] + NotSupported { message: String }, + #[error("Failed to encrypt the NetworkToken payment method details")] + NetworkTokenDetailsEncryptionFailed, } #[derive(Debug, thiserror::Error)] diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 0896899724..5893fbc685 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -933,11 +933,9 @@ pub async fn create_payment_method_core( .await .attach_printable("Failed to add Payment method to DB")?; - let payment_method_data = - pm_types::PaymentMethodVaultingData::from(req.payment_method_data.clone()); - - let payment_method_data = - populate_bin_details_for_payment_method(state, &payment_method_data).await; + let payment_method_data = domain::PaymentMethodVaultingData::from(req.payment_method_data) + .populate_bin_details_for_payment_method(state) + .await; let vaulting_result = vault_payment_method( state, @@ -958,15 +956,7 @@ pub async fn create_payment_method_core( profile.is_network_tokenization_enabled, &customer_id, ) - .await - .map_err(|e| { - services::logger::error!( - "Failed to network tokenize the payment method for customer: {}. Error: {} ", - customer_id.get_string_repr(), - e - ); - }) - .ok(); + .await; let (response, payment_method) = match vaulting_result { Ok((vaulting_resp, fingerprint_id)) => { @@ -1035,86 +1025,83 @@ pub struct NetworkTokenPaymentMethodDetails { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub async fn network_tokenize_and_vault_the_pmd( state: &SessionState, - payment_method_data: &pm_types::PaymentMethodVaultingData, + payment_method_data: &domain::PaymentMethodVaultingData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, network_tokenization: Option, network_tokenization_enabled_for_profile: bool, customer_id: &id_type::GlobalCustomerId, -) -> RouterResult { - when(!network_tokenization_enabled_for_profile, || { - Err(report!(errors::ApiErrorResponse::NotSupported { - message: "Network Tokenization is not enabled for this payment method".to_string() - })) - })?; +) -> Option { + let network_token_pm_details_result: errors::CustomResult< + NetworkTokenPaymentMethodDetails, + errors::NetworkTokenizationError, + > = async { + when(!network_tokenization_enabled_for_profile, || { + Err(report!( + errors::NetworkTokenizationError::NetworkTokenizationNotEnabledForProfile + )) + })?; - let is_network_tokenization_enabled_for_pm = network_tokenization - .as_ref() - .map(|nt| matches!(nt.enable, common_enums::NetworkTokenizationToggle::Enable)) - .unwrap_or(false); + let is_network_tokenization_enabled_for_pm = network_tokenization + .as_ref() + .map(|nt| matches!(nt.enable, common_enums::NetworkTokenizationToggle::Enable)) + .unwrap_or(false); - let card_data = match payment_method_data { - pm_types::PaymentMethodVaultingData::Card(data) - if is_network_tokenization_enabled_for_pm => - { - Ok(data) - } - _ => Err(report!(errors::ApiErrorResponse::NotSupported { - message: "Network Tokenization is not supported for this payment method".to_string() - })), - }?; + let card_data = payment_method_data + .get_card() + .and_then(|card| is_network_tokenization_enabled_for_pm.then_some(card)) + .ok_or_else(|| { + report!(errors::NetworkTokenizationError::NotSupported { + message: "Payment method".to_string(), + }) + })?; - let (resp, network_token_req_ref_id) = - network_tokenization::make_card_network_tokenization_request(state, card_data, customer_id) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to generate network token")?; - - let network_token_vaulting_data = pm_types::PaymentMethodVaultingData::NetworkToken(resp); - let vaulting_resp = vault::add_payment_method_to_vault( - state, - merchant_account, - &network_token_vaulting_data, - None, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to vault the network token data")?; - - let key_manager_state = &(state).into(); - let network_token = match network_token_vaulting_data { - pm_types::PaymentMethodVaultingData::Card(card) => { - payment_method_data::PaymentMethodsData::Card( - payment_method_data::CardDetailsPaymentMethod::from(card.clone()), + let (resp, network_token_req_ref_id) = + network_tokenization::make_card_network_tokenization_request( + state, + card_data, + customer_id, ) - } - pm_types::PaymentMethodVaultingData::NetworkToken(network_token) => { - payment_method_data::PaymentMethodsData::NetworkToken( - payment_method_data::NetworkTokenDetailsPaymentMethod::from(network_token.clone()), - ) - } - }; + .await?; - let network_token_pmd = - cards::create_encrypted_data(key_manager_state, key_store, network_token) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt Payment method data")?; + let network_token_vaulting_data = domain::PaymentMethodVaultingData::NetworkToken(resp); + let vaulting_resp = vault::add_payment_method_to_vault( + state, + merchant_account, + &network_token_vaulting_data, + None, + ) + .await + .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) + .attach_printable("Failed to vault network token")?; - Ok(NetworkTokenPaymentMethodDetails { - network_token_requestor_reference_id: network_token_req_ref_id, - network_token_locker_id: vaulting_resp.vault_id.get_string_repr().clone(), - network_token_pmd, - }) + let key_manager_state = &(state).into(); + let network_token_pmd = cards::create_encrypted_data( + key_manager_state, + key_store, + network_token_vaulting_data.get_payment_methods_data(), + ) + .await + .change_context(errors::NetworkTokenizationError::NetworkTokenDetailsEncryptionFailed) + .attach_printable("Failed to encrypt PaymentMethodsData")?; + + Ok(NetworkTokenPaymentMethodDetails { + network_token_requestor_reference_id: network_token_req_ref_id, + network_token_locker_id: vaulting_resp.vault_id.get_string_repr().clone(), + network_token_pmd, + }) + } + .await; + network_token_pm_details_result.ok() } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub async fn populate_bin_details_for_payment_method( state: &SessionState, - payment_method_data: &pm_types::PaymentMethodVaultingData, -) -> pm_types::PaymentMethodVaultingData { + payment_method_data: &domain::PaymentMethodVaultingData, +) -> domain::PaymentMethodVaultingData { match payment_method_data { - pm_types::PaymentMethodVaultingData::Card(card) => { + domain::PaymentMethodVaultingData::Card(card) => { let card_isin = card.card_number.get_card_isin(); if card.card_issuer.is_some() @@ -1122,7 +1109,7 @@ pub async fn populate_bin_details_for_payment_method( && card.card_type.is_some() && card.card_issuing_country.is_some() { - pm_types::PaymentMethodVaultingData::Card(card.clone()) + domain::PaymentMethodVaultingData::Card(card.clone()) } else { let card_info = state .store @@ -1132,7 +1119,7 @@ pub async fn populate_bin_details_for_payment_method( .ok() .flatten(); - pm_types::PaymentMethodVaultingData::Card(payment_methods::CardDetail { + domain::PaymentMethodVaultingData::Card(payment_methods::CardDetail { card_number: card.card_number.clone(), card_exp_month: card.card_exp_month.clone(), card_exp_year: card.card_exp_year.clone(), @@ -1163,6 +1150,67 @@ pub async fn populate_bin_details_for_payment_method( _ => payment_method_data.clone(), } } +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +pub trait PaymentMethodExt { + async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self; +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[async_trait::async_trait] +impl PaymentMethodExt for domain::PaymentMethodVaultingData { + async fn populate_bin_details_for_payment_method(&self, state: &SessionState) -> Self { + match self { + Self::Card(card) => { + let card_isin = card.card_number.get_card_isin(); + + if card.card_issuer.is_some() + && card.card_network.is_some() + && card.card_type.is_some() + && card.card_issuing_country.is_some() + { + Self::Card(card.clone()) + } else { + let card_info = state + .store + .get_card_info(&card_isin) + .await + .map_err(|error| services::logger::error!(card_info_error=?error)) + .ok() + .flatten(); + + Self::Card(payment_methods::CardDetail { + card_number: card.card_number.clone(), + card_exp_month: card.card_exp_month.clone(), + card_exp_year: card.card_exp_year.clone(), + card_holder_name: card.card_holder_name.clone(), + nick_name: card.nick_name.clone(), + card_issuing_country: card_info.as_ref().and_then(|val| { + val.card_issuing_country + .as_ref() + .map(|c| api_enums::CountryAlpha2::from_str(c)) + .transpose() + .ok() + .flatten() + }), + card_network: card_info.as_ref().and_then(|val| val.card_network.clone()), + card_issuer: card_info.as_ref().and_then(|val| val.card_issuer.clone()), + card_type: card_info.as_ref().and_then(|val| { + val.card_type + .as_ref() + .map(|c| payment_methods::CardType::from_str(c)) + .transpose() + .ok() + .flatten() + }), + card_cvc: card.card_cvc.clone(), + }) + } + } + _ => self.clone(), + } + } +} #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[instrument(skip_all)] @@ -1557,7 +1605,7 @@ fn create_connector_token_details_update( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[allow(clippy::too_many_arguments)] pub async fn create_pm_additional_data_update( - payment_method_vaulting_data: Option<&pm_types::PaymentMethodVaultingData>, + pmd: Option<&domain::PaymentMethodVaultingData>, state: &SessionState, key_store: &domain::MerchantKeyStore, vault_id: Option, @@ -1568,15 +1616,15 @@ pub async fn create_pm_additional_data_update( payment_method_type: Option, payment_method_subtype: Option, ) -> RouterResult { - let encrypted_payment_method_data = payment_method_vaulting_data + let encrypted_payment_method_data = pmd .map( |payment_method_vaulting_data| match payment_method_vaulting_data { - pm_types::PaymentMethodVaultingData::Card(card) => { + domain::PaymentMethodVaultingData::Card(card) => { payment_method_data::PaymentMethodsData::Card( payment_method_data::CardDetailsPaymentMethod::from(card.clone()), ) } - pm_types::PaymentMethodVaultingData::NetworkToken(network_token) => { + domain::PaymentMethodVaultingData::NetworkToken(network_token) => { payment_method_data::PaymentMethodsData::NetworkToken( payment_method_data::NetworkTokenDetailsPaymentMethod::from( network_token.clone(), @@ -1625,7 +1673,7 @@ pub async fn create_pm_additional_data_update( #[instrument(skip_all)] pub async fn vault_payment_method( state: &SessionState, - pmd: &pm_types::PaymentMethodVaultingData, + pmd: &domain::PaymentMethodVaultingData, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, existing_vault_id: Option, @@ -1893,11 +1941,12 @@ pub async fn update_payment_method_core( }, )?; - let pmd = vault::retrieve_payment_method_from_vault(state, merchant_account, &payment_method) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to retrieve payment method from vault")? - .data; + let pmd: domain::PaymentMethodVaultingData = + vault::retrieve_payment_method_from_vault(state, merchant_account, &payment_method) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to retrieve payment method from vault")? + .data; let vault_request_data = request.payment_method_data.map(|payment_method_data| { pm_transforms::generate_pm_vaulting_req_from_update_request(pmd, payment_method_data) diff --git a/crates/router/src/core/payment_methods/network_tokenization.rs b/crates/router/src/core/payment_methods/network_tokenization.rs index 41a94402ba..8aa371e7a7 100644 --- a/crates/router/src/core/payment_methods/network_tokenization.rs +++ b/crates/router/src/core/payment_methods/network_tokenization.rs @@ -30,7 +30,7 @@ use crate::{ routes::{self, metrics}, services::{self, encryption}, settings, - types::{api, domain}, + types::{api, domain, payment_methods as pm_types}, }; pub const NETWORK_TOKEN_SERVICE: &str = "NETWORK_TOKEN"; @@ -45,7 +45,7 @@ pub async fn mk_tokenization_req( customer_id: id_type::CustomerId, tokenization_service: &settings::NetworkTokenizationService, ) -> CustomResult< - (domain::CardNetworkTokenResponsePayload, Option), + (pm_types::CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError, > { let enc_key = tokenization_service.public_key.peek().clone(); @@ -62,12 +62,12 @@ pub async fn mk_tokenization_req( .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) .attach_printable("Error on jwe encrypt")?; - let order_data = domain::OrderData { + let order_data = pm_types::OrderData { consent_id: uuid::Uuid::new_v4().to_string(), customer_id, }; - let api_payload = domain::ApiPayload { + let api_payload = pm_types::ApiPayload { service: NETWORK_TOKEN_SERVICE.to_string(), card_data: Secret::new(jwt), order_data, @@ -103,7 +103,7 @@ pub async fn mk_tokenization_req( .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { - let parsed_error: domain::NetworkTokenErrorResponse = err_res + let parsed_error: pm_types::NetworkTokenErrorResponse = err_res .response .parse_struct("Card Network Tokenization Response") .change_context( @@ -124,7 +124,7 @@ pub async fn mk_tokenization_req( logger::error!("Error while deserializing response: {:?}", err); })?; - let network_response: domain::CardNetworkTokenResponse = res + let network_response: pm_types::CardNetworkTokenResponse = res .response .parse_struct("Card Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; @@ -143,7 +143,7 @@ pub async fn mk_tokenization_req( "Failed to decrypt the tokenization response from the tokenization service", )?; - let cn_response: domain::CardNetworkTokenResponsePayload = + let cn_response: pm_types::CardNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; Ok((cn_response.clone(), Some(cn_response.card_reference))) @@ -156,7 +156,7 @@ pub async fn generate_network_token( customer_id: id_type::GlobalCustomerId, tokenization_service: &settings::NetworkTokenizationService, ) -> CustomResult< - (domain::GenerateNetworkTokenResponsePayload, String), + (pm_types::GenerateNetworkTokenResponsePayload, String), errors::NetworkTokenizationError, > { let enc_key = tokenization_service.public_key.peek().clone(); @@ -173,12 +173,12 @@ pub async fn generate_network_token( .change_context(errors::NetworkTokenizationError::SaveNetworkTokenFailed) .attach_printable("Error on jwe encrypt")?; - let order_data = domain::OrderData { + let order_data = pm_types::OrderData { consent_id: uuid::Uuid::new_v4().to_string(), customer_id, }; - let api_payload = domain::ApiPayload { + let api_payload = pm_types::ApiPayload { service: NETWORK_TOKEN_SERVICE.to_string(), card_data: Secret::new(jwt), order_data, @@ -214,7 +214,7 @@ pub async fn generate_network_token( .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { - let parsed_error: domain::NetworkTokenErrorResponse = err_res + let parsed_error: pm_types::NetworkTokenErrorResponse = err_res .response .parse_struct("Card Network Tokenization Response") .change_context( @@ -235,7 +235,7 @@ pub async fn generate_network_token( logger::error!("Error while deserializing response: {:?}", err); })?; - let network_response: domain::CardNetworkTokenResponse = res + let network_response: pm_types::CardNetworkTokenResponse = res .response .parse_struct("Card Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; @@ -255,7 +255,7 @@ pub async fn generate_network_token( "Failed to decrypt the tokenization response from the tokenization service", )?; - let cn_response: domain::GenerateNetworkTokenResponsePayload = + let cn_response: pm_types::GenerateNetworkTokenResponsePayload = serde_json::from_str(&card_network_token_response) .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; Ok((cn_response.clone(), cn_response.card_reference)) @@ -271,10 +271,10 @@ pub async fn make_card_network_tokenization_request( optional_cvc: Option>, customer_id: &id_type::CustomerId, ) -> CustomResult< - (domain::CardNetworkTokenResponsePayload, Option), + (pm_types::CardNetworkTokenResponsePayload, Option), errors::NetworkTokenizationError, > { - let card_data = domain::CardData { + let card_data = pm_types::CardData { card_number: card.card_number.clone(), exp_month: card.card_exp_month.clone(), exp_year: card.card_exp_year.clone(), @@ -320,7 +320,7 @@ pub async fn make_card_network_tokenization_request( card: &api_payment_methods::CardDetail, customer_id: &id_type::GlobalCustomerId, ) -> CustomResult<(NetworkTokenDetails, String), errors::NetworkTokenizationError> { - let card_data = domain::CardData { + let card_data = pm_types::CardData { card_number: card.card_number.clone(), exp_month: card.card_exp_month.clone(), exp_year: card.card_exp_year.clone(), @@ -376,12 +376,12 @@ pub async fn get_network_token( customer_id: id_type::CustomerId, network_token_requestor_ref_id: String, tokenization_service: &settings::NetworkTokenizationService, -) -> CustomResult { +) -> CustomResult { let mut request = services::Request::new( services::Method::Post, tokenization_service.fetch_token_url.as_str(), ); - let payload = domain::GetCardToken { + let payload = pm_types::GetCardToken { card_reference: network_token_requestor_ref_id, customer_id, }; @@ -411,7 +411,7 @@ pub async fn get_network_token( .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { - let parsed_error: domain::NetworkTokenErrorResponse = err_res + let parsed_error: pm_types::NetworkTokenErrorResponse = err_res .response .parse_struct("Card Network Tokenization Response") .change_context( @@ -429,7 +429,7 @@ pub async fn get_network_token( Ok(res) => Ok(res), })?; - let token_response: domain::TokenResponse = res + let token_response: pm_types::TokenResponse = res .response .parse_struct("Get Network Token Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; @@ -444,12 +444,12 @@ pub async fn get_network_token( customer_id: &id_type::GlobalCustomerId, network_token_requestor_ref_id: String, tokenization_service: &settings::NetworkTokenizationService, -) -> CustomResult { +) -> CustomResult { let mut request = services::Request::new( services::Method::Post, tokenization_service.fetch_token_url.as_str(), ); - let payload = domain::GetCardToken { + let payload = pm_types::GetCardToken { card_reference: network_token_requestor_ref_id, customer_id: customer_id.clone(), }; @@ -479,7 +479,7 @@ pub async fn get_network_token( .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { - let parsed_error: domain::NetworkTokenErrorResponse = err_res + let parsed_error: pm_types::NetworkTokenErrorResponse = err_res .response .parse_struct("Card Network Tokenization Response") .change_context( @@ -497,7 +497,7 @@ pub async fn get_network_token( Ok(res) => Ok(res), })?; - let token_response: domain::TokenResponse = res + let token_response: pm_types::TokenResponse = res .response .parse_struct("Get Network Token Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; @@ -656,7 +656,7 @@ pub async fn check_token_status_with_tokenization_service( services::Method::Post, tokenization_service.check_token_status_url.as_str(), ); - let payload = domain::CheckTokenStatus { + let payload = pm_types::CheckTokenStatus { card_reference: network_token_requestor_reference_id, customer_id: customer_id.clone(), }; @@ -683,7 +683,7 @@ pub async fn check_token_status_with_tokenization_service( .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { - let parsed_error: domain::NetworkTokenErrorResponse = err_res + let parsed_error: pm_types::NetworkTokenErrorResponse = err_res .response .parse_struct("Delete Network Tokenization Response") .change_context( @@ -704,17 +704,17 @@ pub async fn check_token_status_with_tokenization_service( logger::error!("Error while deserializing response: {:?}", err); })?; - let check_token_status_response: domain::CheckTokenStatusResponse = res + let check_token_status_response: pm_types::CheckTokenStatusResponse = res .response .parse_struct("Delete Network Tokenization Response") .change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?; match check_token_status_response.payload.token_status { - domain::TokenStatus::Active => Ok(( + pm_types::TokenStatus::Active => Ok(( Some(check_token_status_response.payload.token_expiry_month), Some(check_token_status_response.payload.token_expiry_year), )), - domain::TokenStatus::Inactive => Ok((None, None)), + pm_types::TokenStatus::Inactive => Ok((None, None)), } } @@ -791,7 +791,7 @@ pub async fn delete_network_token_from_tokenization_service( services::Method::Post, tokenization_service.delete_token_url.as_str(), ); - let payload = domain::DeleteCardToken { + let payload = pm_types::DeleteCardToken { card_reference: network_token_requestor_reference_id, customer_id: customer_id.clone(), }; @@ -820,7 +820,7 @@ pub async fn delete_network_token_from_tokenization_service( .attach_printable("Error while receiving response") .and_then(|inner| match inner { Err(err_res) => { - let parsed_error: domain::NetworkTokenErrorResponse = err_res + let parsed_error: pm_types::NetworkTokenErrorResponse = err_res .response .parse_struct("Delete Network Tokenization Response") .change_context( @@ -841,14 +841,14 @@ pub async fn delete_network_token_from_tokenization_service( logger::error!("Error while deserializing response: {:?}", err); })?; - let delete_token_response: domain::DeleteNetworkTokenResponse = res + let delete_token_response: pm_types::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.status == domain::DeleteNetworkTokenStatus::Success { + if delete_token_response.status == pm_types::DeleteNetworkTokenStatus::Success { Ok(true) } else { Err(errors::NetworkTokenizationError::DeleteNetworkTokenFailed) diff --git a/crates/router/src/core/payment_methods/tokenize.rs b/crates/router/src/core/payment_methods/tokenize.rs index 853d3c0a2b..bd0a5a2192 100644 --- a/crates/router/src/core/payment_methods/tokenize.rs +++ b/crates/router/src/core/payment_methods/tokenize.rs @@ -7,9 +7,7 @@ use common_utils::{ transformers::{ForeignFrom, ForeignTryFrom}, }; use error_stack::{report, ResultExt}; -use hyperswitch_domain_models::{ - network_tokenization as nt_domain_types, router_request_types as domain_request_types, -}; +use hyperswitch_domain_models::router_request_types as domain_request_types; use masking::{ExposeInterface, Secret}; use router_env::logger; @@ -21,7 +19,7 @@ use crate::{ }, errors::{self, RouterResult}, services, - types::{api, domain}, + types::{api, domain, payment_methods as pm_types}, SessionState, }; @@ -137,10 +135,7 @@ pub async fn tokenize_cards( } // Data types -type NetworkTokenizationResponse = ( - nt_domain_types::CardNetworkTokenResponsePayload, - Option, -); +type NetworkTokenizationResponse = (pm_types::CardNetworkTokenResponsePayload, Option); pub struct StoreLockerResponse { pub store_card_resp: pm_transformers::StoreCardRespPayload, @@ -162,7 +157,7 @@ pub struct NetworkTokenizationBuilder<'a, S: State> { pub card_cvc: Option>, /// Network token details - pub network_token: Option<&'a nt_domain_types::CardNetworkTokenResponsePayload>, + pub network_token: Option<&'a pm_types::CardNetworkTokenResponsePayload>, /// Stored card details pub stored_card: Option<&'a pm_transformers::StoreCardRespPayload>, diff --git a/crates/router/src/core/payment_methods/transformers.rs b/crates/router/src/core/payment_methods/transformers.rs index 233dade651..87f7e38c93 100644 --- a/crates/router/src/core/payment_methods/transformers.rs +++ b/crates/router/src/core/payment_methods/transformers.rs @@ -522,14 +522,14 @@ pub fn mk_add_card_response_hs( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub fn generate_pm_vaulting_req_from_update_request( - pm_create: pm_types::PaymentMethodVaultingData, + pm_create: domain::PaymentMethodVaultingData, pm_update: api::PaymentMethodUpdateData, -) -> pm_types::PaymentMethodVaultingData { +) -> domain::PaymentMethodVaultingData { match (pm_create, pm_update) { ( - pm_types::PaymentMethodVaultingData::Card(card_create), + domain::PaymentMethodVaultingData::Card(card_create), api::PaymentMethodUpdateData::Card(update_card), - ) => pm_types::PaymentMethodVaultingData::Card(api::CardDetail { + ) => domain::PaymentMethodVaultingData::Card(api::CardDetail { card_number: card_create.card_number, card_exp_month: card_create.card_exp_month, card_exp_year: card_create.card_exp_year, @@ -571,6 +571,20 @@ pub fn generate_payment_method_response( .map(transformers::ForeignFrom::foreign_from) .collect::>() }); + let network_token_pmd = payment_method + .network_token_payment_method_data + .clone() + .map(|data| data.into_inner()) + .and_then(|data| match data { + domain::PaymentMethodsData::NetworkToken(token) => { + Some(api::NetworkTokenDetailsPaymentMethod::from(token)) + } + _ => None, + }); + + let network_token = network_token_pmd.map(|pmd| api::NetworkTokenResponse { + payment_method_data: pmd, + }); let resp = api::PaymentMethodResponse { merchant_id: payment_method.merchant_id.to_owned(), @@ -583,6 +597,7 @@ pub fn generate_payment_method_response( last_used_at: Some(payment_method.last_used_at), payment_method_data: pmd, connector_tokens, + network_token, }; Ok(resp) @@ -839,15 +854,6 @@ pub fn get_card_detail( Ok(card_detail) } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -impl From for pm_types::PaymentMethodVaultingData { - fn from(item: api::PaymentMethodCreateData) -> Self { - match item { - api::PaymentMethodCreateData::Card(card) => Self::Card(card), - } - } -} - //------------------------------------------------TokenizeService------------------------------------------------ pub fn mk_crud_locker_request( locker: &settings::Locker, @@ -961,6 +967,21 @@ impl transformers::ForeignTryFrom for api::CustomerPaymen .map(|billing| billing.into_inner()) .map(From::from); + let network_token_pmd = item + .network_token_payment_method_data + .clone() + .map(|data| data.into_inner()) + .and_then(|data| match data { + domain::PaymentMethodsData::NetworkToken(token) => { + Some(api::NetworkTokenDetailsPaymentMethod::from(token)) + } + _ => None, + }); + + let network_token_resp = network_token_pmd.map(|pmd| api::NetworkTokenResponse { + payment_method_data: pmd, + }); + // TODO: check how we can get this field let recurring_enabled = true; @@ -977,6 +998,7 @@ impl transformers::ForeignTryFrom for api::CustomerPaymen requires_cvv: true, is_default: false, billing: payment_method_billing, + network_tokenization: network_token_resp, }) } } @@ -1033,7 +1055,7 @@ impl transformers::ForeignFrom( #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[instrument(skip_all)] -pub async fn get_fingerprint_id_from_vault< - D: pm_types::VaultingDataInterface + serde::Serialize, ->( +pub async fn get_fingerprint_id_from_vault( state: &routes::SessionState, data: &D, key: String, @@ -1286,7 +1284,7 @@ pub async fn get_fingerprint_id_from_vault< pub async fn add_payment_method_to_vault( state: &routes::SessionState, merchant_account: &domain::MerchantAccount, - pmd: &pm_types::PaymentMethodVaultingData, + pmd: &domain::PaymentMethodVaultingData, existing_vault_id: Option, ) -> CustomResult { let payload = pm_types::AddVaultRequest { diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 1c69852a10..bcf0d624ff 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -2613,7 +2613,7 @@ impl PostUpdateTracker, types::SetupMandateRe original_payment_authorized_amount: Some(net_amount), original_payment_authorized_currency: Some(currency), metadata: None, - token, + token: masking::Secret::new(token), token_type: common_enums::TokenizationType::MultiUse, }; diff --git a/crates/router/src/types/api/payment_methods.rs b/crates/router/src/types/api/payment_methods.rs index 87475fcd89..9c5030a30a 100644 --- a/crates/router/src/types/api/payment_methods.rs +++ b/crates/router/src/types/api/payment_methods.rs @@ -4,14 +4,15 @@ pub use api_models::payment_methods::{ CardNetworkTokenizeResponse, CardType, CustomerPaymentMethod, CustomerPaymentMethodsListResponse, DeleteTokenizeByTokenRequest, GetTokenizePayloadRequest, GetTokenizePayloadResponse, ListCountriesCurrenciesRequest, MigrateCardDetail, - PaymentMethodCollectLinkRenderRequest, PaymentMethodCollectLinkRequest, PaymentMethodCreate, - PaymentMethodCreateData, PaymentMethodDeleteResponse, PaymentMethodId, - PaymentMethodIntentConfirm, PaymentMethodIntentCreate, PaymentMethodListData, - PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodMigrate, - PaymentMethodMigrateResponse, PaymentMethodResponse, PaymentMethodResponseData, - PaymentMethodUpdate, PaymentMethodUpdateData, PaymentMethodsData, TokenizePayloadEncrypted, - TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, - TokenizedWalletValue2, TotalPaymentMethodCountResponse, + NetworkTokenDetailsPaymentMethod, NetworkTokenResponse, PaymentMethodCollectLinkRenderRequest, + PaymentMethodCollectLinkRequest, PaymentMethodCreate, PaymentMethodCreateData, + PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodIntentConfirm, + PaymentMethodIntentCreate, PaymentMethodListData, PaymentMethodListRequest, + PaymentMethodListResponse, PaymentMethodMigrate, PaymentMethodMigrateResponse, + PaymentMethodResponse, PaymentMethodResponseData, PaymentMethodUpdate, PaymentMethodUpdateData, + PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, + TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2, + TotalPaymentMethodCountResponse, }; #[cfg(all( any(feature = "v2", feature = "v1"), diff --git a/crates/router/src/types/domain.rs b/crates/router/src/types/domain.rs index 51f07fd6d2..88bc1bbd34 100644 --- a/crates/router/src/types/domain.rs +++ b/crates/router/src/types/domain.rs @@ -40,6 +40,15 @@ pub mod payment_methods { pub mod consts { pub use hyperswitch_domain_models::consts::*; } +pub mod payment_method_data { + pub use hyperswitch_domain_models::payment_method_data::*; +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub mod vault { + pub use hyperswitch_domain_models::vault::*; +} + pub mod payments; pub mod types; #[cfg(feature = "olap")] @@ -54,8 +63,10 @@ pub use event::*; pub use merchant_connector_account::*; pub use merchant_key_store::*; pub use network_tokenization::*; +pub use payment_method_data::*; pub use payment_methods::*; -pub use payments::*; #[cfg(feature = "olap")] pub use user::*; pub use user_key_store::*; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +pub use vault::*; diff --git a/crates/router/src/types/payment_methods.rs b/crates/router/src/types/payment_methods.rs index d639bc44bc..2f580b7d3e 100644 --- a/crates/router/src/types/payment_methods.rs +++ b/crates/router/src/types/payment_methods.rs @@ -1,9 +1,20 @@ +use std::fmt::Debug; + +use api_models::enums as api_enums; +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +use cards::CardNumber; +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +use cards::{CardNumber, NetworkToken}; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] use common_utils::generate_id; +use common_utils::id_type; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] use hyperswitch_domain_models::payment_method_data::NetworkTokenDetails; -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] use masking::Secret; +use serde::{Deserialize, Serialize}; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] use crate::{ @@ -18,11 +29,6 @@ pub trait VaultingInterface { fn get_vaulting_flow_name() -> &'static str; } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -pub trait VaultingDataInterface { - fn get_vaulting_data_key(&self) -> String; -} - #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct VaultFingerprintRequest { @@ -39,7 +45,7 @@ pub struct VaultFingerprintResponse { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct AddVaultRequest { - pub entity_id: common_utils::id_type::MerchantId, + pub entity_id: id_type::MerchantId, pub vault_id: domain::VaultId, pub data: D, pub ttl: i64, @@ -48,7 +54,7 @@ pub struct AddVaultRequest { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct AddVaultResponse { - pub entity_id: common_utils::id_type::MerchantId, + pub entity_id: id_type::MerchantId, pub vault_id: domain::VaultId, pub fingerprint_id: Option, } @@ -113,23 +119,6 @@ impl VaultingInterface for VaultDelete { } } -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] -pub enum PaymentMethodVaultingData { - Card(api::CardDetail), - NetworkToken(NetworkTokenDetails), -} - -#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -impl VaultingDataInterface for PaymentMethodVaultingData { - fn get_vaulting_data_key(&self) -> String { - match &self { - Self::Card(card) => card.card_number.to_string(), - Self::NetworkToken(network_token) => network_token.network_token.to_string(), - } - } -} - #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub struct SavedPMLPaymentsInfo { pub payment_intent: storage::PaymentIntent, @@ -142,26 +131,235 @@ pub struct SavedPMLPaymentsInfo { #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct VaultRetrieveRequest { - pub entity_id: common_utils::id_type::MerchantId, + pub entity_id: id_type::MerchantId, pub vault_id: domain::VaultId, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct VaultRetrieveResponse { - pub data: PaymentMethodVaultingData, + pub data: domain::PaymentMethodVaultingData, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct VaultDeleteRequest { - pub entity_id: common_utils::id_type::MerchantId, + pub entity_id: id_type::MerchantId, pub vault_id: domain::VaultId, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct VaultDeleteResponse { - pub entity_id: common_utils::id_type::MerchantId, + pub entity_id: id_type::MerchantId, pub vault_id: domain::VaultId, } + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CardData { + pub card_number: CardNumber, + pub exp_month: Secret, + pub exp_year: Secret, + pub card_security_code: Option>, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CardData { + pub card_number: CardNumber, + pub exp_month: Secret, + pub exp_year: Secret, + #[serde(skip_serializing_if = "Option::is_none")] + pub card_security_code: Option>, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OrderData { + pub consent_id: String, + pub customer_id: id_type::CustomerId, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OrderData { + pub consent_id: String, + pub customer_id: id_type::GlobalCustomerId, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ApiPayload { + pub service: String, + pub card_data: Secret, //encrypted card data + pub order_data: OrderData, + pub key_id: String, + pub should_send_token: bool, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct CardNetworkTokenResponse { + pub payload: Secret, //encrypted payload +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CardNetworkTokenResponsePayload { + pub card_brand: api_enums::CardNetwork, + pub card_fingerprint: Option>, + 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, + pub token_last_four: String, + pub token_status: String, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GenerateNetworkTokenResponsePayload { + pub card_brand: api_enums::CardNetwork, + pub card_fingerprint: Option>, + pub card_reference: String, + pub correlation_id: String, + pub customer_id: String, + pub par: String, + pub token: NetworkToken, + pub token_expiry_month: Secret, + pub token_expiry_year: Secret, + pub token_isin: String, + pub token_last_four: String, + pub token_status: String, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +#[derive(Debug, Serialize)] +pub struct GetCardToken { + pub card_reference: String, + pub customer_id: id_type::CustomerId, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Serialize)] +pub struct GetCardToken { + pub card_reference: String, + pub customer_id: id_type::GlobalCustomerId, +} +#[derive(Debug, Deserialize)] +pub struct AuthenticationDetails { + pub cryptogram: Secret, + pub token: CardNumber, //network token +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct TokenDetails { + pub exp_month: Secret, + pub exp_year: Secret, +} + +#[derive(Debug, Deserialize)] +pub struct TokenResponse { + pub authentication_details: AuthenticationDetails, + pub network: api_enums::CardNetwork, + pub token_details: TokenDetails, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +#[derive(Debug, Serialize, Deserialize)] +pub struct DeleteCardToken { + pub card_reference: String, //network token requestor ref id + pub customer_id: id_type::CustomerId, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Serialize, Deserialize)] +pub struct DeleteCardToken { + pub card_reference: String, //network token requestor ref id + pub customer_id: id_type::GlobalCustomerId, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "UPPERCASE")] +pub enum DeleteNetworkTokenStatus { + Success, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct NetworkTokenErrorInfo { + pub code: String, + pub developer_message: String, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct NetworkTokenErrorResponse { + pub error_message: String, + pub error_info: NetworkTokenErrorInfo, +} + +#[derive(Debug, Deserialize, Eq, PartialEq)] +pub struct DeleteNetworkTokenResponse { + pub status: DeleteNetworkTokenStatus, +} + +#[cfg(all( + any(feature = "v1", feature = "v2"), + not(feature = "payment_methods_v2") +))] +#[derive(Debug, Serialize, Deserialize)] +pub struct CheckTokenStatus { + pub card_reference: String, + pub customer_id: id_type::CustomerId, +} + +#[cfg(all(feature = "v2", feature = "payment_methods_v2"))] +#[derive(Debug, Serialize, Deserialize)] +pub struct CheckTokenStatus { + pub card_reference: String, + pub customer_id: id_type::GlobalCustomerId, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum TokenStatus { + Active, + Inactive, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CheckTokenStatusResponsePayload { + pub token_expiry_month: Secret, + pub token_expiry_year: Secret, + pub token_status: TokenStatus, +} + +#[derive(Debug, Deserialize)] +pub struct CheckTokenStatusResponse { + pub payload: CheckTokenStatusResponsePayload, +}