Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use card bin to get additional card details #3036

Merged
merged 13 commits into from
Dec 4, 2023
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 @@ -175,10 +172,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 @@ -405,7 +398,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 @@ -415,7 +407,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 @@ -436,39 +428,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 @@ -977,6 +937,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 @@ -3617,31 +3617,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 @@ -157,7 +157,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;
hrithikesh026 marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -591,10 +606,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 @@ -602,7 +610,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 @@ -614,7 +625,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 @@ -629,35 +641,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
Loading