mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
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:
9
.github/workflows/manual-release.yml
vendored
9
.github/workflows/manual-release.yml
vendored
@ -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:
|
||||||
|
|||||||
@ -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 = []
|
||||||
|
|||||||
@ -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")]
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user