// File: crates/hyperswitch_connectors/src/connectors/tesouro.rs
// Module: hyperswitch_connectors
// Public functions: 1
// Public structs: 1

pub mod transformers;

use std::sync::LazyLock;

use common_enums::enums;
use common_utils::{
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentsResponseData, RefundsResponseData, SupportedPaymentMethods,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{self, Response},
    webhooks,
};
use masking::{ExposeInterface, Mask};
use transformers as tesouro;

use crate::{constants::headers, types::ResponseRouterData, utils};

#[derive(Clone)]
pub struct Tesouro {
    amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}

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

impl api::Payment for Tesouro {}
impl api::PaymentSession for Tesouro {}
impl api::ConnectorAccessToken for Tesouro {}
impl api::MandateSetup for Tesouro {}
impl api::PaymentAuthorize for Tesouro {}
impl api::PaymentSync for Tesouro {}
impl api::PaymentCapture for Tesouro {}
impl api::PaymentVoid for Tesouro {}
impl api::Refund for Tesouro {}
impl api::RefundExecute for Tesouro {}
impl api::RefundSync for Tesouro {}
impl api::PaymentToken for Tesouro {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Tesouro
{
    // Not Implemented (R)
}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Tesouro
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

impl ConnectorCommon for Tesouro {
    fn id(&self) -> &'static str {
        "tesouro"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
        //    TODO! Check connector documentation, on which unit they are processing the currency.
        //    If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor,
        //    if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base
    }

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.tesouro.base_url.as_ref()
    }

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth = tesouro::TesouroAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::AUTHORIZATION.to_string(),
            auth.api_key.expose().into_masked(),
        )])
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: tesouro::TesouroErrorResponse = res
            .response
            .parse_struct("TesouroErrorResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.code,
            message: response.message,
            reason: response.reason,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Tesouro {
    fn validate_mandate_payment(
        &self,
        _pm_type: Option<enums::PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        match pm_data {
            PaymentMethodData::Card(_) => Err(errors::ConnectorError::NotImplemented(
                "validate_mandate_payment does not support cards".to_string(),
            )
            .into()),
            _ => Ok(()),
        }
    }

    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), errors::ConnectorError> {
        Ok(())
    }
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Tesouro {
    //TODO: implement sessions flow
}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Tesouro {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Tesouro {}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Tesouro {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = tesouro::TesouroRouterData::from((amount, req));
        let connector_req = tesouro::TesouroPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(types::PaymentsAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: tesouro::TesouroPaymentsResponse = res
            .response
            .parse_struct("Tesouro PaymentsAuthorizeResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Tesouro {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: tesouro::TesouroPaymentsResponse = res
            .response
            .parse_struct("tesouro PaymentsSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Tesouro {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::PaymentsCaptureType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(types::PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, errors::ConnectorError> {
        let response: tesouro::TesouroPaymentsResponse = res
            .response
            .parse_struct("Tesouro PaymentsCaptureResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Tesouro {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Tesouro {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let refund_amount = utils::convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data = tesouro::TesouroRouterData::from((refund_amount, req));
        let connector_req = tesouro::TesouroRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        let request = RequestBuilder::new()
            .method(Method::Post)
            .url(&types::RefundExecuteType::get_url(self, req, connectors)?)
            .attach_default_headers()
            .headers(types::RefundExecuteType::get_headers(
                self, req, connectors,
            )?)
            .set_body(types::RefundExecuteType::get_request_body(
                self, req, connectors,
            )?)
            .build();
        Ok(Some(request))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: tesouro::RefundResponse = res
            .response
            .parse_struct("tesouro RefundResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Tesouro {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &RefundSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
    }

    fn build_request(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&types::RefundSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(types::RefundSyncType::get_headers(self, req, connectors)?)
                .set_body(types::RefundSyncType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: tesouro::RefundResponse = res
            .response
            .parse_struct("tesouro RefundSyncResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Tesouro {
    fn get_webhook_object_reference_id(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}

static TESOURO_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(SupportedPaymentMethods::new);

static TESOURO_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Tesouro",
    description: "Tesouro connects software companies and banks in one unified ecosystem, simplifying payments",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: enums::ConnectorIntegrationStatus::Sandbox,
};

static TESOURO_SUPPORTED_WEBHOOK_FLOWS: [enums::EventClass; 0] = [];

impl ConnectorSpecifications for Tesouro {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&TESOURO_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*TESOURO_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&TESOURO_SUPPORTED_WEBHOOK_FLOWS)
    }
}


// Struct: RefundListFilters
// File: crates/api_models/src/refunds.rs
// Module: api_models
// Implementations: 0

pub struct RefundListFilters


// Implementation: impl std::error::Error for for InvalidCrmConfig
// File: crates/external_services/src/crm.rs
// Module: external_services
// Methods: 0 total (0 public)

impl std::error::Error for for InvalidCrmConfig


// Function: get_pending_captures_without_connector_capture_id
// File: crates/router/src/core/payments/types.rs
// Module: router

pub fn get_pending_captures_without_connector_capture_id(&self) -> Vec<&storage::Capture>


// Struct: CheckoutWebhookBody
// File: crates/hyperswitch_connectors/src/connectors/checkout/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CheckoutWebhookBody


// Function: get_dispute_status
// File: crates/hyperswitch_connectors/src/connectors/worldpayvantiv/transformers.rs
// Module: hyperswitch_connectors

pub fn get_dispute_status(
    dispute_cycle: String,
    dispute_activities: Vec<Activity>,
) -> Result<common_enums::DisputeStatus, error_stack::Report<errors::ConnectorError>>


// Struct: RevenueRecoveryMetadata
// File: crates/hyperswitch_domain_models/src/merchant_connector_account.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct RevenueRecoveryMetadata


// Function: validate
// File: crates/router/src/configs/validations.rs
// Module: router

pub fn validate(&self) -> Result<(), ApplicationError>


// Implementation: impl api::PaymentSession for for Bluecode
// File: crates/hyperswitch_connectors/src/connectors/bluecode.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Bluecode


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

pub struct BraintreeRouterData<T>


// File: crates/router/src/core/tokenization.rs
// Module: router
// Public functions: 3

#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use actix_web::{web, HttpRequest, HttpResponse};
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use api_models;
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use common_enums::enums;
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use common_utils::{
    crypto::{DecodeMessage, EncodeMessage, GcmAes256},
    errors::CustomResult,
    ext_traits::{BytesExt, Encode, StringExt},
    fp_utils::when,
    id_type,
};
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use error_stack::ResultExt;
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use hyperswitch_domain_models;
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use router_env::{instrument, logger, tracing, Flow};
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use serde::Serialize;

#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
use crate::{
    core::{
        errors::{self, RouterResponse, RouterResult},
        payment_methods::vault as pm_vault,
    },
    db::errors::StorageErrorExt,
    routes::{app::StorageInterface, AppState, SessionState},
    services::{self, api as api_service, authentication as auth},
    types::{api, domain, payment_methods as pm_types},
};

#[instrument(skip_all)]
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
pub async fn create_vault_token_core(
    state: SessionState,
    merchant_account: &domain::MerchantAccount,
    merchant_key_store: &domain::MerchantKeyStore,
    req: api_models::tokenization::GenericTokenizationRequest,
) -> RouterResponse<api_models::tokenization::GenericTokenizationResponse> {
    // Generate a unique vault ID
    let vault_id = domain::VaultId::generate(uuid::Uuid::now_v7().to_string());
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();
    let customer_id = req.customer_id.clone();
    // Create vault request
    let payload = pm_types::AddVaultRequest {
        entity_id: req.customer_id.to_owned(),
        vault_id: vault_id.clone(),
        data: req.token_request.clone(),
        ttl: state.conf.locker.ttl_for_storage_in_secs,
    }
    .encode_to_vec()
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to encode Request")?;

    // Call the vault service
    let resp = pm_vault::call_to_vault::<pm_types::AddVault>(&state, payload.clone())
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Call to vault failed")?;

    // Parse the response
    let stored_resp: pm_types::AddVaultResponse = resp
        .parse_struct("AddVaultResponse")
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to parse data into AddVaultResponse")?;

    // Create new tokenization record
    let tokenization_new = hyperswitch_domain_models::tokenization::Tokenization {
        id: id_type::GlobalTokenId::generate(&state.conf.cell_information.id),
        merchant_id: merchant_account.get_id().clone(),
        customer_id: customer_id.clone(),
        locker_id: stored_resp.vault_id.get_string_repr().to_string(),
        created_at: common_utils::date_time::now(),
        updated_at: common_utils::date_time::now(),
        flag: enums::TokenizationFlag::Enabled,
        version: enums::ApiVersion::V2,
    };

    // Insert into database
    let tokenization = db
        .insert_tokenization(
            tokenization_new,
            &(merchant_key_store.clone()),
            key_manager_state,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to insert tokenization record")?;

    // Convert to TokenizationResponse
    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        api_models::tokenization::GenericTokenizationResponse {
            id: tokenization.id,
            created_at: tokenization.created_at,
            flag: tokenization.flag,
        },
    ))
}

#[cfg(feature = "v2")]
#[instrument(skip_all)]
pub async fn delete_tokenized_data_core(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    token_id: &id_type::GlobalTokenId,
    payload: api_models::tokenization::DeleteTokenDataRequest,
) -> RouterResponse<api_models::tokenization::DeleteTokenDataResponse> {
    let db = &*state.store;
    let key_manager_state = &(&state).into();

    // Retrieve the tokenization record
    let tokenization_record = db
        .get_entity_id_vault_id_by_token_id(
            token_id,
            merchant_context.get_merchant_key_store(),
            key_manager_state,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::TokenizationRecordNotFound {
            id: token_id.get_string_repr().to_string(),
        })
        .attach_printable("Failed to get tokenization record")?;

    when(
        tokenization_record.customer_id != payload.customer_id,
        || {
            Err(errors::ApiErrorResponse::UnprocessableEntity {
                message: "Tokenization record does not belong to the customer".to_string(),
            })
        },
    )?;

    when(tokenization_record.is_disabled(), || {
        Err(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Tokenization is already disabled for the id".to_string(),
        })
    })?;

    //fetch locker id
    let vault_id = domain::VaultId::generate(tokenization_record.locker_id.clone());
    //delete card from vault
    pm_vault::delete_payment_method_data_from_vault_internal(
        &state,
        &merchant_context,
        vault_id,
        &tokenization_record.customer_id,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to delete payment method from vault")?;

    //update the status with Disabled
    let tokenization_update = hyperswitch_domain_models::tokenization::TokenizationUpdate::DeleteTokenizationRecordUpdate {
        flag: Some(enums::TokenizationFlag::Disabled),
    };
    db.update_tokenization_record(
        tokenization_record,
        tokenization_update,
        merchant_context.get_merchant_key_store(),
        key_manager_state,
    )
    .await
    .change_context(errors::ApiErrorResponse::InternalServerError)
    .attach_printable("Failed to update tokenization record")?;

    Ok(hyperswitch_domain_models::api::ApplicationResponse::Json(
        api_models::tokenization::DeleteTokenDataResponse {
            id: token_id.clone(),
        },
    ))
}

#[instrument(skip_all)]
#[cfg(all(feature = "v2", feature = "tokenization_v2"))]
pub async fn get_token_vault_core(
    state: SessionState,
    merchant_account: &domain::MerchantAccount,
    merchant_key_store: &domain::MerchantKeyStore,
    query: id_type::GlobalTokenId,
) -> CustomResult<serde_json::Value, errors::ApiErrorResponse> {
    let db = state.store.as_ref();
    let key_manager_state = &(&state).into();

    let tokenization_record = db
        .get_entity_id_vault_id_by_token_id(
            &query,
            &(merchant_key_store.clone()),
            key_manager_state,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to get tokenization record")?;

    if tokenization_record.flag == enums::TokenizationFlag::Disabled {
        return Err(errors::ApiErrorResponse::GenericNotFoundError {
            message: "Tokenization is disabled for the id".to_string(),
        }
        .into());
    }

    let vault_request = pm_types::VaultRetrieveRequest {
        entity_id: tokenization_record.customer_id.clone(),
        vault_id: hyperswitch_domain_models::payment_methods::VaultId::generate(
            tokenization_record.locker_id.clone(),
        ),
    };

    let vault_data = pm_vault::retrieve_value_from_vault(&state, vault_request)
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Failed to retrieve vault data")?;

    let data_json = vault_data
        .get("data")
        .cloned()
        .unwrap_or(serde_json::Value::Null);

    // Create the response
    Ok(data_json)
}


// File: crates/router/src/routes/files/transformers.rs
// Module: router
// Public functions: 1

use actix_multipart::Multipart;
use actix_web::web::Bytes;
use common_utils::errors::CustomResult;
use error_stack::ResultExt;
use futures::{StreamExt, TryStreamExt};

use crate::{
    core::{errors, files::helpers},
    types::api::files::{self, CreateFileRequest},
    utils::OptionExt,
};

pub async fn get_create_file_request(
    mut payload: Multipart,
) -> CustomResult<CreateFileRequest, errors::ApiErrorResponse> {
    let mut option_purpose: Option<files::FilePurpose> = None;
    let mut dispute_id: Option<String> = None;

    let mut file_name: Option<String> = None;
    let mut file_content: Option<Vec<Bytes>> = None;

    while let Ok(Some(mut field)) = payload.try_next().await {
        let content_disposition = field.content_disposition();
        let field_name = content_disposition.get_name();
        // Parse the different parameters expected in the multipart request
        match field_name {
            Some("purpose") => {
                option_purpose = helpers::get_file_purpose(&mut field).await;
            }
            Some("file") => {
                file_name = content_disposition.get_filename().map(String::from);

                //Collect the file content and throw error if something fails
                let mut file_data = Vec::new();
                let mut stream = field.into_stream();
                while let Some(chunk) = stream.next().await {
                    match chunk {
                        Ok(bytes) => file_data.push(bytes),
                        Err(err) => Err(errors::ApiErrorResponse::InternalServerError)
                            .attach_printable(format!("{}{}", "File parsing error: ", err))?,
                    }
                }
                file_content = Some(file_data)
            }
            Some("dispute_id") => {
                dispute_id = helpers::read_string(&mut field).await;
            }
            // Can ignore other params
            _ => (),
        }
    }
    let purpose = option_purpose.get_required_value("purpose")?;
    let file = match file_content {
        Some(valid_file_content) => valid_file_content.concat().to_vec(),
        None => Err(errors::ApiErrorResponse::MissingFile)
            .attach_printable("Missing / Invalid file in the request")?,
    };
    //Get and validate file size
    let file_size = i32::try_from(file.len())
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("File size error")?;
    // Check if empty file and throw error
    if file_size <= 0 {
        Err(errors::ApiErrorResponse::MissingFile)
            .attach_printable("Missing / Invalid file in the request")?
    }
    // Get file mime type using 'infer'
    let kind = infer::get(&file).ok_or(errors::ApiErrorResponse::MissingFileContentType)?;
    let file_type = kind
        .mime_type()
        .parse::<mime::Mime>()
        .change_context(errors::ApiErrorResponse::MissingFileContentType)
        .attach_printable("File content type error")?;
    Ok(CreateFileRequest {
        file,
        file_name,
        file_size,
        file_type,
        purpose,
        dispute_id,
    })
}


// Struct: HubspotRequest
// File: crates/external_services/src/hubspot_proxy.rs
// Module: external_services
// Implementations: 1

pub struct HubspotRequest


// Implementation: impl DebitRoutingDecisionData
// File: crates/router/src/core/routing/helpers.rs
// Module: router
// Methods: 1 total (1 public)

impl DebitRoutingDecisionData


// Implementation: impl api::Payment for for Inespay
// File: crates/hyperswitch_connectors/src/connectors/inespay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Inespay


// Struct: PaypalSourceVerificationResponse
// File: crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaypalSourceVerificationResponse


// Struct: WorldpayWebhookTransactionId
// File: crates/hyperswitch_connectors/src/connectors/worldpay/response.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct WorldpayWebhookTransactionId


// Implementation: impl AuthenticationType
// File: crates/router/src/services/authentication.rs
// Module: router
// Methods: 1 total (1 public)

impl AuthenticationType


// Struct: PaymentLinkConfigRequest
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct PaymentLinkConfigRequest


// Struct: CardDetailsForNetworkTransactionId
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 1

pub struct CardDetailsForNetworkTransactionId


// Struct: OvercaptureEnabledBool
// File: crates/common_types/src/primitive_wrappers.rs
// Module: common_types
// Implementations: 3
// Traits: Default, Deref

pub struct OvercaptureEnabledBool


// Implementation: impl Responder
// File: crates/router/src/routes/routing.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Implementation: impl api::RefundExecute for for Noon
// File: crates/hyperswitch_connectors/src/connectors/noon.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Noon


// Struct: PayoutListParams
// File: crates/hyperswitch_domain_models/src/payouts.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct PayoutListParams


                .parse_value::<payment_types::GpaySessionTokenData>("GpaySessionTokenData")
                .change_context(errors::ConnectorError::NoConnectorMetaData)
                .attach_printable(format!(
                    "cannot parse gpay metadata from the given value {connector_metadata:?}"
                ))
                .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                    field_name: "connector_metadata".to_string(),
                    expected_format: "gpay_metadata_format".to_string(),
                })?;

            let gpay_allowed_payment_methods = gpay_data
                .data
                .allowed_payment_methods
                .into_iter()
                .map(
                    |allowed_payment_methods| payment_types::GpayAllowedPaymentMethods {
                        parameters: payment_types::GpayAllowedMethodsParameters {
                            billing_address_required: Some(is_billing_details_required),
                            billing_address_parameters: billing_address_parameters.clone(),
                            ..allowed_payment_methods.parameters
                        },
                        ..allowed_payment_methods
                    },
                )
                .collect();

            Ok(types::PaymentsSessionRouterData {
                response: Ok(types::PaymentsResponseData::SessionResponse {
                    session_token: payment_types::SessionToken::GooglePay(Box::new(
                        payment_types::GpaySessionTokenResponse::GooglePaySession(
                            payment_types::GooglePaySessionResponse {
                                merchant_info: gpay_data.data.merchant_info,
                                allowed_payment_methods: gpay_allowed_payment_methods,
                                transaction_info,
                                connector: connector.connector_name.to_string(),
                                sdk_next_action: payment_types::SdkNextAction {
                                    next_action: payment_types::NextActionCall::Confirm,
                                },
                                delayed_session_token: false,
                                secrets: None,
                                shipping_address_required: required_shipping_contact_fields,
                                // We pass Email as a required field irrespective of
                                // collect_billing_details_from_wallet_connector or
                                // collect_shipping_details_from_wallet_connector as it is common to both.
                                email_required: required_shipping_contact_fields
                                    || is_billing_details_required,
                                shipping_address_parameters:
                                    api_models::payments::GpayShippingAddressParameters {
                                        phone_number_required: required_shipping_contact_fields,
                                    },
                            },
                        ),
                    )),
                }),
                ..router_data.clone()
            })
        }
    }
}

/// Card Type for Google Pay Allowerd Payment Methods
pub(crate) const CARD: &str = "CARD";

fn get_allowed_payment_methods_from_cards(
    gpay_info: payment_types::GooglePayWalletDetails,
    gpay_token_specific_data: &payment_types::GooglePayTokenizationSpecification,
    is_billing_details_required: bool,
) -> RouterResult<payment_types::GpayAllowedPaymentMethods> {
    let billing_address_parameters =
        is_billing_details_required.then_some(payment_types::GpayBillingAddressParameters {
            phone_number_required: is_billing_details_required,
            format: payment_types::GpayBillingAddressFormat::FULL,
        });

    let protocol_version: Option<String> = gpay_token_specific_data
        .parameters
        .public_key
        .as_ref()
        .map(|_| PROTOCOL.to_string());

    Ok(payment_types::GpayAllowedPaymentMethods {
        parameters: payment_types::GpayAllowedMethodsParameters {
            billing_address_required: Some(is_billing_details_required),
            billing_address_parameters: billing_address_parameters.clone(),
            ..gpay_info.google_pay.cards
        },
        payment_method_type: CARD.to_string(),
        tokenization_specification: payment_types::GpayTokenizationSpecification {
            token_specification_type: gpay_token_specific_data.tokenization_type.to_string(),
            parameters: payment_types::GpayTokenParameters {
                protocol_version,
                public_key: gpay_token_specific_data.parameters.public_key.clone(),
                gateway: gpay_token_specific_data.parameters.gateway.clone(),
                gateway_merchant_id: gpay_token_specific_data
                    .parameters
                    .gateway_merchant_id
                    .clone()
                    .expose_option(),
                stripe_publishable_key: gpay_token_specific_data
                    .parameters
                    .stripe_publishable_key
                    .clone()
                    .expose_option(),
                stripe_version: gpay_token_specific_data
                    .parameters
                    .stripe_version
                    .clone()
                    .expose_option(),
            },
        },
    })
}

fn is_session_response_delayed(
    state: &routes::SessionState,
    connector: &api::ConnectorData,
) -> bool {
    let connectors_with_delayed_response = &state
        .conf
        .delayed_session_response
        .connectors_with_delayed_session_response;

    connectors_with_delayed_response.contains(&connector.connector_name)
}

fn log_session_response_if_error(
    response: &Result<Result<types::Response, types::Response>, Report<errors::ApiClientError>>,
) {
    if let Err(error) = response.as_ref() {
        logger::error!(?error);
    };
    response
        .as_ref()
        .ok()
        .map(|res| res.as_ref().map_err(|error| logger::error!(?error)));
}

#[async_trait]
pub trait RouterDataSession
where
    Self: Sized,
{
    async fn decide_flow<'a, 'b>(
        &'b self,
        state: &'a routes::SessionState,
        connector: &api::ConnectorData,
        _confirm: Option<bool>,
        call_connector_action: payments::CallConnectorAction,
        business_profile: &domain::Profile,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<Self>;
}

fn create_paypal_sdk_session_token(
    _state: &routes::SessionState,
    router_data: &types::PaymentsSessionRouterData,
    connector: &api::ConnectorData,
    _business_profile: &domain::Profile,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let connector_metadata = router_data.connector_meta_data.clone();

    let paypal_sdk_data = connector_metadata
        .clone()
        .parse_value::<payment_types::PaypalSdkSessionTokenData>("PaypalSdkSessionTokenData")
        .change_context(errors::ConnectorError::NoConnectorMetaData)
        .attach_printable(format!(
            "cannot parse paypal_sdk metadata from the given value {connector_metadata:?}"
        ))
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "connector_metadata".to_string(),
            expected_format: "paypal_sdk_metadata_format".to_string(),
        })?;

    Ok(types::PaymentsSessionRouterData {
        response: Ok(types::PaymentsResponseData::SessionResponse {
            session_token: payment_types::SessionToken::Paypal(Box::new(
                payment_types::PaypalSessionTokenResponse {
                    connector: connector.connector_name.to_string(),
                    session_token: paypal_sdk_data.data.client_id,
                    sdk_next_action: payment_types::SdkNextAction {
                        next_action: payment_types::NextActionCall::PostSessionTokens,
                    },
                },
            )),
        }),
        ..router_data.clone()
    })
}

async fn create_amazon_pay_session_token(
    router_data: &types::PaymentsSessionRouterData,
    state: &routes::SessionState,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let amazon_pay_session_token_data = router_data
        .connector_wallets_details
        .clone()
        .parse_value::<payment_types::AmazonPaySessionTokenData>("AmazonPaySessionTokenData")
        .change_context(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "merchant_id or store_id",
        })?;
    let amazon_pay_metadata = amazon_pay_session_token_data.data;
    let merchant_id = amazon_pay_metadata.merchant_id;
    let store_id = amazon_pay_metadata.store_id;
    let amazonpay_supported_currencies =
        payments::cards::list_countries_currencies_for_connector_payment_method_util(
            state.conf.pm_filters.clone(),
            enums::Connector::Amazonpay,
            enums::PaymentMethodType::AmazonPay,
        )
        .await
        .currencies;
    // currently supports only the US region hence USD is the only supported currency
    payment_types::AmazonPayDeliveryOptions::validate_currency(
        router_data.request.currency,
        amazonpay_supported_currencies.clone(),
    )
    .change_context(errors::ApiErrorResponse::CurrencyNotSupported {
        message: "USD is the only supported currency.".to_string(),
    })?;
    let ledger_currency = router_data.request.currency;
    // currently supports only the 'automatic' capture_method
    let payment_intent = payment_types::AmazonPayPaymentIntent::AuthorizeWithCapture;
    let required_amount_type = StringMajorUnitForConnector;
    let total_tax_amount = required_amount_type
        .convert(
            router_data.request.order_tax_amount.unwrap_or_default(),
            router_data.request.currency,
        )
        .change_context(errors::ApiErrorResponse::AmountConversionFailed {
            amount_type: "StringMajorUnit",
        })?;
    let total_base_amount = required_amount_type
        .convert(
            router_data.request.minor_amount,
            router_data.request.currency,
        )
        .change_context(errors::ApiErrorResponse::AmountConversionFailed {
            amount_type: "StringMajorUnit",
        })?;

    let delivery_options_request = router_data
        .request
        .metadata
        .clone()
        .and_then(|metadata| {
            metadata
                .expose()
                .get("delivery_options")
                .and_then(|value| value.as_array().cloned())
        })
        .ok_or(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "metadata.delivery_options",
        })?;

    let mut delivery_options =
        payment_types::AmazonPayDeliveryOptions::parse_delivery_options_request(
            &delivery_options_request,
        )
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "delivery_options".to_string(),
            expected_format: r#""delivery_options": [{"id": String, "price": {"amount": Number, "currency_code": String}, "shipping_method":{"shipping_method_name": String, "shipping_method_code": String}, "is_default": Boolean}]"#.to_string(),
        })?;

    let default_amount = payment_types::AmazonPayDeliveryOptions::get_default_delivery_amount(
        delivery_options.clone(),
    )
    .change_context(errors::ApiErrorResponse::InvalidDataValue {
        field_name: "is_default",
    })?;

    for option in &delivery_options {
        payment_types::AmazonPayDeliveryOptions::validate_currency(
            option.price.currency_code,
            amazonpay_supported_currencies.clone(),
        )
        .change_context(errors::ApiErrorResponse::CurrencyNotSupported {
            message: "USD is the only supported currency.".to_string(),
        })?;
    }

    payment_types::AmazonPayDeliveryOptions::insert_display_amount(
        &mut delivery_options,
        router_data.request.currency,
    )
    .change_context(errors::ApiErrorResponse::AmountConversionFailed {
        amount_type: "StringMajorUnit",
    })?;

    let total_shipping_amount = match router_data.request.shipping_cost {
        Some(shipping_cost) => {
            if shipping_cost == default_amount {
                required_amount_type
                    .convert(shipping_cost, router_data.request.currency)
                    .change_context(errors::ApiErrorResponse::AmountConversionFailed {
                        amount_type: "StringMajorUnit",
                    })?
            } else {
                return Err(errors::ApiErrorResponse::InvalidDataValue {
                    field_name: "shipping_cost",
                })
                .attach_printable(format!(
                    "Provided shipping_cost ({shipping_cost}) does not match the default delivery amount ({default_amount})"
                ));
            }
        }
        None => {
            return Err(errors::ApiErrorResponse::MissingRequiredField {
                field_name: "shipping_cost",
            }
            .into());
        }
    };

    Ok(types::PaymentsSessionRouterData {
        response: Ok(types::PaymentsResponseData::SessionResponse {
            session_token: payment_types::SessionToken::AmazonPay(Box::new(
                payment_types::AmazonPaySessionTokenResponse {
                    merchant_id,
                    ledger_currency,
                    store_id,
                    payment_intent,
                    total_shipping_amount,
                    total_tax_amount,
                    total_base_amount,
                    delivery_options,
                },
            )),
        }),
        ..router_data.clone()
    })
}

