// File: crates/router/src/routes/payments.rs
// Module: router
// Public functions: 57
// Public structs: 1

use crate::{
    core::api_locking::{self, GetLockingInput},
    services::authorization::permissions::Permission,
};
pub mod helpers;
use actix_web::{web, Responder};
use error_stack::report;
use hyperswitch_domain_models::payments::HeaderPayload;
use masking::PeekInterface;
use router_env::{env, instrument, logger, tracing, types, Flow};

use super::app::ReqState;
#[cfg(feature = "v2")]
use crate::core::gift_card;
#[cfg(feature = "v2")]
use crate::core::revenue_recovery::api as recovery;
use crate::{
    self as app,
    core::{
        errors::{self, http_not_implemented},
        payments::{self, PaymentRedirectFlow},
    },
    routes::lock_utils,
    services::{api, authentication as auth},
    types::{
        api::{
            self as api_types, enums as api_enums,
            payments::{self as payment_types, PaymentIdTypeExt},
        },
        domain,
        transformers::ForeignTryFrom,
    },
};

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCreate, payment_id))]
pub async fn payments_create(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsRequest>,
) -> impl Responder {
    let flow = Flow::PaymentsCreate;
    let mut payload = json_payload.into_inner();
    if let Err(err) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(err.into());
    };

    if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
        return http_not_implemented();
    };

    if let Err(err) = get_or_generate_payment_id(&mut payload) {
        return api::log_and_return_error_response(err);
    }

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

    tracing::Span::current().record(
        "payment_id",
        payload
            .payment_id
            .as_ref()
            .map(|payment_id_type| payment_id_type.get_payment_intent_id())
            .transpose()
            .unwrap_or_default()
            .as_ref()
            .map(|id| id.get_string_repr())
            .unwrap_or_default(),
    );

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

    let auth_type = match env::which() {
        env::Env::Production => {
            &auth::InternalMerchantIdProfileIdAuth(auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: true,
            }))
        }
        _ => auth::auth_type(
            &auth::InternalMerchantIdProfileIdAuth(auth::HeaderAuth(auth::ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: true,
            })),
            &auth::InternalMerchantIdProfileIdAuth(auth::JWTAuth {
                permission: Permission::ProfilePaymentWrite,
            }),
            req.headers(),
        ),
    };

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            authorize_verify_select::<_>(
                payments::PaymentCreate,
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                header_payload.clone(),
                req,
                api::AuthFlow::Client,
            )
        },
        auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v2")]
pub async fn recovery_payments_create(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::RecoveryPaymentsCreate>,
) -> impl Responder {
    let flow = Flow::RecoveryPaymentsCreate;
    let mut payload = json_payload.into_inner();
    Box::pin(api::server_wrap(
        flow,
        state,
        &req.clone(),
        payload,
        |state, auth: auth::AuthenticationData, req_payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            recovery::custom_revenue_recovery_core(
                state.to_owned(),
                req_state,
                merchant_context,
                auth.profile,
                req_payload,
            )
        },
        &auth::V2ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCreateIntent, payment_id))]
pub async fn payments_create_intent(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsCreateIntentRequest>,
) -> impl Responder {
    use hyperswitch_domain_models::payments::PaymentIntentData;

    let flow = Flow::PaymentsCreateIntent;
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };
    let global_payment_id =
        common_utils::id_type::GlobalPaymentId::generate(&state.conf.cell_information.id);

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        json_payload.into_inner(),
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));

            payments::payments_intent_core::<
                api_types::PaymentCreateIntent,
                payment_types::PaymentsIntentResponse,
                _,
                _,
                PaymentIntentData<api_types::PaymentCreateIntent>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                payments::operations::PaymentIntentCreate,
                req,
                global_payment_id.clone(),
                header_payload.clone(),
            )
        },
        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(),
            ),
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

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

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

    let payload = PaymentsGetIntentRequest {
        id: path.into_inner(),
    };

    let global_payment_id = payload.id.clone();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_intent_core::<
                api_types::PaymentGetIntent,
                payment_types::PaymentsIntentResponse,
                _,
                _,
                PaymentIntentData<api_types::PaymentGetIntent>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                payments::operations::PaymentGetIntent,
                req,
                global_payment_id.clone(),
                header_payload.clone(),
            )
        },
        auth::api_or_client_or_jwt_auth(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
                global_payment_id.clone(),
            )),
            &auth::JWTAuth {
                permission: Permission::ProfileRevenueRecoveryRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentAttemptsList, payment_id,))]
