Issue:
Bug: [FEATURE] Add delete profile endpoint in v2



### Feature Description

Add delete profile endpoint in v2

### Possible Implementation

Add a new endpoint in routes for delete profile in v2

### Have you spent some time checking if this feature request has been raised before?

- [x] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [x] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!

Pull request description:
## Summary
- Added v2 API endpoint for deleting business profiles
- Implements hard delete functionality for profiles

## Details
This PR adds the missing delete profile endpoint for the v2 API as requested in issue #8715.

### Implementation Details:
- Added `ProfileDeleteResponse` struct for standardized v2 API response
- Implemented `delete_profile_v2` function in core admin module
- Added route handler `profile_delete_v2` with v2-specific authentication
- Updated OpenAPI documentation with the new endpoint
- Registered the route in both app.rs and openapi_v2.rs

### API Endpoint:
```
DELETE /v2/profiles/{id}
```

### Response:
```json
{
  "profile_id": "pro_abcdefghijklmnopqrstuvwxyz",
  "deleted": true
}
```

### Authentication:
- Uses admin API key or JWT authentication
- Requires `MerchantAccountWrite` permission

### Note on Delete Type:
Currently implementing hard delete for profiles. This can be changed to soft delete in the future if audit trails are needed (similar to merchant account deletion).

## Test plan
- [ ] Manually tested the endpoint with v2 API
- [ ] Verified authentication and authorization
- [ ] Confirmed profile is deleted from database
- [ ] Checked OpenAPI documentation renders correctly

Closes #8715

🤖 Generated with [Claude Code](https://claude.ai/code)

Testing:

Patch:
diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs
index 22c1868f6ac..bcaeb68f66f 100644
--- a/crates/api_models/src/admin.rs
+++ b/crates/api_models/src/admin.rs
@@ -765,6 +765,16 @@ pub struct MerchantAccountDeleteResponse {
     pub deleted: bool,
 }
 
+#[derive(Debug, Serialize, ToSchema)]
+pub struct ProfileDeleteResponse {
+    /// The identifier for the Profile
+    #[schema(max_length = 255, example = "pro_abcdefghijklmnopqrstuvwxyz", value_type = String)]
+    pub profile_id: id_type::ProfileId,
+    /// If the profile is deleted or not
+    #[schema(example = true)]
+    pub deleted: bool,
+}
+
 #[derive(Default, Debug, Deserialize, Serialize)]
 pub struct MerchantId {
     pub merchant_id: id_type::MerchantId,
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index 4b88ec4c1ab..ef94ed82393 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -91,6 +91,7 @@ Never share your secret api keys. Keep them guarded and secure.
         routes::profile::profile_create,
         routes::profile::profile_retrieve,
         routes::profile::profile_update,
+        routes::profile::profile_delete_v2,
         routes::profile::connector_list,
 
         // Routes for routing under profile
diff --git a/crates/openapi/src/routes/profile.rs b/crates/openapi/src/routes/profile.rs
index 265e26f5209..4bd3b61d455 100644
--- a/crates/openapi/src/routes/profile.rs
+++ b/crates/openapi/src/routes/profile.rs
@@ -204,6 +204,28 @@ pub async fn profile_create() {}
 )]
 pub async fn profile_update() {}
 
+#[cfg(feature = "v2")]
+/// Profile - Delete
+///
+/// Delete a *profile*
+#[utoipa::path(
+    delete,
+    path = "/v2/profiles/{id}",
+    params(
+        ("id" = String, Path, description = "The unique identifier for the profile")
+    ),
+    responses(
+        (status = 200, description = "Profile Deleted", body = ProfileDeleteResponse),
+        (status = 404, description = "Profile not found"),
+        (status = 400, description = "Invalid request data"),
+        (status = 401, description = "Unauthorized request")
+    ),
+    tag = "Profile",
+    operation_id = "Delete a Profile",
+    security(("admin_api_key" = []))
+)]
+pub async fn profile_delete_v2() {}
+
 #[cfg(feature = "v2")]
 /// Profile - Activate routing algorithm
 ///
diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs
index 6fb6b9a3b7d..4c6ba9266c1 100644
--- a/crates/router/src/core/admin.rs
+++ b/crates/router/src/core/admin.rs
@@ -3785,6 +3785,47 @@ pub async fn delete_profile(
     Ok(service_api::ApplicationResponse::Json(delete_result))
 }
 
+#[cfg(feature = "v2")]
+pub async fn delete_profile_v2(
+    state: SessionState,
+    profile_id: id_type::ProfileId,
+    merchant_id: &id_type::MerchantId,
+    _key_store: domain::MerchantKeyStore,
+) -> RouterResponse<api::ProfileDeleteResponse> {
+    let db = state.store.as_ref();
+
+    // First, check if the profile exists
+    let key_manager_state = &(&state).into();
+    let _profile = db
+        .find_business_profile_by_merchant_id_profile_id(
+            key_manager_state,
+            &_key_store,
+            merchant_id,
+            &profile_id,
+        )
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
+            id: profile_id.get_string_repr().to_owned(),
+        })?;
+
+    // SOFT DELETE IMPLEMENTATION:
+    // For now, we'll perform a hard delete, but this can be changed to soft delete
+    // similar to merchant deletion if needed for audit trails
+    let is_deleted = db
+        .delete_profile_by_profile_id_merchant_id(&profile_id, merchant_id)
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
+            id: profile_id.get_string_repr().to_owned(),
+        })?;
+
+    let response = api::ProfileDeleteResponse {
+        profile_id,
+        deleted: is_deleted,
+    };
+
+    Ok(service_api::ApplicationResponse::Json(response))
+}
+
 #[cfg(feature = "olap")]
 #[async_trait::async_trait]
 trait ProfileUpdateBridge {
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index dbf4c5af13e..cadeaf4649d 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -2058,7 +2058,8 @@ impl Profile {
                     .service(
                         web::resource("")
                             .route(web::get().to(profiles::profile_retrieve))
-                            .route(web::put().to(profiles::profile_update)),
+                            .route(web::put().to(profiles::profile_update))
+                            .route(web::delete().to(profiles::profile_delete_v2)),
                     )
                     .service(
                         web::resource("/connector-accounts")
diff --git a/crates/router/src/routes/profiles.rs b/crates/router/src/routes/profiles.rs
index a854a68a2a3..0b5b1dac341 100644
--- a/crates/router/src/routes/profiles.rs
+++ b/crates/router/src/routes/profiles.rs
@@ -284,6 +284,42 @@ pub async fn profile_delete(
     .await
 }
 
+#[cfg(feature = "v2")]
+#[instrument(skip_all, fields(flow = ?Flow::ProfileDelete))]
+pub async fn profile_delete_v2(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    path: web::Path<common_utils::id_type::ProfileId>,
+) -> HttpResponse {
+    let flow = Flow::ProfileDelete;
+    let profile_id = path.into_inner();
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        profile_id.clone(),
+        |state, auth_data, profile_id, _| {
+            delete_profile_v2(
+                state,
+                profile_id,
+                auth_data.merchant_account.get_id(),
+                auth_data.key_store,
+            )
+        },
+        auth::auth_type(
+            &auth::V2AdminApiAuth,
+            &auth::JWTAuthProfileFromRoute {
+                profile_id,
+                required_permission: permissions::Permission::MerchantAccountWrite,
+            },
+            req.headers(),
+        ),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
+
 #[cfg(feature = "v1")]
 #[instrument(skip_all, fields(flow = ?Flow::ProfileList))]
 pub async fn profiles_list(