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

pub mod transformers;

use std::sync::LazyLock;

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

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

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

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

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

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

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

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

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        let connector_router_data = finix::FinixRouterData::from((amount, req));
        let connector_req = finix::FinixPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, errors::ConnectorError> {
        let response: finix::FinixPaymentsResponse = res
            .response
            .parse_struct("Finix 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 Finix {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

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

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: finix::FinixPaymentsResponse = res
            .response
            .parse_struct("finix 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 Finix {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

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

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

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

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

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

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

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

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

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

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

        let connector_router_data = finix::FinixRouterData::from((refund_amount, req));
        let connector_req = finix::FinixRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

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

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, errors::ConnectorError> {
        let response: finix::RefundResponse = res
            .response
            .parse_struct("finix 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 Finix {
    fn get_headers(
        &self,
        req: &RefundSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        self.build_headers(req, connectors)
    }

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

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

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

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

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

#[async_trait::async_trait]
impl webhooks::IncomingWebhook for Finix {
    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 FINIX_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> =
    LazyLock::new(SupportedPaymentMethods::new);

static FINIX_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
    display_name: "Finix",
    description: "Finix is a payments technology provider enabling businesses to accept and send payments online or in person",
    connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
    integration_status: ConnectorIntegrationStatus::Alpha,
};

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

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

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

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


// Struct: KlarnaDetails
// File: crates/hyperswitch_connectors/src/connectors/airwallex/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct KlarnaDetails


// Function: new
// File: crates/masking/src/bytes.rs
// Module: masking

// Documentation: Wrap bytes in `SecretBytesMut`

pub fn new(bytes: impl Into<BytesMut>) -> Self


// Function: add_publishable_key
// File: crates/router/src/services/authentication/decision.rs
// Module: router

pub fn add_publishable_key(
    state: &SessionState,
    api_key: Secret<String>,
    merchant_id: common_utils::id_type::MerchantId,
    expiry: Option<u64>,
) -> CustomResult<(), ApiClientError>


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

pub struct ConfiguredRouting


// Struct: ExchangeTokenRequest
// File: crates/pm_auth/src/types.rs
// Module: pm_auth
// Implementations: 0

pub struct ExchangeTokenRequest


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

pub fn is_vault_sdk_enabled(&self) -> bool


// File: crates/config_importer/src/cli.rs
// Module: config_importer

use std::path::PathBuf;

/// Utility to import a hyperswitch TOML configuration file, convert it into environment variable
/// key-value pairs, and export it in the specified format.
#[derive(clap::Parser, Debug)]
#[command(arg_required_else_help = true)]
pub(crate) struct Args {
    /// Input TOML configuration file.
    #[arg(short, long, value_name = "FILE")]
    pub(crate) input_file: PathBuf,

    /// The format to convert the environment variables to.
    #[arg(
        value_enum,
        short = 'f',
        long,
        value_name = "FORMAT",
        default_value = "kubernetes-json"
    )]
    pub(crate) output_format: OutputFormat,

    /// Output file. Output will be written to stdout if not specified.
    #[arg(short, long, value_name = "FILE")]
    pub(crate) output_file: Option<PathBuf>,

    /// Prefix to be used for each environment variable in the generated output.
    #[arg(short, long, default_value = "ROUTER")]
    pub(crate) prefix: String,
}

/// The output format to convert environment variables to.
#[derive(clap::ValueEnum, Clone, Copy, Debug)]
pub(crate) enum OutputFormat {
    /// Converts each environment variable to an object containing `name` and `value` fields.
    ///
    /// ```json
    /// {
    ///   "name": "ENVIRONMENT",
    ///   "value": "PRODUCTION"
    /// }
    /// ```
    KubernetesJson,
}


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

impl UserKeyStoreNew


// Struct: Datatrans3DSResponse
// File: crates/hyperswitch_connectors/src/connectors/datatrans/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct Datatrans3DSResponse


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

pub struct OrderInformationWithBill


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

impl api::RefundExecute for for Affirm


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

// Public submodules:
pub mod transformers;


// Struct: CountAccumulator
// File: crates/analytics/src/sdk_events/accumulator.rs
// Module: analytics
// Implementations: 1
// Traits: SdkEventMetricAccumulator

pub struct CountAccumulator


// Struct: HostedUrlResponse
// File: crates/hyperswitch_connectors/src/connectors/boku/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct HostedUrlResponse


// Function: find_last_successful_attempt_by_payment_id_merchant_id
// File: crates/diesel_models/src/query/payment_attempt.rs
// Module: diesel_models

pub fn find_last_successful_attempt_by_payment_id_merchant_id(
        conn: &PgPooledConn,
        payment_id: &common_utils::id_type::PaymentId,
        merchant_id: &common_utils::id_type::MerchantId,
    ) -> StorageResult<Self>


// Function: set_stored_token_response
// File: crates/router/src/core/payment_methods/tokenize/payment_method_executor.rs
// Module: router

pub fn set_stored_token_response(
        self,
        store_token_response: &'a pm_transformers::StoreCardRespPayload,
    ) -> NetworkTokenizationBuilder<'a, PmTokenStored>


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

impl api::Payment for for Itaubank


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

// Public submodules:
pub mod transformers;


// File: crates/router/src/core/payments/flows/session_flow.rs
// Module: router

use api_models::{admin as admin_types, payments as payment_types};
use async_trait::async_trait;
use common_utils::{
    ext_traits::ByteSliceExt,
    request::RequestContent,
    types::{AmountConvertor, StringMajorUnitForConnector},
};
use error_stack::{Report, ResultExt};
#[cfg(feature = "v2")]
use hyperswitch_domain_models::payments::PaymentIntentData;
use masking::{ExposeInterface, ExposeOptionInterface};

use super::{ConstructFlowSpecificData, Feature};
use crate::{
    consts::PROTOCOL,
    core::{
        errors::{self, ConnectorErrorExt, RouterResult},
        payments::{self, access_token, customers, helpers, transformers, PaymentData},
    },
    headers, logger,
    routes::{self, app::settings, metrics},
    services,
    types::{
        self,
        api::{self, enums},
        domain,
    },
    utils::OptionExt,
};

#[cfg(feature = "v2")]
#[async_trait]
impl
    ConstructFlowSpecificData<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
    for PaymentIntentData<api::Session>
{
    async fn construct_router_data<'a>(
        &self,
        state: &routes::SessionState,
        connector_id: &str,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &domain::MerchantConnectorAccountTypeDetails,
        merchant_recipient_data: Option<types::MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<types::PaymentsSessionRouterData> {
        Box::pin(transformers::construct_payment_router_data_for_sdk_session(
            state,
            self.clone(),
            connector_id,
            merchant_context,
            customer,
            merchant_connector_account,
            merchant_recipient_data,
            header_payload,
        ))
        .await
    }
}

#[cfg(feature = "v1")]
#[async_trait]
impl
    ConstructFlowSpecificData<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
    for PaymentData<api::Session>
{
    async fn construct_router_data<'a>(
        &self,
        state: &routes::SessionState,
        connector_id: &str,
        merchant_context: &domain::MerchantContext,
        customer: &Option<domain::Customer>,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
        merchant_recipient_data: Option<types::MerchantRecipientData>,
        header_payload: Option<hyperswitch_domain_models::payments::HeaderPayload>,
    ) -> RouterResult<types::PaymentsSessionRouterData> {
        Box::pin(transformers::construct_payment_router_data::<
            api::Session,
            types::PaymentsSessionData,
        >(
            state,
            self.clone(),
            connector_id,
            merchant_context,
            customer,
            merchant_connector_account,
            merchant_recipient_data,
            header_payload,
        ))
        .await
    }
}

#[async_trait]
impl Feature<api::Session, types::PaymentsSessionData> for types::PaymentsSessionRouterData {
    async fn decide_flows<'a>(
        self,
        state: &routes::SessionState,
        connector: &api::ConnectorData,
        call_connector_action: payments::CallConnectorAction,
        _connector_request: Option<services::Request>,
        business_profile: &domain::Profile,
        header_payload: hyperswitch_domain_models::payments::HeaderPayload,
        _return_raw_connector_response: Option<bool>,
    ) -> RouterResult<Self> {
        metrics::SESSION_TOKEN_CREATED.add(
            1,
            router_env::metric_attributes!(("connector", connector.connector_name.to_string())),
        );
        self.decide_flow(
            state,
            connector,
            Some(true),
            call_connector_action,
            business_profile,
            header_payload,
        )
        .await
    }

    async fn add_access_token<'a>(
        &self,
        state: &routes::SessionState,
        connector: &api::ConnectorData,
        merchant_context: &domain::MerchantContext,
        creds_identifier: Option<&str>,
    ) -> RouterResult<types::AddAccessTokenResult> {
        Box::pin(access_token::add_access_token(
            state,
            connector,
            merchant_context,
            self,
            creds_identifier,
        ))
        .await
    }

    async fn create_connector_customer<'a>(
        &self,
        state: &routes::SessionState,
        connector: &api::ConnectorData,
    ) -> RouterResult<Option<String>> {
        customers::create_connector_customer(
            state,
            connector,
            self,
            types::ConnectorCustomerData::try_from(self)?,
        )
        .await
    }
}