pub async fn list_payment_attempts(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<common_utils::id_type::GlobalPaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentAttemptsList;
    let payment_intent_id = path.into_inner();

    let payload = api_models::payments::PaymentAttemptListRequest {
        payment_intent_id: payment_intent_id.clone(),
    };

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload.clone(),
        |session_state, auth, req_payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));

            payments::payments_list_attempts_using_payment_intent_id::<
                payments::operations::PaymentGetListAttempts,
                api_models::payments::PaymentAttemptListResponse,
                api_models::payments::PaymentAttemptListRequest,
                payments::operations::payment_attempt_list::PaymentGetListAttempts,
                hyperswitch_domain_models::payments::PaymentAttemptListData<
                    payments::operations::PaymentGetListAttempts,
                >,
            >(
                session_state,
                req_state,
                merchant_context,
                auth.profile,
                payments::operations::PaymentGetListAttempts,
                payload.clone(),
                req_payload.payment_intent_id,
                header_payload.clone(),
            )
        },
        auth::auth_type(
            &auth::V2ApiKeyAuth {
                is_connected_allowed: false,
                is_platform_allowed: false,
            },
            &auth::JWTAuth {
                permission: Permission::ProfilePaymentRead,
            },
            req.headers(),
        ),
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v2")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCreateAndConfirmIntent, payment_id))]
pub async fn payments_create_and_confirm_intent(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsRequest>,
) -> impl Responder {
    let flow = Flow::PaymentsCreateAndConfirmIntent;
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    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,
        json_payload.into_inner(),
        |state, auth: auth::AuthenticationData, request, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_create_and_confirm_intent(
                state,
                req_state,
                merchant_context,
                auth.profile,
                request,
                header_payload.clone(),
            )
        },
        auth_type,
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

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

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

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

    let global_payment_id = internal_payload.global_payment_id.clone();

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        internal_payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account.clone(), auth.key_store.clone()),
            ));
            payments::payments_intent_core::<
                api_types::PaymentUpdateIntent,
                payment_types::PaymentsIntentResponse,
                _,
                _,
                PaymentIntentData<api_types::PaymentUpdateIntent>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                payments::operations::PaymentUpdateIntent,
                req.payload,
                global_payment_id.clone(),
                header_payload.clone(),
            )
        },
        &auth::V2ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: false,
        },
        api_locking::LockAction::NotApplicable,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip(state, req), fields(flow = ?Flow::PaymentsStart, payment_id))]
pub async fn payments_start(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<(
        common_utils::id_type::PaymentId,
        common_utils::id_type::MerchantId,
        String,
    )>,
) -> impl Responder {
    let flow = Flow::PaymentsStart;
    let (payment_id, merchant_id, attempt_id) = path.into_inner();
    let payload = payment_types::PaymentsStartRequest {
        payment_id: payment_id.clone(),
        merchant_id: merchant_id.clone(),
        attempt_id: attempt_id.clone(),
    };

    let locking_action = payload.get_locking_input(flow.clone());
    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Authorize,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Authorize>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::operations::PaymentStart,
                req,
                api::AuthFlow::Client,
                payments::CallConnectorAction::Trigger,
                None,
                HeaderPayload::default(),
            )
        },
        &auth::MerchantIdAuth(merchant_id),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip(state, req), fields(flow, payment_id))]
