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

pub mod transformers;
use std::sync::LazyLock;

use common_enums::enums;
use common_utils::{
    crypto::{self, GenerateDigest},
    errors::CustomResult,
    ext_traits::BytesExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, MinorUnit, MinorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hex::encode;
use hyperswitch_domain_models::{
    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, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundSyncRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts, errors,
    events::connector_api_logs::ConnectorEvent,
    types::{PaymentsAuthorizeType, PaymentsSyncType, RefundExecuteType, RefundSyncType, Response},
    webhooks,
};
use masking::ExposeInterface;
use rand::distributions::DistString;
use time::OffsetDateTime;
use transformers as globepay;

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

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

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

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

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Globepay
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 header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.get_content_type().to_string().into(),
        )];
        Ok(header)
    }
}

fn get_globlepay_query_params(
    connector_auth_type: &ConnectorAuthType,
) -> CustomResult<String, errors::ConnectorError> {
    let auth_type = globepay::GlobepayAuthType::try_from(connector_auth_type)?;
    let time = (OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000_000).to_string();
    let nonce_str = rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 12);
    let valid_string = format!(
        "{}&{time}&{nonce_str}&{}",
        auth_type.partner_code.expose(),
        auth_type.credential_code.expose()
    );
    let digest = crypto::Sha256
        .generate_digest(valid_string.as_bytes())
        .change_context(errors::ConnectorError::RequestEncodingFailed)
        .attach_printable("error encoding the query params")?;
    let sign = encode(digest).to_lowercase();
    let param = format!("?sign={sign}&time={time}&nonce_str={nonce_str}");
    Ok(param)
}