/// This function checks if for a given connector, payment_method and payment_method_type,
/// the list of required_field_type is present in dynamic fields
#[cfg(feature = "v1")]
fn is_dynamic_fields_required(
    required_fields: &settings::RequiredFields,
    payment_method: enums::PaymentMethod,
    payment_method_type: enums::PaymentMethodType,
    connector: types::Connector,
    required_field_type: Vec<enums::FieldType>,
) -> bool {
    required_fields
        .0
        .get(&payment_method)
        .and_then(|pm_type| pm_type.0.get(&payment_method_type))
        .and_then(|required_fields_for_connector| {
            required_fields_for_connector.fields.get(&connector)
        })
        .map(|required_fields_final| {
            required_fields_final
                .non_mandate
                .iter()
                .any(|(_, val)| required_field_type.contains(&val.field_type))
                || required_fields_final
                    .mandate
                    .iter()
                    .any(|(_, val)| required_field_type.contains(&val.field_type))
                || required_fields_final
                    .common
                    .iter()
                    .any(|(_, val)| required_field_type.contains(&val.field_type))
        })
        .unwrap_or(false)
}

/// This function checks if for a given connector, payment_method and payment_method_type,
/// the list of required_field_type is present in dynamic fields
#[cfg(feature = "v2")]
fn is_dynamic_fields_required(
    required_fields: &settings::RequiredFields,
    payment_method: enums::PaymentMethod,
    payment_method_type: enums::PaymentMethodType,
    connector: types::Connector,
    required_field_type: Vec<enums::FieldType>,
) -> bool {
    required_fields
        .0
        .get(&payment_method)
        .and_then(|pm_type| pm_type.0.get(&payment_method_type))
        .and_then(|required_fields_for_connector| {
            required_fields_for_connector.fields.get(&connector)
        })
        .map(|required_fields_final| {
            required_fields_final
                .non_mandate
                .iter()
                .flatten()
                .any(|field_info| required_field_type.contains(&field_info.field_type))
                || required_fields_final
                    .mandate
                    .iter()
                    .flatten()
                    .any(|field_info| required_field_type.contains(&field_info.field_type))
                || required_fields_final
                    .common
                    .iter()
                    .flatten()
                    .any(|field_info| required_field_type.contains(&field_info.field_type))
        })
        .unwrap_or(false)
}

