Skip to content

Commit

Permalink
fix: use card bin to get additional card details (#3036)
Browse files Browse the repository at this point in the history
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
hrithikesh026 and hyperswitch-bot[bot] authored Dec 4, 2023
1 parent a0cfdd3 commit 6c7d3a2
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 103 deletions.
2 changes: 2 additions & 0 deletions crates/api_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,8 @@ pub struct SurchargeDetailsResponse {
pub display_surcharge_amount: f64,
/// tax on surcharge amount for this payment
pub display_tax_on_surcharge_amount: f64,
/// sum of display_surcharge_amount and display_tax_on_surcharge_amount
pub display_total_surcharge_amount: f64,
/// sum of original amount,
pub display_final_amount: f64,
}
Expand Down
42 changes: 42 additions & 0 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,33 @@ pub struct Card {
pub nick_name: Option<Secret<String>>,
}

impl Card {
fn apply_additional_card_info(&self, additional_card_info: AdditionalCardInfo) -> Self {
Self {
card_number: self.card_number.clone(),
card_exp_month: self.card_exp_month.clone(),
card_exp_year: self.card_exp_year.clone(),
card_holder_name: self.card_holder_name.clone(),
card_cvc: self.card_cvc.clone(),
card_issuer: self
.card_issuer
.clone()
.or(additional_card_info.card_issuer),
card_network: self
.card_network
.clone()
.or(additional_card_info.card_network),
card_type: self.card_type.clone().or(additional_card_info.card_type),
card_issuing_country: self
.card_issuing_country
.clone()
.or(additional_card_info.card_issuing_country),
bank_code: self.bank_code.clone().or(additional_card_info.bank_code),
nick_name: self.nick_name.clone(),
}
}
}

#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema, Default)]
#[serde(rename_all = "snake_case")]
pub struct CardToken {
Expand Down Expand Up @@ -882,6 +909,21 @@ impl PaymentMethodData {
| Self::CardToken(_) => None,
}
}
pub fn apply_additional_payment_data(
&self,
additional_payment_data: AdditionalPaymentData,
) -> Self {
if let AdditionalPaymentData::Card(additional_card_info) = additional_payment_data {
match self {
Self::Card(card) => {
Self::Card(card.apply_additional_card_info(*additional_card_info))
}
_ => self.to_owned(),
}
} else {
self.to_owned()
}
}
}

