refactor(multiple_mca): make primary_business_detail optional and remove default values (#1677)

Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
Narayan Bhat
2023-08-01 12:45:44 +05:30
committed by GitHub
parent 8a638e4a08
commit 9c7ac6246d
8 changed files with 85 additions and 127 deletions

View File

@ -11,11 +11,6 @@ on:
options: options:
- sandbox - sandbox
- production - production
multiple_mca:
description: "Whether to enable the multiple_mca feature"
required: true
default: false
type: boolean
jobs: jobs:
build: build:
@ -32,10 +27,6 @@ jobs:
username: ${{ secrets.DOCKERHUB_USER }} username: ${{ secrets.DOCKERHUB_USER }}
password: ${{ secrets.DOCKERHUB_PASSWD }} password: ${{ secrets.DOCKERHUB_PASSWD }}
- name: Add multiple_mca feature if enabled
if: ${{ inputs.multiple_mca == true }}
run: echo 'EXTRA_FEATURES="--features multiple_mca"' >> $GITHUB_ENV
- name: Build and push router Docker image - name: Build and push router Docker image
uses: docker/build-push-action@v4 uses: docker/build-push-action@v4
with: with:

View File

@ -10,7 +10,6 @@ license.workspace = true
[features] [features]
default = ["payouts"] default = ["payouts"]
errors = ["dep:actix-web", "dep:reqwest"] errors = ["dep:actix-web", "dep:reqwest"]
multiple_mca = []
dummy_connector = ["common_enums/dummy_connector"] dummy_connector = ["common_enums/dummy_connector"]
detailed_errors = [] detailed_errors = []
payouts = [] payouts = []

View File

@ -79,11 +79,6 @@ pub struct MerchantAccountCreate {
pub locker_id: Option<String>, pub locker_id: Option<String>,
///Default business details for connector routing ///Default business details for connector routing
#[cfg(feature = "multiple_mca")]
#[schema(value_type = PrimaryBusinessDetails)]
pub primary_business_details: Vec<PrimaryBusinessDetails>,
#[cfg(not(feature = "multiple_mca"))]
#[schema(value_type = Option<PrimaryBusinessDetails>)] #[schema(value_type = Option<PrimaryBusinessDetails>)]
pub primary_business_details: Option<Vec<PrimaryBusinessDetails>>, pub primary_business_details: Option<Vec<PrimaryBusinessDetails>>,
@ -598,21 +593,10 @@ pub struct MerchantConnectorCreate {
#[schema(example = json!(common_utils::consts::FRM_CONFIGS_EG))] #[schema(example = json!(common_utils::consts::FRM_CONFIGS_EG))]
pub frm_configs: Option<Vec<FrmConfigs>>, pub frm_configs: Option<Vec<FrmConfigs>>,
/// Business Country of the connector
#[cfg(feature = "multiple_mca")]
#[schema(value_type = CountryAlpha2, example = "US")] #[schema(value_type = CountryAlpha2, example = "US")]
pub business_country: api_enums::CountryAlpha2, pub business_country: api_enums::CountryAlpha2,
#[cfg(not(feature = "multiple_mca"))]
#[schema(value_type = Option<CountryAlpha2>, example = "US")]
pub business_country: Option<api_enums::CountryAlpha2>,
///Business Type of the merchant
#[schema(example = "travel")]
#[cfg(feature = "multiple_mca")]
pub business_label: String, pub business_label: String,
#[cfg(not(feature = "multiple_mca"))]
pub business_label: Option<String>,
/// Business Sub label of the merchant /// Business Sub label of the merchant
#[schema(example = "chase")] #[schema(example = "chase")]

View File

@ -22,7 +22,6 @@ kv_store = []
accounts_cache = [] accounts_cache = []
openapi = ["olap", "oltp", "payouts"] openapi = ["olap", "oltp", "payouts"]
vergen = ["router_env/vergen"] vergen = ["router_env/vergen"]
multiple_mca = ["api_models/multiple_mca"]
dummy_connector = ["api_models/dummy_connector"] dummy_connector = ["api_models/dummy_connector"]
external_access_dc = ["dummy_connector"] external_access_dc = ["dummy_connector"]
detailed_errors = ["api_models/detailed_errors", "error-stack/serde"] detailed_errors = ["api_models/detailed_errors", "error-stack/serde"]

View File

@ -39,31 +39,6 @@ pub fn create_merchant_publishable_key() -> String {
) )
} }
fn get_primary_business_details(
request: &api::MerchantAccountCreate,
) -> Vec<PrimaryBusinessDetails> {
// In this case, business details is not optional, it will always be passed
#[cfg(feature = "multiple_mca")]
{
request.primary_business_details.to_owned()
}
// In this case, business details will be optional, if it is not passed, then create the
// default value
#[cfg(not(feature = "multiple_mca"))]
{
request
.primary_business_details
.to_owned()
.unwrap_or_else(|| {
vec![PrimaryBusinessDetails {
country: enums::CountryAlpha2::US,
business: "default".to_string(),
}]
})
}
}
pub async fn create_merchant_account( pub async fn create_merchant_account(
db: &dyn StorageInterface, db: &dyn StorageInterface,
req: api::MerchantAccountCreate, req: api::MerchantAccountCreate,
@ -77,7 +52,7 @@ pub async fn create_merchant_account(
let publishable_key = Some(create_merchant_publishable_key()); let publishable_key = Some(create_merchant_publishable_key());
let primary_business_details = utils::Encode::<Vec<PrimaryBusinessDetails>>::encode_to_value( let primary_business_details = utils::Encode::<Vec<PrimaryBusinessDetails>>::encode_to_value(
&get_primary_business_details(&req), &req.primary_business_details.unwrap_or_default(),
) )
.change_context(errors::ApiErrorResponse::InvalidDataValue { .change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "primary_business_details", field_name: "primary_business_details",
@ -384,27 +359,6 @@ async fn validate_merchant_id<S: Into<String>>(
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
} }
fn get_business_details_wrapper(
request: &api::MerchantConnectorCreate,
_merchant_account: &domain::MerchantAccount,
) -> RouterResult<(enums::CountryAlpha2, String)> {
#[cfg(feature = "multiple_mca")]
{
// The fields are mandatory
Ok((request.business_country, request.business_label.to_owned()))
}
#[cfg(not(feature = "multiple_mca"))]
{
// If the value is not passed, then take it from Merchant account
helpers::get_business_details(
request.business_country,
request.business_label.as_ref(),
_merchant_account,
)
}
}
fn validate_certificate_in_mca_metadata( fn validate_certificate_in_mca_metadata(
connector_metadata: Secret<serde_json::Value>, connector_metadata: Secret<serde_json::Value>,
) -> RouterResult<()> { ) -> RouterResult<()> {
@ -465,11 +419,15 @@ pub async fn create_payment_connector(
.await .await
.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?;
let (business_country, business_label) = get_business_details_wrapper(&req, &merchant_account)?; helpers::validate_business_details(
req.business_country,
&req.business_label,
&merchant_account,
)?;
let connector_label = helpers::get_connector_label( let connector_label = helpers::get_connector_label(
business_country, req.business_country,
&business_label, &req.business_label,
req.business_sub_label.as_ref(), req.business_sub_label.as_ref(),
&req.connector_name.to_string(), &req.connector_name.to_string(),
); );
@ -532,8 +490,8 @@ pub async fn create_payment_connector(
metadata: req.metadata, metadata: req.metadata,
frm_configs, frm_configs,
connector_label: connector_label.clone(), connector_label: connector_label.clone(),
business_country, business_country: req.business_country,
business_label, business_label: req.business_label.clone(),
business_sub_label: req.business_sub_label, business_sub_label: req.business_sub_label,
created_at: common_utils::date_time::now(), created_at: common_utils::date_time::now(),
modified_at: common_utils::date_time::now(), modified_at: common_utils::date_time::now(),

View File

@ -1913,6 +1913,32 @@ pub fn get_connector_label(
connector_label connector_label
} }
/// Check whether the business details are configured in the merchant account
pub fn validate_business_details(
business_country: api_enums::CountryAlpha2,
business_label: &String,
merchant_account: &domain::MerchantAccount,
) -> RouterResult<()> {
let primary_business_details = merchant_account
.primary_business_details
.clone()
.parse_value::<Vec<api_models::admin::PrimaryBusinessDetails>>("PrimaryBusinessDetails")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to parse primary business details")?;
primary_business_details
.iter()
.find(|business_details| {
&business_details.business == business_label
&& business_details.country == business_country
})
.ok_or(errors::ApiErrorResponse::PreconditionFailed {
message: "business_details are not configured in the merchant account".to_string(),
})?;
Ok(())
}
/// Do lazy parsing of primary business details /// Do lazy parsing of primary business details
/// If both country and label are passed, no need to parse business details from merchant_account /// If both country and label are passed, no need to parse business details from merchant_account
/// If any one is missing, get it from merchant_account /// If any one is missing, get it from merchant_account
@ -1923,42 +1949,29 @@ pub fn get_business_details(
business_label: Option<&String>, business_label: Option<&String>,
merchant_account: &domain::MerchantAccount, merchant_account: &domain::MerchantAccount,
) -> RouterResult<(api_enums::CountryAlpha2, String)> { ) -> RouterResult<(api_enums::CountryAlpha2, String)> {
let (business_country, business_label) = match business_country.zip(business_label) { let primary_business_details = merchant_account
.primary_business_details
.clone()
.parse_value::<Vec<api_models::admin::PrimaryBusinessDetails>>("PrimaryBusinessDetails")
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to parse primary business details")?;
match business_country.zip(business_label) {
Some((business_country, business_label)) => { Some((business_country, business_label)) => {
(business_country.to_owned(), business_label.to_owned()) Ok((business_country.to_owned(), business_label.to_owned()))
} }
None => { _ => match primary_business_details.first() {
// Parse the primary business details from merchant account Some(business_details) if primary_business_details.len() == 1 => Ok((
let primary_business_details: Vec<api_models::admin::PrimaryBusinessDetails> = business_country.unwrap_or_else(|| business_details.country.to_owned()),
merchant_account business_label
.primary_business_details .map(ToString::to_string)
.clone() .unwrap_or_else(|| business_details.business.to_owned()),
.parse_value("PrimaryBusinessDetails") )),
.change_context(errors::ApiErrorResponse::InternalServerError) _ => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
.attach_printable("failed to parse primary business details")?; field_name: "business_country, business_label"
})),
if primary_business_details.len() == 1 { },
let primary_business_details = primary_business_details.first().ok_or( }
errors::ApiErrorResponse::MissingRequiredField {
field_name: "primary_business_details",
},
)?;
(
business_country.unwrap_or_else(|| primary_business_details.country.to_owned()),
business_label
.map(ToString::to_string)
.unwrap_or_else(|| primary_business_details.business.to_owned()),
)
} else {
// If primary business details are not present or more than one
Err(report!(errors::ApiErrorResponse::MissingRequiredField {
field_name: "business_country, business_label"
}))?
}
}
};
Ok((business_country, business_label))
} }
#[inline] #[inline]

View File

@ -589,11 +589,29 @@ impl PaymentCreate {
.change_context(errors::ApiErrorResponse::InternalServerError) .change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to convert order details to value")?; .attach_printable("Failed to convert order details to value")?;
let (business_country, business_label) = helpers::get_business_details( let (business_country, business_label) =
request.business_country, match (request.business_country, request.business_label.as_ref()) {
request.business_label.as_ref(), (Some(business_country), Some(business_label)) => {
merchant_account, helpers::validate_business_details(
)?; business_country,
business_label,
merchant_account,
)?;
Ok((business_country, business_label.clone()))
}
(None, Some(_)) => Err(errors::ApiErrorResponse::MissingRequiredField {
field_name: "business_country",
}),
(Some(_), None) => Err(errors::ApiErrorResponse::MissingRequiredField {
field_name: "business_label",
}),
(None, None) => Ok(helpers::get_business_details(
request.business_country,
request.business_label.as_ref(),
merchant_account,
)?),
}?;
let allowed_payment_method_types = request let allowed_payment_method_types = request
.get_allowed_payment_method_types_as_value() .get_allowed_payment_method_types_as_value()

View File

@ -5749,7 +5749,9 @@
"required": [ "required": [
"connector_type", "connector_type",
"connector_name", "connector_name",
"connector_label" "connector_label",
"business_country",
"business_label"
], ],
"properties": { "properties": {
"connector_type": { "connector_type": {
@ -5846,16 +5848,10 @@
"nullable": true "nullable": true
}, },
"business_country": { "business_country": {
"allOf": [ "$ref": "#/components/schemas/CountryAlpha2"
{
"$ref": "#/components/schemas/CountryAlpha2"
}
],
"nullable": true
}, },
"business_label": { "business_label": {
"type": "string", "type": "string"
"nullable": true
}, },
"business_sub_label": { "business_sub_label": {
"type": "string", "type": "string",