fn build_apple_pay_session_request(
    state: &routes::SessionState,
    request: payment_types::ApplepaySessionRequest,
    apple_pay_merchant_cert: masking::Secret<String>,
    apple_pay_merchant_cert_key: masking::Secret<String>,
) -> RouterResult<services::Request> {
    let mut url = state.conf.connectors.applepay.base_url.to_owned();
    url.push_str("paymentservices/paymentSession");

    let session_request = services::RequestBuilder::new()
        .method(services::Method::Post)
        .url(url.as_str())
        .attach_default_headers()
        .headers(vec![(
            headers::CONTENT_TYPE.to_string(),
            "application/json".to_string().into(),
        )])
        .set_body(RequestContent::Json(Box::new(request)))
        .add_certificate(Some(apple_pay_merchant_cert))
        .add_certificate_key(Some(apple_pay_merchant_cert_key))
        .build();
    Ok(session_request)
}

async fn create_applepay_session_token(
    state: &routes::SessionState,
    router_data: &types::PaymentsSessionRouterData,
    connector: &api::ConnectorData,
    business_profile: &domain::Profile,
    header_payload: hyperswitch_domain_models::payments::HeaderPayload,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let delayed_response = is_session_response_delayed(state, connector);
    if delayed_response {
        let delayed_response_apple_pay_session =
            Some(payment_types::ApplePaySessionResponse::NoSessionResponse(
                api_models::payments::NullObject,
            ));
        create_apple_pay_session_response(
            router_data,
            delayed_response_apple_pay_session,
            None, // Apple pay payment request will be none for delayed session response
            connector.connector_name.to_string(),
            delayed_response,
            payment_types::NextActionCall::Confirm,
            header_payload,
        )
    } else {
        // Get the apple pay metadata
        let apple_pay_metadata =
            helpers::get_applepay_metadata(router_data.connector_meta_data.clone())
                .attach_printable(
                    "Failed to to fetch apple pay certificates during session call",
                )?;

        // Get payment request data , apple pay session request and merchant keys
        let (
            payment_request_data,
            apple_pay_session_request_optional,
            apple_pay_merchant_cert,
            apple_pay_merchant_cert_key,
            apple_pay_merchant_identifier,
            merchant_business_country,
            merchant_configured_domain_optional,
        ) = match apple_pay_metadata {
            payment_types::ApplepaySessionTokenMetadata::ApplePayCombined(
                apple_pay_combined_metadata,
            ) => match apple_pay_combined_metadata {
                payment_types::ApplePayCombinedMetadata::Simplified {
                    payment_request_data,
                    session_token_data,
                } => {
                    logger::info!("Apple pay simplified flow");

                    let merchant_identifier = state
                        .conf
                        .applepay_merchant_configs
                        .get_inner()
                        .common_merchant_identifier
                        .clone()
                        .expose();

                    let merchant_business_country = session_token_data.merchant_business_country;

                    let apple_pay_session_request = get_session_request_for_simplified_apple_pay(
                        merchant_identifier.clone(),
                        session_token_data.clone(),
                    );

                    let apple_pay_merchant_cert = state
                        .conf
                        .applepay_decrypt_keys
                        .get_inner()
                        .apple_pay_merchant_cert
                        .clone();

                    let apple_pay_merchant_cert_key = state
                        .conf
                        .applepay_decrypt_keys
                        .get_inner()
                        .apple_pay_merchant_cert_key
                        .clone();

                    (
                        payment_request_data,
                        Ok(apple_pay_session_request),
                        apple_pay_merchant_cert,
                        apple_pay_merchant_cert_key,
                        merchant_identifier,
                        merchant_business_country,
                        Some(session_token_data.initiative_context),
                    )
                }
                payment_types::ApplePayCombinedMetadata::Manual {
                    payment_request_data,
                    session_token_data,
                } => {
                    logger::info!("Apple pay manual flow");

                    let apple_pay_session_request = get_session_request_for_manual_apple_pay(
                        session_token_data.clone(),
                        header_payload.x_merchant_domain.clone(),
                    );

                    let merchant_business_country = session_token_data.merchant_business_country;

                    (
                        payment_request_data,
                        apple_pay_session_request,
                        session_token_data.certificate.clone(),
                        session_token_data.certificate_keys,
                        session_token_data.merchant_identifier,
                        merchant_business_country,
                        session_token_data.initiative_context,
                    )
                }
            },
            payment_types::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => {
                logger::info!("Apple pay manual flow");

                let apple_pay_session_request = get_session_request_for_manual_apple_pay(
                    apple_pay_metadata.session_token_data.clone(),
                    header_payload.x_merchant_domain.clone(),
                );

                let merchant_business_country = apple_pay_metadata
                    .session_token_data
                    .merchant_business_country;
                (
                    apple_pay_metadata.payment_request_data,
                    apple_pay_session_request,
                    apple_pay_metadata.session_token_data.certificate.clone(),
                    apple_pay_metadata
                        .session_token_data
                        .certificate_keys
                        .clone(),
                    apple_pay_metadata.session_token_data.merchant_identifier,
                    merchant_business_country,
                    apple_pay_metadata.session_token_data.initiative_context,
                )
            }
        };

        // Get amount info for apple pay
        let amount_info = get_apple_pay_amount_info(
            payment_request_data.label.as_str(),
            router_data.request.to_owned(),
        )?;

        let required_billing_contact_fields = if business_profile
            .always_collect_billing_details_from_wallet_connector
            .unwrap_or(false)
        {
            Some(payment_types::ApplePayBillingContactFields(vec![
                payment_types::ApplePayAddressParameters::PostalAddress,
            ]))
        } else if business_profile
            .collect_billing_details_from_wallet_connector
            .unwrap_or(false)
        {
            let billing_variants = enums::FieldType::get_billing_variants();
            is_dynamic_fields_required(
                &state.conf.required_fields,
                enums::PaymentMethod::Wallet,
                enums::PaymentMethodType::ApplePay,
                connector.connector_name,
                billing_variants,
            )
            .then_some(payment_types::ApplePayBillingContactFields(vec![
                payment_types::ApplePayAddressParameters::PostalAddress,
            ]))
        } else {
            None
        };

        let required_shipping_contact_fields = if business_profile
            .always_collect_shipping_details_from_wallet_connector
            .unwrap_or(false)
        {
            Some(payment_types::ApplePayShippingContactFields(vec![
                payment_types::ApplePayAddressParameters::PostalAddress,
                payment_types::ApplePayAddressParameters::Phone,
                payment_types::ApplePayAddressParameters::Email,
            ]))
        } else if business_profile
            .collect_shipping_details_from_wallet_connector
            .unwrap_or(false)
        {
            let shipping_variants = enums::FieldType::get_shipping_variants();
            is_dynamic_fields_required(
                &state.conf.required_fields,
                enums::PaymentMethod::Wallet,
                enums::PaymentMethodType::ApplePay,
                connector.connector_name,
                shipping_variants,
            )
            .then_some(payment_types::ApplePayShippingContactFields(vec![
                payment_types::ApplePayAddressParameters::PostalAddress,
                payment_types::ApplePayAddressParameters::Phone,
                payment_types::ApplePayAddressParameters::Email,
            ]))
        } else {
            None
        };

        // If collect_shipping_details_from_wallet_connector is false, we check if
        // collect_billing_details_from_wallet_connector is true. If it is, then we pass the Email and Phone in
        // ApplePayShippingContactFields as it is a required parameter and ApplePayBillingContactFields
        // does not contain Email and Phone.
        let required_shipping_contact_fields_updated = if required_billing_contact_fields.is_some()
            && required_shipping_contact_fields.is_none()
        {
            Some(payment_types::ApplePayShippingContactFields(vec![
                payment_types::ApplePayAddressParameters::Phone,
                payment_types::ApplePayAddressParameters::Email,
            ]))
        } else {
            required_shipping_contact_fields
        };

        // Get apple pay payment request
        let applepay_payment_request = get_apple_pay_payment_request(
            amount_info,
            payment_request_data,
            router_data.request.to_owned(),
            apple_pay_merchant_identifier.as_str(),
            merchant_business_country,
            required_billing_contact_fields,
            required_shipping_contact_fields_updated,
        )?;

        let apple_pay_session_response = match (
            header_payload.browser_name.clone(),
            header_payload.x_client_platform.clone(),
        ) {
            (Some(common_enums::BrowserName::Safari), Some(common_enums::ClientPlatform::Web))
            | (None, None) => {
                let apple_pay_session_request = apple_pay_session_request_optional
                    .attach_printable("Failed to obtain apple pay session request")?;
                let applepay_session_request = build_apple_pay_session_request(
                    state,
                    apple_pay_session_request.clone(),
                    apple_pay_merchant_cert.clone(),
                    apple_pay_merchant_cert_key.clone(),
                )?;

                let response = services::call_connector_api(
                    state,
                    applepay_session_request,
                    "create_apple_pay_session_token",
                )
                .await;

                let updated_response = match (
                    response.as_ref().ok(),
                    header_payload.x_merchant_domain.clone(),
                ) {
                    (Some(Err(error)), Some(_)) => {
                        logger::error!(
                            "Retry apple pay session call with the merchant configured domain {error:?}"
                        );
                        let merchant_configured_domain = merchant_configured_domain_optional
                            .get_required_value("apple pay domain")
                            .attach_printable("Failed to get domain for apple pay session call")?;
                        let apple_pay_retry_session_request =
                            payment_types::ApplepaySessionRequest {
                                initiative_context: merchant_configured_domain,
                                ..apple_pay_session_request
                            };
                        let applepay_retry_session_request = build_apple_pay_session_request(
                            state,
                            apple_pay_retry_session_request,
                            apple_pay_merchant_cert,
                            apple_pay_merchant_cert_key,
                        )?;
                        services::call_connector_api(
                            state,
                            applepay_retry_session_request,
                            "create_apple_pay_session_token",
                        )
                        .await
                    }
                    _ => response,
                };

                // logging the error if present in session call response
                log_session_response_if_error(&updated_response);
                updated_response
                    .ok()
                    .and_then(|apple_pay_res| {
                        apple_pay_res
                            .map(|res| {
                                let response: Result<
                                    payment_types::NoThirdPartySdkSessionResponse,
                                    Report<common_utils::errors::ParsingError>,
                                > = res.response.parse_struct("NoThirdPartySdkSessionResponse");

                                // logging the parsing failed error
                                if let Err(error) = response.as_ref() {
                                    logger::error!(?error);
                                };

                                response.ok()
                            })
                            .ok()
                    })
                    .flatten()
            }
            _ => {
                logger::debug!("Skipping apple pay session call based on the browser name");
                None
            }
        };

        let session_response =
            apple_pay_session_response.map(payment_types::ApplePaySessionResponse::NoThirdPartySdk);

        create_apple_pay_session_response(
            router_data,
            session_response,
            Some(applepay_payment_request),
            connector.connector_name.to_string(),
            delayed_response,
            payment_types::NextActionCall::Confirm,
            header_payload,
        )
    }
}

