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

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

use base64::Engine;
use common_enums::enums::{self, PaymentMethodType};
use common_utils::{
    consts,
    errors::CustomResult,
    ext_traits::{ByteSliceExt, OptionExt},
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{
        AmountConvertor, MinorUnit, MinorUnitForConnector, StringMinorUnit,
        StringMinorUnitForConnector,
    },
};
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    api::ApplicationResponse,
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{
            Authorize, Capture, PSync, PaymentMethodToken, PreProcessing, Session, SetupMandate,
            Void,
        },
        refunds::{Execute, RSync},
        Accept, Defend, Evidence, GiftCardBalanceCheck, Retrieve, Upload,
    },
    router_request_types::{
        AcceptDisputeRequestData, AccessTokenRequestData, DefendDisputeRequestData,
        GiftCardBalanceCheckRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData, PaymentsSessionData,
        PaymentsSyncData, RefundsData, RetrieveFileRequestData, SetupMandateRequestData,
        SubmitEvidenceRequestData, SyncRequestType, UploadFileRequestData,
    },
    router_response_types::{
        AcceptDisputeResponse, ConnectorInfo, DefendDisputeResponse,
        GiftCardBalanceCheckResponseData, PaymentMethodDetails, PaymentsResponseData,
        RefundsResponseData, RetrieveFileResponse, SubmitEvidenceResponse, SupportedPaymentMethods,
        SupportedPaymentMethodsExt, UploadFileResponse,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsGiftCardBalanceCheckRouterData, PaymentsPreProcessingRouterData,
        PaymentsSyncRouterData, RefundsRouterData, SetupMandateRouterData,
    },
};
#[cfg(feature = "payouts")]
use hyperswitch_domain_models::{
    router_flow_types::payouts::{PoCancel, PoCreate, PoEligibility, PoFulfill},
    router_response_types::PayoutsResponseData,
    types::{PayoutsData, PayoutsRouterData},
};
#[cfg(feature = "payouts")]
use hyperswitch_interfaces::types::{
    PayoutCancelType, PayoutCreateType, PayoutEligibilityType, PayoutFulfillType,
};
use hyperswitch_interfaces::{
    api::{
        self,
        disputes::{AcceptDispute, DefendDispute, Dispute, SubmitEvidence},
        files::{FilePurpose, FileUpload, RetrieveFile, UploadFile},
        CaptureSyncMethod, ConnectorCommon, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    configs::Connectors,
    consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE},
    disputes, errors,
    events::connector_api_logs::ConnectorEvent,
    types::{
        AcceptDisputeType, DefendDisputeType, PaymentsAuthorizeType, PaymentsCaptureType,
        PaymentsGiftCardBalanceCheckType, PaymentsPreProcessingType, PaymentsSyncType,
        PaymentsVoidType, RefundExecuteType, Response, SetupMandateType, SubmitEvidenceType,
    },
    webhooks::{IncomingWebhook, IncomingWebhookFlowError, IncomingWebhookRequestDetails},
};
use masking::{ExposeInterface, Mask, Maskable, Secret};
use ring::hmac;
use router_env::{instrument, tracing};
use transformers as adyen;

#[cfg(feature = "payouts")]
use crate::utils::PayoutsData as UtilsPayoutData;
use crate::{
    capture_method_not_supported,
    constants::{self, headers},
    types::{
        AcceptDisputeRouterData, DefendDisputeRouterData, ResponseRouterData,
        SubmitEvidenceRouterData,
    },
    utils::{
        convert_amount, convert_payment_authorize_router_response,
        convert_setup_mandate_router_data_to_authorize_router_data, is_mandate_supported,
        ForeignTryFrom, PaymentMethodDataType,
    },
};
const ADYEN_API_VERSION: &str = "v68";

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