#[async_trait]
impl RouterDataSession for types::PaymentsSessionRouterData {
    async fn decide_flow<'a, 'b>(
        &'b self,
        state: &'a routes::SessionState,
        connector: &api::ConnectorData,
        _confirm: Option<bool>,
        call_connector_action: payments::CallConnectorAction,
        business_profile: &domain::Profile,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<Self> {
        match connector.get_token {
            api::GetToken::GpayMetadata => {
                create_gpay_session_token(state, self, connector, business_profile)
            }
            api::GetToken::SamsungPayMetadata => create_samsung_pay_session_token(
                state,
                self,
                header_payload,
                connector,
                business_profile,
            ),
            api::GetToken::ApplePayMetadata => {
                create_applepay_session_token(
                    state,
                    self,
                    connector,
                    business_profile,
                    header_payload,
                )
                .await
            }
            api::GetToken::PaypalSdkMetadata => {
                create_paypal_sdk_session_token(state, self, connector, business_profile)
            }
            api::GetToken::PazeMetadata => create_paze_session_token(self, header_payload),
            api::GetToken::Connector => {
                let connector_integration: services::BoxedPaymentConnectorIntegrationInterface<
                    api::Session,
                    types::PaymentsSessionData,
                    types::PaymentsResponseData,
                > = connector.connector.get_connector_integration();
                let resp = services::execute_connector_processing_step(
                    state,
                    connector_integration,
                    self,
                    call_connector_action,
                    None,
                    None,
                )
                .await
                .to_payment_failed_response()?;

                Ok(resp)
            }
            api::GetToken::AmazonPayMetadata => create_amazon_pay_session_token(self, state).await,
        }
    }
}


// Struct: PeachpaymentsAuthType
// File: crates/hyperswitch_connectors/src/connectors/peachpayments/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PeachpaymentsAuthType


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

pub struct PanMetadataUpdateBody


// Function: new
// File: crates/router_env/src/logger/formatter.rs
// Module: router_env

// Documentation: ```

pub fn new(
        service: &str,
        dst_writer: W,
        formatter: F,
    ) -> error_stack::Result<Self, ConfigError>


// Struct: Name
// File: crates/hyperswitch_connectors/src/connectors/affirm/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Name


// Function: generate_org_payment_report
// File: crates/router/src/analytics.rs
// Module: router

pub fn generate_org_payment_report(
        state: web::Data<AppState>,
        req: actix_web::HttpRequest,
        json_payload: web::Json<ReportRequest>,
    ) -> impl Responder


// Implementation: impl Customers
// File: crates/router/src/compatibility/stripe/app.rs
// Module: router
// Methods: 1 total (1 public)

impl Customers


// Function: get_error_reason
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors

pub fn get_error_reason(
    error_info: Option<String>,
    detailed_error_info: Option<String>,
    avs_error_info: Option<String>,
) -> Option<String>


// Struct: PayoutCreateRequest
// File: crates/api_models/src/payouts.rs
// Module: api_models
// Implementations: 1

pub struct PayoutCreateRequest


// Function: internal_trigger_refund_to_gateway
// File: crates/router/src/core/refunds_v2.rs
// Module: router

pub fn internal_trigger_refund_to_gateway(
    state: &SessionState,
    refund: &diesel_refund::Refund,
    merchant_context: &domain::MerchantContext,
    payment_attempt: &storage::PaymentAttempt,
    payment_intent: &storage::PaymentIntent,
    merchant_connector_details: common_types::domain::MerchantConnectorAuthDetails,
) -> errors::RouterResult<diesel_refund::Refund>


// Struct: SantanderPixPSyncResponse
// File: crates/hyperswitch_connectors/src/connectors/santander/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SantanderPixPSyncResponse


// Implementation: impl api::MandateSetup for for Wellsfargo
// File: crates/hyperswitch_connectors/src/connectors/wellsfargo.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::MandateSetup for for Wellsfargo


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

impl api::PaymentSync for for Bitpay


// Implementation: impl api::ConnectorAccessToken for for Gpayments
// File: crates/hyperswitch_connectors/src/connectors/gpayments.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::ConnectorAccessToken for for Gpayments


// Implementation: impl api::PaymentSession for for Razorpay
// File: crates/hyperswitch_connectors/src/connectors/razorpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSession for for Razorpay


// Struct: ExternalAuthenticationPayload
// File: crates/hyperswitch_interfaces/src/authentication.rs
// Module: hyperswitch_interfaces
// Implementations: 0

pub struct ExternalAuthenticationPayload


// File: crates/common_utils/src/hashing.rs
// Module: common_utils
// Public structs: 1

use masking::{PeekInterface, Secret, Strategy};
use serde::{Deserialize, Serialize, Serializer};

#[derive(Clone, PartialEq, Debug, Deserialize)]
/// Represents a hashed string using blake3's hashing strategy.
pub struct HashedString<T: Strategy<String>>(Secret<String, T>);

impl<T: Strategy<String>> Serialize for HashedString<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let hashed_value = blake3::hash(self.0.peek().as_bytes()).to_hex();
        hashed_value.serialize(serializer)
    }
}