fn create_paze_session_token(
    router_data: &types::PaymentsSessionRouterData,
    _header_payload: hyperswitch_domain_models::payments::HeaderPayload,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let paze_wallet_details = router_data
        .connector_wallets_details
        .clone()
        .parse_value::<payment_types::PazeSessionTokenData>("PazeSessionTokenData")
        .change_context(errors::ConnectorError::NoConnectorWalletDetails)
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "connector_wallets_details".to_string(),
            expected_format: "paze_metadata_format".to_string(),
        })?;
    let required_amount_type = StringMajorUnitForConnector;
    let transaction_currency_code = router_data.request.currency;
    let transaction_amount = required_amount_type
        .convert(router_data.request.minor_amount, transaction_currency_code)
        .change_context(errors::ApiErrorResponse::PreconditionFailed {
            message: "Failed to convert amount to string major unit for paze".to_string(),
        })?;
    Ok(types::PaymentsSessionRouterData {
        response: Ok(types::PaymentsResponseData::SessionResponse {
            session_token: payment_types::SessionToken::Paze(Box::new(
                payment_types::PazeSessionTokenResponse {
                    client_id: paze_wallet_details.data.client_id,
                    client_name: paze_wallet_details.data.client_name,
                    client_profile_id: paze_wallet_details.data.client_profile_id,
                    transaction_currency_code,
                    transaction_amount,
                    email_address: router_data.request.email.clone(),
                },
            )),
        }),
        ..router_data.clone()
    })
}

