Bug: [BUG] No provision for approving payments kept in ManualReview state for FRM flows



### Bug Description

Once a fraudulent payment transaction is identified before authorization, it can be canceled or kept in manual reviewing state. If it is canceled, the payment is marked as a `failed` transaction. If the decision for the transaction is `manual_review`, it needs to be reviewed by the consumer and can be approved or rejected.

If the transaction seem legit, the consumer can hit the `/approve` endpoint for approving the payments. This flow is broken. 

### Expected Behavior

If a txn is marked as `fraudulent`, it should still proceed for authorization, without capturing the amount.

Upon approval, the amount can be captured.
Upon rejection, the txn can be voided.

### Actual Behavior

If a txn is marked as `fraudulent`, the flow would store the `payment_method_data` in locker, which could be later fetched if approval was given.

Upon approval, it tries to fetch `payment_method_data` from locker, but fails (due to recent locker changes).
Upon rejection, it marks the txn as `failed`.

### Steps To Reproduce

1. Create a merchant account
2. Create an API key
3. Add payment connector
4. Add FRM connector (enable pre flows for card, and set merchant_decision to `manual_review`)
5. Create a payment using debit / credit card w huge amounts, so it can be marked as fraudulent
6. Returned status - `requires_merchant_action`
7. Try to approve this payment
8. Returned error - payment_method_data not found

### Context For The Bug

This is specifically for Pre-Transaction FRM flows, where the decision is set to `manual_review`. If a txn is stuck with state `requires_merchant_action`, those transactions can never be approved. They can only be rejected.

### Environment

Are you using hyperswitch hosted version? Yes/No
If yes, please provide the value of the `x-request-id` response header to help us debug your issue.

If not (or if building/running locally), please provide the following details:
1. Operating System or Linux distribution:
2. Rust version (output of `rustc --version`): `rustc 1.74.1 (a28077b28 2023-12-04)`
3. App version (output of `cargo r --features vergen -- --version`): ``


### Have you spent some time checking if this bug 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!
diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs
index ad3a7638774..0e3f67c051b 100644
--- a/crates/router/src/core/fraud_check.rs
+++ b/crates/router/src/core/fraud_check.rs
@@ -431,6 +431,7 @@ pub async fn pre_payment_frm_core<'a, F>(
     frm_configs: FrmConfigsObject,
     customer: &Option<domain::Customer>,
     should_continue_transaction: &mut bool,
+    should_continue_capture: &mut bool,
     key_store: domain::MerchantKeyStore,
 ) -> RouterResult<Option<FrmData>>
 where
@@ -466,13 +467,12 @@ where
                 .await?;
             let frm_fraud_check = frm_data_updated.fraud_check.clone();
             payment_data.frm_message = Some(frm_fraud_check.clone());
-            if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud)
-            //DontTakeAction
-            {
-                *should_continue_transaction = false;
+            if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) {
                 if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) {
+                    *should_continue_transaction = false;
                     frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction);
                 } else if matches!(frm_configs.frm_action, api_enums::FrmAction::ManualReview) {
+                    *should_continue_capture = false;
                     frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview);
                 }
             }
@@ -582,6 +582,7 @@ pub async fn call_frm_before_connector_call<'a, F, Req, Ctx>(
     frm_info: &mut Option<FrmInfo<F>>,
     customer: &Option<domain::Customer>,
     should_continue_transaction: &mut bool,
+    should_continue_capture: &mut bool,
     key_store: domain::MerchantKeyStore,
 ) -> RouterResult<Option<FrmConfigsObject>>
 where
@@ -615,6 +616,7 @@ where
                         frm_configs,
                         customer,
                         should_continue_transaction,
+                        should_continue_capture,
                         key_store,
                     )
                     .await?;
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 21cdec92ccb..5b6789dad0b 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -181,6 +181,8 @@ where
         #[allow(unused_variables, unused_mut)]
         let mut should_continue_transaction: bool = true;
         #[cfg(feature = "frm")]
+        let mut should_continue_capture: bool = true;
+        #[cfg(feature = "frm")]
         let frm_configs = if state.conf.frm.enabled {
             frm_core::call_frm_before_connector_call(
                 db,
@@ -191,6 +193,7 @@ where
                 &mut frm_info,
                 &customer,
                 &mut should_continue_transaction,
+                &mut should_continue_capture,
                 key_store.clone(),
             )
             .await?
@@ -199,12 +202,25 @@ where
         };
         #[cfg(feature = "frm")]
         logger::debug!(
-            "should_cancel_transaction: {:?} {:?} ",
+            "frm_configs: {:?}\nshould_cancel_transaction: {:?}\nshould_continue_capture: {:?}",
             frm_configs,
-            should_continue_transaction
+            should_continue_transaction,
+            should_continue_capture,
         );
 
         if should_continue_transaction {
+            #[cfg(feature = "frm")]
+            match (
+                should_continue_capture,
+                payment_data.payment_attempt.capture_method,
+            ) {
+                (false, Some(storage_enums::CaptureMethod::Automatic))
+                | (false, Some(storage_enums::CaptureMethod::Scheduled)) => {
+                    payment_data.payment_attempt.capture_method =
+                        Some(storage_enums::CaptureMethod::Manual);
+                }
+                _ => (),
+            };
             payment_data = match connector_details {
                 api::ConnectorCallType::PreDetermined(connector) => {
                     let schedule_time = if should_add_task_to_process_tracker {
@@ -233,6 +249,10 @@ where
                         &validate_result,
                         schedule_time,
                         header_payload,
+                        #[cfg(feature = "frm")]
+                        frm_info.as_ref().and_then(|fi| fi.suggested_action),
+                        #[cfg(not(feature = "frm"))]
+                        None,
                     )
                     .await?;
                     let operation = Box::new(PaymentResponse);
@@ -284,6 +304,10 @@ where
                         &validate_result,
                         schedule_time,
                         header_payload,
+                        #[cfg(feature = "frm")]
+                        frm_info.as_ref().and_then(|fi| fi.suggested_action),
+                        #[cfg(not(feature = "frm"))]
+                        None,
                     )
                     .await?;
 
@@ -311,6 +335,10 @@ where
                                 &customer,
                                 &validate_result,
                                 schedule_time,
+                                #[cfg(feature = "frm")]
+                                frm_info.as_ref().and_then(|fi| fi.suggested_action),
+                                #[cfg(not(feature = "frm"))]
+                                None,
                             )
                             .await?;
                         };
