mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +08:00 
			
		
		
		
	refactor(core): use profile id to find connector (#2020)
This commit is contained in:
		| @ -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" | ||||
|             })), | ||||
|         }, | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Narayan Bhat
					Narayan Bhat