fn create_samsung_pay_session_token(
    state: &routes::SessionState,
    router_data: &types::PaymentsSessionRouterData,
    header_payload: hyperswitch_domain_models::payments::HeaderPayload,
    connector: &api::ConnectorData,
    business_profile: &domain::Profile,
) -> RouterResult<types::PaymentsSessionRouterData> {
    let samsung_pay_session_token_data = router_data
        .connector_wallets_details
        .clone()
        .parse_value::<payment_types::SamsungPaySessionTokenData>("SamsungPaySessionTokenData")
        .change_context(errors::ConnectorError::NoConnectorWalletDetails)
        .change_context(errors::ApiErrorResponse::InvalidDataFormat {
            field_name: "connector_wallets_details".to_string(),
            expected_format: "samsung_pay_metadata_format".to_string(),
        })?;

    let required_amount_type = StringMajorUnitForConnector;
    let samsung_pay_amount = required_amount_type
        .convert(
            router_data.request.minor_amount,
            router_data.request.currency,
        )
        .change_context(errors::ApiErrorResponse::PreconditionFailed {
            message: "Failed to convert amount to string major unit for Samsung Pay".to_string(),
        })?;

    let merchant_domain = match header_payload.x_client_platform {
        Some(common_enums::ClientPlatform::Web) => Some(
            header_payload
                .x_merchant_domain
                .get_required_value("samsung pay domain")
                .attach_printable("Failed to get domain for samsung pay session call")?,
        ),
        _ => None,
    };

    let samsung_pay_wallet_details = match samsung_pay_session_token_data.data {
        payment_types::SamsungPayCombinedMetadata::MerchantCredentials(
            samsung_pay_merchant_credentials,
        ) => samsung_pay_merchant_credentials,
        payment_types::SamsungPayCombinedMetadata::ApplicationCredentials(
            _samsung_pay_application_credentials,
        ) => Err(errors::ApiErrorResponse::NotSupported {
            message: "Samsung Pay decryption flow with application credentials is not implemented"
                .to_owned(),
        })?,
    };

    let formatted_payment_id = router_data.payment_id.replace("_", "-");

    let billing_address_required = is_billing_address_required_to_be_collected_from_wallet(
        state,
        connector,
        business_profile,
        enums::PaymentMethodType::SamsungPay,
    );

    let shipping_address_required = is_shipping_address_required_to_be_collected_form_wallet(
        state,
        connector,
        business_profile,
        enums::PaymentMethodType::SamsungPay,
    );

    Ok(types::PaymentsSessionRouterData {
        response: Ok(types::PaymentsResponseData::SessionResponse {
            session_token: payment_types::SessionToken::SamsungPay(Box::new(
                payment_types::SamsungPaySessionTokenResponse {
                    version: "2".to_string(),
                    service_id: samsung_pay_wallet_details.service_id,
                    order_number: formatted_payment_id,
                    merchant_payment_information:
                        payment_types::SamsungPayMerchantPaymentInformation {
                            name: samsung_pay_wallet_details.merchant_display_name,
                            url: merchant_domain,
                            country_code: samsung_pay_wallet_details.merchant_business_country,
                        },
                    amount: payment_types::SamsungPayAmountDetails {
                        amount_format: payment_types::SamsungPayAmountFormat::FormatTotalPriceOnly,
                        currency_code: router_data.request.currency,
                        total_amount: samsung_pay_amount,
                    },
                    protocol: payment_types::SamsungPayProtocolType::Protocol3ds,
                    allowed_brands: samsung_pay_wallet_details.allowed_brands,
                    billing_address_required,
                    shipping_address_required,
                },
            )),
        }),
        ..router_data.clone()
    })
}

