pub async fn payments_manual_update(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsManualUpdateRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentsManualUpdate;
    let mut payload = json_payload.into_inner();
    let payment_id = path.into_inner();

    let locking_action = payload.get_locking_input(flow.clone());

    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    payload.payment_id = payment_id;

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, _auth, req, _req_state| payments::payments_manual_update(state, req),
        &auth::AdminApiAuthWithMerchantIdFromHeader,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
/// Retrieve endpoint for merchant to fetch the encrypted customer payment method data
#[instrument(skip_all, fields(flow = ?Flow::GetExtendedCardInfo, payment_id))]
pub async fn retrieve_extended_card_info(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::GetExtendedCardInfo;
    let payment_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payment_id,
        |state, auth: auth::AuthenticationData, payment_id, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::get_extended_card_info(
                state,
                merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id,
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: true,
        }),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
pub fn get_or_generate_payment_id(
    payload: &mut payment_types::PaymentsRequest,
) -> errors::RouterResult<()> {
    let given_payment_id = payload
        .payment_id
        .clone()
        .map(|payment_id| {
            payment_id
                .get_payment_intent_id()
                .map_err(|err| err.change_context(errors::ApiErrorResponse::PaymentNotFound))
        })
        .transpose()?;

    let payment_id = given_payment_id.unwrap_or(common_utils::id_type::PaymentId::default());

    payload.is_payment_id_from_merchant = matches!(
        &payload.payment_id,
        Some(payment_types::PaymentIdType::PaymentIntentId(_))
    );

    payload.payment_id = Some(api_models::payments::PaymentIdType::PaymentIntentId(
        payment_id,
    ));

    Ok(())
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        match self.payment_id {
            Some(payment_types::PaymentIdType::PaymentIntentId(ref id)) => {
                let api_identifier = lock_utils::ApiIdentifier::from(flow);
                let intent_id_locking_input = api_locking::LockingInput {
                    unique_locking_key: id.get_string_repr().to_owned(),
                    api_identifier: api_identifier.clone(),
                    override_lock_retries: None,
                };
                if let Some(customer_id) = self
                    .customer_id
                    .as_ref()
                    .or(self.customer.as_ref().map(|customer| &customer.id))
                {
                    api_locking::LockAction::HoldMultiple {
                        inputs: vec![
                            intent_id_locking_input,
                            api_locking::LockingInput {
                                unique_locking_key: customer_id.get_string_repr().to_owned(),
                                api_identifier,
                                override_lock_retries: None,
                            },
                        ],
                    }
                } else {
                    api_locking::LockAction::Hold {
                        input: intent_id_locking_input,
                    }
                }
            }
            _ => api_locking::LockAction::NotApplicable,
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsStartRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsRetrieveRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        match self.resource_id {
            payment_types::PaymentIdType::PaymentIntentId(ref id) if self.force_sync => {
                api_locking::LockAction::Hold {
                    input: api_locking::LockingInput {
                        unique_locking_key: id.get_string_repr().to_owned(),
                        api_identifier: lock_utils::ApiIdentifier::from(flow),
                        override_lock_retries: None,
                    },
                }
            }
            _ => api_locking::LockAction::NotApplicable,
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsSessionRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v2")]
impl GetLockingInput for payment_types::PaymentsSessionRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
    {
        api_locking::LockAction::NotApplicable
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsDynamicTaxCalculationRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsPostSessionTokensRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsUpdateMetadataRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payments::PaymentsRedirectResponseData {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        match self.resource_id {
            payment_types::PaymentIdType::PaymentIntentId(ref id) => {
                api_locking::LockAction::Hold {
                    input: api_locking::LockingInput {
                        unique_locking_key: id.get_string_repr().to_owned(),
                        api_identifier: lock_utils::ApiIdentifier::from(flow),
                        override_lock_retries: None,
                    },
                }
            }
            _ => api_locking::LockAction::NotApplicable,
        }
    }
}

#[cfg(feature = "v2")]
impl GetLockingInput for payments::PaymentsRedirectResponseData {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsCompleteAuthorizeRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsCancelRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsCancelPostCaptureRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsCaptureRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "oltp")]
struct FPaymentsApproveRequest<'a>(&'a payment_types::PaymentsApproveRequest);

#[cfg(feature = "oltp")]
impl GetLockingInput for FPaymentsApproveRequest<'_> {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.0.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "oltp")]
struct FPaymentsRejectRequest<'a>(&'a payment_types::PaymentsRejectRequest);

#[cfg(feature = "oltp")]
impl GetLockingInput for FPaymentsRejectRequest<'_> {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.0.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsIncrementalAuthorizationRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsExternalAuthenticationRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[cfg(feature = "v1")]
impl GetLockingInput for payment_types::PaymentsManualUpdateRequest {
    fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
    where
        F: types::FlowMetric,
        lock_utils::ApiIdentifier: From<F>,
    {
        api_locking::LockAction::Hold {
            input: api_locking::LockingInput {
                unique_locking_key: self.payment_id.get_string_repr().to_owned(),
                api_identifier: lock_utils::ApiIdentifier::from(flow),
                override_lock_retries: None,
            },
        }
    }
}

#[instrument(skip_all, fields(flow = ?Flow::PaymentsAggregate))]
#[cfg(all(feature = "olap", feature = "v1"))]
pub async fn get_payments_aggregates_profile(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Query<common_utils::types::TimeRange>,
) -> impl Responder {
    let flow = Flow::PaymentsAggregate;
    let payload = payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::get_aggregates_for_payments(
                state,
                merchant_context,
                auth.profile_id.map(|profile_id| vec![profile_id]),
                req,
            )
        },
        &auth::JWTAuth {
            permission: Permission::ProfilePaymentRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}
#[instrument(skip_all, fields(flow = ?Flow::PaymentsAggregate))]
#[cfg(all(feature = "olap", feature = "v2"))]
pub async fn get_payments_aggregates_profile(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Query<common_utils::types::TimeRange>,
) -> impl Responder {
    let flow = Flow::PaymentsAggregate;
    let payload = payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, _| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::get_aggregates_for_payments(
                state,
                merchant_context,
                Some(vec![auth.profile.get_id().clone()]),
                req,
            )
        },
        &auth::JWTAuth {
            permission: Permission::ProfilePaymentRead,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v2")]
/// A private module to hold internal types to be used in route handlers.
/// This is because we will need to implement certain traits on these types which will have the resource id
/// But the api payload will not contain the resource id
/// So these types can hold the resource id along with actual api payload, on which api event and locking action traits can be implemented
mod internal_payload_types {
    use super::*;

    // Serialize is implemented because of api events
    #[derive(Debug, serde::Serialize)]
    pub struct PaymentsGenericRequestWithResourceId<T: serde::Serialize> {
        pub global_payment_id: common_utils::id_type::GlobalPaymentId,
        #[serde(flatten)]
        pub payload: T,
    }

    impl<T: serde::Serialize> GetLockingInput for PaymentsGenericRequestWithResourceId<T> {
        fn get_locking_input<F>(&self, flow: F) -> api_locking::LockAction
        where
            F: types::FlowMetric,
            lock_utils::ApiIdentifier: From<F>,
        {
            api_locking::LockAction::Hold {
                input: api_locking::LockingInput {
                    unique_locking_key: self.global_payment_id.get_string_repr().to_owned(),
                    api_identifier: lock_utils::ApiIdentifier::from(flow),
                    override_lock_retries: None,
                },
            }
        }
    }

    impl<T: serde::Serialize> common_utils::events::ApiEventMetric
        for PaymentsGenericRequestWithResourceId<T>
    {
        fn get_api_event_type(&self) -> Option<common_utils::events::ApiEventsType> {
            Some(common_utils::events::ApiEventsType::Payment {
                payment_id: self.global_payment_id.clone(),
            })
        }
    }
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentStartRedirection, payment_id))]
pub async fn payments_start_redirection(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Query<api_models::payments::PaymentStartRedirectionParams>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentStartRedirection;

    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    let publishable_key = &payload.publishable_key;
    let profile_id = &payload.profile_id;

    let payment_start_redirection_request = api_models::payments::PaymentStartRedirectionRequest {
        id: global_payment_id.clone(),
    };

    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id: global_payment_id.clone(),
        payload: payment_start_redirection_request.clone(),
    };

    let locking_action = internal_payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payment_start_redirection_request.clone(),
        |state, auth: auth::AuthenticationData, _req, req_state| async {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payment_start_redirection(
                state,
                merchant_context,
                payment_start_redirection_request.clone(),
            )
            .await
        },
        &auth::PublishableKeyAndProfileIdAuth {
            publishable_key: publishable_key.clone(),
            profile_id: profile_id.clone(),
        },
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsConfirmIntent, payment_id))]
pub async fn payment_confirm_intent(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<api_models::payments::PaymentsConfirmIntentRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentConfirmData;

    let flow = Flow::PaymentsConfirmIntent;

    // TODO: Populate browser information into the payload
    // if let Err(err) = helpers::populate_ip_into_browser_info(&req, &mut payload) {
    //     return api::log_and_return_error_response(err);
    // }

    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id: global_payment_id.clone(),
        payload: json_payload.into_inner(),
    };

    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    let locking_action = internal_payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| async {
            let payment_id = req.global_payment_id;
            let request = req.payload;

            let operation = payments::operations::PaymentIntentConfirm;
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));

            Box::pin(payments::payments_core::<
                api_types::Authorize,
                api_models::payments::PaymentsResponse,
                _,
                _,
                _,
                PaymentConfirmData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
            ))
            .await
        },
        auth::api_or_client_auth(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
                global_payment_id,
            )),
            req.headers(),
        ),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::ProxyConfirmIntent, payment_id))]
