Bug: [Refactor] PostAuth FRM flow




This issues is regarding implementing revised PostAuth Frm. Below is the revised flow.


**PostAuth flow**
- The underlying FRM connector is invoked post authentication of the user from the bank.
- If a transaction’s capture method was set to automatic, it is updated to manual for avoiding automatic capture.
- Once the user completes the authentication, the underlying FRM connector is invoked. The decision whether or not to 
   proceed with the txn is based on the status / score returned.


**Possible actions based on the status**
- Continue on **Accept**
              - Continue with the **transaction**
- Halt on **Decline**
              - Mark the transaction as cancelled
- Approve / Decline on **Manual Review**
              - Hold the txn in manual review state. Merchants can list and review such transactions.
              - If approved, payment is captured
              - If declined, payment is voided


diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs
index 0681edbb15a..44e17754696 100644
--- a/crates/common_enums/src/enums.rs
+++ b/crates/common_enums/src/enums.rs
@@ -2249,7 +2249,7 @@ pub enum FrmSuggestion {
     #[default]
     FrmCancelTransaction,
     FrmManualReview,
-    FrmAutoRefund,
+    FrmAuthorizeTransaction, // When manual capture payment which was marked fraud and held, when approved needs to be authorized.
 }
 
 #[derive(
diff --git a/crates/data_models/src/payments/payment_attempt.rs b/crates/data_models/src/payments/payment_attempt.rs
index 0c165632047..7e406ca24d5 100644
--- a/crates/data_models/src/payments/payment_attempt.rs
+++ b/crates/data_models/src/payments/payment_attempt.rs
@@ -303,6 +303,7 @@ pub enum PaymentAttemptUpdate {
         currency: storage_enums::Currency,
         status: storage_enums::AttemptStatus,
         authentication_type: Option<storage_enums::AuthenticationType>,
+        capture_method: Option<storage_enums::CaptureMethod>,
         payment_method: Option<storage_enums::PaymentMethod>,
         browser_info: Option<serde_json::Value>,
         connector: Option<String>,
diff --git a/crates/data_models/src/payments/payment_intent.rs b/crates/data_models/src/payments/payment_intent.rs
index 0ab39bcbad2..a5e631b5ac3 100644
--- a/crates/data_models/src/payments/payment_intent.rs
+++ b/crates/data_models/src/payments/payment_intent.rs
@@ -182,6 +182,7 @@ pub enum PaymentIntentUpdate {
         updated_by: String,
     },
     ApproveUpdate {
+        status: storage_enums::IntentStatus,
         merchant_decision: Option<String>,
         updated_by: String,
     },
@@ -382,9 +383,11 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
                 ..Default::default()
             },
             PaymentIntentUpdate::ApproveUpdate {
+                status,
                 merchant_decision,
                 updated_by,
             } => Self {
+                status: Some(status),
                 merchant_decision,
                 updated_by,
                 ..Default::default()
diff --git a/crates/diesel_models/src/fraud_check.rs b/crates/diesel_models/src/fraud_check.rs
index 8c20fe466af..5513afcfbd1 100644
--- a/crates/diesel_models/src/fraud_check.rs
+++ b/crates/diesel_models/src/fraud_check.rs
@@ -1,3 +1,4 @@
+use common_enums as storage_enums;
 use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
 use masking::{Deserialize, Serialize};
 use time::PrimitiveDateTime;
@@ -25,6 +26,7 @@ pub struct FraudCheck {
     pub metadata: Option<serde_json::Value>,
     pub modified_at: PrimitiveDateTime,
     pub last_step: FraudCheckLastStep,
+    pub payment_capture_method: Option<storage_enums::CaptureMethod>, // In postFrm, we are updating capture method from automatic to manual. To store the merchant actual capture method, we are storing the actual capture method in payment_capture_method. It will be useful while approving the FRM decision.
 }
 
 #[derive(router_derive::Setter, Clone, Debug, Insertable, router_derive::DebugAsDisplay)]
@@ -46,6 +48,7 @@ pub struct FraudCheckNew {
     pub metadata: Option<serde_json::Value>,
     pub modified_at: PrimitiveDateTime,
     pub last_step: FraudCheckLastStep,
+    pub payment_capture_method: Option<storage_enums::CaptureMethod>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -59,6 +62,7 @@ pub enum FraudCheckUpdate {
         metadata: Option<serde_json::Value>,
         modified_at: PrimitiveDateTime,
         last_step: FraudCheckLastStep,
+        payment_capture_method: Option<storage_enums::CaptureMethod>,
     },
     ErrorUpdate {
         status: FraudCheckStatus,
@@ -76,6 +80,7 @@ pub struct FraudCheckUpdateInternal {
     frm_error: Option<Option<String>>,
     metadata: Option<serde_json::Value>,
     last_step: FraudCheckLastStep,
+    payment_capture_method: Option<storage_enums::CaptureMethod>,
 }
 
 impl From<FraudCheckUpdate> for FraudCheckUpdateInternal {
@@ -89,6 +94,7 @@ impl From<FraudCheckUpdate> for FraudCheckUpdateInternal {
                 metadata,
                 modified_at: _,
                 last_step,
+                payment_capture_method,
             } => Self {
                 frm_status: Some(frm_status),
                 frm_transaction_id,
@@ -96,6 +102,7 @@ impl From<FraudCheckUpdate> for FraudCheckUpdateInternal {
                 frm_score,
                 metadata,
                 last_step,
+                payment_capture_method,
                 ..Default::default()
             },
             FraudCheckUpdate::ErrorUpdate {
diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs
index 603e0f4ebf2..65481d93bec 100644
--- a/crates/diesel_models/src/payment_attempt.rs
+++ b/crates/diesel_models/src/payment_attempt.rs
@@ -209,6 +209,7 @@ pub enum PaymentAttemptUpdate {
         currency: storage_enums::Currency,
         status: storage_enums::AttemptStatus,
         authentication_type: Option<storage_enums::AuthenticationType>,
+        capture_method: Option<storage_enums::CaptureMethod>,
         payment_method: Option<storage_enums::PaymentMethod>,
         browser_info: Option<serde_json::Value>,
         connector: Option<String>,
@@ -559,6 +560,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 amount,
                 currency,
                 authentication_type,
+                capture_method,
                 status,
                 payment_method,
                 browser_info,
@@ -610,6 +612,7 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 payment_method_billing_address_id,
                 fingerprint_id,
                 payment_method_id,
+                capture_method,
                 ..Default::default()
             },
             PaymentAttemptUpdate::VoidUpdate {
diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs
index d1711c8ba00..ee6e3960b73 100644
--- a/crates/diesel_models/src/payment_intent.rs
+++ b/crates/diesel_models/src/payment_intent.rs
@@ -180,6 +180,7 @@ pub enum PaymentIntentUpdate {
         updated_by: String,
     },
     ApproveUpdate {
+        status: storage_enums::IntentStatus,
         merchant_decision: Option<String>,
         updated_by: String,
     },
@@ -456,9 +457,11 @@ impl From<PaymentIntentUpdate> for PaymentIntentUpdateInternal {
                 ..Default::default()
             },
             PaymentIntentUpdate::ApproveUpdate {
+                status,
                 merchant_decision,
                 updated_by,
             } => Self {
+                status: Some(status),
                 merchant_decision,
                 updated_by,
                 ..Default::default()
diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs
index 44429cc4cad..e983b09ff21 100644
--- a/crates/diesel_models/src/schema.rs
+++ b/crates/diesel_models/src/schema.rs
@@ -450,6 +450,7 @@ diesel::table! {
         modified_at -> Timestamp,
         #[max_length = 64]
         last_step -> Varchar,
+        payment_capture_method -> Nullable<CaptureMethod>,
     }
 }
 
diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs
index 0e3f67c051b..b62d3afd4a8 100644
--- a/crates/router/src/core/fraud_check.rs
+++ b/crates/router/src/core/fraud_check.rs
@@ -1,6 +1,7 @@
 use std::fmt::Debug;
 
 use api_models::{admin::FrmConfigs, enums as api_enums, payments::AdditionalPaymentData};
+use common_enums::CaptureMethod;
 use error_stack::ResultExt;
 use masking::{ExposeInterface, PeekInterface};
 use router_env::{
@@ -26,11 +27,14 @@ use crate::{
         utils as core_utils,
     },
     db::StorageInterface,
-    routes::AppState,
+    routes::{app::ReqState, AppState},
     services,
     types::{
         self as oss_types,
-        api::{routing::FrmRoutingAlgorithm, Connector, FraudCheckConnectorData, Fulfillment},
+        api::{
+            fraud_check as frm_api, routing::FrmRoutingAlgorithm, Connector,
+            FraudCheckConnectorData, Fulfillment,
+        },
         domain, fraud_check as frm_types,
         storage::{
             enums::{
@@ -94,6 +98,15 @@ where
         .await?;
 
     router_data.status = payment_data.payment_attempt.status;
+    if matches!(
+        frm_data.fraud_check.frm_transaction_type,
+        FraudCheckType::PreFrm
+    ) && matches!(
+        frm_data.fraud_check.last_step,
+        FraudCheckLastStep::CheckoutOrSale
+    ) {
+        frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund
+    }
 
     let connector =
         FraudCheckConnectorData::get_connector_by_name(&frm_data.connector_details.connector_name)?;
@@ -295,7 +308,7 @@ where
                             let is_frm_enabled =
                                 is_frm_connector_enabled && is_frm_pm_enabled && is_frm_pmt_enabled;
                             logger::debug!(
-                                "frm_configs {:?} {:?} {:?} {:?}",
+                                "is_frm_connector_enabled {:?}, is_frm_pm_enabled:  {:?},is_frm_pmt_enabled : {:?},  is_frm_enabled :{:?}",
                                 is_frm_connector_enabled,
                                 is_frm_pm_enabled,
                                 is_frm_pmt_enabled,
@@ -423,7 +436,7 @@ where
 }
 
 #[allow(clippy::too_many_arguments)]
-pub async fn pre_payment_frm_core<'a, F>(
+pub async fn pre_payment_frm_core<'a, F, Req, Ctx>(
     state: &AppState,
     merchant_account: &domain::MerchantAccount,
     payment_data: &mut payments::PaymentData<F>,
@@ -433,85 +446,108 @@ pub async fn pre_payment_frm_core<'a, F>(
     should_continue_transaction: &mut bool,
     should_continue_capture: &mut bool,
     key_store: domain::MerchantKeyStore,
+    operation: &BoxedOperation<'_, F, Req, Ctx>,
 ) -> RouterResult<Option<FrmData>>
 where
     F: Send + Clone,
 {
-    if let Some(frm_data) = &mut frm_info.frm_data {
-        if matches!(
-            frm_configs.frm_preferred_flow_type,
-            api_enums::FrmPreferredFlowTypes::Pre
-        ) {
-            let fraud_check_operation = &mut frm_info.fraud_check_operation;
+    let mut frm_data = None;
+    if is_operation_allowed(operation) {
+        frm_data = if let Some(frm_data) = &mut frm_info.frm_data {
+            if matches!(
+                frm_configs.frm_preferred_flow_type,
+                api_enums::FrmPreferredFlowTypes::Pre
+            ) {
+                let fraud_check_operation = &mut frm_info.fraud_check_operation;
 
-            let frm_router_data = fraud_check_operation
-                .to_domain()?
-                .pre_payment_frm(
+                let frm_router_data = fraud_check_operation
+                    .to_domain()?
+                    .pre_payment_frm(
+                        state,
+                        payment_data,
+                        frm_data,
+                        merchant_account,
+                        customer,
+                        key_store.clone(),
+                    )
+                    .await?;
+                let _router_data = call_frm_service::<F, frm_api::Transaction, _>(
                     state,
                     payment_data,
                     frm_data,
                     merchant_account,
+                    &key_store,
                     customer,
-                    key_store,
                 )
                 .await?;
-            let frm_data_updated = fraud_check_operation
-                .to_update_tracker()?
-                .update_tracker(
-                    &*state.store,
-                    frm_data.clone(),
-                    payment_data,
-                    None,
-                    frm_router_data,
-                )
-                .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) {
-                if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) {
+                let frm_data_updated = fraud_check_operation
+                    .to_update_tracker()?
+                    .update_tracker(
+                        &*state.store,
+                        frm_data.clone(),
+                        payment_data,
+                        None,
+                        frm_router_data,
+                    )
+                    .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) {
                     *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);
+                    if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) {
+                        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);
+                    }
                 }
+                logger::debug!(
+                    "frm_updated_data: {:?} {:?}",
+                    frm_info.fraud_check_operation,
+                    frm_info.suggested_action
+                );
+                Some(frm_data_updated)
+            } else if matches!(
+                frm_configs.frm_preferred_flow_type,
+                api_enums::FrmPreferredFlowTypes::Post
+            ) {
+                *should_continue_capture = false;
+                Some(frm_data.to_owned())
+            } else {
+                Some(frm_data.to_owned())
             }
-            logger::debug!(
-                "frm_updated_data: {:?} {:?}",
-                frm_info.fraud_check_operation,
-                frm_info.suggested_action
-            );
-            Ok(Some(frm_data_updated))
         } else {
-            Ok(Some(frm_data.to_owned()))
-        }
-    } else {
-        Ok(None)
+            None
+        };
     }
+    Ok(frm_data)
 }
 
 #[allow(clippy::too_many_arguments)]
 pub async fn post_payment_frm_core<'a, F>(
     state: &AppState,
+    req_state: ReqState,
     merchant_account: &domain::MerchantAccount,
     payment_data: &mut payments::PaymentData<F>,
     frm_info: &mut FrmInfo<F>,
     frm_configs: FrmConfigsObject,
     customer: &Option<domain::Customer>,
     key_store: domain::MerchantKeyStore,
+    should_continue_capture: &mut bool,
 ) -> RouterResult<Option<FrmData>>
 where
     F: Send + Clone,
 {
     if let Some(frm_data) = &mut frm_info.frm_data {
-        // Allow the Post flow only if the payment is succeeded,
+        // Allow the Post flow only if the payment is authorized,
         // this logic has to be removed if we are going to call /sale or /transaction after failed transaction
         let fraud_check_operation = &mut frm_info.fraud_check_operation;
-        if payment_data.payment_attempt.status == AttemptStatus::Charged {
+        if payment_data.payment_attempt.status == AttemptStatus::Authorized {
             let frm_router_data_opt = fraud_check_operation
                 .to_domain()?
                 .post_payment_frm(
                     state,
+                    req_state.clone(),
                     payment_data,
                     frm_data,
                     merchant_account,
@@ -530,18 +566,23 @@ where
                         frm_router_data.to_owned(),
                     )
                     .await?;
-
-                payment_data.frm_message = Some(frm_data.fraud_check.clone());
-                logger::debug!(
-                    "frm_updated_data: {:?} {:?}",
-                    frm_data,
-                    payment_data.frm_message
-                );
+                let frm_fraud_check = frm_data.fraud_check.clone();
                 let mut frm_suggestion = None;
+                payment_data.frm_message = Some(frm_fraud_check.clone());
+                if matches!(frm_fraud_check.frm_status, FraudCheckStatus::Fraud) {
+                    if matches!(frm_configs.frm_action, api_enums::FrmAction::CancelTxn) {
+                        frm_info.suggested_action = Some(FrmSuggestion::FrmCancelTransaction);
+                    } else if matches!(frm_configs.frm_action, api_enums::FrmAction::ManualReview) {
+                        frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview);
+                    }
+                } else if matches!(frm_fraud_check.frm_status, FraudCheckStatus::ManualReview) {
+                    frm_info.suggested_action = Some(FrmSuggestion::FrmManualReview);
+                }
                 fraud_check_operation
                     .to_domain()?
                     .execute_post_tasks(
                         state,
+                        req_state,
                         &mut frm_data,
                         merchant_account,
                         frm_configs,
@@ -549,6 +590,7 @@ where
                         key_store,
                         payment_data,
                         customer,
+                        should_continue_capture,
                     )
                     .await?;
                 logger::debug!("frm_post_tasks_data: {:?}", frm_data);
@@ -588,51 +630,76 @@ pub async fn call_frm_before_connector_call<'a, F, Req, Ctx>(
 where
     F: Send + Clone,
 {
-    if is_operation_allowed(operation) {
-        let (is_frm_enabled, frm_routing_algorithm, frm_connector_label, frm_configs) =
-            should_call_frm(merchant_account, payment_data, db, key_store.clone()).await?;
-        if let Some((frm_routing_algorithm_val, profile_id)) =
-            frm_routing_algorithm.zip(frm_connector_label)
-        {
-            if let Some(frm_configs) = frm_configs.clone() {
-                let mut updated_frm_info = make_frm_data_and_fraud_check_operation(
-                    db,
+    let (is_frm_enabled, frm_routing_algorithm, frm_connector_label, frm_configs) =
+        should_call_frm(merchant_account, payment_data, db, key_store.clone()).await?;
+    if let Some((frm_routing_algorithm_val, profile_id)) =
+        frm_routing_algorithm.zip(frm_connector_label)
+    {
+        if let Some(frm_configs) = frm_configs.clone() {
+            let mut updated_frm_info = make_frm_data_and_fraud_check_operation(
+                db,
+                state,
+                merchant_account,
+                payment_data.to_owned(),
+                frm_routing_algorithm_val,
+                profile_id,
+                frm_configs.clone(),
+                customer,
+            )
+            .await?;
+
+            if is_frm_enabled {
+                pre_payment_frm_core(
                     state,
                     merchant_account,
-                    payment_data.to_owned(),
-                    frm_routing_algorithm_val,
-                    profile_id,
-                    frm_configs.clone(),
+                    payment_data,
+                    &mut updated_frm_info,
+                    frm_configs,
                     customer,
+                    should_continue_transaction,
+                    should_continue_capture,
+                    key_store,
+                    operation,
                 )
                 .await?;
-
-                if is_frm_enabled {
-                    pre_payment_frm_core(
-                        state,
-                        merchant_account,
-                        payment_data,
-                        &mut updated_frm_info,
-                        frm_configs,
-                        customer,
-                        should_continue_transaction,
-                        should_continue_capture,
-                        key_store,
-                    )
-                    .await?;
-                }
-                *frm_info = Some(updated_frm_info);
             }
+            *frm_info = Some(updated_frm_info);
         }
-        logger::debug!("frm_configs: {:?} {:?}", frm_configs, is_frm_enabled);
-        return Ok(frm_configs);
     }
-    Ok(None)
+    let fraud_capture_method = frm_info.as_ref().and_then(|frm_info| {
+        frm_info
+            .frm_data
+            .as_ref()
+            .map(|frm_data| frm_data.fraud_check.payment_capture_method)
+    });
+    if matches!(fraud_capture_method, Some(Some(CaptureMethod::Manual)))
+        && matches!(
+            payment_data.payment_attempt.status,
+            api_models::enums::AttemptStatus::Unresolved
+        )
+    {
+        if let Some(info) = frm_info {
+            info.suggested_action = Some(FrmSuggestion::FrmAuthorizeTransaction)
+        };
+        *should_continue_transaction = false;
+        logger::debug!(
+            "skipping connector call since payment_capture_method is already {:?}",
+            fraud_capture_method
+        );
+    };
+    logger::debug!("frm_configs: {:?} {:?}", frm_configs, is_frm_enabled);
+    Ok(frm_configs)
 }
 
 pub fn is_operation_allowed<Op: Debug>(operation: &Op) -> bool {
-    !["PaymentSession", "PaymentApprove", "PaymentReject"]
-        .contains(&format!("{operation:?}").as_str())
+    ![
+        "PaymentSession",
+        "PaymentApprove",
+        "PaymentReject",
+        "PaymentCapture",
+        "PaymentsCancel",
+    ]
+    .contains(&format!("{operation:?}").as_str())
 }
 
 impl From<PaymentToFrmData> for PaymentDetails {
@@ -759,6 +826,7 @@ pub async fn make_fulfillment_api_call(
         metadata: fraud_check.metadata,
         modified_at: common_utils::date_time::now(),
         last_step: FraudCheckLastStep::Fulfillment,
+        payment_capture_method: fraud_check.payment_capture_method,
     };
     let _updated = db
         .update_fraud_check_response_with_attempt_id(fraud_check_copy, fraud_check_update)
diff --git a/crates/router/src/core/fraud_check/operation.rs b/crates/router/src/core/fraud_check/operation.rs
index e7677dad6f3..a9b95cdf3b5 100644
--- a/crates/router/src/core/fraud_check/operation.rs
+++ b/crates/router/src/core/fraud_check/operation.rs
@@ -15,7 +15,7 @@ use crate::{
         payments,
     },
     db::StorageInterface,
-    routes::AppState,
+    routes::{app::ReqState, AppState},
     types::{domain, fraud_check::FrmRouterData},
 };
 
@@ -47,10 +47,12 @@ pub trait GetTracker<D>: Send {
 }
 
 #[async_trait]
+#[allow(clippy::too_many_arguments)]
 pub trait Domain<F>: Send + Sync {
     async fn post_payment_frm<'a>(
         &'a self,
         state: &'a AppState,
+        req_state: ReqState,
         payment_data: &mut payments::PaymentData<F>,
         frm_data: &mut FrmData,
         merchant_account: &domain::MerchantAccount,
@@ -78,6 +80,7 @@ pub trait Domain<F>: Send + Sync {
     async fn execute_post_tasks(
         &self,
         _state: &AppState,
+        _req_state: ReqState,
         frm_data: &mut FrmData,
         _merchant_account: &domain::MerchantAccount,
         _frm_configs: FrmConfigsObject,
@@ -85,6 +88,7 @@ pub trait Domain<F>: Send + Sync {
         _key_store: domain::MerchantKeyStore,
         _payment_data: &mut payments::PaymentData<F>,
         _customer: &Option<domain::Customer>,
+        _should_continue_capture: &mut bool,
     ) -> RouterResult<Option<FrmData>>
     where
         F: Send + Clone,
diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs
index a6cd0976129..d59c8c5ff47 100644
--- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs
+++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs
@@ -1,5 +1,6 @@
+use api_models::payments::HeaderPayload;
 use async_trait::async_trait;
-use common_enums::FrmSuggestion;
+use common_enums::{CaptureMethod, FrmSuggestion};
 use common_utils::ext_traits::Encode;
 use data_models::payments::{
     payment_attempt::PaymentAttemptUpdate, payment_intent::PaymentIntentUpdate,
@@ -13,18 +14,20 @@ use crate::{
         errors::{RouterResult, StorageErrorExt},
         fraud_check::{
             self as frm_core,
-            types::{FrmData, PaymentDetails, PaymentToFrmData, REFUND_INITIATED},
+            types::{FrmData, PaymentDetails, PaymentToFrmData, CANCEL_INITIATED},
             ConnectorDetailsCore, FrmConfigsObject,
         },
-        payments, refunds,
+        payment_methods::Oss,
+        payments,
     },
     db::StorageInterface,
-    errors, services,
+    errors,
+    routes::app::ReqState,
+    services::{self, api},
     types::{
         api::{
             enums::{AttemptStatus, FrmAction, IntentStatus},
-            fraud_check as frm_api,
-            refunds::{RefundRequest, RefundType},
+            fraud_check as frm_api, payments as payment_types, Capture, Void,
         },
         domain,
         fraud_check::{
@@ -107,6 +110,7 @@ impl GetTracker<PaymentToFrmData> for FraudCheckPost {
                     metadata: None,
                     modified_at: common_utils::date_time::now(),
                     last_step: FraudCheckLastStep::Processing,
+                    payment_capture_method: payment_data.payment_attempt.capture_method,
                 })
                 .await
             }
@@ -140,6 +144,7 @@ impl<F: Send + Clone> Domain<F> for FraudCheckPost {
     async fn post_payment_frm<'a>(
         &'a self,
         state: &'a AppState,
+        _req_state: ReqState,
         payment_data: &mut payments::PaymentData<F>,
         frm_data: &mut FrmData,
         merchant_account: &domain::MerchantAccount,
@@ -177,6 +182,7 @@ impl<F: Send + Clone> Domain<F> for FraudCheckPost {
     async fn execute_post_tasks(
         &self,
         state: &AppState,
+        req_state: ReqState,
         frm_data: &mut FrmData,
         merchant_account: &domain::MerchantAccount,
         frm_configs: FrmConfigsObject,
@@ -184,38 +190,47 @@ impl<F: Send + Clone> Domain<F> for FraudCheckPost {
         key_store: domain::MerchantKeyStore,
         payment_data: &mut payments::PaymentData<F>,
         customer: &Option<domain::Customer>,
+        _should_continue_capture: &mut bool,
     ) -> RouterResult<Option<FrmData>> {
         if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Fraud)
-            && matches!(frm_configs.frm_action, FrmAction::AutoRefund)
+            && matches!(frm_configs.frm_action, FrmAction::CancelTxn)
             && matches!(
                 frm_data.fraud_check.last_step,
                 FraudCheckLastStep::CheckoutOrSale
             )
         {
-            *frm_suggestion = Some(FrmSuggestion::FrmAutoRefund);
-            let ref_req = RefundRequest {
-                refund_id: None,
-                payment_id: payment_data.payment_intent.payment_id.clone(),
-                merchant_id: Some(merchant_account.merchant_id.clone()),
-                amount: None,
-                reason: frm_data
-                    .fraud_check
-                    .frm_reason
-                    .clone()
-                    .map(|data| data.to_string()),
-                refund_type: Some(RefundType::Instant),
-                metadata: None,
+            *frm_suggestion = Some(FrmSuggestion::FrmCancelTransaction);
+
+            let cancel_req = api_models::payments::PaymentsCancelRequest {
+                payment_id: frm_data.payment_intent.payment_id.clone(),
+                cancellation_reason: frm_data.fraud_check.frm_error.clone(),
                 merchant_connector_details: None,
             };
-            let refund = Box::pin(refunds::refund_create_core(
+            let cancel_res = Box::pin(payments::payments_core::<
+                Void,
+                payment_types::PaymentsResponse,
+                _,
+                _,
+                _,
+                Oss,
+            >(
                 state.clone(),
+                req_state.clone(),
                 merchant_account.clone(),
                 key_store.clone(),
-                ref_req,
+                payments::PaymentCancel,
+                cancel_req,
+                api::AuthFlow::Merchant,
+                payments::CallConnectorAction::Trigger,
+                None,
+                HeaderPayload::default(),
             ))
             .await?;
-            if let services::ApplicationResponse::Json(new_refund) = refund {
-                frm_data.refund = Some(new_refund);
+            logger::debug!("payment_id : {:?} has been cancelled since it has been found fraudulent by configured frm connector",payment_data.payment_attempt.payment_id);
+            if let services::ApplicationResponse::JsonWithHeaders((payments_response, _)) =
+                cancel_res
+            {
+                payment_data.payment_intent.status = payments_response.status;
             }
             let _router_data = frm_core::call_frm_service::<F, frm_api::RecordReturn, _>(
                 state,
@@ -227,6 +242,51 @@ impl<F: Send + Clone> Domain<F> for FraudCheckPost {
             )
             .await?;
             frm_data.fraud_check.last_step = FraudCheckLastStep::TransactionOrRecordRefund;
+        } else if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Fraud)
+            && matches!(frm_configs.frm_action, FrmAction::ManualReview)
+        {
+            *frm_suggestion = Some(FrmSuggestion::FrmManualReview);
+        } else if matches!(frm_data.fraud_check.frm_status, FraudCheckStatus::Legit)
+            && matches!(
+                frm_data.fraud_check.payment_capture_method,
+                Some(CaptureMethod::Automatic)
+            )
+        {
+            let capture_request = api_models::payments::PaymentsCaptureRequest {
+                payment_id: frm_data.payment_intent.payment_id.clone(),
+                merchant_id: None,
+                amount_to_capture: None,
+                refund_uncaptured_amount: None,
+                statement_descriptor_suffix: None,
+                statement_descriptor_prefix: None,
+                merchant_connector_details: None,
+            };
+            let capture_response = Box::pin(payments::payments_core::<
+                Capture,
+                payment_types::PaymentsResponse,
+                _,
+                _,
+                _,
+                Oss,
+            >(
+                state.clone(),
+                req_state.clone(),
+                merchant_account.clone(),
+                key_store.clone(),
+                payments::PaymentCapture,
+                capture_request,
+                api::AuthFlow::Merchant,
+                payments::CallConnectorAction::Trigger,
+                None,
+                HeaderPayload::default(),
+            ))
+            .await?;
+            logger::debug!("payment_id : {:?} has been captured since it has been found legit by configured frm connector",payment_data.payment_attempt.payment_id);
+            if let services::ApplicationResponse::JsonWithHeaders((payments_response, _)) =
+                capture_response
+            {
+                payment_data.payment_intent.status = payments_response.status;
+            }
         };
         return Ok(Some(frm_data.to_owned()));
     }
@@ -302,6 +362,7 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPost {
                             metadata: connector_metadata,
                             modified_at: common_utils::date_time::now(),
                             last_step: frm_data.fraud_check.last_step,
+                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                         };
                         Some(fraud_check_update)
                     },
@@ -346,6 +407,7 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPost {
                             metadata: connector_metadata,
                             modified_at: common_utils::date_time::now(),
                             last_step: frm_data.fraud_check.last_step,
+                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                         };
                         Some(fraud_check_update)
                     }
@@ -396,6 +458,7 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPost {
                             metadata: connector_metadata,
                             modified_at: common_utils::date_time::now(),
                             last_step: frm_data.fraud_check.last_step,
+                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                         };
                         Some(fraud_check_update)
                     }
@@ -412,15 +475,27 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPost {
             }
         };
 
-        if frm_suggestion == Some(FrmSuggestion::FrmAutoRefund) {
+        if let Some(frm_suggestion) = frm_suggestion {
+            let (payment_attempt_status, payment_intent_status) = match frm_suggestion {
+                FrmSuggestion::FrmCancelTransaction => {
+                    (AttemptStatus::Failure, IntentStatus::Failed)
+                }
+                FrmSuggestion::FrmManualReview => (
+                    AttemptStatus::Unresolved,
+                    IntentStatus::RequiresMerchantAction,
+                ),
+                FrmSuggestion::FrmAuthorizeTransaction => {
+                    (AttemptStatus::Authorized, IntentStatus::RequiresCapture)
+                }
+            };
             payment_data.payment_attempt = db
                 .update_payment_attempt_with_attempt_id(
                     payment_data.payment_attempt.clone(),
                     PaymentAttemptUpdate::RejectUpdate {
-                        status: AttemptStatus::Failure,
+                        status: payment_attempt_status,
                         error_code: Some(Some(frm_data.fraud_check.frm_status.to_string())),
-                        error_message: Some(Some(REFUND_INITIATED.to_string())),
-                        updated_by: frm_data.merchant_account.storage_scheme.to_string(), // merchant_decision: Some(MerchantDecision::AutoRefunded),
+                        error_message: Some(Some(CANCEL_INITIATED.to_string())),
+                        updated_by: frm_data.merchant_account.storage_scheme.to_string(),
                     },
                     frm_data.merchant_account.storage_scheme,
                 )
@@ -431,8 +506,8 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPost {
                 .update_payment_intent(
                     payment_data.payment_intent.clone(),
                     PaymentIntentUpdate::RejectUpdate {
-                        status: IntentStatus::Failed,
-                        merchant_decision: Some(MerchantDecision::AutoRefunded.to_string()),
+                        status: payment_intent_status,
+                        merchant_decision: Some(MerchantDecision::Rejected.to_string()),
                         updated_by: frm_data.merchant_account.storage_scheme.to_string(),
                     },
                     frm_data.merchant_account.storage_scheme,
diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs
index ed582574bf5..eac8dc84da5 100644
--- a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs
+++ b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs
@@ -18,6 +18,7 @@ use crate::{
     },
     db::StorageInterface,
     errors,
+    routes::app::ReqState,
     types::{
         api::fraud_check as frm_api,
         domain,
@@ -104,6 +105,7 @@ impl GetTracker<PaymentToFrmData> for FraudCheckPre {
                     metadata: None,
                     modified_at: common_utils::date_time::now(),
                     last_step: FraudCheckLastStep::Processing,
+                    payment_capture_method: payment_data.payment_attempt.capture_method,
                 })
                 .await
             }
@@ -138,6 +140,7 @@ impl<F: Send + Clone> Domain<F> for FraudCheckPre {
     async fn post_payment_frm<'a>(
         &'a self,
         state: &'a AppState,
+        _req_state: ReqState,
         payment_data: &mut payments::PaymentData<F>,
         frm_data: &mut FrmData,
         merchant_account: &domain::MerchantAccount,
@@ -248,6 +251,7 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPre {
                             metadata: connector_metadata,
                             modified_at: common_utils::date_time::now(),
                             last_step: frm_data.fraud_check.last_step,
+                            payment_capture_method: frm_data.fraud_check.payment_capture_method,
                         };
                         Some(fraud_check_update)
                     }
@@ -300,6 +304,7 @@ impl<F: Clone + Send> UpdateTracker<FrmData, F> for FraudCheckPre {
                             metadata: connector_metadata,
                             modified_at: common_utils::date_time::now(),
                             last_step: frm_data.fraud_check.last_step,
+                            payment_capture_method: None,
                         };
                         Some(fraud_check_update)
                     }
diff --git a/crates/router/src/core/fraud_check/types.rs b/crates/router/src/core/fraud_check/types.rs
index e60458646f3..46a341142f7 100644
--- a/crates/router/src/core/fraud_check/types.rs
+++ b/crates/router/src/core/fraud_check/types.rs
@@ -215,4 +215,4 @@ pub struct FrmFulfillmentSignifydApiResponse {
     pub shipment_ids: Vec<String>,
 }
 
-pub const REFUND_INITIATED: &str = "Refund Initiated with the processor";
+pub const CANCEL_INITIATED: &str = "Cancel Initiated with the processor";
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 2eb080f952c..416ec38e577 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -222,7 +222,7 @@ where
         };
         #[cfg(feature = "frm")]
         logger::debug!(
-            "frm_configs: {:?}\nshould_cancel_transaction: {:?}\nshould_continue_capture: {:?}",
+            "frm_configs: {:?}\nshould_continue_transaction: {:?}\nshould_continue_capture: {:?}",
             frm_configs,
             should_continue_transaction,
             should_continue_capture,
@@ -239,7 +239,6 @@ where
                 &key_store,
             )
             .await?;
-
         if should_continue_transaction {
             #[cfg(feature = "frm")]
             match (
@@ -248,8 +247,15 @@ where
             ) {
                 (false, Some(storage_enums::CaptureMethod::Automatic))
                 | (false, Some(storage_enums::CaptureMethod::Scheduled)) => {
+                    if let Some(info) = &mut frm_info {
+                        if let Some(frm_data) = &mut info.frm_data {
+                            frm_data.fraud_check.payment_capture_method =
+                                payment_data.payment_attempt.capture_method;
+                        }
+                    }
                     payment_data.payment_attempt.capture_method =
                         Some(storage_enums::CaptureMethod::Manual);
+                    logger::debug!("payment_id : {:?} capture method has been changed to manual, since it has configured Post FRM flow",payment_data.payment_attempt.payment_id);
                 }
                 _ => (),
             };
@@ -270,7 +276,7 @@ where
                     };
                     let router_data = call_connector_service(
                         state,
-                        req_state,
+                        req_state.clone(),
                         &merchant_account,
                         &key_store,
                         connector.clone(),
@@ -370,7 +376,7 @@ where
                         if config_bool && router_data.should_call_gsm() {
                             router_data = retry::do_gsm_actions(
                                 state,
-                                req_state,
+                                req_state.clone(),
                                 &mut payment_data,
                                 connectors,
                                 connector_data.clone(),
@@ -446,6 +452,7 @@ where
             if let Some(fraud_info) = &mut frm_info {
                 Box::pin(frm_core::post_payment_frm_core(
                     state,
+                    req_state,
                     &merchant_account,
                     &mut payment_data,
                     fraud_info,
@@ -457,6 +464,7 @@ where
                         .attach_printable("Frm configs label not found")?,
                     &customer,
                     key_store.clone(),
+                    &mut should_continue_capture,
                 ))
                 .await?;
             }
diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs
index be73d0836dd..bc82474f7e5 100644
--- a/crates/router/src/core/payments/operations/payment_approve.rs
+++ b/crates/router/src/core/payments/operations/payment_approve.rs
@@ -1,6 +1,6 @@
 use std::marker::PhantomData;
 
-use api_models::enums::FrmSuggestion;
+use api_models::enums::{AttemptStatus, FrmSuggestion, IntentStatus};
 use async_trait::async_trait;
 use error_stack::ResultExt;
 use router_derive::PaymentOperation;
@@ -206,7 +206,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         storage_scheme: storage_enums::MerchantStorageScheme,
         _updated_customer: Option<storage::CustomerUpdate>,
         _merchant_key_store: &domain::MerchantKeyStore,
-        _frm_suggestion: Option<FrmSuggestion>,
+        frm_suggestion: Option<FrmSuggestion>,
         _header_payload: api::HeaderPayload,
     ) -> RouterResult<(
         BoxedOperation<'b, F, api::PaymentsCaptureRequest, Ctx>,
@@ -215,7 +215,12 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
     where
         F: 'b + Send,
     {
+        if matches!(frm_suggestion, Some(FrmSuggestion::FrmAuthorizeTransaction)) {
+            payment_data.payment_intent.status = IntentStatus::RequiresCapture; // In Approve flow, payment which has payment_capture_method "manual" and attempt status as "Unresolved",
+            payment_data.payment_attempt.status = AttemptStatus::Authorized; // We shouldn't call the connector instead we need to update the payment attempt and payment intent.
+        }
         let intent_status_update = storage::PaymentIntentUpdate::ApproveUpdate {
+            status: payment_data.payment_intent.status,
             merchant_decision: Some(api_models::enums::MerchantDecision::Approved.to_string()),
             updated_by: storage_scheme.to_string(),
         };
@@ -228,6 +233,17 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
             )
             .await
             .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
+        db.store
+            .update_payment_attempt_with_attempt_id(
+                payment_data.payment_attempt.clone(),
+                storage::PaymentAttemptUpdate::StatusUpdate {
+                    status: payment_data.payment_attempt.status,
+                    updated_by: storage_scheme.to_string(),
+                },
+                storage_scheme,
+            )
+            .await
+            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
 
         Ok((Box::new(self), payment_data))
     }
diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs
index de0e6604098..4a096620e7f 100644
--- a/crates/router/src/core/payments/operations/payment_confirm.rs
+++ b/crates/router/src/core/payments/operations/payment_confirm.rs
@@ -922,6 +922,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         let payment_method = payment_data.payment_attempt.payment_method;
         let browser_info = payment_data.payment_attempt.browser_info.clone();
         let frm_message = payment_data.frm_message.clone();
+        let capture_method = payment_data.payment_attempt.capture_method;
 
         let default_status_result = (
             storage_enums::IntentStatus::Processing,
@@ -944,7 +945,11 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
                 storage_enums::AttemptStatus::Unresolved,
                 (None, None),
             ),
-            FrmSuggestion::FrmAutoRefund => default_status_result.clone(),
+            FrmSuggestion::FrmAuthorizeTransaction => (
+                storage_enums::IntentStatus::RequiresCapture,
+                storage_enums::AttemptStatus::Authorized,
+                (None, None),
+            ),
         };
 
         let status_handler_for_authentication_results =
@@ -1037,6 +1042,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
         let m_payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
         let m_browser_info = browser_info.clone();
         let m_connector = connector.clone();
+        let m_capture_method = capture_method;
         let m_payment_token = payment_token.clone();
         let m_additional_pm_data = additional_pm_data.clone();
         let m_business_sub_label = business_sub_label.clone();
@@ -1077,6 +1083,7 @@ impl<F: Clone, Ctx: PaymentMethodRetrieve>
                         status: attempt_status,
                         payment_method,
                         authentication_type,
+                        capture_method: m_capture_method,
                         browser_info: m_browser_info,
                         connector: m_connector,
                         payment_token: m_payment_token,
diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs
index 85ef0cfe6b7..2d4b34b30d5 100644
--- a/crates/router/src/utils.rs
+++ b/crates/router/src/utils.rs
@@ -849,6 +849,7 @@ where
         enums::IntentStatus::Succeeded
             | enums::IntentStatus::Failed
             | enums::IntentStatus::PartiallyCaptured
+            | enums::IntentStatus::RequiresMerchantAction
     ) {
         let payments_response = crate::core::payments::transformers::payments_to_payments_response(
             payment_data,
diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs
index 1fd25f3dedb..683b0469a2a 100644
--- a/crates/storage_impl/src/payments/payment_attempt.rs
+++ b/crates/storage_impl/src/payments/payment_attempt.rs
@@ -1445,6 +1445,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 currency,
                 status,
                 authentication_type,
+                capture_method,
                 payment_method,
                 browser_info,
                 connector,
@@ -1472,6 +1473,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 currency,
                 status,
                 authentication_type,
+                capture_method,
                 payment_method,
                 browser_info,
                 connector,
@@ -1744,6 +1746,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 currency,
                 status,
                 authentication_type,
+                capture_method,
                 payment_method,
                 browser_info,
                 connector,
@@ -1771,6 +1774,7 @@ impl DataModelExt for PaymentAttemptUpdate {
                 currency,
                 status,
                 authentication_type,
+                capture_method,
                 payment_method,
                 browser_info,
                 connector,
diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs
index 6b4cf8b82e1..4d984a02cc9 100644
--- a/crates/storage_impl/src/payments/payment_intent.rs
+++ b/crates/storage_impl/src/payments/payment_intent.rs
@@ -1113,9 +1113,11 @@ impl DataModelExt for PaymentIntentUpdate {
                 updated_by,
             },
             Self::ApproveUpdate {
+                status,
                 merchant_decision,
                 updated_by,
             } => DieselPaymentIntentUpdate::ApproveUpdate {
+                status,
                 merchant_decision,
                 updated_by,
             },
diff --git a/migrations/2024-04-24-104042_add_capture_method_in_fraud_check_table/down.sql b/migrations/2024-04-24-104042_add_capture_method_in_fraud_check_table/down.sql
new file mode 100644
index 00000000000..596fb1022fd
--- /dev/null
+++ b/migrations/2024-04-24-104042_add_capture_method_in_fraud_check_table/down.sql
@@ -0,0 +1,2 @@
+ALTER TABLE fraud_check 
+DROP COLUMN IF EXISTS payment_capture_method;
\ No newline at end of file
diff --git a/migrations/2024-04-24-104042_add_capture_method_in_fraud_check_table/up.sql b/migrations/2024-04-24-104042_add_capture_method_in_fraud_check_table/up.sql
new file mode 100644
index 00000000000..08e89a49c11
--- /dev/null
+++ b/migrations/2024-04-24-104042_add_capture_method_in_fraud_check_table/up.sql
@@ -0,0 +1,2 @@
+ALTER TABLE fraud_check 
+ADD COLUMN IF NOT EXISTS payment_capture_method "CaptureMethod" NULL;
\ No newline at end of file

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

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

## Description
<!-- Describe your changes in detail -->
PostAuth flow
- The underlying FRM connector is invoked post authentication of the user from the bank.
- If a transaction’s capture method was set to automatic, it is updated to manual for avoiding automatic capture.
- Once the user completes the authentication, the underlying FRM connector is invoked. The decision whether or not to 
   proceed with the txn is based on the status / score returned.


**Possible actions based on the status**
- Continue on **Accept**
              - Continue with the **transaction**
- Halt on **Decline**
              - Mark the transaction as cancelled
- Approve / Decline on **Manual Review**
              - Hold the txn in manual review state. Merchants can list and review such transactions.
              - If approved, payment is captured
              - If declined, payment is voided

This flow works same as payments flow incase of 3ds, non 3ds, manual and auto capture for **Legit** payments.

### 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`
-->
up.sql
```
ALTER TABLE fraud_check 
ADD COLUMN IF NOT EXISTS payment_capture_method "CaptureMethod" NULL;
```

down.sql 
```
ALTER TABLE fraud_check 
DROP COLUMN IF EXISTS payment_capture_method;
```

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

Create merchant connector account with stripe and then create new merchant connector account with signifyd. 
```
{
  "connector_type": "payment_VAS",
  "connector_name": "signifyd",
  "connector_account_details": {
    "auth_type": "HeaderKey",
    "api_key": "YOUR_API_KEY"
  },
  "test_mode": false,
  "disabled": false,
  "business_country": "US",
  "business_label": "default",
  "payment_methods_enabled": [
    {
      "payment_method": "card",
      "payment_method_types": [
        {
          "payment_method_type": "credit",
          "card_networks": [
            "Visa",
            "Mastercard"
          ],
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        },
        {
          "payment_method_type": "debit",
          "card_networks": [
            "Visa",
            "Mastercard"
          ],
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        }
      ]
    },
    {
      "payment_method": "pay_later",
      "payment_method_types": [
        {
          "payment_method_type": "klarna",
          "payment_experience": "redirect_to_url",
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        },
        {
          "payment_method_type": "affirm",
          "payment_experience": "redirect_to_url",
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        },
        {
          "payment_method_type": "afterpay_clearpay",
          "payment_experience": "redirect_to_url",
          "minimum_amount": 1,
          "maximum_amount": 68607706,
          "recurring_enabled": true,
          "installment_payment_enabled": true
        }
      ]
    }
  ],
  "metadata": {
    "city": "NY",
    "unit": "245"
  },
  "frm_configs": [
        {
            "gateway": "stripe",
            "payment_methods": [
                {
                    "payment_method": "card",
                    "payment_method_types": [
                        {
                            "payment_method_type": "credit",
                            "card_networks": [
                                "Visa"
                            ],
                            "flow": "post",
                            "action": "cancel_txn"
                        },
                        {
                            "payment_method_type": "debit",
                            "card_networks": [
                                "Visa"
                            ],
                            "flow": "post",
                            "action": "manual_review"
                        }
                    ]
                }
            ]
        }
    ]
}
```
**TEST CASE 1 :**  Create legit card payment(amount =150) for both credit and debit.  This should go as normal payment flow. Interchange CaptureMethod and CardType(3ds, non 3ds).
**TEST CASE 2 :**  Create  Credit(pmt type) Payment with amount equals to 15000. This should Fail the payment . 
```
{
  "amount": 15000,
  "currency": "USD",
  "confirm": true,
  "capture_method": "manual",
  "capture_on": "2022-09-10T10:11:12Z",
  "amount_to_capture": 1500,
  "customer_id": "StripeCustomer2",
  "email": "guest@example.com",
  "name": "Bob Smith",
  "phone": "999999999",
  "phone_country_code": "+91",
  "description": "Its my first payment request",
  "authentication_type": "no_three_ds",
  "return_url": "https://google.com",
  "payment_method": "card",
  "payment_method_type": "credit",
  "payment_method_data": {
    "card": {
      "card_number": "4242424242424242",
      "card_exp_month": "10",
      "card_exp_year": "25",
      "card_holder_name": "joseph Doe",
      "card_cvc": "123"
    }
  },
  "billing": {
    "address": {
      "line1": "1467",
      "line2": "Harrison Street",
      "line3": "Harrison Street",
      "city": "San Fransico",
      "state": "California",
      "zip": "94122",
      "country": "US",
      "first_name": "joseph",
      "last_name": "Doe"
    },
    "phone": {
      "number": "8056594427",
      "country_code": "+91"
    }
  },
  "shipping": {
    "address": {
      "line1": "1467",
      "line2": "31 Sherwood Gardens",
      "line3": "31 Sherwood Gardens",
      "city": "London",
      "state": "Manchester",
      "zip": "E14 9wn",
      "country": "GB",
      "first_name": "Bob",
      "last_name": "Smith"
    },
    "phone": {
      "number": "8056594427",
      "country_code": "+91"
    }
  },
  "statement_descriptor_name": "joseph",
  "statement_descriptor_suffix": "JS",
  "metadata": {
    "udf1": "value1",
    "new_customer": "true",
    "login_date": "2019-09-10T10:11:12Z"
  },
  "order_details" : [{
        "product_name" : "gillete creme",
        "quantity" : 2,
        "amount" : 600
    },
    {
        "product_name" : "gillete razor",
        "quantity" : 1,
        "amount" : 300
    }]
}
```

**TESTCASE 3** : Create a payment with payment method type as `debit`.  set capture method to `automatic` and use same json above. Payment status should be moved to `RequiresMerchantAction`.
Response looks like this 
```
{
    "payment_id": "pay_9ZgUMXOiLv35wrF6oOyd",
    "merchant_id": "merchant_1713525778",
    "status": "requires_merchant_action",
    "amount": 150000,
    "net_amount": 150000,
    "amount_capturable": 150000,
    "amount_received": 0,
    "connector": "stripe",
    "client_secret": "pay_9ZgUMXOiLv35wrF6oOyd_secret_wU5hfAMsYjBe0fXepJmj",
    "created": "2024-04-19T11:58:40.749Z",
    "currency": "USD",
    "customer_id": "StripeCustomer2",
    "customer": {
        "id": "StripeCustomer2",
        "name": "Bob Smith",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+91"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "manual",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4242",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "424242",
            "card_extended_bin": "42424242",
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": {
                "cvc_check": "pass",
                "address_line1_check": "pass",
                "address_postal_code_check": "pass"
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "London",
            "country": "GB",
            "line1": "1467",
            "line2": "31 Sherwood Gardens",
            "line3": "31 Sherwood Gardens",
            "zip": "E14 9wn",
            "state": "Manchester",
            "first_name": "Bob",
            "last_name": "Smith"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": [
        {
            "brand": null,
            "amount": 600,
            "category": null,
            "quantity": 2,
            "product_id": null,
            "product_name": "gillete creme",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        },
        {
            "brand": null,
            "amount": 300,
            "category": null,
            "quantity": 1,
            "product_id": null,
            "product_name": "gillete razor",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        }
    ],
    "email": "guest@example.com",
    "name": "Bob Smith",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": "fraud",
    "error_message": "Refund Initiated with the processor",
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "StripeCustomer2",
        "created_at": 1713527920,
        "expires": 1713531520,
        "secret": "epk_22d24f1ef64b44a0b28b72a81c2b2392"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "frm_message": {
        "frm_name": "signifyd",
        "frm_transaction_id": "pay_9ZgUMXOiLv35wrF6oOyd_1",
        "frm_transaction_type": "post_frm",
        "frm_status": "fraud",
        "frm_score": 497,
        "frm_reason": "SIGNIFYD_DECLINED",
        "frm_error": null
    },
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "payment_link": null,
    "profile_id": "pro_DOE6fPp88zY1M4xUyO0H",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_U0XVEDfTM0JMpiv9GOr5",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-19T12:13:40.749Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-04-19T11:58:41.929Z"
}
```
`/approve` and `/reject` should hit after above step for the same payment which will mark payment as succeeded and failed by merchant.

Request curl : 
```
curl --location --request POST 'http://localhost:8080/payments/{{payment_id}}/approve' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key:{{api_key}}' \
--data-raw '{}'
```
Response json : 

```
{
    "payment_id": "pay_9ZgUMXOiLv35wrF6oOyd",
    "merchant_id": "merchant_1713525778",
    "status": "succeeded",
    "amount": 150000,
    "net_amount": 150000,
    "amount_capturable": 0,
    "amount_received": 150000,
    "connector": "stripe",
    "client_secret": "pay_9ZgUMXOiLv35wrF6oOyd_secret_wU5hfAMsYjBe0fXepJmj",
    "created": "2024-04-19T11:58:40.749Z",
    "currency": "USD",
    "customer_id": "StripeCustomer2",
    "customer": {
        "id": "StripeCustomer2",
        "name": "Bob Smith",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+91"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "manual",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4242",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "424242",
            "card_extended_bin": "42424242",
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": {
                "cvc_check": "pass",
                "address_line1_check": "pass",
                "address_postal_code_check": "pass"
            },
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "London",
            "country": "GB",
            "line1": "1467",
            "line2": "31 Sherwood Gardens",
            "line3": "31 Sherwood Gardens",
            "zip": "E14 9wn",
            "state": "Manchester",
            "first_name": "Bob",
            "last_name": "Smith"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": {
            "number": "8056594427",
            "country_code": "+91"
        },
        "email": null
    },
    "order_details": [
        {
            "brand": null,
            "amount": 600,
            "category": null,
            "quantity": 2,
            "product_id": null,
            "product_name": "gillete creme",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        },
        {
            "brand": null,
            "amount": 300,
            "category": null,
            "quantity": 1,
            "product_id": null,
            "product_name": "gillete razor",
            "product_type": null,
            "product_img_link": null,
            "requires_shipping": null
        }
    ],
    "email": "guest@example.com",
    "name": "Bob Smith",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": false,
    "connector_transaction_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "frm_message": {
        "frm_name": "signifyd",
        "frm_transaction_id": "pay_9ZgUMXOiLv35wrF6oOyd_1",
        "frm_transaction_type": "post_frm",
        "frm_status": "fraud",
        "frm_score": 497,
        "frm_reason": "SIGNIFYD_DECLINED",
        "frm_error": null
    },
    "metadata": {
        "udf1": "value1",
        "login_date": "2019-09-10T10:11:12Z",
        "new_customer": "true"
    },
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pi_3P7G5VD5R7gDAGff0XEZqQkO",
    "payment_link": null,
    "profile_id": "pro_DOE6fPp88zY1M4xUyO0H",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": "approved",
    "merchant_connector_id": "mca_U0XVEDfTM0JMpiv9GOr5",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-19T12:13:40.749Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-04-19T12:01:50.944Z"
}
```

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