mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +08:00
feat(router): add merchantId authentication for Payments v2 (#8239)
Co-authored-by: Aishwariyaa Anand <aishwariyaa.anand@Aishwariyaa-Anand-C3PGW02T6Y.local>
This commit is contained in:
committed by
GitHub
parent
4fa7b12400
commit
45d4ebfde3
@ -162,6 +162,11 @@ max_age = 365 # Max age of a refund in days.
|
|||||||
outgoing_enabled = true
|
outgoing_enabled = true
|
||||||
redis_lock_expiry_seconds = 180
|
redis_lock_expiry_seconds = 180
|
||||||
|
|
||||||
|
# Controls whether merchant ID authentication is enabled.
|
||||||
|
# When enabled, payment endpoints will accept and require a x-merchant-id header in the request.
|
||||||
|
[merchant_id_auth]
|
||||||
|
merchant_id_auth_enabled = false
|
||||||
|
|
||||||
# Validity of an Ephemeral Key in Hours
|
# Validity of an Ephemeral Key in Hours
|
||||||
[eph_key]
|
[eph_key]
|
||||||
validity = 1
|
validity = 1
|
||||||
|
|||||||
@ -155,6 +155,11 @@ bg_metrics_collection_interval_in_secs = 15 # Interval for collecting
|
|||||||
delay_between_retries_in_milliseconds = 500 # Delay between retries in milliseconds
|
delay_between_retries_in_milliseconds = 500 # Delay between retries in milliseconds
|
||||||
redis_lock_expiry_seconds = 180 # Seconds before the redis lock expires
|
redis_lock_expiry_seconds = 180 # Seconds before the redis lock expires
|
||||||
|
|
||||||
|
# Controls whether merchant ID authentication is enabled.
|
||||||
|
# When enabled, payment endpoints will accept and require a x-merchant-id header in the request.
|
||||||
|
[merchant_id_auth]
|
||||||
|
merchant_id_auth_enabled = false
|
||||||
|
|
||||||
# Main SQL data store credentials
|
# Main SQL data store credentials
|
||||||
[master_database]
|
[master_database]
|
||||||
username = "db_user" # DB Username
|
username = "db_user" # DB Username
|
||||||
|
|||||||
@ -197,6 +197,9 @@ max_age = 365
|
|||||||
outgoing_enabled = true
|
outgoing_enabled = true
|
||||||
redis_lock_expiry_seconds = 180 # 3 * 60 seconds
|
redis_lock_expiry_seconds = 180 # 3 * 60 seconds
|
||||||
|
|
||||||
|
[merchant_id_auth]
|
||||||
|
merchant_id_auth_enabled = false
|
||||||
|
|
||||||
[eph_key]
|
[eph_key]
|
||||||
validity = 1
|
validity = 1
|
||||||
|
|
||||||
|
|||||||
@ -893,6 +893,9 @@ delay_between_retries_in_milliseconds = 500
|
|||||||
outgoing_enabled = true
|
outgoing_enabled = true
|
||||||
redis_lock_expiry_seconds = 180 # 3 * 60 seconds
|
redis_lock_expiry_seconds = 180 # 3 * 60 seconds
|
||||||
|
|
||||||
|
[merchant_id_auth]
|
||||||
|
merchant_id_auth_enabled = false
|
||||||
|
|
||||||
[events.kafka]
|
[events.kafka]
|
||||||
brokers = ["localhost:9092"]
|
brokers = ["localhost:9092"]
|
||||||
fraud_check_analytics_topic = "hyperswitch-fraud-check-events"
|
fraud_check_analytics_topic = "hyperswitch-fraud-check-events"
|
||||||
|
|||||||
@ -540,6 +540,7 @@ pub(crate) async fn fetch_raw_secrets(
|
|||||||
revenue_recovery: conf.revenue_recovery,
|
revenue_recovery: conf.revenue_recovery,
|
||||||
debit_routing_config: conf.debit_routing_config,
|
debit_routing_config: conf.debit_routing_config,
|
||||||
clone_connector_allowlist: conf.clone_connector_allowlist,
|
clone_connector_allowlist: conf.clone_connector_allowlist,
|
||||||
|
merchant_id_auth: conf.merchant_id_auth,
|
||||||
infra_values: conf.infra_values,
|
infra_values: conf.infra_values,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -158,6 +158,7 @@ pub struct Settings<S: SecretState> {
|
|||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
pub revenue_recovery: revenue_recovery::RevenueRecoverySettings,
|
pub revenue_recovery: revenue_recovery::RevenueRecoverySettings,
|
||||||
pub clone_connector_allowlist: Option<CloneConnectorAllowlistConfig>,
|
pub clone_connector_allowlist: Option<CloneConnectorAllowlistConfig>,
|
||||||
|
pub merchant_id_auth: MerchantIdAuthSettings,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub infra_values: Option<HashMap<String, String>>,
|
pub infra_values: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
@ -817,6 +818,12 @@ pub struct DrainerSettings {
|
|||||||
pub loop_interval: u32, // in milliseconds
|
pub loop_interval: u32, // in milliseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct MerchantIdAuthSettings {
|
||||||
|
pub merchant_id_auth_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct WebhooksSettings {
|
pub struct WebhooksSettings {
|
||||||
|
|||||||
@ -262,6 +262,27 @@ pub async fn payments_create_and_confirm_intent(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let auth_type = if state.conf.merchant_id_auth.merchant_id_auth_enabled {
|
||||||
|
&auth::MerchantIdAuth
|
||||||
|
} else {
|
||||||
|
match env::which() {
|
||||||
|
env::Env::Production => &auth::V2ApiKeyAuth {
|
||||||
|
is_connected_allowed: false,
|
||||||
|
is_platform_allowed: false,
|
||||||
|
},
|
||||||
|
_ => auth::auth_type(
|
||||||
|
&auth::V2ApiKeyAuth {
|
||||||
|
is_connected_allowed: false,
|
||||||
|
is_platform_allowed: false,
|
||||||
|
},
|
||||||
|
&auth::JWTAuth {
|
||||||
|
permission: Permission::ProfilePaymentWrite,
|
||||||
|
},
|
||||||
|
req.headers(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Box::pin(api::server_wrap(
|
Box::pin(api::server_wrap(
|
||||||
flow,
|
flow,
|
||||||
state,
|
state,
|
||||||
@ -280,22 +301,7 @@ pub async fn payments_create_and_confirm_intent(
|
|||||||
header_payload.clone(),
|
header_payload.clone(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
match env::which() {
|
auth_type,
|
||||||
env::Env::Production => &auth::V2ApiKeyAuth {
|
|
||||||
is_connected_allowed: false,
|
|
||||||
is_platform_allowed: false,
|
|
||||||
},
|
|
||||||
_ => auth::auth_type(
|
|
||||||
&auth::V2ApiKeyAuth {
|
|
||||||
is_connected_allowed: false,
|
|
||||||
is_platform_allowed: false,
|
|
||||||
},
|
|
||||||
&auth::JWTAuth {
|
|
||||||
permission: Permission::ProfilePaymentWrite,
|
|
||||||
},
|
|
||||||
req.headers(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
api_locking::LockAction::NotApplicable,
|
api_locking::LockAction::NotApplicable,
|
||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -2184,6 +2184,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[cfg(feature = "v1")]
|
||||||
pub struct MerchantIdAuth(pub id_type::MerchantId);
|
pub struct MerchantIdAuth(pub id_type::MerchantId);
|
||||||
|
|
||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
@ -2233,6 +2234,10 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
pub struct MerchantIdAuth;
|
||||||
|
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<A> AuthenticateAndFetch<AuthenticationData, A> for MerchantIdAuth
|
impl<A> AuthenticateAndFetch<AuthenticationData, A> for MerchantIdAuth
|
||||||
@ -2249,6 +2254,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let key_manager_state = &(&state.session_state()).into();
|
let key_manager_state = &(&state.session_state()).into();
|
||||||
|
let merchant_id = HeaderMapStruct::new(request_headers)
|
||||||
|
.get_id_type_from_header::<id_type::MerchantId>(headers::X_MERCHANT_ID)?;
|
||||||
let profile_id =
|
let profile_id =
|
||||||
get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)?
|
get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)?
|
||||||
.get_required_value(headers::X_PROFILE_ID)?;
|
.get_required_value(headers::X_PROFILE_ID)?;
|
||||||
@ -2256,7 +2263,7 @@ where
|
|||||||
.store()
|
.store()
|
||||||
.get_merchant_key_store_by_merchant_id(
|
.get_merchant_key_store_by_merchant_id(
|
||||||
key_manager_state,
|
key_manager_state,
|
||||||
&self.0,
|
&merchant_id,
|
||||||
&state.store().get_master_key().to_vec().into(),
|
&state.store().get_master_key().to_vec().into(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -2267,14 +2274,14 @@ where
|
|||||||
.find_business_profile_by_merchant_id_profile_id(
|
.find_business_profile_by_merchant_id_profile_id(
|
||||||
key_manager_state,
|
key_manager_state,
|
||||||
&key_store,
|
&key_store,
|
||||||
&self.0,
|
&merchant_id,
|
||||||
&profile_id,
|
&profile_id,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
|
.to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
let merchant = state
|
let merchant = state
|
||||||
.store()
|
.store()
|
||||||
.find_merchant_account_by_merchant_id(key_manager_state, &self.0, &key_store)
|
.find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store)
|
||||||
.await
|
.await
|
||||||
.to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
|
.to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user