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

feat: calculate surcharge for customer saved card list #3039

Merged
merged 23 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cd8d96a
refactor: create separate struct for surcharge details response
hrithikesh026 Nov 30, 2023
f829a8c
change function name
hrithikesh026 Nov 30, 2023
ccca67d
fix: use card bin to get additional card details
hrithikesh026 Dec 1, 2023
8043b99
revert few changes
hrithikesh026 Dec 2, 2023
ecc07d9
Merge branch 'use-card-bin' into surcharge-genration-for-saved-card
hrithikesh026 Dec 2, 2023
1684dae
chore: run formatter
hyperswitch-bot[bot] Dec 3, 2023
377fd63
calculate surcharge in saved card fetch and remove surcharge validati…
hrithikesh026 Dec 3, 2023
d86fae6
Merge branch 'main' into surcharge-api-model-changes
hrithikesh026 Dec 3, 2023
671db52
Merge branch 'main' into use-card-bin
hrithikesh026 Dec 3, 2023
6621225
Merge branch 'main' into surcharge-genration-for-saved-card
hrithikesh026 Dec 3, 2023
a3260f1
Merge branch 'surcharge-genration-for-saved-card' of github.com:juspa…
hrithikesh026 Dec 3, 2023
1eae927
Merge branch 'surcharge-api-model-changes' into use-card-bin
hrithikesh026 Dec 3, 2023
1133a53
chore: run formatter
hyperswitch-bot[bot] Dec 3, 2023
b4f74fe
generate openapi spec
hrithikesh026 Dec 3, 2023
dd4d582
chore: update Cargo.lock
hyperswitch-bot[bot] Dec 3, 2023
cc72c91
address comments
hrithikesh026 Dec 4, 2023
f939cda
Merge branch 'surcharge-api-model-changes' into surcharge-genration-f…
hrithikesh026 Dec 4, 2023
09adb02
cargo clippy
hrithikesh026 Dec 4, 2023
e428e8e
add display_total_surcharge_amount field in SurchargeDetailsResponse
hrithikesh026 Dec 4, 2023
9296073
Merge branch 'use-card-bin' into surcharge-genration-for-saved-card
hrithikesh026 Dec 4, 2023
5845bfa
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Dec 4, 2023
a80d968
address comments
hrithikesh026 Dec 4, 2023
255b107
Merge branch 'main' into surcharge-genration-for-saved-card
hrithikesh026 Dec 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 38 additions & 30 deletions crates/api_models/src/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use common_utils::{
types::{Percentage, Surcharge},
};
use serde::de;
use utoipa::ToSchema;
use utoipa::{schema, ToSchema};

#[cfg(feature = "payouts")]
use crate::payouts;
Expand Down Expand Up @@ -264,19 +264,6 @@ pub struct CardNetworkTypes {
pub card_network: api_enums::CardNetwork,

/// surcharge details for this card network
#[schema(example = r#"
{
"surcharge": {
"type": "rate",
"value": {
"percentage": 2.5
}
},
"tax_on_surcharge": {
"percentage": 1.5
}
}
"#)]
pub surcharge_details: Option<SurchargeDetailsResponse>,

/// The list of eligible connectors for a given card network
Expand Down Expand Up @@ -313,31 +300,19 @@ pub struct ResponsePaymentMethodTypes {
pub required_fields: Option<HashMap<String, RequiredFieldInfo>>,

/// surcharge details for this payment method type if exists
#[schema(example = r#"
{
"surcharge": {
"type": "rate",
"value": {
"percentage": 2.5
}
},
"tax_on_surcharge": {
"percentage": 1.5
}
}
"#)]
pub surcharge_details: Option<SurchargeDetailsResponse>,

/// auth service connector label for this payment method type, if exists
pub pm_auth_connector: Option<String>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]

#[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct SurchargeDetailsResponse {
/// surcharge value
pub surcharge: Surcharge,
pub surcharge: SurchargeResponse,
/// tax on surcharge value
pub tax_on_surcharge: Option<Percentage<SURCHARGE_PERCENTAGE_PRECISION_LENGTH>>,
pub tax_on_surcharge: Option<SurchargePercentage>,
/// surcharge amount for this payment
pub display_surcharge_amount: f64,
/// tax on surcharge amount for this payment
Expand All @@ -348,6 +323,36 @@ pub struct SurchargeDetailsResponse {
pub display_final_amount: f64,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, ToSchema)]
#[serde(rename_all = "snake_case", tag = "type", content = "value")]
pub enum SurchargeResponse {
/// Fixed Surcharge value
Fixed(i64),
/// Surcharge percentage
Rate(SurchargePercentage),
}

impl From<Surcharge> for SurchargeResponse {
fn from(value: Surcharge) -> Self {
match value {
Surcharge::Fixed(amount) => Self::Fixed(amount),
Surcharge::Rate(percentage) => Self::Rate(percentage.into()),
}
}
}

#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, ToSchema)]
pub struct SurchargePercentage {
percentage: f32,
}

