Bug: [FEATURE]: Add `Payment Method - Delete` endpoint to payment methods session for V2



Required for Payment Methods Service
diff --git a/api-reference-v2/api-reference/payment-method-session/payment-method-session--delete-a-saved-payment-method.mdx b/api-reference-v2/api-reference/payment-method-session/payment-method-session--delete-a-saved-payment-method.mdx
new file mode 100644
index 00000000000..2f9e800ebe8
--- /dev/null
+++ b/api-reference-v2/api-reference/payment-method-session/payment-method-session--delete-a-saved-payment-method.mdx
@@ -0,0 +1,3 @@
+---
+openapi: delete /v2/payment-method-session/:id
+---
diff --git a/api-reference-v2/mint.json b/api-reference-v2/mint.json
index 2b0997326d8..d0ad2e87996 100644
--- a/api-reference-v2/mint.json
+++ b/api-reference-v2/mint.json
@@ -67,7 +67,8 @@
         "api-reference/payment-method-session/payment-method-session--retrieve",
         "api-reference/payment-method-session/payment-method-session--list-payment-methods",
         "api-reference/payment-method-session/payment-method-session--update-a-saved-payment-method",
-        "api-reference/payment-method-session/payment-method-session--confirm-a-payment-method-session"
+        "api-reference/payment-method-session/payment-method-session--confirm-a-payment-method-session",
+        "api-reference/payment-method-session/payment-method-session--delete-a-saved-payment-method"
       ]
     },
     {
diff --git a/api-reference-v2/openapi_spec.json b/api-reference-v2/openapi_spec.json
index 9467ac0820b..e2cfb06b6e4 100644
--- a/api-reference-v2/openapi_spec.json
+++ b/api-reference-v2/openapi_spec.json
@@ -2960,6 +2960,62 @@
             "ephemeral_key": []
           }
         ]
+      },
+      "delete": {
+        "tags": [
+          "Payment Method Session"
+        ],
+        "summary": "Payment Method Session - Delete a saved payment method",
+        "description": "Delete a saved payment method from the given payment method session.",
+        "operationId": "Delete a saved payment method",
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "The unique identifier for the Payment Method Session",
+            "required": true,
+            "schema": {
+              "type": "string"
+            }
+          }
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/PaymentMethodSessionDeleteSavedPaymentMethod"
+              },
+              "examples": {
+                "Update the card holder name": {
+                  "value": {
+                    "payment_method_id": "12345_pm_0194b1ecabc172e28aeb71f70a4daba3"
+                  }
+                }
+              }
+            }
+          },
+          "required": true
+        },
+        "responses": {
+          "200": {
+            "description": "The payment method has been updated successfully",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/PaymentMethodDeleteResponse"
+                }
+              }
+            }
+          },
+          "404": {
+            "description": "The request is invalid"
+          }
+        },
+        "security": [
+          {
+            "ephemeral_key": []
+          }
+        ]
       }
     },
     "/v2/payment-method-session/:id/list-payment-methods": {
@@ -4457,15 +4513,19 @@
       "AuthenticationDetails": {
         "type": "object",
         "required": [
-          "status",
-          "error"
+          "status"
         ],
         "properties": {
           "status": {
             "$ref": "#/components/schemas/IntentStatus"
           },
           "error": {
-            "$ref": "#/components/schemas/ErrorDetails"
+            "allOf": [
+              {
+                "$ref": "#/components/schemas/ErrorDetails"
+              }
+            ],
+            "nullable": true
           }
         }
       },
@@ -15602,6 +15662,19 @@
           }
         }
       },