impl<T: Strategy<String>> From<Secret<String, T>> for HashedString<T> {
    fn from(value: Secret<String, T>) -> Self {
        Self(value)
    }
}


// Module Structure
// File: crates/hyperswitch_connectors/src/connectors/finix.rs
// Module: hyperswitch_connectors

// Public submodules:
pub mod transformers;


// Struct: EliminationRoutingPayloadWrapper
// File: crates/api_models/src/routing.rs
// Module: api_models
// Implementations: 0

pub struct EliminationRoutingPayloadWrapper


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

pub struct CybersourceProcessingInfo


// Struct: NetworkTokenDetails
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct NetworkTokenDetails


// Implementation: impl api::Payment for for Fiserv
// File: crates/hyperswitch_connectors/src/connectors/fiserv.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Fiserv


// Function: find_by_merchant_id_fingerprint_id
// File: crates/diesel_models/src/query/blocklist.rs
// Module: diesel_models

pub fn find_by_merchant_id_fingerprint_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        fingerprint_id: &str,
    ) -> StorageResult<Self>


// File: crates/api_models/src/lib.rs
// Module: api_models

pub mod admin;
pub mod analytics;
pub mod api_keys;
pub mod apple_pay_certificates_migration;
pub mod authentication;
pub mod blocklist;
pub mod cards_info;
pub mod chat;
pub mod conditional_configs;
pub mod connector_enums;
pub mod connector_onboarding;
pub mod consts;
pub mod currency;
pub mod customers;
pub mod disputes;
pub mod enums;
pub mod ephemeral_key;
#[cfg(feature = "errors")]
pub mod errors;
pub mod events;
pub mod external_service_auth;
pub mod feature_matrix;
pub mod files;
pub mod gsm;
pub mod health_check;
pub mod locker_migration;
pub mod mandates;
pub mod open_router;
pub mod organization;
pub mod payment_methods;
pub mod payments;
#[cfg(feature = "payouts")]
pub mod payouts;
pub mod pm_auth;
pub mod poll;
pub mod process_tracker;
pub mod profile_acquirer;
#[cfg(feature = "v2")]
pub mod proxy;
#[cfg(feature = "recon")]
pub mod recon;
pub mod refunds;
pub mod relay;
#[cfg(feature = "v2")]
pub mod revenue_recovery_data_backfill;
pub mod routing;
#[cfg(feature = "v1")]
pub mod subscription;
pub mod surcharge_decision_configs;
pub mod three_ds_decision_rule;
#[cfg(feature = "tokenization_v2")]
pub mod tokenization;
pub mod user;
pub mod user_role;
pub mod verifications;
pub mod verify_connector;
pub mod webhook_events;
pub mod webhooks;

