mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-11-01 02:57:02 +08:00 
			
		
		
		
	feat(router): Move organization_id to request header from request body for v2 (#6277)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com>
This commit is contained in:
		| @ -453,6 +453,20 @@ | |||||||
|         "summary": "Merchant Account - Create", |         "summary": "Merchant Account - Create", | ||||||
|         "description": "Create a new account for a *merchant* and the *merchant* could be a seller or retailer or client who likes to receive and send payments.\n\nBefore creating the merchant account, it is mandatory to create an organization.", |         "description": "Create a new account for a *merchant* and the *merchant* could be a seller or retailer or client who likes to receive and send payments.\n\nBefore creating the merchant account, it is mandatory to create an organization.", | ||||||
|         "operationId": "Create a Merchant Account", |         "operationId": "Create a Merchant Account", | ||||||
|  |         "parameters": [ | ||||||
|  |           { | ||||||
|  |             "name": "X-Organization-Id", | ||||||
|  |             "in": "header", | ||||||
|  |             "description": "Organization ID for which the merchant account has to be created.", | ||||||
|  |             "required": true, | ||||||
|  |             "schema": { | ||||||
|  |               "type": "string" | ||||||
|  |             }, | ||||||
|  |             "example": { | ||||||
|  |               "X-Organization-Id": "org_abcdefghijklmnop" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ], | ||||||
|         "requestBody": { |         "requestBody": { | ||||||
|           "content": { |           "content": { | ||||||
|             "application/json": { |             "application/json": { | ||||||
| @ -466,8 +480,7 @@ | |||||||
|                       "primary_contact_person": "John Doe", |                       "primary_contact_person": "John Doe", | ||||||
|                       "primary_email": "example@company.com" |                       "primary_email": "example@company.com" | ||||||
|                     }, |                     }, | ||||||
|                     "merchant_name": "Cloth Store", |                     "merchant_name": "Cloth Store" | ||||||
|                     "organization_id": "org_abcdefghijklmnop" |  | ||||||
|                   } |                   } | ||||||
|                 }, |                 }, | ||||||
|                 "Create a merchant account with metadata": { |                 "Create a merchant account with metadata": { | ||||||
| @ -476,14 +489,12 @@ | |||||||
|                     "metadata": { |                     "metadata": { | ||||||
|                       "key_1": "John Doe", |                       "key_1": "John Doe", | ||||||
|                       "key_2": "Trends" |                       "key_2": "Trends" | ||||||
|                     }, |                     } | ||||||
|                     "organization_id": "org_abcdefghijklmnop" |  | ||||||
|                   } |                   } | ||||||
|                 }, |                 }, | ||||||
|                 "Create a merchant account with minimal fields": { |                 "Create a merchant account with minimal fields": { | ||||||
|                   "value": { |                   "value": { | ||||||
|                     "merchant_name": "Cloth Store", |                     "merchant_name": "Cloth Store" | ||||||
|                     "organization_id": "org_abcdefghijklmnop" |  | ||||||
|                   } |                   } | ||||||
|                 } |                 } | ||||||
|               } |               } | ||||||
| @ -9385,8 +9396,7 @@ | |||||||
|       "MerchantAccountCreate": { |       "MerchantAccountCreate": { | ||||||
|         "type": "object", |         "type": "object", | ||||||
|         "required": [ |         "required": [ | ||||||
|           "merchant_name", |           "merchant_name" | ||||||
|           "organization_id" |  | ||||||
|         ], |         ], | ||||||
|         "properties": { |         "properties": { | ||||||
|           "merchant_name": { |           "merchant_name": { | ||||||
| @ -9407,13 +9417,6 @@ | |||||||
|             "type": "object", |             "type": "object", | ||||||
|             "description": "Metadata is useful for storing additional, unstructured information about the merchant account.", |             "description": "Metadata is useful for storing additional, unstructured information about the merchant account.", | ||||||
|             "nullable": true |             "nullable": true | ||||||
|           }, |  | ||||||
|           "organization_id": { |  | ||||||
|             "type": "string", |  | ||||||
|             "description": "The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization", |  | ||||||
|             "example": "org_q98uSGAYbjEwqs0mJwnz", |  | ||||||
|             "maxLength": 64, |  | ||||||
|             "minLength": 1 |  | ||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "additionalProperties": false |         "additionalProperties": false | ||||||
|  | |||||||
| @ -178,7 +178,8 @@ impl MerchantAccountCreate { | |||||||
| #[cfg(feature = "v2")] | #[cfg(feature = "v2")] | ||||||
| #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] | #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)] | ||||||
| #[serde(deny_unknown_fields)] | #[serde(deny_unknown_fields)] | ||||||
| pub struct MerchantAccountCreate { | #[schema(as = MerchantAccountCreate)] | ||||||
|  | pub struct MerchantAccountCreateWithoutOrgId { | ||||||
|     /// Name of the Merchant Account, This will be used as a prefix to generate the id |     /// Name of the Merchant Account, This will be used as a prefix to generate the id | ||||||
|     #[schema(value_type= String, max_length = 64, example = "NewAge Retailer")] |     #[schema(value_type= String, max_length = 64, example = "NewAge Retailer")] | ||||||
|     pub merchant_name: Secret<common_utils::new_type::MerchantName>, |     pub merchant_name: Secret<common_utils::new_type::MerchantName>, | ||||||
| @ -189,9 +190,17 @@ pub struct MerchantAccountCreate { | |||||||
|     /// Metadata is useful for storing additional, unstructured information about the merchant account. |     /// Metadata is useful for storing additional, unstructured information about the merchant account. | ||||||
|     #[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)] |     #[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)] | ||||||
|     pub metadata: Option<pii::SecretSerdeValue>, |     pub metadata: Option<pii::SecretSerdeValue>, | ||||||
|  | } | ||||||
|  |  | ||||||
|     /// The id of the organization to which the merchant belongs to. Please use the organization endpoint to create an organization | // In v2 the struct used in the API is MerchantAccountCreateWithoutOrgId | ||||||
|     #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")] | // The following struct is only used internally, so we can reuse the common | ||||||
|  | // part of `create_merchant_account` without duplicating its code for v2 | ||||||
|  | #[cfg(feature = "v2")] | ||||||
|  | #[derive(Clone, Debug, Serialize)] | ||||||
|  | pub struct MerchantAccountCreate { | ||||||
|  |     pub merchant_name: Secret<common_utils::new_type::MerchantName>, | ||||||
|  |     pub merchant_details: Option<MerchantDetails>, | ||||||
|  |     pub metadata: Option<pii::SecretSerdeValue>, | ||||||
|     pub organization_id: id_type::OrganizationId, |     pub organization_id: id_type::OrganizationId, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | use crate::errors::{CustomResult, ValidationError}; | ||||||
|  |  | ||||||
| crate::id_type!( | crate::id_type!( | ||||||
|     OrganizationId, |     OrganizationId, | ||||||
|     "A type for organization_id that can be used for organization ids" |     "A type for organization_id that can be used for organization ids" | ||||||
| @ -13,3 +15,10 @@ crate::impl_generate_id_id_type!(OrganizationId, "org"); | |||||||
| crate::impl_serializable_secret_id_type!(OrganizationId); | crate::impl_serializable_secret_id_type!(OrganizationId); | ||||||
| crate::impl_queryable_id_type!(OrganizationId); | crate::impl_queryable_id_type!(OrganizationId); | ||||||
| crate::impl_to_sql_from_sql_id_type!(OrganizationId); | crate::impl_to_sql_from_sql_id_type!(OrganizationId); | ||||||
|  |  | ||||||
|  | impl OrganizationId { | ||||||
|  |     /// Get an organization id from String | ||||||
|  |     pub fn wrap(org_id: String) -> CustomResult<Self, ValidationError> { | ||||||
|  |         Self::try_from(std::borrow::Cow::from(org_id)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -154,7 +154,7 @@ Never share your secret api keys. Keep them guarded and secure. | |||||||
|         api_models::organization::OrganizationCreateRequest, |         api_models::organization::OrganizationCreateRequest, | ||||||
|         api_models::organization::OrganizationUpdateRequest, |         api_models::organization::OrganizationUpdateRequest, | ||||||
|         api_models::organization::OrganizationResponse, |         api_models::organization::OrganizationResponse, | ||||||
|         api_models::admin::MerchantAccountCreate, |         api_models::admin::MerchantAccountCreateWithoutOrgId, | ||||||
|         api_models::admin::MerchantAccountUpdate, |         api_models::admin::MerchantAccountUpdate, | ||||||
|         api_models::admin::MerchantAccountDeleteResponse, |         api_models::admin::MerchantAccountDeleteResponse, | ||||||
|         api_models::admin::MerchantConnectorDeleteResponse, |         api_models::admin::MerchantConnectorDeleteResponse, | ||||||
|  | |||||||
| @ -51,6 +51,13 @@ pub async fn merchant_account_create() {} | |||||||
| #[utoipa::path( | #[utoipa::path( | ||||||
|     post, |     post, | ||||||
|     path = "/v2/merchant_accounts", |     path = "/v2/merchant_accounts", | ||||||
|  |     params( | ||||||
|  |       ( | ||||||
|  |         "X-Organization-Id" = String, Header, | ||||||
|  |         description = "Organization ID for which the merchant account has to be created.", | ||||||
|  |         example = json!({"X-Organization-Id": "org_abcdefghijklmnop"}) | ||||||
|  |       ), | ||||||
|  |     ), | ||||||
|     request_body( |     request_body( | ||||||
|         content = MerchantAccountCreate, |         content = MerchantAccountCreate, | ||||||
|         examples( |         examples( | ||||||
| @ -58,7 +65,6 @@ pub async fn merchant_account_create() {} | |||||||
|                 "Create a merchant account with minimal fields" = ( |                 "Create a merchant account with minimal fields" = ( | ||||||
|                     value = json!({ |                     value = json!({ | ||||||
|                         "merchant_name": "Cloth Store", |                         "merchant_name": "Cloth Store", | ||||||
|                         "organization_id": "org_abcdefghijklmnop" |  | ||||||
|                     }) |                     }) | ||||||
|                 ) |                 ) | ||||||
|             ), |             ), | ||||||
| @ -66,7 +72,6 @@ pub async fn merchant_account_create() {} | |||||||
|                 "Create a merchant account with merchant details" = ( |                 "Create a merchant account with merchant details" = ( | ||||||
|                     value = json!({ |                     value = json!({ | ||||||
|                         "merchant_name": "Cloth Store", |                         "merchant_name": "Cloth Store", | ||||||
|                         "organization_id": "org_abcdefghijklmnop", |  | ||||||
|                         "merchant_details": { |                         "merchant_details": { | ||||||
|                                 "primary_contact_person": "John Doe", |                                 "primary_contact_person": "John Doe", | ||||||
|                                 "primary_email": "example@company.com" |                                 "primary_email": "example@company.com" | ||||||
| @ -78,7 +83,6 @@ pub async fn merchant_account_create() {} | |||||||
|                 "Create a merchant account with metadata" = ( |                 "Create a merchant account with metadata" = ( | ||||||
|                     value = json!({ |                     value = json!({ | ||||||
|                         "merchant_name": "Cloth Store", |                         "merchant_name": "Cloth Store", | ||||||
|                         "organization_id": "org_abcdefghijklmnop", |  | ||||||
|                         "metadata": { |                         "metadata": { | ||||||
|                                 "key_1": "John Doe", |                                 "key_1": "John Doe", | ||||||
|                                 "key_2": "Trends" |                                 "key_2": "Trends" | ||||||
|  | |||||||
| @ -66,6 +66,7 @@ pub mod headers { | |||||||
|     pub const X_API_VERSION: &str = "X-ApiVersion"; |     pub const X_API_VERSION: &str = "X-ApiVersion"; | ||||||
|     pub const X_FORWARDED_FOR: &str = "X-Forwarded-For"; |     pub const X_FORWARDED_FOR: &str = "X-Forwarded-For"; | ||||||
|     pub const X_MERCHANT_ID: &str = "X-Merchant-Id"; |     pub const X_MERCHANT_ID: &str = "X-Merchant-Id"; | ||||||
|  |     pub const X_ORGANIZATION_ID: &str = "X-Organization-Id"; | ||||||
|     pub const X_LOGIN: &str = "X-Login"; |     pub const X_LOGIN: &str = "X-Login"; | ||||||
|     pub const X_TRANS_KEY: &str = "X-Trans-Key"; |     pub const X_TRANS_KEY: &str = "X-Trans-Key"; | ||||||
|     pub const X_VERSION: &str = "X-Version"; |     pub const X_VERSION: &str = "X-Version"; | ||||||
|  | |||||||
| @ -92,7 +92,7 @@ pub async fn organization_retrieve( | |||||||
|     .await |     .await | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(feature = "olap")] | #[cfg(all(feature = "olap", feature = "v1"))] | ||||||
| #[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))] | #[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))] | ||||||
| pub async fn merchant_account_create( | pub async fn merchant_account_create( | ||||||
|     state: web::Data<AppState>, |     state: web::Data<AppState>, | ||||||
| @ -112,6 +112,43 @@ pub async fn merchant_account_create( | |||||||
|     .await |     .await | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(all(feature = "olap", feature = "v2"))] | ||||||
|  | #[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))] | ||||||
|  | pub async fn merchant_account_create( | ||||||
|  |     state: web::Data<AppState>, | ||||||
|  |     req: HttpRequest, | ||||||
|  |     json_payload: web::Json<api_models::admin::MerchantAccountCreateWithoutOrgId>, | ||||||
|  | ) -> HttpResponse { | ||||||
|  |     let flow = Flow::MerchantsAccountCreate; | ||||||
|  |     let headers = req.headers(); | ||||||
|  |  | ||||||
|  |     let org_id = match auth::HeaderMapStruct::new(headers).get_organization_id_from_header() { | ||||||
|  |         Ok(org_id) => org_id, | ||||||
|  |         Err(e) => return api::log_and_return_error_response(e), | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Converting from MerchantAccountCreateWithoutOrgId to MerchantAccountCreate so we can use the existing | ||||||
|  |     // `create_merchant_account` function for v2 as well | ||||||
|  |     let json_payload = json_payload.into_inner(); | ||||||
|  |     let new_request_payload_with_org_id = api_models::admin::MerchantAccountCreate { | ||||||
|  |         merchant_name: json_payload.merchant_name, | ||||||
|  |         merchant_details: json_payload.merchant_details, | ||||||
|  |         metadata: json_payload.metadata, | ||||||
|  |         organization_id: org_id, | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     Box::pin(api::server_wrap( | ||||||
|  |         flow, | ||||||
|  |         state, | ||||||
|  |         &req, | ||||||
|  |         new_request_payload_with_org_id, | ||||||
|  |         |state, _, req, _| create_merchant_account(state, req), | ||||||
|  |         &auth::AdminApiAuth, | ||||||
|  |         api_locking::LockAction::NotApplicable, | ||||||
|  |     )) | ||||||
|  |     .await | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Merchant Account - Retrieve | /// Merchant Account - Retrieve | ||||||
| /// | /// | ||||||
| /// Retrieve a merchant account details. | /// Retrieve a merchant account details. | ||||||
|  | |||||||
| @ -931,7 +931,7 @@ where | |||||||
| } | } | ||||||
|  |  | ||||||
| /// A helper struct to extract headers from the request | /// A helper struct to extract headers from the request | ||||||
| struct HeaderMapStruct<'a> { | pub(crate) struct HeaderMapStruct<'a> { | ||||||
|     headers: &'a HeaderMap, |     headers: &'a HeaderMap, | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -981,6 +981,18 @@ impl<'a> HeaderMapStruct<'a> { | |||||||
|                 ) |                 ) | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
|  |     #[cfg(feature = "v2")] | ||||||
|  |     pub fn get_organization_id_from_header(&self) -> RouterResult<id_type::OrganizationId> { | ||||||
|  |         self.get_mandatory_header_value_by_key(headers::X_ORGANIZATION_ID) | ||||||
|  |             .map(|val| val.to_owned()) | ||||||
|  |             .and_then(|organization_id| { | ||||||
|  |                 id_type::OrganizationId::wrap(organization_id).change_context( | ||||||
|  |                     errors::ApiErrorResponse::InvalidRequestData { | ||||||
|  |                         message: format!("`{}` header is invalid", headers::X_ORGANIZATION_ID), | ||||||
|  |                     }, | ||||||
|  |                 ) | ||||||
|  |             }) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Get the merchant-id from `x-merchant-id` header | /// Get the merchant-id from `x-merchant-id` header | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| { | { | ||||||
|   "ma_create": { |   "ma_create": { | ||||||
|     "merchant_name": "Hyperswitch Seller", |     "merchant_name": "Hyperswitch Seller" | ||||||
|     "organization_id": "" |  | ||||||
|   }, |   }, | ||||||
|   "ma_update": { |   "ma_update": { | ||||||
|     "merchant_name": "Hyperswitch" |     "merchant_name": "Hyperswitch" | ||||||
|  | |||||||
| @ -173,15 +173,13 @@ Cypress.Commands.add( | |||||||
|       .replaceAll(" ", "") |       .replaceAll(" ", "") | ||||||
|       .toLowerCase(); |       .toLowerCase(); | ||||||
|  |  | ||||||
|     // Update request body |  | ||||||
|     merchantAccountCreateBody.organization_id = organization_id; |  | ||||||
|  |  | ||||||
|     cy.request({ |     cy.request({ | ||||||
|       method: "POST", |       method: "POST", | ||||||
|       url: url, |       url: url, | ||||||
|       headers: { |       headers: { | ||||||
|         "Content-Type": "application/json", |         "Content-Type": "application/json", | ||||||
|         "api-key": api_key, |         "api-key": api_key, | ||||||
|  |         "X-Organization-Id": organization_id, | ||||||
|       }, |       }, | ||||||
|       body: merchantAccountCreateBody, |       body: merchantAccountCreateBody, | ||||||
|       failOnStatusCode: false, |       failOnStatusCode: false, | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Anurag Thakur
					Anurag Thakur