fn get_partner_code(
    connector_auth_type: &ConnectorAuthType,
) -> CustomResult<String, errors::ConnectorError> {
    let auth_type = globepay::GlobepayAuthType::try_from(connector_auth_type)?;
    Ok(auth_type.partner_code.expose())
}

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

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

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

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.return_code.to_string(),
            message: consts::NO_ERROR_MESSAGE.to_string(),
            reason: Some(response.return_msg),
            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 Globepay {}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Globepay {}

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

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for Globepay
{
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(
            errors::ConnectorError::NotImplemented("Setup Mandate flow for Globepay".to_string())
                .into(),
        )
    }
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Globepay {
    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> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        if matches!(
            req.request.capture_method,
            Some(enums::CaptureMethod::Automatic) | Some(enums::CaptureMethod::SequentialAutomatic)
        ) {
            Ok(format!(
                "{}api/v1.0/gateway/partners/{}/orders/{}{query_params}",
                self.base_url(connectors),
                get_partner_code(&req.connector_auth_type)?,
                req.payment_id
            ))
        } else {
            Err(errors::ConnectorError::FlowNotSupported {
                flow: "Manual Capture".to_owned(),
                connector: "Globepay".to_owned(),
            }
            .into())
        }
    }

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

        let connector_router_data = globepay::GlobepayRouterData::from((amount, req));
        let connector_req = globepay::GlobepayPaymentsRequest::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::Put)
                .url(&PaymentsAuthorizeType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsAuthorizeType::get_headers(self, req, connectors)?)
                .set_body(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: globepay::GlobepayPaymentsResponse = res
            .response
            .parse_struct("Globepay 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 Globepay {
    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> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        Ok(format!(
            "{}api/v1.0/gateway/partners/{}/orders/{}{query_params}",
            self.base_url(connectors),
            get_partner_code(&req.connector_auth_type)?,
            req.payment_id
        ))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Get)
                .url(&PaymentsSyncType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(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: globepay::GlobepaySyncResponse = res
            .response
            .parse_struct("globepay 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 Globepay {
    fn build_request(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Manual Capture".to_owned(),
            connector: "Globepay".to_owned(),
        }
        .into())
    }
}

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

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Globepay {
    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> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        Ok(format!(
            "{}api/v1.0/gateway/partners/{}/orders/{}/refunds/{}{query_params}",
            self.base_url(connectors),
            get_partner_code(&req.connector_auth_type)?,
            req.payment_id,
            req.request.refund_id
        ))
    }

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

        let connector_router_data = globepay::GlobepayRouterData::from((refund_amount, req));
        let connector_req = globepay::GlobepayRefundRequest::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> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Put)
                .url(&RefundExecuteType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(RefundExecuteType::get_headers(self, req, connectors)?)
                .set_body(RefundExecuteType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: globepay::GlobepayRefundResponse = res
            .response
            .parse_struct("Globalpay 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 Globepay {
    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> {
        let query_params = get_globlepay_query_params(&req.connector_auth_type)?;
        Ok(format!(
            "{}api/v1.0/gateway/partners/{}/orders/{}/refunds/{}{query_params}",
            self.base_url(connectors),
            get_partner_code(&req.connector_auth_type)?,
            req.payment_id,
            req.request.refund_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &RefundSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundSyncRouterData, errors::ConnectorError> {
        let response: globepay::GlobepayRefundResponse = res
            .response
            .parse_struct("Globalpay 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)
    }
}

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Globepay {
    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 GLOBEPAY_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(|| {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let mut globepay_supported_payment_methods = SupportedPaymentMethods::new();

        globepay_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::AliPay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        globepay_supported_payment_methods.add(
            enums::PaymentMethod::Wallet,
            enums::PaymentMethodType::WeChatPay,
            PaymentMethodDetails {
                mandates: enums::FeatureStatus::NotSupported,
                refunds: enums::FeatureStatus::Supported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        globepay_supported_payment_methods
    });

static GLOBEPAY_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Globepay",
        description: "GlobePay Limited is a professional cross-border payment solution provider (WeChat Pay & Alipay) in the UK",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Sandbox,
    };

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

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

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

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


// Implementation: impl api::PaymentCapture for for Dwolla
// File: crates/hyperswitch_connectors/src/connectors/dwolla.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentCapture for for Dwolla


// Struct: ChallengeFlowCount
// File: crates/api_models/src/analytics/auth_events.rs
// Module: api_models
// Implementations: 0

pub struct ChallengeFlowCount


// Struct: PaymeQuerySaleRequest
// File: crates/hyperswitch_connectors/src/connectors/payme/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymeQuerySaleRequest


// Implementation: impl DefaultFallbackRoutingConfigUpdate
// File: crates/router/src/core/admin.rs
// Module: router
// Methods: 2 total (0 public)

impl DefaultFallbackRoutingConfigUpdate


// File: crates/external_services/src/hashicorp_vault/implementers.rs
// Module: external_services

//! Trait implementations for Hashicorp vault client

use common_utils::errors::CustomResult;
use error_stack::ResultExt;
use hyperswitch_interfaces::secrets_interface::{
    SecretManagementInterface, SecretsManagementError,
};
use masking::{ExposeInterface, Secret};

use crate::hashicorp_vault::core::{HashiCorpVault, Kv2};

#[async_trait::async_trait]
impl SecretManagementInterface for HashiCorpVault {
    async fn get_secret(
        &self,
        input: Secret<String>,
    ) -> CustomResult<Secret<String>, SecretsManagementError> {
        self.fetch::<Kv2, Secret<String>>(input.expose())
            .await
            .map(|val| val.expose().to_owned())
            .change_context(SecretsManagementError::FetchSecretFailed)
            .map(Into::into)
    }
}


// Struct: PaymentMethodCreateWrapper
// File: crates/router/src/core/webhooks/network_tokenization_incoming.rs
// Module: router
// Implementations: 1

pub struct PaymentMethodCreateWrapper


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

impl api::Refund for for Inespay


// Implementation: impl ConnectorSpecifications for for Prophetpay
// File: crates/hyperswitch_connectors/src/connectors/prophetpay.rs
// Module: hyperswitch_connectors
// Methods: 3 total (0 public)

impl ConnectorSpecifications for for Prophetpay


// Implementation: impl LineageIds
// File: crates/external_services/src/grpc_client.rs
// Module: external_services
// Methods: 2 total (2 public)

impl LineageIds


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

impl common_utils::events::ApiEventMetric for for ProfileAcquirerResponse


// Struct: FileRetrieveQuery
// File: crates/api_models/src/files.rs
// Module: api_models
// Implementations: 0

pub struct FileRetrieveQuery


// Implementation: impl api::RefundSync for for Tesouro
// File: crates/hyperswitch_connectors/src/connectors/tesouro.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::RefundSync for for Tesouro


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

pub fn update_connector_mandate_details_status(
    merchant_connector_id: id_type::MerchantConnectorAccountId,
    mut payment_mandate_reference: PaymentsMandateReference,
    status: ConnectorMandateStatus,
) -> RouterResult<Option<CommonMandateReference>>


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

pub struct BluesnapCardHolderInfo


// Struct: VoltRefundsWebhookBodyEventType
// File: crates/hyperswitch_connectors/src/connectors/volt/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct VoltRefundsWebhookBodyEventType


// File: crates/diesel_models/src/query/blocklist.rs
// Module: diesel_models
// Public functions: 5

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};

use super::generics;
use crate::{
    blocklist::{Blocklist, BlocklistNew},
    schema::blocklist::dsl,
    PgPooledConn, StorageResult,
};

impl BlocklistNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Blocklist> {
        generics::generic_insert(conn, self).await
    }
}

impl Blocklist {
    pub async fn find_by_merchant_id_fingerprint_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        fingerprint_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::fingerprint_id.eq(fingerprint_id.to_owned())),
        )
        .await
    }

    pub async fn list_by_merchant_id_data_kind(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        data_kind: common_enums::BlocklistDataKind,
        limit: i64,
        offset: i64,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::data_kind.eq(data_kind.to_owned())),
            Some(limit),
            Some(offset),
            Some(dsl::created_at.desc()),
        )
        .await
    }

    pub async fn list_by_merchant_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Vec<Self>> {
        generics::generic_filter::<<Self as HasTable>::Table, _, _, _>(
            conn,
            dsl::merchant_id.eq(merchant_id.to_owned()),
            None,
            None,
            Some(dsl::created_at.desc()),
        )
        .await
    }

    pub async fn delete_by_merchant_id_fingerprint_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        fingerprint_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_delete_one_with_result::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::fingerprint_id.eq(fingerprint_id.to_owned())),
        )
        .await
    }
}