/// Function to determine whether the billing address is required to be collected from the wallet,
/// based on business profile settings, the payment method type, and the connector's required fields
/// for the specific payment method.
///
/// If `always_collect_billing_details_from_wallet_connector` is enabled, it indicates that the
/// billing address is always required to be collected from the wallet.
///
/// If only `collect_billing_details_from_wallet_connector` is enabled, the billing address will be
/// collected only if the connector required fields for the specific payment method type contain
/// the billing fields.
fn is_billing_address_required_to_be_collected_from_wallet(
    state: &routes::SessionState,
    connector: &api::ConnectorData,
    business_profile: &domain::Profile,
    payment_method_type: enums::PaymentMethodType,
) -> bool {
    let always_collect_billing_details_from_wallet_connector = business_profile
        .always_collect_billing_details_from_wallet_connector
        .unwrap_or(false);

    if always_collect_billing_details_from_wallet_connector {
        always_collect_billing_details_from_wallet_connector
    } else if business_profile
        .collect_billing_details_from_wallet_connector
        .unwrap_or(false)
    {
        let billing_variants = enums::FieldType::get_billing_variants();

        is_dynamic_fields_required(
            &state.conf.required_fields,
            enums::PaymentMethod::Wallet,
            payment_method_type,
            connector.connector_name,
            billing_variants,
        )
    } else {
        false
    }
}

/// Function to determine whether the shipping address is required to be collected from the wallet,
/// based on business profile settings, the payment method type, and the connector required fields
/// for the specific payment method type.
///
/// If `always_collect_shipping_details_from_wallet_connector` is enabled, it indicates that the
/// shipping address is always required to be collected from the wallet.
///
/// If only `collect_shipping_details_from_wallet_connector` is enabled, the shipping address will be
/// collected only if the connector required fields for the specific payment method type contain
/// the shipping fields.
fn is_shipping_address_required_to_be_collected_form_wallet(
    state: &routes::SessionState,
    connector: &api::ConnectorData,
    business_profile: &domain::Profile,
    payment_method_type: enums::PaymentMethodType,
) -> bool {
    let always_collect_shipping_details_from_wallet_connector = business_profile
        .always_collect_shipping_details_from_wallet_connector
        .unwrap_or(false);

    if always_collect_shipping_details_from_wallet_connector {
        always_collect_shipping_details_from_wallet_connector
    } else if business_profile
        .collect_shipping_details_from_wallet_connector
        .unwrap_or(false)
    {
        let shipping_variants = enums::FieldType::get_shipping_variants();

        is_dynamic_fields_required(
            &state.conf.required_fields,
            enums::PaymentMethod::Wallet,
            payment_method_type,
            connector.connector_name,
            shipping_variants,
        )
    } else {
        false
    }
}

fn get_session_request_for_simplified_apple_pay(
    apple_pay_merchant_identifier: String,
    session_token_data: payment_types::SessionTokenForSimplifiedApplePay,
) -> payment_types::ApplepaySessionRequest {
    payment_types::ApplepaySessionRequest {
        merchant_identifier: apple_pay_merchant_identifier,
        display_name: "Apple pay".to_string(),
        initiative: "web".to_string(),
        initiative_context: session_token_data.initiative_context,
    }
}

fn get_session_request_for_manual_apple_pay(
    session_token_data: payment_types::SessionTokenInfo,
    merchant_domain: Option<String>,
) -> RouterResult<payment_types::ApplepaySessionRequest> {
    let initiative_context = merchant_domain
        .or_else(|| session_token_data.initiative_context.clone())
        .get_required_value("apple pay domain")
        .attach_printable("Failed to get domain for apple pay session call")?;

    Ok(payment_types::ApplepaySessionRequest {
        merchant_identifier: session_token_data.merchant_identifier.clone(),
        display_name: session_token_data.display_name.clone(),
        initiative: session_token_data.initiative.to_string(),
        initiative_context,
    })
}

fn get_apple_pay_amount_info(
    label: &str,
    session_data: types::PaymentsSessionData,
) -> RouterResult<payment_types::AmountInfo> {
    let required_amount_type = StringMajorUnitForConnector;
    let apple_pay_amount = required_amount_type
        .convert(session_data.minor_amount, session_data.currency)
        .change_context(errors::ApiErrorResponse::PreconditionFailed {
            message: "Failed to convert amount to string major unit for applePay".to_string(),
        })?;
    let amount_info = payment_types::AmountInfo {
        label: label.to_string(),
        total_type: Some("final".to_string()),
        amount: apple_pay_amount,
    };

    Ok(amount_info)
}

