feat(core): add backwards compatibility for multiple mca (#866)

This commit is contained in:
Narayan Bhat
2023-04-13 17:04:36 +05:30
committed by GitHub
parent aebb4dca66
commit cf902f19e5
11 changed files with 287 additions and 151 deletions

View File

@ -9,6 +9,7 @@ errors = [
"dep:actix-web",
"dep:reqwest",
]
multiple_mca = []
[dependencies]
actix-web = { version = "4.3.1", optional = true }
@ -24,6 +25,7 @@ time = { version = "0.3.20", features = ["serde", "serde-well-known", "std"] }
url = { version = "2.3.1", features = ["serde"] }
utoipa = { version = "3.2.0", features = ["preserve_order"] }
# First party crates
common_utils = { version = "0.1.0", path = "../common_utils" }
masking = { version = "0.1.0", path = "../masking" }

View File

@ -68,8 +68,13 @@ pub struct MerchantAccountCreate {
pub locker_id: Option<String>,
///Default business details for connector routing
#[cfg(feature = "multiple_mca")]
#[schema(value_type = PrimaryBusinessDetails)]
pub primary_business_details: pii::SecretSerdeValue,
pub primary_business_details: Vec<PrimaryBusinessDetails>,
#[cfg(not(feature = "multiple_mca"))]
#[schema(value_type = Option<PrimaryBusinessDetails>)]
pub primary_business_details: Option<Vec<PrimaryBusinessDetails>>,
}
#[derive(Clone, Debug, Deserialize, ToSchema)]
@ -194,8 +199,8 @@ pub struct MerchantAccountResponse {
#[schema(example = "locker_abc123")]
pub locker_id: Option<String>,
///Default business details for connector routing
#[schema(value_type = Option<PrimaryBusinessDetails>)]
pub primary_business_details: pii::SecretSerdeValue,
#[schema(value_type = Vec<PrimaryBusinessDetails>)]
pub primary_business_details: Vec<PrimaryBusinessDetails>,
}
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
@ -249,8 +254,8 @@ pub enum RoutingAlgorithm {
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)]
pub struct PrimaryBusinessDetails {
pub country: Vec<api_enums::CountryCode>,
pub business: Vec<String>,
pub country: api_enums::CountryCode,
pub business: String,
}
#[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
@ -309,7 +314,7 @@ pub struct MerchantConnectorId {
/// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc."
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct MerchantConnector {
pub struct MerchantConnectorCreate {
/// Type of the Connector for the financial use case. Could range from Payments to Accounting to Banking.
#[schema(value_type = ConnectorType, example = "payment_processor")]
pub connector_type: api_enums::ConnectorType,
@ -320,15 +325,7 @@ pub struct MerchantConnector {
#[serde(skip_deserializing)]
#[schema(example = "stripe_US_travel")]
pub connector_label: String,
/// Country through which payment should be processed
#[schema(example = "US")]
pub business_country: api_enums::CountryCode,
///Business Type of the merchant
#[schema(example = "travel")]
pub business_label: String,
/// Business Sub label of the merchant
#[schema(example = "chase")]
pub business_sub_label: Option<String>,
/// Unique ID of the connector
#[schema(example = "mca_5apGeP94tMts6rg3U3kR")]
pub merchant_connector_id: Option<String>,
@ -375,6 +372,99 @@ pub struct MerchantConnector {
/// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>,max_length = 255,example = json!({ "city": "NY", "unit": "245" }))]
pub metadata: Option<pii::SecretSerdeValue>,
/// Business Country of the connector
#[schema(example = "US")]
#[cfg(feature = "multiple_mca")]
pub business_country: api_enums::CountryCode,
#[cfg(not(feature = "multiple_mca"))]
pub business_country: Option<api_enums::CountryCode>,
///Business Type of the merchant
#[schema(example = "travel")]
#[cfg(feature = "multiple_mca")]
pub business_label: String,
#[cfg(not(feature = "multiple_mca"))]
pub business_label: Option<String>,
/// Business Sub label of the merchant
#[schema(example = "chase")]
pub business_sub_label: Option<String>,
}
/// Response of creating a new Merchant Connector for the merchant account."
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(deny_unknown_fields)]
pub struct MerchantConnectorResponse {
/// Type of the Connector for the financial use case. Could range from Payments to Accounting to Banking.
#[schema(value_type = ConnectorType, example = "payment_processor")]
pub connector_type: api_enums::ConnectorType,
/// Name of the Connector
#[schema(example = "stripe")]
pub connector_name: String,
// /// Connector label for specific country and Business
#[serde(skip_deserializing)]
#[schema(example = "stripe_US_travel")]
pub connector_label: String,
/// Unique ID of the connector
#[schema(example = "mca_5apGeP94tMts6rg3U3kR")]
pub merchant_connector_id: String,
/// Account details of the Connector. You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>,example = json!({ "auth_type": "HeaderKey","api_key": "Basic MyVerySecretApiKey" }))]
pub connector_account_details: pii::SecretSerdeValue,
/// A boolean value to indicate if the connector is in Test mode. By default, its value is false.
#[schema(default = false, example = false)]
pub test_mode: Option<bool>,
/// A boolean value to indicate if the connector is disabled. By default, its value is false.
#[schema(default = false, example = false)]
pub disabled: Option<bool>,
/// Refers to the Parent Merchant ID if the merchant being created is a sub-merchant
#[schema(example = json!([
{
"payment_method": "wallet",
"payment_method_types": [
"upi_collect",
"upi_intent"
],
"payment_method_issuers": [
"labore magna ipsum",
"aute"
],
"payment_schemes": [
"Discover",
"Discover"
],
"accepted_currencies": {
"type": "enable_only",
"list": ["USD", "EUR"]
},
"accepted_countries": {
"type": "disable_only",
"list": ["FR", "DE","IN"]
},
"minimum_amount": 1,
"maximum_amount": 68607706,
"recurring_enabled": true,
"installment_payment_enabled": true
}
]))]
pub payment_methods_enabled: Option<Vec<PaymentMethodsEnabled>>,
/// You can specify up to 50 keys, with key names up to 40 characters long and values up to 500 characters long. Metadata is useful for storing additional, structured information on an object.
#[schema(value_type = Option<Object>,max_length = 255,example = json!({ "city": "NY", "unit": "245" }))]
pub metadata: Option<pii::SecretSerdeValue>,
/// Business Country of the connector
#[schema(example = "US")]
pub business_country: api_enums::CountryCode,
///Business Type of the merchant
#[schema(example = "travel")]
pub business_label: String,
/// Business Sub label of the merchant
#[schema(example = "chase")]
pub business_sub_label: Option<String>,
}
/// Create a new Merchant Connector for the merchant account. The connector could be a payment processor / facilitator / acquirer or specialized services like Fraud / Accounting etc."