impl Adyen {
    pub const fn new() -> &'static Self {
        &Self {
            amount_converter: &MinorUnitForConnector,
            amount_converter_webhooks: &StringMinorUnitForConnector,
        }
    }
}
impl ConnectorCommon for Adyen {
    fn id(&self) -> &'static str {
        "adyen"
    }
    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Minor
    }
    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let auth = adyen::AdyenAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::X_API_KEY.to_string(),
            auth.api_key.into_masked(),
        )])
    }
    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.adyen.base_url.as_ref()
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        let response: adyen::AdyenErrorResponse = res
            .response
            .parse_struct("ErrorResponse")
            .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.error_code,
            message: response.message.to_owned(),
            reason: Some(response.message),
            attempt_status: None,
            connector_transaction_id: response.psp_reference,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Adyen {
    fn validate_connector_against_payment_request(
        &self,
        capture_method: Option<enums::CaptureMethod>,
        _payment_method: enums::PaymentMethod,
        pmt: Option<PaymentMethodType>,
    ) -> CustomResult<(), errors::ConnectorError> {
        let capture_method = capture_method.unwrap_or_default();
        let connector = self.id();
        match pmt {
            Some(payment_method_type) => match payment_method_type {
                #[cfg(feature = "v1")]
                PaymentMethodType::Affirm
                | PaymentMethodType::AfterpayClearpay
                | PaymentMethodType::ApplePay
                | PaymentMethodType::Credit
                | PaymentMethodType::Debit
                | PaymentMethodType::GooglePay
                | PaymentMethodType::MobilePay
                | PaymentMethodType::PayBright
                | PaymentMethodType::Sepa
                | PaymentMethodType::Vipps
                | PaymentMethodType::Venmo
                | PaymentMethodType::Paypal => match capture_method {
                    enums::CaptureMethod::Automatic
                    | enums::CaptureMethod::SequentialAutomatic
                    | enums::CaptureMethod::Manual
                    | enums::CaptureMethod::ManualMultiple => Ok(()),
                    enums::CaptureMethod::Scheduled => {
                        capture_method_not_supported!(
                            connector,
                            capture_method,
                            payment_method_type
                        )
                    }
                },
                #[cfg(feature = "v2")]
                PaymentMethodType::Affirm
                | PaymentMethodType::AfterpayClearpay
                | PaymentMethodType::ApplePay
                | PaymentMethodType::Credit
                | PaymentMethodType::Debit
                | PaymentMethodType::Card
                | PaymentMethodType::GooglePay
                | PaymentMethodType::MobilePay
                | PaymentMethodType::PayBright
                | PaymentMethodType::Sepa
                | PaymentMethodType::Vipps
                | PaymentMethodType::Venmo
                | PaymentMethodType::Skrill
                | PaymentMethodType::Paypal => match capture_method {
                    enums::CaptureMethod::Automatic
                    | enums::CaptureMethod::SequentialAutomatic
                    | enums::CaptureMethod::Manual
                    | enums::CaptureMethod::ManualMultiple => Ok(()),
                    enums::CaptureMethod::Scheduled => {
                        capture_method_not_supported!(
                            connector,
                            capture_method,
                            payment_method_type
                        )
                    }
                },
                PaymentMethodType::Ach
                | PaymentMethodType::SamsungPay
                | PaymentMethodType::Paze
                | PaymentMethodType::Alma
                | PaymentMethodType::Bacs
                | PaymentMethodType::Givex
                | PaymentMethodType::Klarna
                | PaymentMethodType::Twint
                | PaymentMethodType::Walley => match capture_method {
                    enums::CaptureMethod::Automatic
                    | enums::CaptureMethod::Manual
                    | enums::CaptureMethod::SequentialAutomatic => Ok(()),
                    enums::CaptureMethod::ManualMultiple | enums::CaptureMethod::Scheduled => {
                        capture_method_not_supported!(
                            connector,
                            capture_method,
                            payment_method_type
                        )
                    }
                },

                PaymentMethodType::AliPay
                | PaymentMethodType::AliPayHk
                | PaymentMethodType::Atome
                | PaymentMethodType::BancontactCard
                | PaymentMethodType::Benefit
                | PaymentMethodType::Bizum
                | PaymentMethodType::Blik
                | PaymentMethodType::Boleto
                | PaymentMethodType::Dana
                | PaymentMethodType::Eps
                | PaymentMethodType::OnlineBankingFpx
                | PaymentMethodType::Gcash
                | PaymentMethodType::GoPay
                | PaymentMethodType::Ideal
                | PaymentMethodType::KakaoPay
                | PaymentMethodType::Knet
                | PaymentMethodType::MbWay
                | PaymentMethodType::Momo
                | PaymentMethodType::MomoAtm
                | PaymentMethodType::OnlineBankingFinland
                | PaymentMethodType::OnlineBankingPoland
                | PaymentMethodType::OnlineBankingSlovakia
                | PaymentMethodType::OnlineBankingThailand
                | PaymentMethodType::Oxxo
                | PaymentMethodType::PaySafeCard
                | PaymentMethodType::Pix
                | PaymentMethodType::Swish
                | PaymentMethodType::TouchNGo
                | PaymentMethodType::Trustly
                | PaymentMethodType::WeChatPay
                | PaymentMethodType::DanamonVa
                | PaymentMethodType::BcaBankTransfer
                | PaymentMethodType::BriVa
                | PaymentMethodType::BniVa
                | PaymentMethodType::CimbVa
                | PaymentMethodType::MandiriVa
                | PaymentMethodType::Alfamart
                | PaymentMethodType::Indomaret
                | PaymentMethodType::FamilyMart
                | PaymentMethodType::Seicomart
                | PaymentMethodType::PayEasy
                | PaymentMethodType::MiniStop
                | PaymentMethodType::Lawson
                | PaymentMethodType::SevenEleven
                | PaymentMethodType::OpenBankingUk
                | PaymentMethodType::OnlineBankingCzechRepublic
                | PaymentMethodType::PermataBankTransfer => match capture_method {
                    enums::CaptureMethod::Automatic | enums::CaptureMethod::SequentialAutomatic => {
                        Ok(())
                    }
                    enums::CaptureMethod::Manual
                    | enums::CaptureMethod::ManualMultiple
                    | enums::CaptureMethod::Scheduled => {
                        capture_method_not_supported!(
                            connector,
                            capture_method,
                            payment_method_type
                        )
                    }
                },
                PaymentMethodType::AmazonPay
                | PaymentMethodType::Breadpay
                | PaymentMethodType::Paysera
                | PaymentMethodType::Skrill
                | PaymentMethodType::CardRedirect
                | PaymentMethodType::DirectCarrierBilling
                | PaymentMethodType::Fps
                | PaymentMethodType::BhnCardNetwork
                | PaymentMethodType::DuitNow
                | PaymentMethodType::Interac
                | PaymentMethodType::Multibanco
                | PaymentMethodType::Przelewy24
                | PaymentMethodType::Becs
                | PaymentMethodType::Eft
                | PaymentMethodType::ClassicReward
                | PaymentMethodType::Pse
                | PaymentMethodType::LocalBankTransfer
                | PaymentMethodType::Efecty
                | PaymentMethodType::Giropay
                | PaymentMethodType::PagoEfectivo
                | PaymentMethodType::PromptPay
                | PaymentMethodType::RedCompra
                | PaymentMethodType::RedPagos
                | PaymentMethodType::Sofort
                | PaymentMethodType::CryptoCurrency
                | PaymentMethodType::Evoucher
                | PaymentMethodType::Cashapp
                | PaymentMethodType::UpiCollect
                | PaymentMethodType::UpiIntent
                | PaymentMethodType::VietQr
                | PaymentMethodType::Mifinity
                | PaymentMethodType::LocalBankRedirect
                | PaymentMethodType::OpenBankingPIS
                | PaymentMethodType::InstantBankTransfer
                | PaymentMethodType::InstantBankTransferFinland
                | PaymentMethodType::InstantBankTransferPoland
                | PaymentMethodType::IndonesianBankTransfer
                | PaymentMethodType::SepaBankTransfer
                | PaymentMethodType::Flexiti
                | PaymentMethodType::RevolutPay
                | PaymentMethodType::Bluecode => {
                    capture_method_not_supported!(connector, capture_method, payment_method_type)
                }
            },
            None => match capture_method {
                enums::CaptureMethod::Automatic
                | enums::CaptureMethod::SequentialAutomatic
                | enums::CaptureMethod::Manual
                | enums::CaptureMethod::ManualMultiple => Ok(()),
                enums::CaptureMethod::Scheduled => {
                    capture_method_not_supported!(connector, capture_method)
                }
            },
        }
    }
    fn validate_mandate_payment(
        &self,
        pm_type: Option<PaymentMethodType>,
        pm_data: PaymentMethodData,
    ) -> CustomResult<(), errors::ConnectorError> {
        let mandate_supported_pmd = std::collections::HashSet::from([
            PaymentMethodDataType::Card,
            PaymentMethodDataType::ApplePay,
            PaymentMethodDataType::GooglePay,
            PaymentMethodDataType::PaypalRedirect,
            PaymentMethodDataType::MomoRedirect,
            PaymentMethodDataType::KakaoPayRedirect,
            PaymentMethodDataType::GoPayRedirect,
            PaymentMethodDataType::GcashRedirect,
            PaymentMethodDataType::DanaRedirect,
            PaymentMethodDataType::TwintRedirect,
            PaymentMethodDataType::VippsRedirect,
            PaymentMethodDataType::KlarnaRedirect,
            PaymentMethodDataType::Ideal,
            PaymentMethodDataType::OpenBankingUk,
            PaymentMethodDataType::Trustly,
            PaymentMethodDataType::BancontactCard,
            PaymentMethodDataType::AchBankDebit,
            PaymentMethodDataType::SepaBankDebit,
            PaymentMethodDataType::BecsBankDebit,
        ]);
        is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }

    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> {
        if data.encoded_data.is_some() {
            return Ok(());
        }
        Err(errors::ConnectorError::MissingRequiredField {
            field_name: "encoded_data",
        }
        .into())
    }
    fn is_webhook_source_verification_mandatory(&self) -> bool {
        true
    }
}