fn get_apple_pay_payment_request(
    amount_info: payment_types::AmountInfo,
    payment_request_data: payment_types::PaymentRequestMetadata,
    session_data: types::PaymentsSessionData,
    merchant_identifier: &str,
    merchant_business_country: Option<api_models::enums::CountryAlpha2>,
    required_billing_contact_fields: Option<payment_types::ApplePayBillingContactFields>,
    required_shipping_contact_fields: Option<payment_types::ApplePayShippingContactFields>,
) -> RouterResult<payment_types::ApplePayPaymentRequest> {
    let applepay_payment_request = payment_types::ApplePayPaymentRequest {
        country_code: merchant_business_country.or(session_data.country).ok_or(
            errors::ApiErrorResponse::MissingRequiredField {
                field_name: "country_code",
            },
        )?,
        currency_code: session_data.currency,
        total: amount_info,
        merchant_capabilities: Some(payment_request_data.merchant_capabilities),
        supported_networks: Some(payment_request_data.supported_networks),
        merchant_identifier: Some(merchant_identifier.to_string()),
        required_billing_contact_fields,
        required_shipping_contact_fields,
        recurring_payment_request: session_data.apple_pay_recurring_details,
    };
    Ok(applepay_payment_request)
}

fn create_apple_pay_session_response(
    router_data: &types::PaymentsSessionRouterData,
    session_response: Option<payment_types::ApplePaySessionResponse>,
    apple_pay_payment_request: Option<payment_types::ApplePayPaymentRequest>,
    connector_name: String,
    delayed_response: bool,
    next_action: payment_types::NextActionCall,
    header_payload: hyperswitch_domain_models::payments::HeaderPayload,
) -> RouterResult<types::PaymentsSessionRouterData> {
    match session_response {
        Some(response) => Ok(types::PaymentsSessionRouterData {
            response: Ok(types::PaymentsResponseData::SessionResponse {
                session_token: payment_types::SessionToken::ApplePay(Box::new(
                    payment_types::ApplepaySessionTokenResponse {
                        session_token_data: Some(response),
                        payment_request_data: apple_pay_payment_request,
                        connector: connector_name,
                        delayed_session_token: delayed_response,
                        sdk_next_action: { payment_types::SdkNextAction { next_action } },
                        connector_reference_id: None,
                        connector_sdk_public_key: None,
                        connector_merchant_id: None,
                    },
                )),
            }),
            ..router_data.clone()
        }),
        None => {
            match (
                header_payload.browser_name,
                header_payload.x_client_platform,
            ) {
                (
                    Some(common_enums::BrowserName::Safari),
                    Some(common_enums::ClientPlatform::Web),
                )
                | (None, None) => Ok(types::PaymentsSessionRouterData {
                    response: Ok(types::PaymentsResponseData::SessionResponse {
                        session_token: payment_types::SessionToken::NoSessionTokenReceived,
                    }),
                    ..router_data.clone()
                }),
                _ => Ok(types::PaymentsSessionRouterData {
                    response: Ok(types::PaymentsResponseData::SessionResponse {
                        session_token: payment_types::SessionToken::ApplePay(Box::new(
                            payment_types::ApplepaySessionTokenResponse {
                                session_token_data: None,
                                payment_request_data: apple_pay_payment_request,
                                connector: connector_name,
                                delayed_session_token: delayed_response,
                                sdk_next_action: { payment_types::SdkNextAction { next_action } },
                                connector_reference_id: None,
                                connector_sdk_public_key: None,
                                connector_merchant_id: None,
                            },
                        )),
                    }),
                    ..router_data.clone()
                }),
            }
        }
    }
}