pub async fn proxy_confirm_intent(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<api_models::payments::ProxyPaymentsRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentConfirmData;

    let flow = Flow::ProxyConfirmIntent;

    // Extract the payment ID from the path
    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    // Parse and validate headers
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    // Prepare the internal payload
    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id,
        payload: json_payload.into_inner(),
    };

    // Determine the locking action, if required
    let locking_action = internal_payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let payment_id = req.global_payment_id;
            let request = req.payload;

            // Define the operation for proxy payments intent
            let operation = payments::operations::proxy_payments_intent::PaymentProxyIntent;

            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            // Call the core proxy logic
            Box::pin(payments::proxy_for_payments_core::<
                api_types::Authorize,
                api_models::payments::PaymentsResponse,
                _,
                _,
                _,
                PaymentConfirmData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
                None,
            ))
        },
        &auth::V2ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::ProxyConfirmIntent, payment_id))]
pub async fn confirm_intent_with_external_vault_proxy(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<api_models::payments::ExternalVaultProxyPaymentsRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentConfirmData;

    let flow = Flow::ProxyConfirmIntent;

    // Extract the payment ID from the path
    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    // Parse and validate headers
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    // Prepare the internal payload
    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id: global_payment_id.clone(),
        payload: json_payload.into_inner(),
    };

    // Determine the locking action, if required
    let locking_action = internal_payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let payment_id = req.global_payment_id;
            let request = req.payload;

            // Define the operation for proxy payments intent
            let operation = payments::operations::external_vault_proxy_payment_intent::ExternalVaultProxyPaymentIntent;

            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            // Call the core proxy logic
            Box::pin(payments::external_vault_proxy_for_payments_core::<
                api_types::ExternalVaultProxy,
                api_models::payments::PaymentsResponse,
                _,
                _,
                _,
                PaymentConfirmData<api_types::ExternalVaultProxy>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
                None,
            ))
        },
        auth::api_or_client_auth(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
                global_payment_id,
            )),
            req.headers(),
        ),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip(state, req), fields(flow, payment_id))]
pub async fn payment_status(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Query<api_models::payments::PaymentsStatusRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentStatusData;

    let flow = match payload.force_sync {
        true => Flow::PaymentsRetrieveForceSync,
        false => Flow::PaymentsRetrieve,
    };

    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    let payload = payment_types::PaymentsRetrieveRequest {
        force_sync: payload.force_sync,
        expand_attempts: payload.expand_attempts,
        param: payload.param.clone(),
        return_raw_connector_response: payload.return_raw_connector_response,
        merchant_connector_details: None,
    };

    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id,
        payload,
    };

    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    let locking_action = internal_payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| async {
            let payment_id = req.global_payment_id;
            let request = req.payload;

            let operation = payments::operations::PaymentGet;

            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            Box::pin(payments::payments_core::<
                api_types::PSync,
                api_models::payments::PaymentsResponse,
                _,
                _,
                _,
                PaymentStatusData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
            ))
            .await
        },
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::ProfilePaymentRead,
            },
            req.headers(),
        ),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip(state, req), fields(flow, payment_id))]
pub async fn payments_status_with_gateway_creds(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    payload: web::Json<api_models::payments::PaymentsRetrieveRequest>,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentStatusData;

    let flow = match payload.force_sync {
        true => Flow::PaymentsRetrieveForceSync,
        false => Flow::PaymentsRetrieve,
    };
    tracing::Span::current().record("flow", flow.to_string());

    let global_payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", global_payment_id.get_string_repr());

    let internal_payload = internal_payload_types::PaymentsGenericRequestWithResourceId {
        global_payment_id,
        payload: payload.into_inner(),
    };

    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    let locking_action = internal_payload.get_locking_input(flow.clone());

    let auth_type = if state.conf.merchant_id_auth.merchant_id_auth_enabled {
        &auth::MerchantIdAuth
    } else {
        match env::which() {
            env::Env::Production => &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            _ => auth::auth_type(
                &auth::V2ApiKeyAuth {
                    is_connected_allowed: false,
                    is_platform_allowed: false,
                },
                &auth::JWTAuth {
                    permission: Permission::ProfilePaymentWrite,
                },
                req.headers(),
            ),
        }
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| async {
            let payment_id = req.global_payment_id;
            let request = req.payload;

            let operation = payments::operations::PaymentGet;

            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            Box::pin(payments::payments_core::<
                api_types::PSync,
                api_models::payments::PaymentsResponse,
                _,
                _,
                _,
                PaymentStatusData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
            ))
            .await
        },
        auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip(state, req), fields(flow, payment_id))]