pub async fn payments_retrieve(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<common_utils::id_type::PaymentId>,
    json_payload: web::Query<payment_types::PaymentRetrieveBody>,
) -> impl Responder {
    let flow = match json_payload.force_sync {
        Some(true) => Flow::PaymentsRetrieveForceSync,
        _ => Flow::PaymentsRetrieve,
    };
    let payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    let payload = payment_types::PaymentsRetrieveRequest {
        resource_id: payment_types::PaymentIdType::PaymentIntentId(payment_id),
        merchant_id: json_payload.merchant_id.clone(),
        force_sync: json_payload.force_sync.unwrap_or(false),
        client_secret: json_payload.client_secret.clone(),
        expand_attempts: json_payload.expand_attempts,
        expand_captures: json_payload.expand_captures,
        all_keys_required: json_payload.all_keys_required,
        ..Default::default()
    };
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(err) => {
            return api::log_and_return_error_response(err);
        }
    };

    tracing::Span::current().record("flow", flow.to_string());

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: true,
    };

    let (auth_type, auth_flow) = match auth::check_internal_api_key_auth(
        req.headers(),
        &payload,
        api_auth,
        state.conf.internal_merchant_id_profile_id_auth.clone(),
    ) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::PSync,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentStatus,
                req,
                auth_flow,
                payments::CallConnectorAction::Trigger,
                None,
                header_payload.clone(),
            )
        },
        auth::auth_type(
            &*auth_type,
            &auth::JWTAuth {
                permission: Permission::ProfilePaymentRead,
            },
            req.headers(),
        ),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip(state, req), fields(flow, payment_id))]
pub async fn payments_retrieve_with_gateway_creds(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentRetrieveBodyWithCredentials>,
) -> impl Responder {
    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: true,
    };

    let (auth_type, _auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

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

    let payload = payment_types::PaymentsRetrieveRequest {
        resource_id: payment_types::PaymentIdType::PaymentIntentId(json_payload.payment_id.clone()),
        merchant_id: json_payload.merchant_id.clone(),
        force_sync: json_payload.force_sync.unwrap_or(false),
        merchant_connector_details: json_payload.merchant_connector_details.clone(),
        ..Default::default()
    };

    let flow = match json_payload.force_sync {
        Some(true) => Flow::PaymentsRetrieveForceSync,
        _ => Flow::PaymentsRetrieve,
    };

    tracing::Span::current().record("flow", flow.to_string());

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::PSync,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::PSync>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentStatus,
                req,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                HeaderPayload::default(),
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsUpdate, payment_id))]
pub async fn payments_update(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentsUpdate;
    let mut payload = json_payload.into_inner();
    if let Err(err) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(err.into());
    };

    if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
        return http_not_implemented();
    };

    let payment_id = path.into_inner();

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

    payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id));
    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: true,
    };
    let (auth_type, auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
        Ok(auth) => auth,
        Err(err) => return api::log_and_return_error_response(report!(err)),
    };

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            authorize_verify_select::<_>(
                payments::PaymentUpdate,
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                HeaderPayload::default(),
                req,
                auth_flow,
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsPostSessionTokens, payment_id))]
pub async fn payments_post_session_tokens(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsPostSessionTokensRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentsPostSessionTokens;

    let payment_id = path.into_inner();
    let payload = payment_types::PaymentsPostSessionTokensRequest {
        payment_id,
        ..json_payload.into_inner()
    };
    tracing::Span::current().record("payment_id", payload.payment_id.get_string_repr());
    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 = 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::payments_core::<
                api_types::PostSessionTokens,
                payment_types::PaymentsPostSessionTokensResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::PostSessionTokens>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentPostSessionTokens,
                req,
                api::AuthFlow::Client,
                payments::CallConnectorAction::Trigger,
                None,
                header_payload.clone(),
            )
        },
        &auth::PublishableKeyAuth,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsUpdateMetadata, payment_id))]