pub trait GetPaymentMethodType {
Expand Down
1 change: 0 additions & 1 deletion crates/api_models/src/surcharge_decision_configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ impl EuclidDirFilter for SurchargeDecisionConfigs {
DirKeyKind::PaymentAmount,
DirKeyKind::PaymentCurrency,
DirKeyKind::BillingCountry,
DirKeyKind::CardType,
DirKeyKind::CardNetwork,
DirKeyKind::PayLaterType,
DirKeyKind::WalletType,
Expand Down
50 changes: 7 additions & 43 deletions crates/router/src/core/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ pub mod types;

use std::{fmt::Debug, marker::PhantomData, ops::Deref, time::Instant, vec::IntoIter};

use api_models::{
self, enums,
payments::{self, HeaderPayload},
};
use api_models::{self, enums, payments::HeaderPayload};
use common_utils::{ext_traits::AsyncExt, pii, types::Surcharge};
use data_models::mandates::MandateData;
use diesel_models::{ephemeral_key, fraud_check::FraudCheck};
Expand Down Expand Up @@ -176,10 +173,6 @@ where
let mut connector_http_status_code = None;
let mut external_latency = None;
if let Some(connector_details) = connector {
operation
.to_domain()?
.populate_payment_data(state, &mut payment_data, &req, &merchant_account)
.await?;
payment_data = match connector_details {
api::ConnectorCallType::PreDetermined(connector) => {
let schedule_time = if should_add_task_to_process_tracker {
Expand Down Expand Up @@ -406,7 +399,6 @@ where
async fn populate_surcharge_details<F>(
state: &AppState,
payment_data: &mut PaymentData<F>,
request: &payments::PaymentsRequest,
) -> RouterResult<()>
where
F: Send + Clone,
Expand All @@ -416,7 +408,7 @@ where
.surcharge_applicable
.unwrap_or(false)
{
let payment_method_data = request
let payment_method_data = payment_data
.payment_method_data
.clone()
.get_required_value("payment_method_data")?;
Expand All @@ -437,39 +429,7 @@ where
Err(err) => Err(err).change_context(errors::ApiErrorResponse::InternalServerError)?,
};

let request_surcharge_details = request.surcharge_details;

match (request_surcharge_details, calculated_surcharge_details) {
(Some(request_surcharge_details), Some(calculated_surcharge_details)) => {
if calculated_surcharge_details
.is_request_surcharge_matching(request_surcharge_details)
{
payment_data.surcharge_details = Some(calculated_surcharge_details);
} else {
return Err(errors::ApiErrorResponse::InvalidRequestData {
message: "Invalid value provided: 'surcharge_details'. surcharge details provided do not match with surcharge details sent in payment_methods list response".to_string(),
}
.into());
}
}
(None, Some(_calculated_surcharge_details)) => {
return Err(errors::ApiErrorResponse::MissingRequiredField {
field_name: "surcharge_details",
}
.into());
}
(Some(request_surcharge_details), None) => {
if request_surcharge_details.is_surcharge_zero() {
return Ok(());
} else {
return Err(errors::ApiErrorResponse::InvalidRequestData {
message: "Invalid value provided: 'surcharge_details'. surcharge details provided do not match with surcharge details sent in payment_methods list response".to_string(),
}
.into());
}
}
(None, None) => return Ok(()),
};
payment_data.surcharge_details = calculated_surcharge_details;
} else {
let surcharge_details =
payment_data
Expand Down Expand Up @@ -978,6 +938,10 @@ where
payment_data,
)
.await?;
operation
.to_domain()?
.populate_payment_data(state, payment_data, merchant_account)
.await?;

let mut router_data = payment_data
.construct_router_data(
Expand Down
29 changes: 7 additions & 22 deletions crates/router/src/core/payments/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3634,31 +3634,16 @@ pub fn get_key_params_for_surcharge_details(
)> {
match payment_method_data {
api_models::payments::PaymentMethodData::Card(card) => {
let card_type = card
.card_type
.get_required_value("payment_method_data.card.card_type")?;
let card_network = card
.card_network
.get_required_value("payment_method_data.card.card_network")?;
match card_type.to_lowercase().as_str() {
"credit" => Ok((
common_enums::PaymentMethod::Card,
common_enums::PaymentMethodType::Credit,
Some(card_network),
)),
"debit" => Ok((
common_enums::PaymentMethod::Card,
common_enums::PaymentMethodType::Debit,
Some(card_network),
)),
_ => {
logger::debug!("Invalid Card type found in payment confirm call, hence surcharge not applicable");
Err(errors::ApiErrorResponse::InvalidDataValue {
field_name: "payment_method_data.card.card_type",
}
.into())
}
}
// surcharge generated will always be same for credit as well as debit
// since surcharge conditions cannot be defined on card_type
Ok((
common_enums::PaymentMethod::Card,
common_enums::PaymentMethodType::Credit,
Some(card_network),
))
}
api_models::payments::PaymentMethodData::CardRedirect(card_redirect_data) => Ok((
common_enums::PaymentMethod::CardRedirect,
Expand Down
1 change: 0 additions & 1 deletion crates/router/src/core/payments/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ pub trait Domain<F: Clone, R, Ctx: PaymentMethodRetrieve>: Send + Sync {
&'a self,
_state: &AppState,
_payment_data: &mut PaymentData<F>,
_request: &R,
_merchant_account: &domain::MerchantAccount,
) -> CustomResult<(), errors::ApiErrorResponse> {
Ok(())
Expand Down
20 changes: 17 additions & 3 deletions crates/router/src/core/payments/operations/payment_confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,21 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
)
.await?;

let additional_pm_data = request
.payment_method_data
.as_ref()
.async_map(|payment_method_data| async {
helpers::get_additional_payment_data(payment_method_data, &*state.store).await
})
.await;
let payment_method_data_after_card_bin_call = request
.payment_method_data
.as_ref()
.zip(additional_pm_data)
.map(|(payment_method_data, additional_payment_data)| {
payment_method_data.apply_additional_payment_data(additional_payment_data)
});

let payment_data = PaymentData {
flow: PhantomData,
payment_intent,
Expand All @@ -462,7 +477,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
billing: billing_address.as_ref().map(|a| a.into()),
},
confirm: request.confirm,
payment_method_data: request.payment_method_data.clone(),
payment_method_data: payment_method_data_after_card_bin_call,
force_sync: None,
refunds: vec![],
disputes: vec![],
Expand Down Expand Up @@ -593,10 +608,9 @@ impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest
&'a self,
state: &AppState,
payment_data: &mut PaymentData<F>,
request: &api::PaymentsRequest,
_merchant_account: &domain::MerchantAccount,
) -> CustomResult<(), errors::ApiErrorResponse> {
populate_surcharge_details(state, payment_data, request).await
populate_surcharge_details(state, payment_data).await
}
}

Expand Down
81 changes: 48 additions & 33 deletions crates/router/src/core/payments/operations/payment_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
)
.await?;

let payment_attempt_new = Self::make_payment_attempt(
let (payment_attempt_new, additional_payment_data) = Self::make_payment_attempt(
&payment_id,
merchant_id,
money,
Expand Down Expand Up @@ -290,6 +290,14 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
payments::SurchargeDetails::from((&request_surcharge_details, &payment_attempt))
});

let payment_method_data_after_card_bin_call = request
.payment_method_data
.as_ref()
.zip(additional_payment_data)
.map(|(payment_method_data, additional_payment_data)| {
payment_method_data.apply_additional_payment_data(additional_payment_data)
});

let payment_data = PaymentData {
flow: PhantomData,
payment_intent,
Expand All @@ -306,7 +314,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
billing: billing_address.as_ref().map(|a| a.into()),
},
confirm: request.confirm,
payment_method_data: request.payment_method_data.clone(),
payment_method_data: payment_method_data_after_card_bin_call,
refunds: vec![],
disputes: vec![],
attempts: None,
Expand Down Expand Up @@ -604,7 +612,10 @@ impl PaymentCreate {
request: &api::PaymentsRequest,
browser_info: Option<serde_json::Value>,
state: &AppState,
) -> RouterResult<storage::PaymentAttemptNew> {
) -> RouterResult<(
storage::PaymentAttemptNew,
Option<api_models::payments::AdditionalPaymentData>,
)> {
let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now());
let status =
helpers::payment_attempt_status_fsm(&request.payment_method_data, request.confirm);
Expand All @@ -616,7 +627,8 @@ impl PaymentCreate {
.async_map(|payment_method_data| async {
helpers::get_additional_payment_data(payment_method_data, &*state.store).await
})
.await
.await;
let additional_pm_data_value = additional_pm_data
.as_ref()
.map(Encode::<api_models::payments::AdditionalPaymentData>::encode_to_value)
.transpose()
Expand All @@ -631,35 +643,38 @@ impl PaymentCreate {
utils::get_payment_attempt_id(payment_id, 1)
};

Ok(storage::PaymentAttemptNew {
payment_id: payment_id.to_string(),
merchant_id: merchant_id.to_string(),
attempt_id,
status,
currency,
amount: amount.into(),
payment_method,
capture_method: request.capture_method,
capture_on: request.capture_on,
confirm: request.confirm.unwrap_or(false),
created_at,
modified_at,
last_synced,
authentication_type: request.authentication_type,
browser_info,
payment_experience: request.payment_experience,
payment_method_type,
payment_method_data: additional_pm_data,
amount_to_capture: request.amount_to_capture,
payment_token: request.payment_token.clone(),
mandate_id: request.mandate_id.clone(),
business_sub_label: request.business_sub_label.clone(),
mandate_details: request
.mandate_data
.as_ref()
.and_then(|inner| inner.mandate_type.clone().map(Into::into)),
..storage::PaymentAttemptNew::default()
})
Ok((
storage::PaymentAttemptNew {
payment_id: payment_id.to_string(),
merchant_id: merchant_id.to_string(),
attempt_id,
status,
currency,
amount: amount.into(),
payment_method,
capture_method: request.capture_method,
capture_on: request.capture_on,
confirm: request.confirm.unwrap_or(false),
created_at,
modified_at,
last_synced,
authentication_type: request.authentication_type,
browser_info,
payment_experience: request.payment_experience,
payment_method_type,
payment_method_data: additional_pm_data_value,
amount_to_capture: request.amount_to_capture,
payment_token: request.payment_token.clone(),
mandate_id: request.mandate_id.clone(),
business_sub_label: request.business_sub_label.clone(),
mandate_details: request
.mandate_data
.as_ref()
.and_then(|inner| inner.mandate_type.clone().map(Into::into)),
..storage::PaymentAttemptNew::default()
},
additional_pm_data,
))
}

#[instrument(skip_all)]
Expand Down
2 changes: 2 additions & 0 deletions crates/router/src/core/payments/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ impl ForeignTryFrom<(&SurchargeDetails, &PaymentAttempt)> for SurchargeDetailsRe
tax_on_surcharge: surcharge_details.tax_on_surcharge.clone(),
display_surcharge_amount,
display_tax_on_surcharge_amount,
display_total_surcharge_amount: display_surcharge_amount
+ display_tax_on_surcharge_amount,
display_final_amount,
})
}
Expand Down

0 comments on commit 6c7d3a2

Please sign in to comment.