// Struct: AccountNumber
// File: crates/hyperswitch_connectors/src/connectors/nordea/requests.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AccountNumber


// Function: update_default_routing_config
// File: crates/router/src/core/routing.rs
// Module: router

pub fn update_default_routing_config(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    updated_config: Vec<routing_types::RoutableConnectorChoice>,
    transaction_type: &enums::TransactionType,
) -> RouterResponse<Vec<routing_types::RoutableConnectorChoice>>


// File: crates/router/src/core/payouts/validator.rs
// Module: router
// Public functions: 5

use std::collections::HashSet;

use actix_web::http::header;
#[cfg(feature = "olap")]
use common_utils::errors::CustomResult;
use common_utils::{
    id_type::{self, GenerateId},
    validation::validate_domain_against_allowed_domains,
};
use diesel_models::generic_link::PayoutLink;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::payment_methods::PaymentMethod;
use router_env::{instrument, tracing, which as router_env_which, Env};
use url::Url;

use super::helpers;
#[cfg(feature = "v1")]
use crate::core::payment_methods::cards::get_pm_list_context;
use crate::{
    core::{
        errors::{self, RouterResult},
        utils as core_utils,
    },
    db::StorageInterface,
    errors::StorageError,
    routes::SessionState,
    types::{api::payouts, domain, storage},
    utils,
    utils::OptionExt,
};

#[instrument(skip(db))]
pub async fn validate_uniqueness_of_payout_id_against_merchant_id(
    db: &dyn StorageInterface,
    payout_id: &id_type::PayoutId,
    merchant_id: &id_type::MerchantId,
    storage_scheme: storage::enums::MerchantStorageScheme,
) -> RouterResult<Option<storage::Payouts>> {
    let maybe_payouts = db
        .find_optional_payout_by_merchant_id_payout_id(merchant_id, payout_id, storage_scheme)
        .await;
    match maybe_payouts {
        Err(err) => {
            let storage_err = err.current_context();
            match storage_err {
                StorageError::ValueNotFound(_) => Ok(None),
                _ => Err(err
                    .change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("Failed while finding payout_attempt, database error")),
            }
        }
        Ok(payout) => Ok(payout),
    }
}

#[cfg(feature = "v2")]
pub async fn validate_create_request(
    _state: &SessionState,
    _merchant_context: &domain::MerchantContext,
    _req: &payouts::PayoutCreateRequest,
) -> RouterResult<(
    String,
    Option<payouts::PayoutMethodData>,
    String,
    Option<domain::Customer>,
    Option<PaymentMethod>,
)> {
    todo!()
}

