Bug: [REFACTOR]: (merchant_account_v2) move `organization_id` from request body to `headers`



When creating the merchant account, the request body is 
```json
{
    "merchant_name": "cloth_seller",
    "organization_id": "org_tdhdTbv3z85XtxXGgVHS"
}
```

Here `organization_id` is the hierarchy_id / lineage id. This should be moved to a header called `X-Organization-Id`. Necessary changes have to be made in the handlers wherever this is affected. In this case only merchant account create would be affected.
diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json
index bd9de3a2962..9a406e9c733 100644
--- a/api-reference-v2/openapi_spec.json
+++ b/api-reference-v2/openapi_spec.json
@@ -453,6 +453,20 @@
         "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.",
         "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": {
           "content": {
             "application/json": {
@@ -466,8 +480,7 @@
                       "primary_contact_person": "John Doe",
                       "primary_email": "example@company.com"
                     },
-                    "merchant_name": "Cloth Store",
-                    "organization_id": "org_abcdefghijklmnop"
+                    "merchant_name": "Cloth Store"
                   }
                 },
                 "Create a merchant account with metadata": {
@@ -476,14 +489,12 @@
                     "metadata": {
                       "key_1": "John Doe",
                       "key_2": "Trends"
-                    },
-                    "organization_id": "org_abcdefghijklmnop"
+                    }
                   }
                 },
                 "Create a merchant account with minimal fields": {
                   "value": {
-                    "merchant_name": "Cloth Store",
-                    "organization_id": "org_abcdefghijklmnop"
+                    "merchant_name": "Cloth Store"
                   }
                 }
               }