View File

@ -22,6 +22,7 @@ kv_store = []
accounts_cache = []
openapi = ["olap", "oltp"]
vergen = ["router_env/vergen"]
multiple_mca = ["api_models/multiple_mca"]
[dependencies]

View File

@ -1,7 +1,6 @@
use api_models::admin::PrimaryBusinessDetails;
use common_utils::ext_traits::ValueExt;
use error_stack::{report, FutureExt, IntoReport, ResultExt};
use masking::ExposeInterface;
use storage_models::{enums, merchant_account};
use uuid::Uuid;
@ -13,13 +12,12 @@ use crate::{
payments::helpers,
},
db::StorageInterface,
pii::Secret,
routes::AppState,
services::api as service_api,
types::{
self, api,
storage::{self, MerchantAccount},
transformers::{ForeignInto, ForeignTryInto},
transformers::{ForeignInto, ForeignTryFrom, ForeignTryInto},
},
utils::{self, OptionExt},
};
@ -33,6 +31,31 @@ 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::CountryCode::US,
business: "default".to_string(),
}]
})
}
}
pub async fn create_merchant_account(
state: &AppState,
req: api::MerchantAccountCreate,
@ -66,37 +89,34 @@ pub async fn create_merchant_account(
.attach_printable("Unexpected create API key response"),
}?;
let merchant_details = req
.merchant_details
.map(|md| {
utils::Encode::<api::MerchantDetails>::encode_to_value(&md).change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "merchant_details",
},
)
})
.transpose()?;
let webhook_details = req
.webhook_details
.map(|wd| {
utils::Encode::<api::WebhookDetails>::encode_to_value(&wd).change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "webhook details",
},
)
})
.transpose()?;
let primary_business_details = req.primary_business_details.expose();
let _valid_business_details: PrimaryBusinessDetails = primary_business_details
.clone()
.parse_value("primary_business_details")
let primary_business_details =
utils::Encode::<api::WebhookDetails>::encode_to_value(&get_primary_business_details(&req))
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "primary_business_details",
})?;
let merchant_details =
req.merchant_details
.as_ref()
.map(|merchant_details| {
utils::Encode::<api::MerchantDetails>::encode_to_value(merchant_details)
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "merchant_details",
})
})
.transpose()?;
let webhook_details =
req.webhook_details
.as_ref()
.map(|webhook_details| {
utils::Encode::<api::WebhookDetails>::encode_to_value(webhook_details)
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "webhook details",
})
})
.transpose()?;
if let Some(ref routing_algorithm) = req.routing_algorithm {
let _: api::RoutingAlgorithm = routing_algorithm
.clone()
@ -139,7 +159,11 @@ pub async fn create_merchant_account(
})?;
Ok(service_api::ApplicationResponse::Json(
merchant_account.foreign_into(),
ForeignTryFrom::foreign_try_from(merchant_account).change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "merchant_account",
},
)?,
))
}
@ -155,7 +179,11 @@ pub async fn get_merchant_account(
})?;
Ok(service_api::ApplicationResponse::Json(
merchant_account.foreign_into(),
ForeignTryFrom::foreign_try_from(merchant_account).change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "merchant_account",
},
)?,
))
}
@ -236,7 +264,11 @@ pub async fn merchant_account_update(
})?;
Ok(service_api::ApplicationResponse::Json(
response.foreign_into(),
ForeignTryFrom::foreign_try_from(response).change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "merchant_account",
},
)?,
))
}
@ -295,27 +327,49 @@ async fn validate_merchant_id<S: Into<String>>(
})
}
fn get_business_details_wrapper(
request: &api::MerchantConnectorCreate,
_merchant_account: &MerchantAccount,
) -> RouterResult<(enums::CountryCode, 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,
)
}
}
pub async fn create_payment_connector(
store: &dyn StorageInterface,
req: api::MerchantConnector,
req: api::MerchantConnectorCreate,
merchant_id: &String,
) -> RouterResponse<api::MerchantConnector> {
let _merchant_account = store
) -> RouterResponse<api_models::admin::MerchantConnectorResponse> {
let merchant_account = store
.find_merchant_account_by_merchant_id(merchant_id)
.await
.map_err(|error| {
error.to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)
})?;
let (business_country, business_label) = get_business_details_wrapper(&req, &merchant_account)?;
let connector_label = helpers::get_connector_label(
req.business_country,
&req.business_label,
business_country,
&business_label,
req.business_sub_label.as_ref(),
&req.connector_name,
);
let mut vec = Vec::new();
let mut response = req.clone();
let payment_methods_enabled = match req.payment_methods_enabled {
Some(val) => {
for pm in val.into_iter() {
@ -352,8 +406,8 @@ pub async fn create_payment_connector(
disabled: req.disabled,
metadata: req.metadata,
connector_label: connector_label.clone(),
business_country: req.business_country,
business_label: req.business_label,
business_country,
business_label,
business_sub_label: req.business_sub_label,
};
@ -364,19 +418,16 @@ pub async fn create_payment_connector(
error.to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantConnectorAccount)
})?;
response.merchant_connector_id = Some(mca.merchant_connector_id);
response.connector_label = connector_label;
response.business_country = mca.business_country;
response.business_label = mca.business_label;
let mca_response = ForeignTryFrom::foreign_try_from(mca)?;
Ok(service_api::ApplicationResponse::Json(response))
Ok(service_api::ApplicationResponse::Json(mca_response))
}
pub async fn retrieve_payment_connector(
store: &dyn StorageInterface,
merchant_id: String,
merchant_connector_id: String,
) -> RouterResponse<api::MerchantConnector> {
) -> RouterResponse<api_models::admin::MerchantConnectorResponse> {
let _merchant_account = store
.find_merchant_account_by_merchant_id(&merchant_id)
.await
@ -395,14 +446,14 @@ pub async fn retrieve_payment_connector(
})?;
Ok(service_api::ApplicationResponse::Json(
mca.foreign_try_into()?,
ForeignTryFrom::foreign_try_from(mca)?,
))
}
pub async fn list_payment_connectors(
store: &dyn StorageInterface,
merchant_id: String,
) -> RouterResponse<Vec<api::MerchantConnector>> {
) -> RouterResponse<Vec<api_models::admin::MerchantConnectorResponse>> {
// Validate merchant account
store
.find_merchant_account_by_merchant_id(&merchant_id)
@ -432,7 +483,7 @@ pub async fn update_payment_connector(
merchant_id: &str,
merchant_connector_id: &str,
req: api_models::admin::MerchantConnectorUpdate,
) -> RouterResponse<api::MerchantConnector> {
) -> RouterResponse<api_models::admin::MerchantConnectorResponse> {
let _merchant_account = db
.find_merchant_account_by_merchant_id(merchant_id)
.await
@ -478,32 +529,9 @@ pub async fn update_payment_connector(
format!("Failed while updating MerchantConnectorAccount: id: {merchant_connector_id}")
})?;
let updated_pm_enabled = updated_mca.payment_methods_enabled.map(|pm| {
pm.into_iter()
.flat_map(|pm_value| {
ValueExt::<api_models::admin::PaymentMethodsEnabled>::parse_value(
pm_value,
"PaymentMethods",
)
})
.collect::<Vec<api_models::admin::PaymentMethodsEnabled>>()
});
let mca_response = ForeignTryFrom::foreign_try_from(updated_mca)?;
let response = api::MerchantConnector {
connector_type: updated_mca.connector_type.foreign_into(),
connector_name: updated_mca.connector_name,
merchant_connector_id: Some(updated_mca.merchant_connector_id),
connector_account_details: Some(Secret::new(updated_mca.connector_account_details)),
test_mode: updated_mca.test_mode,
disabled: updated_mca.disabled,
payment_methods_enabled: updated_pm_enabled,
metadata: updated_mca.metadata,
connector_label: updated_mca.connector_label,
business_country: updated_mca.business_country,
business_label: updated_mca.business_label,
business_sub_label: updated_mca.business_sub_label,
};
Ok(service_api::ApplicationResponse::Json(response))
Ok(service_api::ApplicationResponse::Json(mca_response))
}
pub async fn delete_payment_connector(

View File

@ -1285,14 +1285,14 @@ pub fn get_business_details(
business_country: Option<api_enums::CountryCode>,
business_label: Option<&String>,
merchant_account: &storage_models::merchant_account::MerchantAccount,
) -> Result<(api_enums::CountryCode, String), error_stack::Report<errors::ApiErrorResponse>> {
) -> RouterResult<(api_enums::CountryCode, String)> {
let (business_country, business_label) = match business_country.zip(business_label) {
Some((business_country, business_label)) => {
(business_country.to_owned(), business_label.to_owned())
}
None => {
// Parse the primary business details from merchant account
let primary_business_details: api_models::admin::PrimaryBusinessDetails =
let primary_business_details: Vec<api_models::admin::PrimaryBusinessDetails> =
merchant_account
.primary_business_details
.clone()
@ -1300,28 +1300,20 @@ pub fn get_business_details(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("failed to parse primary business details")?;
if primary_business_details.country.len() == 1
&& primary_business_details.business.len() == 1
{
let primary_business_country = primary_business_details
.country
.first()
.get_required_value("business_country")?
.to_owned();
let primary_business_label = primary_business_details
.business
.first()
.get_required_value("business_label")?
.to_owned();
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(primary_business_country),
business_country.unwrap_or(primary_business_details.country.to_owned()),
business_label
.map(ToString::to_string)
.unwrap_or(primary_business_label),
.unwrap_or(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"
}))?

View File

@ -149,7 +149,7 @@ Never share your secret api keys. Keep them guarded and secure.
api_models::enums::DisputeStage,
api_models::enums::DisputeStatus,
api_models::enums::CountryCode,
api_models::admin::MerchantConnector,
api_models::admin::MerchantConnectorCreate,
api_models::admin::PaymentMethodsEnabled,
api_models::disputes::DisputeResponse,
api_models::payments::AddressDetails,

View File

@ -172,7 +172,7 @@ pub async fn payment_connector_create(
state: web::Data<AppState>,
req: HttpRequest,
path: web::Path<String>,
json_payload: web::Json<admin::MerchantConnector>,
json_payload: web::Json<admin::MerchantConnectorCreate>,
) -> HttpResponse {
let flow = Flow::MerchantConnectorsCreate;
let merchant_id = path.into_inner();

View File

@ -1,17 +1,25 @@
pub use api_models::admin::{
MerchantAccountCreate, MerchantAccountDeleteResponse, MerchantAccountResponse,
MerchantAccountUpdate, MerchantConnector, MerchantConnectorDeleteResponse,
MerchantAccountUpdate, MerchantConnectorCreate, MerchantConnectorDeleteResponse,
MerchantConnectorDetails, MerchantConnectorDetailsWrap, MerchantConnectorId, MerchantDetails,
MerchantId, PaymentMethodsEnabled, RoutingAlgorithm, ToggleKVRequest, ToggleKVResponse,
WebhookDetails,
};
use common_utils::ext_traits::ValueExt;
use crate::types::{storage, transformers::ForeignFrom};
use crate::{
core::errors,
types::{storage, transformers::ForeignTryFrom},
};
impl ForeignFrom<storage::MerchantAccount> for MerchantAccountResponse {
fn foreign_from(value: storage::MerchantAccount) -> Self {
let item = value;
Self {
impl ForeignTryFrom<storage::MerchantAccount> for MerchantAccountResponse {
type Error = error_stack::Report<errors::ParsingError>;
fn foreign_try_from(item: storage::MerchantAccount) -> Result<Self, Self::Error> {
let primary_business_details: Vec<api_models::admin::PrimaryBusinessDetails> = item
.primary_business_details
.parse_value("primary_business_details")?;
Ok(Self {
merchant_id: item.merchant_id,
merchant_name: item.merchant_name,
api_key: item.api_key,
@ -27,7 +35,7 @@ impl ForeignFrom<storage::MerchantAccount> for MerchantAccountResponse {
publishable_key: item.publishable_key,
metadata: item.metadata,
locker_id: item.locker_id,
primary_business_details: item.primary_business_details.into(),
}
primary_business_details,
})
}
}

View File

@ -1,6 +1,7 @@
use api_models::enums as api_enums;
use common_utils::ext_traits::ValueExt;
use error_stack::ResultExt;
use masking::Secret;
use storage_models::enums as storage_enums;
use crate::{
@ -336,37 +337,6 @@ impl<'a> ForeignFrom<&'a storage::Address> for api_types::Address {
}
}
impl ForeignTryFrom<storage::MerchantConnectorAccount> for api_models::admin::MerchantConnector {
type Error = error_stack::Report<errors::ApiErrorResponse>;
fn foreign_try_from(item: storage::MerchantConnectorAccount) -> Result<Self, Self::Error> {
let merchant_ca = item;
let payment_methods_enabled = match merchant_ca.payment_methods_enabled {
Some(val) => serde_json::Value::Array(val)
.parse_value("PaymentMethods")
.change_context(errors::ApiErrorResponse::InternalServerError)?,
None => None,
};
Ok(Self {
connector_type: merchant_ca.connector_type.foreign_into(),
connector_name: merchant_ca.connector_name,
merchant_connector_id: Some(merchant_ca.merchant_connector_id),
connector_account_details: Some(masking::Secret::new(
merchant_ca.connector_account_details,
)),
test_mode: merchant_ca.test_mode,
disabled: merchant_ca.disabled,
metadata: merchant_ca.metadata,
payment_methods_enabled,
connector_label: merchant_ca.connector_label,
business_country: merchant_ca.business_country,
business_label: merchant_ca.business_label,
business_sub_label: merchant_ca.business_sub_label,
})
}
}
impl ForeignFrom<api_models::enums::PaymentMethodType>
for storage_models::enums::PaymentMethodType
{
@ -551,3 +521,34 @@ impl ForeignFrom<storage_models::cards_info::CardInfo>
}
}
}
impl ForeignTryFrom<storage_models::merchant_connector_account::MerchantConnectorAccount>
for api_models::admin::MerchantConnectorResponse
{
type Error = error_stack::Report<errors::ApiErrorResponse>;
fn foreign_try_from(
item: storage_models::merchant_connector_account::MerchantConnectorAccount,
) -> Result<Self, Self::Error> {
let payment_methods_enabled = match item.payment_methods_enabled {
Some(val) => serde_json::Value::Array(val)
.parse_value("PaymentMethods")
.change_context(errors::ApiErrorResponse::InternalServerError)?,
None => None,
};
Ok(Self {
connector_type: item.connector_type.foreign_into(),
connector_name: item.connector_name,
connector_label: item.connector_label,
merchant_connector_id: item.merchant_connector_id,
connector_account_details: Secret::new(item.connector_account_details),
test_mode: item.test_mode,
disabled: item.disabled,
payment_methods_enabled,
metadata: item.metadata,
business_country: item.business_country,
business_label: item.business_label,
business_sub_label: item.business_sub_label,
})
}
}

View File

@ -0,0 +1,7 @@
-- This file should undo anything in `up.sql`
UPDATE merchant_account
SET primary_business_details = '{"country": ["US"], "business": ["default"]}';
ALTER TABLE merchant_connector_account
ALTER COLUMN business_sub_label
SET DEFAULT 'default';

View File

@ -0,0 +1,7 @@
-- This change will allow older merchant accounts to be used with new changes
UPDATE merchant_account
SET primary_business_details = '[{"country": "US", "business": "default"}]';
-- Since this field is optional, default is not required
ALTER TABLE merchant_connector_account
ALTER COLUMN business_sub_label DROP DEFAULT;