/// Validates the request on below checks
/// - merchant_id passed is same as the one in merchant_account table
/// - payout_id is unique against merchant_id
/// - payout_token provided is legitimate
#[cfg(feature = "v1")]
pub async fn validate_create_request(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    req: &payouts::PayoutCreateRequest,
) -> RouterResult<(
    id_type::PayoutId,
    Option<payouts::PayoutMethodData>,
    id_type::ProfileId,
    Option<domain::Customer>,
    Option<PaymentMethod>,
)> {
    if req.payout_method_id.is_some() && req.confirm != Some(true) {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
            message: "Confirm must be true for recurring payouts".to_string(),
        }));
    }
    let merchant_id = merchant_context.get_merchant_account().get_id();

    if let Some(payout_link) = &req.payout_link {
        if *payout_link {
            validate_payout_link_request(req)?;
        }
    };

    // Merchant ID
    let predicate = req.merchant_id.as_ref().map(|mid| mid != merchant_id);
    utils::when(predicate.unwrap_or(false), || {
        Err(report!(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "merchant_id".to_string(),
            expected_format: "merchant_id from merchant account".to_string(),
        })
        .attach_printable("invalid merchant_id in request"))
    })?;

    // Payout ID
    let db: &dyn StorageInterface = &*state.store;
    let payout_id = match req.payout_id.as_ref() {
        Some(provided_payout_id) => provided_payout_id.clone(),
        None => id_type::PayoutId::generate(),
    };

    match validate_uniqueness_of_payout_id_against_merchant_id(
        db,
        &payout_id,
        merchant_id,
        merchant_context.get_merchant_account().storage_scheme,
    )
    .await
    .attach_printable_lazy(|| {
        format!(
            "Unique violation while checking payout_id: {payout_id:?} against merchant_id: {merchant_id:?}"
        )
    })? {
        Some(_) => Err(report!(errors::ApiErrorResponse::DuplicatePayout {
            payout_id: payout_id.clone()
        })),
        None => Ok(()),
    }?;

    // Fetch customer details (merge of loose fields + customer object) and create DB entry
    let customer_in_request = helpers::get_customer_details_from_request(req);
    let customer = if customer_in_request.customer_id.is_some()
        || customer_in_request.name.is_some()
        || customer_in_request.email.is_some()
        || customer_in_request.phone.is_some()
        || customer_in_request.phone_country_code.is_some()
    {
        helpers::get_or_create_customer_details(state, &customer_in_request, merchant_context)
            .await?
    } else {
        None
    };

    #[cfg(feature = "v1")]
    let profile_id = core_utils::get_profile_id_from_business_details(
        &state.into(),
        req.business_country,
        req.business_label.as_ref(),
        merchant_context,
        req.profile_id.as_ref(),
        &*state.store,
        false,
    )
    .await?;

    #[cfg(feature = "v2")]
    // Profile id will be mandatory in v2 in the request / headers
    let profile_id = req
        .profile_id
        .clone()
        .ok_or(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "profile_id",
        })
        .attach_printable("Profile id is a mandatory parameter")?;

    let payment_method: Option<PaymentMethod> =
        match (req.payout_token.as_ref(), req.payout_method_id.clone()) {
            (Some(_), Some(_)) => Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                message: "Only one of payout_method_id or payout_token should be provided."
                    .to_string(),
            })),
            (None, Some(payment_method_id)) => match customer.as_ref() {
                Some(customer) => {
                    let payment_method = db
                        .find_payment_method(
                            &state.into(),
                            merchant_context.get_merchant_key_store(),
                            &payment_method_id,
                            merchant_context.get_merchant_account().storage_scheme,
                        )
                        .await
                        .change_context(errors::ApiErrorResponse::PaymentMethodNotFound)
                        .attach_printable("Unable to find payment method")?;

                    utils::when(payment_method.customer_id != customer.customer_id, || {
                        Err(report!(errors::ApiErrorResponse::InvalidRequestData {
                        message: "Payment method does not belong to this customer_id".to_string(),
                    })
                    .attach_printable(
                        "customer_id in payment_method does not match with customer_id in request",
                    ))
                    })?;
                    Ok(Some(payment_method))
                }
                None => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
                    field_name: "customer_id when payment_method_id is passed",
                })),
            },
            _ => Ok(None),
        }?;

    // payout_token
    let payout_method_data = match (
        req.payout_token.as_ref(),
        customer.as_ref(),
        payment_method.as_ref(),
    ) {
        (Some(_), None, _) => Err(report!(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "customer or customer_id when payout_token is provided"
        })),
        (Some(payout_token), Some(customer), _) => {
            helpers::make_payout_method_data(
                state,
                req.payout_method_data.as_ref(),
                Some(payout_token),
                &customer.customer_id,
                merchant_context.get_merchant_account().get_id(),
                req.payout_type,
                merchant_context.get_merchant_key_store(),
                None,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
        }
        (_, Some(_), Some(payment_method)) => {
            // Check if we have a stored transfer_method_id first
            if payment_method
                .get_common_mandate_reference()
                .ok()
                .and_then(|common_mandate_ref| common_mandate_ref.payouts)
                .map(|payouts_mandate_ref| !payouts_mandate_ref.0.is_empty())
                .unwrap_or(false)
            {
                Ok(None)
            } else {
                // No transfer_method_id available, proceed with vault fetch for raw card details
                match get_pm_list_context(
                    state,
                    payment_method
                        .payment_method
                        .as_ref()
                        .get_required_value("payment_method_id")?,
                    merchant_context.get_merchant_key_store(),
                    payment_method,
                    None,
                    false,
                    true,
                    merchant_context,
                )
                .await?
                {
                    Some(pm) => match (pm.card_details, pm.bank_transfer_details) {
                        (Some(card), _) => Ok(Some(payouts::PayoutMethodData::Card(
                            api_models::payouts::CardPayout {
                                card_number: card.card_number.get_required_value("card_number")?,
                                card_holder_name: card.card_holder_name,
                                expiry_month: card
                                    .expiry_month
                                    .get_required_value("expiry_month")?,
                                expiry_year: card.expiry_year.get_required_value("expiry_year")?,
                            },
                        ))),
                        (_, Some(bank)) => Ok(Some(payouts::PayoutMethodData::Bank(bank))),
                        _ => Ok(None),
                    },
                    None => Ok(None),
                }
            }
        }
        _ => Ok(None),
    }?;

    Ok((
        payout_id,
        payout_method_data,
        profile_id,
        customer,
        payment_method,
    ))
}

