mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
feat(authentication): added profile acquirer create module (#8155)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -61,6 +61,7 @@ pub mod verification;
|
||||
pub mod verify_connector;
|
||||
pub mod webhooks;
|
||||
|
||||
pub mod profile_acquirer;
|
||||
pub mod unified_authentication_service;
|
||||
|
||||
#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
|
||||
|
||||
@ -372,13 +372,13 @@ pub async fn payouts_confirm_core(
|
||||
merchant_context: domain::MerchantContext,
|
||||
req: payouts::PayoutCreateRequest,
|
||||
) -> RouterResponse<payouts::PayoutCreateResponse> {
|
||||
let mut payout_data = make_payout_data(
|
||||
let mut payout_data = Box::pin(make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&payouts::PayoutRequest::PayoutCreateRequest(Box::new(req.to_owned())),
|
||||
&state.locale,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
let payout_attempt = payout_data.payout_attempt.to_owned();
|
||||
let status = payout_attempt.status;
|
||||
@ -435,13 +435,13 @@ pub async fn payouts_update_core(
|
||||
req: payouts::PayoutCreateRequest,
|
||||
) -> RouterResponse<payouts::PayoutCreateResponse> {
|
||||
let payout_id = req.payout_id.clone().get_required_value("payout_id")?;
|
||||
let mut payout_data = make_payout_data(
|
||||
let mut payout_data = Box::pin(make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&payouts::PayoutRequest::PayoutCreateRequest(Box::new(req.to_owned())),
|
||||
&state.locale,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
let payout_attempt = payout_data.payout_attempt.to_owned();
|
||||
@ -509,13 +509,13 @@ pub async fn payouts_retrieve_core(
|
||||
profile_id: Option<common_utils::id_type::ProfileId>,
|
||||
req: payouts::PayoutRetrieveRequest,
|
||||
) -> RouterResponse<payouts::PayoutCreateResponse> {
|
||||
let mut payout_data = make_payout_data(
|
||||
let mut payout_data = Box::pin(make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
profile_id,
|
||||
&payouts::PayoutRequest::PayoutRetrieveRequest(req.to_owned()),
|
||||
&state.locale,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
let payout_attempt = payout_data.payout_attempt.to_owned();
|
||||
let status = payout_attempt.status;
|
||||
@ -550,13 +550,13 @@ pub async fn payouts_cancel_core(
|
||||
merchant_context: domain::MerchantContext,
|
||||
req: payouts::PayoutActionRequest,
|
||||
) -> RouterResponse<payouts::PayoutCreateResponse> {
|
||||
let mut payout_data = make_payout_data(
|
||||
let mut payout_data = Box::pin(make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&payouts::PayoutRequest::PayoutActionRequest(req.to_owned()),
|
||||
&state.locale,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
let payout_attempt = payout_data.payout_attempt.to_owned();
|
||||
@ -641,13 +641,13 @@ pub async fn payouts_fulfill_core(
|
||||
merchant_context: domain::MerchantContext,
|
||||
req: payouts::PayoutActionRequest,
|
||||
) -> RouterResponse<payouts::PayoutCreateResponse> {
|
||||
let mut payout_data = make_payout_data(
|
||||
let mut payout_data = Box::pin(make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&payouts::PayoutRequest::PayoutActionRequest(req.to_owned()),
|
||||
&state.locale,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
let payout_attempt = payout_data.payout_attempt.to_owned();
|
||||
|
||||
114
crates/router/src/core/profile_acquirer.rs
Normal file
114
crates/router/src/core/profile_acquirer.rs
Normal file
@ -0,0 +1,114 @@
|
||||
use api_models::profile_acquirer;
|
||||
use common_utils::types::keymanager::KeyManagerState;
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use crate::{
|
||||
core::errors::{self, utils::StorageErrorExt, RouterResponse},
|
||||
services::api,
|
||||
types::domain,
|
||||
SessionState,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
pub async fn create_profile_acquirer(
|
||||
state: SessionState,
|
||||
request: profile_acquirer::ProfileAcquirerCreate,
|
||||
merchant_context: domain::MerchantContext,
|
||||
) -> RouterResponse<profile_acquirer::ProfileAcquirerResponse> {
|
||||
let db = state.store.as_ref();
|
||||
let profile_acquirer_id = common_utils::generate_profile_acquirer_id_of_default_length();
|
||||
let key_manager_state: KeyManagerState = (&state).into();
|
||||
let merchant_key_store = merchant_context.get_merchant_key_store();
|
||||
|
||||
let mut business_profile = db
|
||||
.find_business_profile_by_profile_id(
|
||||
&key_manager_state,
|
||||
merchant_key_store,
|
||||
&request.profile_id,
|
||||
)
|
||||
.await
|
||||
.to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
|
||||
id: request.profile_id.get_string_repr().to_owned(),
|
||||
})?;
|
||||
|
||||
let incoming_acquirer_config = common_types::domain::AcquirerConfig {
|
||||
acquirer_assigned_merchant_id: request.acquirer_assigned_merchant_id.clone(),
|
||||
merchant_name: request.merchant_name.clone(),
|
||||
merchant_country_code: request.merchant_country_code,
|
||||
network: request.network.clone(),
|
||||
acquirer_bin: request.acquirer_bin.clone(),
|
||||
acquirer_ica: request.acquirer_ica.clone(),
|
||||
acquirer_fraud_rate: request.acquirer_fraud_rate,
|
||||
};
|
||||
|
||||
// Check for duplicates before proceeding
|
||||
|
||||
business_profile
|
||||
.acquirer_config_map
|
||||
.as_ref()
|
||||
.map_or(Ok(()), |configs_wrapper| {
|
||||
match configs_wrapper.0.values().any(|existing_config| existing_config == &incoming_acquirer_config) {
|
||||
true => Err(error_stack::report!(
|
||||
errors::ApiErrorResponse::GenericDuplicateError {
|
||||
message: format!(
|
||||
"Duplicate acquirer configuration found for profile_id: {}. Conflicting configuration: {:?}",
|
||||
request.profile_id.get_string_repr(),
|
||||
incoming_acquirer_config
|
||||
),
|
||||
}
|
||||
)),
|
||||
false => Ok(()),
|
||||
}
|
||||
})?;
|
||||
|
||||
// Get a mutable reference to the HashMap inside AcquirerConfigMap,
|
||||
// initializing if it's None or the inner HashMap is not present.
|
||||
let configs_map = &mut business_profile
|
||||
.acquirer_config_map
|
||||
.get_or_insert_with(|| {
|
||||
common_types::domain::AcquirerConfigMap(std::collections::HashMap::new())
|
||||
})
|
||||
.0;
|
||||
|
||||
configs_map.insert(
|
||||
profile_acquirer_id.clone(),
|
||||
incoming_acquirer_config.clone(),
|
||||
);
|
||||
|
||||
let profile_update = domain::ProfileUpdate::AcquirerConfigMapUpdate {
|
||||
acquirer_config_map: business_profile.acquirer_config_map.clone(),
|
||||
};
|
||||
let updated_business_profile = db
|
||||
.update_profile_by_profile_id(
|
||||
&key_manager_state,
|
||||
merchant_key_store,
|
||||
business_profile,
|
||||
profile_update,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to update business profile with new acquirer config")?;
|
||||
|
||||
let updated_acquire_details = updated_business_profile
|
||||
.acquirer_config_map
|
||||
.as_ref()
|
||||
.and_then(|acquirer_configs_wrapper| acquirer_configs_wrapper.0.get(&profile_acquirer_id))
|
||||
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to get updated acquirer config")?;
|
||||
|
||||
let response = profile_acquirer::ProfileAcquirerResponse {
|
||||
profile_acquirer_id,
|
||||
profile_id: request.profile_id.clone(),
|
||||
acquirer_assigned_merchant_id: updated_acquire_details
|
||||
.acquirer_assigned_merchant_id
|
||||
.clone(),
|
||||
merchant_name: updated_acquire_details.merchant_name.clone(),
|
||||
merchant_country_code: updated_acquire_details.merchant_country_code,
|
||||
network: updated_acquire_details.network.clone(),
|
||||
acquirer_bin: updated_acquire_details.acquirer_bin.clone(),
|
||||
acquirer_ica: updated_acquire_details.acquirer_ica.clone(),
|
||||
acquirer_fraud_rate: updated_acquire_details.acquirer_fraud_rate,
|
||||
};
|
||||
|
||||
Ok(api::ApplicationResponse::Json(response))
|
||||
}
|
||||
@ -822,13 +822,13 @@ async fn payouts_incoming_webhook_flow(
|
||||
payout_id: payouts.payout_id.clone(),
|
||||
});
|
||||
|
||||
let payout_data = payouts::make_payout_data(
|
||||
let payout_data = Box::pin(payouts::make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&action_req,
|
||||
common_utils::consts::DEFAULT_LOCALE,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
let updated_payout_attempt = db
|
||||
|
||||
@ -10,8 +10,7 @@ use common_utils::{
|
||||
#[cfg(feature = "v2")]
|
||||
use diesel_models::ephemeral_key::{ClientSecretType, ClientSecretTypeNew};
|
||||
use diesel_models::{
|
||||
enums,
|
||||
enums::ProcessTrackerStatus,
|
||||
enums::{self, ProcessTrackerStatus},
|
||||
ephemeral_key::{EphemeralKey, EphemeralKeyNew},
|
||||
reverse_lookup::{ReverseLookup, ReverseLookupNew},
|
||||
user_role as user_storage,
|
||||
|
||||
@ -132,7 +132,8 @@ pub fn mk_app(
|
||||
{
|
||||
server_app = server_app
|
||||
.service(routes::ProfileNew::server(state.clone()))
|
||||
.service(routes::Forex::server(state.clone()));
|
||||
.service(routes::Forex::server(state.clone()))
|
||||
.service(routes::ProfileAcquirer::server(state.clone()));
|
||||
}
|
||||
|
||||
server_app = server_app.service(routes::Profile::server(state.clone()));
|
||||
|
||||
@ -41,6 +41,8 @@ pub mod payouts;
|
||||
pub mod pm_auth;
|
||||
pub mod poll;
|
||||
#[cfg(feature = "olap")]
|
||||
pub mod profile_acquirer;
|
||||
#[cfg(feature = "olap")]
|
||||
pub mod profiles;
|
||||
#[cfg(feature = "recon")]
|
||||
pub mod recon;
|
||||
@ -86,8 +88,8 @@ pub use self::app::{
|
||||
ApiKeys, AppState, ApplePayCertificatesMigration, Cache, Cards, Configs, ConnectorOnboarding,
|
||||
Customers, Disputes, EphemeralKey, FeatureMatrix, Files, Forex, Gsm, Health, Hypersense,
|
||||
Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments,
|
||||
Poll, ProcessTracker, Profile, ProfileNew, Refunds, Relay, RelayWebhooks, SessionState,
|
||||
ThreeDsDecisionRule, User, Webhooks,
|
||||
Poll, ProcessTracker, Profile, ProfileAcquirer, ProfileNew, Refunds, Relay, RelayWebhooks,
|
||||
SessionState, ThreeDsDecisionRule, User, Webhooks,
|
||||
};
|
||||
#[cfg(feature = "olap")]
|
||||
pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents};
|
||||
|
||||
@ -85,6 +85,8 @@ use crate::routes::cards_info::{
|
||||
use crate::routes::feature_matrix;
|
||||
#[cfg(all(feature = "frm", feature = "oltp"))]
|
||||
use crate::routes::fraud_check as frm_routes;
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
use crate::routes::profile_acquirer;
|
||||
#[cfg(all(feature = "recon", feature = "olap"))]
|
||||
use crate::routes::recon as recon_routes;
|
||||
pub use crate::{
|
||||
@ -2645,3 +2647,17 @@ impl ProcessTracker {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "olap")]
|
||||
pub struct ProfileAcquirer;
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
impl ProfileAcquirer {
|
||||
pub fn server(state: AppState) -> Scope {
|
||||
web::scope("/profile_acquirer")
|
||||
.app_data(web::Data::new(state))
|
||||
.service(
|
||||
web::resource("").route(web::post().to(profile_acquirer::create_profile_acquirer)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ pub enum ApiIdentifier {
|
||||
PaymentMethodSession,
|
||||
ProcessTracker,
|
||||
Proxy,
|
||||
ProfileAcquirer,
|
||||
ThreeDsDecisionRule,
|
||||
GenericTokenization,
|
||||
}
|
||||
@ -345,6 +346,7 @@ impl From<Flow> for ApiIdentifier {
|
||||
Flow::RevenueRecoveryRetrieve => Self::ProcessTracker,
|
||||
Flow::Proxy => Self::Proxy,
|
||||
|
||||
Flow::ProfileAcquirerCreate => Self::ProfileAcquirer,
|
||||
Flow::ThreeDsDecisionRuleExecute => Self::ThreeDsDecisionRule,
|
||||
Flow::TokenizationCreate | Flow::TokenizationRetrieve => Self::GenericTokenization,
|
||||
}
|
||||
|
||||
50
crates/router/src/routes/profile_acquirer.rs
Normal file
50
crates/router/src/routes/profile_acquirer.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use api_models::profile_acquirer::ProfileAcquirerCreate;
|
||||
use router_env::{instrument, tracing, Flow};
|
||||
|
||||
use super::app::AppState;
|
||||
use crate::{
|
||||
core::api_locking,
|
||||
services::{api, authentication as auth, authorization::permissions::Permission},
|
||||
types::domain,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "olap", feature = "v1"))]
|
||||
#[instrument(skip_all, fields(flow = ?Flow::ProfileAcquirerCreate))]
|
||||
pub async fn create_profile_acquirer(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
json_payload: web::Json<ProfileAcquirerCreate>,
|
||||
) -> HttpResponse {
|
||||
let flow = Flow::ProfileAcquirerCreate;
|
||||
let payload = json_payload.into_inner();
|
||||
|
||||
Box::pin(api::server_wrap(
|
||||
flow,
|
||||
state,
|
||||
&req,
|
||||
payload,
|
||||
|state: super::SessionState, auth_data, req, _| {
|
||||
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
|
||||
domain::Context(auth_data.merchant_account, auth_data.key_store),
|
||||
));
|
||||
crate::core::profile_acquirer::create_profile_acquirer(
|
||||
state,
|
||||
req,
|
||||
merchant_context.clone(),
|
||||
)
|
||||
},
|
||||
auth::auth_type(
|
||||
&auth::HeaderAuth(auth::ApiKeyAuth {
|
||||
is_connected_allowed: false,
|
||||
is_platform_allowed: true,
|
||||
}),
|
||||
&auth::JWTAuth {
|
||||
permission: Permission::ProfileAccountWrite,
|
||||
},
|
||||
req.headers(),
|
||||
),
|
||||
api_locking::LockAction::NotApplicable,
|
||||
))
|
||||
.await
|
||||
}
|
||||
@ -197,6 +197,7 @@ impl ForeignTryFrom<domain::Profile> for ProfileResponse {
|
||||
is_debit_routing_enabled: Some(item.is_debit_routing_enabled),
|
||||
merchant_business_country: item.merchant_business_country,
|
||||
is_pre_network_tokenization_enabled: item.is_pre_network_tokenization_enabled,
|
||||
acquirer_configs: item.acquirer_config_map,
|
||||
is_iframe_redirection_enabled: item.is_iframe_redirection_enabled,
|
||||
})
|
||||
}
|
||||
|
||||
@ -53,9 +53,14 @@ impl ProcessTrackerWorkflow<SessionState> for AttachPayoutAccountWorkflow {
|
||||
merchant_account.clone(),
|
||||
key_store.clone(),
|
||||
)));
|
||||
let mut payout_data =
|
||||
payouts::make_payout_data(state, &merchant_context, None, &request, DEFAULT_LOCALE)
|
||||
.await?;
|
||||
let mut payout_data = Box::pin(payouts::make_payout_data(
|
||||
state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&request,
|
||||
DEFAULT_LOCALE,
|
||||
))
|
||||
.await?;
|
||||
|
||||
payouts::payouts_core(state, &merchant_context, &mut payout_data, None, None).await?;
|
||||
|
||||
|
||||
@ -542,13 +542,13 @@ async fn get_outgoing_webhook_content_and_event_type(
|
||||
payout_models::PayoutActionRequest { payout_id },
|
||||
);
|
||||
|
||||
let payout_data = payouts::make_payout_data(
|
||||
let payout_data = Box::pin(payouts::make_payout_data(
|
||||
&state,
|
||||
&merchant_context,
|
||||
None,
|
||||
&request,
|
||||
DEFAULT_LOCALE,
|
||||
)
|
||||
))
|
||||
.await?;
|
||||
|
||||
let router_response =
|
||||
|
||||
Reference in New Issue
Block a user