fn create_gpay_session_token(
    state: &routes::SessionState,
    router_data: &types::PaymentsSessionRouterData,
    connector: &api::ConnectorData,
    business_profile: &domain::Profile,
) -> RouterResult<types::PaymentsSessionRouterData> {
    // connector_wallet_details is being parse into admin types to check specifically if google_pay field is present
    // this is being done because apple_pay details from metadata is also being filled into connector_wallets_details
    let google_pay_wallets_details = router_data
        .connector_wallets_details
        .clone()
        .parse_value::<admin_types::ConnectorWalletDetails>("ConnectorWalletDetails")
        .change_context(errors::ConnectorError::NoConnectorWalletDetails)
        .attach_printable(format!(
            "cannot parse connector_wallets_details from the given value {:?}",
            router_data.connector_wallets_details
        ))
        .map_err(|err| {
            logger::debug!(
                "Failed to parse connector_wallets_details for google_pay flow: {:?}",
                err
            );
        })
        .ok()
        .and_then(|connector_wallets_details| connector_wallets_details.google_pay);
    let connector_metadata = router_data.connector_meta_data.clone();
    let delayed_response = is_session_response_delayed(state, connector);

    if delayed_response {
        Ok(types::PaymentsSessionRouterData {
            response: Ok(types::PaymentsResponseData::SessionResponse {
                session_token: payment_types::SessionToken::GooglePay(Box::new(
                    payment_types::GpaySessionTokenResponse::ThirdPartyResponse(
                        payment_types::GooglePayThirdPartySdk {
                            delayed_session_token: true,
                            connector: connector.connector_name.to_string(),
                            sdk_next_action: payment_types::SdkNextAction {
                                next_action: payment_types::NextActionCall::Confirm,
                            },
                        },
                    ),
                )),
            }),
            ..router_data.clone()
        })
    } else {
        let is_billing_details_required = is_billing_address_required_to_be_collected_from_wallet(
            state,
            connector,
            business_profile,
            enums::PaymentMethodType::GooglePay,
        );

        let required_amount_type = StringMajorUnitForConnector;
        let google_pay_amount = required_amount_type
            .convert(
                router_data.request.minor_amount,
                router_data.request.currency,
            )
            .change_context(errors::ApiErrorResponse::PreconditionFailed {
                message: "Failed to convert amount to string major unit for googlePay".to_string(),
            })?;
        let session_data = router_data.request.clone();
        let transaction_info = payment_types::GpayTransactionInfo {
            country_code: session_data.country.unwrap_or_default(),
            currency_code: router_data.request.currency,
            total_price_status: "Final".to_string(),
            total_price: google_pay_amount,
        };

        let required_shipping_contact_fields =
            is_shipping_address_required_to_be_collected_form_wallet(
                state,
                connector,
                business_profile,
                enums::PaymentMethodType::GooglePay,
            );

        if google_pay_wallets_details.is_some() {
            let gpay_data = router_data
                .connector_wallets_details
                .clone()
                .parse_value::<payment_types::GooglePayWalletDetails>("GooglePayWalletDetails")
                .change_context(errors::ConnectorError::NoConnectorWalletDetails)
                .attach_printable(format!(
                    "cannot parse gpay connector_wallets_details from the given value {:?}",
                    router_data.connector_wallets_details
                ))
                .change_context(errors::ApiErrorResponse::InvalidDataFormat {
                    field_name: "connector_wallets_details".to_string(),
                    expected_format: "gpay_connector_wallets_details_format".to_string(),
                })?;

            let payment_types::GooglePayProviderDetails::GooglePayMerchantDetails(gpay_info) =
                gpay_data.google_pay.provider_details.clone();

            let gpay_allowed_payment_methods = get_allowed_payment_methods_from_cards(
                gpay_data,
                &gpay_info.merchant_info.tokenization_specification,
                is_billing_details_required,
            )?;

            Ok(types::PaymentsSessionRouterData {
                response: Ok(types::PaymentsResponseData::SessionResponse {
                    session_token: payment_types::SessionToken::GooglePay(Box::new(
                        payment_types::GpaySessionTokenResponse::GooglePaySession(
                            payment_types::GooglePaySessionResponse {
                                merchant_info: payment_types::GpayMerchantInfo {
                                    merchant_name: gpay_info.merchant_info.merchant_name,
                                    merchant_id: gpay_info.merchant_info.merchant_id,
                                },
                                allowed_payment_methods: vec![gpay_allowed_payment_methods],
                                transaction_info,
                                connector: connector.connector_name.to_string(),
                                sdk_next_action: payment_types::SdkNextAction {
                                    next_action: payment_types::NextActionCall::Confirm,
                                },
                                delayed_session_token: false,
                                secrets: None,
                                shipping_address_required: required_shipping_contact_fields,
                                // We pass Email as a required field irrespective of
                                // collect_billing_details_from_wallet_connector or
                                // collect_shipping_details_from_wallet_connector as it is common to both.
                                email_required: required_shipping_contact_fields
                                    || is_billing_details_required,
                                shipping_address_parameters:
                                    api_models::payments::GpayShippingAddressParameters {
                                        phone_number_required: required_shipping_contact_fields,
                                    },
                            },
                        ),
                    )),
                }),
                ..router_data.clone()
            })
        } else {
            let billing_address_parameters = is_billing_details_required.then_some(
                payment_types::GpayBillingAddressParameters {
                    phone_number_required: is_billing_details_required,
                    format: payment_types::GpayBillingAddressFormat::FULL,
                },
            );

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

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


// Function: find_by_fingerprint_id
// File: crates/diesel_models/src/query/payment_method.rs
// Module: diesel_models

pub fn find_by_fingerprint_id(
        conn: &PgPooledConn,
        fingerprint_id: &str,
    ) -> StorageResult<Self>


// Struct: ProcessingOptions
// File: crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ProcessingOptions


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

impl api::Payment for for Coinbase


// Implementation: impl NetworkTokenizationBuilder
// File: crates/router/src/core/payment_methods/tokenize/card_executor.rs
// Module: router
// Methods: 1 total (1 public)

impl NetworkTokenizationBuilder


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

pub fn perform_static_routing_v1(
    state: &SessionState,
    merchant_id: &common_utils::id_type::MerchantId,
    algorithm_id: Option<&common_utils::id_type::RoutingId>,
    business_profile: &domain::Profile,
    transaction_data: &routing::TransactionData<'_>,
) -> RoutingResult<(
    Vec<routing_types::RoutableConnectorChoice>,
    Option<common_enums::RoutingApproach>,
)>


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

impl api::PaymentToken for for Datatrans


// Struct: StripePaymentListConstraints
// File: crates/router/src/compatibility/stripe/payment_intents/types.rs
// Module: router
// Implementations: 0

pub struct StripePaymentListConstraints


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

pub struct BluesnapAuthErrorResponse


// Struct: MonerisPaymentsRequest
// File: crates/hyperswitch_connectors/src/connectors/moneris/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct MonerisPaymentsRequest


// Implementation: impl Webhooks
// File: crates/router/src/routes/app.rs
// Module: router
// Methods: 1 total (1 public)

impl Webhooks


// Function: get_total_count_of_attempts
// File: crates/diesel_models/src/query/payment_attempt.rs
// Module: diesel_models

pub fn get_total_count_of_attempts(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        active_attempt_ids: &[String],
        connector: Option<Vec<String>>,
        payment_method: Option<Vec<enums::PaymentMethod>>,
        payment_method_type: Option<Vec<enums::PaymentMethodType>>,
        authentication_type: Option<Vec<enums::AuthenticationType>>,
        merchant_connector_id: Option<Vec<common_utils::id_type::MerchantConnectorAccountId>>,
        card_network: Option<Vec<enums::CardNetwork>>,
        card_discovery: Option<Vec<enums::CardDiscovery>>,
    ) -> StorageResult<i64>