pub fn validate_payout_link_request(
    req: &payouts::PayoutCreateRequest,
) -> Result<(), errors::ApiErrorResponse> {
    if req.confirm.unwrap_or(false) {
        return Err(errors::ApiErrorResponse::InvalidRequestData {
            message: "cannot confirm a payout while creating a payout link".to_string(),
        });
    }

    if req.customer_id.is_none() {
        return Err(errors::ApiErrorResponse::MissingRequiredField {
            field_name: "customer or customer_id when payout_link is true",
        });
    }

    Ok(())
}

#[cfg(feature = "olap")]
pub(super) fn validate_payout_list_request(
    req: &payouts::PayoutListConstraints,
) -> CustomResult<(), errors::ApiErrorResponse> {
    use common_utils::consts::PAYOUTS_LIST_MAX_LIMIT_GET;

    utils::when(
        req.limit > PAYOUTS_LIST_MAX_LIMIT_GET || req.limit < 1,
        || {
            Err(errors::ApiErrorResponse::InvalidRequestData {
                message: format!("limit should be in between 1 and {PAYOUTS_LIST_MAX_LIMIT_GET}"),
            })
        },
    )?;
    Ok(())
}

#[cfg(feature = "olap")]
pub(super) fn validate_payout_list_request_for_joins(
    limit: u32,
) -> CustomResult<(), errors::ApiErrorResponse> {
    use common_utils::consts::PAYOUTS_LIST_MAX_LIMIT_POST;

    utils::when(!(1..=PAYOUTS_LIST_MAX_LIMIT_POST).contains(&limit), || {
        Err(errors::ApiErrorResponse::InvalidRequestData {
            message: format!("limit should be in between 1 and {PAYOUTS_LIST_MAX_LIMIT_POST}"),
        })
    })?;
    Ok(())
}

pub fn validate_payout_link_render_request_and_get_allowed_domains(
    request_headers: &header::HeaderMap,
    payout_link: &PayoutLink,
) -> RouterResult<HashSet<String>> {
    let link_id = payout_link.link_id.to_owned();
    let link_data = payout_link.link_data.to_owned();

    let is_test_mode_enabled = link_data.test_mode.unwrap_or(false);

    match (router_env_which(), is_test_mode_enabled) {
        // Throw error in case test_mode was enabled in production
        (Env::Production, true) => Err(report!(errors::ApiErrorResponse::LinkConfigurationError {
            message: "test_mode cannot be true for rendering payout_links in production"
                .to_string()
        })),
        // Skip all validations when test mode is enabled in non prod env
        (_, true) => Ok(HashSet::new()),
        // Otherwise, perform validations
        (_, false) => {
            // Fetch destination is "iframe"
            match request_headers.get("sec-fetch-dest").and_then(|v| v.to_str().ok()) {
                Some("iframe") => Ok(()),
                Some(requestor) => Err(report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payout_link".to_string(),
                }))
                .attach_printable_lazy(|| {
                    format!(
                        "Access to payout_link [{link_id}] is forbidden when requested through {requestor}",

                    )
                }),
                None => Err(report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payout_link".to_string(),
                }))
                .attach_printable_lazy(|| {
                    format!(
                        "Access to payout_link [{link_id}] is forbidden when sec-fetch-dest is not present in request headers",

                    )
                }),
            }?;

            // Validate origin / referer
            let domain_in_req = {
                let origin_or_referer = request_headers
                    .get("origin")
                    .or_else(|| request_headers.get("referer"))
                    .and_then(|v| v.to_str().ok())
                    .ok_or_else(|| {
                        report!(errors::ApiErrorResponse::AccessForbidden {
                            resource: "payout_link".to_string(),
                        })
                    })
                    .attach_printable_lazy(|| {
                        format!(
                            "Access to payout_link [{link_id}] is forbidden when origin or referer is not present in request headers",

                        )
                    })?;

                let url = Url::parse(origin_or_referer)
                    .map_err(|_| {
                        report!(errors::ApiErrorResponse::AccessForbidden {
                            resource: "payout_link".to_string(),
                        })
                    })
                    .attach_printable_lazy(|| {
                        format!("Invalid URL found in request headers {origin_or_referer}")
                    })?;

                url.host_str()
                    .and_then(|host| url.port().map(|port| format!("{host}:{port}")))
                    .or_else(|| url.host_str().map(String::from))
                    .ok_or_else(|| {
                        report!(errors::ApiErrorResponse::AccessForbidden {
                            resource: "payout_link".to_string(),
                        })
                    })
                    .attach_printable_lazy(|| {
                        format!("host or port not found in request headers {url:?}")
                    })?
            };

            if validate_domain_against_allowed_domains(
                &domain_in_req,
                link_data.allowed_domains.clone(),
            ) {
                Ok(link_data.allowed_domains)
            } else {
                Err(report!(errors::ApiErrorResponse::AccessForbidden {
                    resource: "payout_link".to_string(),
                }))
                .attach_printable_lazy(|| {
                    format!(
                        "Access to payout_link [{link_id}] is forbidden from requestor - {domain_in_req}",

                    )
                })
            }
        }
    }
}


