refactor(core): use profile id to find connector (#2020)

This commit is contained in:
Narayan Bhat
2023-09-11 12:55:22 +05:30
committed by GitHub
parent 60c5fdb89a
commit 5b29c25210
37 changed files with 776 additions and 363 deletions

View File

@ -4,7 +4,7 @@ use api_models::enums::{DisputeStage, DisputeStatus};
#[cfg(feature = "payouts")]
use common_utils::{crypto::Encryptable, pii::Email};
use common_utils::{errors::CustomResult, ext_traits::AsyncExt};
use error_stack::{IntoReport, ResultExt};
use error_stack::{report, IntoReport, ResultExt};
use router_env::{instrument, tracing};
use uuid::Uuid;
@ -45,21 +45,23 @@ pub async fn get_mca_for_payout<'a>(
match payout_data.merchant_connector_account.to_owned() {
Some(mca) => Ok(mca),
None => {
let (business_country, business_label) = helpers::get_business_details(
payout_attempt.business_country,
payout_attempt.business_label.as_ref(),
merchant_account,
)?;
let connector_label =
helpers::get_connector_label(business_country, &business_label, None, connector_id);
let profile_id = payout_attempt
.profile_id
.as_ref()
.ok_or(errors::ApiErrorResponse::MissingRequiredField {
field_name: "business_profile",
})
.into_report()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in payment_intent")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
None,
key_store,
profile_id,
connector_id,
)
.await?;
Ok(merchant_connector_account)
@ -203,19 +205,24 @@ pub async fn construct_refund_router_data<'a, F>(
refund: &'a storage::Refund,
creds_identifier: Option<String>,
) -> RouterResult<types::RefundsRouterData<F>> {
let connector_label = helpers::get_connector_label(
let profile_id = get_profile_id_from_business_details(
payment_intent.business_country,
&payment_intent.business_label,
None,
connector_id,
);
payment_intent.business_label.as_ref(),
merchant_account,
payment_intent.profile_id.as_ref(),
&*state.store,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in payment_intent")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
creds_identifier,
key_store,
&profile_id,
connector_id,
)
.await?;
@ -471,21 +478,27 @@ pub async fn construct_accept_dispute_router_data<'a>(
key_store: &domain::MerchantKeyStore,
dispute: &storage::Dispute,
) -> RouterResult<types::AcceptDisputeRouterData> {
let connector_id = &dispute.connector;
let connector_label = helpers::get_connector_label(
let profile_id = get_profile_id_from_business_details(
payment_intent.business_country,
&payment_intent.business_label,
payment_attempt.business_sub_label.as_ref(),
connector_id,
);
payment_intent.business_label.as_ref(),
merchant_account,
payment_intent.profile_id.as_ref(),
&*state.store,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in payment_intent")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
None,
key_store,
&profile_id,
&dispute.connector,
)
.await?;
let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
let auth_type: types::ConnectorAuthType = merchant_connector_account
.get_connector_account_details()
@ -497,7 +510,7 @@ pub async fn construct_accept_dispute_router_data<'a>(
let router_data = types::RouterData {
flow: PhantomData,
merchant_id: merchant_account.merchant_id.clone(),
connector: connector_id.to_string(),
connector: dispute.connector.to_string(),
payment_id: payment_attempt.payment_id.clone(),
attempt_id: payment_attempt.attempt_id.clone(),
status: payment_attempt.status,
@ -551,20 +564,27 @@ pub async fn construct_submit_evidence_router_data<'a>(
submit_evidence_request_data: types::SubmitEvidenceRequestData,
) -> RouterResult<types::SubmitEvidenceRouterData> {
let connector_id = &dispute.connector;
let connector_label = helpers::get_connector_label(
let profile_id = get_profile_id_from_business_details(
payment_intent.business_country,
&payment_intent.business_label,
payment_attempt.business_sub_label.as_ref(),
connector_id,
);
payment_intent.business_label.as_ref(),
merchant_account,
payment_intent.profile_id.as_ref(),
&*state.store,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in payment_intent")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
None,
key_store,
&profile_id,
connector_id,
)
.await?;
let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
let auth_type: types::ConnectorAuthType = merchant_connector_account
.get_connector_account_details()
@ -627,14 +647,25 @@ pub async fn construct_upload_file_router_data<'a>(
create_file_request: &types::api::CreateFileRequest,
connector_id: &str,
file_key: String,
connector_label: String,
) -> RouterResult<types::UploadFileRouterData> {
let profile_id = get_profile_id_from_business_details(
payment_intent.business_country,
payment_intent.business_label.as_ref(),
merchant_account,
payment_intent.profile_id.as_ref(),
&*state.store,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in payment_intent")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
None,
key_store,
&profile_id,
connector_id,
)
.await?;
let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
@ -704,20 +735,27 @@ pub async fn construct_defend_dispute_router_data<'a>(
) -> RouterResult<types::DefendDisputeRouterData> {
let _db = &*state.store;
let connector_id = &dispute.connector;
let connector_label = helpers::get_connector_label(
let profile_id = get_profile_id_from_business_details(
payment_intent.business_country,
&payment_intent.business_label,
payment_attempt.business_sub_label.as_ref(),
connector_id,
);
payment_intent.business_label.as_ref(),
merchant_account,
payment_intent.profile_id.as_ref(),
&*state.store,
)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in payment_intent")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
None,
key_store,
&profile_id,
connector_id,
)
.await?;
let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
let auth_type: types::ConnectorAuthType = merchant_connector_account
.get_connector_account_details()
@ -780,20 +818,26 @@ pub async fn construct_retrieve_file_router_data<'a>(
file_metadata: &diesel_models::file::FileMetadata,
connector_id: &str,
) -> RouterResult<types::RetrieveFileRouterData> {
let connector_label = file_metadata
.connector_label
.clone()
.ok_or(errors::ApiErrorResponse::InternalServerError)
let profile_id = file_metadata
.profile_id
.as_ref()
.ok_or(errors::ApiErrorResponse::MissingRequiredField {
field_name: "profile_id",
})
.into_report()
.attach_printable("Missing connector label")?;
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("profile_id is not set in file_metadata")?;
let merchant_connector_account = helpers::get_merchant_connector_account(
state,
merchant_account.merchant_id.as_str(),
&connector_label,
None,
key_store,
profile_id,
connector_id,
)
.await?;
let test_mode: Option<bool> = merchant_connector_account.is_test_mode_on();
let auth_type: types::ConnectorAuthType = merchant_connector_account
.get_connector_account_details()
@ -890,7 +934,9 @@ pub async fn validate_and_get_business_profile(
.map(|business_profile| {
// Check if the merchant_id of business profile is same as the current merchant_id
if business_profile.merchant_id.ne(merchant_id) {
Err(errors::ApiErrorResponse::AccessForbidden)
Err(errors::ApiErrorResponse::AccessForbidden {
resource: business_profile.profile_id,
})
} else {
Ok(business_profile)
}
@ -898,3 +944,84 @@ pub async fn validate_and_get_business_profile(
.transpose()
.into_report()
}
fn connector_needs_business_sub_label(connector_name: &str) -> bool {
let connectors_list = [api_models::enums::Connector::Cybersource];
connectors_list
.map(|connector| connector.to_string())
.contains(&connector_name.to_string())
}
/// Create the connector label
/// {connector_name}_{country}_{business_label}
pub fn get_connector_label(
business_country: Option<api_models::enums::CountryAlpha2>,
business_label: Option<&String>,
business_sub_label: Option<&String>,
connector_name: &str,
) -> Option<String> {
business_country
.zip(business_label)
.map(|(business_country, business_label)| {
let mut connector_label =
format!("{connector_name}_{business_country}_{business_label}");
// Business sub label is currently being used only for cybersource
// To ensure backwards compatibality, cybersource mca's created before this change
// will have the business_sub_label value as default.
//
// Even when creating the connector account, if no sub label is provided, default will be used
if connector_needs_business_sub_label(connector_name) {
if let Some(sub_label) = business_sub_label {
connector_label.push_str(&format!("_{sub_label}"));
} else {
connector_label.push_str("_default"); // For backwards compatibality
}
}
connector_label
})
}
/// If profile_id is not passed, use default profile if available, or
/// If business_details (business_country and business_label) are passed, get the business_profile
/// or return a `MissingRequiredField` error
pub async fn get_profile_id_from_business_details(
business_country: Option<api_models::enums::CountryAlpha2>,
business_label: Option<&String>,
merchant_account: &domain::MerchantAccount,
request_profile_id: Option<&String>,
db: &dyn StorageInterface,
) -> RouterResult<String> {
match request_profile_id.or(merchant_account.default_profile.as_ref()) {
Some(profile_id) => {
// Check whether this business profile belongs to the merchant
let _ = validate_and_get_business_profile(
db,
Some(profile_id),
&merchant_account.merchant_id,
)
.await?;
Ok(profile_id.clone())
}
None => match business_country.zip(business_label) {
Some((business_country, business_label)) => {
let profile_name = format!("{business_country}_{business_label}");
let business_profile = db
.find_business_profile_by_profile_name_merchant_id(
&profile_name,
&merchant_account.merchant_id,
)
.await
.to_not_found_response(errors::ApiErrorResponse::BusinessProfileNotFound {
id: profile_name,
})?;
Ok(business_profile.profile_id)
}
_ => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
field_name: "profile_id or business_country, business_label"
})),
},
}
}