pub async fn payments_update_metadata(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsUpdateMetadataRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentsUpdateMetadata;

    let payment_id = path.into_inner();
    let payload = payment_types::PaymentsUpdateMetadataRequest {
        payment_id,
        ..json_payload.into_inner()
    };
    tracing::Span::current().record("payment_id", payload.payment_id.get_string_repr());
    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 = 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::payments_core::<
                api_types::UpdateMetadata,
                payment_types::PaymentsUpdateMetadataResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::UpdateMetadata>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentUpdateMetadata,
                req,
                api::AuthFlow::Client,
                payments::CallConnectorAction::Trigger,
                None,
                header_payload.clone(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: true,
        }),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsConfirm, payment_id))]
pub async fn payments_confirm(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::PaymentsConfirm;
    let mut payload = json_payload.into_inner();
    if let Err(err) = payload
        .validate()
        .map_err(|message| errors::ApiErrorResponse::InvalidRequestData { message })
    {
        return api::log_and_return_error_response(err.into());
    };

    if let Some(api_enums::CaptureMethod::Scheduled) = payload.capture_method {
        return http_not_implemented();
    };

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

    if let Err(err) = helpers::populate_browser_info(&req, &mut payload, &header_payload) {
        return api::log_and_return_error_response(err);
    }

    let payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", payment_id.get_string_repr());
    payload.payment_id = Some(payment_types::PaymentIdType::PaymentIntentId(payment_id));
    payload.confirm = Some(true);

    let api_auth = auth::ApiKeyAuth {
        is_connected_allowed: false,
        is_platform_allowed: true,
    };

    let (auth_type, auth_flow) =
        match auth::check_client_secret_and_get_auth(req.headers(), &payload, api_auth) {
            Ok(auth) => auth,
            Err(e) => return api::log_and_return_error_response(e),
        };

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, req, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            authorize_verify_select::<_>(
                payments::PaymentConfirm,
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                header_payload.clone(),
                req,
                auth_flow,
            )
        },
        &*auth_type,
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsCapture, payment_id))]
pub async fn payments_capture(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsCaptureRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let payment_id = path.into_inner();
    tracing::Span::current().record("payment_id", payment_id.get_string_repr());

    let flow = Flow::PaymentsCapture;
    let payload = payment_types::PaymentsCaptureRequest {
        payment_id,
        ..json_payload.into_inner()
    };

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Capture,
                payment_types::PaymentsResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Capture>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentCapture,
                payload,
                api::AuthFlow::Merchant,
                payments::CallConnectorAction::Trigger,
                None,
                HeaderPayload::default(),
            )
        },
        &auth::HeaderAuth(auth::ApiKeyAuth {
            is_connected_allowed: false,
            is_platform_allowed: true,
        }),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::SessionUpdateTaxCalculation, payment_id))]
pub async fn payments_dynamic_tax_calculation(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsDynamicTaxCalculationRequest>,
    path: web::Path<common_utils::id_type::PaymentId>,
) -> impl Responder {
    let flow = Flow::SessionUpdateTaxCalculation;
    let payment_id = path.into_inner();
    let payload = payment_types::PaymentsDynamicTaxCalculationRequest {
        payment_id,
        ..json_payload.into_inner()
    };
    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(error) => {
            logger::error!(
                ?error,
                "Failed to get headers in payments_connector_session"
            );
            HeaderPayload::default()
        }
    };
    tracing::Span::current().record("payment_id", payload.payment_id.get_string_repr());
    let locking_action = payload.get_locking_input(flow.clone());
    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::SdkSessionUpdate,
                payment_types::PaymentsDynamicTaxCalculationResponse,
                _,
                _,
                _,
                _,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentSessionUpdate,
                payload,
                api::AuthFlow::Client,
                payments::CallConnectorAction::Trigger,
                None,
                header_payload.clone(),
            )
        },
        &auth::PublishableKeyAuth,
        locking_action,
    ))
    .await
}

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

    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| {
            let payment_id = req.global_payment_id;
            let request = req.payload;
            let operation = payments::operations::PaymentSessionIntent;
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_session_core::<
                api_types::Session,
                payment_types::PaymentsSessionResponse,
                _,
                _,
                _,
                PaymentIntentData<api_types::Session>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile,
                operation,
                request,
                payment_id,
                payments::CallConnectorAction::Trigger,
                header_payload.clone(),
            )
        },
        &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
            global_payment_id,
        )),
        locking_action,
    ))
    .await
}

