diff --git a/crates/api_models/src/open_router.rs b/crates/api_models/src/open_router.rs index 98fa8932764..16d3b951340 100644 --- a/crates/api_models/src/open_router.rs +++ b/crates/api_models/src/open_router.rs @@ -1,5 +1,7 @@ use std::{collections::HashMap, fmt::Debug}; +use crate::enums::{Currency, PaymentMethod}; +use crate::payment_methods; use common_utils::{id_type, types::MinorUnit}; pub use euclid::{ dssa::types::EuclidAnalysable, @@ -10,8 +12,6 @@ pub use euclid::{ }; use serde::{Deserialize, Serialize}; -use crate::enums::{Currency, PaymentMethod}; - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct OpenRouterDecideGatewayRequest { @@ -49,7 +49,6 @@ pub struct PaymentInfo { // paymentSource: Option, // authType: Option, // cardIssuerBankName: Option, - pub card_isin: Option, // cardType: Option, // cardSwitchProvider: Option>, @@ -70,6 +69,30 @@ pub struct DebitRoutingOutput { pub card_type: common_enums::CardType, } +impl From<&DebitRoutingOutput> for payment_methods::CoBadgedCardData { + fn from(output: &DebitRoutingOutput) -> Self { + Self { + co_badged_card_networks: output.co_badged_card_networks.clone(), + issuer_country: output.issuer_country, + is_regulated: output.is_regulated, + regulated_name: output.regulated_name, + card_type: output.card_type, + } + } +} + +impl From for DebitRoutingRequestData { + fn from(output: payment_methods::CoBadgedCardData) -> Self { + Self { + co_badged_card_networks: output.co_badged_card_networks, + issuer_country: output.issuer_country, + is_regulated: output.is_regulated, + regulated_name: output.regulated_name, + card_type: output.card_type, + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CoBadgedCardRequest { pub merchant_category_code: common_enums::MerchantCategoryCode, diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs index 4313d224019..16bf6f760fe 100644 --- a/crates/api_models/src/payment_methods.rs +++ b/crates/api_models/src/payment_methods.rs @@ -987,6 +987,16 @@ pub struct CardDetailsPaymentMethod { pub card_type: Option, #[serde(default = "saved_in_locker_default")] pub saved_to_locker: bool, + pub co_badged_card_data: Option, +} + +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoBadgedCardData { + pub co_badged_card_networks: Vec, + pub issuer_country: common_enums::CountryAlpha2, + pub is_regulated: bool, + pub regulated_name: Option, + pub card_type: common_enums::CardType, } #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ToSchema)] @@ -1314,6 +1324,7 @@ impl From for CardDetailsPaymentMethod { card_network: item.card_network, card_type: item.card_type.map(|card| card.to_string()), saved_to_locker: true, + co_badged_card_data: None, } } } @@ -1322,8 +1333,10 @@ impl From for CardDetailsPaymentMethod { any(feature = "v1", feature = "v2"), not(feature = "payment_methods_v2") ))] -impl From for CardDetailsPaymentMethod { - fn from(item: CardDetailFromLocker) -> Self { +impl From<(CardDetailFromLocker, Option<&CoBadgedCardData>)> for CardDetailsPaymentMethod { + fn from( + (item, co_badged_card_data): (CardDetailFromLocker, Option<&CoBadgedCardData>), + ) -> Self { Self { issuer_country: item.issuer_country, last4_digits: item.last4_digits, @@ -1336,6 +1349,7 @@ impl From for CardDetailsPaymentMethod { card_network: item.card_network, card_type: item.card_type, saved_to_locker: item.saved_to_locker, + co_badged_card_data: co_badged_card_data.cloned(), } } } @@ -1355,6 +1369,7 @@ impl From for CardDetailsPaymentMethod { card_network: item.card_network, card_type: item.card_type, saved_to_locker: item.saved_to_locker, + co_badged_card_data: None, } } } diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 6a2fc15e53f..be5b7d67d08 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -2272,6 +2272,7 @@ pub enum MandateStatus { /// Indicates the card network. #[derive( Clone, + Copy, Debug, Eq, Hash, diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 2d386d0f002..b3c4cb9e7a8 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -77,6 +77,14 @@ impl PaymentMethodData { pub fn is_network_token_payment_method_data(&self) -> bool { matches!(self, Self::NetworkToken(_)) } + + pub fn get_co_badged_card_data(&self) -> Option<&payment_methods::CoBadgedCardData> { + if let Self::Card(card) = self { + card.co_badged_card_data.as_ref() + } else { + None + } + } } #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)] @@ -87,11 +95,13 @@ pub struct Card { pub card_cvc: Secret, pub card_issuer: Option, pub card_network: Option, + // we use this as the card type for co-badged cards as well pub card_type: Option, pub card_issuing_country: Option, pub bank_code: Option, pub nick_name: Option>, pub card_holder_name: Option>, + pub co_badged_card_data: Option, } #[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Default)] @@ -120,6 +130,7 @@ pub struct CardDetail { pub bank_code: Option, pub nick_name: Option>, pub card_holder_name: Option>, + pub co_badged_card_data: Option, } impl CardDetailsForNetworkTransactionId { @@ -162,6 +173,7 @@ impl From<&Card> for CardDetail { bank_code: item.bank_code.to_owned(), nick_name: item.nick_name.to_owned(), card_holder_name: item.card_holder_name.to_owned(), + co_badged_card_data: item.co_badged_card_data.to_owned(), } } } @@ -723,6 +735,7 @@ impl TryFrom for PaymentMethodData { bank_code: None, nick_name, card_holder_name, + co_badged_card_data: None, })), } } @@ -732,7 +745,7 @@ impl From for PaymentMethodData { fn from(api_model_payment_method_data: api_models::payments::PaymentMethodData) -> Self { match api_model_payment_method_data { api_models::payments::PaymentMethodData::Card(card_data) => { - Self::Card(Card::from(card_data)) + Self::Card(Card::from((card_data, None))) } api_models::payments::PaymentMethodData::CardRedirect(card_redirect) => { Self::CardRedirect(From::from(card_redirect)) @@ -782,8 +795,25 @@ impl From for PaymentMethodData { } } -impl From for Card { - fn from(value: api_models::payments::Card) -> Self { +impl + From<( + api_models::payments::Card, + Option, + )> for Card +{ + fn from( + (value, co_badged_card_data_optional): ( + api_models::payments::Card, + Option, + ), + ) -> Self { + let first_co_badged_card_network = + co_badged_card_data_optional + .as_ref() + .and_then(|co_badged_card_data| { + co_badged_card_data.co_badged_card_networks.first().cloned() + }); + let api_models::payments::Card { card_number, card_exp_month, @@ -804,12 +834,13 @@ impl From for Card { card_exp_year, card_cvc, card_issuer, - card_network, + card_network: first_co_badged_card_network.or(card_network), card_type, card_issuing_country, bank_code, nick_name, card_holder_name, + co_badged_card_data: co_badged_card_data_optional, } } } @@ -1875,6 +1906,16 @@ pub enum PaymentMethodsData { NetworkToken(NetworkTokenDetailsPaymentMethod), } +impl PaymentMethodsData { + pub fn get_co_badged_card_data(&self) -> Option { + if let Self::Card(card) = self { + card.co_badged_card_data.clone() + } else { + None + } + } +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct NetworkTokenDetailsPaymentMethod { pub last4_digits: Option, @@ -1909,6 +1950,7 @@ pub struct CardDetailsPaymentMethod { pub card_type: Option, #[serde(default = "saved_in_locker_default")] pub saved_to_locker: bool, + pub co_badged_card_data: Option, } #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -1926,6 +1968,7 @@ impl From for CardDetailsPaymentMethod { card_network: item.card_network, card_type: item.card_type.map(|card| card.to_string()), saved_to_locker: true, + co_badged_card_data: None, } } } diff --git a/crates/hyperswitch_domain_models/src/payment_methods.rs b/crates/hyperswitch_domain_models/src/payment_methods.rs index 955d01c94df..aa2f6223bdc 100644 --- a/crates/hyperswitch_domain_models/src/payment_methods.rs +++ b/crates/hyperswitch_domain_models/src/payment_methods.rs @@ -1,5 +1,11 @@ #[cfg(feature = "v2")] use api_models::payment_methods::PaymentMethodsData; + +use crate::payment_method_data as domain_payment_method_data; +#[cfg(feature = "v1")] +use masking::ExposeInterface; +#[cfg(feature = "v1")] +use router_env::logger; // specific imports because of using the macro use common_enums::enums::MerchantStorageScheme; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] @@ -20,10 +26,7 @@ use serde_json::Value; use time::PrimitiveDateTime; #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] -use crate::{ - address::Address, payment_method_data as domain_payment_method_data, - type_encryption::OptionalEncryptableJsonType, -}; +use crate::{address::Address, type_encryption::OptionalEncryptableJsonType}; use crate::{ mandates::{self, CommonMandateReference}, merchant_key_store::MerchantKeyStore, @@ -131,6 +134,25 @@ impl PaymentMethod { &self.payment_method_id } + #[cfg(feature = "v1")] + pub fn get_payment_methods_data( + &self, + ) -> Option { + self.payment_method_data + .clone() + .map(|value| value.into_inner().expose()) + .and_then(|value| { + serde_json::from_value::(value) + .map_err(|error| { + logger::warn!( + ?error, + "Failed to parse payment method data in payment method info" + ); + }) + .ok() + }) + } + #[cfg(all(feature = "v2", feature = "payment_methods_v2"))] pub fn get_id(&self) -> &id_type::GlobalPaymentMethodId { &self.id diff --git a/crates/router/src/core/debit_routing.rs b/crates/router/src/core/debit_routing.rs index 35135932b06..58f6029eb67 100644 --- a/crates/router/src/core/debit_routing.rs +++ b/crates/router/src/core/debit_routing.rs @@ -1,4 +1,4 @@ -use api_models::enums as api_enums; +use api_models::{enums as api_enums, payment_methods}; use common_enums::enums; use masking::Secret; @@ -26,7 +26,7 @@ pub async fn perform_debit_routing( operation: &BoxedOperation<'_, F, Req, D>, state: &SessionState, business_profile: &domain::Profile, - payment_data: &D, + payment_data: &mut D, connector: Option, ) -> (Option, bool) where @@ -208,7 +208,7 @@ async fn handle_pre_determined_connector( debit_routing_config: &settings::DebitRoutingConfig, debit_routing_supported_connectors: HashSet, connector_data: &api::ConnectorRoutingData, - payment_data: &D, + payment_data: &mut D, acquirer_country: enums::CountryAlpha2, ) -> Option where @@ -243,9 +243,12 @@ where None } -pub async fn get_sorted_co_badged_networks_by_fee>( +pub async fn get_sorted_co_badged_networks_by_fee< + F: Clone, + D: OperationSessionGetters + OperationSessionSetters, +>( state: &SessionState, - payment_data: &D, + payment_data: &mut D, acquirer_country: enums::CountryAlpha2, ) -> Option> { logger::debug!("Fetching sorted card networks"); @@ -253,35 +256,67 @@ pub async fn get_sorted_co_badged_networks_by_fee None, + _ => { + let co_badged_card_request = api_models::open_router::CoBadgedCardRequest { + merchant_category_code: enums::MerchantCategoryCode::Mcc0001, + acquirer_country, + co_badged_card_data: saved_co_badged_card_data + .map(api_models::open_router::DebitRoutingRequestData::from), + }; + + routing::perform_open_routing_for_debit_routing( + state, + payment_attempt, + co_badged_card_request, + card_isin, + ) + .await + .map_err(|error| { + logger::warn!(?error, "Failed to calculate total fees per network"); + }) + .ok() + } + }; - return debit_routing_output_optional; - } - None + debit_routing_output_optional + .as_ref() + .map(|debit_routing_output| { + payment_data.set_co_badged_card_data(payment_methods::CoBadgedCardData::from( + debit_routing_output, + )) + }); + + debit_routing_output_optional.map(|data| data.co_badged_card_networks) } async fn handle_retryable_connector( @@ -289,7 +324,7 @@ async fn handle_retryable_connector( debit_routing_config: &settings::DebitRoutingConfig, debit_routing_supported_connectors: HashSet, connector_data_list: Vec, - payment_data: &D, + payment_data: &mut D, acquirer_country: enums::CountryAlpha2, ) -> Option where diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 5749715e51b..5fe4a390d11 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -774,7 +774,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; let payment_method_card_details = - PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())); + PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None))); let payment_method_data_encrypted: Option>> = Some( create_encrypted_data( @@ -935,7 +935,7 @@ pub async fn save_network_token_and_update_payment_method( let pm_token_details = token_pm_resp .card .as_ref() - .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))); + .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None)))); let pm_network_token_data_encrypted = pm_token_details .async_map(|pm_card| { create_encrypted_data( @@ -1382,6 +1382,7 @@ pub async fn add_payment_method_data( card_issuer: card_info.as_ref().and_then(|ci| ci.card_issuer.clone()), card_type: card_info.as_ref().and_then(|ci| ci.card_type.clone()), saved_to_locker: true, + co_badged_card_data: None, }; let pm_data_encrypted: Encryptable> = create_encrypted_data( @@ -1647,7 +1648,7 @@ pub async fn add_payment_method( }); let updated_pmd = updated_card.as_ref().map(|card| { - PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) + PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None))) }); let pm_data_encrypted: Option>> = updated_pmd @@ -1911,7 +1912,7 @@ pub async fn save_migration_payment_method( }); let updated_pmd = updated_card.as_ref().map(|card| { - PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) + PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None))) }); let pm_data_encrypted: Option>> = updated_pmd @@ -2024,7 +2025,7 @@ pub async fn insert_payment_method( let pm_card_details = resp .card .clone() - .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))); + .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None)))); let key_manager_state = state.into(); let pm_data_encrypted: crypto::OptionalEncryptableValue = pm_card_details .clone() @@ -2249,7 +2250,7 @@ pub async fn update_customer_payment_method( let updated_pmd = updated_card .as_ref() - .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))); + .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from((card.clone(), None)))); let key_manager_state = (&state).into(); let pm_data_encrypted: Option>> = updated_pmd .async_map(|updated_pmd| { diff --git a/crates/router/src/core/payment_methods/tokenize.rs b/crates/router/src/core/payment_methods/tokenize.rs index 21e2ed4feef..cce741d2c40 100644 --- a/crates/router/src/core/payment_methods/tokenize.rs +++ b/crates/router/src/core/payment_methods/tokenize.rs @@ -273,6 +273,7 @@ where card_network: card_details.card_network.clone(), card_type: card_details.card_type.clone(), saved_to_locker, + co_badged_card_data: card_details.co_badged_card_data.clone(), }); create_encrypted_data(&self.state.into(), self.key_store, pm_data) .await @@ -298,6 +299,7 @@ where card_network: card_details.card_network.clone(), card_type: card_details.card_type.clone(), saved_to_locker, + co_badged_card_data: None, }); create_encrypted_data(&self.state.into(), self.key_store, token_data) .await diff --git a/crates/router/src/core/payment_methods/tokenize/card_executor.rs b/crates/router/src/core/payment_methods/tokenize/card_executor.rs index eb9d3d6b6c6..e04e688933d 100644 --- a/crates/router/src/core/payment_methods/tokenize/card_executor.rs +++ b/crates/router/src/core/payment_methods/tokenize/card_executor.rs @@ -131,6 +131,7 @@ impl<'a> NetworkTokenizationBuilder<'a, CardRequestValidated> { .map_or(card_req.card_issuing_country.clone(), |card_info| { card_info.card_issuing_country.clone() }), + co_badged_card_data: None, }; NetworkTokenizationBuilder { state: std::marker::PhantomData, diff --git a/crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs b/crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs index 2249434b107..a55896b36c5 100644 --- a/crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs +++ b/crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs @@ -151,6 +151,7 @@ impl<'a> NetworkTokenizationBuilder<'a, PmValidated> { card_issuing_country: optional_card_info .as_ref() .and_then(|card_info| card_info.card_issuing_country.clone()), + co_badged_card_data: None, }; NetworkTokenizationBuilder { state: std::marker::PhantomData, diff --git a/crates/router/src/core/payment_methods/vault.rs b/crates/router/src/core/payment_methods/vault.rs index 2231f7d89f6..af60d07a466 100644 --- a/crates/router/src/core/payment_methods/vault.rs +++ b/crates/router/src/core/payment_methods/vault.rs @@ -121,6 +121,7 @@ impl Vaultable for domain::Card { card_type: None, nick_name: value1.nickname.map(masking::Secret::new), card_holder_name: value1.card_holder_name, + co_badged_card_data: None, }; let supp_data = SupplementaryVaultData { diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index c4ada7fe21f..e72b8bf63b8 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -26,9 +26,7 @@ pub mod payment_methods; #[cfg(feature = "olap")] use api_models::admin::MerchantConnectorInfo; use api_models::{ - self, enums, - mandates::RecurringDetails, - payments::{self as payments_api}, + self, enums, mandates::RecurringDetails, payments::{self as payments_api}, }; pub use common_enums::enums::CallConnectorAction; use common_utils::{ @@ -361,7 +359,7 @@ where &operation, state, &business_profile, - &payment_data, + &mut payment_data, connector, ).await; @@ -612,8 +610,8 @@ where let mut connectors = connectors.clone().into_iter(); let connector_data = if business_profile.is_debit_routing_enabled && is_debit_routing_performed { - if let Some((connector_data, local_network)) = find_connector_with_networks(&mut connectors) { - payment_data.set_local_network(local_network); + if let Some((connector_data, network)) = find_connector_with_networks(&mut connectors) { + payment_data.set_card_network(network); connector_data } else { get_connector_data(&mut connectors)?.connector_data @@ -8076,7 +8074,11 @@ pub trait OperationSessionSetters { &mut self, merchant_connector_id: Option, ); - fn set_local_network(&mut self, local_network: enums::CardNetwork); + fn set_card_network(&mut self, card_network: enums::CardNetwork); + fn set_co_badged_card_data( + &mut self, + co_badged_card_data: api_models::payment_methods::CoBadgedCardData, + ); #[cfg(feature = "v1")] fn set_capture_method_in_attempt(&mut self, capture_method: enums::CaptureMethod); fn set_frm_message(&mut self, frm_message: FraudCheck); @@ -8306,12 +8308,27 @@ impl OperationSessionSetters for PaymentData { self.payment_attempt.merchant_connector_id = merchant_connector_id; } - fn set_local_network(&mut self, local_network: enums::CardNetwork) { + fn set_card_network(&mut self, card_network: enums::CardNetwork) { if let Some(domain::PaymentMethodData::Card(card)) = &mut self.payment_method_data { - card.card_network = Some(local_network); + card.card_network = Some(card_network); }; } + fn set_co_badged_card_data(&mut self, co_badged_card_data: api_models::payment_methods::CoBadgedCardData) { + logger::debug!("set co-badged card data"); + if let Some(domain::PaymentMethodData::Card(card)) = &mut self.payment_method_data { + card.co_badged_card_data = Some(co_badged_card_data); + logger::debug!("set co-badged card data in payment method data"); + } else if let Some(hyperswitch_domain_models::payment_method_data::PaymentMethodsData::Card(card)) = &mut self + .get_payment_method_info() + .and_then(|payment_method_info| payment_method_info.get_payment_methods_data()) + { + card.co_badged_card_data = Some(co_badged_card_data); + logger::debug!("set co-badged card data in payment method info"); + }; + } + + #[cfg(feature = "v1")] fn set_capture_method_in_attempt(&mut self, capture_method: enums::CaptureMethod) { self.payment_attempt.capture_method = Some(capture_method); @@ -8534,7 +8551,14 @@ impl OperationSessionSetters for PaymentIntentData { todo!() } - fn set_local_network(&mut self, local_network: enums::CardNetwork) { + fn set_card_network(&mut self, card_network: enums::CardNetwork) { + todo!() + } + + fn set_co_badged_card_data( + &mut self, + co_badged_card_data: api_models::payment_methods::CoBadgedCardData, + ) { todo!() } @@ -8775,7 +8799,14 @@ impl OperationSessionSetters for PaymentConfirmData { todo!() } - fn set_local_network(&mut self, local_network: enums::CardNetwork) { + fn set_card_network(&mut self, card_network: enums::CardNetwork) { + todo!() + } + + fn set_co_badged_card_data( + &mut self, + co_badged_card_data: api_models::payment_methods::CoBadgedCardData, + ) { todo!() } @@ -8988,7 +9019,14 @@ impl OperationSessionSetters for PaymentStatusData { todo!() } - fn set_local_network(&mut self, local_network: enums::CardNetwork) { + fn set_card_network(&mut self, card_network: enums::CardNetwork) { + todo!() + } + + fn set_co_badged_card_data( + &mut self, + co_badged_card_data: api_models::payment_methods::CoBadgedCardData, + ) { todo!() } @@ -9211,7 +9249,14 @@ impl OperationSessionSetters for PaymentCaptureData { todo!() } - fn set_local_network(&mut self, local_network: enums::CardNetwork) { + fn set_card_network(&mut self, card_network: enums::CardNetwork) { + todo!() + } + + fn set_co_badged_card_data( + &mut self, + co_badged_card_data: api_models::payment_methods::CoBadgedCardData, + ) { todo!() } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 43ed8253ec0..d05ee1483db 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2116,6 +2116,11 @@ pub async fn retrieve_payment_method_data_with_permanent_token( .network_token_requestor_reference_id .clone(), ); + + let co_badged_card_data = payment_method_info + .get_payment_methods_data() + .and_then(|payment_methods_data| payment_methods_data.get_co_badged_card_data()); + match vault_fetch_action { VaultFetchAction::FetchCardDetailsFromLocker => { let card = vault_data @@ -2128,6 +2133,7 @@ pub async fn retrieve_payment_method_data_with_permanent_token( &payment_intent.merchant_id, locker_id, card_token_data, + co_badged_card_data, ) .await }) @@ -2178,6 +2184,7 @@ pub async fn retrieve_payment_method_data_with_permanent_token( &payment_intent.merchant_id, locker_id, card_token_data, + co_badged_card_data, ) .await }) @@ -2242,6 +2249,7 @@ pub async fn retrieve_card_with_permanent_token_for_external_authentication( &payment_intent.merchant_id, locker_id, card_token_data, + None, ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2259,7 +2267,9 @@ pub async fn fetch_card_details_from_locker( merchant_id: &id_type::MerchantId, locker_id: &str, card_token_data: Option<&domain::CardToken>, + co_badged_card_data: Option, ) -> RouterResult { + logger::debug!("Fetching card details from locker"); let card = cards::get_card_from_locker(state, customer_id, merchant_id, locker_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2304,7 +2314,7 @@ pub async fn fetch_card_details_from_locker( card_issuing_country: None, bank_code: None, }; - Ok(api_card.into()) + Ok(domain::Card::from((api_card, co_badged_card_data))) } #[cfg(all( diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index 40cd4c81612..1817c05e648 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -195,6 +195,11 @@ where payment_method_billing_address, ) .await?; + let payment_methods_data = + &save_payment_method_data.request.get_payment_method_data(); + + let co_badged_card_data = payment_methods_data.get_co_badged_card_data(); + let customer_id = customer_id.to_owned().get_required_value("customer_id")?; let merchant_id = merchant_context.get_merchant_account().get_id(); let is_network_tokenization_enabled = @@ -263,7 +268,7 @@ where save_payment_method_data.request.get_payment_method_data(), ) { (Some(card), _) => Some(PaymentMethodsData::Card( - CardDetailsPaymentMethod::from(card.clone()), + CardDetailsPaymentMethod::from((card.clone(), co_badged_card_data)), )), ( _, @@ -294,7 +299,10 @@ where > = match network_token_resp { Some(token_resp) => { let pm_token_details = token_resp.card.as_ref().map(|card| { - PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone())) + PaymentMethodsData::Card(CardDetailsPaymentMethod::from(( + card.clone(), + None, + ))) }); pm_token_details @@ -641,9 +649,10 @@ where }); let updated_pmd = updated_card.as_ref().map(|card| { - PaymentMethodsData::Card(CardDetailsPaymentMethod::from( + PaymentMethodsData::Card(CardDetailsPaymentMethod::from(( card.clone(), - )) + None, + ))) }); let pm_data_encrypted: Option< Encryptable>, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 2cbb532bc35..8b71c02349b 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -494,6 +494,7 @@ pub async fn save_payout_data_to_locker( card_network: card_info.card_network, card_type: card_info.card_type, saved_to_locker: true, + co_badged_card_data: None, }, ) }) @@ -514,6 +515,7 @@ pub async fn save_payout_data_to_locker( card_network: None, card_type: None, saved_to_locker: true, + co_badged_card_data: None, }, ) }); diff --git a/crates/router/src/utils/verify_connector.rs b/crates/router/src/utils/verify_connector.rs index b13ba70e6fb..0f5d9e6b318 100644 --- a/crates/router/src/utils/verify_connector.rs +++ b/crates/router/src/utils/verify_connector.rs @@ -24,6 +24,7 @@ pub fn generate_card_from_details( card_issuing_country: None, bank_code: None, card_holder_name: None, + co_badged_card_data: None, }) } diff --git a/crates/router/tests/connectors/aci.rs b/crates/router/tests/connectors/aci.rs index 8b3372d7dec..3497a048a8a 100644 --- a/crates/router/tests/connectors/aci.rs +++ b/crates/router/tests/connectors/aci.rs @@ -52,6 +52,7 @@ fn construct_payment_router_data() -> types::PaymentsAuthorizeRouterData { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), confirm: true, statement_descriptor_suffix: None, @@ -302,6 +303,7 @@ async fn payments_create_failure() { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }); let response = services::api::execute_connector_processing_step( diff --git a/crates/router/tests/connectors/adyen.rs b/crates/router/tests/connectors/adyen.rs index c7ad3b69a32..d7017d3d44e 100644 --- a/crates/router/tests/connectors/adyen.rs +++ b/crates/router/tests/connectors/adyen.rs @@ -154,6 +154,7 @@ impl AdyenTest { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), confirm: true, statement_descriptor_suffix: None, diff --git a/crates/router/tests/connectors/airwallex.rs b/crates/router/tests/connectors/airwallex.rs index 0e538818d50..bf5a06f157a 100644 --- a/crates/router/tests/connectors/airwallex.rs +++ b/crates/router/tests/connectors/airwallex.rs @@ -83,6 +83,7 @@ fn payment_method_details() -> Option { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), router_return_url: Some("https://google.com".to_string()), diff --git a/crates/router/tests/connectors/fiserv.rs b/crates/router/tests/connectors/fiserv.rs index 78235278ed9..77ab8223145 100644 --- a/crates/router/tests/connectors/fiserv.rs +++ b/crates/router/tests/connectors/fiserv.rs @@ -54,6 +54,7 @@ fn payment_method_details() -> Option { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 diff --git a/crates/router/tests/connectors/rapyd.rs b/crates/router/tests/connectors/rapyd.rs index ee6ac303e88..e173f4753d6 100644 --- a/crates/router/tests/connectors/rapyd.rs +++ b/crates/router/tests/connectors/rapyd.rs @@ -54,6 +54,7 @@ async fn should_only_authorize_payment() { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), capture_method: Some(diesel_models::enums::CaptureMethod::Manual), ..utils::PaymentAuthorizeType::default().0 @@ -82,6 +83,7 @@ async fn should_authorize_and_capture_payment() { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), ..utils::PaymentAuthorizeType::default().0 }), diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index ede3aeb17db..d6925ae1667 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -941,6 +941,7 @@ impl Default for CCardType { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }) } } diff --git a/crates/router/tests/connectors/worldline.rs b/crates/router/tests/connectors/worldline.rs index 793a1cc15f4..22fd198bd0a 100644 --- a/crates/router/tests/connectors/worldline.rs +++ b/crates/router/tests/connectors/worldline.rs @@ -84,6 +84,7 @@ impl WorldlineTest { bank_code: None, nick_name: Some(Secret::new("nick_name".into())), card_holder_name: Some(Secret::new("card holder name".into())), + co_badged_card_data: None, }), confirm: true, statement_descriptor_suffix: None,