@@ -9385,8 +9396,7 @@
       "MerchantAccountCreate": {
         "type": "object",
         "required": [
-          "merchant_name",
-          "organization_id"
+          "merchant_name"
         ],
         "properties": {
           "merchant_name": {
@@ -9407,13 +9417,6 @@
             "type": "object",
             "description": "Metadata is useful for storing additional, unstructured information about the merchant account.",
             "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
diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs
index 8f24bdf65bc..c9da386a11d 100644
--- a/crates/api_models/src/admin.rs
+++ b/crates/api_models/src/admin.rs
@@ -178,7 +178,8 @@ impl MerchantAccountCreate {
 #[cfg(feature = "v2")]
 #[derive(Clone, Debug, Deserialize, ToSchema, Serialize)]
 #[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
     #[schema(value_type= String, max_length = 64, example = "NewAge Retailer")]
     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.
     #[schema(value_type = Option<Object>, example = r#"{ "city": "NY", "unit": "245" }"#)]
     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
-    #[schema(value_type = String, max_length = 64, min_length = 1, example = "org_q98uSGAYbjEwqs0mJwnz")]
+// In v2 the struct used in the API is MerchantAccountCreateWithoutOrgId
+// 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,
 }
 
diff --git a/crates/common_utils/src/id_type/organization.rs b/crates/common_utils/src/id_type/organization.rs
index f88a62daa1d..a83f35db1d8 100644
--- a/crates/common_utils/src/id_type/organization.rs
+++ b/crates/common_utils/src/id_type/organization.rs
@@ -1,3 +1,5 @@
+use crate::errors::{CustomResult, ValidationError};
+
 crate::id_type!(
     OrganizationId,
     "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_queryable_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))
+    }
+}
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index 0a19c5a63c5..7bbe019dcba 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -154,7 +154,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::organization::OrganizationCreateRequest,
         api_models::organization::OrganizationUpdateRequest,
         api_models::organization::OrganizationResponse,
-        api_models::admin::MerchantAccountCreate,
+        api_models::admin::MerchantAccountCreateWithoutOrgId,
         api_models::admin::MerchantAccountUpdate,
         api_models::admin::MerchantAccountDeleteResponse,
         api_models::admin::MerchantConnectorDeleteResponse,
diff --git a/crates/openapi/src/routes/merchant_account.rs b/crates/openapi/src/routes/merchant_account.rs
index b92285245a4..01571da1de9 100644
--- a/crates/openapi/src/routes/merchant_account.rs
+++ b/crates/openapi/src/routes/merchant_account.rs
@@ -51,6 +51,13 @@ pub async fn merchant_account_create() {}
 #[utoipa::path(
     post,
     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(
         content = MerchantAccountCreate,
         examples(
@@ -58,7 +65,6 @@ pub async fn merchant_account_create() {}
                 "Create a merchant account with minimal fields" = (
                     value = json!({
                         "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" = (
                     value = json!({
                         "merchant_name": "Cloth Store",
-                        "organization_id": "org_abcdefghijklmnop",
                         "merchant_details": {
                                 "primary_contact_person": "John Doe",
                                 "primary_email": "example@company.com"
@@ -78,7 +83,6 @@ pub async fn merchant_account_create() {}
                 "Create a merchant account with metadata" = (
                     value = json!({
                         "merchant_name": "Cloth Store",
-                        "organization_id": "org_abcdefghijklmnop",
                         "metadata": {
                                 "key_1": "John Doe",
                                 "key_2": "Trends"
diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs
index 0eff78d38b4..06ab971925b 100644
--- a/crates/router/src/lib.rs
+++ b/crates/router/src/lib.rs
@@ -66,6 +66,7 @@ pub mod headers {
     pub const X_API_VERSION: &str = "X-ApiVersion";
     pub const X_FORWARDED_FOR: &str = "X-Forwarded-For";
     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_TRANS_KEY: &str = "X-Trans-Key";
     pub const X_VERSION: &str = "X-Version";
diff --git a/crates/router/src/routes/admin.rs b/crates/router/src/routes/admin.rs
index 78238d3af07..16aebeabe73 100644
--- a/crates/router/src/routes/admin.rs
+++ b/crates/router/src/routes/admin.rs
@@ -95,7 +95,7 @@ pub async fn organization_retrieve(
     .await
 }
 
-#[cfg(feature = "olap")]
+#[cfg(all(feature = "olap", feature = "v1"))]
 #[instrument(skip_all, fields(flow = ?Flow::MerchantsAccountCreate))]
 pub async fn merchant_account_create(
     state: web::Data<AppState>,
@@ -115,6 +115,43 @@ pub async fn merchant_account_create(
     .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
 ///
 /// Retrieve a merchant account details.
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index b64c5d14f32..fa5041ab6b3 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -931,7 +931,7 @@ where
 }
 
 /// A helper struct to extract headers from the request
-struct HeaderMapStruct<'a> {
+pub(crate) struct HeaderMapStruct<'a> {
     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
diff --git a/cypress-tests-v2/cypress/fixtures/merchant_account.json b/cypress-tests-v2/cypress/fixtures/merchant_account.json
index ac24e956513..dbe88ae10d4 100644
--- a/cypress-tests-v2/cypress/fixtures/merchant_account.json
+++ b/cypress-tests-v2/cypress/fixtures/merchant_account.json
@@ -1,7 +1,6 @@
 {
   "ma_create": {
-    "merchant_name": "Hyperswitch Seller",
-    "organization_id": ""
+    "merchant_name": "Hyperswitch Seller"
   },
   "ma_update": {
     "merchant_name": "Hyperswitch"
diff --git a/cypress-tests-v2/cypress/support/commands.js b/cypress-tests-v2/cypress/support/commands.js
index c9d1ef3f24f..abdf95194bb 100644
--- a/cypress-tests-v2/cypress/support/commands.js
+++ b/cypress-tests-v2/cypress/support/commands.js
@@ -173,15 +173,13 @@ Cypress.Commands.add(
       .replaceAll(" ", "")
       .toLowerCase();
 
-    // Update request body
-    merchantAccountCreateBody.organization_id = organization_id;
-
     cy.request({
       method: "POST",
       url: url,
       headers: {
         "Content-Type": "application/json",
         "api-key": api_key,
+        "X-Organization-Id": organization_id,
       },
       body: merchantAccountCreateBody,
       failOnStatusCode: false,

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [x] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
For create_merchant request organization_id is currently being passed in request body. Moved it to request header

### Additional Changes

- [x] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->

1a. Correct Request

```
curl --location 'http://localhost:8080/v2/merchant_accounts' \
--header 'X-Organization-Id: org_55QZkS5H9AR8deTk9mRs' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "merchant_name": "Cloth Seller Person"
}'
```


1b. Response

```json
{
    "id": "clothsellerperson_iG5VNjsN9xuCg7Xx0uWh",
    "merchant_name": "Cloth Seller Person",
    "merchant_details": null,
    "publishable_key": "pk_dev_1114028c68c242dc917bde0ec8fea75b",
    "metadata": null,
    "organization_id": "org_55QZkS5H9AR8deTk9mRs",
    "recon_status": "not_requested"
}
```

2a. Non-existant Org-ID Request
```
curl --location 'http://localhost:8080/v2/merchant_accounts' \
--header 'X-Organization-Id: org_67ZZlS5H9AR8deTk8oPi' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "merchant_name": "Cloth Seller Person"
    
}'
```

2b. Response

```json
{
    "error": {
        "type": "invalid_request",
        "message": "organization with the given id does not exist",
        "code": "IR_37"
    }
}
```

3a. Invalid Org_id request

```
curl --location 'http://localhost:8080/v2/merchant_accounts' \
--header 'X-Organization-Id: I dont exist' \
--header 'Content-Type: application/json' \
--header 'api-key: test_admin' \
--data '{
    "merchant_name": "Cloth Seller Person"
    
}'
```

3b. Response

```json
{
    "error": {
        "type": "invalid_request",
        "message": "`X-Organization-Id` header is invalid",
        "code": "IR_06"
    }
}
```

## Cypress Screenshot:
<img width="1392" alt="image" src="https://github.com/user-attachments/assets/909b4754-b760-4850-adee-42786fa83614">


## Checklist
<!-- Put an `x` in the boxes that apply -->

- [x] I formatted the code `cargo +nightly fmt --all`
- [x] I addressed lints thrown by `cargo clippy`
- [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