// Implementation: impl api::Refund for for Breadpay
// File: crates/hyperswitch_connectors/src/connectors/breadpay.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Refund for for Breadpay


// Implementation: impl api::PaymentsCompleteAuthorize for for Deutschebank
// File: crates/hyperswitch_connectors/src/connectors/deutschebank.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentsCompleteAuthorize for for Deutschebank


// Struct: PaymentMethodMetadata
// File: crates/router/src/core/payment_methods/transformers.rs
// Module: router
// Implementations: 0

pub struct PaymentMethodMetadata


// Implementation: impl AccountsStorageInterface for for Store
// File: crates/router/src/db.rs
// Module: router
// Methods: 0 total (0 public)

impl AccountsStorageInterface for for Store


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

pub struct NmiWebhookBody


// Struct: ExternalVaultConnectorDetails
// File: crates/diesel_models/src/business_profile.rs
// Module: diesel_models
// Implementations: 0

pub struct ExternalVaultConnectorDetails


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

impl api::PaymentAuthorize for for Trustpay


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

pub struct Capture


// Struct: SilverflowCardData
// File: crates/hyperswitch_connectors/src/connectors/silverflow/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SilverflowCardData


// Struct: KafkaFraudCheck
// File: crates/router/src/services/kafka/fraud_check.rs
// Module: router
// Implementations: 2
// Traits: super::KafkaMessage

pub struct KafkaFraudCheck<'a>


// Struct: OnboardingSurvey
// File: crates/api_models/src/user/dashboard_metadata.rs
// Module: api_models
// Implementations: 0

pub struct OnboardingSurvey


// Implementation: impl ConnectorValidation for for Tesouro
// File: crates/hyperswitch_connectors/src/connectors/tesouro.rs
// Module: hyperswitch_connectors
// Methods: 2 total (0 public)

impl ConnectorValidation for for Tesouro


// Struct: AVSDetails
// File: crates/hyperswitch_connectors/src/connectors/bambora/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AVSDetails


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

pub fn proxy_for_payments_core<F, Res, Req, Op, FData, D>(
    state: SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile_id: Option<id_type::ProfileId>,
    operation: Op,
    req: Req,
    auth_flow: services::AuthFlow,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
    return_raw_connector_response: Option<bool>,
) -> RouterResponse<Res>
where
    F: Send + Clone + Sync,
    FData: Send + Sync + Clone,
    Op: Operation<F, Req, Data = D> + Send + Sync + Clone,
    Req: Debug + Authenticate + Clone,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,
    Res: transformers::ToResponse<F, D, Op>,
    // To create connector flow specific interface data
    D: ConstructFlowSpecificData<F, FData, router_types::PaymentsResponseData>,
    RouterData<F, FData, router_types::PaymentsResponseData>: Feature<F, FData>,

    // To construct connector flow specific api
    dyn api::Connector:
        services::api::ConnectorIntegration<F, FData, router_types::PaymentsResponseData>,

    // To perform router related operation for PaymentResponse
    PaymentResponse: Operation<F, FData, Data = D>,


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