@@ -996,6 +1024,7 @@ pub async fn call_connector_service<F, RouterDReq, ApiRequest, Ctx>(
     validate_result: &operations::ValidateResult<'_>,
     schedule_time: Option<time::PrimitiveDateTime>,
     header_payload: HeaderPayload,
+    frm_suggestion: Option<storage_enums::FrmSuggestion>,
 ) -> RouterResult<router_types::RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
 where
     F: Send + Clone + Sync,
@@ -1172,7 +1201,7 @@ where
             merchant_account.storage_scheme,
             updated_customer,
             key_store,
-            None,
+            frm_suggestion,
             header_payload,
         )
         .await?;
@@ -2109,6 +2138,7 @@ pub fn should_call_connector<Op: Debug, F: Clone>(
         }
         "CompleteAuthorize" => true,
         "PaymentApprove" => true,
+        "PaymentReject" => true,
         "PaymentSession" => true,
         "PaymentIncrementalAuthorization" => matches!(
             payment_data.payment_intent.status,
diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs
index cddbc89acff..6d3697caabd 100644
--- a/crates/router/src/core/payments/operations/payment_approve.rs
+++ b/crates/router/src/core/payments/operations/payment_approve.rs
@@ -2,54 +2,51 @@ use std::marker::PhantomData;
 
 use api_models::enums::FrmSuggestion;
 use async_trait::async_trait;
-use data_models::mandates::MandateData;
-use error_stack::{report, IntoReport, ResultExt};
+use error_stack::{IntoReport, ResultExt};
 use router_derive::PaymentOperation;
 use router_env::{instrument, tracing};
 
 use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
 use crate::{
     core::{
-        errors::{self, CustomResult, RouterResult, StorageErrorExt},
+        errors::{self, RouterResult, StorageErrorExt},
         payment_methods::PaymentMethodRetrieve,
-        payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData},
+        payments::{helpers, operations, PaymentAddress, PaymentData},
         utils as core_utils,
     },
-    db::StorageInterface,
     routes::AppState,
     services,
     types::{
-        self,
         api::{self, PaymentIdTypeExt},
         domain,
         storage::{self, enums as storage_enums},
     },
-    utils::{self, OptionExt},
+    utils::OptionExt,
 };
 
 #[derive(Debug, Clone, Copy, PaymentOperation)]
-#[operation(operations = "all", flow = "authorize")]
+#[operation(operations = "all", flow = "capture")]
 pub struct PaymentApprove;
 
 #[async_trait]
 impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
-    GetTracker<F, PaymentData<F>, api::PaymentsRequest, Ctx> for PaymentApprove
+    GetTracker<F, PaymentData<F>, api::PaymentsCaptureRequest, Ctx> for PaymentApprove
 {
     #[instrument(skip_all)]
     async fn get_trackers<'a>(
         &'a self,
         state: &'a AppState,
         payment_id: &api::PaymentIdType,
-        request: &api::PaymentsRequest,
-        mandate_type: Option<api::MandateTransactionType>,
+        _request: &api::PaymentsCaptureRequest,
+        _mandate_type: Option<api::MandateTransactionType>,
         merchant_account: &domain::MerchantAccount,
         key_store: &domain::MerchantKeyStore,
         _auth_flow: services::AuthFlow,
-    ) -> RouterResult<operations::GetTrackerResponse<'a, F, api::PaymentsRequest, Ctx>> {
+    ) -> RouterResult<operations::GetTrackerResponse<'a, F, api::PaymentsCaptureRequest, Ctx>> {
         let db = &*state.store;
         let merchant_id = &merchant_account.merchant_id;
         let storage_scheme = merchant_account.storage_scheme;
-        let (mut payment_intent, mut payment_attempt, currency, amount);
+        let (mut payment_intent, payment_attempt, currency, amount);
 
         let payment_id = payment_id
             .get_payment_intent_id()
@@ -59,9 +56,6 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
             .find_payment_intent_by_payment_id_merchant_id(&payment_id, merchant_id, storage_scheme)
             .await
             .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
-        payment_intent.setup_future_usage = request
-            .setup_future_usage
-            .or(payment_intent.setup_future_usage);
 
         helpers::validate_payment_status_against_not_allowed_statuses(
             &payment_intent.status,
@@ -69,7 +63,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
                 storage_enums::IntentStatus::Failed,
                 storage_enums::IntentStatus::Succeeded,
             ],
