refactor(session_token): add support for business filtering in payments session (#1128)

This commit is contained in:
Narayan Bhat
2023-05-11 20:34:45 +05:30
committed by GitHub
parent e779ee78a4
commit 2b0ed12530
13 changed files with 127 additions and 104 deletions

View File

@ -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,

View File

@ -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(

View File

@ -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>,

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
))
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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),
}