feat(business_profile): introduce business profile v2 update endpoint (#5641)

This commit is contained in:
Sanchith Hegde
2024-08-21 14:27:18 +05:30
committed by GitHub
parent cc5b06e725
commit beb4fb050f
23 changed files with 689 additions and 223 deletions

View File

@ -3344,8 +3344,8 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate {
helpers::validate_session_expiry(session_expiry.to_owned())?;
}
if let Some(intent_fulfillment_expiry) = &self.intent_fulfillment_time {
helpers::validate_intent_fulfillment_expiry(intent_fulfillment_expiry.to_owned())?;
if let Some(intent_fulfillment_expiry) = self.intent_fulfillment_time {
helpers::validate_intent_fulfillment_expiry(intent_fulfillment_expiry)?;
}
if let Some(ref routing_algorithm) = self.routing_algorithm {
@ -3543,7 +3543,7 @@ impl BusinessProfileCreateBridge for api::BusinessProfileCreate {
payout_routing_algorithm_id: None,
order_fulfillment_time: self
.order_fulfillment_time
.map(|order_fulfillment_time| i64::from(order_fulfillment_time.into_inner()))
.map(|order_fulfillment_time| order_fulfillment_time.into_inner())
.or(Some(common_utils::consts::DEFAULT_ORDER_FULFILLMENT_TIME)),
order_fulfillment_time_origin: self.order_fulfillment_time_origin,
default_fallback_routing: None,
@ -3695,10 +3695,214 @@ pub async fn delete_business_profile(
Ok(service_api::ApplicationResponse::Json(delete_result))
}
#[cfg(feature = "olap")]
#[async_trait::async_trait]
trait BusinessProfileUpdateBridge {
async fn get_update_business_profile_object(
self,
state: &SessionState,
key_store: &domain::MerchantKeyStore,
) -> RouterResult<domain::BusinessProfileUpdate>;
}
#[cfg(all(
feature = "olap",
any(feature = "v1", feature = "v2"),
not(feature = "business_profile_v2")
))]
#[async_trait::async_trait]
impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate {
async fn get_update_business_profile_object(
self,
state: &SessionState,
key_store: &domain::MerchantKeyStore,
) -> RouterResult<domain::BusinessProfileUpdate> {
if let Some(session_expiry) = &self.session_expiry {
helpers::validate_session_expiry(session_expiry.to_owned())?;
}
if let Some(intent_fulfillment_expiry) = self.intent_fulfillment_time {
helpers::validate_intent_fulfillment_expiry(intent_fulfillment_expiry)?;
}
let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);
if let Some(ref routing_algorithm) = self.routing_algorithm {
let _: api_models::routing::RoutingAlgorithm = routing_algorithm
.clone()
.parse_value("RoutingAlgorithm")
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "routing_algorithm",
})
.attach_printable("Invalid routing algorithm given")?;
}
let payment_link_config = self
.payment_link_config
.map(|payment_link_conf| match payment_link_conf.validate() {
Ok(_) => Ok(payment_link_conf.foreign_into()),
Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: e.to_string()
})),
})
.transpose()?;
let extended_card_info_config = self
.extended_card_info_config
.as_ref()
.map(|config| {
config.encode_to_value().change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "extended_card_info_config",
},
)
})
.transpose()?
.map(Secret::new);
let outgoing_webhook_custom_http_headers = self
.outgoing_webhook_custom_http_headers
.async_map(|headers| cards::create_encrypted_data(state, key_store, headers))
.await
.transpose()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;
let payout_link_config = self
.payout_link_config
.map(|payout_conf| match payout_conf.config.validate() {
Ok(_) => Ok(payout_conf.foreign_into()),
Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: e.to_string()
})),
})
.transpose()?;
Ok(domain::BusinessProfileUpdate::Update(Box::new(
domain::BusinessProfileGeneralUpdate {
profile_name: self.profile_name,
return_url: self.return_url.map(|return_url| return_url.to_string()),
enable_payment_response_hash: self.enable_payment_response_hash,
payment_response_hash_key: self.payment_response_hash_key,
redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
webhook_details,
metadata: self.metadata,
routing_algorithm: self.routing_algorithm,
intent_fulfillment_time: self.intent_fulfillment_time.map(i64::from),
frm_routing_algorithm: self.frm_routing_algorithm,
#[cfg(feature = "payouts")]
payout_routing_algorithm: self.payout_routing_algorithm,
#[cfg(not(feature = "payouts"))]
payout_routing_algorithm: None,
applepay_verified_domains: self.applepay_verified_domains,
payment_link_config,
session_expiry: self.session_expiry.map(i64::from),
authentication_connector_details: self
.authentication_connector_details
.map(ForeignInto::foreign_into),
payout_link_config,
extended_card_info_config,
use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing,
collect_shipping_details_from_wallet_connector: self
.collect_shipping_details_from_wallet_connector,
collect_billing_details_from_wallet_connector: self
.collect_billing_details_from_wallet_connector,
is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers
.map(Into::into),
},
)))
}
}
#[cfg(all(feature = "olap", feature = "v2", feature = "business_profile_v2"))]
#[async_trait::async_trait]
impl BusinessProfileUpdateBridge for api::BusinessProfileUpdate {
async fn get_update_business_profile_object(
self,
state: &SessionState,
key_store: &domain::MerchantKeyStore,
) -> RouterResult<domain::BusinessProfileUpdate> {
if let Some(session_expiry) = &self.session_expiry {
helpers::validate_session_expiry(session_expiry.to_owned())?;
}
let webhook_details = self.webhook_details.map(ForeignInto::foreign_into);
let payment_link_config = self
.payment_link_config
.map(|payment_link_conf| match payment_link_conf.validate() {
Ok(_) => Ok(payment_link_conf.foreign_into()),
Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: e.to_string()
})),
})
.transpose()?;
let extended_card_info_config = self
.extended_card_info_config
.as_ref()
.map(|config| {
config.encode_to_value().change_context(
errors::ApiErrorResponse::InvalidDataValue {
field_name: "extended_card_info_config",
},
)
})
.transpose()?
.map(Secret::new);
let outgoing_webhook_custom_http_headers = self
.outgoing_webhook_custom_http_headers
.async_map(|headers| cards::create_encrypted_data(state, key_store, headers))
.await
.transpose()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;
let payout_link_config = self
.payout_link_config
.map(|payout_conf| match payout_conf.config.validate() {
Ok(_) => Ok(payout_conf.foreign_into()),
Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: e.to_string()
})),
})
.transpose()?;
Ok(domain::BusinessProfileUpdate::Update(Box::new(
domain::BusinessProfileGeneralUpdate {
profile_name: self.profile_name,
return_url: self.return_url.map(|return_url| return_url.to_string()),
enable_payment_response_hash: self.enable_payment_response_hash,
payment_response_hash_key: self.payment_response_hash_key,
redirect_to_merchant_with_http_post: self.redirect_to_merchant_with_http_post,
webhook_details,
metadata: self.metadata,
applepay_verified_domains: self.applepay_verified_domains,
payment_link_config,
session_expiry: self.session_expiry.map(i64::from),
authentication_connector_details: self
.authentication_connector_details
.map(ForeignInto::foreign_into),
payout_link_config,
extended_card_info_config,
use_billing_as_payment_method_billing: self.use_billing_as_payment_method_billing,
collect_shipping_details_from_wallet_connector: self
.collect_shipping_details_from_wallet_connector,
collect_billing_details_from_wallet_connector: self
.collect_billing_details_from_wallet_connector,
is_connector_agnostic_mit_enabled: self.is_connector_agnostic_mit_enabled,
outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers
.map(Into::into),
order_fulfillment_time: self
.order_fulfillment_time
.map(|order_fulfillment_time| order_fulfillment_time.into_inner()),
order_fulfillment_time_origin: self.order_fulfillment_time_origin,
},
)))
}
}
#[cfg(feature = "olap")]
pub async fn update_business_profile(
state: SessionState,
profile_id: &str,
@ -3730,100 +3934,9 @@ pub async fn update_business_profile(
})?
}
if let Some(session_expiry) = &request.session_expiry {
helpers::validate_session_expiry(session_expiry.to_owned())?;
}
if let Some(intent_fulfillment_expiry) = &request.intent_fulfillment_time {
helpers::validate_intent_fulfillment_expiry(intent_fulfillment_expiry.to_owned())?;
}
let webhook_details = request.webhook_details.map(ForeignInto::foreign_into);
if let Some(ref routing_algorithm) = request.routing_algorithm {
let _: api_models::routing::RoutingAlgorithm = routing_algorithm
.clone()
.parse_value("RoutingAlgorithm")
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "routing_algorithm",
})
.attach_printable("Invalid routing algorithm given")?;
}
let payment_link_config = request
.payment_link_config
.map(|payment_link_conf| match payment_link_conf.validate() {
Ok(_) => Ok(payment_link_conf.foreign_into()),
Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: e.to_string()
})),
})
.transpose()?;
let extended_card_info_config = request
.extended_card_info_config
.as_ref()
.map(|config| {
config
.encode_to_value()
.change_context(errors::ApiErrorResponse::InvalidDataValue {
field_name: "extended_card_info_config",
})
})
.transpose()?
.map(Secret::new);
let outgoing_webhook_custom_http_headers = request
.outgoing_webhook_custom_http_headers
.async_map(|headers| cards::create_encrypted_data(&state, &key_store, headers))
.await
.transpose()
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?;
let payout_link_config = request
.payout_link_config
.map(|payout_conf| match payout_conf.config.validate() {
Ok(_) => Ok(payout_conf.foreign_into()),
Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: e.to_string()
})),
})
.transpose()?;
let business_profile_update =
domain::BusinessProfileUpdate::Update(Box::new(domain::BusinessProfileGeneralUpdate {
profile_name: request.profile_name,
return_url: request.return_url.map(|return_url| return_url.to_string()),
enable_payment_response_hash: request.enable_payment_response_hash,
payment_response_hash_key: request.payment_response_hash_key,
redirect_to_merchant_with_http_post: request.redirect_to_merchant_with_http_post,
webhook_details,
metadata: request.metadata,
routing_algorithm: request.routing_algorithm,
intent_fulfillment_time: request.intent_fulfillment_time.map(i64::from),
frm_routing_algorithm: request.frm_routing_algorithm,
#[cfg(feature = "payouts")]
payout_routing_algorithm: request.payout_routing_algorithm,
#[cfg(not(feature = "payouts"))]
payout_routing_algorithm: None,
is_recon_enabled: None,
applepay_verified_domains: request.applepay_verified_domains,
payment_link_config,
session_expiry: request.session_expiry.map(i64::from),
authentication_connector_details: request
.authentication_connector_details
.map(ForeignInto::foreign_into),
payout_link_config,
extended_card_info_config,
use_billing_as_payment_method_billing: request.use_billing_as_payment_method_billing,
collect_shipping_details_from_wallet_connector: request
.collect_shipping_details_from_wallet_connector,
collect_billing_details_from_wallet_connector: request
.collect_billing_details_from_wallet_connector,
is_connector_agnostic_mit_enabled: request.is_connector_agnostic_mit_enabled,
outgoing_webhook_custom_http_headers: outgoing_webhook_custom_http_headers
.map(Into::into),
}));
let business_profile_update = request
.get_update_business_profile_object(&state, &key_store)
.await?;
let updated_business_profile = db
.update_business_profile_by_profile_id(
@ -3843,15 +3956,7 @@ pub async fn update_business_profile(
.attach_printable("Failed to parse business profile details")?,
))
}
#[cfg(all(feature = "v2", feature = "business_profile_v2"))]
pub async fn update_business_profile(
_state: SessionState,
_profile_id: &str,
_merchant_id: &id_type::MerchantId,
_request: api::BusinessProfileUpdate,
) -> RouterResponse<api::BusinessProfileResponse> {
todo!()
}
#[cfg(all(
feature = "v2",
feature = "routing_v2",