-            "confirm",
+            "approve",
         )?;
 
         let profile_id = payment_intent
@@ -87,31 +81,6 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
                 id: profile_id.to_string(),
             })?;
 
-        let (
-            token,
-            payment_method,
-            payment_method_type,
-            setup_mandate,
-            recurring_mandate_payment_data,
-            mandate_connector,
-        ) = helpers::get_token_pm_type_mandate_details(
-            state,
-            request,
-            mandate_type.clone(),
-            merchant_account,
-            key_store,
-        )
-        .await?;
-
-        let browser_info = request
-            .browser_info
-            .clone()
-            .map(|x| utils::Encode::<types::BrowserInformation>::encode_to_value(&x))
-            .transpose()
-            .change_context(errors::ApiErrorResponse::InvalidDataValue {
-                field_name: "browser_info",
-            })?;
-
         let attempt_id = payment_intent.active_attempt.get_id().clone();
         payment_attempt = db
             .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
@@ -123,35 +92,12 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
             .await
             .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
 
-        let token = token.or_else(|| payment_attempt.payment_token.clone());
-
-        helpers::validate_pm_or_token_given(
-            &request.payment_method,
-            &request.payment_method_data,
-            &request.payment_method_type,
-            &mandate_type,
-            &token,
-        )?;
-
-        payment_attempt.payment_method = payment_method.or(payment_attempt.payment_method);
-        payment_attempt.browser_info = browser_info;
-        payment_attempt.payment_method_type =
-            payment_method_type.or(payment_attempt.payment_method_type);
-        payment_attempt.payment_experience = request.payment_experience;
         currency = payment_attempt.currency.get_required_value("currency")?;
         amount = payment_attempt.get_total_amount().into();
 
-        helpers::validate_customer_id_mandatory_cases(
-            request.setup_future_usage.is_some(),
-            &payment_intent
-                .customer_id
-                .clone()
-                .or_else(|| request.customer_id.clone()),
-        )?;
-
         let shipping_address = helpers::create_or_find_address_for_payment_by_request(
             db,
-            request.shipping.as_ref(),
+            None,
             payment_intent.shipping_address_id.as_deref(),
             merchant_id,
             payment_intent.customer_id.as_ref(),
@@ -162,7 +108,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
         .await?;
         let billing_address = helpers::create_or_find_address_for_payment_by_request(
             db,
-            request.billing.as_ref(),
+            None,
             payment_intent.billing_address_id.as_deref(),
             merchant_id,
             payment_intent.customer_id.as_ref(),
@@ -172,47 +118,8 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
         )
         .await?;
 
-        let redirect_response = request
-            .feature_metadata
-            .as_ref()
-            .and_then(|fm| fm.redirect_response.clone());
-
         payment_intent.shipping_address_id = shipping_address.clone().map(|i| i.address_id);
         payment_intent.billing_address_id = billing_address.clone().map(|i| i.address_id);
-        payment_intent.return_url = request
-            .return_url
-            .as_ref()
-            .map(|a| a.to_string())
-            .or(payment_intent.return_url);
-
-        payment_intent.allowed_payment_method_types = request
-            .get_allowed_payment_method_types_as_value()
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Error converting allowed_payment_types to Value")?
-            .or(payment_intent.allowed_payment_method_types);
-
-        payment_intent.connector_metadata = request
-            .get_connector_metadata_as_value()
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Error converting connector_metadata to Value")?
-            .or(payment_intent.connector_metadata);
-
-        payment_intent.feature_metadata = request
-            .get_feature_metadata_as_value()
-            .change_context(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("Error converting feature_metadata to Value")?
-            .or(payment_intent.feature_metadata);
-
-        payment_intent.metadata = request.metadata.clone().or(payment_intent.metadata);
-
-        // The operation merges mandate data from both request and payment_attempt
-        let setup_mandate = setup_mandate.map(|mandate_data| MandateData {
-            customer_acceptance: mandate_data.customer_acceptance,
-            mandate_type: payment_attempt
-                .mandate_details
-                .clone()
-                .or(mandate_data.mandate_type),
-        });
 
         let frm_response = db
         .find_fraud_check_by_payment_id(payment_intent.payment_id.clone(), merchant_account.merchant_id.clone())
@@ -228,49 +135,41 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
             payment_attempt,
             currency,
             amount,
-            email: request.email.clone(),
+            email: None,
             mandate_id: None,
-            mandate_connector,
-            setup_mandate,
-            token,
+            mandate_connector: None,
+            setup_mandate: None,
+            token: None,
             address: PaymentAddress {
                 shipping: shipping_address.as_ref().map(|a| a.into()),
                 billing: billing_address.as_ref().map(|a| a.into()),
             },
-            confirm: request.confirm,
-            payment_method_data: request.payment_method_data.clone(),
+            confirm: None,
+            payment_method_data: None,
             force_sync: None,
             refunds: vec![],
             disputes: vec![],
             attempts: None,
             sessions_token: vec![],
-            card_cvc: request.card_cvc.clone(),
+            card_cvc: None,
             creds_identifier: None,
             pm_token: None,
             connector_customer_id: None,
-            recurring_mandate_payment_data,
+            recurring_mandate_payment_data: None,
             ephemeral_key: None,
             multiple_capture_data: None,
-            redirect_response,
+            redirect_response: None,
             surcharge_details: None,
             frm_message: frm_response.ok(),
             payment_link_data: None,
             incremental_authorization_details: None,
             authorizations: vec![],
-            frm_metadata: request.frm_metadata.clone(),
+            frm_metadata: None,
         };
 
-        let customer_details = Some(CustomerDetails {
-            customer_id: request.customer_id.clone(),
-            name: request.name.clone(),
-            email: request.email.clone(),
-            phone: request.phone.clone(),
-            phone_country_code: request.phone_country_code.clone(),
-        });
-
         let get_trackers_response = operations::GetTrackerResponse {
             operation: Box::new(self),
-            customer_details,
+            customer_details: None,
             payment_data,
             business_profile,
         };
@@ -279,91 +178,9 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
     }
 }
 