impl From<Percentage<SURCHARGE_PERCENTAGE_PRECISION_LENGTH>> for SurchargePercentage {
fn from(value: Percentage<SURCHARGE_PERCENTAGE_PRECISION_LENGTH>) -> Self {
Self {
percentage: value.get_percentage(),
}
}
}
/// Required fields info used while listing the payment_method_data
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq, ToSchema, Hash)]
pub struct RequiredFieldInfo {
Expand Down Expand Up @@ -716,6 +721,9 @@ pub struct CustomerPaymentMethod {
#[schema(example = json!({"mask": "0000"}))]
pub bank: Option<MaskedBankDetails>,

/// Surcharge details for this saved card
pub surcharge_details: Option<SurchargeDetailsResponse>,

/// Whether this payment method requires CVV to be collected
#[schema(example = true)]
pub requires_cvv: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/common_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ strum = { version = "0.24.1", features = ["derive"] }
thiserror = "1.0.40"
time = { version = "0.3.21", features = ["serde", "serde-well-known", "std"] }
tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"], optional = true }
utoipa = { version = "3.3.0", features = ["preserve_order"] }

# First party crates
common_enums = { version = "0.1.0", path = "../common_enums" }
Expand Down
108 changes: 93 additions & 15 deletions crates/router/src/core/payment_methods/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ use error_stack::{report, IntoReport, ResultExt};
use masking::Secret;
use router_env::{instrument, tracing};

use super::surcharge_decision_configs::perform_surcharge_decision_management_for_payment_method_list;
use super::surcharge_decision_configs::{
perform_surcharge_decision_management_for_payment_method_list,
perform_surcharge_decision_management_for_saved_cards,
};
use crate::{
configs::settings,
core::{
Expand All @@ -38,7 +41,6 @@ use crate::{
helpers,
routing::{self, SessionFlowRoutingInput},
},
utils::persist_individual_surcharge_details_in_redis,
},
db, logger,
pii::prelude::*,
Expand Down Expand Up @@ -1687,12 +1689,9 @@ pub async fn call_surcharge_decision_management(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error performing surcharge decision operation")?;
if !surcharge_results.is_empty_result() {
persist_individual_surcharge_details_in_redis(
&state,
merchant_account,
&surcharge_results,
)
.await?;
surcharge_results
.persist_individual_surcharge_details_in_redis(&state, merchant_account)
.await?;
let _ = state
.store
.update_payment_intent(
Expand All @@ -1711,6 +1710,56 @@ pub async fn call_surcharge_decision_management(
}
}

pub async fn call_surcharge_decision_management_for_saved_card(
state: &routes::AppState,
merchant_account: &domain::MerchantAccount,
payment_attempt: &storage::PaymentAttempt,
payment_intent: storage::PaymentIntent,
customer_payment_method_response: &mut api::CustomerPaymentMethodsListResponse,
) -> errors::RouterResult<()> {
if payment_attempt.surcharge_amount.is_some() {
Ok(())
} else {
let algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account
.routing_algorithm
.clone()
.map(|val| val.parse_value("routing algorithm"))
.transpose()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Could not decode the routing algorithm")?
.unwrap_or_default();
let surcharge_results = perform_surcharge_decision_management_for_saved_cards(
state,
algorithm_ref,
payment_attempt,
&payment_intent,
&mut customer_payment_method_response.customer_payment_methods,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("error performing surcharge decision operation")?;
if !surcharge_results.is_empty_result() {
surcharge_results
.persist_individual_surcharge_details_in_redis(state, merchant_account)
.await?;
let _ = state
.store
.update_payment_intent(
payment_intent,
storage::PaymentIntentUpdate::SurchargeApplicableUpdate {
surcharge_applicable: true,
updated_by: merchant_account.storage_scheme.to_string(),
},
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
.attach_printable("Failed to update surcharge_applicable in Payment Intent");
}
Ok(())
}
}

#[allow(clippy::too_many_arguments)]
pub async fn filter_payment_methods(
payment_methods: Vec<serde_json::Value>,
Expand Down Expand Up @@ -2195,12 +2244,13 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed(
.await
} else {
let cloned_secret = req.and_then(|r| r.client_secret.as_ref().cloned());
let payment_intent = helpers::verify_payment_intent_time_and_client_secret(
db,
&merchant_account,
cloned_secret,
)
.await?;
let payment_intent: Option<data_models::payments::PaymentIntent> =
helpers::verify_payment_intent_time_and_client_secret(
db,
&merchant_account,
cloned_secret,
)
.await?;
let customer_id = payment_intent
.as_ref()
.and_then(|intent| intent.customer_id.to_owned())
Expand Down Expand Up @@ -2326,6 +2376,7 @@ pub async fn list_customer_payment_method(
created: Some(pm.created_at),
bank_transfer: pmd,
bank: bank_details,
surcharge_details: None,
requires_cvv,
};
customer_pms.push(pma.to_owned());
Expand Down Expand Up @@ -2377,9 +2428,36 @@ pub async fn list_customer_payment_method(
}
}

let response = api::CustomerPaymentMethodsListResponse {
let mut response = api::CustomerPaymentMethodsListResponse {
customer_payment_methods: customer_pms,
};
let payment_attempt = payment_intent
.as_ref()
.async_map(|payment_intent| async {
state
.store
.find_payment_attempt_by_payment_id_merchant_id_attempt_id(
&payment_intent.payment_id,
&merchant_account.merchant_id,
&payment_intent.active_attempt.get_id(),
merchant_account.storage_scheme,
)
.await
.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
})
.await
.transpose()?;

if let Some((payment_attempt, payment_intent)) = payment_attempt.zip(payment_intent) {
call_surcharge_decision_management_for_saved_card(
state,
&merchant_account,
&payment_attempt,
payment_intent,
&mut response,
)
.await?;
}

Ok(services::ApplicationResponse::Json(response))
}
Expand Down
Loading
Loading