View File

@ -861,7 +861,7 @@ impl<'a> VerifyIdForUpdateCustomer<'a> {
.find_customer_by_global_id(
self.key_manager_state,
&id,
&self.merchant_account.get_id(),
self.merchant_account.get_id(),
self.key_store,
self.merchant_account.storage_scheme,
)
@ -1027,7 +1027,7 @@ impl CustomerUpdateBridge for customers::CustomerUpdateRequest {
default_shipping_address: encrypted_customer_shipping_address.map(Into::into),
default_payment_method_id: Some(self.default_payment_method_id.clone()),
},
&key_store,
key_store,
merchant_account.storage_scheme,
)
.await

View File

@ -1218,7 +1218,7 @@ pub async fn create_recipient(
&state.into(),
global_id,
customer,
&merchant_account.get_id(),
merchant_account.get_id(),
updated_customer,
key_store,
merchant_account.storage_scheme,

View File

@ -117,7 +117,7 @@ where
async fn find_customer_by_global_id(
&self,
state: &KeyManagerState,
id: &String,
id: &str,
merchant_id: &id_type::MerchantId,
key_store: &domain::MerchantKeyStore,
storage_scheme: MerchantStorageScheme,
@ -719,7 +719,7 @@ mod storage {
async fn find_customer_by_global_id(
&self,
state: &KeyManagerState,
id: &String,
id: &str,
_merchant_id: &id_type::MerchantId,
key_store: &domain::MerchantKeyStore,
storage_scheme: MerchantStorageScheme,
@ -1154,7 +1154,7 @@ mod storage {
async fn find_customer_by_global_id(
&self,
state: &KeyManagerState,
id: &String,
id: &str,
merchant_id: &id_type::MerchantId,
key_store: &domain::MerchantKeyStore,
_storage_scheme: MerchantStorageScheme,
@ -1372,7 +1372,7 @@ impl CustomerInterface for MockDb {
async fn find_customer_by_global_id(
&self,
_state: &KeyManagerState,
_id: &String,
_id: &str,
_merchant_id: &id_type::MerchantId,
_key_store: &domain::MerchantKeyStore,
_storage_scheme: MerchantStorageScheme,

View File

@ -498,7 +498,7 @@ impl CustomerInterface for KafkaStore {
async fn find_customer_by_global_id(
&self,
state: &KeyManagerState,
id: &String,
id: &str,
merchant_id: &id_type::MerchantId,
key_store: &domain::MerchantKeyStore,
storage_scheme: MerchantStorageScheme,

View File

@ -955,6 +955,11 @@ pub async fn business_profile_retrieve(
.await
}
#[cfg(all(
feature = "olap",
any(feature = "v1", feature = "v2"),
not(feature = "business_profile_v2")
))]
#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileUpdate))]
pub async fn business_profile_update(
state: web::Data<AppState>,
@ -983,6 +988,44 @@ pub async fn business_profile_update(
))
.await
}
#[cfg(all(feature = "v2", feature = "business_profile_v2"))]
#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileUpdate))]
pub async fn business_profile_update(
state: web::Data<AppState>,
req: HttpRequest,
path: web::Path<String>,
json_payload: web::Json<api_models::admin::BusinessProfileUpdate>,
) -> HttpResponse {
let flow = Flow::BusinessProfileUpdate;
let profile_id = path.into_inner();
let merchant_id = match HeaderMapStruct::from(&req).get_merchant_id_from_header() {
Ok(val) => val,
Err(err) => {
return api::log_and_return_error_response(err);
}
};
Box::pin(api::server_wrap(
flow,
state,
&req,
json_payload.into_inner(),
|state, _, req, _| update_business_profile(state, &profile_id, &merchant_id, req),
auth::auth_type(
&auth::AdminApiAuth,
&auth::JWTAuthMerchantFromRoute {
merchant_id: merchant_id.clone(),
required_permission: Permission::MerchantAccountWrite,
},
req.headers(),
),
api_locking::LockAction::NotApplicable,
))
.await
}
#[instrument(skip_all, fields(flow = ?Flow::BusinessProfileDelete))]
pub async fn business_profile_delete(
state: web::Data<AppState>,

View File

@ -1469,7 +1469,11 @@ impl BusinessProfile {
.service(web::resource("").route(web::post().to(business_profile_create)))
.service(
web::scope("/{profile_id}")
.service(web::resource("").route(web::get().to(business_profile_retrieve)))
.service(
web::resource("")
.route(web::get().to(business_profile_retrieve))
.route(web::post().to(business_profile_update)),
)
.service(
web::resource("/fallback_routing")
.route(web::get().to(routing::routing_retrieve_default_config))

View File

@ -185,7 +185,7 @@ impl ForeignTryFrom<domain::BusinessProfile> for BusinessProfileResponse {
let order_fulfillment_time = item
.order_fulfillment_time
.map(|time| api_models::admin::OrderFulfillmentTime::new(time))
.map(api_models::admin::OrderFulfillmentTime::new)
.transpose()
.change_context(errors::ParsingError::IntegerOverflow)?;