diff --git a/crates/masking/src/secret.rs b/crates/masking/src/secret.rs index a5ab4103f5..96411d4632 100644 --- a/crates/masking/src/secret.rs +++ b/crates/masking/src/secret.rs @@ -59,6 +59,28 @@ where masking_strategy: PhantomData, } } + + /// Zip 2 secrets with the same masking strategy into one + pub fn zip( + self, + other: Secret, + ) -> Secret<(SecretValue, OtherSecretValue), MaskingStrategy> + where + MaskingStrategy: Strategy + Strategy<(SecretValue, OtherSecretValue)>, + { + (self.inner_secret, other.inner_secret).into() + } + + /// consume self and modify the inner value + pub fn map( + self, + f: impl FnOnce(SecretValue) -> OtherSecretValue, + ) -> Secret + where + MaskingStrategy: Strategy, + { + f(self.inner_secret).into() + } } impl PeekInterface diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index 2f681a72f7..ffab313bde 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -1,12 +1,12 @@ use std::str::FromStr; use error_stack::report; +use masking::Secret; use serde::{Deserialize, Serialize}; use super::result_codes::{FAILURE_CODES, PENDING_CODES, SUCCESSFUL_CODES}; use crate::{ core::errors, - pii::PeekInterface, types::{self, api, storage::enums}, }; @@ -62,15 +62,15 @@ pub enum PaymentDetails { #[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct CardDetails { #[serde(rename = "card.number")] - pub card_number: String, + pub card_number: Secret, #[serde(rename = "card.holder")] - pub card_holder: String, + pub card_holder: Secret, #[serde(rename = "card.expiryMonth")] - pub card_expiry_month: String, + pub card_expiry_month: Secret, #[serde(rename = "card.expiryYear")] - pub card_expiry_year: String, + pub card_expiry_year: Secret, #[serde(rename = "card.cvv")] - pub card_cvv: String, + pub card_cvv: Secret, } #[derive(Debug, Clone, Eq, PartialEq, Serialize)] @@ -100,13 +100,13 @@ pub enum AciPaymentType { impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { - let payment_details: PaymentDetails = match item.request.payment_method_data { - api::PaymentMethod::Card(ref ccard) => PaymentDetails::Card(CardDetails { - card_number: ccard.card_number.peek().clone(), - card_holder: ccard.card_holder_name.peek().clone(), - card_expiry_month: ccard.card_exp_month.peek().clone(), - card_expiry_year: ccard.card_exp_year.peek().clone(), - card_cvv: ccard.card_cvc.peek().clone(), + let payment_details: PaymentDetails = match item.request.payment_method_data.clone() { + api::PaymentMethod::Card(ccard) => PaymentDetails::Card(CardDetails { + card_number: ccard.card_number, + card_holder: ccard.card_holder_name, + card_expiry_month: ccard.card_exp_month, + card_expiry_year: ccard.card_exp_year, + card_cvv: ccard.card_cvc, }), api::PaymentMethod::BankTransfer => PaymentDetails::BankAccount(BankDetails { account_holder: "xyz".to_string(), diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 027ecd30c9..b0a744d128 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize}; use crate::{ connector::utils::RefundsRequestData, core::errors, - pii::PeekInterface, types::{self, api, storage::enums}, utils::OptionExt, }; @@ -71,12 +70,14 @@ impl From for PaymentDetails { fn from(value: api_models::payments::PaymentMethod) -> Self { match value { api::PaymentMethod::Card(ref ccard) => { - let expiry_month = ccard.card_exp_month.peek().clone(); - let expiry_year = ccard.card_exp_year.peek().clone(); - Self::CreditCard(CreditCardDetails { card_number: ccard.card_number.clone(), - expiration_date: format!("{expiry_year}-{expiry_month}").into(), + // expiration_date: format!("{expiry_year}-{expiry_month}").into(), + expiration_date: ccard + .card_exp_month + .clone() + .zip(ccard.card_exp_year.clone()) + .map(|(expiry_month, expiry_year)| format!("{expiry_year}-{expiry_month}")), card_code: Some(ccard.card_cvc.clone()), }) } diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index 09962ecd78..cd116779de 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -1,12 +1,12 @@ use api_models::payments; use base64::Engine; use error_stack::ResultExt; +use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ consts, core::errors, - pii::PeekInterface, types::{self, api, storage::enums}, utils::OptionExt, }; @@ -79,10 +79,10 @@ pub struct Card { #[derive(Default, Debug, Serialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct CardDetails { - number: String, - expiration_month: String, - expiration_year: String, - cvv: String, + number: Secret, + expiration_month: Secret, + expiration_year: Secret, + cvv: Secret, } impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { @@ -100,13 +100,13 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { }; let kind = "sale".to_string(); - let payment_method_data_type = match item.request.payment_method_data { - api::PaymentMethod::Card(ref ccard) => Ok(PaymentMethodType::CreditCard(Card { + let payment_method_data_type = match item.request.payment_method_data.clone() { + api::PaymentMethod::Card(ccard) => Ok(PaymentMethodType::CreditCard(Card { credit_card: CardDetails { - number: ccard.card_number.peek().clone(), - expiration_month: ccard.card_exp_month.peek().clone(), - expiration_year: ccard.card_exp_year.peek().clone(), - cvv: ccard.card_cvc.peek().clone(), + number: ccard.card_number, + expiration_month: ccard.card_exp_month, + expiration_year: ccard.card_exp_year, + cvv: ccard.card_cvc, }, })), api::PaymentMethod::Wallet(ref wallet_data) => { diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 23f555d364..6dfe630ccf 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -41,10 +41,10 @@ pub struct PaymentInformation { #[derive(Default, Debug, Serialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Card { - number: String, - expiration_month: String, - expiration_year: String, - security_code: String, + number: Secret, + expiration_month: Secret, + expiration_year: Secret, + security_code: Secret, } #[derive(Default, Debug, Serialize, Eq, PartialEq)] @@ -107,8 +107,8 @@ fn build_bill_to( impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { - match item.request.payment_method_data { - api::PaymentMethod::Card(ref ccard) => { + match item.request.payment_method_data.clone() { + api::PaymentMethod::Card(ccard) => { let phone = item.get_billing_phone()?; let phone_number = phone.get_number()?; let country_code = phone.get_country_code()?; @@ -131,10 +131,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for CybersourcePaymentsRequest let payment_information = PaymentInformation { card: Card { - number: ccard.card_number.peek().clone(), - expiration_month: ccard.card_exp_month.peek().clone(), - expiration_year: ccard.card_exp_year.peek().clone(), - security_code: ccard.card_cvc.peek().clone(), + number: ccard.card_number, + expiration_month: ccard.card_exp_month, + expiration_year: ccard.card_exp_year, + security_code: ccard.card_cvc, }, }; diff --git a/crates/router/src/connector/globalpay.rs b/crates/router/src/connector/globalpay.rs index 580dd3fccc..61844e64e3 100644 --- a/crates/router/src/connector/globalpay.rs +++ b/crates/router/src/connector/globalpay.rs @@ -596,12 +596,11 @@ impl ConnectorIntegration, /// The numeric value printed on the physical card. - pub cvv: String, + pub cvv: Secret, /// Card Verification Value Indicator sent by the Merchant indicating the CVV /// availability. pub cvv_indicator: CvvIndicator, /// The 2 digit expiry date month of the card. - pub expiry_month: String, + pub expiry_month: Secret, /// The 2 digit expiry date year of the card. - pub expiry_year: String, + pub expiry_year: Secret, /// Indicates whether the card is a debit or credit card. pub funding: Option, /// The the card account number used to authorize the transaction. Also known as PAN. - pub number: String, + pub number: Secret, /// Contains the pin block info, relating to the pin code the Payer entered. pub pin_block: Option, /// The full card tag data for an EMV/chip card transaction. diff --git a/crates/router/src/connector/globalpay/transformers.rs b/crates/router/src/connector/globalpay/transformers.rs index 717cd1e9ef..258d066629 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -27,6 +27,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest { .map(|o| o.to_string()) .ok_or_else(utils::missing_field_err("connector_meta.account_name"))?; let card = item.get_card()?; + let expiry_year = card.get_card_expiry_year_2_digit(); Ok(Self { account_name, amount: Some(item.request.amount.to_string()), @@ -39,10 +40,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest { }), payment_method: requests::PaymentMethod { card: Some(requests::Card { - number: card.get_card_number(), - expiry_month: card.get_card_expiry_month(), - expiry_year: card.get_card_expiry_year_2_digit(), - cvv: card.get_card_cvc(), + number: card.card_number, + expiry_month: card.card_exp_month, + expiry_year, + cvv: card.card_cvc, ..Default::default() }), ..Default::default() diff --git a/crates/router/src/connector/shift4/transformers.rs b/crates/router/src/connector/shift4/transformers.rs index bb744fe067..76370d53c0 100644 --- a/crates/router/src/connector/shift4/transformers.rs +++ b/crates/router/src/connector/shift4/transformers.rs @@ -1,8 +1,8 @@ +use masking::Secret; use serde::{Deserialize, Serialize}; use crate::{ core::errors, - pii::PeekInterface, types::{self, api, storage::enums}, }; @@ -22,17 +22,17 @@ pub struct DeviceData; #[derive(Default, Debug, Serialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Card { - number: String, - exp_month: String, - exp_year: String, - cardholder_name: String, + number: Secret, + exp_month: Secret, + exp_year: Secret, + cardholder_name: Secret, } impl TryFrom<&types::PaymentsAuthorizeRouterData> for Shift4PaymentsRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { - match item.request.payment_method_data { - api::PaymentMethod::Card(ref ccard) => { + match item.request.payment_method_data.clone() { + api::PaymentMethod::Card(ccard) => { let submit_for_settlement = matches!( item.request.capture_method, Some(enums::CaptureMethod::Automatic) | None @@ -40,10 +40,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for Shift4PaymentsRequest { let payment_request = Self { amount: item.request.amount.to_string(), card: Card { - number: ccard.card_number.peek().clone(), - exp_month: ccard.card_exp_month.peek().clone(), - exp_year: ccard.card_exp_year.peek().clone(), - cardholder_name: ccard.card_holder_name.peek().clone(), + number: ccard.card_number, + exp_month: ccard.card_exp_month, + exp_year: ccard.card_exp_year, + cardholder_name: ccard.card_holder_name, }, currency: item.request.currency.to_string(), description: item.description.clone(), diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 605822a96c..bab9d21cd1 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -94,29 +94,14 @@ impl PaymentsRequestData for types::PaymentsAuthorizeRouterData { } pub trait CardData { - fn get_card_number(&self) -> String; - fn get_card_expiry_month(&self) -> String; - fn get_card_expiry_year(&self) -> String; - fn get_card_expiry_year_2_digit(&self) -> String; - fn get_card_cvc(&self) -> String; + fn get_card_expiry_year_2_digit(&self) -> Secret; } impl CardData for api::Card { - fn get_card_number(&self) -> String { - self.card_number.peek().clone() - } - fn get_card_expiry_month(&self) -> String { - self.card_exp_month.peek().clone() - } - fn get_card_expiry_year(&self) -> String { - self.card_exp_year.peek().clone() - } - fn get_card_expiry_year_2_digit(&self) -> String { - let year = self.card_exp_year.peek().clone(); - year[year.len() - 2..].to_string() - } - fn get_card_cvc(&self) -> String { - self.card_cvc.peek().clone() + fn get_card_expiry_year_2_digit(&self) -> Secret { + let binding = self.card_exp_year.clone(); + let year = binding.peek(); + Secret::new(year[year.len() - 2..].to_string()) } } pub trait PhoneDetailsData { diff --git a/crates/router/src/connector/worldpay/requests.rs b/crates/router/src/connector/worldpay/requests.rs index a76b02d7cf..757942a535 100644 --- a/crates/router/src/connector/worldpay/requests.rs +++ b/crates/router/src/connector/worldpay/requests.rs @@ -1,3 +1,5 @@ +use common_utils::pii; +use masking::Secret; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -148,10 +150,10 @@ pub struct CardPayment { pub card_holder_name: Option, pub card_expiry_date: CardExpiryDate, #[serde(skip_serializing_if = "Option::is_none")] - pub cvc: Option, + pub cvc: Option>, #[serde(rename = "type")] pub payment_type: PaymentType, - pub card_number: String, + pub card_number: Secret, } #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] @@ -174,8 +176,8 @@ pub struct WalletPayment { #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] pub struct CardExpiryDate { - pub month: u8, - pub year: u16, + pub month: Secret, + pub year: Secret, } #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] diff --git a/crates/router/src/connector/worldpay/transformers.rs b/crates/router/src/connector/worldpay/transformers.rs index 19b297b0d1..de16075684 100644 --- a/crates/router/src/connector/worldpay/transformers.rs +++ b/crates/router/src/connector/worldpay/transformers.rs @@ -1,8 +1,5 @@ -use std::str::FromStr; - use common_utils::errors::CustomResult; use error_stack::ResultExt; -use masking::PeekInterface; use storage_models::enums; use super::{requests::*, response::*}; @@ -12,30 +9,16 @@ use crate::{ utils::OptionExt, }; -fn parse_int( - val: masking::Secret, -) -> CustomResult -where - ::Err: Sync, -{ - let res = val.peek().parse::(); - if let Ok(val) = res { - Ok(val) - } else { - Err(errors::ConnectorError::RequestEncodingFailed)? - } -} - fn fetch_payment_instrument( payment_method: api::PaymentMethod, ) -> CustomResult { match payment_method { api::PaymentMethod::Card(card) => Ok(PaymentInstrument::Card(CardPayment { card_expiry_date: CardExpiryDate { - month: parse_int::(card.card_exp_month)?, - year: parse_int::(card.card_exp_year)?, + month: card.card_exp_month, + year: card.card_exp_year, }, - card_number: card.card_number.peek().to_string(), + card_number: card.card_number, ..CardPayment::default() })), api::PaymentMethod::Wallet(wallet) => match wallet.issuer_name { diff --git a/crates/router/src/core/errors/api_error_response.rs b/crates/router/src/core/errors/api_error_response.rs index e4adf87673..1aa55720e3 100644 --- a/crates/router/src/core/errors/api_error_response.rs +++ b/crates/router/src/core/errors/api_error_response.rs @@ -88,6 +88,7 @@ pub enum ApiErrorResponse { message: String, connector: String, status_code: u16, + reason: Option, }, #[error(error_type = ErrorType::ProcessingError, code = "CE_01", message = "Payment failed during authorization with connector. Retry payment")] PaymentAuthorizationFailed { data: Option }, @@ -340,8 +341,9 @@ impl common_utils::errors::ErrorSwitch AER::ConnectorError(ApiError::new("CE", 0, format!("{code}: {message}"), Some(Extra {connector: Some(connector.clone()), ..Default::default()})), StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)), + } => AER::ConnectorError(ApiError::new("CE", 0, format!("{code}: {message}"), Some(Extra {connector: Some(connector.clone()), reason: reason.clone(), ..Default::default()})), StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)), Self::PaymentAuthorizationFailed { data } => { AER::BadRequest(ApiError::new("CE", 1, "Payment failed during authorization with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()}))) } diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index 6f8761363e..5dc58a076d 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -68,6 +68,7 @@ impl PostUpdateTracker, types::PaymentsAuthorizeData message: error_response.message, connector, status_code: error_response.status_code, + reason: error_response.reason, }) }) })?; @@ -126,6 +127,7 @@ impl PostUpdateTracker, types::PaymentsSessionData> message: error_response.message, code: error_response.code, status_code: error_response.status_code, + reason: error_response.reason, connector, } })?; @@ -166,6 +168,7 @@ impl PostUpdateTracker, types::PaymentsCaptureData> message: error_response.message, code: error_response.code, status_code: error_response.status_code, + reason: error_response.reason, connector, } })?; @@ -205,6 +208,7 @@ impl PostUpdateTracker, types::PaymentsCancelData> f message: error_response.message, code: error_response.code, status_code: error_response.status_code, + reason: error_response.reason, connector, } })?; @@ -249,6 +253,7 @@ impl PostUpdateTracker, types::VerifyRequestData> fo message: error_response.message, code: error_response.code, status_code: error_response.status_code, + reason: error_response.reason, connector, } })?; diff --git a/crates/router/tests/connectors/globalpay.rs b/crates/router/tests/connectors/globalpay.rs index 971f42f3bc..204c300ca2 100644 --- a/crates/router/tests/connectors/globalpay.rs +++ b/crates/router/tests/connectors/globalpay.rs @@ -1,5 +1,3 @@ -use std::{thread::sleep, time::Duration}; - use masking::Secret; use router::types::{ self, @@ -54,6 +52,10 @@ fn get_default_payment_info() -> Option { }), ..Default::default() }), + access_token: Some(types::AccessToken { + token: "".to_string(), + expires: 18600, + }), ..Default::default() }) } @@ -100,7 +102,6 @@ async fn should_sync_payment() { .await .unwrap(); let txn_id = utils::get_connector_transaction_id(authorize_response.response); - sleep(Duration::from_secs(5)); // to avoid 404 error as globalpay takes some time to process the new transaction let response = connector .psync_retry_till_status_matches( enums::AttemptStatus::Authorized, @@ -111,7 +112,7 @@ async fn should_sync_payment() { encoded_data: None, capture_method: None, }), - None, + get_default_payment_info(), ) .await .unwrap();