pub async fn payment_get_intent_using_merchant_reference_id(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<common_utils::id_type::PaymentReferenceId>,
) -> impl Responder {
    use crate::db::domain::merchant_context;

    let flow = Flow::PaymentsRetrieveUsingMerchantReferenceId;
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    let merchant_reference_id = path.into_inner();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        (),
        |state, auth: auth::AuthenticationData, _, req_state| async {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            Box::pin(payments::payments_get_intent_using_merchant_reference(
                state,
                merchant_context,
                auth.profile,
                req_state,
                &merchant_reference_id,
                header_payload.clone(),
            ))
            .await
        },
        &auth::V2ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsRedirect, payment_id))]
pub async fn payments_finish_redirection(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: Option<web::Form<serde_json::Value>>,
    path: web::Path<(
        common_utils::id_type::GlobalPaymentId,
        String,
        common_utils::id_type::ProfileId,
    )>,
) -> impl Responder {
    let flow = Flow::PaymentsRedirect;
    let (payment_id, publishable_key, profile_id) = path.into_inner();
    let param_string = req.query_string();

    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    let payload = payments::PaymentsRedirectResponseData {
        payment_id,
        json_payload: json_payload.map(|payload| payload.0),
        query_params: param_string.to_string(),
    };

    let locking_action = payload.get_locking_input(flow.clone());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            <payments::PaymentRedirectSync as PaymentRedirectFlow>::handle_payments_redirect_response(
                &payments::PaymentRedirectSync {},
                state,
                req_state,
                merchant_context,


pub async fn delete_recovery_code_from_redis(
    state: &SessionState,
    user_id: &str,
) -> UserResult<()> {
    let redis_conn = super::get_redis_connection_for_global_tenant(state)?;
    let key = format!("{}{}", consts::user::REDIS_RECOVERY_CODE_PREFIX, user_id);
    redis_conn
        .delete_key(&key.into())
        .await
        .change_context(UserErrors::InternalServerError)
        .map(|_| ())
}


impl PaymentCreate {
    #[cfg(feature = "v2")]
    #[instrument(skip_all)]
    #[allow(clippy::too_many_arguments)]
    pub async fn make_payment_attempt(
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        organization_id: &common_utils::id_type::OrganizationId,
        money: (api::Amount, enums::Currency),
        payment_method: Option<enums::PaymentMethod>,
        payment_method_type: Option<enums::PaymentMethodType>,
        request: &api::PaymentsRequest,
        browser_info: Option<serde_json::Value>,
        state: &SessionState,
        payment_method_billing_address_id: Option<String>,
        payment_method_info: &Option<domain::PaymentMethod>,
        _key_store: &domain::MerchantKeyStore,
        profile_id: common_utils::id_type::ProfileId,
        customer_acceptance: &Option<payments::CustomerAcceptance>,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<(
        storage::PaymentAttemptNew,
        Option<api_models::payments::AdditionalPaymentData>,
    )> {
        todo!()
    }

    #[cfg(feature = "v1")]
    #[instrument(skip_all)]
    #[allow(clippy::too_many_arguments)]
    pub async fn make_payment_attempt(
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
        organization_id: &common_utils::id_type::OrganizationId,
        money: (api::Amount, enums::Currency),
        payment_method: Option<enums::PaymentMethod>,
        payment_method_type: Option<enums::PaymentMethodType>,
        request: &api::PaymentsRequest,
        browser_info: Option<serde_json::Value>,
        state: &SessionState,
        optional_payment_method_billing_address_id: Option<String>,
        payment_method_info: &Option<domain::PaymentMethod>,
        key_store: &domain::MerchantKeyStore,
        profile_id: common_utils::id_type::ProfileId,
        customer_acceptance: &Option<common_payments_types::CustomerAcceptance>,
        storage_scheme: enums::MerchantStorageScheme,
    ) -> RouterResult<(
        storage::PaymentAttemptNew,
        Option<api_models::payments::AdditionalPaymentData>,
    )> {
        let payment_method_data =
            request
                .payment_method_data
                .as_ref()
                .and_then(|payment_method_data_request| {
                    payment_method_data_request.payment_method_data.as_ref()
                });

        let created_at @ modified_at @ last_synced = Some(common_utils::date_time::now());
        let status = helpers::payment_attempt_status_fsm(payment_method_data, request.confirm);
        let (amount, currency) = (money.0, Some(money.1));

        let mut additional_pm_data = request
            .payment_method_data
            .as_ref()
            .and_then(|payment_method_data_request| {
                payment_method_data_request.payment_method_data.clone()
            })
            .async_map(|payment_method_data| async {
                helpers::get_additional_payment_data(
                    &payment_method_data.into(),
                    &*state.store,
                    &profile_id,
                )
                .await
            })
            .await
            .transpose()?
            .flatten();

        if additional_pm_data.is_none() {
            // If recurring payment is made using payment_method_id, then fetch payment_method_data from retrieved payment_method object
            additional_pm_data = payment_method_info.as_ref().and_then(|pm_info| {
                pm_info
                    .payment_method_data
                    .clone()
                    .map(|x| x.into_inner().expose())
                    .and_then(|v| {
                        serde_json::from_value::<PaymentMethodsData>(v)
                            .map_err(|err| {
                                logger::error!(
                                    "Unable to deserialize payment methods data: {:?}",
                                    err
                                )
                            })
                            .ok()
                    })
                    .and_then(|pmd| match pmd {
                        PaymentMethodsData::Card(card) => {
                            Some(api_models::payments::AdditionalPaymentData::Card(Box::new(
                                api::CardDetailFromLocker::from(card).into(),
                            )))
                        }
                        PaymentMethodsData::WalletDetails(wallet) => match payment_method_type {
                            Some(enums::PaymentMethodType::ApplePay) => {
                                Some(api_models::payments::AdditionalPaymentData::Wallet {
                                    apple_pay: api::payments::ApplepayPaymentMethod::try_from(
                                        wallet,
                                    )
                                    .inspect_err(|err| {
                                        logger::error!(
                                            "Unable to transform PaymentMethodDataWalletInfo to ApplepayPaymentMethod: {:?}",
                                            err
                                        )
                                    })
                                    .ok(),
                                    google_pay: None,
                                    samsung_pay: None,
                                })
                            }
                            Some(enums::PaymentMethodType::GooglePay) => {
                                Some(api_models::payments::AdditionalPaymentData::Wallet {
                                    apple_pay: None,
                                    google_pay: Some(wallet.into()),
                                    samsung_pay: None,
                                })
                            }
                            Some(enums::PaymentMethodType::SamsungPay) => {
                                Some(api_models::payments::AdditionalPaymentData::Wallet {
                                    apple_pay: None,
                                    google_pay: None,
                                    samsung_pay: Some(wallet.into()),
                                })
                            }
                            _ => None,
                        },
                        _ => None,
                    })
                    .or_else(|| match payment_method_type {
                        Some(enums::PaymentMethodType::Paypal) => {
                            Some(api_models::payments::AdditionalPaymentData::Wallet {
                                apple_pay: None,
                                google_pay: None,
                                samsung_pay: None,
                            })
                        }
                        _ => None,
                    })
            });
        };

        let additional_pm_data_value = additional_pm_data
            .as_ref()
            .map(Encode::encode_to_value)
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to encode additional pm data")?;
        let attempt_id = if core_utils::is_merchant_enabled_for_payment_id_as_connector_request_id(
            &state.conf,
            merchant_id,
        ) {
            payment_id.get_string_repr().to_owned()
        } else {
            payment_id.get_attempt_id(1)
        };

        if request.mandate_data.as_ref().is_some_and(|mandate_data| {
            mandate_data.update_mandate_id.is_some() && mandate_data.mandate_type.is_some()
        }) {
            Err(errors::ApiErrorResponse::InvalidRequestData {message:"Only one field out of 'mandate_type' and 'update_mandate_id' was expected, found both".to_string()})?
        }

        let mandate_data = if let Some(update_id) = request
            .mandate_data
            .as_ref()
            .and_then(|inner| inner.update_mandate_id.clone())
        {
            let mandate_details = MandateDetails {
                update_mandate_id: Some(update_id),
            };
            Some(mandate_details)
        } else {
            None
        };

        let payment_method_type = Option::<enums::PaymentMethodType>::foreign_from((
            payment_method_type,
            additional_pm_data.as_ref(),
            payment_method,
        ));

        // TODO: remove once https://github.com/juspay/hyperswitch/issues/7421 is fixed
        let payment_method_billing_address_id = match optional_payment_method_billing_address_id {
            None => payment_method_info
                .as_ref()
                .and_then(|pm_info| pm_info.payment_method_billing_address.as_ref())
                .map(|address| {
                    address.clone().deserialize_inner_value(|value| {
                        value.parse_value::<api_models::payments::Address>("Address")
                    })
                })
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)
                .ok()
                .flatten()
                .async_map(|addr| async move {
                    helpers::create_or_find_address_for_payment_by_request(
                        state,
                        Some(addr.get_inner()),
                        None,
                        merchant_id,
                        payment_method_info
                            .as_ref()
                            .map(|pmd_info| pmd_info.customer_id.clone())
                            .as_ref(),
                        key_store,
                        payment_id,
                        storage_scheme,
                    )
                    .await
                })
                .await
                .transpose()
                .change_context(errors::ApiErrorResponse::InternalServerError)?
                .flatten()
                .map(|address| address.address_id),
            address_id => address_id,
        };

        Ok((
            storage::PaymentAttemptNew {
                payment_id: payment_id.to_owned(),
                merchant_id: merchant_id.to_owned(),
                attempt_id,
                status,
                currency,
                payment_method,
                capture_method: request.capture_method,
                capture_on: request.capture_on,
                confirm: request.confirm.unwrap_or(false),
                created_at,
                modified_at,
                last_synced,
                authentication_type: request.authentication_type,
                browser_info,
                payment_experience: request.payment_experience,
                payment_method_type,
                payment_method_data: additional_pm_data_value,
                amount_to_capture: request.amount_to_capture,
                payment_token: request.payment_token.clone(),
                mandate_id: request.mandate_id.clone(),
                business_sub_label: request.business_sub_label.clone(),
                mandate_details: request
                    .mandate_data
                    .as_ref()
                    .and_then(|inner| inner.mandate_type.clone().map(Into::into)),
                external_three_ds_authentication_attempted: None,
                mandate_data,
                payment_method_billing_address_id,
                net_amount: hyperswitch_domain_models::payments::payment_attempt::NetAmount::from_payments_request(
                    request,
                    MinorUnit::from(amount),
                ),
                save_to_locker: None,
                connector: None,
                error_message: None,
                offer_amount: None,
                payment_method_id: payment_method_info
                    .as_ref()
                    .map(|pm_info| pm_info.get_id().clone()),
                cancellation_reason: None,
                error_code: None,
                connector_metadata: None,
                straight_through_algorithm: None,
                preprocessing_step_id: None,
                error_reason: None,
                connector_response_reference_id: None,
                multiple_capture_count: None,
                amount_capturable: MinorUnit::new(i64::default()),
                updated_by: String::default(),
                authentication_data: None,
                encoded_data: None,
                merchant_connector_id: None,
                unified_code: None,
                unified_message: None,
                fingerprint_id: None,
                authentication_connector: None,
                authentication_id: None,
                client_source: None,
                client_version: None,
                customer_acceptance: customer_acceptance
                    .clone()
                    .map(|customer_acceptance| customer_acceptance.encode_to_value())
                    .transpose()
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed to serialize customer_acceptance")?
                    .map(Secret::new),
                organization_id: organization_id.clone(),
                profile_id,
                connector_mandate_detail: None,
                request_extended_authorization: None,
                extended_authorization_applied: None,
                capture_before: None,
                card_discovery: None,
                processor_merchant_id: merchant_id.to_owned(),
                created_by: None,
                setup_future_usage_applied: request.setup_future_usage,
                routing_approach: Some(common_enums::RoutingApproach::default()),
                connector_request_reference_id: None,
                network_transaction_id:None,
            },
            additional_pm_data,

        ))
    }

    #[instrument(skip_all)]
    #[allow(clippy::too_many_arguments)]
    async fn make_payment_intent(
        state: &SessionState,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_context: &domain::MerchantContext,
        money: (api::Amount, enums::Currency),
        request: &api::PaymentsRequest,
        shipping_address_id: Option<String>,
        payment_link_data: Option<api_models::payments::PaymentLinkResponse>,
        billing_address_id: Option<String>,
        active_attempt_id: String,
        profile_id: common_utils::id_type::ProfileId,
        session_expiry: PrimitiveDateTime,
        business_profile: &domain::Profile,
        is_payment_id_from_merchant: bool,
    ) -> RouterResult<storage::PaymentIntent> {
        let created_at @ modified_at @ last_synced = common_utils::date_time::now();

        let status = helpers::payment_intent_status_fsm(
            request
                .payment_method_data
                .as_ref()
                .and_then(|request_payment_method_data| {
                    request_payment_method_data.payment_method_data.as_ref()
                }),
            request.confirm,
        );
        let client_secret = payment_id.generate_client_secret();
        let (amount, currency) = (money.0, Some(money.1));

        let order_details = request
            .get_order_details_as_value()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Failed to convert order details to value")?;

        let allowed_payment_method_types = request
            .get_allowed_payment_method_types_as_value()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error converting allowed_payment_types to Value")?;

        let connector_metadata = request
            .get_connector_metadata_as_value()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error converting connector_metadata to Value")?;

        let feature_metadata = request
            .get_feature_metadata_as_value()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error converting feature_metadata to Value")?;

        let payment_link_id = payment_link_data.map(|pl_data| pl_data.payment_link_id);

        let request_incremental_authorization =
            core_utils::get_request_incremental_authorization_value(
                request.request_incremental_authorization,
                request.capture_method,
            )?;

        let split_payments = request.split_payments.clone();

        // Derivation of directly supplied Customer data in our Payment Create Request
        let raw_customer_details = if request.customer_id.is_none()
            && (request.name.is_some()
                || request.email.is_some()
                || request.phone.is_some()
                || request.phone_country_code.is_some())
        {
            Some(CustomerData {
                name: request.name.clone(),
                phone: request.phone.clone(),
                email: request.email.clone(),
                phone_country_code: request.phone_country_code.clone(),
                tax_registration_id: None,
            })
        } else {
            None
        };
        let is_payment_processor_token_flow = request.recurring_details.as_ref().and_then(
            |recurring_details| match recurring_details {
                RecurringDetails::ProcessorPaymentToken(_) => Some(true),
                _ => None,
            },
        );

        let key = merchant_context
            .get_merchant_key_store()
            .key
            .get_inner()
            .peek();
        let identifier = Identifier::Merchant(
            merchant_context
                .get_merchant_key_store()
                .merchant_id
                .clone(),
        );
        let key_manager_state: KeyManagerState = state.into();

        let shipping_details_encoded = request
            .shipping
            .clone()
            .map(|shipping| Encode::encode_to_value(&shipping).map(Secret::new))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encode billing details to serde_json::Value")?;

        let billing_details_encoded = request
            .billing
            .clone()
            .map(|billing| Encode::encode_to_value(&billing).map(Secret::new))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encode billing details to serde_json::Value")?;

        let customer_details_encoded = raw_customer_details
            .map(|customer| Encode::encode_to_value(&customer).map(Secret::new))
            .transpose()
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encode shipping details to serde_json::Value")?;

        let encrypted_data = domain::types::crypto_operation(
            &key_manager_state,
            type_name!(storage::PaymentIntent),
            domain::types::CryptoOperation::BatchEncrypt(
                FromRequestEncryptablePaymentIntent::to_encryptable(
                    FromRequestEncryptablePaymentIntent {
                        shipping_details: shipping_details_encoded,
                        billing_details: billing_details_encoded,
                        customer_details: customer_details_encoded,
                    },
                ),
            ),
            identifier.clone(),
            key,
        )
        .await
        .and_then(|val| val.try_into_batchoperation())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Unable to encrypt data")?;

        let encrypted_data = FromRequestEncryptablePaymentIntent::from_encryptable(encrypted_data)
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Unable to encrypt the payment intent data")?;

        let skip_external_tax_calculation = request.skip_external_tax_calculation;

        let tax_details = request
            .order_tax_amount
            .map(|tax_amount| diesel_models::TaxDetails {
                default: Some(diesel_models::DefaultTax {
                    order_tax_amount: tax_amount,
                }),
                payment_method_type: None,
            });
        let force_3ds_challenge_trigger = request
            .force_3ds_challenge
            .unwrap_or(business_profile.force_3ds_challenge);

        Ok(storage::PaymentIntent {
            payment_id: payment_id.to_owned(),
            merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
            status,
            amount: MinorUnit::from(amount),
            currency,
            description: request.description.clone(),
            created_at,
            modified_at,
            last_synced: Some(last_synced),
            client_secret: Some(client_secret),
            setup_future_usage: request.setup_future_usage,
            off_session: request.off_session,
            return_url: request.return_url.as_ref().map(|a| a.to_string()),
            shipping_address_id,
            billing_address_id,
            statement_descriptor_name: request.statement_descriptor_name.clone(),
            statement_descriptor_suffix: request.statement_descriptor_suffix.clone(),
            metadata: request.metadata.clone(),
            business_country: request.business_country,
            business_label: request.business_label.clone(),
            active_attempt: hyperswitch_domain_models::RemoteStorageObject::ForeignID(
                active_attempt_id,
            ),
            order_details,
            amount_captured: None,
            customer_id: request.get_customer_id().cloned(),
            connector_id: None,
            allowed_payment_method_types,
            connector_metadata,
            feature_metadata,
            attempt_count: 1,
            profile_id: Some(profile_id),
            merchant_decision: None,
            payment_link_id,
            payment_confirm_source: None,
            surcharge_applicable: None,
            updated_by: merchant_context
                .get_merchant_account()
                .storage_scheme
                .to_string(),
            request_incremental_authorization,
            incremental_authorization_allowed: None,
            authorization_count: None,
            fingerprint_id: None,
            session_expiry: Some(session_expiry),
            request_external_three_ds_authentication: request
                .request_external_three_ds_authentication,
            split_payments,
            frm_metadata: request.frm_metadata.clone(),
            billing_details: encrypted_data.billing_details,
            customer_details: encrypted_data.customer_details,
            merchant_order_reference_id: request.merchant_order_reference_id.clone(),
            shipping_details: encrypted_data.shipping_details,
            is_payment_processor_token_flow,
            organization_id: merchant_context
                .get_merchant_account()
                .organization_id
                .clone(),
            shipping_cost: request.shipping_cost,
            tax_details,
            skip_external_tax_calculation,
            request_extended_authorization: request.request_extended_authorization,
            psd2_sca_exemption_type: request.psd2_sca_exemption_type,
            processor_merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
            created_by: None,
            force_3ds_challenge: request.force_3ds_challenge,
            force_3ds_challenge_trigger: Some(force_3ds_challenge_trigger),
            is_iframe_redirection_enabled: request
                .is_iframe_redirection_enabled
                .or(business_profile.is_iframe_redirection_enabled),
            is_payment_id_from_merchant: Some(is_payment_id_from_merchant),
            payment_channel: request.payment_channel.clone(),
            order_date: request.order_date,
            discount_amount: request.discount_amount,
            duty_amount: request.duty_amount,
            tax_status: request.tax_status,
            shipping_amount_tax: request.shipping_amount_tax,
            enable_partial_authorization: request.enable_partial_authorization,
        })
    }

    #[instrument(skip_all)]
    pub async fn get_ephemeral_key(
        request: &api::PaymentsRequest,
        state: &SessionState,
        merchant_context: &domain::MerchantContext,
    ) -> Option<ephemeral_key::EphemeralKey> {
        match request.get_customer_id() {
            Some(customer_id) => helpers::make_ephemeral_key(
                state.clone(),
                customer_id.clone(),
                merchant_context
                    .get_merchant_account()
                    .get_id()
                    .to_owned()
                    .clone(),
            )
            .await
            .ok()
            .and_then(|ek| {
                if let services::ApplicationResponse::Json(ek) = ek {
                    Some(ek)
                } else {
                    None
                }
            }),
            None => None,
        }
    }
}


pub async fn merchant_account_create(
    state: web::Data<AppState>,
    req: HttpRequest,
    json_payload: web::Json<api_models::admin::MerchantAccountCreateWithoutOrgId>,
) -> HttpResponse {
    let flow = Flow::MerchantsAccountCreate;
    let headers = req.headers();

    let org_id = match auth::HeaderMapStruct::new(headers).get_organization_id_from_header() {
        Ok(org_id) => org_id,
        Err(e) => return api::log_and_return_error_response(e),
    };

    // Converting from MerchantAccountCreateWithoutOrgId to MerchantAccountCreate so we can use the existing
    // `create_merchant_account` function for v2 as well
    let json_payload = json_payload.into_inner();
    let new_request_payload_with_org_id = api_models::admin::MerchantAccountCreate {
        merchant_name: json_payload.merchant_name,
        merchant_details: json_payload.merchant_details,
        metadata: json_payload.metadata,
        organization_id: org_id,
        product_type: json_payload.product_type,
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        new_request_payload_with_org_id,
        |state, _, req, _| create_merchant_account(state, req, None),
        &auth::V2AdminApiAuth,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}


File: crates/router/src/types/api/refunds.rs

#[cfg(feature = "v1")]
pub use api_models::refunds::RefundRequest;
pub use api_models::refunds::{
    RefundListRequest, RefundListResponse, RefundResponse, RefundStatus, RefundType,
    RefundUpdateRequest, RefundsRetrieveBody, RefundsRetrieveRequest,
};
#[cfg(feature = "v2")]
pub use api_models::refunds::{RefundMetadataUpdateRequest, RefundsCreateRequest};
pub use hyperswitch_domain_models::router_flow_types::refunds::{Execute, RSync};
pub use hyperswitch_interfaces::api::refunds::{Refund, RefundExecute, RefundSync};

use crate::types::{storage::enums as storage_enums, transformers::ForeignFrom};

impl ForeignFrom<storage_enums::RefundStatus> for RefundStatus {
    fn foreign_from(status: storage_enums::RefundStatus) -> Self {
        match status {
            storage_enums::RefundStatus::Failure
            | storage_enums::RefundStatus::TransactionFailure => Self::Failed,
            storage_enums::RefundStatus::ManualReview => Self::Review,
            storage_enums::RefundStatus::Pending => Self::Pending,
            storage_enums::RefundStatus::Success => Self::Succeeded,
        }
    }
}


pub struct NetworkTokenDetailsPaymentMethod {
    pub last4_digits: Option<String>,
    pub issuer_country: Option<common_enums::CountryAlpha2>,
    pub network_token_expiry_month: Option<Secret<String>>,
    pub network_token_expiry_year: Option<Secret<String>>,
    pub nick_name: Option<Secret<String>>,
    pub card_holder_name: Option<Secret<String>>,
    pub card_isin: Option<String>,
    pub card_issuer: Option<String>,
    pub card_network: Option<api_enums::CardNetwork>,
    pub card_type: Option<String>,
    #[serde(default = "saved_in_locker_default")]
    pub saved_to_locker: bool,
}


File: crates/router_derive/src/macros/schema.rs
 Public functions: 2

mod helpers;

use quote::quote;

use crate::macros::{
    helpers as macro_helpers,
    schema::helpers::{HasSchemaParameters, IsSchemaFieldApplicableForValidation},
};

pub fn validate_schema_derive(input: syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
    let name = &input.ident;

    // Extract struct fields
    let fields = macro_helpers::get_struct_fields(input.data)
        .map_err(|error| syn::Error::new(proc_macro2::Span::call_site(), error))?;

    // Map over each field
    let validation_checks = fields.iter().filter_map(|field| {
        let field_name = field.ident.as_ref()?;
        let field_type = &field.ty;

        // Check if field type is valid for validation
        let is_field_valid = match IsSchemaFieldApplicableForValidation::from(field_type) {
            IsSchemaFieldApplicableForValidation::Invalid => return None,
            val => val,
        };

        // Parse attribute parameters for 'schema'
        let schema_params = match field.get_schema_parameters() {
            Ok(params) => params,
            Err(_) => return None,
        };

        let min_length = schema_params.min_length;
        let max_length = schema_params.max_length;

        // Skip if no length validation is needed
        if min_length.is_none() && max_length.is_none() {
            return None;
        }

        let min_check = min_length.map(|min_val| {
            quote! {
                if value_len < #min_val {
                    return Err(format!("{} must be at least {} characters long. Received {} characters",
                        stringify!(#field_name), #min_val, value_len));
                }
            }
        }).unwrap_or_else(|| quote! {});

        let max_check = max_length.map(|max_val| {
            quote! {
                if value_len > #max_val {
                    return Err(format!("{} must be at most {} characters long. Received {} characters",
                        stringify!(#field_name), #max_val, value_len));
                }
            }
        }).unwrap_or_else(|| quote! {});

        // Generate length validation
        if is_field_valid == IsSchemaFieldApplicableForValidation::ValidOptional {
            Some(quote! {
                if let Some(value) = &self.#field_name {
                    let value_len = value.as_str().len();
                    #min_check
                    #max_check
                }
            })
        } else {
            Some(quote! {
                {
                    let value_len = self.#field_name.as_str().len();
                    #min_check
                    #max_check
                }
            })
        }
    }).collect::<Vec<_>>();

    Ok(quote! {
        impl #name {
            pub fn validate(&self) -> Result<(), String> {
                #(#validation_checks)*
                Ok(())
            }
        }
    })
}


pub struct TotalEventsResponse {
    /// The list of events
    pub events: Vec<EventListItemResponse>,
    /// Count of total events
    pub total_count: i64,
}


pub struct CreditCardData {
    number: cards::CardNumber,
    expiration_year: Secret<String>,
    expiration_month: Secret<String>,
    cvv: Secret<String>,
    cardholder_name: Secret<String>,
}


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &FloatMajorUnitForConnector,
        }
    }


pub fn sort_and_minify_json(value: &Value) -> Result<String, errors::ConnectorError> {
    fn sort_value(val: &Value) -> Value {
        match val {
            Value::Object(map) => {
                let mut entries: Vec<_> = map.iter().collect();
                entries.sort_by_key(|(k, _)| k.to_owned());

                let sorted_map: Map<String, Value> = entries
                    .into_iter()
                    .map(|(k, v)| (k.clone(), sort_value(v)))
                    .collect();

                Value::Object(sorted_map)
            }
            Value::Array(arr) => Value::Array(arr.iter().map(sort_value).collect()),
            _ => val.clone(),
        }
    }

    let sorted_value = sort_value(value);
    serde_json::to_string(&sorted_value)
        .map_err(|_| errors::ConnectorError::WebhookBodyDecodingFailed)
}


impl api::PaymentSync for Chargebee {}


pub struct UserKeyStoreNew {
    pub user_id: String,
    pub key: Encryption,
    pub created_at: PrimitiveDateTime,
}


pub async fn delete_user_scoped_dashboard_metadata_by_merchant_id_data_key(
        conn: &PgPooledConn,
        user_id: String,
        merchant_id: id_type::MerchantId,
        data_key: enums::DashboardMetadata,
    ) -> StorageResult<Self> {
        generics::generic_delete_one_with_result::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::user_id
                .eq(user_id)
                .and(dsl::merchant_id.eq(merchant_id))
                .and(dsl::data_key.eq(data_key)),
        )
        .await
    }


pub struct EncodedPaymentToken {
    billing_contact: BillingDetails,
    token: ApplepayPaymentData,
}


File: crates/api_models/src/process_tracker.rs

#[cfg(feature = "v2")]
pub mod revenue_recovery;


pub struct StaxWebhookBody {
    #[serde(rename = "type")]
    pub transaction_type: StaxWebhookEventType,
    pub id: String,
    pub auth_id: Option<String>,
    pub success: bool,
}


impl Default for RecordMigrationStatusBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl Default for for RecordMigrationStatusBuilder


File: crates/router/src/types/api/authentication_v2.rs

pub use hyperswitch_domain_models::router_request_types::authentication::MessageCategory;
pub use hyperswitch_interfaces::api::authentication_v2::ExternalAuthenticationV2;


File: crates/common_utils/src/consts.rs
 Public functions: 2

//! Commonly used constants

/// Number of characters in a generated ID
pub const ID_LENGTH: usize = 20;

/// Characters to use for generating NanoID
pub(crate) const ALPHABETS: [char; 62] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
    'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B',
    'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
    'V', 'W', 'X', 'Y', 'Z',
];

/// TTL for token
pub const TOKEN_TTL: i64 = 900;

///an example of the frm_configs json
pub static FRM_CONFIGS_EG: &str = r#"
[{"gateway":"stripe","payment_methods":[{"payment_method":"card","payment_method_types":[{"payment_method_type":"credit","card_networks":["Visa"],"flow":"pre","action":"cancel_txn"},{"payment_method_type":"debit","card_networks":["Visa"],"flow":"pre"}]}]}]
"#;

/// Maximum limit for payments list get api
pub const PAYMENTS_LIST_MAX_LIMIT_V1: u32 = 100;
/// Maximum limit for payments list post api with filters
pub const PAYMENTS_LIST_MAX_LIMIT_V2: u32 = 50;
/// Default limit for payments list API
pub fn default_payments_list_limit() -> u32 {
    10
}

/// Average delay (in seconds) between account onboarding's API response and the changes to actually reflect at Stripe's end
pub const STRIPE_ACCOUNT_ONBOARDING_DELAY_IN_SECONDS: i64 = 15;

/// Maximum limit for payment link list get api
pub const PAYMENTS_LINK_LIST_LIMIT: u32 = 100;

/// Maximum limit for payouts list get api
pub const PAYOUTS_LIST_MAX_LIMIT_GET: u32 = 100;
/// Maximum limit for payouts list post api
pub const PAYOUTS_LIST_MAX_LIMIT_POST: u32 = 20;
/// Default limit for payouts list API
pub fn default_payouts_list_limit() -> u32 {
    10
}

/// surcharge percentage maximum precision length
pub const SURCHARGE_PERCENTAGE_PRECISION_LENGTH: u8 = 2;

/// Header Key for application overhead of a request
pub const X_HS_LATENCY: &str = "x-hs-latency";

/// Redirect url for Prophetpay
pub const PROPHETPAY_REDIRECT_URL: &str = "https://ccm-thirdparty.cps.golf/hp/tokenize/";

/// Variable which store the card token for Prophetpay
pub const PROPHETPAY_TOKEN: &str = "cctoken";

/// Payment intent default client secret expiry (in seconds)
pub const DEFAULT_SESSION_EXPIRY: i64 = 15 * 60;

/// Payment intent fulfillment time (in seconds)
pub const DEFAULT_INTENT_FULFILLMENT_TIME: i64 = 15 * 60;

/// Payment order fulfillment time (in seconds)
pub const DEFAULT_ORDER_FULFILLMENT_TIME: i64 = 15 * 60;

/// Default ttl for Extended card info  in redis (in seconds)
pub const DEFAULT_TTL_FOR_EXTENDED_CARD_INFO: u16 = 15 * 60;

/// Max ttl for Extended card info in redis (in seconds)
pub const MAX_TTL_FOR_EXTENDED_CARD_INFO: u16 = 60 * 60 * 2;

/// Default tenant to be used when multitenancy is disabled
pub const DEFAULT_TENANT: &str = "public";

/// Default tenant to be used when multitenancy is disabled
pub const TENANT_HEADER: &str = "x-tenant-id";

/// Max Length for MerchantReferenceId
pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64;

/// Maximum length allowed for a global id
pub const MIN_GLOBAL_ID_LENGTH: u8 = 32;

/// Minimum length required for a global id
pub const MAX_GLOBAL_ID_LENGTH: u8 = 64;

/// Minimum allowed length for MerchantReferenceId
pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1;

/// Length of a cell identifier in a distributed system
pub const CELL_IDENTIFIER_LENGTH: u8 = 5;

/// General purpose base64 engine
pub const BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD;

/// URL Safe base64 engine
pub const BASE64_ENGINE_URL_SAFE: base64::engine::GeneralPurpose =
    base64::engine::general_purpose::URL_SAFE;
/// Regex for matching a domain
/// Eg -
/// http://www.example.com
/// https://www.example.com
/// www.example.com
/// example.io
pub const STRICT_DOMAIN_REGEX: &str = r"^(https?://)?(([A-Za-z0-9][-A-Za-z0-9]\.)*[A-Za-z0-9][-A-Za-z0-9]*|(\d{1,3}\.){3}\d{1,3})+(:[0-9]{2,4})?$";

/// Regex for matching a wildcard domain
/// Eg -
/// *.example.com
/// *.subdomain.domain.com
/// *://example.com
/// *example.com
pub const WILDCARD_DOMAIN_REGEX: &str = r"^((\*|https?)?://)?((\*\.|[A-Za-z0-9][-A-Za-z0-9]*\.)*[A-Za-z0-9][-A-Za-z0-9]*|((\d{1,3}|\*)\.){3}(\d{1,3}|\*)|\*)(:\*|:[0-9]{2,4})?(/\*)?$";

/// Maximum allowed length for MerchantName
pub const MAX_ALLOWED_MERCHANT_NAME_LENGTH: usize = 64;

/// Default locale
pub const DEFAULT_LOCALE: &str = "en";

/// Role ID for Tenant Admin
pub const ROLE_ID_TENANT_ADMIN: &str = "tenant_admin";
/// Role ID for Org Admin
pub const ROLE_ID_ORGANIZATION_ADMIN: &str = "org_admin";
/// Role ID for Internal View Only
pub const ROLE_ID_INTERNAL_VIEW_ONLY_USER: &str = "internal_view_only";
/// Role ID for Internal Admin
pub const ROLE_ID_INTERNAL_ADMIN: &str = "internal_admin";
/// Role ID for Internal Demo
pub const ROLE_ID_INTERNAL_DEMO: &str = "internal_demo";

/// Max length allowed for Description
pub const MAX_DESCRIPTION_LENGTH: u16 = 255;

/// Max length allowed for Statement Descriptor
pub const MAX_STATEMENT_DESCRIPTOR_LENGTH: u16 = 22;
/// Payout flow identifier used for performing GSM operations
pub const PAYOUT_FLOW_STR: &str = "payout_flow";

/// length of the publishable key
pub const PUBLISHABLE_KEY_LENGTH: u16 = 39;

/// The number of bytes allocated for the hashed connector transaction ID.
/// Total number of characters equals CONNECTOR_TRANSACTION_ID_HASH_BYTES times 2.
pub const CONNECTOR_TRANSACTION_ID_HASH_BYTES: usize = 25;

/// Apple Pay validation url
pub const APPLEPAY_VALIDATION_URL: &str =
    "https://apple-pay-gateway-cert.apple.com/paymentservices/startSession";

/// Request ID
pub const X_REQUEST_ID: &str = "x-request-id";

/// Chat Session ID
pub const X_CHAT_SESSION_ID: &str = "x-chat-session-id";

/// Merchant ID Header
pub const X_MERCHANT_ID: &str = "x-merchant-id";

/// Default Tenant ID for the `Global` tenant
pub const DEFAULT_GLOBAL_TENANT_ID: &str = "global";

/// Default status of Card IP Blocking
pub const DEFAULT_CARD_IP_BLOCKING_STATUS: bool = false;

/// Default Threshold for Card IP Blocking
pub const DEFAULT_CARD_IP_BLOCKING_THRESHOLD: i32 = 3;

/// Default status of Guest User Card Blocking
pub const DEFAULT_GUEST_USER_CARD_BLOCKING_STATUS: bool = false;

/// Default Threshold for Card Blocking for Guest Users
pub const DEFAULT_GUEST_USER_CARD_BLOCKING_THRESHOLD: i32 = 10;

/// Default status of Customer ID Blocking
pub const DEFAULT_CUSTOMER_ID_BLOCKING_STATUS: bool = false;

/// Default Threshold for Customer ID Blocking
pub const DEFAULT_CUSTOMER_ID_BLOCKING_THRESHOLD: i32 = 5;

/// Default Card Testing Guard Redis Expiry in seconds
pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY_IN_SECS: i32 = 3600;

/// SOAP 1.1 Envelope Namespace
pub const SOAP_ENV_NAMESPACE: &str = "http://schemas.xmlsoap.org/soap/envelope/";

#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
/// Length of generated tokens
pub const TOKEN_LENGTH: usize = 32;

/// The tag name used for identifying the host in metrics.
pub const METRICS_HOST_TAG_NAME: &str = "host";

/// API client request timeout (in seconds)
pub const REQUEST_TIME_OUT: u64 = 30;

/// API client request timeout for ai service (in seconds)
pub const REQUEST_TIME_OUT_FOR_AI_SERVICE: u64 = 120;


pub fn get_role_name(self) -> String {
        self.0
    }


pub fn server(state: AppState) -> Scope {
        let mut route = web::scope("/v2/refunds").app_data(web::Data::new(state));

        #[cfg(feature = "olap")]
        {
            route =
                route.service(web::resource("/list").route(web::post().to(refunds::refunds_list)));
        }
        #[cfg(feature = "oltp")]
        {
            route = route
                .service(web::resource("").route(web::post().to(refunds::refunds_create)))
                .service(
                    web::resource("/{id}")
                        .route(web::get().to(refunds::refunds_retrieve))
                        .route(web::post().to(refunds::refunds_retrieve_with_gateway_creds)),
                )
                .service(
                    web::resource("/{id}/update-metadata")
                        .route(web::put().to(refunds::refunds_metadata_update)),
                );
        }

        route
    }


impl ForeignIDRef for PaymentAttempt {
    fn foreign_id(&self) -> String {
        self.attempt_id.clone()
    }
}

impl ForeignIDRef for for PaymentAttempt


pub async fn decide_unified_connector_service_call<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    call_connector_action: CallConnectorAction,
    schedule_time: Option<time::PrimitiveDateTime>,
    header_payload: HeaderPayload,
    frm_suggestion: Option<storage_enums::FrmSuggestion>,
    business_profile: &domain::Profile,
    is_retry_payment: bool,
    should_retry_with_pan: bool,
    return_raw_connector_response: Option<bool>,
    merchant_connector_account_type_details: domain::MerchantConnectorAccountTypeDetails,
    mut router_data: RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    updated_customer: Option<storage::CustomerUpdate>,
) -> RouterResult<RouterData<F, RouterDReq, router_types::PaymentsResponseData>>
where
    F: Send + Clone + Sync,
    RouterDReq: Send + Sync,

    // To create connector flow specific interface data
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    D: ConstructFlowSpecificData<F, RouterDReq, router_types::PaymentsResponseData>,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>: Feature<F, RouterDReq> + Send,
    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, RouterDReq, router_types::PaymentsResponseData>,
{
    record_time_taken_with(|| async {
        if should_call_unified_connector_service(
            state,
            merchant_context,
            &router_data,
            Some(payment_data),
        )
        .await?
        {
            router_env::logger::info!(
                "Executing payment through UCS gateway system - payment_id={}, attempt_id={}",
                payment_data.get_payment_intent().id.get_string_repr(),
                payment_data.get_payment_attempt().id.get_string_repr()
            );
            if should_add_task_to_process_tracker(payment_data) {
                operation
                    .to_domain()?
                    .add_task_to_process_tracker(
                        state,
                        payment_data.get_payment_attempt(),
                        false,
                        schedule_time,
                    )
                    .await
                    .map_err(|error| logger::error!(process_tracker_error=?error))
                    .ok();
            }

            (_, *payment_data) = operation
                .to_update_tracker()?
                .update_trackers(
                    state,
                    req_state,
                    payment_data.clone(),
                    customer.clone(),
                    merchant_context.get_merchant_account().storage_scheme,
                    None,
                    merchant_context.get_merchant_key_store(),
                    frm_suggestion,
                    header_payload.clone(),
                )
                .await?;

            router_data
                .call_unified_connector_service(
                    state,
                    merchant_connector_account_type_details.clone(),
                    merchant_context,
                )
                .await?;

            Ok(router_data)
        } else {
            router_env::logger::info!(
                "Processing payment through Direct gateway system - payment_id={}, attempt_id={}",
                payment_data.get_payment_intent().id.get_string_repr(),
                payment_data.get_payment_attempt().id.get_string_repr()
            );

            call_connector_service(
                state,
                req_state,
                merchant_context,
                connector,
                operation,
                payment_data,
                customer,
                call_connector_action,
                schedule_time,
                header_payload,
                frm_suggestion,
                business_profile,
                is_retry_payment,
                should_retry_with_pan,
                return_raw_connector_response,
                merchant_connector_account_type_details,
                router_data,
                updated_customer,
            )
            .await
        }
    })
    .await
}


pub struct DisputeFiltersResponse {
    pub query_data: Vec<DisputeFilterValue>,
}


pub struct PmCards<'a> {
    pub state: &'a routes::SessionState,
    pub merchant_context: &'a domain::MerchantContext,
}


pub struct PaymentsRequest {
    pub source: PaymentSource,
    pub amount: MinorUnit,
    pub currency: String,
    pub processing_channel_id: Secret<String>,
    #[serde(rename = "3ds")]
    pub three_ds: CheckoutThreeDS,
    #[serde(flatten)]
    pub return_url: ReturnUrl,
    pub capture: bool,
    pub reference: String,
    pub metadata: Option<Secret<serde_json::Value>>,
}


pub async fn update_profile_and_invalidate_routing_config_for_active_algorithm_id_update(
        self,
        db: &dyn StorageInterface,
        key_manager_state: &KeyManagerState,
        merchant_key_store: &domain::MerchantKeyStore,
        algorithm_id: id_type::RoutingId,
        transaction_type: &storage::enums::TransactionType,
    ) -> RouterResult<()> {
        let routing_cache_key = self.clone().get_routing_config_cache_key();

        let (routing_algorithm_id, payout_routing_algorithm_id) = match transaction_type {
            storage::enums::TransactionType::Payment => (Some(algorithm_id), None),
            #[cfg(feature = "payouts")]
            storage::enums::TransactionType::Payout => (None, Some(algorithm_id)),
            //TODO: Handle ThreeDsAuthentication Transaction Type for Three DS Decision Rule Algorithm configuration
            storage::enums::TransactionType::ThreeDsAuthentication => todo!(),
        };

        let profile_update = domain::ProfileUpdate::RoutingAlgorithmUpdate {
            routing_algorithm_id,
            payout_routing_algorithm_id,
        };

        let profile = self.profile;

        db.update_profile_by_profile_id(
            key_manager_state,
            merchant_key_store,
            profile,
            profile_update,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to update routing algorithm ref in business profile")?;

        storage_impl::redis::cache::redact_from_redis_and_publish(
            db.get_cache_store().as_ref(),
            [routing_cache_key],
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to invalidate routing cache")?;
        Ok(())
    }


pub struct IdealRedirectionResponse {
    name: Option<Secret<String>>,
    country_code: Option<enums::CountryAlpha2>,
    bic: Option<Secret<String>>,
    iban_last_chars: Option<Secret<String>>,
}