impl api::Payment for Adyen {}
impl api::PaymentAuthorize for Adyen {}
impl api::PaymentSync for Adyen {}
impl api::PaymentVoid for Adyen {}
impl api::PaymentCapture for Adyen {}
impl api::MandateSetup for Adyen {}
impl api::ConnectorAccessToken for Adyen {}
impl api::PaymentToken for Adyen {}
impl api::PaymentsGiftCardBalanceCheck for Adyen {}

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

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Adyen {
    // Not Implemented (R)
}

fn build_env_specific_endpoint(
    base_url: &str,
    test_mode: Option<bool>,
    connector_metadata: &Option<common_utils::pii::SecretSerdeValue>,
) -> CustomResult<String, errors::ConnectorError> {
    if test_mode.unwrap_or(true) {
        Ok(base_url.to_string())
    } else {
        let adyen_connector_metadata_object =
            transformers::AdyenConnectorMetadataObject::try_from(connector_metadata)?;
        let endpoint_prefix = adyen_connector_metadata_object.endpoint_prefix.ok_or(
            errors::ConnectorError::InvalidConnectorConfig {
                config: "metadata.endpoint_prefix",
            },
        )?;
        Ok(base_url.replace("{{merchant_endpoint_prefix}}", &endpoint_prefix))
    }
}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Adyen {
    fn get_headers(
        &self,
        req: &SetupMandateRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            SetupMandateType::get_content_type(self).to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_url(
        &self,
        req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!("{endpoint}{ADYEN_API_VERSION}/payments"))
    }
    fn get_request_body(
        &self,
        req: &SetupMandateRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let authorize_req = convert_payment_authorize_router_response((
            req,
            convert_setup_mandate_router_data_to_authorize_router_data(req),
        ));

        let amount = convert_amount(
            self.amount_converter,
            authorize_req.request.minor_amount,
            authorize_req.request.currency,
        )?;

        let connector_router_data = adyen::AdyenRouterData::try_from((amount, &authorize_req))?;
        let connector_req = adyen::AdyenPaymentRequest::try_from(&connector_router_data)?;

        Ok(RequestContent::Json(Box::new(connector_req)))
    }
    fn build_request(
        &self,
        req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&SetupMandateType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(SetupMandateType::get_headers(self, req, connectors)?)
                .set_body(SetupMandateType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }
    fn handle_response(
        &self,
        data: &SetupMandateRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<
        RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        errors::ConnectorError,
    >
    where
        SetupMandate: Clone,
        SetupMandateRequestData: Clone,
        PaymentsResponseData: Clone,
    {
        let response: adyen::AdyenPaymentResponse = res
            .response
            .parse_struct("AdyenPaymentResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::foreign_try_from((
            ResponseRouterData {
                response,
                data: data.clone(),
                http_code: res.status_code,
            },
            None,
            false,
            data.request.payment_method_type,
        ))
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }
    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
    fn get_5xx_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl api::PaymentSession for Adyen {}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Adyen {
    // Not Implemented (R)
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Adyen {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            self.common_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)
    }

    fn get_url(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let id = req.request.connector_transaction_id.as_str();

        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}{ADYEN_API_VERSION}/payments/{id}/captures",
        ))
    }
    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount_to_capture = convert_amount(
            self.amount_converter,
            req.request.minor_amount_to_capture,
            req.request.currency,
        )?;

        let connector_router_data = adyen::AdyenRouterData::try_from((amount_to_capture, req))?;
        let connector_req = adyen::AdyenCaptureRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }
    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsCaptureType::get_url(self, req, connectors)?)
                .attach_default_headers()
                .headers(PaymentsCaptureType::get_headers(self, req, connectors)?)
                .set_body(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: adyen::AdyenCaptureResponse = res
            .response
            .parse_struct("AdyenCaptureResponse")
            .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,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }
    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
    fn get_5xx_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, errors::ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

