feat(connector): mask pii information in connector request and response for stripe, bluesnap, checkout, zen (#1435)

This commit is contained in:
Arjun Karthik
2023-06-14 17:43:27 +05:30
committed by GitHub
parent fda3fb4d2b
commit 5535159d5c
6 changed files with 111 additions and 111 deletions

View File

@ -10,7 +10,9 @@ use masking::ExposeInterface;
use serde::{Deserialize, Serialize};
use crate::{
connector::utils::{self, AddressDetailsData, PaymentsAuthorizeRequestData, RouterData},
connector::utils::{
self, AddressDetailsData, ApplePay, PaymentsAuthorizeRequestData, RouterData,
},
consts,
core::errors,
pii::Secret,
@ -80,7 +82,7 @@ pub struct Card {
#[serde(rename_all = "camelCase")]
pub struct BluesnapWallet {
wallet_type: BluesnapWalletTypes,
encoded_payment_token: String,
encoded_payment_token: Secret<String>,
}
#[derive(Debug, Serialize)]
@ -182,21 +184,22 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
Ok((
PaymentMethodDetails::Wallet(BluesnapWallet {
wallet_type: BluesnapWalletTypes::GooglePay,
encoded_payment_token: consts::BASE64_ENGINE.encode(gpay_object),
encoded_payment_token: Secret::new(
consts::BASE64_ENGINE.encode(gpay_object),
),
}),
None,
))
}
api_models::payments::WalletData::ApplePay(payment_method_data) => {
let apple_pay_payment_data = consts::BASE64_ENGINE
.decode(payment_method_data.payment_data)
.into_report()
.change_context(errors::ConnectorError::ParsingFailed)?;
let apple_pay_payment_data = payment_method_data
.get_applepay_decoded_payment_data()
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
let apple_pay_payment_data: ApplePayEncodedPaymentData = apple_pay_payment_data
[..]
.expose()[..]
.as_bytes()
.parse_struct("ApplePayEncodedPaymentData")
.change_context(errors::ConnectorError::ParsingFailed)?;
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
let billing = item
.address
@ -249,7 +252,9 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BluesnapPaymentsRequest {
Ok((
PaymentMethodDetails::Wallet(BluesnapWallet {
wallet_type: BluesnapWalletTypes::ApplePay,
encoded_payment_token: consts::BASE64_ENGINE.encode(apple_pay_object),
encoded_payment_token: Secret::new(
consts::BASE64_ENGINE.encode(apple_pay_object),
),
}),
None,
))
@ -528,7 +533,7 @@ impl TryFrom<&types::ConnectorCustomerRouterData> for BluesnapCustomerRequest {
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BluesnapCustomerResponse {
vaulted_shopper_id: u64,
vaulted_shopper_id: Secret<u64>,
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, BluesnapCustomerResponse, T, types::PaymentsResponseData>>
@ -545,7 +550,7 @@ impl<F, T>
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::PaymentsResponseData::ConnectorCustomerResponse {
connector_customer_id: item.response.vaulted_shopper_id.to_string(),
connector_customer_id: item.response.vaulted_shopper_id.expose().to_string(),
}),
..item.data
})
@ -636,7 +641,7 @@ pub struct Refund {
pub struct ProcessingInfoResponse {
processing_status: BluesnapProcessingStatus,
authorization_code: Option<String>,
network_transaction_id: Option<String>,
network_transaction_id: Option<Secret<String>>,
}
impl<F, T>

View File

@ -1,5 +1,6 @@
use common_utils::errors::CustomResult;
use error_stack::{IntoReport, ResultExt};
use masking::ExposeInterface;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;
@ -73,7 +74,7 @@ impl TryFrom<&types::TokenizationRouterData> for TokenRequest {
#[derive(Debug, Eq, PartialEq, Deserialize)]
pub struct CheckoutTokenResponse {
token: String,
token: pii::Secret<String>,
}
impl<F, T>
@ -86,7 +87,7 @@ impl<F, T>
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::PaymentsResponseData::TokenizationResponse {
token: item.response.token,
token: item.response.token.expose(),
}),
..item.data
})

View File

@ -1,7 +1,6 @@
use std::ops::Deref;
use api_models::{self, enums as api_enums, payments};
use base64::Engine;
use common_utils::{
errors::CustomResult,
ext_traits::{ByteSliceExt, BytesExt},
@ -15,7 +14,11 @@ use url::Url;
use uuid::Uuid;
use crate::{
collect_missing_value_keys, connector, consts,
collect_missing_value_keys,
connector::{
self,
utils::{ApplePay, RouterData},
},
core::errors,
services,
types::{self, api, storage::enums, transformers::ForeignFrom},
@ -99,9 +102,9 @@ pub struct PaymentIntentRequest {
pub metadata_txn_uuid: String,
pub return_url: String,
pub confirm: bool,
pub mandate: Option<String>,
pub mandate: Option<Secret<String>>,
pub payment_method: Option<String>,
pub customer: Option<String>,
pub customer: Option<Secret<String>>,
#[serde(flatten)]
pub setup_mandate_details: Option<StripeMandateRequest>,
pub description: Option<String>,
@ -127,7 +130,7 @@ pub struct SetupIntentRequest {
pub metadata_txn_uuid: String,
pub confirm: bool,
pub usage: Option<enums::FutureUsage>,
pub customer: Option<String>,
pub customer: Option<Secret<String>>,
pub off_session: Option<bool>,
pub return_url: Option<String>,
#[serde(flatten)]
@ -177,7 +180,7 @@ pub struct CustomerRequest {
pub description: Option<String>,
pub email: Option<Email>,
pub phone: Option<Secret<String>>,
pub name: Option<String>,
pub name: Option<Secret<String>>,
pub source: Option<String>,
}
@ -187,15 +190,15 @@ pub struct StripeCustomerResponse {
pub description: Option<String>,
pub email: Option<Email>,
pub phone: Option<Secret<String>>,
pub name: Option<String>,
pub name: Option<Secret<String>>,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct ChargesRequest {
pub amount: String,
pub currency: String,
pub customer: String,
pub source: String,
pub customer: Secret<String>,
pub source: Secret<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize)]
@ -407,7 +410,7 @@ pub enum StripeWallet {
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct StripeApplePay {
pub pk_token: String,
pub pk_token: Secret<String>,
pub pk_token_instrument_name: String,
pub pk_token_payment_network: String,
pub pk_token_transaction_id: String,
@ -418,13 +421,13 @@ pub struct GooglePayToken {
#[serde(rename = "payment_method_data[type]")]
pub payment_type: StripePaymentMethodType,
#[serde(rename = "payment_method_data[card][token]")]
pub token: String,
pub token: Secret<String>,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct ApplepayPayment {
#[serde(rename = "payment_method_data[card][token]")]
pub token: String,
pub token: Secret<String>,
#[serde(rename = "payment_method_data[type]")]
pub payment_method_types: StripePaymentMethodType,
}
@ -1019,14 +1022,9 @@ fn create_stripe_payment_method(
payments::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
payments::WalletData::ApplePay(applepay_data) => Ok((
StripePaymentMethodData::Wallet(StripeWallet::ApplepayToken(StripeApplePay {
pk_token: String::from_utf8(
consts::BASE64_ENGINE
.decode(&applepay_data.payment_data)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
pk_token: applepay_data
.get_applepay_decoded_payment_data()
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
pk_token_instrument_name: applepay_data.payment_method.pm_type.to_owned(),
pk_token_payment_network: applepay_data.payment_method.network.to_owned(),
pk_token_transaction_id: applepay_data.transaction_identifier.to_owned(),
@ -1144,13 +1142,15 @@ impl TryFrom<&payments::GooglePayWalletData> for StripePaymentMethodData {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(gpay_data: &payments::GooglePayWalletData) -> Result<Self, Self::Error> {
Ok(Self::Wallet(StripeWallet::GooglepayToken(GooglePayToken {
token: gpay_data
.tokenization_data
.token
.as_bytes()
.parse_struct::<StripeGpayToken>("StripeGpayToken")
.change_context(errors::ConnectorError::RequestEncodingFailed)?
.id,
token: Secret::new(
gpay_data
.tokenization_data
.token
.as_bytes()
.parse_struct::<StripeGpayToken>("StripeGpayToken")
.change_context(errors::ConnectorError::RequestEncodingFailed)?
.id,
),
payment_type: StripePaymentMethodType::Card,
})))
}
@ -1216,7 +1216,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentIntentRequest {
mandate_options: None,
network_transaction_id: None,
mit_exemption: Some(MitExemption {
network_transaction_id,
network_transaction_id: Secret::new(network_transaction_id),
}),
});
(None, None, None, StripeBillingAddress::default())
@ -1243,11 +1243,12 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentIntentRequest {
payment_data = match item.request.payment_method_data {
payments::PaymentMethodData::Wallet(payments::WalletData::ApplePay(_)) => Some(
StripePaymentMethodData::Wallet(StripeWallet::ApplepayPayment(ApplepayPayment {
token: item
.payment_method_token
.to_owned()
.get_required_value("payment_token")
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
token: Secret::new(
item.payment_method_token
.to_owned()
.get_required_value("payment_token")
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
),
payment_method_types: StripePaymentMethodType::Card,
})),
),
@ -1299,10 +1300,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PaymentIntentRequest {
billing: billing_address,
capture_method: StripeCaptureMethod::from(item.request.capture_method),
payment_data,
mandate,
mandate: mandate.map(Secret::new),
payment_method_options,
payment_method,
customer: item.connector_customer.to_owned(),
customer: item.connector_customer.to_owned().map(Secret::new),
setup_mandate_details,
off_session: item.request.off_session,
setup_future_usage: item.request.setup_future_usage,
@ -1335,7 +1336,7 @@ impl TryFrom<&types::VerifyRouterData> for SetupIntentRequest {
off_session: item.request.off_session,
usage: item.request.setup_future_usage,
payment_method_options: None,
customer: item.connector_customer.to_owned(),
customer: item.connector_customer.to_owned().map(Secret::new),
})
}
}
@ -1362,7 +1363,7 @@ impl TryFrom<&types::ConnectorCustomerRouterData> for CustomerRequest {
description: item.request.description.to_owned(),
email: item.request.email.to_owned(),
phone: item.request.phone.to_owned(),
name: item.request.name.to_owned(),
name: item.request.name.to_owned().map(Secret::new),
source: item.request.preprocessing_id.to_owned(),
})
}
@ -1774,7 +1775,7 @@ impl ForeignFrom<Option<LatestAttempt>> for Option<String> {
StripePaymentMethodOptions::Card {
network_transaction_id,
..
} => network_transaction_id,
} => network_transaction_id.map(|network_id| network_id.expose()),
_ => None,
}),
_ => None,
@ -2059,7 +2060,7 @@ impl TryFrom<&types::PaymentsCancelRouterData> for CancelRequest {
pub enum StripePaymentMethodOptions {
Card {
mandate_options: Option<StripeMandateOptions>,
network_transaction_id: Option<String>,
network_transaction_id: Option<Secret<String>>,
mit_exemption: Option<MitExemption>, // To be used for MIT mandate txns
},
Klarna {},
@ -2087,7 +2088,7 @@ pub enum StripePaymentMethodOptions {
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct MitExemption {
pub network_transaction_id: String,
pub network_transaction_id: Secret<String>,
}
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
@ -2182,20 +2183,8 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for ChargesRequest {
Ok(Self {
amount: value.request.amount.to_string(),
currency: value.request.currency.to_string(),
customer: value
.connector_customer
.to_owned()
.get_required_value("customer_id")
.change_context(errors::ConnectorError::MissingRequiredField {
field_name: "customer_id",
})?,
source: value
.preprocessing_id
.to_owned()
.get_required_value("source")
.change_context(errors::ConnectorError::MissingRequiredField {
field_name: "source",
})?,
customer: Secret::new(value.get_connector_customer_id()?),
source: Secret::new(value.get_preprocessing_id()?),
})
}
}
@ -2506,14 +2495,9 @@ impl
api::PaymentMethodData::Wallet(wallet_data) => match wallet_data {
payments::WalletData::ApplePay(data) => {
let wallet_info = StripeWallet::ApplepayToken(StripeApplePay {
pk_token: String::from_utf8(
consts::BASE64_ENGINE
.decode(data.payment_data)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
)
.into_report()
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
pk_token: data
.get_applepay_decoded_payment_data()
.change_context(errors::ConnectorError::RequestEncodingFailed)?,
pk_token_instrument_name: data.payment_method.pm_type,
pk_token_payment_network: data.payment_method.network,
pk_token_transaction_id: data.transaction_identifier,

View File

@ -1,7 +1,7 @@
use std::collections::HashMap;
use api_models::payments::BankRedirectData;
use common_utils::{errors::CustomResult, pii::Email};
use common_utils::{errors::CustomResult, pii};
use error_stack::{IntoReport, ResultExt};
use masking::Secret;
use reqwest::Url;
@ -131,9 +131,9 @@ pub struct PaymentRequestCards {
#[serde(rename = "billing[postcode]")]
pub billing_postcode: Secret<String>,
#[serde(rename = "customer[email]")]
pub customer_email: Email,
pub customer_email: pii::Email,
#[serde(rename = "customer[ipAddress]")]
pub customer_ip_address: std::net::IpAddr,
pub customer_ip_address: Secret<String, pii::IpAddress>,
#[serde(rename = "browser[acceptHeader]")]
pub browser_accept_header: String,
#[serde(rename = "browser[language]")]

View File

@ -5,7 +5,7 @@ use base64::Engine;
use common_utils::{
date_time,
errors::ReportSwitchExt,
pii::{self, Email},
pii::{self, Email, IpAddress},
};
use error_stack::{report, IntoReport, ResultExt};
use masking::Secret;
@ -64,6 +64,7 @@ pub trait RouterData {
fn get_payment_method_token(&self) -> Result<String, Error>;
fn get_customer_id(&self) -> Result<String, Error>;
fn get_connector_customer_id(&self) -> Result<String, Error>;
fn get_preprocessing_id(&self) -> Result<String, Error>;
}
impl<Flow, Request, Response> RouterData for types::RouterData<Flow, Request, Response> {
@ -157,6 +158,11 @@ impl<Flow, Request, Response> RouterData for types::RouterData<Flow, Request, Re
.to_owned()
.ok_or_else(missing_field_err("connector_customer_id"))
}
fn get_preprocessing_id(&self) -> Result<String, Error> {
self.preprocessing_id
.to_owned()
.ok_or_else(missing_field_err("preprocessing_id"))
}
}
pub trait PaymentsPreProcessingData {
@ -257,13 +263,15 @@ impl PaymentsAuthorizeRequestData for types::PaymentsAuthorizeData {
}
pub trait BrowserInformationData {
fn get_ip_address(&self) -> Result<std::net::IpAddr, Error>;
fn get_ip_address(&self) -> Result<Secret<String, IpAddress>, Error>;
}
impl BrowserInformationData for types::BrowserInformation {
fn get_ip_address(&self) -> Result<std::net::IpAddr, Error> {
self.ip_address
.ok_or_else(missing_field_err("browser_info.ip_address"))
fn get_ip_address(&self) -> Result<Secret<String, IpAddress>, Error> {
let ip_address = self
.ip_address
.ok_or_else(missing_field_err("browser_info.ip_address"))?;
Ok(Secret::new(ip_address.to_string()))
}
}
@ -368,7 +376,7 @@ pub struct GooglePayPaymentMethodInfo {
pub struct GpayTokenizationData {
#[serde(rename = "type")]
pub token_type: String,
pub token: String,
pub token: Secret<String>,
}
impl From<api_models::payments::GooglePayWalletData> for GooglePayWalletData {
@ -382,7 +390,7 @@ impl From<api_models::payments::GooglePayWalletData> for GooglePayWalletData {
},
tokenization_data: GpayTokenizationData {
token_type: data.tokenization_data.token_type,
token: data.tokenization_data.token,
token: Secret::new(data.tokenization_data.token),
},
}
}
@ -488,18 +496,18 @@ fn get_card_issuer(card_number: &str) -> Result<CardIssuer, Error> {
))
}
pub trait WalletData {
fn get_wallet_token(&self) -> Result<String, Error>;
fn get_wallet_token(&self) -> Result<Secret<String>, Error>;
fn get_wallet_token_as_json<T>(&self) -> Result<T, Error>
where
T: serde::de::DeserializeOwned;
}
impl WalletData for api::WalletData {
fn get_wallet_token(&self) -> Result<String, Error> {
fn get_wallet_token(&self) -> Result<Secret<String>, Error> {
match self {
Self::GooglePay(data) => Ok(data.tokenization_data.token.clone()),
Self::GooglePay(data) => Ok(Secret::new(data.tokenization_data.token.clone())),
Self::ApplePay(data) => Ok(data.get_applepay_decoded_payment_data()?),
Self::PaypalSdk(data) => Ok(data.token.clone()),
Self::PaypalSdk(data) => Ok(Secret::new(data.token.clone())),
_ => Err(errors::ConnectorError::InvalidWallet.into()),
}
}
@ -507,26 +515,28 @@ impl WalletData for api::WalletData {
where
T: serde::de::DeserializeOwned,
{
serde_json::from_str::<T>(&self.get_wallet_token()?)
serde_json::from_str::<T>(self.get_wallet_token()?.peek())
.into_report()
.change_context(errors::ConnectorError::InvalidWalletToken)
}
}
pub trait ApplePay {
fn get_applepay_decoded_payment_data(&self) -> Result<String, Error>;
fn get_applepay_decoded_payment_data(&self) -> Result<Secret<String>, Error>;
}
impl ApplePay for payments::ApplePayWalletData {
fn get_applepay_decoded_payment_data(&self) -> Result<String, Error> {
let token = String::from_utf8(
consts::BASE64_ENGINE
.decode(&self.payment_data)
.into_report()
.change_context(errors::ConnectorError::InvalidWalletToken)?,
)
.into_report()
.change_context(errors::ConnectorError::InvalidWalletToken)?;
fn get_applepay_decoded_payment_data(&self) -> Result<Secret<String>, Error> {
let token = Secret::new(
String::from_utf8(
consts::BASE64_ENGINE
.decode(&self.payment_data)
.into_report()
.change_context(errors::ConnectorError::InvalidWalletToken)?,
)
.into_report()
.change_context(errors::ConnectorError::InvalidWalletToken)?,
);
Ok(token)
}
}

View File

@ -1,8 +1,6 @@
use std::net::IpAddr;
use api_models::payments::{ApplePayRedirectData, Card, GooglePayWalletData};
use cards::CardNumber;
use common_utils::{ext_traits::ValueExt, pii::Email};
use common_utils::{ext_traits::ValueExt, pii};
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use ring::digest;
@ -82,8 +80,8 @@ pub enum ZenPaymentChannels {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ZenCustomerDetails {
email: Email,
ip: IpAddr,
email: pii::Email,
ip: Secret<String, pii::IpAddress>,
}
#[derive(Debug, Serialize)]
@ -93,7 +91,7 @@ pub struct ZenPaymentData {
#[serde(rename = "type")]
payment_type: ZenPaymentTypes,
#[serde(skip_serializing_if = "Option::is_none")]
token: Option<String>,
token: Option<Secret<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
card: Option<ZenCardDetails>,
descriptor: String,
@ -196,7 +194,9 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, &GooglePayWalletData)> for Ze
browser_details,
//Connector Specific for wallet
payment_type: ZenPaymentTypes::ExternalPaymentToken,
token: Some(gpay_pay_redirect_data.tokenization_data.token.clone()),
token: Some(Secret::new(
gpay_pay_redirect_data.tokenization_data.token.clone(),
)),
card: None,
descriptor: item.get_description()?.chars().take(24).collect(),
return_verify_url: item.request.router_return_url.clone(),
@ -323,7 +323,7 @@ fn get_signature_data(checkout_request: &CheckoutRequest) -> String {
fn get_customer(
item: &types::PaymentsAuthorizeRouterData,
ip: IpAddr,
ip: Secret<String, pii::IpAddress>,
) -> Result<ZenCustomerDetails, error_stack::Report<errors::ConnectorError>> {
Ok(ZenCustomerDetails {
email: item.request.get_email()?,