refactor(connector): remove peek() on PII info (#642)

Co-authored-by: Nishant Joshi <nishant.joshi@juspay.in>
This commit is contained in:
Jagan
2023-02-25 18:22:01 +05:30
committed by GitHub
parent d27e6be599
commit 46f77d078b
15 changed files with 113 additions and 110 deletions

View File

@ -59,6 +59,28 @@ where
masking_strategy: PhantomData,
}
}
/// Zip 2 secrets with the same masking strategy into one
pub fn zip<OtherSecretValue>(
self,
other: Secret<OtherSecretValue, MaskingStrategy>,
) -> Secret<(SecretValue, OtherSecretValue), MaskingStrategy>
where
MaskingStrategy: Strategy<OtherSecretValue> + Strategy<(SecretValue, OtherSecretValue)>,
{
(self.inner_secret, other.inner_secret).into()
}
/// consume self and modify the inner value
pub fn map<OtherSecretValue>(
self,
f: impl FnOnce(SecretValue) -> OtherSecretValue,
) -> Secret<OtherSecretValue, MaskingStrategy>
where
MaskingStrategy: Strategy<OtherSecretValue>,
{
f(self.inner_secret).into()
}
}
impl<SecretValue, MaskingStrategy> PeekInterface<SecretValue>

View File

@ -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<String, common_utils::pii::CardNumber>,
#[serde(rename = "card.holder")]
pub card_holder: String,
pub card_holder: Secret<String>,
#[serde(rename = "card.expiryMonth")]
pub card_expiry_month: String,
pub card_expiry_month: Secret<String>,
#[serde(rename = "card.expiryYear")]
pub card_expiry_year: String,
pub card_expiry_year: Secret<String>,
#[serde(rename = "card.cvv")]
pub card_cvv: String,
pub card_cvv: Secret<String>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
@ -100,13 +100,13 @@ pub enum AciPaymentType {
impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
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(),

View File

@ -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<api_models::payments::PaymentMethod> 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()),
})
}

View File

@ -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<String, common_utils::pii::CardNumber>,
expiration_month: Secret<String>,
expiration_year: Secret<String>,
cvv: Secret<String>,
}
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) => {

View File

@ -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<String, pii::CardNumber>,
expiration_month: Secret<String>,
expiration_year: Secret<String>,
security_code: Secret<String>,
}
#[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<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
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,
},
};

View File

@ -596,12 +596,11 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
.response
.parse_struct("globalpay RefundResponse")
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
types::ResponseRouterData {
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
}
.try_into()
})
.change_context(errors::ConnectorError::ResponseHandlingFailed)
}

View File

@ -1,3 +1,5 @@
use common_utils::pii;
use masking::Secret;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Serialize)]
@ -298,18 +300,18 @@ pub struct Card {
/// did not work as expected.
pub chip_condition: Option<ChipCondition>,
/// The numeric value printed on the physical card.
pub cvv: String,
pub cvv: Secret<String>,
/// 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<String>,
/// The 2 digit expiry date year of the card.
pub expiry_year: String,
pub expiry_year: Secret<String>,
/// Indicates whether the card is a debit or credit card.
pub funding: Option<Funding>,
/// The the card account number used to authorize the transaction. Also known as PAN.
pub number: String,
pub number: Secret<String, pii::CardNumber>,
/// Contains the pin block info, relating to the pin code the Payer entered.
pub pin_block: Option<String>,
/// The full card tag data for an EMV/chip card transaction.

View File

@ -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()

View File

@ -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<String, common_utils::pii::CardNumber>,
exp_month: Secret<String>,
exp_year: Secret<String>,
cardholder_name: Secret<String>,
}
impl TryFrom<&types::PaymentsAuthorizeRouterData> for Shift4PaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
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(),

View File

@ -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<String>;
}
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<String> {
let binding = self.card_exp_year.clone();
let year = binding.peek();
Secret::new(year[year.len() - 2..].to_string())
}
}
pub trait PhoneDetailsData {

View File

@ -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<String>,
pub card_expiry_date: CardExpiryDate,
#[serde(skip_serializing_if = "Option::is_none")]
pub cvc: Option<String>,
pub cvc: Option<Secret<String>>,
#[serde(rename = "type")]
pub payment_type: PaymentType,
pub card_number: String,
pub card_number: Secret<String, pii::CardNumber>,
}
#[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<String>,
pub year: Secret<String>,
}
#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)]

View File

@ -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<T: FromStr>(
val: masking::Secret<String, masking::WithType>,
) -> CustomResult<T, errors::ConnectorError>
where
<T as FromStr>::Err: Sync,
{
let res = val.peek().parse::<T>();
if let Ok(val) = res {
Ok(val)
} else {
Err(errors::ConnectorError::RequestEncodingFailed)?
}
}
fn fetch_payment_instrument(
payment_method: api::PaymentMethod,
) -> CustomResult<PaymentInstrument, errors::ConnectorError> {
match payment_method {
api::PaymentMethod::Card(card) => Ok(PaymentInstrument::Card(CardPayment {
card_expiry_date: CardExpiryDate {
month: parse_int::<u8>(card.card_exp_month)?,
year: parse_int::<u16>(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 {

View File

@ -88,6 +88,7 @@ pub enum ApiErrorResponse {
message: String,
connector: String,
status_code: u16,
reason: Option<String>,
},
#[error(error_type = ErrorType::ProcessingError, code = "CE_01", message = "Payment failed during authorization with connector. Retry payment")]
PaymentAuthorizationFailed { data: Option<serde_json::Value> },
@ -340,8 +341,9 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon
code,
message,
connector,
reason,
status_code,
} => 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()})))
}

View File

@ -68,6 +68,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData
message: error_response.message,
connector,
status_code: error_response.status_code,
reason: error_response.reason,
})
})
})?;
@ -126,6 +127,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, 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<F: Clone> PostUpdateTracker<F, PaymentData<F>, 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<F: Clone> PostUpdateTracker<F, PaymentData<F>, 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<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::VerifyRequestData> fo
message: error_response.message,
code: error_response.code,
status_code: error_response.status_code,
reason: error_response.reason,
connector,
}
})?;

View File

@ -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<PaymentInfo> {
}),
..Default::default()
}),
access_token: Some(types::AccessToken {
token: "<access_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();