/// Payment Sync can be useful only incase of Redirect flow.
/// For payments which doesn't involve redrection we have to rely on webhooks.
impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Adyen {
    fn get_headers(
        &self,
        req: &RouterData<PSync, PaymentsSyncData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsSyncType::get_content_type(self).to_string().into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_request_body(
        &self,
        req: &RouterData<PSync, PaymentsSyncData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let encoded_data = req
            .request
            .encoded_data
            .clone()
            .get_required_value("encoded_data")
            .change_context(errors::ConnectorError::RequestEncodingFailed)?;
        let adyen_redirection_type = serde_urlencoded::from_str::<
            transformers::AdyenRedirectRequestTypes,
        >(encoded_data.as_str())
        .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

        let connector_req = match adyen_redirection_type {
            adyen::AdyenRedirectRequestTypes::AdyenRedirection(req) => {
                adyen::AdyenRedirectRequest {
                    details: adyen::AdyenRedirectRequestTypes::AdyenRedirection(
                        adyen::AdyenRedirection {
                            redirect_result: req.redirect_result,
                            type_of_redirection_result: None,
                            result_code: None,
                        },
                    ),
                }
            }
            adyen::AdyenRedirectRequestTypes::AdyenThreeDS(req) => adyen::AdyenRedirectRequest {
                details: adyen::AdyenRedirectRequestTypes::AdyenThreeDS(adyen::AdyenThreeDS {
                    three_ds_result: req.three_ds_result,
                    type_of_redirection_result: None,
                    result_code: None,
                }),
            },
            adyen::AdyenRedirectRequestTypes::AdyenRefusal(req) => adyen::AdyenRedirectRequest {
                details: adyen::AdyenRedirectRequestTypes::AdyenRefusal(adyen::AdyenRefusal {
                    payload: req.payload,
                    type_of_redirection_result: None,
                    result_code: None,
                }),
            },
        };

        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn get_url(
        &self,
        req: &RouterData<PSync, PaymentsSyncData, PaymentsResponseData>,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!("{endpoint}{ADYEN_API_VERSION}/payments/details"))
    }

    fn build_request(
        &self,
        req: &RouterData<PSync, PaymentsSyncData, PaymentsResponseData>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        // Adyen doesn't support PSync flow. We use PSync flow to fetch payment details,
        // specifically the redirect URL that takes the user to their Payment page. In non-redirection flows,
        // we rely on webhooks to obtain the payment status since there is no encoded data available.
        // encoded_data only includes the redirect URL and is only relevant in redirection flows.
        if req
            .request
            .encoded_data
            .clone()
            .get_required_value("encoded_data")
            .is_ok()
        {
            Ok(Some(
                RequestBuilder::new()
                    .method(Method::Post)
                    .url(&PaymentsSyncType::get_url(self, req, connectors)?)
                    .attach_default_headers()
                    .headers(PaymentsSyncType::get_headers(self, req, connectors)?)
                    .set_body(PaymentsSyncType::get_request_body(self, req, connectors)?)
                    .build(),
            ))
        } else {
            Ok(None)
        }
    }

    fn handle_response(
        &self,
        data: &RouterData<PSync, PaymentsSyncData, PaymentsResponseData>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        router_env::logger::debug!(payment_sync_response=?res);
        let response: adyen::AdyenPaymentResponse = res
            .response
            .parse_struct("AdyenPaymentResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;

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

        let is_multiple_capture_sync = match data.request.sync_type {
            SyncRequestType::MultipleCaptureSync(_) => true,
            SyncRequestType::SinglePaymentSync => false,
        };
        RouterData::foreign_try_from((
            ResponseRouterData {
                response,
                data: data.clone(),
                http_code: res.status_code,
            },
            data.request.capture_method,
            is_multiple_capture_sync,
            data.request.payment_method_type,
        ))
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

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

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Adyen {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError>
    where
        Self: ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData>,
    {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsAuthorizeType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_url(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!("{endpoint}{ADYEN_API_VERSION}/payments"))
    }

    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 = adyen::AdyenRouterData::try_from((amount, req))?;
        let connector_req = adyen::AdyenPaymentRequest::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(&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: adyen::AdyenPaymentResponse = res
            .response
            .parse_struct("AdyenPaymentResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::foreign_try_from((
            ResponseRouterData {
                response,
                data: data.clone(),
                http_code: res.status_code,
            },
            data.request.capture_method,
            false,
            data.request.payment_method_type,
        ))
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

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

impl api::PaymentsPreProcessing for Adyen {}

impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResponseData>
    for Adyen
{
    fn get_headers(
        &self,
        req: &PaymentsPreProcessingRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![(
            headers::CONTENT_TYPE.to_string(),
            PaymentsPreProcessingType::get_content_type(self)
                .to_string()
                .into(),
        )];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }

    fn get_url(
        &self,
        req: &PaymentsPreProcessingRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let endpoint = build_env_specific_endpoint(
            self.base_url(connectors),
            req.test_mode,
            &req.connector_meta_data,
        )?;
        Ok(format!(
            "{endpoint}{ADYEN_API_VERSION}/paymentMethods/balance",
        ))
    }

    fn get_request_body(
        &self,
        req: &PaymentsPreProcessingRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let connector_req = adyen::AdyenBalanceRequest::try_from(req)?;

        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsPreProcessingRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsPreProcessingRouterData, errors::ConnectorError> {
        let response: adyen::AdyenBalanceResponse = res
            .response
            .parse_struct("AdyenBalanceResponse")
            .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        let currency = match data.request.currency {
            Some(currency) => currency,


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

impl api::RefundExecute for for Trustpay


// Struct: ApplePayTokenPaymentInformation
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ApplePayTokenPaymentInformation


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

pub fn external_vault_proxy_for_payments_operation_core<F, Req, Op, FData, D>(
    state: &SessionState,
    req_state: ReqState,
    merchant_context: domain::MerchantContext,
    profile: domain::Profile,
    operation: Op,
    req: Req,
    get_tracker_response: operations::GetTrackerResponse<D>,
    call_connector_action: CallConnectorAction,
    header_payload: HeaderPayload,
    return_raw_connector_response: Option<bool>,
) -> RouterResult<(D, Req, Option<u16>, Option<u128>)>
where
    F: Send + Clone + Sync,
    Req: Send + Sync + Authenticate,
    Op: Operation<F, Req, Data = D> + Send + Sync,
    D: OperationSessionGetters<F> + OperationSessionSetters<F> + Send + Sync + Clone,

    // 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>,

    RouterData<F, FData, router_types::PaymentsResponseData>:
        hyperswitch_domain_models::router_data::TrackerPostUpdateObjects<F, FData, D>,

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


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

impl Responder


// Struct: InviteUser
// File: crates/router/src/services/email/types.rs
// Module: router
// Implementations: 1
// Traits: EmailData

pub struct InviteUser


// Struct: Checkout
// File: crates/hyperswitch_domain_models/src/router_flow_types/fraud_check.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct Checkout


// Function: post_3ds_payments_authorize
// File: crates/router/src/routes/payments.rs
// Module: router

pub fn post_3ds_payments_authorize(
    state: web::Data<app::AppState>,
    req: actix_web::HttpRequest,
    json_payload: Option<web::Form<serde_json::Value>>,
    path: web::Path<(
        common_utils::id_type::PaymentId,
        common_utils::id_type::MerchantId,
        String,
    )


// Struct: TsysReturnRequest
// File: crates/hyperswitch_connectors/src/connectors/tsys/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct TsysReturnRequest


// Struct: AddressNew
// File: crates/diesel_models/src/address.rs
// Module: diesel_models
// Implementations: 0

pub struct AddressNew


// Struct: UnifiedAuthenticationService
// File: crates/hyperswitch_connectors/src/connectors/unified_authentication_service.rs
// Module: hyperswitch_connectors
// Implementations: 22
// Traits: api::Payment, api::PaymentSession, api::ConnectorAccessToken, api::MandateSetup, api::PaymentAuthorize, api::PaymentSync, api::PaymentCapture, api::PaymentVoid, api::Refund, api::RefundExecute, api::RefundSync, api::PaymentToken, api::UnifiedAuthenticationService, api::UasPreAuthentication, api::UasPostAuthentication, api::UasAuthenticationConfirmation, api::UasAuthentication, ConnectorCommon, ConnectorValidation, webhooks::IncomingWebhook, ConnectorSpecifications

pub struct UnifiedAuthenticationService

api::Payment api::PaymentSession api::ConnectorAccessToken api::MandateSetup api::PaymentAuthorize api::PaymentSync api::PaymentCapture api::PaymentVoid api::Refund api::RefundExecute api::RefundSync api::PaymentToken api::UnifiedAuthenticationService api::UasPreAuthentication api::UasPostAuthentication api::UasAuthenticationConfirmation api::UasAuthentication ConnectorCommon ConnectorValidation webhooks::IncomingWebhook ConnectorSpecifications


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

impl ConnectorCommon for for Stax


// Implementation: impl events::EventInfo for for AuthenticationType
// File: crates/router/src/services/authentication.rs
// Module: router
// Methods: 2 total (0 public)

impl events::EventInfo for for AuthenticationType


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

pub struct SkrillData


// File: crates/router_env/src/metrics.rs
// Module: router_env
// Public functions: 1

//! Utilities to easily create opentelemetry contexts, meters and metrics.

/// Create a global [`Meter`][Meter] with the specified name and an optional description.
///
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! global_meter {
    ($name:ident) => {
        static $name: ::std::sync::LazyLock<$crate::opentelemetry::metrics::Meter> =
            ::std::sync::LazyLock::new(|| $crate::opentelemetry::global::meter(stringify!($name)));
    };
    ($meter:ident, $name:literal) => {
        static $meter: ::std::sync::LazyLock<$crate::opentelemetry::metrics::Meter> =
            ::std::sync::LazyLock::new(|| $crate::opentelemetry::global::meter(stringify!($name)));
    };
}

/// Create a [`Counter`][Counter] metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Counter]: opentelemetry::metrics::Counter
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! counter_metric {
    ($name:ident, $meter:ident) => {
        pub(crate) static $name: ::std::sync::LazyLock<
            $crate::opentelemetry::metrics::Counter<u64>,
        > = ::std::sync::LazyLock::new(|| $meter.u64_counter(stringify!($name)).build());
    };
    ($name:ident, $meter:ident, description:literal) => {
        #[doc = $description]
        pub(crate) static $name: ::std::sync::LazyLock<
            $crate::opentelemetry::metrics::Counter<u64>,
        > = ::std::sync::LazyLock::new(|| {
            $meter
                .u64_counter(stringify!($name))
                .with_description($description)
                .build()
        });
    };
}

/// Create a [`Histogram`][Histogram] f64 metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Histogram]: opentelemetry::metrics::Histogram
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! histogram_metric_f64 {
    ($name:ident, $meter:ident) => {
        pub(crate) static $name: ::std::sync::LazyLock<
            $crate::opentelemetry::metrics::Histogram<f64>,
        > = ::std::sync::LazyLock::new(|| {
            $meter
                .f64_histogram(stringify!($name))
                .with_boundaries($crate::metrics::f64_histogram_buckets())
                .build()
        });
    };
    ($name:ident, $meter:ident, $description:literal) => {
        #[doc = $description]
        pub(crate) static $name: ::std::sync::LazyLock<
            $crate::opentelemetry::metrics::Histogram<f64>,
        > = ::std::sync::LazyLock::new(|| {
            $meter
                .f64_histogram(stringify!($name))
                .with_description($description)
                .with_boundaries($crate::metrics::f64_histogram_buckets())
                .build()
        });
    };
}

/// Create a [`Histogram`][Histogram] u64 metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Histogram]: opentelemetry::metrics::Histogram
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! histogram_metric_u64 {
    ($name:ident, $meter:ident) => {
        pub(crate) static $name: ::std::sync::LazyLock<
            $crate::opentelemetry::metrics::Histogram<u64>,
        > = ::std::sync::LazyLock::new(|| {
            $meter
                .u64_histogram(stringify!($name))
                .with_boundaries($crate::metrics::f64_histogram_buckets())
                .build()
        });
    };
    ($name:ident, $meter:ident, $description:literal) => {
        #[doc = $description]
        pub(crate) static $name: ::std::sync::LazyLock<
            $crate::opentelemetry::metrics::Histogram<u64>,
        > = ::std::sync::LazyLock::new(|| {
            $meter
                .u64_histogram(stringify!($name))
                .with_description($description)
                .with_boundaries($crate::metrics::f64_histogram_buckets())
                .build()
        });
    };
}

/// Create a [`Gauge`][Gauge] metric with the specified name and an optional description,
/// associated with the specified meter. Note that the meter must be a valid [`Meter`][Meter].
///
/// [Gauge]: opentelemetry::metrics::Gauge
/// [Meter]: opentelemetry::metrics::Meter
#[macro_export]
macro_rules! gauge_metric {
    ($name:ident, $meter:ident) => {
        pub(crate) static $name: ::std::sync::LazyLock<$crate::opentelemetry::metrics::Gauge<u64>> =
            ::std::sync::LazyLock::new(|| $meter.u64_gauge(stringify!($name)).build());
    };
    ($name:ident, $meter:ident, description:literal) => {
        #[doc = $description]
        pub(crate) static $name: ::std::sync::LazyLock<$crate::opentelemetry::metrics::Gauge<u64>> =
            ::std::sync::LazyLock::new(|| {
                $meter
                    .u64_gauge(stringify!($name))
                    .with_description($description)
                    .build()
            });
    };
}

/// Create attributes to associate with a metric from key-value pairs.
#[macro_export]
macro_rules! metric_attributes {
    ($(($key:expr, $value:expr $(,)?)),+ $(,)?) => {
        &[$($crate::opentelemetry::KeyValue::new($key, $value)),+]
    };
}

pub use helpers::f64_histogram_buckets;

mod helpers {
    /// Returns the buckets to be used for a f64 histogram
    #[inline(always)]
    pub fn f64_histogram_buckets() -> Vec<f64> {
        let mut init = 0.01;
        let mut buckets: [f64; 15] = [0.0; 15];

        for bucket in &mut buckets {
            init *= 2.0;
            *bucket = init;
        }

        Vec::from(buckets)
    }
}


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

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

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

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

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

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

static CONNECTOR: BokuTest = BokuTest {};

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


// Function: export_collection
// File: crates/test_utils/src/newman_runner.rs
// Module: test_utils

pub fn export_collection(connector_name: &str, collection_dir_path: String)


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

pub struct CaptureBuyerRequest


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

// Public submodules:
pub mod transformers;


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

impl api::PaymentCapture for for Celero


// Implementation: impl PaymentMethodsData
// File: crates/hyperswitch_domain_models/src/payment_method_data.rs
// Module: hyperswitch_domain_models
// Methods: 4 total (4 public)

impl PaymentMethodsData


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

impl api::Refund for for Globepay


    merchant_context: domain::MerchantContext,
    payment_intent: &PaymentIntent,
    revenue_recovery_payment_data: &storage::revenue_recovery::RevenueRecoveryPaymentData,
    latest_attempt_id: &id_type::GlobalAttemptId,
) -> RecoveryResult<()> {
    let db = &*state.store;
    let id = payment_intent.id.clone();
    let task = revenue_recovery_core::CALCULATE_WORKFLOW;
    let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;

    let old_tracking_data: pcr::RevenueRecoveryWorkflowTrackingData =
        serde_json::from_value(process.tracking_data.clone())
            .change_context(errors::RecoveryError::ValueNotFound)
            .attach_printable("Failed to deserialize the tracking data from process tracker")?;

    let new_tracking_data = pcr::RevenueRecoveryWorkflowTrackingData {
        payment_attempt_id: latest_attempt_id.clone(),
        ..old_tracking_data
    };

    let tracking_data = serde_json::to_value(new_tracking_data)
        .change_context(errors::RecoveryError::ValueNotFound)
        .attach_printable("Failed to serialize the tracking data for process tracker")?;

    // Construct the process tracker ID for CALCULATE_WORKFLOW
    let process_tracker_id = format!("{}_{}_{}", runner, task, id.get_string_repr());

    logger::info!(
        payment_id = %id.get_string_repr(),
        process_tracker_id = %process_tracker_id,
        "Attempting to reopen CALCULATE_WORKFLOW on payment failure"
    );

    // Find the existing CALCULATE_WORKFLOW process tracker
    let calculate_process = db
        .find_process_by_id(&process_tracker_id)
        .await
        .change_context(errors::RecoveryError::ProcessTrackerFailure)
        .attach_printable("Failed to find CALCULATE_WORKFLOW process tracker")?;

    match calculate_process {
        Some(process) => {
            logger::info!(
                payment_id = %id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                current_status = %process.business_status,
                current_retry_count = process.retry_count,
                "Found existing CALCULATE_WORKFLOW, updating status and retry count"
            );

            // Update the process tracker to reopen the calculate workflow
            // 1. Change status from "finish" to "pending"
            // 2. Increase retry count by 1
            // 3. Set business status to QUEUED
            // 4. Schedule for immediate execution
            let new_retry_count = process.retry_count + 1;
            let new_schedule_time = common_utils::date_time::now()
                + time::Duration::seconds(
                    state
                        .conf
                        .revenue_recovery
                        .recovery_timestamp
                        .reopen_workflow_buffer_time_in_seconds,
                );

            let pt_update = storage::ProcessTrackerUpdate::Update {
                name: Some(task.to_string()),
                retry_count: Some(new_retry_count),
                schedule_time: Some(new_schedule_time),
                tracking_data: Some(tracking_data),
                business_status: Some(String::from(business_status::PENDING)),
                status: Some(common_enums::ProcessTrackerStatus::Pending),
                updated_at: Some(common_utils::date_time::now()),
            };

            db.update_process(process.clone(), pt_update)
                .await
                .change_context(errors::RecoveryError::ProcessTrackerFailure)
                .attach_printable("Failed to update CALCULATE_WORKFLOW process tracker")?;

            logger::info!(
                payment_id = %id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                new_retry_count = new_retry_count,
                new_schedule_time = %new_schedule_time,
                "Successfully reopened CALCULATE_WORKFLOW with increased retry count"
            );
        }
        None => {
            logger::info!(
                payment_id = %id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                "CALCULATE_WORKFLOW process tracker not found, creating new entry"
            );

            let task = "CALCULATE_WORKFLOW";

            let db = &*state.store;

            // Create process tracker ID in the format: CALCULATE_WORKFLOW_{payment_intent_id}
            let process_tracker_id = format!("{runner}_{task}_{}", id.get_string_repr());

            // Set scheduled time to current time + buffer time set in configuration
            let schedule_time = common_utils::date_time::now()
                + time::Duration::seconds(
                    state
                        .conf
                        .revenue_recovery
                        .recovery_timestamp
                        .reopen_workflow_buffer_time_in_seconds,
                );

            let new_retry_count = process.retry_count + 1;

            // Check if a process tracker entry already exists for this payment intent
            let existing_entry = db
                .as_scheduler()
                .find_process_by_id(&process_tracker_id)
                .await
                .change_context(errors::RecoveryError::ProcessTrackerFailure)
                .attach_printable(
                    "Failed to check for existing calculate workflow process tracker entry",
                )?;

            // No entry exists - create a new one
            router_env::logger::info!(
                    "No existing CALCULATE_WORKFLOW task found for payment_intent_id: {}, creating new entry... ",
                    id.get_string_repr()
                );

            let tag = ["PCR"];
            let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;

            let process_tracker_entry = storage::ProcessTrackerNew::new(
                &process_tracker_id,
                task,
                runner,
                tag,
                process.tracking_data.clone(),
                Some(new_retry_count),
                schedule_time,
                common_types::consts::API_VERSION,
            )
            .change_context(errors::RecoveryError::ProcessTrackerFailure)
            .attach_printable("Failed to construct calculate workflow process tracker entry")?;

            // Insert into process tracker with status New
            db.as_scheduler()
                .insert_process(process_tracker_entry)
                .await
                .change_context(errors::RecoveryError::ProcessTrackerFailure)
                .attach_printable(
                    "Failed to enter calculate workflow process_tracker_entry in DB",
                )?;

            router_env::logger::info!(
                "Successfully created new CALCULATE_WORKFLOW task for payment_intent_id: {}",
                id.get_string_repr()
            );

            logger::info!(
                payment_id = %id.get_string_repr(),
                process_tracker_id = %process_tracker_id,
                "Successfully created new CALCULATE_WORKFLOW entry using perform_calculate_workflow"
            );
        }
    }

    Ok(())
}

// TODO: Move these to impl based functions
async fn record_back_to_billing_connector(
    state: &SessionState,
    payment_attempt: &PaymentAttempt,
    payment_intent: &PaymentIntent,
    billing_mca: &merchant_connector_account::MerchantConnectorAccount,
) -> RecoveryResult<()> {
    logger::info!("Entering record_back_to_billing_connector");

    let connector_name = billing_mca.connector_name.to_string();
    let connector_data = api_types::ConnectorData::get_connector_by_name(
        &state.conf.connectors,
        &connector_name,
        api_types::GetToken::Connector,
        Some(billing_mca.get_id()),
    )
    .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
    .attach_printable("invalid connector name received in billing merchant connector account")?;

    let connector_integration: services::BoxedRevenueRecoveryRecordBackInterface<
        router_flow_types::InvoiceRecordBack,
        revenue_recovery_request::InvoiceRecordBackRequest,
        revenue_recovery_response::InvoiceRecordBackResponse,
    > = connector_data.connector.get_connector_integration();

    let router_data = construct_invoice_record_back_router_data(
        state,
        billing_mca,
        payment_attempt,
        payment_intent,
    )?;

    let response = services::execute_connector_processing_step(
        state,
        connector_integration,
        &router_data,
        payments::CallConnectorAction::Trigger,
        None,
        None,
    )
    .await
    .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
    .attach_printable("Failed while handling response of record back to billing connector")?;

    match response.response {
        Ok(response) => Ok(response),
        error @ Err(_) => {
            router_env::logger::error!(?error);
            Err(errors::RecoveryError::RecordBackToBillingConnectorFailed)
                .attach_printable("Failed while recording back to billing connector")
        }
    }?;
    Ok(())
}

pub fn construct_invoice_record_back_router_data(
    state: &SessionState,
    billing_mca: &merchant_connector_account::MerchantConnectorAccount,
    payment_attempt: &PaymentAttempt,
    payment_intent: &PaymentIntent,
) -> RecoveryResult<hyperswitch_domain_models::types::InvoiceRecordBackRouterData> {
    logger::info!("Entering construct_invoice_record_back_router_data");

    let auth_type: types::ConnectorAuthType =
        helpers::MerchantConnectorAccountType::DbVal(Box::new(billing_mca.clone()))
            .get_connector_account_details()
            .parse_value("ConnectorAuthType")
            .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)?;

    let merchant_reference_id = payment_intent
        .merchant_reference_id
        .clone()
        .ok_or(errors::RecoveryError::RecordBackToBillingConnectorFailed)
        .attach_printable(
            "Merchant reference id not found while recording back to billing connector",
        )?;
    let connector_name = billing_mca.get_connector_name_as_string();
    let connector = common_enums::connector_enums::Connector::from_str(connector_name.as_str())
        .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
        .attach_printable("Cannot find connector from the connector_name")?;

    let connector_params =
        hyperswitch_domain_models::connector_endpoints::Connectors::get_connector_params(
            &state.conf.connectors,
            connector,
        )
        .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
        .attach_printable(format!(
            "cannot find connector params for this connector {connector} in this flow",
        ))?;

    let router_data = router_data_v2::RouterDataV2 {
        flow: PhantomData::<router_flow_types::InvoiceRecordBack>,
        tenant_id: state.tenant.tenant_id.clone(),
        resource_common_data: flow_common_types::InvoiceRecordBackData,
        connector_auth_type: auth_type,
        request: revenue_recovery_request::InvoiceRecordBackRequest {
            merchant_reference_id,
            amount: payment_attempt.get_total_amount(),
            currency: payment_intent.amount_details.currency,
            payment_method_type: Some(payment_attempt.payment_method_subtype),
            attempt_status: payment_attempt.status,
            connector_transaction_id: payment_attempt
                .connector_payment_id
                .as_ref()
                .map(|id| common_utils::types::ConnectorTransactionId::TxnId(id.clone())),
            connector_params,
        },
        response: Err(types::ErrorResponse::default()),
    };
    let old_router_data = flow_common_types::InvoiceRecordBackData::to_old_router_data(router_data)
        .change_context(errors::RecoveryError::RecordBackToBillingConnectorFailed)
        .attach_printable("Cannot construct record back router data")?;
    Ok(old_router_data)
}

pub fn get_payment_processor_token_id_from_payment_attempt(
    payment_attempt: &PaymentAttempt,
) -> Option<String> {
    let used_token = payment_attempt
        .connector_token_details
        .as_ref()
        .and_then(|t| t.connector_mandate_id.clone());
    logger::info!("Used token in the payment attempt : {:?}", used_token);

    used_token
}

pub struct RevenueRecoveryOutgoingWebhook;

impl RevenueRecoveryOutgoingWebhook {
    #[allow(clippy::too_many_arguments)]
    pub async fn send_outgoing_webhook_based_on_revenue_recovery_status(
        state: &SessionState,
        event_class: common_enums::EventClass,
        event_status: common_enums::EventType,
        payment_intent: &PaymentIntent,
        merchant_context: &domain::MerchantContext,
        profile: &domain::Profile,
        payment_attempt_id: String,
        payments_response: ApplicationResponse<api_models::payments::PaymentsResponse>,
    ) -> RecoveryResult<()> {
        match payments_response {
            ApplicationResponse::JsonWithHeaders((response, _headers)) => {
                let outgoing_webhook_content =
                    api_models::webhooks::OutgoingWebhookContent::PaymentDetails(Box::new(
                        response,
                    ));
                create_event_and_trigger_outgoing_webhook(
                    state.clone(),
                    profile.clone(),
                    merchant_context.get_merchant_key_store(),
                    event_status,
                    event_class,
                    payment_attempt_id,
                    enums::EventObjectType::PaymentDetails,
                    outgoing_webhook_content,
                    payment_intent.created_at,
                )
                .await
                .change_context(errors::RecoveryError::InvalidTask)
                .attach_printable("Failed to send out going webhook")?;

                Ok(())
            }

            _other_variant => {
                // Handle other successful response types if needed
                logger::warn!("Unexpected application response variant for outgoing webhook");
                Err(errors::RecoveryError::RevenueRecoveryOutgoingWebhookFailed.into())
            }
        }
    }
}


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

impl common_utils::events::ApiEventMetric for for TotalPaymentMethodCountResponse


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

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

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

struct PayUSeleniumTest;

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

async fn should_make_no_3ds_card_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = PayUSeleniumTest {};
    conn.make_redirection_payment(
        web_driver,
        vec![
            Event::Trigger(Trigger::Goto(&format!("{CHECKOUT_BASE_URL}/saved/72"))),
            Event::Trigger(Trigger::Click(By::Id("card-submit-btn"))),
            Event::Trigger(Trigger::Sleep(1)),
            Event::Assert(Assert::IsPresent("status")),
            Event::Assert(Assert::IsPresent("processing")),
        ],
    )
    .await?;
    Ok(())
}

async fn should_make_gpay_payment(web_driver: WebDriver) -> Result<(), WebDriverError> {
    let conn = PayUSeleniumTest {};
    conn.make_gpay_payment(web_driver,
        &format!("{CHECKOUT_BASE_URL}/gpay?gatewayname=payu&gatewaymerchantid=459551&amount=70.00&country=US&currency=PLN"),
        vec![
        Event::Assert(Assert::IsPresent("Status")),
        Event::Assert(Assert::IsPresent("processing")),
    ]).await?;
    Ok(())
}

#[test]
#[serial]
fn should_make_no_3ds_card_payment_test() {
    tester!(should_make_no_3ds_card_payment);
}

#[test]
#[serial]
#[ignore]
fn should_make_gpay_payment_test() {
    tester!(should_make_gpay_payment);
}


// File: crates/router/src/types/api/api_keys.rs
// Module: router

pub use api_models::api_keys::{
    ApiKeyExpiration, CreateApiKeyRequest, CreateApiKeyResponse, ListApiKeyConstraints,
    RetrieveApiKeyResponse, RevokeApiKeyResponse, UpdateApiKeyRequest,
};


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

pub fn render_pm_collect_link(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    req: payment_methods::PaymentMethodCollectLinkRenderRequest,
) -> RouterResponse<services::GenericLinkFormData>


// Struct: SyncBodyResponse
// File: crates/hyperswitch_connectors/src/connectors/bamboraapac/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct SyncBodyResponse