Bug: [REFACTOR]: Create a domain function for saving payment method and updating pm_id in attempt



<img width="1404" alt="Screenshot 2024-03-21 at 22 08 28" src="https://github.com/juspay/hyperswitch/assets/61520228/c6cdfd23-096a-402b-8449-b7afdd2614de">

diff --git a/crates/data_models/src/payments/payment_attempt.rs b/crates/data_models/src/payments/payment_attempt.rs
index ec0e0873168..0c165632047 100644
--- a/crates/data_models/src/payments/payment_attempt.rs
+++ b/crates/data_models/src/payments/payment_attempt.rs
@@ -338,6 +338,10 @@ pub enum PaymentAttemptUpdate {
         error_message: Option<Option<String>>,
         updated_by: String,
     },
+    PaymentMethodDetailsUpdate {
+        payment_method_id: Option<String>,
+        updated_by: String,
+    },
     VoidUpdate {
         status: storage_enums::AttemptStatus,
         cancellation_reason: Option<String>,
diff --git a/crates/diesel_models/src/payment_attempt.rs b/crates/diesel_models/src/payment_attempt.rs
index 6ad21eab12b..603e0f4ebf2 100644
--- a/crates/diesel_models/src/payment_attempt.rs
+++ b/crates/diesel_models/src/payment_attempt.rs
@@ -237,6 +237,10 @@ pub enum PaymentAttemptUpdate {
         cancellation_reason: Option<String>,
         updated_by: String,
     },
+    PaymentMethodDetailsUpdate {
+        payment_method_id: Option<String>,
+        updated_by: String,
+    },
     BlocklistUpdate {
         status: storage_enums::AttemptStatus,
         error_code: Option<Option<String>>,
@@ -644,6 +648,14 @@ impl From<PaymentAttemptUpdate> for PaymentAttemptUpdateInternal {
                 merchant_connector_id: Some(None),
                 ..Default::default()
             },
+            PaymentAttemptUpdate::PaymentMethodDetailsUpdate {
+                payment_method_id,
+                updated_by,
+            } => Self {
+                payment_method_id,
+                updated_by,
+                ..Default::default()
+            },
             PaymentAttemptUpdate::ResponseUpdate {
                 status,
                 connector,
diff --git a/crates/router/src/core/mandate.rs b/crates/router/src/core/mandate.rs
index b54091e4923..84aa9b48e6f 100644
--- a/crates/router/src/core/mandate.rs
+++ b/crates/router/src/core/mandate.rs
@@ -340,156 +340,134 @@ where
         .change_context(errors::ApiErrorResponse::MandateUpdateFailed)?;
     Ok(resp)
 }
+
 pub async fn mandate_procedure<F, FData>(
     state: &AppState,
-    mut resp: types::RouterData<F, FData, types::PaymentsResponseData>,
-    maybe_customer: &Option<domain::Customer>,
+    resp: &types::RouterData<F, FData, types::PaymentsResponseData>,
+    customer_id: &Option<String>,
     pm_id: Option<String>,
     merchant_connector_id: Option<String>,
     storage_scheme: MerchantStorageScheme,
-) -> errors::RouterResult<types::RouterData<F, FData, types::PaymentsResponseData>>
+) -> errors::RouterResult<Option<String>>
 where
     FData: MandateBehaviour,
 {
-    match resp.response {
-        Err(_) => {}
-        Ok(_) => match resp.request.get_mandate_id() {
-            Some(mandate_id) => {
-                if let Some(ref mandate_id) = mandate_id.mandate_id {
-                    let orig_mandate = state
-                        .store
-                        .find_mandate_by_merchant_id_mandate_id(
-                            resp.merchant_id.as_ref(),
-                            mandate_id,
-                            storage_scheme,
-                        )
-                        .await
-                        .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
-                    let mandate = match orig_mandate.mandate_type {
-                        storage_enums::MandateType::SingleUse => state
-                            .store
-                            .update_mandate_by_merchant_id_mandate_id(
-                                &resp.merchant_id,
-                                mandate_id,
-                                storage::MandateUpdate::StatusUpdate {
-                                    mandate_status: storage_enums::MandateStatus::Revoked,
-                                },
-                                orig_mandate,
-                                storage_scheme,
-                            )
-                            .await
-                            .change_context(errors::ApiErrorResponse::MandateUpdateFailed),
-                        storage_enums::MandateType::MultiUse => state
-                            .store
-                            .update_mandate_by_merchant_id_mandate_id(
-                                &resp.merchant_id,
-                                mandate_id,
-                                storage::MandateUpdate::CaptureAmountUpdate {
-                                    amount_captured: Some(
-                                        orig_mandate.amount_captured.unwrap_or(0)
-                                            + resp.request.get_amount(),
-                                    ),
-                                },
-                                orig_mandate,
-                                storage_scheme,
-                            )
-                            .await
-                            .change_context(errors::ApiErrorResponse::MandateUpdateFailed),
-                    }?;
-                    metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
-                        &metrics::CONTEXT,
-                        1,
-                        &[metrics::request::add_attributes(
-                            "connector",
-                            mandate.connector,
-                        )],
-                    );
-                    resp.payment_method_id = Some(mandate.payment_method_id);
-                }
-            }
-            None => {
-                if resp.request.get_setup_mandate_details().is_some() {
-                    resp.payment_method_id = pm_id.clone();
-                    let (mandate_reference, network_txn_id) = match resp.response.as_ref().ok() {
-                        Some(types::PaymentsResponseData::TransactionResponse {
-                            mandate_reference,
-                            network_txn_id,
-                            ..
-                        }) => (mandate_reference.clone(), network_txn_id.clone()),
-                        _ => (None, None),
-                    };
-
-                    let mandate_ids = mandate_reference
-                        .as_ref()
-                        .map(|md| {
-                            md.encode_to_value()
-                                .change_context(
-                                    errors::ApiErrorResponse::MandateSerializationFailed,
-                                )
-                                .map(masking::Secret::new)
-                        })
-                        .transpose()?;
-
-                    if let Some(new_mandate_data) = payment_helper::generate_mandate(
-                        resp.merchant_id.clone(),
-                        resp.payment_id.clone(),
-                        resp.connector.clone(),
-                        resp.request.get_setup_mandate_details().cloned(),
-                        maybe_customer,
-                        pm_id.get_required_value("payment_method_id")?,
-                        mandate_ids,
-                        network_txn_id,
-                        get_insensitive_payment_method_data_if_exists(&resp),
-                        mandate_reference,
-                        merchant_connector_id,
-                    )? {
-                        let connector = new_mandate_data.connector.clone();
-                        logger::debug!("{:?}", new_mandate_data);
-                        resp.request
-                        .set_mandate_id(Some(api_models::payments::MandateIds {
-                            mandate_id: Some(new_mandate_data.mandate_id.clone()),
-                            mandate_reference_id: new_mandate_data
-                                .connector_mandate_ids
-                                .clone()
-                            .map(|ids| {
-                                Some(ids)
-                                    .parse_value::<api_models::payments::ConnectorMandateReferenceId>(
-                                        "ConnectorMandateId",
-                                    )
-                                    .change_context(errors::ApiErrorResponse::MandateDeserializationFailed)
-                            })
-                            .transpose()?
-                            .map_or(
-                                new_mandate_data.network_transaction_id.clone().map(|id| {
-                                    api_models::payments::MandateReferenceId::NetworkMandateId(
-                                        id,
-                                    )
-                                }),
-                                |connector_id| Some(api_models::payments::MandateReferenceId::ConnectorMandateId(
-                                    api_models::payments::ConnectorMandateReferenceId {
-                                        connector_mandate_id: connector_id.connector_mandate_id,
-                                        payment_method_id: connector_id.payment_method_id,
-                                        update_history:None,
-
-                                    }
-                                )))
-                        }));
-                        state
-                            .store
-                            .insert_mandate(new_mandate_data, storage_scheme)
-                            .await
-                            .to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?;
-                        metrics::MANDATE_COUNT.add(
-                            &metrics::CONTEXT,
-                            1,
-                            &[metrics::request::add_attributes("connector", connector)],
-                        );
-                    };
-                }
-            }
-        },
+    let Ok(ref response) = resp.response else {
+        return Ok(None);
+    };
+
+    match resp.request.get_mandate_id() {
+        Some(mandate_id) => {
+            let Some(ref mandate_id) = mandate_id.mandate_id else {
+                return Ok(None);
+            };
+            let orig_mandate = state
+                .store
+                .find_mandate_by_merchant_id_mandate_id(
+                    resp.merchant_id.as_ref(),
+                    mandate_id,
+                    storage_scheme,
+                )
+                .await
+                .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
+            let mandate = match orig_mandate.mandate_type {
+                storage_enums::MandateType::SingleUse => state
+                    .store
+                    .update_mandate_by_merchant_id_mandate_id(
+                        &resp.merchant_id,
+                        mandate_id,
+                        storage::MandateUpdate::StatusUpdate {
+                            mandate_status: storage_enums::MandateStatus::Revoked,
+                        },
+                        orig_mandate,
+                        storage_scheme,
+                    )
+                    .await
+                    .change_context(errors::ApiErrorResponse::MandateUpdateFailed),
+                storage_enums::MandateType::MultiUse => state
+                    .store
+                    .update_mandate_by_merchant_id_mandate_id(
+                        &resp.merchant_id,
+                        mandate_id,
+                        storage::MandateUpdate::CaptureAmountUpdate {
+                            amount_captured: Some(
+                                orig_mandate.amount_captured.unwrap_or(0)
+                                    + resp.request.get_amount(),
+                            ),
+                        },
+                        orig_mandate,
+                        storage_scheme,
+                    )
+                    .await
+                    .change_context(errors::ApiErrorResponse::MandateUpdateFailed),
+            }?;
+            metrics::SUBSEQUENT_MANDATE_PAYMENT.add(
+                &metrics::CONTEXT,
+                1,
+                &[metrics::request::add_attributes(
+                    "connector",
+                    mandate.connector,
+                )],
+            );
+            Ok(Some(mandate_id.clone()))
+        }
+        None => {
+            let Some(_mandate_details) = resp.request.get_setup_mandate_details() else {
+                return Ok(None);
+            };
+            let (mandate_reference, network_txn_id) = match &response {
+                types::PaymentsResponseData::TransactionResponse {
+                    mandate_reference,
+                    network_txn_id,
+                    ..
+                } => (mandate_reference.clone(), network_txn_id.clone()),
+                _ => (None, None),
+            };
+
+            let mandate_ids = mandate_reference
+                .as_ref()
+                .map(|md| {
+                    md.encode_to_value()
+                        .change_context(errors::ApiErrorResponse::MandateSerializationFailed)
+                        .map(masking::Secret::new)
+                })
+                .transpose()?;
+
+            let Some(new_mandate_data) = payment_helper::generate_mandate(
+                resp.merchant_id.clone(),
+                resp.payment_id.clone(),
+                resp.connector.clone(),
+                resp.request.get_setup_mandate_details().cloned(),
+                customer_id,
+                pm_id.get_required_value("payment_method_id")?,
+                mandate_ids,
+                network_txn_id,
+                get_insensitive_payment_method_data_if_exists(resp),
+                mandate_reference,
+                merchant_connector_id,
+            )?
+            else {
+                return Ok(None);
+            };
+
+            let connector = new_mandate_data.connector.clone();
+            logger::debug!("{:?}", new_mandate_data);
+
+            let res_mandate_id = new_mandate_data.mandate_id.clone();
+
+            state
+                .store
+                .insert_mandate(new_mandate_data, storage_scheme)
+                .await
+                .to_duplicate_response(errors::ApiErrorResponse::DuplicateMandate)?;
+            metrics::MANDATE_COUNT.add(
+                &metrics::CONTEXT,
+                1,
+                &[metrics::request::add_attributes("connector", connector)],
+            );
+            Ok(Some(res_mandate_id))
+        }
     }
-    Ok(resp)
 }
 
 #[instrument(skip(state))]
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index b9a8795aa54..33002dd2cbd 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -120,7 +120,7 @@ where
 
     // To perform router related operation for PaymentResponse
     PaymentResponse: Operation<F, FData, Ctx>,
-    FData: Send + Sync,
+    FData: Send + Sync + Clone,
     Ctx: PaymentMethodRetrieve,
 {
     let operation: BoxedOperation<'_, F, Req, Ctx> = Box::new(operation);
@@ -274,11 +274,11 @@ where
                         req_state,
                         &merchant_account,
                         &key_store,
-                        connector,
+                        connector.clone(),
                         &operation,
                         &mut payment_data,
                         &customer,
-                        call_connector_action,
+                        call_connector_action.clone(),
                         &validate_result,
                         schedule_time,
                         header_payload,
@@ -295,6 +295,18 @@ where
                     external_latency = router_data.external_latency;
                     //add connector http status code metrics
                     add_connector_http_status_code_metrics(connector_http_status_code);
+
+                    operation
+                        .to_post_update_tracker()?
+                        .save_pm_and_mandate(
+                            state,
+                            &router_data,
+                            &merchant_account,
+                            &key_store,
+                            &mut payment_data,
+                        )
+                        .await?;
+
                     operation
                         .to_post_update_tracker()?
                         .update_tracker(
@@ -334,7 +346,7 @@ where
                         &operation,
                         &mut payment_data,
                         &customer,
-                        call_connector_action,
+                        call_connector_action.clone(),
                         &validate_result,
                         schedule_time,
                         header_payload,
@@ -362,7 +374,7 @@ where
                                 req_state,
                                 &mut payment_data,
                                 connectors,
-                                connector_data,
+                                connector_data.clone(),
                                 router_data,
                                 &merchant_account,
                                 &key_store,
@@ -384,6 +396,18 @@ where
                     external_latency = router_data.external_latency;
                     //add connector http status code metrics
                     add_connector_http_status_code_metrics(connector_http_status_code);
+
+                    operation
+                        .to_post_update_tracker()?
+                        .save_pm_and_mandate(
+                            state,
+                            &router_data,
+                            &merchant_account,
+                            &key_store,
+                            &mut payment_data,
+                        )
+                        .await?;
+
                     operation
                         .to_post_update_tracker()?
                         .update_tracker(
@@ -698,7 +722,7 @@ pub async fn payments_core<F, Res, Req, Op, FData, Ctx>(
 ) -> RouterResponse<Res>
 where
     F: Send + Clone + Sync,
-    FData: Send + Sync,
+    FData: Send + Sync + Clone,
     Op: Operation<F, Req, Ctx> + Send + Sync + Clone,
     Req: Debug + Authenticate + Clone,
     Res: transformers::ToResponse<PaymentData<F>, Op>,
@@ -1367,16 +1391,7 @@ where
         // and rely on previous status set in router_data
         router_data.status = payment_data.payment_attempt.status;
         router_data
-            .decide_flows(
-                state,
-                &connector,
-                customer,
-                call_connector_action,
-                merchant_account,
-                connector_request,
-                key_store,
-                payment_data.payment_intent.profile_id.clone(),
-            )
+            .decide_flows(state, &connector, call_connector_action, connector_request)
             .await
     } else {
         Ok(router_data)
@@ -1499,12 +1514,8 @@ where
         let res = router_data.decide_flows(
             state,
             &session_connector_data.connector,
-            customer,
             CallConnectorAction::Trigger,
-            merchant_account,
             None,
-            key_store,
-            payment_data.payment_intent.profile_id.clone(),
         );
 
         join_handlers.push(res);
diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs
index 03cba29d025..59c0b687d64 100644
--- a/crates/router/src/core/payments/flows.rs
+++ b/crates/router/src/core/payments/flows.rs
@@ -44,12 +44,8 @@ pub trait Feature<F, T> {
         self,
         state: &AppState,
         connector: &api::ConnectorData,
-        maybe_customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        key_store: &domain::MerchantKeyStore,
-        profile_id: Option<String>,
     ) -> RouterResult<Self>
     where
         Self: Sized,
diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs
index 43e36847eb9..ffa11fdb0f5 100644
--- a/crates/router/src/core/payments/flows/approve_flow.rs
+++ b/crates/router/src/core/payments/flows/approve_flow.rs
@@ -49,12 +49,8 @@ impl Feature<api::Approve, types::PaymentsApproveData>
         self,
         _state: &AppState,
         _connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         _call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         _connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         Err(ApiErrorResponse::NotImplemented {
             message: NotImplementedMessage::Reason("Flow not supported".to_string()),
diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs
index 10a4206a2b9..e44456fb1c7 100644
--- a/crates/router/src/core/payments/flows/authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/authorize_flow.rs
@@ -61,12 +61,8 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
         mut self,
         state: &AppState,
         connector: &api::ConnectorData,
-        maybe_customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        key_store: &domain::MerchantKeyStore,
-        profile_id: Option<String>,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
@@ -78,7 +74,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
         if self.should_proceed_with_authorize() {
             self.decide_authentication_type();
             logger::debug!(auth_type=?self.auth_type);
-            let mut resp = services::execute_connector_processing_step(
+            let resp = services::execute_connector_processing_step(
                 state,
                 connector_integration,
                 &self,
@@ -89,98 +85,7 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
             .to_payment_failed_response()?;
 
             metrics::PAYMENT_COUNT.add(&metrics::CONTEXT, 1, &[]); // Metrics
-
-            let is_mandate = resp.request.setup_mandate_details.is_some();
-
-            if is_mandate {
-                let (payment_method_id, payment_method_status) =
-                    Box::pin(tokenization::save_payment_method(
-                        state,
-                        connector,
-                        resp.to_owned(),
-                        maybe_customer,
-                        merchant_account,
-                        self.request.payment_method_type,
-                        key_store,
-                        Some(resp.request.amount),
-                        Some(resp.request.currency),
-                        profile_id,
-                    ))
-                    .await?;
-
-                resp.payment_method_id = payment_method_id.clone();
-                resp.payment_method_status = payment_method_status;
-
-                Ok(mandate::mandate_procedure(
-                    state,
-                    resp,
-                    maybe_customer,
-                    payment_method_id,
-                    connector.merchant_connector_id.clone(),
-                    merchant_account.storage_scheme,
-                )
-                .await?)
-            } else {
-                let response = resp.clone();
-
-                logger::info!("Call to save_payment_method in locker");
-
-                let pm = Box::pin(tokenization::save_payment_method(
-                    state,
-                    connector,
-                    response,
-                    maybe_customer,
-                    merchant_account,
-                    self.request.payment_method_type,
-                    key_store,
-                    Some(resp.request.amount),
-                    Some(resp.request.currency),
-                    profile_id,
-                ))
-                .await;
-
-                match pm {
-                    Ok((payment_method_id, payment_method_status)) => {
-                        resp.payment_method_id = payment_method_id.clone();
-                        resp.payment_method_status = payment_method_status;
-                    }
-                    Err(err) => logger::error!("Save pm to locker failed : {err:?}"),
-                }
-
-                Ok(resp)
-            }
-
-            // Async locker code (Commenting out the code for near future refactors)
-            //     logger::info!("Call to save_payment_method in locker");
-            //     let _task_handle = tokio::spawn(
-            //         async move {
-            //             logger::info!("Starting async call to save_payment_method in locker");
-            //
-            //             let result = Box::pin(tokenization::save_payment_method(
-            //                 &state,
-            //                 &connector,
-            //                 response,
-            //                 &maybe_customer,
-            //                 &merchant_account,
-            //                 self.request.payment_method_type,
-            //                 &key_store,
-            //                 Some(resp.request.amount),
-            //                 Some(resp.request.currency),
-            //             ))
-            //             .await;
-            //
-            //             if let Err(err) = result {
-            //                 logger::error!(
-            //                     "Asynchronously saving card in locker failed : {:?}",
-            //                     err
-            //                 );
-            //             }
-            //         }
-            //         .in_current_span(),
-            //     );
-            //
-            //     Ok(resp)
-            // }
+            Ok(resp)
         } else {
             Ok(self.clone())
         }
diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs
index 5814b1cafb1..5f802a0bbe8 100644
--- a/crates/router/src/core/payments/flows/cancel_flow.rs
+++ b/crates/router/src/core/payments/flows/cancel_flow.rs
@@ -48,12 +48,8 @@ impl Feature<api::Void, types::PaymentsCancelData>
         self,
         state: &AppState,
         connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         metrics::PAYMENT_CANCEL_COUNT.add(
             &metrics::CONTEXT,
diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs
index 5f64014bce7..56ae6500c35 100644
--- a/crates/router/src/core/payments/flows/capture_flow.rs
+++ b/crates/router/src/core/payments/flows/capture_flow.rs
@@ -49,12 +49,8 @@ impl Feature<api::Capture, types::PaymentsCaptureData>
         self,
         state: &AppState,
         connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs
index e64240387d2..baf4190395f 100644
--- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs
+++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs
@@ -63,12 +63,8 @@ impl Feature<api::CompleteAuthorize, types::CompleteAuthorizeData>
         mut self,
         state: &AppState,
         connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs
index 99f8e4831bb..e702483022c 100644
--- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs
+++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs
@@ -56,12 +56,8 @@ impl Feature<api::IncrementalAuthorization, types::PaymentsIncrementalAuthorizat
         self,
         state: &AppState,
         connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs
index 6463e87279b..f410b65f076 100644
--- a/crates/router/src/core/payments/flows/psync_flow.rs
+++ b/crates/router/src/core/payments/flows/psync_flow.rs
@@ -52,12 +52,8 @@ impl Feature<api::PSync, types::PaymentsSyncData>
         mut self,
         state: &AppState,
         connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         let connector_integration: services::BoxedConnectorIntegration<
             '_,
diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs
index 4157edf8d0a..89c1585fca1 100644
--- a/crates/router/src/core/payments/flows/reject_flow.rs
+++ b/crates/router/src/core/payments/flows/reject_flow.rs
@@ -48,12 +48,8 @@ impl Feature<api::Reject, types::PaymentsRejectData>
         self,
         _state: &AppState,
         _connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         _call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         _connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         Err(ApiErrorResponse::NotImplemented {
             message: NotImplementedMessage::Reason("Flow not supported".to_string()),
diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs
index 7e15e2ed3f9..3d21aa732de 100644
--- a/crates/router/src/core/payments/flows/session_flow.rs
+++ b/crates/router/src/core/payments/flows/session_flow.rs
@@ -53,12 +53,8 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
         self,
         state: &routes::AppState,
         connector: &api::ConnectorData,
-        customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        _merchant_account: &domain::MerchantAccount,
         _connector_request: Option<services::Request>,
-        _key_store: &domain::MerchantKeyStore,
-        _profile_id: Option<String>,
     ) -> RouterResult<Self> {
         metrics::SESSION_TOKEN_CREATED.add(
             &metrics::CONTEXT,
@@ -68,14 +64,8 @@ impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessio
                 connector.connector_name.to_string(),
             )],
         );
-        self.decide_flow(
-            state,
-            connector,
-            customer,
-            Some(true),
-            call_connector_action,
-        )
-        .await
+        self.decide_flow(state, connector, Some(true), call_connector_action)
+            .await
     }
 
     async fn add_access_token<'a>(
@@ -521,7 +511,6 @@ impl types::PaymentsSessionRouterData {
         &'b self,
         state: &'a routes::AppState,
         connector: &api::ConnectorData,
-        _customer: &Option<domain::Customer>,
         _confirm: Option<bool>,
         call_connector_action: payments::CallConnectorAction,
     ) -> RouterResult<Self> {
diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs
index ff77efa7c46..6a810e9b3dd 100644
--- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs
+++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs
@@ -1,14 +1,10 @@
-use api_models::enums::{PaymentMethod, PaymentMethodType};
 use async_trait::async_trait;
-use error_stack::ResultExt;
 
 use super::{ConstructFlowSpecificData, Feature};
 use crate::{
-    configs::settings,
     core::{
-        errors::{self, ConnectorErrorExt, RouterResult, StorageErrorExt},
+        errors::{self, ConnectorErrorExt, RouterResult},
         mandate,
-        payment_methods::cards,
         payments::{
             self, access_token, customers, helpers, tokenization, transformers, PaymentData,
         },
@@ -57,76 +53,26 @@ impl Feature<api::SetupMandate, types::SetupMandateRequestData> for types::Setup
         self,
         state: &AppState,
         connector: &api::ConnectorData,
-        maybe_customer: &Option<domain::Customer>,
         call_connector_action: payments::CallConnectorAction,
-        merchant_account: &domain::MerchantAccount,
         connector_request: Option<services::Request>,
-        key_store: &domain::MerchantKeyStore,
-        profile_id: Option<String>,
     ) -> RouterResult<Self> {
-        if let Some(mandate_id) = self
-            .request
-            .setup_mandate_details
-            .as_ref()
-            .and_then(|mandate_data| mandate_data.update_mandate_id.clone())
-        {
-            Box::pin(self.update_mandate_flow(
-                state,
-                merchant_account,
-                mandate_id,
-                connector,
-                key_store,
-                call_connector_action,
-                &state.conf.mandates.update_mandate_supported,
-                connector_request,
-                maybe_customer,
-                profile_id,
-            ))
-            .await
-        } else {
-            let connector_integration: services::BoxedConnectorIntegration<
-                '_,
-                api::SetupMandate,
-                types::SetupMandateRequestData,
-                types::PaymentsResponseData,
-            > = connector.connector.get_connector_integration();
-
-            let mut resp = services::execute_connector_processing_step(
-                state,
-                connector_integration,
-                &self,
-                call_connector_action.clone(),
-                connector_request,
-            )
-            .await
-            .to_setup_mandate_failed_response()?;
-
-            let (pm_id, payment_method_status) = Box::pin(tokenization::save_payment_method(
-                state,
-                connector,
-                resp.to_owned(),
-                maybe_customer,
-                merchant_account,
-                self.request.payment_method_type,
-                key_store,
-                resp.request.amount,
-                Some(resp.request.currency),
-                profile_id,
-            ))
-            .await?;
+        let connector_integration: services::BoxedConnectorIntegration<
+            '_,
+            api::SetupMandate,
+            types::SetupMandateRequestData,
+            types::PaymentsResponseData,
+        > = connector.connector.get_connector_integration();
 
-            resp.payment_method_id = pm_id.clone();
-            resp.payment_method_status = payment_method_status;
-            mandate::mandate_procedure(
-                state,
-                resp,
-                maybe_customer,
-                pm_id,
-                connector.merchant_connector_id.clone(),
-                merchant_account.storage_scheme,
-            )
-            .await
-        }
+        let resp = services::execute_connector_processing_step(
+            state,
+            connector_integration,
+            &self,
+            call_connector_action.clone(),
+            connector_request,
+        )
+        .await
+        .to_setup_mandate_failed_response()?;
+        Ok(resp)
     }
 
     async fn add_access_token<'a>(
@@ -210,222 +156,6 @@ impl TryFrom<types::SetupMandateRequestData> for types::ConnectorCustomerData {
     }
 }
 
-#[allow(clippy::too_many_arguments)]
-impl types::SetupMandateRouterData {
-    pub async fn decide_flow<'a, 'b>(
-        &'b self,
-        state: &'a AppState,
-        connector: &api::ConnectorData,
-        maybe_customer: &Option<domain::Customer>,
-        confirm: Option<bool>,
-        call_connector_action: payments::CallConnectorAction,
-        merchant_account: &domain::MerchantAccount,
-        key_store: &domain::MerchantKeyStore,
-        profile_id: Option<String>,
-    ) -> RouterResult<Self> {
-        match confirm {
-            Some(true) => {
-                let connector_integration: services::BoxedConnectorIntegration<
-                    '_,
-                    api::SetupMandate,
-                    types::SetupMandateRequestData,
-                    types::PaymentsResponseData,
-                > = connector.connector.get_connector_integration();
-                let mut resp = services::execute_connector_processing_step(
-                    state,
-                    connector_integration,
-                    self,
-                    call_connector_action,
-                    None,
-                )
-                .await
-                .to_setup_mandate_failed_response()?;
-
-                let payment_method_type = self.request.payment_method_type;
-
-                let (pm_id, payment_method_status) = Box::pin(tokenization::save_payment_method(
-                    state,
-                    connector,
-                    resp.to_owned(),
-                    maybe_customer,
-                    merchant_account,
-                    payment_method_type,
-                    key_store,
-                    resp.request.amount,
-                    Some(resp.request.currency),
-                    profile_id,
-                ))
-                .await?;
-
-                resp.payment_method_id = pm_id.clone();
-                resp.payment_method_status = payment_method_status;
-
-                Ok(mandate::mandate_procedure(
-                    state,
-                    resp,
-                    maybe_customer,
-                    pm_id,
-                    connector.merchant_connector_id.clone(),
-                    merchant_account.storage_scheme,
-                )
-                .await?)
-            }
-            _ => Ok(self.clone()),
-        }
-    }
-
-    async fn update_mandate_flow(
-        self,
-        state: &AppState,
-        merchant_account: &domain::MerchantAccount,
-        mandate_id: String,
-        connector: &api::ConnectorData,
-        key_store: &domain::MerchantKeyStore,
-        call_connector_action: payments::CallConnectorAction,
-        supported_connectors_for_update_mandate: &settings::SupportedPaymentMethodsForMandate,
-        connector_request: Option<services::Request>,
-        maybe_customer: &Option<domain::Customer>,
-        profile_id: Option<String>,
-    ) -> RouterResult<Self> {
-        let payment_method_type = self.request.payment_method_type;
-
-        let payment_method = self.request.payment_method_data.get_payment_method();
-        let supported_connectors_config = payment_method.zip(payment_method_type).map_or_else(
-            || {
-                if payment_method == Some(PaymentMethod::Card) {
-                    cards::filter_pm_based_on_update_mandate_support_for_connector(
-                        supported_connectors_for_update_mandate,
-                        &PaymentMethod::Card,
-                        &PaymentMethodType::Credit,
-                        connector.connector_name,
-                    ) && cards::filter_pm_based_on_update_mandate_support_for_connector(
-                        supported_connectors_for_update_mandate,
-                        &PaymentMethod::Card,
-                        &PaymentMethodType::Debit,
-                        connector.connector_name,
-                    )
-                } else {
-                    false
-                }
-            },
-            |(pm, pmt)| {
-                cards::filter_pm_based_on_update_mandate_support_for_connector(
-                    supported_connectors_for_update_mandate,
-                    &pm,
-                    &pmt,
-                    connector.connector_name,
-                )
-            },
-        );
-        if supported_connectors_config {
-            let connector_integration: services::BoxedConnectorIntegration<
-                '_,
-                api::SetupMandate,
-                types::SetupMandateRequestData,
-                types::PaymentsResponseData,
-            > = connector.connector.get_connector_integration();
-
-            let resp = services::execute_connector_processing_step(
-                state,
-                connector_integration,
-                &self,
-                call_connector_action.clone(),
-                connector_request,
-            )
-            .await
-            .to_setup_mandate_failed_response()?;
-            let pm_id = Box::pin(tokenization::save_payment_method(
-                state,
-                connector,
-                resp.to_owned(),
-                maybe_customer,
-                merchant_account,
-                self.request.payment_method_type,
-                key_store,
-                resp.request.amount,
-                Some(resp.request.currency),
-                profile_id,
-            ))
-            .await?
-            .0;
-            let mandate = state
-                .store
-                .find_mandate_by_merchant_id_mandate_id(
-                    &merchant_account.merchant_id,
-                    &mandate_id,
-                    merchant_account.storage_scheme,
-                )
-                .await
-                .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?;
-
-            let profile_id = mandate::helpers::get_profile_id_for_mandate(
-                state,
-                merchant_account,
-                mandate.clone(),
-            )
-            .await?;
-            match resp.response {
-                Ok(types::PaymentsResponseData::TransactionResponse { .. }) => {
-                    let connector_integration: services::BoxedConnectorIntegration<
-                        '_,
-                        types::api::MandateRevoke,
-                        types::MandateRevokeRequestData,
-                        types::MandateRevokeResponseData,
-                    > = connector.connector.get_connector_integration();
-                    let merchant_connector_account = helpers::get_merchant_connector_account(
-                        state,
-                        &merchant_account.merchant_id,
-                        None,
-                        key_store,
-                        &profile_id,
-                        &mandate.connector,
-                        mandate.merchant_connector_id.as_ref(),
-                    )
-                    .await?;
-
-                    let router_data = mandate::utils::construct_mandate_revoke_router_data(
-                        merchant_connector_account,
-                        merchant_account,
-                        mandate.clone(),
-                    )
-                    .await?;
-
-                    let _response = services::execute_connector_processing_step(
-                        state,
-                        connector_integration,
-                        &router_data,
-                        call_connector_action,
-                        None,
-                    )
-                    .await
-                    .change_context(errors::ApiErrorResponse::InternalServerError)?;
-                    // TODO:Add the revoke mandate task to process tracker
-                    mandate::update_mandate_procedure(
-                        state,
-                        resp,
-                        mandate,
-                        &merchant_account.merchant_id,
-                        pm_id,
-                        merchant_account.storage_scheme,
-                    )
-                    .await
-                }
-                Ok(_) => Err(errors::ApiErrorResponse::InternalServerError)
-                    .attach_printable("Unexpected response received")?,
-                Err(_) => Ok(resp),
-            }
-        } else {
-            Err(errors::ApiErrorResponse::PreconditionFailed {
-                message: format!(
-                    "Update Mandate flow not implemented for the connector {:?}",
-                    connector.connector_name
-                ),
-            }
-            .into())
-        }
-    }
-}
-
 impl mandate::MandateBehaviour for types::SetupMandateRequestData {
     fn get_amount(&self) -> i64 {
         0
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index de2fdd99b7b..c8763584ccc 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -1247,7 +1247,7 @@ pub(crate) async fn get_payment_method_create_request(
     payment_method_data: Option<&domain::PaymentMethodData>,
     payment_method: Option<storage_enums::PaymentMethod>,
     payment_method_type: Option<storage_enums::PaymentMethodType>,
-    customer: &domain::Customer,
+    customer_id: &Option<String>,
     billing_name: Option<masking::Secret<String>>,
 ) -> RouterResult<api::PaymentMethodCreate> {
     match payment_method_data {
@@ -1265,7 +1265,6 @@ pub(crate) async fn get_payment_method_create_request(
                         card_issuer: card.card_issuer.clone(),
                         card_type: card.card_type.clone(),
                     };
-                    let customer_id = customer.customer_id.clone();
                     let payment_method_request = api::PaymentMethodCreate {
                         payment_method: Some(payment_method),
                         payment_method_type,
@@ -1277,7 +1276,7 @@ pub(crate) async fn get_payment_method_create_request(
                         wallet: None,
                         card: Some(card_detail),
                         metadata: None,
-                        customer_id: Some(customer_id),
+                        customer_id: customer_id.clone(),
                         card_network: card
                             .card_network
                             .as_ref()
@@ -1299,7 +1298,7 @@ pub(crate) async fn get_payment_method_create_request(
                         wallet: None,
                         card: None,
                         metadata: None,
-                        customer_id: Some(customer.customer_id.to_owned()),
+                        customer_id: customer_id.clone(),
                         card_network: None,
                         client_secret: None,
                         payment_method_data: None,
@@ -2618,7 +2617,7 @@ pub fn generate_mandate(
     payment_id: String,
     connector: String,
     setup_mandate_details: Option<MandateData>,
-    customer: &Option<domain::Customer>,
+    customer_id: &Option<String>,
     payment_method_id: String,
     connector_mandate_id: Option<pii::SecretSerdeValue>,
     network_txn_id: Option<String>,
@@ -2626,8 +2625,8 @@ pub fn generate_mandate(
     mandate_reference: Option<MandateReference>,
     merchant_connector_id: Option<String>,
 ) -> CustomResult<Option<storage::MandateNew>, errors::ApiErrorResponse> {
-    match (setup_mandate_details, customer) {
-        (Some(data), Some(cus)) => {
+    match (setup_mandate_details, customer_id) {
+        (Some(data), Some(cus_id)) => {
             let mandate_id = utils::generate_id(consts::ID_LENGTH, "man");
 
             // The construction of the mandate new must be visible
@@ -2638,7 +2637,7 @@ pub fn generate_mandate(
                 .get_required_value("customer_acceptance")?;
             new_mandate
                 .set_mandate_id(mandate_id)
-                .set_customer_id(cus.customer_id.clone())
+                .set_customer_id(cus_id.clone())
                 .set_merchant_id(merchant_id)
                 .set_original_payment_id(Some(payment_id))
                 .set_payment_method_id(payment_method_id)
diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs
index 6b04c500dc1..d4590782b0a 100644
--- a/crates/router/src/core/payments/operations.rs
+++ b/crates/router/src/core/payments/operations.rs
@@ -212,7 +212,7 @@ pub trait UpdateTracker<F, D, Req, Ctx: PaymentMethodRetrieve>: Send {
 }
 
 #[async_trait]
-pub trait PostUpdateTracker<F, D, R>: Send {
+pub trait PostUpdateTracker<F, D, R: Send>: Send {
     async fn update_tracker<'b>(
         &'b self,
         db: &'b AppState,
@@ -222,7 +222,21 @@ pub trait PostUpdateTracker<F, D, R>: Send {
         storage_scheme: enums::MerchantStorageScheme,
     ) -> RouterResult<D>
     where
-        F: 'b + Send;
+        F: 'b + Send + Sync;
+
+    async fn save_pm_and_mandate<'b>(
+        &self,
+        _state: &AppState,
+        _resp: &types::RouterData<F, R, PaymentsResponseData>,
+        _merchant_account: &domain::MerchantAccount,
+        _key_store: &domain::MerchantKeyStore,
+        _payment_data: &mut PaymentData<F>,
+    ) -> CustomResult<(), errors::ApiErrorResponse>
+    where
+        F: 'b + Clone + Send + Sync,
+    {
+        Ok(())
+    }
 }
 
 #[async_trait]
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index b96cf483236..4580181d394 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -15,7 +15,7 @@ use super::{Operation, PostUpdateTracker};
 use crate::{
     connector::utils::PaymentResponseRouterData,
     core::{
-        errors::{self, RouterResult, StorageErrorExt},
+        errors::{self, CustomResult, RouterResult, StorageErrorExt},
         mandate,
         payment_methods::{self, PaymentMethodRetrieve},
         payments::{
@@ -24,6 +24,7 @@ use crate::{
                 self as payments_helpers,
                 update_additional_payment_data_with_connector_response_pm_data,
             },
+            tokenization,
             types::MultipleCaptureData,
             PaymentData,
         },
@@ -31,7 +32,7 @@ use crate::{
     },
     routes::{metrics, AppState},
     types::{
-        self, api,
+        self, api, domain,
         storage::{self, enums},
         transformers::{ForeignFrom, ForeignTryFrom},
         CaptureSyncResponse, ErrorResponse,
@@ -42,12 +43,12 @@ use crate::{
 #[derive(Debug, Clone, Copy, router_derive::PaymentOperation)]
 #[operation(
     operations = "post_update_tracker",
-    flow = "sync_data, authorize_data, cancel_data, capture_data, complete_authorize_data, approve_data, reject_data, setup_mandate_data, session_data,incremental_authorization_data"
+    flow = "sync_data, cancel_data, authorize_data, capture_data, complete_authorize_data, approve_data, reject_data, setup_mandate_data, session_data,incremental_authorization_data"
 )]
 pub struct PaymentResponse;
 
 #[async_trait]
-impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData>
+impl<F: Send + Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData>
     for PaymentResponse
 {
     async fn update_tracker<'b>(
@@ -63,7 +64,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData
         storage_scheme: enums::MerchantStorageScheme,
     ) -> RouterResult<PaymentData<F>>
     where
-        F: 'b + Send,
+        F: 'b,
     {
         payment_data.mandate_id = payment_data
             .mandate_id
@@ -80,6 +81,151 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsAuthorizeData
 
         Ok(payment_data)
     }
+
+    async fn save_pm_and_mandate<'b>(
+        &self,
+        state: &AppState,
+        resp: &types::RouterData<F, types::PaymentsAuthorizeData, types::PaymentsResponseData>,
+        merchant_account: &domain::MerchantAccount,
+        key_store: &domain::MerchantKeyStore,
+        payment_data: &mut PaymentData<F>,
+    ) -> CustomResult<(), errors::ApiErrorResponse>
+    where
+        F: 'b + Clone + Send + Sync,
+    {
+        let customer_id = payment_data.payment_intent.customer_id.clone();
+        let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
+        let profile_id = payment_data.payment_intent.profile_id.clone();
+
+        let connector_name = payment_data
+            .payment_attempt
+            .connector
+            .clone()
+            .ok_or_else(|| {
+                logger::error!("Missing required Param connector_name");
+                errors::ApiErrorResponse::MissingRequiredField {
+                    field_name: "connector_name",
+                }
+            })?;
+        let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
+        let billing_name = resp
+            .address
+            .get_payment_method_billing()
+            .and_then(|billing_details| billing_details.address.as_ref())
+            .and_then(|address| address.get_optional_full_name());
+
+        let save_payment_call_future = Box::pin(tokenization::save_payment_method(
+            state,
+            connector_name.clone(),
+            merchant_connector_id.clone(),
+            save_payment_data,
+            customer_id.clone(),
+            merchant_account,
+            resp.request.payment_method_type,
+            key_store,
+            Some(resp.request.amount),
+            Some(resp.request.currency),
+            profile_id,
+            billing_name.clone(),
+        ));
+
+        let is_connector_mandate = resp.request.customer_acceptance.is_some()
+            && matches!(
+                resp.request.setup_future_usage,
+                Some(enums::FutureUsage::OffSession)
+            );
+
+        let is_legacy_mandate = resp.request.setup_mandate_details.is_some()
+            && matches!(
+                resp.request.setup_future_usage,
+                Some(enums::FutureUsage::OffSession)
+            );
+
+        if is_legacy_mandate {
+            // Mandate is created on the application side and at the connector.
+            let (payment_method_id, _payment_method_status) = save_payment_call_future.await?;
+
+            let mandate_id = mandate::mandate_procedure(
+                state,
+                resp,
+                &customer_id.clone(),
+                payment_method_id.clone(),
+                merchant_connector_id.clone(),
+                merchant_account.storage_scheme,
+            )
+            .await?;
+            payment_data.payment_attempt.payment_method_id = payment_method_id;
+            payment_data.payment_attempt.mandate_id = mandate_id;
+            Ok(())
+        } else if is_connector_mandate {
+            // The mandate is created on connector's end.
+            let (payment_method_id, _payment_method_status) = save_payment_call_future.await?;
+            payment_data.payment_attempt.payment_method_id = payment_method_id;
+            Ok(())
+        } else {
+            // Save card flow
+            let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
+            let merchant_account = merchant_account.clone();
+            let key_store = key_store.clone();
+            let state = state.clone();
+            let customer_id = payment_data.payment_intent.customer_id.clone();
+            let profile_id = payment_data.payment_intent.profile_id.clone();
+
+            let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
+            let payment_attempt = payment_data.payment_attempt.clone();
+
+            let amount = resp.request.amount;
+            let currency = resp.request.currency;
+            let payment_method_type = resp.request.payment_method_type;
+            let storage_scheme = merchant_account.clone().storage_scheme;
+
+            logger::info!("Call to save_payment_method in locker");
+            let _task_handle = tokio::spawn(
+                async move {
+                    logger::info!("Starting async call to save_payment_method in locker");
+
+                    let result = Box::pin(tokenization::save_payment_method(
+                        &state,
+                        connector_name,
+                        merchant_connector_id,
+                        save_payment_data,
+                        customer_id,
+                        &merchant_account,
+                        payment_method_type,
+                        &key_store,
+                        Some(amount),
+                        Some(currency),
+                        profile_id,
+                        billing_name,
+                    ))
+                    .await;
+
+                    if let Err(err) = result {
+                        logger::error!("Asynchronously saving card in locker failed : {:?}", err);
+                    } else if let Ok((payment_method_id, _pm_status)) = result {
+                        let payment_attempt_update =
+                            storage::PaymentAttemptUpdate::PaymentMethodDetailsUpdate {
+                                payment_method_id,
+                                updated_by: storage_scheme.clone().to_string(),
+                            };
+                        let respond = state
+                            .store
+                            .update_payment_attempt_with_attempt_id(
+                                payment_attempt,
+                                payment_attempt_update,
+                                storage_scheme,
+                            )
+                            .await;
+                        if let Err(err) = respond {
+                            logger::error!("Error updating payment attempt: {:?}", err);
+                        };
+                    }
+                }
+                .in_current_span(),
+            );
+            Ok(())
+        }
+    }
 }
 
 #[async_trait]
@@ -234,6 +380,28 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::PaymentsSyncData> for
         ))
         .await
     }
+
+    async fn save_pm_and_mandate<'b>(
+        &self,
+        state: &AppState,
+        resp: &types::RouterData<F, types::PaymentsSyncData, types::PaymentsResponseData>,
+        merchant_account: &domain::MerchantAccount,
+        _key_store: &domain::MerchantKeyStore,
+        payment_data: &mut PaymentData<F>,
+    ) -> CustomResult<(), errors::ApiErrorResponse>
+    where
+        F: 'b + Clone + Send + Sync,
+    {
+        update_payment_method_status_and_ntid(
+            state,
+            payment_data,
+            resp.status,
+            resp.response.clone(),
+            merchant_account.storage_scheme,
+        )
+        .await?;
+        Ok(())
+    }
 }
 
 #[async_trait]
@@ -411,6 +579,67 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::SetupMandateRequestDa
 
         Ok(payment_data)
     }
+
+    async fn save_pm_and_mandate<'b>(
+        &self,
+        state: &AppState,
+        resp: &types::RouterData<F, types::SetupMandateRequestData, types::PaymentsResponseData>,
+        merchant_account: &domain::MerchantAccount,
+        key_store: &domain::MerchantKeyStore,
+        payment_data: &mut PaymentData<F>,
+    ) -> CustomResult<(), errors::ApiErrorResponse>
+    where
+        F: 'b + Clone + Send + Sync,
+    {
+        let billing_name = resp
+            .address
+            .get_payment_method_billing()
+            .and_then(|billing_details| billing_details.address.as_ref())
+            .and_then(|address| address.get_optional_full_name());
+        let save_payment_data = tokenization::SavePaymentMethodData::from(resp);
+        let customer_id = payment_data.payment_intent.customer_id.clone();
+        let profile_id = payment_data.payment_intent.profile_id.clone();
+        let connector_name = payment_data
+            .payment_attempt
+            .connector
+            .clone()
+            .ok_or_else(|| {
+                logger::error!("Missing required Param connector_name");
+                errors::ApiErrorResponse::MissingRequiredField {
+                    field_name: "connector_name",
+                }
+            })?;
+        let merchant_connector_id = payment_data.payment_attempt.merchant_connector_id.clone();
+        let (payment_method_id, _payment_method_status) =
+            Box::pin(tokenization::save_payment_method(
+                state,
+                connector_name,
+                merchant_connector_id.clone(),
+                save_payment_data,
+                customer_id.clone(),
+                merchant_account,
+                resp.request.payment_method_type,
+                key_store,
+                resp.request.amount,
+                Some(resp.request.currency),
+                profile_id,
+                billing_name,
+            ))
+            .await?;
+
+        let mandate_id = mandate::mandate_procedure(
+            state,
+            resp,
+            &customer_id,
+            payment_method_id.clone(),
+            merchant_connector_id.clone(),
+            merchant_account.storage_scheme,
+        )
+        .await?;
+        payment_data.payment_attempt.payment_method_id = payment_method_id;
+        payment_data.payment_attempt.mandate_id = mandate_id;
+        Ok(())
+    }
 }
 
 #[async_trait]
@@ -437,6 +666,28 @@ impl<F: Clone> PostUpdateTracker<F, PaymentData<F>, types::CompleteAuthorizeData
         ))
         .await
     }
+
+    async fn save_pm_and_mandate<'b>(
+        &self,
+        state: &AppState,
+        resp: &types::RouterData<F, types::CompleteAuthorizeData, types::PaymentsResponseData>,
+        merchant_account: &domain::MerchantAccount,
+        _key_store: &domain::MerchantKeyStore,
+        payment_data: &mut PaymentData<F>,
+    ) -> CustomResult<(), errors::ApiErrorResponse>
+    where
+        F: 'b + Clone + Send + Sync,
+    {
+        update_payment_method_status_and_ntid(
+            state,
+            payment_data,
+            resp.status,
+            resp.response.clone(),
+            merchant_account.storage_scheme,
+        )
+        .await?;
+        Ok(())
+    }
 }
 
 #[instrument(skip_all)]
@@ -587,7 +838,10 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                     let payment_attempt_update =
                         storage::PaymentAttemptUpdate::PreprocessingUpdate {
                             status: updated_attempt_status,
-                            payment_method_id: router_data.payment_method_id,
+                            payment_method_id: payment_data
+                                .payment_attempt
+                                .payment_method_id
+                                .clone(),
                             connector_metadata,
                             preprocessing_step_id,
                             connector_transaction_id,
@@ -642,7 +896,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                         metrics::SUCCESSFUL_PAYMENT.add(&metrics::CONTEXT, 1, &[]);
                     }
 
-                    let payment_method_id = router_data.payment_method_id.clone();
+                    let payment_method_id = payment_data.payment_attempt.payment_method_id.clone();
 
                     utils::add_apple_pay_payment_status_metrics(
                         router_data.status,
@@ -677,10 +931,7 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
                                     .request
                                     .get_amount_capturable(&payment_data, updated_attempt_status),
                                 payment_method_id,
-                                mandate_id: payment_data
-                                    .mandate_id
-                                    .clone()
-                                    .and_then(|mandate| mandate.mandate_id),
+                                mandate_id: payment_data.payment_attempt.mandate_id.clone(),
                                 connector_metadata,
                                 payment_token: None,
                                 error_code: error_status.clone(),
@@ -862,14 +1113,6 @@ async fn payment_response_update_tracker<F: Clone, T: types::Capturable>(
         },
     };
 
-    update_payment_method_status_and_ntid(
-        state,
-        &mut payment_data,
-        router_data.status,
-        router_data.response.clone(),
-        storage_scheme,
-    )
-    .await?;
     let m_db = state.clone().store;
     let m_payment_data_payment_intent = payment_data.payment_intent.clone();
     let m_payment_intent_update = payment_intent_update.clone();
diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs
index 5bf6b2e7013..304e6edd62e 100644
--- a/crates/router/src/core/payments/tokenization.rs
+++ b/crates/router/src/core/payments/tokenization.rs
@@ -29,38 +29,62 @@ use crate::{
     utils::{generate_id, OptionExt},
 };
 
+pub struct SavePaymentMethodData<Req> {
+    request: Req,
+    response: Result<types::PaymentsResponseData, types::ErrorResponse>,
+    payment_method_token: Option<types::PaymentMethodToken>,
+    payment_method: PaymentMethod,
+    attempt_status: common_enums::AttemptStatus,
+}
+
+impl<F, Req: Clone> From<&types::RouterData<F, Req, types::PaymentsResponseData>>
+    for SavePaymentMethodData<Req>
+{
+    fn from(router_data: &types::RouterData<F, Req, types::PaymentsResponseData>) -> Self {
+        Self {
+            request: router_data.request.clone(),
+            response: router_data.response.clone(),
+            payment_method_token: router_data.payment_method_token.clone(),
+            payment_method: router_data.payment_method,
+            attempt_status: router_data.status,
+        }
+    }
+}
+
 #[instrument(skip_all)]
 #[allow(clippy::too_many_arguments)]
-pub async fn save_payment_method<F: Clone, FData>(
+pub async fn save_payment_method<FData>(
     state: &AppState,
-    connector: &api::ConnectorData,
-    resp: types::RouterData<F, FData, types::PaymentsResponseData>,
-    maybe_customer: &Option<domain::Customer>,
+    connector_name: String,
+    merchant_connector_id: Option<String>,
+    save_payment_method_data: SavePaymentMethodData<FData>,
+    customer_id: Option<String>,
     merchant_account: &domain::MerchantAccount,
     payment_method_type: Option<storage_enums::PaymentMethodType>,
     key_store: &domain::MerchantKeyStore,
     amount: Option<i64>,
     currency: Option<storage_enums::Currency>,
     profile_id: Option<String>,
+    billing_name: Option<masking::Secret<String>>,
 ) -> RouterResult<(Option<String>, Option<common_enums::PaymentMethodStatus>)>
 where
-    FData: mandate::MandateBehaviour,
+    FData: mandate::MandateBehaviour + Clone,
 {
     let mut pm_status = None;
-    match resp.response {
+    match save_payment_method_data.response {
         Ok(responses) => {
             let db = &*state.store;
             let token_store = state
                 .conf
                 .tokenization
                 .0
-                .get(&connector.connector_name.to_string())
+                .get(&connector_name.to_string())
                 .map(|token_filter| token_filter.long_lived_token)
                 .unwrap_or(false);
 
-            let network_transaction_id = match responses.clone() {
+            let network_transaction_id = match &responses {
                 types::PaymentsResponseData::TransactionResponse { network_txn_id, .. } => {
-                    network_txn_id
+                    network_txn_id.clone()
                 }
                 _ => None,
             };
@@ -82,7 +106,7 @@ where
                         .attach_printable("The pg_agnostic config was not found in the DB")?;
 
                     if &pg_agnostic.config == "true"
-                        && resp.request.get_setup_future_usage()
+                        && save_payment_method_data.request.get_setup_future_usage()
                             == Some(storage_enums::FutureUsage::OffSession)
                     {
                         Some(network_transaction_id)
@@ -95,7 +119,7 @@ where
                 };
 
             let connector_token = if token_store {
-                let tokens = resp
+                let tokens = save_payment_method_data
                     .payment_method_token
                     .to_owned()
                     .get_required_value("payment_token")?;
@@ -107,17 +131,17 @@ where
                         })?
                     }
                 };
-                Some((connector, token))
+                Some((connector_name, token))
             } else {
                 None
             };
 
-            let mandate_data_customer_acceptance = resp
+            let mandate_data_customer_acceptance = save_payment_method_data
                 .request
                 .get_setup_mandate_details()
                 .and_then(|mandate_data| mandate_data.customer_acceptance.clone());
 
-            let customer_acceptance = resp
+            let customer_acceptance = save_payment_method_data
                 .request
                 .get_customer_acceptance()
                 .or(mandate_data_customer_acceptance.clone().map(From::from))
@@ -139,8 +163,11 @@ where
                 }
                 _ => None,
             };
-            let check_for_mit_mandates = resp.request.get_setup_mandate_details().is_none()
-                && resp
+            let check_for_mit_mandates = save_payment_method_data
+                .request
+                .get_setup_mandate_details()
+                .is_none()
+                && save_payment_method_data
                     .request
                     .get_setup_future_usage()
                     .map(|future_usage| future_usage == storage_enums::FutureUsage::OffSession)
@@ -151,7 +178,7 @@ where
                     payment_method_type,
                     amount,
                     currency,
-                    connector.merchant_connector_id.clone(),
+                    merchant_connector_id.clone(),
                     connector_mandate_id.clone(),
                 )
             } else {
@@ -163,23 +190,16 @@ where
             .attach_printable("Unable to serialize customer acceptance to value")?;
 
             let pm_id = if customer_acceptance.is_some() {
-                let customer = maybe_customer.to_owned().get_required_value("customer")?;
-                let billing_name = resp
-                    .address
-                    .get_payment_method_billing()
-                    .and_then(|billing_details| billing_details.address.as_ref())
-                    .and_then(|address| address.get_optional_full_name());
-
                 let payment_method_create_request = helpers::get_payment_method_create_request(
-                    Some(&resp.request.get_payment_method_data()),
-                    Some(resp.payment_method),
+                    Some(&save_payment_method_data.request.get_payment_method_data()),
+                    Some(save_payment_method_data.payment_method),
                     payment_method_type,
-                    &customer,
+                    &customer_id.clone(),
                     billing_name,
                 )
                 .await?;
+                let customer_id = customer_id.to_owned().get_required_value("customer_id")?;
                 let merchant_id = &merchant_account.merchant_id;
-
                 let (mut resp, duplication_check) = if !state.conf.locker.locker_enabled {
                     skip_saving_card_in_locker(
                         merchant_account,
@@ -187,7 +207,9 @@ where
                     )
                     .await?
                 } else {
-                    pm_status = Some(common_enums::PaymentMethodStatus::from(resp.status));
+                    pm_status = Some(common_enums::PaymentMethodStatus::from(
+                        save_payment_method_data.attempt_status,
+                    ));
                     Box::pin(save_in_locker(
                         state,
                         merchant_account,
@@ -280,7 +302,7 @@ where
                                                 payment_method_type,
                                                 amount,
                                                 currency,
-                                                connector.merchant_connector_id.clone(),
+                                                merchant_connector_id.clone(),
                                                 connector_mandate_id.clone(),
                                             )?;
 
@@ -297,7 +319,7 @@ where
                                         payment_methods::cards::create_payment_method(
                                             db,
                                             &payment_method_create_request,
-                                            &customer.customer_id,
+                                            customer_id.as_str(),
                                             &resp.payment_method_id,
                                             locker_id,
                                             merchant_id,
@@ -371,7 +393,7 @@ where
                                                     payment_method_type,
                                                     amount,
                                                     currency,
-                                                    connector.merchant_connector_id.clone(),
+                                                    merchant_connector_id.clone(),
                                                     connector_mandate_id.clone(),
                                                 )?;
 
@@ -390,7 +412,7 @@ where
                                                 payment_method_create_request.clone(),
                                                 key_store,
                                                 &merchant_account.merchant_id,
-                                                &customer.customer_id,
+                                                customer_id.as_str(),
                                                 resp.metadata.clone().map(|val| val.expose()),
                                                 customer_acceptance,
                                                 locker_id,
@@ -413,7 +435,7 @@ where
 
                                 payment_methods::cards::delete_card_from_locker(
                                     state,
-                                    &customer.customer_id,
+                                    customer_id.as_str(),
                                     merchant_id,
                                     existing_pm
                                         .locker_id
@@ -426,7 +448,7 @@ where
                                     state,
                                     payment_method_create_request,
                                     &card,
-                                    customer.customer_id.clone(),
+                                    customer_id.clone(),
                                     merchant_account,
                                     api::enums::LockerChoice::HyperswitchCardVault,
                                     Some(
@@ -516,7 +538,7 @@ where
                         payment_methods::cards::create_payment_method(
                             db,
                             &payment_method_create_request,
-                            &customer.customer_id,
+                            customer_id.as_str(),
                             &resp.payment_method_id,
                             locker_id,
                             merchant_id,
@@ -683,7 +705,7 @@ pub async fn save_in_locker(
 
 pub fn create_payment_method_metadata(
     metadata: Option<&pii::SecretSerdeValue>,
-    connector_token: Option<(&api::ConnectorData, String)>,
+    connector_token: Option<(String, String)>,
 ) -> RouterResult<Option<serde_json::Value>> {
     let mut meta = match metadata {
         None => serde_json::Map::new(),
@@ -698,7 +720,7 @@ pub fn create_payment_method_metadata(
     };
     Ok(connector_token.and_then(|connector_and_token| {
         meta.insert(
-            connector_and_token.0.connector_name.to_string(),
+            connector_and_token.0,
             serde_json::Value::String(connector_and_token.1),
         )
     }))
diff --git a/crates/storage_impl/src/payments/payment_attempt.rs b/crates/storage_impl/src/payments/payment_attempt.rs
index 6eda689c8da..1fd25f3dedb 100644
--- a/crates/storage_impl/src/payments/payment_attempt.rs
+++ b/crates/storage_impl/src/payments/payment_attempt.rs
@@ -1433,6 +1433,13 @@ impl DataModelExt for PaymentAttemptUpdate {
                 error_message,
                 updated_by,
             },
+            Self::PaymentMethodDetailsUpdate {
+                payment_method_id,
+                updated_by,
+            } => DieselPaymentAttemptUpdate::PaymentMethodDetailsUpdate {
+                payment_method_id,
+                updated_by,
+            },
             Self::ConfirmUpdate {
                 amount,
                 currency,
@@ -1807,6 +1814,13 @@ impl DataModelExt for PaymentAttemptUpdate {
                 error_message,
                 updated_by,
             },
+            DieselPaymentAttemptUpdate::PaymentMethodDetailsUpdate {
+                payment_method_id,
+                updated_by,
+            } => Self::PaymentMethodDetailsUpdate {
+                payment_method_id,
+                updated_by,
+            },
             DieselPaymentAttemptUpdate::ResponseUpdate {
                 status,
                 connector,

## 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 -->
This PR aims to make the locker call async(again). Moreover the main problem this PR solves is to adding `pm_id` in payment_attempt even in an async locker call. This is achieved by adding up a new function in `PostUpdateTracker` trait named as `save_pm_and_mandate`.

### 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?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Curls for the test cases:
1. Save Card payment
```
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Lhj81dmb5DH3dasSlnRy2LUAHIOc6LJDFSQxyLbrAS7HWS5Hxva2YOJTYea90wXw' \
--data-raw '

{
    "amount": 150,
    "currency": "USD",
    "confirm": true,
    "profile_id": "pro_I8QjDfpUKyQ0EBzKRWfp",
    "capture_method": "automatic",
    "customer_id": "cus_ssOCYK1qRIjeBfMhEy6A",
    "amount_to_capture": 150,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "card_number": "5555 5555 5555 4444",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "737"
        }
    },
    "phone_country_code": "+65",
    "authentication_type": "no_three_ds",
    "description": "Its my first payment request",
    "return_url": "https://google.com",
    "statement_descriptor_name": "Juspay",
    "statement_descriptor_suffix": "Router",
    "metadata": {},
    "customer_acceptance": {
        "acceptance_type": "offline",
        "accepted_at": "1963-05-03T04:07:52.723Z",
        "online": {
            "ip_address": "in sit",
            "user_agent": "amet irure esse"
        }
    },
    "setup_future_usage": "off_session",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "sundari",
            "last_name": "sundari"
        }
    }
}'
```
Response
```
{
    "payment_id": "pay_htNp37U15sOwsXZPZ9Br",
    "merchant_id": "merchant_1712724367",
    "status": "succeeded",
    "amount": 150,
    "net_amount": 150,
    "amount_capturable": 0,
    "amount_received": 150,
    "connector": "cybersource",
    "client_secret": "pay_htNp37U15sOwsXZPZ9Br_secret_I56SQnEKoGQGyHhcIarl",
    "created": "2024-04-11T05:07:46.740Z",
    "currency": "USD",
    "customer_id": "cus_ssOCYK1qRIjeBfMhEy6A",
    "customer": {
        "id": "cus_ssOCYK1qRIjeBfMhEy6A",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+65"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "off_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4444",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "555555",
            "card_extended_bin": "55555555",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_QD71AcirSSN1hroINifk",
    "shipping": null,
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "sundari",
            "last_name": "sundari"
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "Juspay",
    "statement_descriptor_suffix": "Router",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "cus_ssOCYK1qRIjeBfMhEy6A",
        "created_at": 1712812066,
        "expires": 1712815666,
        "secret": "epk_9ea82157d50e457c8f0d56c88f13b117"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7128120679866152903954",
    "frm_message": null,
    "metadata": {},
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_htNp37U15sOwsXZPZ9Br_1",
    "payment_link": null,
    "profile_id": "pro_I8QjDfpUKyQ0EBzKRWfp",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_JGHsMWurht6srLkWsjO3",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-11T05:22:46.740Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null
}
```

Payment Retrieve 
```
{
    "payment_id": "pay_htNp37U15sOwsXZPZ9Br",
    "merchant_id": "merchant_1712724367",
    "status": "succeeded",
    "amount": 150,
    "net_amount": 150,
    "amount_capturable": 0,
    "amount_received": 150,
    "connector": "cybersource",
    "client_secret": "pay_htNp37U15sOwsXZPZ9Br_secret_I56SQnEKoGQGyHhcIarl",
    "created": "2024-04-11T05:07:46.740Z",
    "currency": "USD",
    "customer_id": "cus_ssOCYK1qRIjeBfMhEy6A",
    "customer": {
        "id": "cus_ssOCYK1qRIjeBfMhEy6A",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+65"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "off_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "4444",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "555555",
            "card_extended_bin": "55555555",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_QD71AcirSSN1hroINifk",
    "shipping": null,
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "sundari",
            "last_name": "sundari"
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "Juspay",
    "statement_descriptor_suffix": "Router",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": null,
    "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": "7128120679866152903954",
    "frm_message": null,
    "metadata": {},
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_htNp37U15sOwsXZPZ9Br_1",
    "payment_link": null,
    "profile_id": "pro_I8QjDfpUKyQ0EBzKRWfp",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_JGHsMWurht6srLkWsjO3",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-11T05:22:46.740Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": "pm_2ERfpXUJ7vYzqYSk3hdE",
    "payment_method_status": "active"
}
```


2. Setup Mandate
```
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Lhj81dmb5DH3dasSlnRy2LUAHIOc6LJDFSQxyLbrAS7HWS5Hxva2YOJTYea90wXw' \
--data-raw '
{
    "amount": 0,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "customer_id": "mandate_customer1",
    "email": "guest@example.com",
    "setup_future_usage": "off_session",
    "customer_acceptance": {
        "acceptance_type": "online"
    },
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "Test Holder",
            "card_cvc": "737"
        }
    },
    "payment_type": "setup_mandate",
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "authentication_type": "three_ds"
}'
```

Response
```
{
    "payment_id": "pay_okrJvtehbXTDXcDydQnv",
    "merchant_id": "merchant_1712724367",
    "status": "succeeded",
    "amount": 0,
    "net_amount": 0,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": "cybersource",
    "client_secret": "pay_okrJvtehbXTDXcDydQnv_secret_Y0quTVIFHHwNV1fTLT2T",
    "created": "2024-04-11T05:36:27.333Z",
    "currency": "USD",
    "customer_id": "mandate_customer1",
    "customer": {
        "id": "mandate_customer1",
        "name": null,
        "email": "guest@example.com",
        "phone": null,
        "phone_country_code": null
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": "off_session",
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "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": "03",
            "card_exp_year": "2030",
            "card_holder_name": "Test Holder",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": "token_Xh7IwDTEFet4MJ3XCyYz",
    "shipping": null,
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": null,
    "phone": null,
    "return_url": null,
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "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": {
        "customer_id": "mandate_customer1",
        "created_at": 1712813787,
        "expires": 1712817387,
        "secret": "epk_b75613967b7e4965bad88d90704f985e"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7128137892656606404953",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_okrJvtehbXTDXcDydQnv_1",
    "payment_link": null,
    "profile_id": "pro_I8QjDfpUKyQ0EBzKRWfp",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_JGHsMWurht6srLkWsjO3",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-11T05:51:27.333Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": "pm_WmIHJOFLVZYHH1GMSztb",
    "payment_method_status": null
}
```


3. Recurring mandate
```
curl --location 'http://127.0.0.1:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Lhj81dmb5DH3dasSlnRy2LUAHIOc6LJDFSQxyLbrAS7HWS5Hxva2YOJTYea90wXw' \
--data-raw '
{
    "amount": 499,
    "currency": "USD",
    "confirm": true,
    "profile_id": "pro_I8QjDfpUKyQ0EBzKRWfp",
    "capture_method": "automatic",
    "customer_id": "mandate_customer1",
    "email": "guest@example.com",
    "off_session": true,
    "recurring_details": {
        "type": "payment_method_id",
        "data": "pm_WmIHJOFLVZYHH1GMSztb"
    },
    "payment_method": "card",
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "authentication_type": "three_ds"
}'
```
Response:
```
{
    "payment_id": "pay_mg9hJf7VjCYa50hpSrdG",
    "merchant_id": "merchant_1712724367",
    "status": "succeeded",
    "amount": 499,
    "net_amount": 499,
    "amount_capturable": 0,
    "amount_received": 499,
    "connector": "cybersource",
    "client_secret": "pay_mg9hJf7VjCYa50hpSrdG_secret_EcNG9aGozUOBzs2vZhXj",
    "created": "2024-04-11T05:39:34.990Z",
    "currency": "USD",
    "customer_id": "mandate_customer1",
    "customer": {
        "id": "mandate_customer1",
        "name": null,
        "email": "guest@example.com",
        "phone": null,
        "phone_country_code": null
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "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": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "Test Holder",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": null,
    "phone": null,
    "return_url": null,
    "authentication_type": "three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "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": {
        "customer_id": "mandate_customer1",
        "created_at": 1712813974,
        "expires": 1712817574,
        "secret": "epk_381d81dec1eb4219a7ac9a072fd4402e"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "7128139751946727304953",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_mg9hJf7VjCYa50hpSrdG_1",
    "payment_link": null,
    "profile_id": "pro_I8QjDfpUKyQ0EBzKRWfp",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_JGHsMWurht6srLkWsjO3",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-11T05:54:34.990Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": "pm_WmIHJOFLVZYHH1GMSztb",
    "payment_method_status": "active"
}
```

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