-#[async_trait]
-impl<F: Clone + Send, Ctx: PaymentMethodRetrieve> Domain<F, api::PaymentsRequest, Ctx>
-    for PaymentApprove
-{
-    #[instrument(skip_all)]
-    async fn get_or_create_customer_details<'a>(
-        &'a self,
-        db: &dyn StorageInterface,
-        payment_data: &mut PaymentData<F>,
-        request: Option<CustomerDetails>,
-        key_store: &domain::MerchantKeyStore,
-    ) -> CustomResult<
-        (
-            BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
-            Option<domain::Customer>,
-        ),
-        errors::StorageError,
-    > {
-        helpers::create_customer_if_not_exist(
-            Box::new(self),
-            db,
-            payment_data,
-            request,
-            &key_store.merchant_id,
-            key_store,
-        )
-        .await
-    }
-
-    #[instrument(skip_all)]
-    async fn make_pm_data<'a>(
-        &'a self,
-        state: &'a AppState,
-        payment_data: &mut PaymentData<F>,
-        _storage_scheme: storage_enums::MerchantStorageScheme,
-        merchant_key_store: &domain::MerchantKeyStore,
-        customer: &Option<domain::Customer>,
-    ) -> RouterResult<(
-        BoxedOperation<'a, F, api::PaymentsRequest, Ctx>,
-        Option<api::PaymentMethodData>,
-    )> {
-        let (op, payment_method_data) = helpers::make_pm_data(
-            Box::new(self),
-            state,
-            payment_data,
-            merchant_key_store,
-            customer,
-        )
-        .await?;
-
-        utils::when(payment_method_data.is_none(), || {
-            Err(errors::ApiErrorResponse::PaymentMethodNotFound)
-        })?;
-
-        Ok((op, payment_method_data))
-    }
-
-    #[instrument(skip_all)]
-    async fn add_task_to_process_tracker<'a>(
-        &'a self,
-        _state: &'a AppState,
-        _payment_attempt: &storage::PaymentAttempt,
-        _requeue: bool,
-        _schedule_time: Option<time::PrimitiveDateTime>,
-    ) -> CustomResult<(), errors::ApiErrorResponse> {
-        Ok(())
-    }
-
-    async fn get_connector<'a>(
-        &'a self,
-        _merchant_account: &domain::MerchantAccount,
-        state: &AppState,
-        request: &api::PaymentsRequest,
-        _payment_intent: &storage::PaymentIntent,
-        _key_store: &domain::MerchantKeyStore,
-    ) -> CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
-        // Use a new connector in the confirm call or use the same one which was passed when
-        // creating the payment or if none is passed then use the routing algorithm
-        helpers::get_connector_default(state, request.routing.clone()).await
-    }
-}
-
 #[async_trait]
 impl<F: Clone, Ctx: PaymentMethodRetrieve>
-    UpdateTracker<F, PaymentData<F>, api::PaymentsRequest, Ctx> for PaymentApprove
+    UpdateTracker<F, PaymentData<F>, api::PaymentsCaptureRequest, Ctx> for PaymentApprove
 {
     #[instrument(skip_all)]
     async fn update_trackers<'b>(
@@ -377,7 +194,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         _frm_suggestion: Option<FrmSuggestion>,
         _header_payload: api::HeaderPayload,
     ) -> RouterResult<(
-        BoxedOperation<'b, F, api::PaymentsRequest, Ctx>,
+        BoxedOperation<'b, F, api::PaymentsCaptureRequest, Ctx>,
         PaymentData<F>,
     )>
     where
@@ -401,16 +218,16 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
     }
 }
 
-impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::PaymentsRequest, Ctx>
-    for PaymentApprove
+impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
+    ValidateRequest<F, api::PaymentsCaptureRequest, Ctx> for PaymentApprove
 {
     #[instrument(skip_all)]
     fn validate_request<'a, 'b>(
         &'b self,
-        request: &api::PaymentsRequest,
+        request: &api::PaymentsCaptureRequest,
         merchant_account: &'a domain::MerchantAccount,
     ) -> RouterResult<(
-        BoxedOperation<'b, F, api::PaymentsRequest, Ctx>,
+        BoxedOperation<'b, F, api::PaymentsCaptureRequest, Ctx>,
         operations::ValidateResult<'a>,
     )> {
         let request_merchant_id = request.merchant_id.as_deref();
@@ -420,28 +237,17 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, api::Paymen
                 expected_format: "merchant_id from merchant account".to_string(),
             })?;
 