pub trait ValidateFieldAndGet<Request> {
    fn validate_field_and_get(
        &self,
        request: &Request,
    ) -> common_utils::errors::CustomResult<Self, common_utils::errors::ValidationError>
    where
        Self: Sized;
}


// File: crates/test_utils/tests/connectors/noon_ui.rs
// Module: test_utils

use serial_test::serial;
use thirtyfour::{prelude::*, WebDriver};

use crate::{selenium::*, tester};

struct NoonSeleniumTest;

impl SeleniumTest for NoonSeleniumTest {
    fn get_connector_name(&self) -> String {
        "noon".to_string()
    }
}

async fn should_make_noon_3ds_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = NoonSeleniumTest {};
    conn.make_redirection_payment(
        web_driver,
        vec![
            Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/176"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Trigger(Trigger::SwitchFrame(By::Id("redirectTo3ds1Frame"))),
            Event::Trigger(Trigger::SwitchFrame(By::Css("iframe[frameborder='0']"))),
            Event::Trigger(Trigger::SendKeys(By::Css("input.input-field"), "1234")),
            Event::Trigger(Trigger::Click(By::Css("input.button.primary"))),
            Event::Trigger(Trigger::Sleep(5)),
            Event::Assert(Assert::IsPresent("succeeded")),
        ],
    )
    .await?;
    Ok(())
}