#[cfg(feature = "v1")]
#[instrument(skip_all, fields(flow = ?Flow::PaymentsSessionToken, payment_id))]
pub async fn payments_connector_session(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: web::Json<payment_types::PaymentsSessionRequest>,
) -> impl Responder {
    let flow = Flow::PaymentsSessionToken;
    let payload = json_payload.into_inner();

    let header_payload = match HeaderPayload::foreign_try_from(req.headers()) {
        Ok(headers) => headers,
        Err(error) => {
            logger::error!(
                ?error,
                "Failed to get headers in payments_connector_session"
            );
            HeaderPayload::default()
        }
    };

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

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

    Box::pin(api::server_wrap(
        flow,
        state,
        &req,
        payload,
        |state, auth: auth::AuthenticationData, payload, req_state| {
            let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
                domain::Context(auth.merchant_account, auth.key_store),
            ));
            payments::payments_core::<
                api_types::Session,
                payment_types::PaymentsSessionResponse,
                _,
                _,
                _,
                payments::PaymentData<api_types::Session>,
            >(
                state,
                req_state,
                merchant_context,
                auth.profile_id,
                payments::PaymentSession,
                payload,


// Struct: CybersourceAuthenticationErrorResponse
// File: crates/hyperswitch_connectors/src/connectors/cybersource/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CybersourceAuthenticationErrorResponse


// Struct: BraintreeRefundInput
// File: crates/hyperswitch_connectors/src/connectors/braintree/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BraintreeRefundInput


// Function: get_pm_authentication_processor_config
// File: crates/euclid_wasm/src/lib.rs
// Module: euclid_wasm

pub fn get_pm_authentication_processor_config(key: &str) -> JsResult


// Struct: RecoveryDeciderClientConfig
// File: crates/external_services/src/grpc_client/revenue_recovery/recovery_decider_client.rs
// Module: external_services
// Implementations: 1

pub struct RecoveryDeciderClientConfig


// Struct: DashboardPaymentMethodPayload
// File: crates/connector_configs/src/common_config.rs
// Module: connector_configs
// Implementations: 0

pub struct DashboardPaymentMethodPayload


// Implementation: impl ExtractedPayload
// File: crates/router/src/services/authentication/detached.rs
// Module: router
// Methods: 3 total (2 public)

impl ExtractedPayload


// Function: set_protocol_version
// File: crates/hyperswitch_connectors/src/connectors/redsys/transformers.rs
// Module: hyperswitch_connectors

pub fn set_protocol_version(mut self, protocol_version: String) -> Self


// Struct: SetupMandate
// File: crates/hyperswitch_domain_models/src/router_flow_types/payments.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct SetupMandate


// Struct: MultisafepayPaymentsResponse
// File: crates/hyperswitch_connectors/src/connectors/multisafepay/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MultisafepayPaymentsResponse


// File: crates/hyperswitch_connectors/src/connectors/itaubank/transformers.rs
// Module: hyperswitch_connectors
// Public structs: 18

use api_models::payments::QrCodeInformation;
use common_enums::enums;
use common_utils::{errors::CustomResult, ext_traits::Encode, types::StringMajorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    payment_method_data::{BankTransferData, PaymentMethodData},
    router_data::{AccessToken, ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::ResponseId,
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types,
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use url::Url;

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{get_timestamp_in_milliseconds, QrImage, RouterData as _},
};

pub struct ItaubankRouterData<T> {
    pub amount: StringMajorUnit,
    pub router_data: T,
}

impl<T> From<(StringMajorUnit, T)> for ItaubankRouterData<T> {
    fn from((amount, item): (StringMajorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Default, Debug, Serialize)]
pub struct ItaubankPaymentsRequest {
    valor: PixPaymentValue,  // amount
    chave: Secret<String>,   // pix-key
    devedor: ItaubankDebtor, // debtor
}

#[derive(Default, Debug, Serialize)]
pub struct PixPaymentValue {
    original: StringMajorUnit,
}

#[derive(Default, Debug, Serialize)]
pub struct ItaubankDebtor {
    #[serde(skip_serializing_if = "Option::is_none")]
    cpf: Option<Secret<String>>, // CPF is a Brazilian tax identification number
    #[serde(skip_serializing_if = "Option::is_none")]
    cnpj: Option<Secret<String>>, // CNPJ is a Brazilian company tax identification number
    #[serde(skip_serializing_if = "Option::is_none")]
    nome: Option<Secret<String>>, // name of the debtor
}

impl TryFrom<&ItaubankRouterData<&types::PaymentsAuthorizeRouterData>> for ItaubankPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ItaubankRouterData<&types::PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::BankTransfer(bank_transfer_data) => {
                match *bank_transfer_data {
                    BankTransferData::Pix {
                        pix_key, cpf, cnpj, ..
                    } => {
                        let nome = item.router_data.get_optional_billing_full_name();
                        // cpf and cnpj are mutually exclusive
                        let devedor = match (cnpj, cpf) {
                            (Some(cnpj), _) => ItaubankDebtor {
                                cpf: None,
                                cnpj: Some(cnpj),
                                nome,
                            },
                            (None, Some(cpf)) => ItaubankDebtor {
                                cpf: Some(cpf),
                                cnpj: None,
                                nome,
                            },
                            _ => Err(errors::ConnectorError::MissingRequiredField {
                                field_name: "cpf and cnpj both missing in payment_method_data",
                            })?,
                        };
                        Ok(Self {
                            valor: PixPaymentValue {
                                original: item.amount.to_owned(),
                            },
                            chave: pix_key.ok_or(errors::ConnectorError::MissingRequiredField {
                                field_name: "pix_key",
                            })?,
                            devedor,
                        })
                    }
                    BankTransferData::AchBankTransfer {}
                    | BankTransferData::SepaBankTransfer {}
                    | BankTransferData::BacsBankTransfer {}
                    | BankTransferData::MultibancoBankTransfer {}
                    | BankTransferData::PermataBankTransfer {}
                    | BankTransferData::BcaBankTransfer {}
                    | BankTransferData::BniVaBankTransfer {}
                    | BankTransferData::BriVaBankTransfer {}
                    | BankTransferData::CimbVaBankTransfer {}
                    | BankTransferData::DanamonVaBankTransfer {}
                    | BankTransferData::MandiriVaBankTransfer {}
                    | BankTransferData::Pse {}
                    | BankTransferData::InstantBankTransfer {}
                    | BankTransferData::InstantBankTransferFinland {}
                    | BankTransferData::InstantBankTransferPoland {}
                    | BankTransferData::IndonesianBankTransfer { .. }
                    | BankTransferData::LocalBankTransfer { .. } => {
                        Err(errors::ConnectorError::NotImplemented(
                            "Selected payment method through itaubank".to_string(),
                        )
                        .into())
                    }
                }
            }
            PaymentMethodData::Card(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::NetworkToken(_)
            | PaymentMethodData::CardDetailsForNetworkTransactionId(_) => {
                Err(errors::ConnectorError::NotImplemented(
                    "Selected payment method through itaubank".to_string(),
                )
                .into())
            }
        }
    }
}

pub struct ItaubankAuthType {
    pub(super) client_id: Secret<String>,
    pub(super) client_secret: Secret<String>,
    pub(super) certificate: Option<Secret<String>>,
    pub(super) certificate_key: Option<Secret<String>>,
}

impl TryFrom<&ConnectorAuthType> for ItaubankAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::MultiAuthKey {
                api_key,
                key1,
                api_secret,
                key2,
            } => Ok(Self {
                client_secret: api_key.to_owned(),
                client_id: key1.to_owned(),
                certificate: Some(api_secret.to_owned()),
                certificate_key: Some(key2.to_owned()),
            }),
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                client_secret: api_key.to_owned(),
                client_id: key1.to_owned(),
                certificate: None,
                certificate_key: None,
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Debug, Serialize)]
pub struct ItaubankAuthRequest {
    client_id: Secret<String>,
    client_secret: Secret<String>,
    grant_type: ItaubankGrantType,
}

#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ItaubankGrantType {
    ClientCredentials,
}