-        helpers::validate_payment_method_fields_present(request)?;
-
-        let mandate_type =
-            helpers::validate_mandate(request, payments::is_operation_confirm(self))?;
-        let payment_id = request
-            .payment_id
-            .clone()
-            .ok_or(report!(errors::ApiErrorResponse::PaymentNotFound))?;
-
         Ok((
             Box::new(self),
             operations::ValidateResult {
                 merchant_id: &merchant_account.merchant_id,
-                payment_id: payment_id
-                    .and_then(|id| core_utils::validate_id(id, "payment_id"))
-                    .into_report()?,
-                mandate_type,
-                storage_scheme: merchant_account.storage_scheme,
-                requeue: matches!(
-                    request.retry_action,
-                    Some(api_models::enums::RetryAction::Requeue)
+                payment_id: api::PaymentIdType::PaymentIntentId(
+                    core_utils::validate_id(request.payment_id.clone(), "payment_id")
+                        .into_report()?,
                 ),
+                mandate_type: None,
+                storage_scheme: merchant_account.storage_scheme,
+                requeue: false,
             },
         ))
     }
diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs
index 37c7dfd1bae..e958422d312 100644
--- a/crates/router/src/core/payments/operations/payment_reject.rs
+++ b/crates/router/src/core/payments/operations/payment_reject.rs
@@ -1,6 +1,6 @@
 use std::marker::PhantomData;
 
-use api_models::{enums::FrmSuggestion, payments::PaymentsRejectRequest};
+use api_models::{enums::FrmSuggestion, payments::PaymentsCancelRequest};
 use async_trait::async_trait;
 use error_stack::ResultExt;
 use router_derive;
@@ -24,24 +24,24 @@ use crate::{
 };
 
 #[derive(Debug, Clone, Copy, router_derive::PaymentOperation)]
-#[operation(operations = "all", flow = "reject")]
+#[operation(operations = "all", flow = "cancel")]
 pub struct PaymentReject;
 
 #[async_trait]
 impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
-    GetTracker<F, PaymentData<F>, PaymentsRejectRequest, Ctx> for PaymentReject
+    GetTracker<F, PaymentData<F>, PaymentsCancelRequest, Ctx> for PaymentReject
 {
     #[instrument(skip_all)]
     async fn get_trackers<'a>(
         &'a self,
         state: &'a AppState,
         payment_id: &api::PaymentIdType,
-        _request: &PaymentsRejectRequest,
+        _request: &PaymentsCancelRequest,
         _mandate_type: Option<api::MandateTransactionType>,
         merchant_account: &domain::MerchantAccount,
         key_store: &domain::MerchantKeyStore,
         _auth_flow: services::AuthFlow,
-    ) -> RouterResult<operations::GetTrackerResponse<'a, F, PaymentsRejectRequest, Ctx>> {
+    ) -> RouterResult<operations::GetTrackerResponse<'a, F, PaymentsCancelRequest, Ctx>> {
         let db = &*state.store;
         let merchant_id = &merchant_account.merchant_id;
         let storage_scheme = merchant_account.storage_scheme;
@@ -57,6 +57,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
         helpers::validate_payment_status_against_not_allowed_statuses(
             &payment_intent.status,
             &[
+                enums::IntentStatus::Cancelled,
                 enums::IntentStatus::Failed,
                 enums::IntentStatus::Succeeded,
                 enums::IntentStatus::Processing,
@@ -176,7 +177,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
 
 #[async_trait]
 impl<F: Clone, Ctx: PaymentMethodRetrieve>
-    UpdateTracker<F, PaymentData<F>, PaymentsRejectRequest, Ctx> for PaymentReject
+    UpdateTracker<F, PaymentData<F>, PaymentsCancelRequest, Ctx> for PaymentReject
 {
     #[instrument(skip_all)]
     async fn update_trackers<'b>(
@@ -190,7 +191,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         _should_decline_transaction: Option<FrmSuggestion>,
         _header_payload: api::HeaderPayload,
     ) -> RouterResult<(
-        BoxedOperation<'b, F, PaymentsRejectRequest, Ctx>,
+        BoxedOperation<'b, F, PaymentsCancelRequest, Ctx>,
         PaymentData<F>,
     )>
     where
@@ -242,16 +243,16 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
     }
 }
 
-impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, PaymentsRejectRequest, Ctx>
+impl<F: Send + Clone, Ctx: PaymentMethodRetrieve> ValidateRequest<F, PaymentsCancelRequest, Ctx>
     for PaymentReject
 {
     #[instrument(skip_all)]
     fn validate_request<'a, 'b>(
         &'b self,
-        request: &PaymentsRejectRequest,
+        request: &PaymentsCancelRequest,
         merchant_account: &'a domain::MerchantAccount,
     ) -> RouterResult<(
-        BoxedOperation<'b, F, PaymentsRejectRequest, Ctx>,
+        BoxedOperation<'b, F, PaymentsCancelRequest, Ctx>,
         operations::ValidateResult<'a>,
     )> {
         Ok((
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index 9ab0b4f817f..e5552f0d156 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -512,113 +512,132 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
             };
             (capture_update, attempt_update)
         }
-        Ok(payments_response) => match payments_response {
-            types::PaymentsResponseData::PreProcessingResponse {
-                pre_processing_id,
-                connector_metadata,
-                connector_response_reference_id,
-                ..
-            } => {
-                let connector_transaction_id = match pre_processing_id.to_owned() {
-                    types::PreprocessingResponseId::PreProcessingId(_) => None,
-                    types::PreprocessingResponseId::ConnectorTransactionId(connector_txn_id) => {
-                        Some(connector_txn_id)
-                    }
-                };
-                let preprocessing_step_id = match pre_processing_id {
-                    types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => {
-                        Some(pre_processing_id)
+        Ok(payments_response) => {
+            let attempt_status = payment_data.payment_attempt.status.to_owned();
+            let connector_status = router_data.status.to_owned();
+            let updated_attempt_status = match (
+                connector_status,
+                attempt_status,
+                payment_data.frm_message.to_owned(),
+            ) {
+                (
+                    enums::AttemptStatus::Authorized,
+                    enums::AttemptStatus::Unresolved,
+                    Some(frm_message),
+                ) => match frm_message.frm_status {
+                    enums::FraudCheckStatus::Fraud | enums::FraudCheckStatus::ManualReview => {
+                        attempt_status
                     }
-                    types::PreprocessingResponseId::ConnectorTransactionId(_) => None,
-                };
-                let payment_attempt_update = storage::PaymentAttemptUpdate::PreprocessingUpdate {
-                    status: router_data.get_attempt_status_for_db_update(&payment_data),
-                    payment_method_id: Some(router_data.payment_method_id),
+                    _ => router_data.get_attempt_status_for_db_update(&payment_data),
+                },
+                _ => router_data.get_attempt_status_for_db_update(&payment_data),
+            };
+            match payments_response {
+                types::PaymentsResponseData::PreProcessingResponse {
+                    pre_processing_id,
                     connector_metadata,
-                    preprocessing_step_id,
-                    connector_transaction_id,
                     connector_response_reference_id,
-                    updated_by: storage_scheme.to_string(),
-                };
-
-                (None, Some(payment_attempt_update))
-            }
-            types::PaymentsResponseData::TransactionResponse {
-                resource_id,
-                redirection_data,
-                connector_metadata,
-                connector_response_reference_id,
-                incremental_authorization_allowed,
-                ..
-            } => {
-                payment_data
-                    .payment_intent
-                    .incremental_authorization_allowed =
-                    core_utils::get_incremental_authorization_allowed_value(
-                        incremental_authorization_allowed,
-                        payment_data
-                            .payment_intent
-                            .request_incremental_authorization,
-                    );
-                let connector_transaction_id = match resource_id {
-                    types::ResponseId::NoResponseId => None,
-                    types::ResponseId::ConnectorTransactionId(id)
-                    | types::ResponseId::EncodedData(id) => Some(id),
-                };
-
-                let encoded_data = payment_data.payment_attempt.encoded_data.clone();
-
-                let authentication_data = redirection_data
-                    .map(|data| utils::Encode::<RedirectForm>::encode_to_value(&data))
-                    .transpose()
-                    .change_context(errors::ApiErrorResponse::InternalServerError)
-                    .attach_printable("Could not parse the connector response")?;
-
-                // incase of success, update error code and error message
-                let error_status = if router_data.status == enums::AttemptStatus::Charged {
-                    Some(None)
-                } else {
-                    None
-                };
+                    ..
+                } => {
+                    let connector_transaction_id = match pre_processing_id.to_owned() {
+                        types::PreprocessingResponseId::PreProcessingId(_) => None,
+                        types::PreprocessingResponseId::ConnectorTransactionId(
+                            connector_txn_id,
+                        ) => Some(connector_txn_id),
+                    };
+                    let preprocessing_step_id = match pre_processing_id {
+                        types::PreprocessingResponseId::PreProcessingId(pre_processing_id) => {
+                            Some(pre_processing_id)
+                        }
+                        types::PreprocessingResponseId::ConnectorTransactionId(_) => None,
+                    };
+                    let payment_attempt_update =
+                        storage::PaymentAttemptUpdate::PreprocessingUpdate {
+                            status: updated_attempt_status,
+                            payment_method_id: Some(router_data.payment_method_id),
+                            connector_metadata,
+                            preprocessing_step_id,
+                            connector_transaction_id,
+                            connector_response_reference_id,
+                            updated_by: storage_scheme.to_string(),
+                        };
 
-                if router_data.status == enums::AttemptStatus::Charged {
-                    metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
+                    (None, Some(payment_attempt_update))
                 }
+                types::PaymentsResponseData::TransactionResponse {
+                    resource_id,
+                    redirection_data,
+                    connector_metadata,
+                    connector_response_reference_id,
+                    incremental_authorization_allowed,
+                    ..
+                } => {
+                    payment_data
+                        .payment_intent
+                        .incremental_authorization_allowed =
+                        core_utils::get_incremental_authorization_allowed_value(
+                            incremental_authorization_allowed,
+                            payment_data
+                                .payment_intent
+                                .request_incremental_authorization,
+                        );
+                    let connector_transaction_id = match resource_id {
+                        types::ResponseId::NoResponseId => None,
+                        types::ResponseId::ConnectorTransactionId(id)
+                        | types::ResponseId::EncodedData(id) => Some(id),
+                    };
 
-                utils::add_apple_pay_payment_status_metrics(
-                    router_data.status,
-                    router_data.apple_pay_flow.clone(),
-                    payment_data.payment_attempt.connector.clone(),
-                    payment_data.payment_attempt.merchant_id.clone(),
-                );
+                    let encoded_data = payment_data.payment_attempt.encoded_data.clone();
 
-                let (capture_updates, payment_attempt_update) = match payment_data
-                    .multiple_capture_data
-                {
-                    Some(multiple_capture_data) => {
-                        let capture_update = storage::CaptureUpdate::ResponseUpdate {
-                            status: enums::CaptureStatus::foreign_try_from(router_data.status)?,
-                            connector_capture_id: connector_transaction_id.clone(),
-                            connector_response_reference_id,
-                        };
-                        let capture_update_list = vec![(
-                            multiple_capture_data.get_latest_capture().clone(),
-                            capture_update,
-                        )];
-                        (Some((multiple_capture_data, capture_update_list)), None)
+                    let authentication_data = redirection_data
+                        .map(|data| utils::Encode::<RedirectForm>::encode_to_value(&data))
+                        .transpose()
+                        .change_context(errors::ApiErrorResponse::InternalServerError)
+                        .attach_printable("Could not parse the connector response")?;
+
+                    // incase of success, update error code and error message
+                    let error_status = if router_data.status == enums::AttemptStatus::Charged {
+                        Some(None)
+                    } else {
+                        None
+                    };
+
+                    if router_data.status == enums::AttemptStatus::Charged {
+                        metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
                     }
-                    None => {
-                        let status = router_data.get_attempt_status_for_db_update(&payment_data);
-                        (
+
+                    utils::add_apple_pay_payment_status_metrics(
+                        router_data.status,
+                        router_data.apple_pay_flow.clone(),
+                        payment_data.payment_attempt.connector.clone(),
+                        payment_data.payment_attempt.merchant_id.clone(),
+                    );
+
+                    let (capture_updates, payment_attempt_update) = match payment_data
+                        .multiple_capture_data
+                    {
+                        Some(multiple_capture_data) => {
+                            let capture_update = storage::CaptureUpdate::ResponseUpdate {
+                                status: enums::CaptureStatus::foreign_try_from(router_data.status)?,
+                                connector_capture_id: connector_transaction_id.clone(),
+                                connector_response_reference_id,
+                            };
+                            let capture_update_list = vec![(
+                                multiple_capture_data.get_latest_capture().clone(),
+                                capture_update,
+                            )];
+                            (Some((multiple_capture_data, capture_update_list)), None)
+                        }
+                        None => (
                             None,
                             Some(storage::PaymentAttemptUpdate::ResponseUpdate {
-                                status,
+                                status: updated_attempt_status,
                                 connector: None,
                                 connector_transaction_id: connector_transaction_id.clone(),
                                 authentication_type: None,
                                 amount_capturable: router_data
                                     .request
-                                    .get_amount_capturable(&payment_data, status),
+                                    .get_amount_capturable(&payment_data, updated_attempt_status),
                                 payment_method_id: Some(router_data.payment_method_id),
                                 mandate_id: payment_data
                                     .mandate_id
@@ -636,56 +655,58 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                                 authentication_data,
                                 encoded_data,
                             }),
-                        )
-                    }
-                };
+                        ),
+                    };
 
-                (capture_updates, payment_attempt_update)
-            }
-            types::PaymentsResponseData::TransactionUnresolvedResponse {
-                resource_id,
-                reason,
-                connector_response_reference_id,
-            } => {
-                let connector_transaction_id = match resource_id {
-                    types::ResponseId::NoResponseId => None,
-                    types::ResponseId::ConnectorTransactionId(id)
-                    | types::ResponseId::EncodedData(id) => Some(id),
-                };
-                (
-                    None,
-                    Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate {
-                        status: router_data.get_attempt_status_for_db_update(&payment_data),
-                        connector: None,
-                        connector_transaction_id,
-                        payment_method_id: Some(router_data.payment_method_id),
-                        error_code: Some(reason.clone().map(|cd| cd.code)),
-                        error_message: Some(reason.clone().map(|cd| cd.message)),
-                        error_reason: Some(reason.map(|cd| cd.message)),
-                        connector_response_reference_id,
-                        updated_by: storage_scheme.to_string(),
-                    }),
-                )
-            }
-            types::PaymentsResponseData::SessionResponse { .. } => (None, None),
-            types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None),
-            types::PaymentsResponseData::TokenizationResponse { .. } => (None, None),
-            types::PaymentsResponseData::ConnectorCustomerResponse { .. } => (None, None),
-            types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => (None, None),
-            types::PaymentsResponseData::IncrementalAuthorizationResponse { .. } => (None, None),
-            types::PaymentsResponseData::MultipleCaptureResponse {
-                capture_sync_response_list,
-            } => match payment_data.multiple_capture_data {
-                Some(multiple_capture_data) => {
-                    let capture_update_list = response_to_capture_update(
-                        &multiple_capture_data,
-                        capture_sync_response_list,
-                    )?;
-                    (Some((multiple_capture_data, capture_update_list)), None)
+                    (capture_updates, payment_attempt_update)
                 }
-                None => (None, None),
-            },
-        },
+                types::PaymentsResponseData::TransactionUnresolvedResponse {
+                    resource_id,
+                    reason,
+                    connector_response_reference_id,
+                } => {
+                    let connector_transaction_id = match resource_id {
+                        types::ResponseId::NoResponseId => None,
+                        types::ResponseId::ConnectorTransactionId(id)
+                        | types::ResponseId::EncodedData(id) => Some(id),
+                    };
+                    (
+                        None,
+                        Some(storage::PaymentAttemptUpdate::UnresolvedResponseUpdate {
+                            status: updated_attempt_status,
+                            connector: None,
+                            connector_transaction_id,
+                            payment_method_id: Some(router_data.payment_method_id),
+                            error_code: Some(reason.clone().map(|cd| cd.code)),
+                            error_message: Some(reason.clone().map(|cd| cd.message)),
+                            error_reason: Some(reason.map(|cd| cd.message)),
+                            connector_response_reference_id,
+                            updated_by: storage_scheme.to_string(),
+                        }),
+                    )
+                }
+                types::PaymentsResponseData::SessionResponse { .. } => (None, None),
+                types::PaymentsResponseData::SessionTokenResponse { .. } => (None, None),
+                types::PaymentsResponseData::TokenizationResponse { .. } => (None, None),
+                types::PaymentsResponseData::ConnectorCustomerResponse { .. } => (None, None),
+                types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => (None, None),
+                types::PaymentsResponseData::IncrementalAuthorizationResponse { .. } => {
+                    (None, None)
+                }
+                types::PaymentsResponseData::MultipleCaptureResponse {
+                    capture_sync_response_list,
+                } => match payment_data.multiple_capture_data {
+                    Some(multiple_capture_data) => {
+                        let capture_update_list = response_to_capture_update(
+                            &multiple_capture_data,
+                            capture_sync_response_list,
+                        )?;
+                        (Some((multiple_capture_data, capture_update_list)), None)
+                    }
+                    None => (None, None),
+                },
+            }
+        }
     };
     payment_data.multiple_capture_data = match capture_update {
         Some((mut multiple_capture_data, capture_updates)) => {
diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs
index 0fd45c5af3b..8d74eb3fa96 100644
--- a/crates/router/src/core/payments/retry.rs
+++ b/crates/router/src/core/payments/retry.rs
@@ -40,6 +40,7 @@ pub async fn do_gsm_actions<F, ApiRequest, FData, Ctx>(
     customer: &Option<domain::Customer>,
     validate_result: &operations::ValidateResult<'_>,
     schedule_time: Option<time::PrimitiveDateTime>,
+    frm_suggestion: Option<storage_enums::FrmSuggestion>,
 ) -> RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
 where
     F: Clone + Send + Sync,
@@ -90,6 +91,7 @@ where
             validate_result,
             schedule_time,
             true,
+            frm_suggestion,
         )
         .await?;
     }
@@ -133,6 +135,7 @@ where
                         schedule_time,
                         //this is an auto retry payment, but not step-up
                         false,
+                        frm_suggestion,
                     )
                     .await?;
 
@@ -275,6 +278,7 @@ pub async fn do_retry<F, ApiRequest, FData, Ctx>(
     validate_result: &operations::ValidateResult<'_>,
     schedule_time: Option<time::PrimitiveDateTime>,
     is_step_up: bool,
+    frm_suggestion: Option<storage_enums::FrmSuggestion>,
 ) -> RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
 where
     F: Clone + Send + Sync,
@@ -310,6 +314,7 @@ where
         validate_result,
         schedule_time,
         api::HeaderPayload::default(),
+        frm_suggestion,
     )
     .await
 }
diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs
index 34f41c49cdd..379cd4f2f1f 100644
--- a/crates/router/src/routes/payments.rs
+++ b/crates/router/src/routes/payments.rs
@@ -968,7 +968,7 @@ pub async fn payments_approve(
         payload.clone(),
         |state, auth, req| {
             payments::payments_core::<
-                api_types::Authorize,
+                api_types::Capture,
                 payment_types::PaymentsResponse,
                 _,
                 _,
@@ -979,10 +979,8 @@ pub async fn payments_approve(
                 auth.merchant_account,
                 auth.key_store,
                 payments::PaymentApprove,
-                payment_types::PaymentsRequest {
-                    payment_id: Some(payment_types::PaymentIdType::PaymentIntentId(
-                        req.payment_id,
-                    )),
+                payment_types::PaymentsCaptureRequest {
+                    payment_id: req.payment_id,
                     ..Default::default()
                 },
                 api::AuthFlow::Merchant,
@@ -1030,7 +1028,7 @@ pub async fn payments_reject(
         payload.clone(),
         |state, auth, req| {
             payments::payments_core::<
-                api_types::Reject,
+                api_types::Void,
                 payment_types::PaymentsResponse,
                 _,
                 _,
@@ -1041,7 +1039,11 @@ pub async fn payments_reject(
                 auth.merchant_account,
                 auth.key_store,
                 payments::PaymentReject,
-                req,
+                payment_types::PaymentsCancelRequest {
+                    payment_id: req.payment_id,
+                    cancellation_reason: Some("Rejected by merchant".to_string()),
+                    ..Default::default()
+                },
                 api::AuthFlow::Merchant,
                 payments::CallConnectorAction::Trigger,
                 None,

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

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

## Description
#3175 

### Additional Changes

- [ ] 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?
Tested locally using postman collection

https://galactic-capsule-229427.postman.co/workspace/My-Workspace~2b563e0d-bad3-420f-8c0b-0fd5b278a4fe/collection/9906252-e8ad195c-90f5-4527-a40b-1a25bc9bedd2?action=share&creator=9906252

**Test Cases**
- Payments <> FRM manual flow (pre + post) - Approve + Rejection should be working

## 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
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