+      "PaymentMethodSessionDeleteSavedPaymentMethod": {
+        "type": "object",
+        "required": [
+          "payment_method_id"
+        ],
+        "properties": {
+          "payment_method_id": {
+            "type": "string",
+            "description": "The payment method id of the payment method to be updated",
+            "example": "12345_pm_01926c58bc6e77c09e809964e72af8c8"
+          }
+        }
+      },
       "PaymentMethodSessionRequest": {
         "type": "object",
         "required": [
diff --git a/crates/api_models/src/payment_methods.rs b/crates/api_models/src/payment_methods.rs
index d665337fda0..2dbe16bfaa6 100644
--- a/crates/api_models/src/payment_methods.rs
+++ b/crates/api_models/src/payment_methods.rs
@@ -2781,6 +2781,14 @@ pub struct PaymentMethodSessionUpdateSavedPaymentMethod {
     pub payment_method_update_request: PaymentMethodUpdate,
 }
 
+#[cfg(feature = "v2")]
+#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
+pub struct PaymentMethodSessionDeleteSavedPaymentMethod {
+    /// The payment method id of the payment method to be updated
+    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
+    pub payment_method_id: id_type::GlobalPaymentMethodId,
+}
+
 #[cfg(feature = "v2")]
 #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)]
 pub struct PaymentMethodSessionConfirmRequest {
@@ -2858,6 +2866,6 @@ pub struct AuthenticationDetails {
     pub status: common_enums::IntentStatus,
 
     /// Error details of the authentication
-    #[schema(value_type = ErrorDetails)]
+    #[schema(value_type = Option<ErrorDetails>)]
     pub error: Option<payments::ErrorDetails>,
 }
diff --git a/crates/openapi/src/openapi_v2.rs b/crates/openapi/src/openapi_v2.rs
index ff4459ac56f..a84f04dc6c5 100644
--- a/crates/openapi/src/openapi_v2.rs
+++ b/crates/openapi/src/openapi_v2.rs
@@ -143,6 +143,7 @@ Never share your secret api keys. Keep them guarded and secure.
         routes::payment_method::payment_method_session_retrieve,
         routes::payment_method::payment_method_session_list_payment_methods,
         routes::payment_method::payment_method_session_update_saved_payment_method,
+        routes::payment_method::payment_method_session_delete_saved_payment_method,
         routes::payment_method::payment_method_session_confirm,
 
         //Routes for refunds
@@ -212,6 +213,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::payment_methods::PaymentMethodCreate,
         api_models::payment_methods::PaymentMethodIntentCreate,
         api_models::payment_methods::PaymentMethodIntentConfirm,
+        api_models::payment_methods::AuthenticationDetails,
         api_models::payment_methods::PaymentMethodResponse,
         api_models::payment_methods::PaymentMethodResponseData,
         api_models::payment_methods::CustomerPaymentMethod,
@@ -697,6 +699,7 @@ Never share your secret api keys. Keep them guarded and secure.
         api_models::feature_matrix::CardSpecificFeatures,
         api_models::feature_matrix::SupportedPaymentMethod,
         api_models::payment_methods::PaymentMethodSessionUpdateSavedPaymentMethod,
+        api_models::payment_methods::PaymentMethodSessionDeleteSavedPaymentMethod,
         common_utils::types::BrowserInformation,
         api_models::enums::TokenizationType,
         api_models::enums::NetworkTokenizationToggle,
diff --git a/crates/openapi/src/routes/payment_method.rs b/crates/openapi/src/routes/payment_method.rs
index cf1cd15346f..6dcf3b67753 100644
--- a/crates/openapi/src/routes/payment_method.rs
+++ b/crates/openapi/src/routes/payment_method.rs
@@ -444,6 +444,35 @@ pub fn payment_method_session_list_payment_methods() {}
 )]
 pub fn payment_method_session_update_saved_payment_method() {}
 
+/// Payment Method Session - Delete a saved payment method
+///
+/// Delete a saved payment method from the given payment method session.
+#[cfg(feature = "v2")]
+#[utoipa::path(
+    delete,
+    path = "/v2/payment-method-session/:id",
+    params (
+        ("id" = String, Path, description = "The unique identifier for the Payment Method Session"),
+    ),
+    request_body(
+        content = PaymentMethodSessionDeleteSavedPaymentMethod,
+            examples(( "Update the card holder name" = (
+                value =json!( {
+                    "payment_method_id": "12345_pm_0194b1ecabc172e28aeb71f70a4daba3",
+                }
+            )
+        )))
+    ),
+    responses(
+        (status = 200, description = "The payment method has been updated successfully", body = PaymentMethodDeleteResponse),
+        (status = 404, description = "The request is invalid")
+    ),
+    tag = "Payment Method Session",
+    operation_id = "Delete a saved payment method",
+    security(("ephemeral_key" = []))
+)]
+pub fn payment_method_session_delete_saved_payment_method() {}
+
 /// Card network tokenization - Create using raw card data
 ///
 /// Create a card network token for a customer and store it as a payment method.
diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs
index eec47ca6255..86e4d8e1646 100644
--- a/crates/router/src/core/payment_methods.rs
+++ b/crates/router/src/core/payment_methods.rs
@@ -2021,12 +2021,24 @@ pub async fn delete_payment_method(
     key_store: domain::MerchantKeyStore,
     merchant_account: domain::MerchantAccount,
 ) -> RouterResponse<api::PaymentMethodDeleteResponse> {
-    let db = state.store.as_ref();
-    let key_manager_state = &(&state).into();
-
     let pm_id = id_type::GlobalPaymentMethodId::generate_from_string(pm_id.payment_method_id)
         .change_context(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("Unable to generate GlobalPaymentMethodId")?;
+    let response = delete_payment_method_core(state, pm_id, key_store, merchant_account).await?;
+
+    Ok(services::ApplicationResponse::Json(response))
+}
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all)]
+pub async fn delete_payment_method_core(
+    state: SessionState,
+    pm_id: id_type::GlobalPaymentMethodId,
+    key_store: domain::MerchantKeyStore,
+    merchant_account: domain::MerchantAccount,
+) -> RouterResult<api::PaymentMethodDeleteResponse> {
+    let db = state.store.as_ref();
+    let key_manager_state = &(&state).into();
 
     let payment_method = db
         .find_payment_method(
@@ -2084,7 +2096,7 @@ pub async fn delete_payment_method(
 
     let response = api::PaymentMethodDeleteResponse { id: pm_id };
 
-    Ok(services::ApplicationResponse::Json(response))
+    Ok(response)
 }
 
 #[cfg(feature = "v2")]
@@ -2415,6 +2427,32 @@ pub async fn payment_methods_session_update_payment_method(
     Ok(services::ApplicationResponse::Json(updated_payment_method))
 }
 
+#[cfg(feature = "v2")]
+pub async fn payment_methods_session_delete_payment_method(
+    state: SessionState,
+    key_store: domain::MerchantKeyStore,
+    merchant_account: domain::MerchantAccount,
+    pm_id: id_type::GlobalPaymentMethodId,
+    payment_method_session_id: id_type::GlobalPaymentMethodSessionId,
+) -> RouterResponse<api::PaymentMethodDeleteResponse> {
+    let db = state.store.as_ref();
+    let key_manager_state = &(&state).into();
+
+    // Validate if the session still exists
+    db.get_payment_methods_session(key_manager_state, &key_store, &payment_method_session_id)
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::GenericNotFoundError {
+            message: "payment methods session does not exist or has expired".to_string(),
+        })
+        .attach_printable("Failed to retrieve payment methods session from db")?;
+
+    let response = delete_payment_method_core(state, pm_id, key_store, merchant_account)
+        .await
+        .attach_printable("Failed to delete saved payment method")?;
+
+    Ok(services::ApplicationResponse::Json(response))
+}
+
 #[cfg(feature = "v2")]
 fn construct_zero_auth_payments_request(
     confirm_request: &payment_methods::PaymentMethodSessionConfirmRequest,
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index c4896bed498..c6ba89dd8de 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -1332,7 +1332,10 @@ impl PaymentMethodSession {
                     .service(
                         web::resource("")
                             .route(web::get().to(payment_methods::payment_methods_session_retrieve))
-                            .route(web::put().to(payment_methods::payment_methods_session_update)),
+                            .route(web::put().to(payment_methods::payment_methods_session_update))
+                            .route(web::delete().to(
+                                payment_methods::payment_method_session_delete_saved_payment_method,
+                            )),
                     )
                     .service(web::resource("/list-payment-methods").route(
                         web::get().to(payment_methods::payment_method_session_list_payment_methods),
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index 1cc3442b74b..1c3e23c2b03 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -331,6 +331,7 @@ impl From<Flow> for ApiIdentifier {
             | Flow::PaymentMethodSessionRetrieve
             | Flow::PaymentMethodSessionConfirm
             | Flow::PaymentMethodSessionUpdateSavedPaymentMethod
+            | Flow::PaymentMethodSessionDeleteSavedPaymentMethod
             | Flow::PaymentMethodSessionUpdate => Self::PaymentMethodSession,
         }
     }
diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs
index ec5e1dacf0a..6b60a05c6c1 100644
--- a/crates/router/src/routes/payment_methods.rs
+++ b/crates/router/src/routes/payment_methods.rs
@@ -1327,3 +1327,46 @@ pub async fn payment_method_session_update_saved_payment_method(
     ))
     .await
 }
+
+#[cfg(all(feature = "v2", feature = "payment_methods_v2"))]
+#[instrument(skip_all, fields(flow = ?Flow::PaymentMethodSessionUpdateSavedPaymentMethod))]
+pub async fn payment_method_session_delete_saved_payment_method(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    path: web::Path<id_type::GlobalPaymentMethodSessionId>,
+    json_payload: web::Json<
+        api_models::payment_methods::PaymentMethodSessionDeleteSavedPaymentMethod,
+    >,
+) -> HttpResponse {
+    let flow = Flow::PaymentMethodSessionDeleteSavedPaymentMethod;
+    let payload = json_payload.into_inner();
+    let payment_method_session_id = path.into_inner();
+
+    let request = PaymentMethodsSessionGenericRequest {
+        payment_method_session_id: payment_method_session_id.clone(),
+        request: payload,
+    };
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        request,
+        |state, auth: auth::AuthenticationData, request, _| {
+            payment_methods_routes::payment_methods_session_delete_payment_method(
+                state,
+                auth.key_store,
+                auth.merchant_account,
+                request.request.payment_method_id,
+                request.payment_method_session_id,
+            )
+        },
+        &auth::V2ClientAuth(
+            common_utils::types::authentication::ResourceId::PaymentMethodSession(
+                payment_method_session_id,
+            ),
+        ),
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index 26f88217e4a..890dcae12c6 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -566,6 +566,8 @@ pub enum Flow {
     PaymentMethodSessionUpdate,
     /// Update a saved payment method using the payment methods session
     PaymentMethodSessionUpdateSavedPaymentMethod,
+    /// Delete a saved payment method using the payment methods session
+    PaymentMethodSessionDeleteSavedPaymentMethod,
     /// Confirm a payment method session with payment method data
     PaymentMethodSessionConfirm,
     /// Create Cards Info flow


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

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

## Description
<!-- Describe your changes in detail -->
This endpoint allows deleting saved payment methods for a customer

### 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).
-->

Closes #7407 

## 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)?
-->

1. Request:

```
curl --location --request DELETE 'http://localhost:8080/v2/payment-methods-session/12345_pms_01955b8bed367ee09f81e053cde47593' \
--header 'Authorization: publishable-key=pk_dev_16fea0a9bf5447ed9f25aa835565f285,client-secret=cs_01955b8bed37771282694611c1db81e5' \
--header 'X-Profile-Id: pro_0xaQT1sPjecc284Qtf5N' \
--header 'Content-Type: application/json' \
--header 'api-key: dev_DfzEs7oHcqmWiWeKTPZ8RqHnWyuuPkEheYExXkY1AO1H5EMdkgNcfTFe3JU4vFLt' \
--data '{
    "payment_method_id": "12345_pm_01955b8c21d37b53b999e934b53fffae"
}'
```

2. Response:
```json
{"id":"12345_pm_01955b8c21d37b53b999e934b53fffae"}
```


## 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