use hyperswitch_domain_models::payment_method_data::{Card, PaymentMethodData};
use masking::Secret;
use router::types::{self, api, storage::enums};
use test_utils::connector_auth;

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

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

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

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

static CONNECTOR: RedsysTest = RedsysTest {};

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: PaymentMethodData::Card(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: PaymentMethodData::Card(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: PaymentMethodData::Card(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


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

impl api::Refund for for Checkout


// Function: default_payments_list_limit
// File: crates/common_utils/src/consts.rs
// Module: common_utils

// Documentation: Default limit for payments list API

pub fn default_payments_list_limit() -> u32


// Function: get_revenue_recovery_attempt
// File: crates/router/src/core/webhooks/recovery_incoming.rs
// Module: router

pub fn get_revenue_recovery_attempt(
        payment_intent: &domain_payments::PaymentIntent,
        revenue_recovery_metadata: &api_payments::PaymentRevenueRecoveryMetadata,
        billing_connector_account: &domain::MerchantConnectorAccount,
        card_info: api_payments::AdditionalCardInfo,
        payment_processor_token: &str,
    ) -> CustomResult<Self, errors::RevenueRecoveryError>


// Function: generate_jwt_auth_token_with_attributes
// File: crates/router/src/utils/user.rs
// Module: router

pub fn generate_jwt_auth_token_with_attributes(
    state: &SessionState,
    user_id: String,
    merchant_id: id_type::MerchantId,
    org_id: id_type::OrganizationId,
    role_id: String,
    profile_id: id_type::ProfileId,
    tenant_id: Option<id_type::TenantId>,
) -> UserResult<Secret<String>>


// Struct: TokenioRouterData
// File: crates/hyperswitch_connectors/src/connectors/tokenio/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TokenioRouterData<T>


// Function: apply
// File: crates/api_models/src/payment_methods.rs
// Module: api_models

pub fn apply(&self, card_data_from_locker: Card) -> CardDetail


// Struct: PaymentInitiation
// File: crates/hyperswitch_connectors/src/connectors/tokenio/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaymentInitiation


// Struct: StripeSplitPaymentRequest
// File: crates/common_types/src/payments.rs
// Module: common_types
// Implementations: 0

// Documentation: Fee information for Split Payments to be charged on the payment being collected for Stripe

pub struct StripeSplitPaymentRequest


// Implementation: impl PayoutsNew
// File: crates/diesel_models/src/query/payouts.rs
// Module: diesel_models
// Methods: 1 total (0 public)

impl PayoutsNew


// Function: open_router_config_default
// File: crates/api_models/src/routing.rs
// Module: api_models

pub fn open_router_config_default() -> Self


// Implementation: impl ConnectorCommon for for Plaid
// File: crates/pm_auth/src/connector/plaid.rs
// Module: pm_auth
// Methods: 5 total (0 public)

impl ConnectorCommon for for Plaid


// File: crates/cards/tests/basic.rs
// Module: cards

#![allow(clippy::unwrap_used, clippy::expect_used)]

use cards::{CardExpiration, CardExpirationMonth, CardExpirationYear, CardSecurityCode};
use common_utils::date_time;
use masking::PeekInterface;

#[test]
fn test_card_security_code() {
    // no panic
    let valid_card_security_code = CardSecurityCode::try_from(1234).unwrap();

    assert_eq!(*valid_card_security_code.peek(), 1234);

    let serialized = serde_json::to_string(&valid_card_security_code).unwrap();
    assert_eq!(serialized, "1234");

    let derialized = serde_json::from_str::<CardSecurityCode>(&serialized).unwrap();
    assert_eq!(*derialized.peek(), 1234);

    let invalid_deserialization = serde_json::from_str::<CardSecurityCode>("00");
    assert!(invalid_deserialization.is_err());
}

#[test]
fn test_card_expiration_month() {
    // no panic
    let card_exp_month = CardExpirationMonth::try_from(12).unwrap();

    // will panic on unwrap
    let invalid_card_exp_month = CardExpirationMonth::try_from(13);

    assert_eq!(*card_exp_month.peek(), 12);
    assert!(invalid_card_exp_month.is_err());

    let serialized = serde_json::to_string(&card_exp_month).unwrap();
    assert_eq!(serialized, "12");

    let derialized = serde_json::from_str::<CardExpirationMonth>(&serialized).unwrap();
    assert_eq!(*derialized.peek(), 12);

    let invalid_deserialization = serde_json::from_str::<CardExpirationMonth>("13");
    assert!(invalid_deserialization.is_err());
}

#[test]
fn test_card_expiration_year() {
    let curr_date = date_time::now();
    let curr_year = u16::try_from(curr_date.year()).expect("valid year");

    // no panic
    let card_exp_year = CardExpirationYear::try_from(curr_year).unwrap();

    // will panic on unwrap
    let invalid_card_exp_year = CardExpirationYear::try_from(curr_year - 1);

    assert_eq!(*card_exp_year.peek(), curr_year);
    assert!(invalid_card_exp_year.is_err());

    let serialized = serde_json::to_string(&card_exp_year).unwrap();
    assert_eq!(serialized, curr_year.to_string());

    let derialized = serde_json::from_str::<CardExpirationYear>(&serialized).unwrap();
    assert_eq!(*derialized.peek(), curr_year);

    let invalid_deserialization = serde_json::from_str::<CardExpirationYear>("123");
    assert!(invalid_deserialization.is_err());
}

#[test]
fn test_card_expiration() {
    let curr_date = date_time::now();
    let curr_year = u16::try_from(curr_date.year()).expect("valid year");
    let curr_month = u8::from(curr_date.month());

    // no panic
    let card_exp = CardExpiration::try_from((curr_month, curr_year)).unwrap();

    // will panic on unwrap
    let invalid_card_exp = CardExpiration::try_from((13, curr_year));

    assert_eq!(*card_exp.get_month().peek(), curr_month);
    assert_eq!(*card_exp.get_year().peek(), curr_year);
    assert!(!card_exp.is_expired().unwrap());

    assert!(invalid_card_exp.is_err());

    let serialized = serde_json::to_string(&card_exp).unwrap();
    let expected_string = format!(r#"{{"month":{},"year":{}}}"#, 3, curr_year);
    assert_eq!(serialized, expected_string);

    let derialized = serde_json::from_str::<CardExpiration>(&serialized).unwrap();
    assert_eq!(*derialized.get_month().peek(), 3);
    assert_eq!(*derialized.get_year().peek(), curr_year);

    let invalid_serialized_string = r#"{"month":13,"year":123}"#;
    let invalid_deserialization = serde_json::from_str::<CardExpiration>(invalid_serialized_string);
    assert!(invalid_deserialization.is_err());
}


// Function: create_grpc_request
// File: crates/external_services/src/grpc_client.rs
// Module: external_services

pub fn create_grpc_request<T: Debug>(message: T, headers: GrpcHeaders) -> tonic::Request<T>


// Function: record_value
// File: crates/router_env/src/logger/storage.rs
// Module: router_env

pub fn record_value(&mut self, key: &'a str, value: serde_json::Value)


// Function: dummy_connector_complete_payment
// File: crates/router/src/routes/dummy_connector.rs
// Module: router

pub fn dummy_connector_complete_payment(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    path: web::Path<String>,
    json_payload: web::Query<types::DummyConnectorPaymentCompleteBody>,
) -> impl actix_web::Responder


// Function: is_external_vault_enabled
// File: crates/hyperswitch_domain_models/src/business_profile.rs
// Module: hyperswitch_domain_models

pub fn is_external_vault_enabled(&self) -> bool


// Struct: CardAdditionalData
// File: crates/common_utils/src/payout_method_utils.rs
// Module: common_utils
// Implementations: 0

pub struct CardAdditionalData


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

pub struct AffirmCaptureRequest


// File: crates/hyperswitch_connectors/src/metrics.rs
// Module: hyperswitch_connectors

//! Metrics interface

use router_env::{counter_metric, global_meter};

global_meter!(GLOBAL_METER, "ROUTER_API");

counter_metric!(CONNECTOR_RESPONSE_DESERIALIZATION_FAILURE, GLOBAL_METER);


// Function: diesel_make_pg_pool
// File: crates/drainer/src/connection.rs
// Module: drainer

pub fn diesel_make_pg_pool(
    database: &Database,
    _test_transaction: bool,
    schema: &str,
) -> PgPool


// Struct: AdyenplatformInstantStatus
// File: crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct AdyenplatformInstantStatus


// Struct: ChangePasswordRequest
// File: crates/api_models/src/user.rs
// Module: api_models
// Implementations: 0

pub struct ChangePasswordRequest


// Trait: KafkaMessage
// File: crates/router/src/services/kafka.rs
// Module: router

pub trait KafkaMessage


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

impl api::PaymentsCreateOrder for for Razorpay


// Struct: RiskifiedWebhookBody
// File: crates/hyperswitch_connectors/src/connectors/riskified/transformers/api.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RiskifiedWebhookBody