mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +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:
		 Aishwariyaa Anand
					Aishwariyaa Anand
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							4fa7b12400
						
					
				
				
					commit
					45d4ebfde3
				
			| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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>>, | ||||
| } | ||||
| @ -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 { | ||||
|  | ||||
| @ -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, | ||||
| @ -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 | ||||
|  | ||||
| @ -2184,6 +2184,7 @@ where | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| #[cfg(feature = "v1")] | ||||
| pub struct MerchantIdAuth(pub id_type::MerchantId); | ||||
|  | ||||
| #[cfg(feature = "v1")] | ||||
| @ -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 | ||||
| @ -2249,6 +2254,8 @@ 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)?; | ||||
| @ -2256,7 +2263,7 @@ where | ||||
|             .store() | ||||
|             .get_merchant_key_store_by_merchant_id( | ||||
|                 key_manager_state, | ||||
|                 &self.0, | ||||
|                 &merchant_id, | ||||
|                 &state.store().get_master_key().to_vec().into(), | ||||
|             ) | ||||
|             .await | ||||
| @ -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)?; | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user