impl TryFrom<&types::RefreshTokenRouterData> for ItaubankAuthRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &types::RefreshTokenRouterData) -> Result<Self, Self::Error> {
        let auth_details = ItaubankAuthType::try_from(&item.connector_auth_type)?;

        Ok(Self {
            client_id: auth_details.client_id,
            client_secret: auth_details.client_secret,
            grant_type: ItaubankGrantType::ClientCredentials,
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ItaubankUpdateTokenResponse {
    access_token: Secret<String>,
    expires_in: i64,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ItaubankTokenErrorResponse {
    pub status: i64,
    pub title: Option<String>,
    pub detail: Option<String>,
    pub user_message: Option<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, ItaubankUpdateTokenResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, ItaubankUpdateTokenResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(AccessToken {
                token: item.response.access_token,
                expires: item.response.expires_in,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ItaubankPaymentStatus {
    Ativa,                        // Active
    Concluida,                    // Completed
    RemovidaPeloPsp,              // Removed by PSP
    RemovidaPeloUsuarioRecebedor, // Removed by receiving User
}

impl From<ItaubankPaymentStatus> for enums::AttemptStatus {
    fn from(item: ItaubankPaymentStatus) -> Self {
        match item {
            ItaubankPaymentStatus::Ativa => Self::AuthenticationPending,
            ItaubankPaymentStatus::Concluida => Self::Charged,
            ItaubankPaymentStatus::RemovidaPeloPsp
            | ItaubankPaymentStatus::RemovidaPeloUsuarioRecebedor => Self::Failure,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ItaubankPaymentsResponse {
    status: ItaubankPaymentStatus,
    calendario: ItaubankPixExpireTime,
    txid: String,
    #[serde(rename = "pixCopiaECola")]
    pix_qr_value: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ItaubankPixExpireTime {
    #[serde(with = "common_utils::custom_serde::iso8601")]
    criacao: PrimitiveDateTime,
    expiracao: i64,
}

impl<F, T> TryFrom<ResponseRouterData<F, ItaubankPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, ItaubankPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let connector_metadata = get_qr_code_data(&item.response)?;
        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.txid.to_owned()),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.txid),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

fn get_qr_code_data(
    response: &ItaubankPaymentsResponse,
) -> CustomResult<Option<serde_json::Value>, errors::ConnectorError> {
    let creation_time = get_timestamp_in_milliseconds(&response.calendario.criacao);
    // convert expiration to milliseconds and add to creation time
    let expiration_time = creation_time + (response.calendario.expiracao * 1000);

    let image_data = QrImage::new_from_data(response.pix_qr_value.clone())
        .change_context(errors::ConnectorError::ResponseHandlingFailed)?;

    let image_data_url = Url::parse(image_data.data.clone().as_str())
        .change_context(errors::ConnectorError::ResponseHandlingFailed)?;

    let qr_code_info = QrCodeInformation::QrDataUrl {
        image_data_url,
        display_to_timestamp: Some(expiration_time),
    };

    Some(qr_code_info.encode_to_value())
        .transpose()
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ItaubankPaymentsSyncResponse {
    status: ItaubankPaymentStatus,
    txid: String,
    pix: Vec<ItaubankPixResponse>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItaubankPixResponse {
    #[serde(rename = "endToEndId")]
    pix_id: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ItaubankMetaData {
    pub pix_id: Option<String>,
}

impl<F, T> TryFrom<ResponseRouterData<F, ItaubankPaymentsSyncResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, ItaubankPaymentsSyncResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let pix_data = item
            .response
            .pix
            .first()
            .ok_or(errors::ConnectorError::MissingRequiredField {
                field_name: "pix_id",
            })?
            .to_owned();

        let connector_metadata = Some(serde_json::json!(ItaubankMetaData {
            pix_id: pix_data.pix_id
        }));

        Ok(Self {
            status: enums::AttemptStatus::from(item.response.status),
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(item.response.txid.to_owned()),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.txid),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize)]
pub struct ItaubankRefundRequest {
    pub valor: StringMajorUnit, // refund_amount
}

impl<F> TryFrom<&ItaubankRouterData<&types::RefundsRouterData<F>>> for ItaubankRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &ItaubankRouterData<&types::RefundsRouterData<F>>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            valor: item.amount.to_owned(),
        })
    }
}

#[allow(dead_code)]
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RefundStatus {
    EmProcessamento, // Processing
    Devolvido,       // Returned
    NaoRealizado,    // Unrealized
}

impl From<RefundStatus> for enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Devolvido => Self::Success,
            RefundStatus::NaoRealizado => Self::Failure,
            RefundStatus::EmProcessamento => Self::Pending,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RefundResponse {
    rtr_id: String,
    status: RefundStatus,
}

impl TryFrom<RefundsResponseRouterData<Execute, RefundResponse>>
    for types::RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.rtr_id,
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for types::RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, RefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.rtr_id.to_string(),
                refund_status: enums::RefundStatus::from(item.response.status),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ItaubankErrorResponse {
    pub error: ItaubankErrorBody,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ItaubankErrorBody {
    pub status: u16,
    pub title: Option<String>,
    pub detail: Option<String>,
    pub violacoes: Option<Vec<Violations>>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Violations {
    pub razao: String,
    pub propriedade: String,
    pub valor: String,
}


// Implementation: impl api::PaymentSync for for Adyen
// File: crates/hyperswitch_connectors/src/connectors/adyen.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Adyen


// Function: get_connector_wallets_details
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models

pub fn get_connector_wallets_details(&self) -> Option<Secret<Value>>


// Function: merchant_account_delete
// File: crates/router/src/core/admin.rs
// Module: router

pub fn merchant_account_delete(
    state: SessionState,
    merchant_id: id_type::MerchantId,
) -> RouterResponse<api::MerchantAccountDeleteResponse>


// Struct: MerchantId
// File: crates/router/src/types/domain/user.rs
// Module: router
// Implementations: 1

pub struct MerchantId


// Function: find_metadata_by_algorithm_id_profile_id
// File: crates/diesel_models/src/query/routing_algorithm.rs
// Module: diesel_models

pub fn find_metadata_by_algorithm_id_profile_id(
        conn: &PgPooledConn,
        algorithm_id: &common_utils::id_type::RoutingId,
        profile_id: &common_utils::id_type::ProfileId,
    ) -> StorageResult<RoutingProfileMetadata>


// Implementation: impl RedisTokenManager
// File: crates/router/src/types/storage/revenue_recovery_redis_operation.rs
// Module: router
// Methods: 21 total (4 public)

impl RedisTokenManager


// Struct: ZenCustomerDetails
// File: crates/hyperswitch_connectors/src/connectors/zen/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ZenCustomerDetails


// Struct: BillingDetails
// File: crates/hyperswitch_connectors/src/connectors/bluesnap/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct BillingDetails


// Struct: CardNetworkTokenResponse
// File: crates/router/src/types/payment_methods.rs
// Module: router
// Implementations: 0

pub struct CardNetworkTokenResponse


// Function: construct_payload
// File: crates/analytics/src/opensearch.rs
// Module: analytics

// Documentation: Ensure that the input data and the structure of the query are valid and correctly handled.

pub fn construct_payload(&self, indexes: &[SearchIndex]) -> QueryResult<Vec<Value>>


// Function: set_error
// File: crates/hyperswitch_interfaces/src/events/connector_api_logs.rs
// Module: hyperswitch_interfaces

// Documentation: fn set_error

pub fn set_error(&mut self, error: serde_json::Value)