mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
refactor(session_token): add support for business filtering in payments session (#1128)
This commit is contained in:
@ -838,7 +838,8 @@ pub async fn list_payment_methods(
|
||||
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
|
||||
|
||||
// filter out connectors based on the business country
|
||||
let filtered_mcas = filter_mca_based_on_business_details(all_mcas, payment_intent.as_ref());
|
||||
let filtered_mcas =
|
||||
helpers::filter_mca_based_on_business_details(all_mcas, payment_intent.as_ref());
|
||||
|
||||
logger::debug!(mca_before_filtering=?filtered_mcas);
|
||||
|
||||
@ -1214,25 +1215,6 @@ async fn filter_payment_methods(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn filter_mca_based_on_business_details(
|
||||
merchant_connector_accounts: Vec<
|
||||
storage_models::merchant_connector_account::MerchantConnectorAccount,
|
||||
>,
|
||||
payment_intent: Option<&storage_models::payment_intent::PaymentIntent>,
|
||||
) -> Vec<storage_models::merchant_connector_account::MerchantConnectorAccount> {
|
||||
if let Some(payment_intent) = payment_intent {
|
||||
merchant_connector_accounts
|
||||
.into_iter()
|
||||
.filter(|mca| {
|
||||
mca.business_country == payment_intent.business_country
|
||||
&& mca.business_label == payment_intent.business_label
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
merchant_connector_accounts
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_pm_based_on_config<'a>(
|
||||
config: &'a crate::configs::settings::ConnectorFilters,
|
||||
connector: &'a str,
|
||||
|
||||
@ -536,7 +536,7 @@ where
|
||||
pub async fn call_multiple_connectors_service<F, Op, Req>(
|
||||
state: &AppState,
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
connectors: Vec<api::ConnectorData>,
|
||||
connectors: Vec<api::SessionConnectorData>,
|
||||
_operation: &Op,
|
||||
mut payment_data: PaymentData<F>,
|
||||
customer: &Option<storage::Customer>,
|
||||
@ -558,15 +558,16 @@ where
|
||||
let call_connectors_start_time = Instant::now();
|
||||
let mut join_handlers = Vec::with_capacity(connectors.len());
|
||||
|
||||
for connector in connectors.iter() {
|
||||
let connector_id = connector.connector.id();
|
||||
for session_connector_data in connectors.iter() {
|
||||
let connector_id = session_connector_data.connector.connector.id();
|
||||
|
||||
let router_data = payment_data
|
||||
.construct_router_data(state, connector_id, merchant_account, customer)
|
||||
.await?;
|
||||
|
||||
let res = router_data.decide_flows(
|
||||
state,
|
||||
connector,
|
||||
&session_connector_data.connector,
|
||||
customer,
|
||||
CallConnectorAction::Trigger,
|
||||
merchant_account,
|
||||
@ -577,8 +578,8 @@ where
|
||||
|
||||
let result = join_all(join_handlers).await;
|
||||
|
||||
for (connector_res, connector) in result.into_iter().zip(connectors) {
|
||||
let connector_name = connector.connector_name.to_string();
|
||||
for (connector_res, session_connector) in result.into_iter().zip(connectors) {
|
||||
let connector_name = session_connector.connector.connector_name.to_string();
|
||||
match connector_res {
|
||||
Ok(connector_response) => {
|
||||
if let Ok(types::PaymentsResponseData::SessionResponse { session_token }) =
|
||||
@ -1077,18 +1078,13 @@ where
|
||||
{
|
||||
let connector_choice = operation
|
||||
.to_domain()?
|
||||
.get_connector(merchant_account, state, req)
|
||||
.get_connector(merchant_account, state, req, &payment_data.payment_intent)
|
||||
.await?;
|
||||
|
||||
let connector = if should_call_connector(operation, payment_data) {
|
||||
Some(match connector_choice {
|
||||
api::ConnectorChoice::SessionMultiple(session_connectors) => {
|
||||
api::ConnectorCallType::Multiple(
|
||||
session_connectors
|
||||
.into_iter()
|
||||
.map(|c| c.connector)
|
||||
.collect(),
|
||||
)
|
||||
api::ConnectorCallType::Multiple(session_connectors)
|
||||
}
|
||||
|
||||
api::ConnectorChoice::StraightThrough(straight_through) => connector_selection(
|
||||
|
||||
@ -43,6 +43,25 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub fn filter_mca_based_on_business_details(
|
||||
merchant_connector_accounts: Vec<
|
||||
storage_models::merchant_connector_account::MerchantConnectorAccount,
|
||||
>,
|
||||
payment_intent: Option<&storage_models::payment_intent::PaymentIntent>,
|
||||
) -> Vec<storage_models::merchant_connector_account::MerchantConnectorAccount> {
|
||||
if let Some(payment_intent) = payment_intent {
|
||||
merchant_connector_accounts
|
||||
.into_iter()
|
||||
.filter(|mca| {
|
||||
mca.business_country == payment_intent.business_country
|
||||
&& mca.business_label == payment_intent.business_label
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
merchant_connector_accounts
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_address_for_payment_request(
|
||||
db: &dyn StorageInterface,
|
||||
req_address: Option<&api::Address>,
|
||||
|
||||
@ -127,6 +127,7 @@ pub trait Domain<F: Clone, R>: Send + Sync {
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &R,
|
||||
payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse>;
|
||||
}
|
||||
|
||||
@ -196,6 +197,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsRetrieveRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
@ -263,6 +265,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsCaptureRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
@ -318,6 +321,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsCancelRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
|
||||
@ -266,6 +266,7 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for CompleteAuthorize {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
// Use a new connector in the confirm call or use the same one which was passed when
|
||||
// creating the payment or if none is passed then use the routing algorithm
|
||||
|
||||
@ -285,6 +285,7 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentConfirm {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
// Use a new connector in the confirm call or use the same one which was passed when
|
||||
// creating the payment or if none is passed then use the routing algorithm
|
||||
|
||||
@ -303,6 +303,7 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentCreate {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
|
||||
@ -278,6 +278,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::VerifyRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
|
||||
@ -281,16 +281,26 @@ where
|
||||
Ok((Box::new(self), None))
|
||||
}
|
||||
|
||||
/// Returns `Vec<SessionConnectorData>`
|
||||
/// Steps carried out in this function
|
||||
/// Get all the `merchant_connector_accounts` which are not disabled
|
||||
/// Filter out connectors which have `invoke_sdk_client` enabled in `payment_method_types`
|
||||
/// If session token is requested for certain wallets only, then return them, else
|
||||
/// return all eligible connectors
|
||||
///
|
||||
/// `GetToken` parameter specifies whether to get the session token from connector integration
|
||||
/// or from separate implementation ( for googlepay - from metadata and applepay - from metadata and call connector)
|
||||
async fn get_connector<'a>(
|
||||
&'a self,
|
||||
merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsSessionRequest,
|
||||
payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> RouterResult<api::ConnectorChoice> {
|
||||
let connectors = &state.conf.connectors;
|
||||
let db = &state.store;
|
||||
|
||||
let connector_accounts = db
|
||||
let all_connector_accounts = db
|
||||
.find_merchant_connector_account_by_merchant_id_and_disabled_list(
|
||||
&merchant_account.merchant_id,
|
||||
false,
|
||||
@ -299,84 +309,88 @@ where
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Database error when querying for merchant connector accounts")?;
|
||||
|
||||
let filtered_connector_accounts = helpers::filter_mca_based_on_business_details(
|
||||
all_connector_accounts,
|
||||
Some(payment_intent),
|
||||
);
|
||||
|
||||
let requested_payment_method_types = request.wallets.clone();
|
||||
let mut connector_and_supporting_payment_method_type = Vec::new();
|
||||
|
||||
for connector_account in connector_accounts {
|
||||
let payment_methods = connector_account
|
||||
.payment_methods_enabled
|
||||
.unwrap_or_default();
|
||||
for payment_method in payment_methods {
|
||||
let parsed_payment_method_result: Result<
|
||||
PaymentMethodsEnabled,
|
||||
error_stack::Report<errors::ParsingError>,
|
||||
> = payment_method.clone().parse_value("payment_method");
|
||||
|
||||
match parsed_payment_method_result {
|
||||
Ok(parsed_payment_method) => {
|
||||
let payment_method_types = parsed_payment_method
|
||||
filtered_connector_accounts
|
||||
.into_iter()
|
||||
.for_each(|connector_account| {
|
||||
let res = connector_account
|
||||
.payment_methods_enabled
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|payment_methods_enabled| {
|
||||
payment_methods_enabled
|
||||
.parse_value::<PaymentMethodsEnabled>("payment_methods_enabled")
|
||||
})
|
||||
.filter_map(|parsed_payment_method_result| {
|
||||
let error = parsed_payment_method_result.as_ref().err();
|
||||
logger::error!(session_token_parsing_error=?error);
|
||||
parsed_payment_method_result.ok()
|
||||
})
|
||||
.flat_map(|parsed_payment_methods_enabled| {
|
||||
parsed_payment_methods_enabled
|
||||
.payment_method_types
|
||||
.unwrap_or_default();
|
||||
for payment_method_type in payment_method_types {
|
||||
if matches!(
|
||||
payment_method_type.payment_experience,
|
||||
Some(api_models::enums::PaymentExperience::InvokeSdkClient)
|
||||
) {
|
||||
let connector_and_wallet = (
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.filter(|payment_method_type| {
|
||||
let is_invoke_sdk_client = matches!(
|
||||
payment_method_type.payment_experience,
|
||||
Some(api_models::enums::PaymentExperience::InvokeSdkClient)
|
||||
);
|
||||
|
||||
// If session token is requested for the payment method type,
|
||||
// filter it out
|
||||
// if not, then create all sessions tokens
|
||||
let is_sent_in_request = requested_payment_method_types
|
||||
.contains(&payment_method_type.payment_method_type)
|
||||
|| requested_payment_method_types.is_empty();
|
||||
|
||||
is_invoke_sdk_client && is_sent_in_request
|
||||
})
|
||||
.map(|payment_method_type| {
|
||||
(
|
||||
connector_account.connector_name.to_owned(),
|
||||
payment_method_type.payment_method_type,
|
||||
);
|
||||
connector_and_supporting_payment_method_type
|
||||
.push(connector_and_wallet);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(parsing_error) => {
|
||||
logger::debug!(session_token_parsing_error=?parsing_error);
|
||||
}
|
||||
connector_account.business_sub_label.to_owned(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
connector_and_supporting_payment_method_type.extend(res);
|
||||
});
|
||||
|
||||
let mut session_connector_data =
|
||||
Vec::with_capacity(connector_and_supporting_payment_method_type.len());
|
||||
|
||||
for (connector, payment_method_type, business_sub_label) in
|
||||
connector_and_supporting_payment_method_type
|
||||
{
|
||||
match api::ConnectorData::get_connector_by_name(
|
||||
connectors,
|
||||
&connector,
|
||||
api::GetToken::from(payment_method_type),
|
||||
) {
|
||||
Ok(connector_data) => session_connector_data.push(api::SessionConnectorData {
|
||||
payment_method_type,
|
||||
connector: connector_data,
|
||||
business_sub_label,
|
||||
}),
|
||||
Err(error) => {
|
||||
logger::error!(session_token_error=?error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let requested_payment_method_types = request.wallets.clone();
|
||||
|
||||
let connectors_data = if !requested_payment_method_types.is_empty() {
|
||||
let mut connectors_data = Vec::new();
|
||||
for payment_method_type in requested_payment_method_types {
|
||||
for connector_and_payment_method_type in
|
||||
&connector_and_supporting_payment_method_type
|
||||
{
|
||||
if connector_and_payment_method_type.1 == payment_method_type {
|
||||
let connector_details = api::ConnectorData::get_connector_by_name(
|
||||
connectors,
|
||||
connector_and_payment_method_type.0.as_str(),
|
||||
api::GetToken::from(connector_and_payment_method_type.1),
|
||||
)?;
|
||||
connectors_data.push(api::SessionConnectorData {
|
||||
payment_method_type,
|
||||
connector: connector_details,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
connectors_data
|
||||
} else {
|
||||
let mut connectors_data = Vec::new();
|
||||
|
||||
for connector_and_payment_method_type in connector_and_supporting_payment_method_type {
|
||||
let connector_details = api::ConnectorData::get_connector_by_name(
|
||||
connectors,
|
||||
connector_and_payment_method_type.0.as_str(),
|
||||
api::GetToken::from(connector_and_payment_method_type.1),
|
||||
)?;
|
||||
connectors_data.push(api::SessionConnectorData {
|
||||
payment_method_type: connector_and_payment_method_type.1,
|
||||
connector: connector_details,
|
||||
});
|
||||
}
|
||||
connectors_data
|
||||
};
|
||||
|
||||
Ok(api::ConnectorChoice::SessionMultiple(connectors_data))
|
||||
Ok(api::ConnectorChoice::SessionMultiple(
|
||||
session_connector_data,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -249,6 +249,7 @@ where
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
_request: &api::PaymentsStartRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, None).await
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentStatus {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
|
||||
@ -359,6 +359,7 @@ impl<F: Clone + Send> Domain<F, api::PaymentsRequest> for PaymentUpdate {
|
||||
_merchant_account: &storage::MerchantAccount,
|
||||
state: &AppState,
|
||||
request: &api::PaymentsRequest,
|
||||
_payment_intent: &storage::payment_intent::PaymentIntent,
|
||||
) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
|
||||
helpers::get_connector_default(state, request.routing.clone()).await
|
||||
}
|
||||
|
||||
@ -154,6 +154,7 @@ pub struct ConnectorData {
|
||||
pub struct SessionConnectorData {
|
||||
pub payment_method_type: api_enums::PaymentMethodType,
|
||||
pub connector: ConnectorData,
|
||||
pub business_sub_label: Option<String>,
|
||||
}
|
||||
|
||||
pub enum ConnectorChoice {
|
||||
@ -164,7 +165,7 @@ pub enum ConnectorChoice {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ConnectorCallType {
|
||||
Multiple(Vec<ConnectorData>),
|
||||
Multiple(Vec<SessionConnectorData>),
|
||||
Single(ConnectorData),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user