async fn should_make_noon_3ds_mandate_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = NoonSeleniumTest {};
    conn.make_redirection_payment(
        web_driver,
        vec![
            Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/214"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Trigger(Trigger::SwitchFrame(By::Id("redirectTo3ds1Frame"))),
            Event::Trigger(Trigger::SwitchFrame(By::Css("iframe[frameborder='0']"))),
            Event::Trigger(Trigger::SendKeys(By::Css("input.input-field"), "1234")),
            Event::Trigger(Trigger::Click(By::Css("input.button.primary"))),
            Event::Trigger(Trigger::Sleep(5)),
            Event::Assert(Assert::IsPresent("succeeded")),
            Event::Assert(Assert::IsPresent("Mandate ID")),
            Event::Assert(Assert::IsPresent("man_")), // mandate id starting with man_
            Event::Trigger(Trigger::Click(By::Css("a.btn"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Assert(Assert::IsPresent("succeeded")),
        ],
    )
    .await?;
    Ok(())
}

async fn should_make_noon_non_3ds_mandate_payment(
    web_driver: WebDriver,
) -> Result<(), WebDriverError> {
    let conn = NoonSeleniumTest {};
    conn.make_redirection_payment(
        web_driver,
        vec![
            Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/215"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Assert(Assert::IsPresent("succeeded")),
            Event::Assert(Assert::IsPresent("Mandate ID")),
            Event::Assert(Assert::IsPresent("man_")), // mandate id starting with man_
            Event::Trigger(Trigger::Click(By::Css("a.btn"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Assert(Assert::IsPresent("succeeded")),
        ],
    )
    .await?;
    Ok(())
}

#[test]
#[serial]
fn should_make_noon_3ds_payment_test() {
    tester!(should_make_noon_3ds_payment);
}

#[test]
#[serial]
fn should_make_noon_3ds_mandate_payment_test() {
    tester!(should_make_noon_3ds_mandate_payment);
}

#[test]
#[serial]
fn should_make_noon_non_3ds_mandate_payment_test() {
    tester!(should_make_noon_non_3ds_mandate_payment);
}


// Implementation: impl api::PaymentAuthorize for for Nomupay
// File: crates/hyperswitch_connectors/src/connectors/nomupay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Nomupay


// Struct: RedirectUrl
// File: crates/router/src/compatibility/stripe/setup_intents/types.rs
// Module: router
// Implementations: 0

pub struct RedirectUrl


// Struct: DebitRoutingResult
// File: crates/router/src/core/debit_routing.rs
// Module: router
// Implementations: 0

pub struct DebitRoutingResult


// Struct: CardTypeIndicators
// File: crates/hyperswitch_connectors/src/connectors/jpmorgan/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardTypeIndicators


// Implementation: impl api::PaymentVoid for for Globalpay
// File: crates/hyperswitch_connectors/src/connectors/globalpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentVoid for for Globalpay


// Function: mk_add_bank_response_hs
// File: crates/router/src/core/payment_methods/transformers.rs
// Module: router

pub fn mk_add_bank_response_hs(
    bank: api::BankPayout,
    bank_reference: String,
    req: api::PaymentMethodCreate,
    merchant_id: &id_type::MerchantId,
) -> api::PaymentMethodResponse


// Struct: Adyenplatform
// File: crates/hyperswitch_connectors/src/connectors/adyenplatform.rs
// Module: hyperswitch_connectors
// Implementations: 19
// Traits: ConnectorCommon, api::Payment, api::PaymentAuthorize, api::PaymentSync, api::PaymentVoid, api::PaymentCapture, api::MandateSetup, api::ConnectorAccessToken, api::PaymentToken, api::ConnectorValidation, api::PaymentSession, api::Payouts, api::PayoutFulfill, api::Refund, api::RefundExecute, api::RefundSync, IncomingWebhook, ConnectorSpecifications

pub struct Adyenplatform

ConnectorCommon api::Payment api::PaymentAuthorize api::PaymentSync api::PaymentVoid api::PaymentCapture api::MandateSetup api::ConnectorAccessToken api::PaymentToken api::ConnectorValidation api::PaymentSession api::Payouts api::PayoutFulfill api::Refund api::RefundExecute api::RefundSync IncomingWebhook ConnectorSpecifications


// Struct: CardData
// File: crates/hyperswitch_connectors/src/connectors/nmi/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct CardData


// Implementation: impl MerchantConnectorCreate
// File: crates/api_models/src/admin.rs
// Module: api_models
// Methods: 3 total (3 public)

impl MerchantConnectorCreate


// Function: list_payment_link
// File: crates/router/src/core/payment_link.rs
// Module: router

pub fn list_payment_link(
    state: SessionState,
    merchant: domain::MerchantAccount,
    constraints: api_models::payments::PaymentLinkListConstraints,
) -> RouterResponse<Vec<api_models::payments::RetrievePaymentLinkResponse>>


// Implementation: impl ConnectorCommon for for Celero
// File: crates/hyperswitch_connectors/src/connectors/celero.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl ConnectorCommon for for Celero


// Struct: BusinessPayoutLinkConfig
// File: crates/api_models/src/admin.rs
// Module: api_models
// Implementations: 0

pub struct BusinessPayoutLinkConfig


// Struct: SupplementaryVaultData
// File: crates/router/src/core/payment_methods/vault.rs
// Module: router
// Implementations: 0

pub struct SupplementaryVaultData


// File: crates/router/tests/connectors/ebanx.rs
// Module: router

use masking::Secret;
use router::types::{self, storage::enums};
use test_utils::connector_auth;

use crate::utils::{self, ConnectorActions};

#[derive(Clone, Copy)]
struct EbanxTest;
impl ConnectorActions for EbanxTest {}
impl utils::Connector for EbanxTest {
    fn get_data(&self) -> types::api::ConnectorData {
        use router::connector::Ebanx;
        utils::construct_connector_data_old(
            Box::new(Ebanx::new()),
            types::Connector::Ebanx,
            types::api::GetToken::Connector,
            None,
        )
    }

    fn get_auth_token(&self) -> types::ConnectorAuthType {
        utils::to_connector_auth_type(
            connector_auth::ConnectorAuthentication::new()
                .ebanx
                .expect("Missing connector authentication configuration")
                .into(),
        )
    }

    fn get_name(&self) -> String {
        "ebanx".to_string()
    }
}

static CONNECTOR: EbanxTest = EbanxTest {};

fn get_default_payment_info() -> Option<utils::PaymentInfo> {
    None
}

fn payment_method_details() -> Option<types::PaymentsAuthorizeData> {
    None
}

// Cards Positive Tests
// Creates a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_only_authorize_payment() {
    let response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized);
}

// Captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info())
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Partially captures a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_capture_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_capture_payment(
            payment_method_details(),
            Some(types::PaymentsCaptureData {
                amount_to_capture: 50,
                ..utils::PaymentCaptureType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Capture payment response");
    assert_eq!(response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_authorized_payment() {
    let authorize_response = CONNECTOR
        .authorize_payment(payment_method_details(), get_default_payment_info())
        .await
        .expect("Authorize payment response");
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Authorized,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("PSync response");
    assert_eq!(response.status, enums::AttemptStatus::Authorized,);
}

// Voids a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_void_authorized_payment() {
    let response = CONNECTOR
        .authorize_and_void_payment(
            payment_method_details(),
            Some(types::PaymentsCancelData {
                connector_transaction_id: String::from(""),
                cancellation_reason: Some("requested_by_customer".to_string()),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .expect("Void payment response");
    assert_eq!(response.status, enums::AttemptStatus::Voided);
}

// Refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_manually_captured_payment() {
    let response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Synchronizes a refund using the manual capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_manually_captured_refund() {
    let refund_response = CONNECTOR
        .capture_payment_and_refund(
            payment_method_details(),
            None,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_make_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
}

// Synchronizes a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_auto_captured_payment() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let response = CONNECTOR
        .psync_retry_till_status_matches(
            enums::AttemptStatus::Charged,
            Some(types::PaymentsSyncData {
                connector_transaction_id: types::ResponseId::ConnectorTransactionId(
                    txn_id.unwrap(),
                ),
                capture_method: Some(enums::CaptureMethod::Automatic),
                ..Default::default()
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(response.status, enums::AttemptStatus::Charged,);
}

// Refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_auto_captured_payment() {
    let response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Partially refunds a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_partially_refund_succeeded_payment() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        refund_response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_refund_succeeded_payment_multiple_times() {
    CONNECTOR
        .make_payment_and_multiple_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 50,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await;
}

// Synchronizes a refund using the automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_sync_refund() {
    let refund_response = CONNECTOR
        .make_payment_and_refund(payment_method_details(), None, get_default_payment_info())
        .await
        .unwrap();
    let response = CONNECTOR
        .rsync_retry_till_status_matches(
            enums::RefundStatus::Success,
            refund_response.response.unwrap().connector_refund_id,
            None,
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap().refund_status,
        enums::RefundStatus::Success,
    );
}

// Cards Negative scenarios
// Creates a payment with incorrect CVC.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_cvc() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: types::domain::PaymentMethodData::Card(types::domain::Card {
                    card_cvc: Secret::new("12345".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's security code is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry month.
#[actix_web::test]
async fn should_fail_payment_for_invalid_exp_month() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: types::domain::PaymentMethodData::Card(types::domain::Card {
                    card_exp_month: Secret::new("20".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration month is invalid.".to_string(),
    );
}

// Creates a payment with incorrect expiry year.
#[actix_web::test]
async fn should_fail_payment_for_incorrect_expiry_year() {
    let response = CONNECTOR
        .make_payment(
            Some(types::PaymentsAuthorizeData {
                payment_method_data: types::domain::PaymentMethodData::Card(types::domain::Card {
                    card_exp_year: Secret::new("2000".to_string()),
                    ..utils::CCardType::default().0
                }),
                ..utils::PaymentAuthorizeType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Your card's expiration year is invalid.".to_string(),
    );
}

// Voids a payment using automatic capture flow (Non 3DS).
#[actix_web::test]
async fn should_fail_void_payment_for_auto_capture() {
    let authorize_response = CONNECTOR
        .make_payment(payment_method_details(), get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(authorize_response.status, enums::AttemptStatus::Charged);
    let txn_id = utils::get_connector_transaction_id(authorize_response.response);
    assert_ne!(txn_id, None, "Empty connector transaction id");
    let void_response = CONNECTOR
        .void_payment(txn_id.unwrap(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        void_response.response.unwrap_err().message,
        "You cannot cancel this PaymentIntent because it has a status of succeeded."
    );
}

// Captures a payment using invalid connector payment id.
#[actix_web::test]
async fn should_fail_capture_for_invalid_payment() {
    let capture_response = CONNECTOR
        .capture_payment("123456789".to_string(), None, get_default_payment_info())
        .await
        .unwrap();
    assert_eq!(
        capture_response.response.unwrap_err().message,
        String::from("No such payment_intent: '123456789'")
    );
}

// Refunds a payment with refund amount higher than payment amount.
#[actix_web::test]
async fn should_fail_for_refund_amount_higher_than_payment_amount() {
    let response = CONNECTOR
        .make_payment_and_refund(
            payment_method_details(),
            Some(types::RefundsData {
                refund_amount: 150,
                ..utils::PaymentRefundType::default().0
            }),
            get_default_payment_info(),
        )
        .await
        .unwrap();
    assert_eq!(
        response.response.unwrap_err().message,
        "Refund amount (₹1.50) is greater than charge amount (₹1.00)",
    );
}

// Connector dependent test cases goes here

// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests


// Struct: ConnectorEvent
// File: crates/hyperswitch_interfaces/src/events/connector_api_logs.rs
// Module: hyperswitch_interfaces
// Implementations: 1

pub struct ConnectorEvent


// Function: get_error_message
// File: crates/router/src/core/errors/user.rs
// Module: router

pub fn get_error_message(&self) -> String


// Struct: PaymentLink
// File: crates/hyperswitch_connectors/src/connectors/paysafe/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymentLink


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

pub struct BluesnapGooglePayObject


// Implementation: impl api::PaymentToken for for Checkout
// File: crates/hyperswitch_connectors/src/connectors/checkout.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Checkout


// Implementation: impl crate::events::ApiEventMetric for for ApiKeyId
// File: crates/common_utils/src/id_type/api_key.rs
// Module: common_utils
// Methods: 1 total (0 public)

impl crate::events::ApiEventMetric for for ApiKeyId


// Implementation: impl api::PaymentAuthorize for for Globalpay
// File: crates/hyperswitch_connectors/src/connectors/globalpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Globalpay


// Implementation: impl Responder
// File: crates/router/src/routes/verification.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


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

impl api::PaymentToken for for Adyen


// File: crates/router/src/core/payments/operations/payment_cancel_post_capture.rs
// Module: router
// Public structs: 1

use std::marker::PhantomData;

use api_models::enums::FrmSuggestion;
use async_trait::async_trait;
use error_stack::ResultExt;
use router_derive;
use router_env::{instrument, tracing};

use super::{BoxedOperation, Domain, GetTracker, Operation, UpdateTracker, ValidateRequest};
use crate::{
    core::{
        errors::{self, RouterResult, StorageErrorExt},
        payments::{self, helpers, operations, PaymentData},
    },
    routes::{app::ReqState, SessionState},
    services,
    types::{
        self as core_types,
        api::{self, PaymentIdTypeExt},
        domain,
        storage::{self, enums},
    },
    utils::OptionExt,
};

#[derive(Debug, Clone, Copy, router_derive::PaymentOperation)]
#[operation(operations = "all", flow = "cancel_post_capture")]
pub struct PaymentCancelPostCapture;

type PaymentCancelPostCaptureOperation<'b, F> =
    BoxedOperation<'b, F, api::PaymentsCancelPostCaptureRequest, PaymentData<F>>;

#[async_trait]
impl<F: Send + Clone + Sync> GetTracker<F, PaymentData<F>, api::PaymentsCancelPostCaptureRequest>
    for PaymentCancelPostCapture
{
    #[instrument(skip_all)]
    async fn get_trackers<'a>(
        &'a self,
        state: &'a SessionState,
        payment_id: &api::PaymentIdType,
        request: &api::PaymentsCancelPostCaptureRequest,
        merchant_context: &domain::MerchantContext,
        _auth_flow: services::AuthFlow,
        _header_payload: &hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<
        operations::GetTrackerResponse<
            'a,
            F,
            api::PaymentsCancelPostCaptureRequest,
            PaymentData<F>,
        >,
    > {
        let db = &*state.store;
        let key_manager_state = &state.into();

        let merchant_id = merchant_context.get_merchant_account().get_id();
        let storage_scheme = merchant_context.get_merchant_account().storage_scheme;
        let payment_id = payment_id
            .get_payment_intent_id()
            .change_context(errors::ApiErrorResponse::PaymentNotFound)?;

        let payment_intent = db
            .find_payment_intent_by_payment_id_merchant_id(
                key_manager_state,
                &payment_id,
                merchant_id,
                merchant_context.get_merchant_key_store(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        helpers::validate_payment_status_against_allowed_statuses(
            payment_intent.status,
            &[
                enums::IntentStatus::Succeeded,
                enums::IntentStatus::PartiallyCaptured,
                enums::IntentStatus::PartiallyCapturedAndCapturable,
            ],
            "cancel_post_capture",
        )?;

        let mut payment_attempt = db
            .find_payment_attempt_by_payment_id_merchant_id_attempt_id(
                &payment_intent.payment_id,
                merchant_id,
                payment_intent.active_attempt.get_id().as_str(),
                storage_scheme,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;

        let shipping_address = helpers::get_address_by_id(
            state,
            payment_intent.shipping_address_id.clone(),
            merchant_context.get_merchant_key_store(),
            &payment_intent.payment_id,
            merchant_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let billing_address = helpers::get_address_by_id(
            state,
            payment_intent.billing_address_id.clone(),
            merchant_context.get_merchant_key_store(),
            &payment_intent.payment_id,
            merchant_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let payment_method_billing = helpers::get_address_by_id(
            state,
            payment_attempt.payment_method_billing_address_id.clone(),
            merchant_context.get_merchant_key_store(),
            &payment_intent.payment_id,
            merchant_id,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await?;

        let currency = payment_attempt.currency.get_required_value("currency")?;
        let amount = payment_attempt.get_total_amount().into();

        payment_attempt
            .cancellation_reason
            .clone_from(&request.cancellation_reason);

        let profile_id = payment_intent
            .profile_id
            .as_ref()
            .get_required_value("profile_id")
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("'profile_id' not set in payment intent")?;

        let business_profile = db
            .find_business_profile_by_profile_id(
                key_manager_state,
                merchant_context.get_merchant_key_store(),
                profile_id,
            )
            .await
            .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
                id: profile_id.get_string_repr().to_owned(),
            })?;

        let payment_data = PaymentData {
            flow: PhantomData,
            payment_intent,
            payment_attempt,
            currency,
            amount,
            email: None,
            mandate_id: None,
            mandate_connector: None,
            setup_mandate: None,
            customer_acceptance: None,
            token: None,
            token_data: None,
            address: core_types::PaymentAddress::new(
                shipping_address.as_ref().map(From::from),
                billing_address.as_ref().map(From::from),
                payment_method_billing.as_ref().map(From::from),
                business_profile.use_billing_as_payment_method_billing,
            ),
            confirm: None,
            payment_method_data: None,
            payment_method_token: None,
            payment_method_info: None,
            force_sync: None,
            all_keys_required: None,
            refunds: vec![],
            disputes: vec![],
            attempts: None,
            sessions_token: vec![],
            card_cvc: None,
            creds_identifier: None,
            pm_token: None,
            connector_customer_id: None,
            recurring_mandate_payment_data: None,
            ephemeral_key: None,
            multiple_capture_data: None,
            redirect_response: None,
            surcharge_details: None,
            frm_message: None,
            payment_link_data: None,
            incremental_authorization_details: None,
            authorizations: vec![],
            authentication: None,
            recurring_details: None,
            poll_config: None,
            tax_data: None,
            session_id: None,
            service_details: None,
            card_testing_guard_data: None,
            vault_operation: None,
            threeds_method_comp_ind: None,
            whole_connector_response: None,
            is_manual_retry_enabled: None,
        };

        let get_trackers_response = operations::GetTrackerResponse {
            operation: Box::new(self),
            customer_details: None,
            payment_data,
            business_profile,
            mandate_type: None,
        };

        Ok(get_trackers_response)
    }
}

#[async_trait]
impl<F: Clone + Send + Sync> Domain<F, api::PaymentsCancelPostCaptureRequest, PaymentData<F>>
    for PaymentCancelPostCapture
{
    #[instrument(skip_all)]
    async fn get_or_create_customer_details<'a>(
        &'a self,
        _state: &SessionState,
        _payment_data: &mut PaymentData<F>,
        _request: Option<payments::CustomerDetails>,
        _merchant_key_store: &domain::MerchantKeyStore,
        _storage_scheme: enums::MerchantStorageScheme,
    ) -> errors::CustomResult<
        (
            PaymentCancelPostCaptureOperation<'a, F>,
            Option<domain::Customer>,
        ),
        errors::StorageError,
    > {
        Ok((Box::new(self), None))
    }

    #[instrument(skip_all)]
    async fn make_pm_data<'a>(
        &'a self,
        _state: &'a SessionState,
        _payment_data: &mut PaymentData<F>,
        _storage_scheme: enums::MerchantStorageScheme,
        _merchant_key_store: &domain::MerchantKeyStore,
        _customer: &Option<domain::Customer>,
        _business_profile: &domain::Profile,
        _should_retry_with_pan: bool,
    ) -> RouterResult<(
        PaymentCancelPostCaptureOperation<'a, F>,
        Option<domain::PaymentMethodData>,
        Option<String>,
    )> {
        Ok((Box::new(self), None, None))
    }

    async fn get_connector<'a>(
        &'a self,
        _merchant_context: &domain::MerchantContext,
        state: &SessionState,
        _request: &api::PaymentsCancelPostCaptureRequest,
        _payment_intent: &storage::PaymentIntent,
    ) -> errors::CustomResult<api::ConnectorChoice, errors::ApiErrorResponse> {
        helpers::get_connector_default(state, None).await
    }

    #[instrument(skip_all)]
    async fn guard_payment_against_blocklist<'a>(
        &'a self,
        _state: &SessionState,
        _merchant_context: &domain::MerchantContext,
        _payment_data: &mut PaymentData<F>,
    ) -> errors::CustomResult<bool, errors::ApiErrorResponse> {
        Ok(false)
    }
}

#[async_trait]
impl<F: Clone + Sync> UpdateTracker<F, PaymentData<F>, api::PaymentsCancelPostCaptureRequest>
    for PaymentCancelPostCapture
{
    #[instrument(skip_all)]
    async fn update_trackers<'b>(
        &'b self,
        _state: &'b SessionState,
        _req_state: ReqState,
        payment_data: PaymentData<F>,
        _customer: Option<domain::Customer>,
        _storage_scheme: enums::MerchantStorageScheme,
        _updated_customer: Option<storage::CustomerUpdate>,
        _key_store: &domain::MerchantKeyStore,
        _frm_suggestion: Option<FrmSuggestion>,
        _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    ) -> RouterResult<(PaymentCancelPostCaptureOperation<'b, F>, PaymentData<F>)>
    where
        F: 'b + Send,
    {
        Ok((Box::new(self), payment_data))
    }
}

impl<F: Send + Clone + Sync>
    ValidateRequest<F, api::PaymentsCancelPostCaptureRequest, PaymentData<F>>
    for PaymentCancelPostCapture
{
    #[instrument(skip_all)]
    fn validate_request<'a, 'b>(
        &'b self,
        request: &api::PaymentsCancelPostCaptureRequest,
        merchant_context: &'a domain::MerchantContext,
    ) -> RouterResult<(
        PaymentCancelPostCaptureOperation<'b, F>,
        operations::ValidateResult,
    )> {
        Ok((
            Box::new(self),
            operations::ValidateResult {
                merchant_id: merchant_context.get_merchant_account().get_id().to_owned(),
                payment_id: api::PaymentIdType::PaymentIntentId(request.payment_id.to_owned()),
                storage_scheme: merchant_context.get_merchant_account().storage_scheme,
                requeue: false,
            },
        ))
    }
}


// Implementation: impl Responder
// File: crates/router/src/routes/payments.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Implementation: impl api::RefundExecute for for Cryptopay
// File: crates/hyperswitch_connectors/src/connectors/cryptopay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundExecute for for Cryptopay


// Function: migrate_customers
// File: crates/router/src/core/customers.rs
// Module: router

pub fn migrate_customers(
    state: SessionState,
    customers_migration: Vec<payment_methods_domain::PaymentMethodCustomerMigrate>,
    merchant_context: domain::MerchantContext,
) -> errors::CustomerResponse<()>


// Struct: TokenexRetrieveResponse
// File: crates/hyperswitch_connectors/src/connectors/tokenex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TokenexRetrieveResponse


// Implementation: impl common_utils::events::ApiEventMetric for for ProxyRequest
// File: crates/api_models/src/proxy.rs
// Module: api_models
// Methods: 0 total (0 public)

impl common_utils::events::ApiEventMetric for for ProxyRequest


// Implementation: impl api::PaymentAuthorize for for Affirm
// File: crates/hyperswitch_connectors/src/connectors/affirm.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentAuthorize for for Affirm


// Implementation: impl Authentication
// File: crates/diesel_models/src/query/authentication.rs
// Module: diesel_models
// Methods: 3 total (0 public)

impl Authentication


// Function: relay
// File: crates/openapi/src/routes/relay.rs
// Module: openapi

pub fn relay()


// Function: is_webhook_event_disabled
// File: crates/router/src/core/webhooks/utils.rs
// Module: router

// Documentation: Check whether the merchant has configured to disable the webhook `event` for the `connector` First check for the key "whconf_{merchant_id}_{connector_id}" in redis, if not found, fetch from configs table in database

pub fn is_webhook_event_disabled(
    db: &dyn StorageInterface,
    connector_id: &str,
    merchant_id: &common_utils::id_type::MerchantId,
    event: &api::IncomingWebhookEvent,
) -> bool


// Struct: ExpiryResponse
// File: crates/hyperswitch_connectors/src/connectors/jpmorgan/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ExpiryResponse


// Function: call_connector_service_prerequisites
// File: crates/router/src/core/payments.rs
// Module: router

pub fn call_connector_service_prerequisites<F, RouterDReq, ApiRequest, D>(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    connector: api::ConnectorData,
    operation: &BoxedOperation<'_, F, ApiRequest, D>,
    payment_data: &mut D,
    customer: &Option<domain::Customer>,
    validate_result: &operations::ValidateResult,
    business_profile: &domain::Profile,
    should_retry_with_pan: bool,
    routing_decision: Option<routing_helpers::RoutingDecisionData>,
) -> RouterResult<(
    helpers::MerchantConnectorAccountType,
    RouterData<F, RouterDReq, router_types::PaymentsResponseData>,
    TokenizationAction,
)>
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>,


// Struct: ArchipelTenantId
// File: crates/hyperswitch_connectors/src/connectors/archipel/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ArchipelTenantId


// Implementation: impl api::PaymentToken for for Finix
// File: crates/hyperswitch_connectors/src/connectors/finix.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentToken for for Finix


// Implementation: impl UserErrors
// File: crates/router/src/core/errors/user.rs
// Module: router
// Methods: 1 total (1 public)

impl UserErrors