Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ max_age = 365 # Max age of a refund in days.
outgoing_enabled = true
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
[eph_key]
validity = 1
Expand Down
5 changes: 5 additions & 0 deletions config/deployments/env_specific.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
[master_database]
username = "db_user" # DB Username
Expand Down
3 changes: 3 additions & 0 deletions config/development.toml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ max_age = 365
outgoing_enabled = true
redis_lock_expiry_seconds = 180 # 3 * 60 seconds

[merchant_id_auth]
merchant_id_auth_enabled = false

[eph_key]
validity = 1

Expand Down
3 changes: 3 additions & 0 deletions config/docker_compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,9 @@ delay_between_retries_in_milliseconds = 500
outgoing_enabled = true
redis_lock_expiry_seconds = 180 # 3 * 60 seconds

[merchant_id_auth]
merchant_id_auth_enabled = false

[events.kafka]
brokers = ["localhost:9092"]
fraud_check_analytics_topic = "hyperswitch-fraud-check-events"
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/configs/secrets_transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ pub(crate) async fn fetch_raw_secrets(
revenue_recovery: conf.revenue_recovery,
debit_routing_config: conf.debit_routing_config,
clone_connector_allowlist: conf.clone_connector_allowlist,
merchant_id_auth: conf.merchant_id_auth,
infra_values: conf.infra_values,
}
}
7 changes: 7 additions & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ pub struct Settings<S: SecretState> {
#[cfg(feature = "v2")]
pub revenue_recovery: revenue_recovery::RevenueRecoverySettings,
pub clone_connector_allowlist: Option<CloneConnectorAllowlistConfig>,
pub merchant_id_auth: MerchantIdAuthSettings,
#[serde(default)]
pub infra_values: Option<HashMap<String, String>>,
}
Expand Down Expand Up @@ -817,6 +818,12 @@ pub struct DrainerSettings {
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)]
#[serde(default)]
pub struct WebhooksSettings {
Expand Down
38 changes: 22 additions & 16 deletions crates/router/src/routes/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
flow,
state,
Expand All @@ -280,22 +301,7 @@ pub async fn payments_create_and_confirm_intent(
header_payload.clone(),
)
},
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(),
),
},
auth_type,
api_locking::LockAction::NotApplicable,
))
.await
Expand Down
13 changes: 10 additions & 3 deletions crates/router/src/services/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2184,6 +2184,7 @@ where
}

#[derive(Debug)]
#[cfg(feature = "v1")]
pub struct MerchantIdAuth(pub id_type::MerchantId);

#[cfg(feature = "v1")]
Expand Down Expand Up @@ -2233,6 +2234,10 @@ where
}
}

#[derive(Debug)]
#[cfg(feature = "v2")]
pub struct MerchantIdAuth;

#[cfg(feature = "v2")]
#[async_trait]
impl<A> AuthenticateAndFetch<AuthenticationData, A> for MerchantIdAuth
Expand All @@ -2249,14 +2254,16 @@ where
}

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 =
get_id_type_by_key_from_headers(headers::X_PROFILE_ID.to_string(), request_headers)?
.get_required_value(headers::X_PROFILE_ID)?;
let key_store = state
.store()
.get_merchant_key_store_by_merchant_id(
key_manager_state,
&self.0,
&merchant_id,
&state.store().get_master_key().to_vec().into(),
)
.await
Expand All @@ -2267,14 +2274,14 @@ where
.find_business_profile_by_merchant_id_profile_id(
key_manager_state,
&key_store,
&self.0,
&merchant_id,
&profile_id,
)
.await
.to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;
let merchant = state
.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
.to_not_found_response(errors::ApiErrorResponse::Unauthorized)?;

Expand Down
Loading